Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix depth sampling for ambient occlusion #12201

Merged
merged 7 commits into from
Sep 20, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGES.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@

- Use first geometryBuffer if no best match found in I3SNode [#12132](https://github.com/CesiumGS/cesium/pull/12132)
- Update type definitions to allow undefined for optional parameters [#12193](https://github.com/CesiumGS/cesium/pull/12193)
- Fixed noise in ambient occlusion post process. [#12201](https://github.com/CesiumGS/cesium/pull/12201)

### 1.121.1 - 2024-09-04

Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
precision highp float;

uniform sampler2D randomTexture;
uniform sampler2D depthTexture;
uniform float intensity;
Expand All @@ -6,109 +8,107 @@ uniform float lengthCap;
uniform float stepSize;
uniform float frustumLength;

in vec2 v_textureCoordinates;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I recall we talked offline that this is probably not the best solution for post processing across the board. If that's correct, could you make sure we have an open issue documenting the suggested for other post processing stages?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

See #12208


vec4 clipToEye(vec2 uv, float depth)
vec3 pixelToEye(vec2 screenCoordinate)
{
vec2 xy = vec2((uv.x * 2.0 - 1.0), ((1.0 - uv.y) * 2.0 - 1.0));
vec2 uv = screenCoordinate / czm_viewport.zw;
float depth = czm_readDepth(depthTexture, uv);
vec2 xy = 2.0 * uv - vec2(1.0);
vec4 posEC = czm_inverseProjection * vec4(xy, depth, 1.0);
posEC = posEC / posEC.w;
return posEC;
return posEC.xyz / posEC.w;
}

//Reconstruct Normal Without Edge Removation
vec3 getNormalXEdge(vec3 posInCamera, float depthU, float depthD, float depthL, float depthR, vec2 pixelSize)
// Reconstruct surface normal in eye coordinates, avoiding edges
vec3 getNormalXEdge(vec3 positionEC)
{
vec4 posInCameraUp = clipToEye(v_textureCoordinates - vec2(0.0, pixelSize.y), depthU);
vec4 posInCameraDown = clipToEye(v_textureCoordinates + vec2(0.0, pixelSize.y), depthD);
vec4 posInCameraLeft = clipToEye(v_textureCoordinates - vec2(pixelSize.x, 0.0), depthL);
vec4 posInCameraRight = clipToEye(v_textureCoordinates + vec2(pixelSize.x, 0.0), depthR);

vec3 up = posInCamera.xyz - posInCameraUp.xyz;
vec3 down = posInCameraDown.xyz - posInCamera.xyz;
vec3 left = posInCamera.xyz - posInCameraLeft.xyz;
vec3 right = posInCameraRight.xyz - posInCamera.xyz;

vec3 DX = length(left) < length(right) ? left : right;
vec3 DY = length(up) < length(down) ? up : down;

return normalize(cross(DY, DX));
// Find the 3D surface positions at adjacent screen pixels
vec2 centerCoord = gl_FragCoord.xy;
vec3 positionLeft = pixelToEye(centerCoord + vec2(-1.0, 0.0));
vec3 positionRight = pixelToEye(centerCoord + vec2(1.0, 0.0));
vec3 positionUp = pixelToEye(centerCoord + vec2(0.0, 1.0));
vec3 positionDown = pixelToEye(centerCoord + vec2(0.0, -1.0));

// Compute potential tangent vectors
vec3 dx0 = positionEC - positionLeft;
vec3 dx1 = positionRight - positionEC;
vec3 dy0 = positionEC - positionDown;
vec3 dy1 = positionUp - positionEC;

// The shorter tangent is more likely to be on the same surface
vec3 dx = length(dx0) < length(dx1) ? dx0 : dx1;
vec3 dy = length(dy0) < length(dy1) ? dy0 : dy1;

return normalize(cross(dx, dy));
}

void main(void)
{
float depth = czm_readDepth(depthTexture, v_textureCoordinates);
vec4 posInCamera = clipToEye(v_textureCoordinates, depth);
vec3 positionEC = pixelToEye(gl_FragCoord.xy);

if (posInCamera.z > frustumLength)
if (positionEC.z > frustumLength)
{
out_FragColor = vec4(1.0);
return;
}

vec2 pixelSize = czm_pixelRatio / czm_viewport.zw;
float depthU = czm_readDepth(depthTexture, v_textureCoordinates - vec2(0.0, pixelSize.y));
float depthD = czm_readDepth(depthTexture, v_textureCoordinates + vec2(0.0, pixelSize.y));
float depthL = czm_readDepth(depthTexture, v_textureCoordinates - vec2(pixelSize.x, 0.0));
float depthR = czm_readDepth(depthTexture, v_textureCoordinates + vec2(pixelSize.x, 0.0));
vec3 normalInCamera = getNormalXEdge(posInCamera.xyz, depthU, depthD, depthL, depthR, pixelSize);
vec3 normalEC = getNormalXEdge(positionEC);

float ao = 0.0;
vec2 sampleDirection = vec2(1.0, 0.0);
float gapAngle = 90.0 * czm_radiansPerDegree;

// RandomNoise
float randomVal = texture(randomTexture, v_textureCoordinates / pixelSize / 255.0).x;

//Loop for each direction
for (int i = 0; i < 4; i++)
const int ANGLE_STEPS = 4;
float angleStepScale = 1.0 / float(ANGLE_STEPS);
float angleStep = angleStepScale * czm_twoPi;
float cosStep = cos(angleStep);
float sinStep = sin(angleStep);
mat2 rotateStep = mat2(cosStep, sinStep, -sinStep, cosStep);

// Initial sampling direction (different for each pixel)
const float randomTextureSize = 255.0;
vec2 randomTexCoord = fract(gl_FragCoord.xy / randomTextureSize);
float randomVal = texture(randomTexture, randomTexCoord).x;
vec2 sampleDirection = vec2(cos(angleStep * randomVal), sin(angleStep * randomVal));

// Loop over sampling directions
for (int i = 0; i < ANGLE_STEPS; i++)
{
float newGapAngle = gapAngle * (float(i) + randomVal);
float cosVal = cos(newGapAngle);
float sinVal = sin(newGapAngle);
sampleDirection = rotateStep * sampleDirection;

//Rotate Sampling Direction
vec2 rotatedSampleDirection = vec2(cosVal * sampleDirection.x - sinVal * sampleDirection.y, sinVal * sampleDirection.x + cosVal * sampleDirection.y);
float localAO = 0.0;
float localStepSize = stepSize;
vec2 radialStep = stepSize * sampleDirection;

//Loop for each step
for (int j = 0; j < 6; j++)
{
vec2 newCoords = v_textureCoordinates + rotatedSampleDirection * localStepSize * pixelSize;
// Step along sampling direction, away from output pixel
vec2 newCoords = floor(gl_FragCoord.xy + float(j + 1) * radialStep) + vec2(0.5);

//Exception Handling
if(newCoords.x > 1.0 || newCoords.y > 1.0 || newCoords.x < 0.0 || newCoords.y < 0.0)
// Exit if we stepped off the screen
if (clamp(newCoords, vec2(0.0), czm_viewport.zw) != newCoords)
{
break;
}

float stepDepthInfo = czm_readDepth(depthTexture, newCoords);
vec4 stepPosInCamera = clipToEye(newCoords, stepDepthInfo);
vec3 diffVec = stepPosInCamera.xyz - posInCamera.xyz;
float len = length(diffVec);
vec3 stepPositionEC = pixelToEye(newCoords);
vec3 stepVector = stepPositionEC - positionEC;
float stepLength = length(stepVector);

if (len > lengthCap)
if (stepLength > lengthCap)
{
break;
}

float dotVal = clamp(dot(normalInCamera, normalize(diffVec)), 0.0, 1.0 );
float weight = len / lengthCap;
weight = 1.0 - weight * weight;

float dotVal = clamp(dot(normalEC, normalize(stepVector)), 0.0, 1.0);
if (dotVal < bias)
{
dotVal = 0.0;
}

float weight = stepLength / lengthCap;
weight = 1.0 - weight * weight;
localAO = max(localAO, dotVal * weight);
localStepSize += stepSize;
}
ao += localAO;
}

ao /= 4.0;
ao *= angleStepScale;
ao = 1.0 - clamp(ao, 0.0, 1.0);
ao = pow(ao, intensity);
out_FragColor = vec4(vec3(ao), 1.0);
Expand Down