Part of the Khronos Group
OpenGL.org

The Industry's Foundation for High Performance Graphics

from games to virtual reality, mobile phones to supercomputers

Results 1 to 10 of 10

Thread: How are dFdx and dFdy functions implemented

  1. #1
    Advanced Member Frequent Contributor
    Join Date
    Mar 2009
    Location
    Singapore
    Posts
    800

    How are dFdx and dFdy functions implemented

    Hi all,
    I know from the specs that dFdx and dFdy return the derivatives in screen space for a fragment shader. I have a simple test case I have implemented using GLSL and for comparison I need to do the exact case on CPU. I want to know how to do my own dFdx/dFdy functions on CPU. Based on some hints from the internet, this is how I have proceeded but it does not produce the correct output.
    Code :
    float dFdy(vec2 v) {
       int ix=0, iy=0;
       ToScreenSpace(v, &ix, &iy);
       DWORD pixelA = pixels[iy][ix];
       DWORD pixelB = pixels[iy-1][ix];
     
       //Get rgb components
       GetRGBA(pixelA, rA, gA, bA, aA);
       GetRGBA(pixelB, rB, gB, bB, aB);
     
       //calculate luminance
       float La = 0.299f*rA + 0.587f*gA + 0.114f*bA;
       float Lb = 0.299f*rB + 0.587f*gB + 0.114f*bB;
     
       //using backward finite difference;
       return Lb-La;   
    }
     
    float dFdx(vec2 v) {
       int ix=0, iy=0;
       ToScreenSpace(v, &ix, &iy);
       DWORD pixelA = pixels[iy][ix];
       DWORD pixelB = pixels[iy][ix-1];
     
       //Get rgb components
       GetRGBA(pixelA, rA, gA, bA, aA);
       GetRGBA(pixelB, rB, gB, bB, aB);
     
       //calculate luminance
       float La = 0.299f*rA + 0.587f*gA + 0.114f*bA;
       float Lb = 0.299f*rB + 0.587f*gB + 0.114f*bB;
     
       //using backward finite difference;
       return Lb-La;   
    }
    Is the above code correct? If anyone has any hints/reference/tutorial for this do let me know.
    Regards,
    Mobeen

  2. #2
    Senior Member OpenGL Lord
    Join Date
    May 2009
    Posts
    5,234

    Re: How are dFdx and dFdy functions implemented

    The specification does not say how exactly gradients are computed. It simply gives the general idea of what the functions return. The OpenGL specification isn't intended to be byte-identical, so implementations are free to pick and choose.

    However, what you propose is most certainly not it. The parameter given to the derivative functions does not take a position; it takes the value to take the derivative of. So it only samples pixels from an image if you're taking the derivative of the value of a texture lookup, or what you're writing to the framebuffer.

    If you take `dFdx(vec3)`, you get a `vec3`, where each value is the component-wise derivative of that value in the X direction. You don't pass a `vec2` and get a `float`.

  3. #3
    Advanced Member Frequent Contributor
    Join Date
    Mar 2009
    Location
    Singapore
    Posts
    800

    Re: How are dFdx and dFdy functions implemented

    However, what you propose is most certainly not it. The parameter given to the derivative functions does not take a position; it takes the value to take the derivative of.
    That is what is not making sense to me. If there is a single value, how is it going to take the derivative since as i showed u need to have two values subtracting which the derivative is obtained.
    Regards,
    Mobeen

  4. #4
    Member Regular Contributor
    Join Date
    May 2001
    Posts
    348

    Re: How are dFdx and dFdy functions implemented

    Typically, GPUs calculate 2x2 blocks of fragments in parallel. This is true even if some of those fragments are outside the primitive being drawn, they just get masked before writing to the framebuffer.

    The gradients are then calculated as the difference between the values provided for two neighbouring fragments.

  5. #5
    Advanced Member Frequent Contributor
    Join Date
    Mar 2009
    Location
    Singapore
    Posts
    800

    Re: How are dFdx and dFdy functions implemented

    Quote Originally Posted by Xmas
    Typically, GPUs calculate 2x2 blocks of fragments in parallel. This is true even if some of those fragments are outside the primitive being drawn, they just get masked before writing to the framebuffer.

    The gradients are then calculated as the difference between the values provided for two neighbouring fragments.
    Hi Xmas,
    Thanks for your response. So based on you reply and Alfonse's, it means that the value of the parameter that is passed in to dFdx/dFdy function will be evaluated at the current 2x2 neighborhood. These values will then be subtracted to obtain the derivatives. So it is something along these lines if I understood correctly. Please correct me if I am wrong.
    Code :
    //T is any generic type (float,vec2, vec3 ...)
    //I am assuming forward finite difference
    T dFdx(T x) {
       T x2 = getValueOf(x, gl_FragCoord.x+1);
       T x1 = getValueOf(x, gl_FragCoord.x);
       return x2-x1;
    }
    T dFdy(T y) {
       T y2 = getValueOf(y, gl_FragCoord.y+1);
       T y1 = getValueOf(y, gl_FragCoord.y);
       return y2-y1;
    }
    So this means that the value that we pass in to the derivative functions will be evaluated for each fragment.
    Regards,
    Mobeen

  6. #6
    Super Moderator OpenGL Lord
    Join Date
    Dec 2003
    Location
    Grenoble - France
    Posts
    5,580

    Re: How are dFdx and dFdy functions implemented

    mobeen, my understanding is that your code is only half of it, for even (or odd) values of gl_FragCoord.x/y. For odd (or even) values, it would be like :
    Code :
       T x2 = getValueOf(x, gl_FragCoord.x);
       T x1 = getValueOf(x, gl_FragCoord.x-1);

  7. #7
    Advanced Member Frequent Contributor
    Join Date
    Mar 2009
    Location
    Singapore
    Posts
    800

    Re: How are dFdx and dFdy functions implemented

    Hi Zbuffer,
    Hmmm so this means it alternates btw forward and backward difference depending on whether it is on an even or odd scanline?

    Ok, so I think I understand how it works. One thing though to emulate this behaviour on CPU, if I have a single float value say (x), i think I would need to store (4 values of x) (2x2) neighborhood per pixel. For GPUs, do they keep these values in registers or are these stored elsewhere?
    Regards,
    Mobeen

  8. #8
    Senior Member OpenGL Lord
    Join Date
    May 2009
    Posts
    5,234

    Re: How are dFdx and dFdy functions implemented

    Does it matter whether they're stored in registers? That's an implementation detail.

    The reason the derivative functions are implemented this way is because shaders always work in 2x2 blocks. You never execute a shader on a single fragment; even if the triangle only generates one fragment, it will still run it on a 2x2 block. The system just discards the three useless values.

    The 2x2 block is really just one piece of hardware that does the same operation to four separate pieces of data at once. So every time you say `vec4 * vec4`, this instruction is replicated 4 times and executed on 4 separate pieces of data, and the result is written to 4 separate values.

    Derivative functions are the only ones that break this logic. They cross the boundaries and look at the other guy's values. Those values could be in registers, or they could be in some local memory. It doesn't matter, because it's all the same piece of hardware.

  9. #9
    Super Moderator OpenGL Lord
    Join Date
    Dec 2003
    Location
    Grenoble - France
    Posts
    5,580

    Re: How are dFdx and dFdy functions implemented

    it alternates btw forward and backward difference depending on whether it is on an even or odd scanline?
    And same for even and odd vertical line. That way, each 2x2 block is independent from other blocks.

  10. #10
    Advanced Member Frequent Contributor
    Join Date
    Mar 2009
    Location
    Singapore
    Posts
    800

    Re: How are dFdx and dFdy functions implemented

    Thanks Alfonse and ZbuffeR for your answers. The reason I asked this was so that I could understand what the hardware does and then try to implement it on my CPU raytracer. The derivative functions are useful for doing anti-aliasing and based on the information you people shared, I managed to get it working in my CPU raytracer for my procedural checker texture.

    Attached are two snapshots from my raytracer showing in first the results without anti-aliasing and the second with anti-aliasing using the derivatives.

    Result of No anti-aliasing


    Result of Anti-aliasing


    Thanks once again for the information. You are really really helpful.
    Regards,
    Mobeen

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •