ssao: implement accurate normal reconstruction

This removes "false AO" effect on geometry edges.
Currently this is enabled on desktop only (quality == high).
This commit is contained in:
Mathias Agopian
2021-04-27 16:33:04 -07:00
committed by Mathias Agopian
parent f5adbbddb8
commit dc56408d5f
3 changed files with 54 additions and 14 deletions

View File

@@ -189,14 +189,47 @@ fragment {
// are essentially equivalent to textureGather (which we don't have on ES3.0),
// and this is executed just once.
highp vec3 computeViewSpaceNormal(const highp vec3 position, const highp vec2 uv) {
precision highp float;
highp vec2 uvdx = uv + vec2(materialParams.resolution.z, 0.0);
highp vec2 uvdy = uv + vec2(0.0, materialParams.resolution.w);
highp vec3 px = computeViewSpacePositionFromDepth(uvdx,
vec3 px = computeViewSpacePositionFromDepth(uvdx,
sampleDepthLinear(materialParams_depth, uvdx, 0.0, materialParams.depthParams));
highp vec3 py = computeViewSpacePositionFromDepth(uvdy,
vec3 py = computeViewSpacePositionFromDepth(uvdy,
sampleDepthLinear(materialParams_depth, uvdy, 0.0, materialParams.depthParams));
highp vec3 dpdx = px - position;
highp vec3 dpdy = py - position;
vec3 dpdx = px - position;
vec3 dpdy = py - position;
return faceNormal(dpdx, dpdy);
}
// Accurate view-space normal reconstruction
// Based on Yuwen Wu "Accurate Normal Reconstruction"
// (https://atyuwen.github.io/posts/normal-reconstruction)
highp vec3 computeViewSpaceNormalAccurate(const highp vec3 position, const highp vec2 uv) {
precision highp float;
vec3 pos_c = position;
highp vec2 dx = vec2(materialParams.resolution.z, 0.0);
highp vec2 dy = vec2(0.0, materialParams.resolution.w);
vec4 H;
H.x = sampleDepth(materialParams_depth, uv - dx, 0.0);
H.y = sampleDepth(materialParams_depth, uv + dx, 0.0);
H.z = sampleDepth(materialParams_depth, uv - dx * 2.0, 0.0);
H.w = sampleDepth(materialParams_depth, uv + dx * 2.0, 0.0);
vec2 he = abs((2.0 * H.xy - H.zw) - pos_c.z);
vec3 pos_l = computeViewSpacePositionFromDepth(uv - dx, linearizeDepth(H.x, materialParams.depthParams));
vec3 pos_r = computeViewSpacePositionFromDepth(uv + dx, linearizeDepth(H.y, materialParams.depthParams));
vec3 dpdx = (he.x < he.y) ? (pos_c - pos_l) : (pos_r - pos_c);
vec4 V;
V.x = sampleDepth(materialParams_depth, uv - dy, 0.0);
V.y = sampleDepth(materialParams_depth, uv + dy, 0.0);
V.z = sampleDepth(materialParams_depth, uv - dy * 2.0, 0.0);
V.w = sampleDepth(materialParams_depth, uv + dy * 2.0, 0.0);
vec2 ve = abs((2.0 * V.xy - V.zw) - pos_c.z);
vec3 pos_d = computeViewSpacePositionFromDepth(uv - dy, linearizeDepth(V.x, materialParams.depthParams));
vec3 pos_u = computeViewSpacePositionFromDepth(uv + dy, linearizeDepth(V.y, materialParams.depthParams));
vec3 dpdy = (ve.x < ve.y) ? (pos_c - pos_d) : (pos_u - pos_c);
return faceNormal(dpdx, dpdy);
}
@@ -314,7 +347,13 @@ fragment {
highp float depth = sampleDepthLinear(materialParams_depth, uv, 0.0, materialParams.depthParams);
highp vec3 origin = computeViewSpacePositionFromDepth(uv, depth);
vec3 normal = computeViewSpaceNormal(origin, uv);
// todo: maybe make this a quality parameter of SSAO
#if FILAMENT_QUALITY == FILAMENT_QUALITY_HIGH
vec3 normal = computeViewSpaceNormalAccurate(origin, uv);
#else
vec3 normal = computeViewSpaceNormal(origin, uv);
#endif
float occlusion = 0.0;

View File

@@ -9,15 +9,19 @@ highp float linearizeDepth(highp float depth, highp float depthParams) {
return depthParams / max(depth, preventDiv0);
}
highp float sampleDepthLinear(const highp sampler2D depthTexture, const highp vec2 uv, float lod, highp float depthParams) {
highp float sampleDepth(const highp sampler2D depthTexture, const highp vec2 uv, float lod) {
#if defined(TARGET_METAL_ENVIRONMENT) || defined(TARGET_VULKAN_ENVIRONMENT)
// On metal/vulkan, texture space is flipped vertically and we need to adjust the uv
// coordinates.
return linearizeDepth(textureLod(depthTexture, vec2(uv.x, 1.0 - uv.y), lod).r, depthParams);
return textureLod(depthTexture, vec2(uv.x, 1.0 - uv.y), lod).r;
#else
return linearizeDepth(textureLod(depthTexture, uv, lod).r, depthParams);
return textureLod(depthTexture, uv, lod).r;
#endif
}
highp float sampleDepthLinear(const highp sampler2D depthTexture, const highp vec2 uv, float lod, highp float depthParams) {
return linearizeDepth(sampleDepth(depthTexture, uv, lod), depthParams);
}
#endif // MATERIALS_SSAO_UTILS

View File

@@ -28,13 +28,10 @@ float evaluateSSAO() {
// Read four AO samples and their depths values
#if defined(FILAMENT_HAS_FEATURE_TEXTURE_GATHER)
vec4 ao = textureGather(light_ssao, uv, 0); // 01, 11, 10, 00
vec4 dg = textureGather(light_ssao, uv, 1); // 01, 11, 10, 00
vec4 db = textureGather(light_ssao, uv, 2); // 01, 11, 10, 00
vec4 ao = textureGather(light_ssao, uv, 0);
vec4 dg = textureGather(light_ssao, uv, 1);
vec4 db = textureGather(light_ssao, uv, 2);
#else
// on mobile we can't use textureGather() because we're limited to ES3.0,
// so we emulate it with texelFetch(), on Pixel 4 this doesn't seem to have any
// significant impact on performance.
vec3 s01 = textureLodOffset(light_ssao, uv, 0.0, ivec2(0, 1)).rgb;
vec3 s11 = textureLodOffset(light_ssao, uv, 0.0, ivec2(1, 1)).rgb;
vec3 s10 = textureLodOffset(light_ssao, uv, 0.0, ivec2(1, 0)).rgb;