Reading through the source code of the XeGTAO project I found this function while looking for the denoise filter:

lpfloat4 XeGTAO_CalculateEdges( const lpfloat centerZ, const lpfloat leftZ, const lpfloat rightZ, const lpfloat topZ, const lpfloat bottomZ )
    lpfloat4 edgesLRTB = lpfloat4( leftZ, rightZ, topZ, bottomZ ) - (lpfloat)centerZ;

    lpfloat slopeLR = (edgesLRTB.y - edgesLRTB.x) * 0.5;
    lpfloat slopeTB = (edgesLRTB.w - edgesLRTB.z) * 0.5;
    lpfloat4 edgesLRTBSlopeAdjusted = edgesLRTB + lpfloat4( slopeLR, -slopeLR, slopeTB, -slopeTB );
    edgesLRTB = min( abs( edgesLRTB ), abs( edgesLRTBSlopeAdjusted ) );
    return lpfloat4(saturate( ( 1.25 - edgesLRTB / (centerZ * 0.011) ) ));

(From XeGTAO.hlsli)

Where centerZ, left/right/...Z are view space z values.

As the name suggests the function is used to find edges for the denoising filter.

While I understand the general idea I find it difficult to make sense of the last line, this thing in particular: 1.25 - edgesLRTB / (centerZ * 0.011).

Could you guess where do those constants come from? Could you suggest any resource about edge/silhouette extraction from depth (to be used as input for a filter/displayed for non-photorealistic rendering)?


1 Answer 1


I've also been working on GTAO denoising, and came across the same equation.The function is weighing the neighbouring samples by their depth-difference from the center sample, and also by their slopes. (The higher their slope, the less influence they have)

The final line inverts the weighting, which is required, because as slope and depth differences increase, you want the weight of the sample to decrease. If the slope or depth differences were between 0 and 1, you'd invert it by subtracting it from 1, however as these numbers are essentially unbounded, they are mapped into an 0 to 1 range by inverting, and then adding a small bias of 1.25, then clamping within 0 to 1 with the saturate function.

The other part of the equation is dividing the weights by "centerZ * 0.011." Using a uniform weighting does not work nicely for all distances. Samples close to the camera have much smaller depth deltas and their weights will all be very uniform, whereas samples far away from the camera will be weights that change very quickly, causing aliasing. Dividing the weight by the distance ensures that near and far samples will be affected similarly in screen space. This helps the edge-detection/sharpening appear uniform across different view distances.

For problems like this, it can be handy to convert the constants (1.25, 0.011 etc) to variables, and then playing around with them to see how it affects the image.

It can also be helpful to visualize the equation in desmos or similar, here's a quick example I made for this equation: https://www.desmos.com/calculator/rwsszxin7l

I hope this info helps!


Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.