Reanimator Ltd

High-performance coding by Eddie Edwards

Pixel-perfect Derivatives on GPU

14 Apr 2023, 20:15 UTC

Due to perspective correction, any value that is linear in worldspace (which includes any interpolated vertex attribute) becomes a curved function in screenspace, which means that the approximation used in ddx_fine (subtract adjacent values, which measures the slope of a chord across the curved function) is not equal to the slope at either of the pixel centers, and may be wildly incorrect in some cases. Furthermore, the same value of ddx_fine is used for two pixels, rather than being a per-pixel value.

If a function is linear in screenspace, however, then even basic ddx gives the correct values, since the chord between any two points on a line has the same slope as the line itself.

We can use this fact to compute the correct per-pixel derivative of any function which is linear in worldpsace (e.g. a texture coordinate).

First, note that for any shader value U which is linear in worldspace, U/W is linear in screenspace, where W is the homogeneous W coordinate (camera Z). Therefore ddx(U/W) is correct. 1 is trivially linear in worldspace therefore 1/W is linear in screenspace and ddx(1/W) is also correct. (This fact is used in perspective-correct texture mapping, where U/W and 1/W are linearly interpolated, and U is found per pixel as (U/W)/(1/W). ddx(U/W) and ddx(1/W) are in fact constant for the whole triangle.)

So, suppose we want to find ddx(U) correctly, then we can use the chain rule of differentiation:

ddx(U * 1/W) = ddx(U) * 1/W + U * ddx(1/W)

Which implies that:

ddx(U) = W * (ddx(U/W) - U * ddx(1/W))

Since W,U are known correctly for the pixel, and ddx(U/W) and ddx(1/W) are computed correctly for the pixel, this expression for ddx(U) is also correct, allowing us to write

float ddx_exact(float U, float W) {
    float invW = 1 / W;
    return W * (ddx(U * invW) - U * ddx(invW));
}

which is correct provided U is linear in worldspace, and produces a unique value per pixel.

New comments are disabled for this page