Search code examples
openglimage-processingglslpost-processing

Depth of field artefacts


I began to implement the depth of field in my application, but I ran into a problem. Artifacts appear in the form of a non-smooth transition between depths.

I'm doing the depth of field in the following way:

  1. With the main scene rendering, I record the blur value in the alpha channel. I do this using this: fragColor.a = clamp(abs(focalDepth + fragPos.z) / focalRange, 0.0, 1.0), where focalDepth = 8, focalRange = 20.

  2. After that I apply a two-step (horizontally and vertically) Gaussian blur with dynamic size and sigma, depending on the blur value (which I previously recorded in the alpha channel)(shader below)

But I have an artifact, where you see a clear transition between the depths.

The whole scene:the whole scene And with an increased scale: artifact

My fragment blur shader:

#version 330

precision mediump float;

#define BLOOM_KERNEL_SIZE 8
#define DOF_KERNEL_SIZE 8
/* ^^^ definitions ^^^ */

layout (location = 0) out vec4 bloomFragColor;
layout (location = 1) out vec4 dofFragColor;
in vec2 texCoords;

uniform sampler2D image; // bloom
uniform sampler2D image2; // dof
uniform bool isHorizontal;
uniform float kernel[BLOOM_KERNEL_SIZE];

float dof_kernel[DOF_KERNEL_SIZE];

vec4 tmp;
vec3 bloom_result;
vec3 dof_result;
float fdof;
float dofSigma;
int dofSize;

void makeDofKernel(int size, float sigma) {
    size = size * 2 - 1;
    float tmpKernel[DOF_KERNEL_SIZE * 2 - 1];
    int mean = size / 2;
    float sum = 0; // For accumulating the kernel values
    for (int x = 0; x < size; x++)  {
        tmpKernel[x] = exp(-0.5 * pow((x - mean) / sigma, 2.0));
        // Accumulate the kernel values
        sum += tmpKernel[x];
    }

    // Normalize the kernel
    for (int x = 0; x < size; x++) 
        tmpKernel[x] /= sum;

    // need center and right part
    for (int i = 0; i < mean + 1; i++) dof_kernel[i] = tmpKernel[size / 2 + i];
}

void main() {
    vec2 texOffset = 1.0 / textureSize(image, 0); // gets size of single texel
    tmp = texture(image2, texCoords);
    fdof = tmp.a;
    dofSize = clamp(int(tmp.a * DOF_KERNEL_SIZE), 1, DOF_KERNEL_SIZE);
    if (dofSize % 2 == 0) dofSize++;
    makeDofKernel(dofSize, 12.0 * fdof + 1);

    bloom_result = texture(image, texCoords).rgb * kernel[0]; // current fragment’s contribution
    dof_result = tmp.rgb * dof_kernel[0];

    if(isHorizontal) {
        for(int i = 1; i < kernel.length(); i++) {
            bloom_result += texture(image, texCoords + vec2(texOffset.x * i, 0.0)).rgb * kernel[i];
            bloom_result += texture(image, texCoords - vec2(texOffset.x * i, 0.0)).rgb * kernel[i];
        }

        for(int i = 1; i < dofSize; i++) {
            dof_result += texture(image2, texCoords + vec2(texOffset.x * i, 0.0)).rgb * dof_kernel[i];
            dof_result += texture(image2, texCoords - vec2(texOffset.x * i, 0.0)).rgb * dof_kernel[i];
        }
    } else {
        for(int i = 1; i < kernel.length(); i++) {
            bloom_result += texture(image, texCoords + vec2(0.0, texOffset.y * i)).rgb * kernel[i];
            bloom_result += texture(image, texCoords - vec2(0.0, texOffset.y * i)).rgb * kernel[i];
        }

        for(int i = 1; i < dofSize; i++) {
            dof_result += texture(image2, texCoords + vec2(0.0, texOffset.y * i)).rgb * dof_kernel[i];
            dof_result += texture(image2, texCoords - vec2(0.0, texOffset.y * i)).rgb * dof_kernel[i];
        }
    }

    bloomFragColor = vec4(bloom_result, 1.0);
    dofFragColor = vec4(dof_result, fdof);
}

And the settings for the DOF texture: glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA32F, SCR_W, SCR_H, 0, GL_RGBA, GL_FLOAT, NULL)

Optimization of the shader I'll do later, now I'm very concerned about this artifact. How it can be eliminated? It is desirable not to change the way of realization of the depth of field. But if you know a more productive way - a big request to share it.

I will be grateful for help.


Solution

  • The problem is solved. My mistake was that I changed the size of DOF blur kernel, although I had to change only the sigma. Corrected shader code:

    #version 330
    
    precision mediump float;
    
    #define BLOOM_KERNEL_SIZE 8
    #define DOF_KERNEL_SIZE 8
    /* ^^^ definitions ^^^ */
    
    layout (location = 0) out vec4 bloomFragColor;
    layout (location = 1) out vec4 dofFragColor;
    
    in vec2 texCoords;
    
    uniform sampler2D image; // bloom
    uniform sampler2D image2; // dof
    uniform bool isHorizontal;
    
    uniform float max_sigma = 12.0;
    uniform float min_sigma = 0.0001;
    
    uniform float kernel[BLOOM_KERNEL_SIZE];
    
    float dof_kernel[DOF_KERNEL_SIZE];
    
    vec4 tmp;
    vec3 bloom_result;
    vec3 dof_result;
    float fdof;
    
    const int DOF_LCR_SIZE = DOF_KERNEL_SIZE * 2 - 1; // left-center-right (lllcrrr)
    const int DOF_MEAN = DOF_LCR_SIZE / 2;
    
    void makeDofKernel(float sigma) {
        float sum = 0; // For accumulating the kernel values
        for (int x = DOF_MEAN; x < DOF_LCR_SIZE; x++)  {
            dof_kernel[x - DOF_MEAN] = exp(-0.5 * pow((x - DOF_MEAN) / sigma, 2.0));
            // Accumulate the kernel values
            sum += dof_kernel[x - DOF_MEAN];
        }
    
        sum += sum - dof_kernel[0];
    
        // Normalize the kernel
        for (int x = 0; x < DOF_KERNEL_SIZE; x++) dof_kernel[x] /= sum;
    }
    
    void main() {
        vec2 texOffset = 1.0 / textureSize(image, 0); // gets size of single texel
        tmp = texture(image2, texCoords);
        fdof = tmp.a;
        makeDofKernel(max_sigma * fdof + min_sigma);
    
        bloom_result = texture(image, texCoords).rgb * kernel[0]; // current fragment’s contribution
        dof_result = tmp.rgb * dof_kernel[0];
    
        if(isHorizontal) {
            for(int i = 1; i < BLOOM_KERNEL_SIZE; i++) {
                bloom_result += texture(image, texCoords + vec2(texOffset.x * i, 0.0)).rgb * kernel[i];
                bloom_result += texture(image, texCoords - vec2(texOffset.x * i, 0.0)).rgb * kernel[i];
            }
    
            for(int i = 1; i < DOF_KERNEL_SIZE; i++) {
                dof_result += texture(image2, texCoords + vec2(texOffset.x * i, 0.0)).rgb * dof_kernel[i];
                dof_result += texture(image2, texCoords - vec2(texOffset.x * i, 0.0)).rgb * dof_kernel[i];
            }
        } else {
            for(int i = 1; i < BLOOM_KERNEL_SIZE; i++) {
                bloom_result += texture(image, texCoords + vec2(0.0, texOffset.y * i)).rgb * kernel[i];
                bloom_result += texture(image, texCoords - vec2(0.0, texOffset.y * i)).rgb * kernel[i];
            }
    
            for(int i = 1; i < DOF_KERNEL_SIZE; i++) {
                dof_result += texture(image2, texCoords + vec2(0.0, texOffset.y * i)).rgb * dof_kernel[i];
                dof_result += texture(image2, texCoords - vec2(0.0, texOffset.y * i)).rgb * dof_kernel[i];
            }
        }
    
        bloomFragColor = vec4(bloom_result, 1.0);
        dofFragColor = vec4(dof_result, fdof);
    }
    

    Result: resultresult