From ed0e3d7da837e8b4a3bcb393f9d22dfd57831b1f Mon Sep 17 00:00:00 2001 From: Stayd Date: Sun, 15 Dec 2024 06:41:29 -0700 Subject: [PATCH] Scene Shaders - TBN Fixes Fixes two errors related to the normal, tangent, and bitangent vectors, namely normals not always being inverted on backfaces, and normalization being reversed from what MikkTSpace expects. --- drivers/gles3/shaders/scene.glsl | 61 ++++++++-------- .../scene_forward_clustered.glsl | 70 +++++++++---------- .../forward_mobile/scene_forward_mobile.glsl | 56 ++++++++------- 3 files changed, 96 insertions(+), 91 deletions(-) diff --git a/drivers/gles3/shaders/scene.glsl b/drivers/gles3/shaders/scene.glsl index 8f79d8fad51..7574a060e11 100644 --- a/drivers/gles3/shaders/scene.glsl +++ b/drivers/gles3/shaders/scene.glsl @@ -650,13 +650,16 @@ void main() { #endif vertex_interp = vertex; + + // Normalize TBN vectors before interpolation, per MikkTSpace. + // See: http://www.mikktspace.com/ #ifdef NORMAL_USED - normal_interp = normal; + normal_interp = normalize(normal); #endif #if defined(TANGENT_USED) || defined(NORMAL_MAP_USED) || defined(LIGHT_ANISOTROPY_USED) - tangent_interp = tangent; - binormal_interp = binormal; + tangent_interp = normalize(tangent); + binormal_interp = normalize(binormal); #endif // Calculate shadows. @@ -1799,23 +1802,22 @@ void main() { float alpha = 1.0; #if defined(TANGENT_USED) || defined(NORMAL_MAP_USED) || defined(LIGHT_ANISOTROPY_USED) - vec3 binormal = normalize(binormal_interp); - vec3 tangent = normalize(tangent_interp); + vec3 binormal = binormal_interp; + vec3 tangent = tangent_interp; #else vec3 binormal = vec3(0.0); vec3 tangent = vec3(0.0); #endif #ifdef NORMAL_USED - vec3 normal = normalize(normal_interp); - + vec3 normal = normal_interp; #if defined(DO_SIDE_CHECK) if (!gl_FrontFacing) { normal = -normal; } -#endif - -#endif //NORMAL_USED +#endif // DO_SIDE_CHECK + vec3 geo_normal = normalize(normal); +#endif // NORMAL_USED #ifdef UV_USED vec2 uv = uv_interp; @@ -1900,21 +1902,22 @@ void main() { #endif // !USE_SHADOW_TO_OPACITY -#ifdef NORMAL_MAP_USED - +#if defined(NORMAL_MAP_USED) normal_map.xy = normal_map.xy * 2.0 - 1.0; normal_map.z = sqrt(max(0.0, 1.0 - dot(normal_map.xy, normal_map.xy))); //always ignore Z, as it can be RG packed, Z may be pos/neg, etc. + // Tangent-space transformation is performed using unnormalized TBN vectors, per MikkTSpace. + // See: http://www.mikktspace.com/ normal = normalize(mix(normal, tangent * normal_map.x + binormal * normal_map.y + normal * normal_map.z, normal_map_depth)); - -#endif +#elif defined(NORMAL_USED) + normal = geo_normal; +#endif // NORMAL_MAP_USED #ifdef LIGHT_ANISOTROPY_USED if (anisotropy > 0.01) { - //rotation matrix - mat3 rot = mat3(tangent, binormal, normal); - //make local to space + mat3 rot = mat3(normalize(tangent), normalize(binormal), normal); + // Make local to space. tangent = normalize(rot * vec3(anisotropy_flow.x, anisotropy_flow.y, 0.0)); binormal = normalize(rot * vec3(-anisotropy_flow.y, anisotropy_flow.x, 0.0)); } @@ -2146,8 +2149,8 @@ void main() { rim, rim_tint, #endif #ifdef LIGHT_CLEARCOAT_USED - clearcoat, clearcoat_roughness, normalize(normal_interp), -#endif + clearcoat, clearcoat_roughness, geo_normal, +#endif // LIGHT_CLEARCOAT_USED #ifdef LIGHT_ANISOTROPY_USED binormal, tangent, anisotropy, @@ -2172,8 +2175,8 @@ void main() { rim_tint, #endif #ifdef LIGHT_CLEARCOAT_USED - clearcoat, clearcoat_roughness, normalize(normal_interp), -#endif + clearcoat, clearcoat_roughness, geo_normal, +#endif // LIGHT_CLEARCOAT_USED #ifdef LIGHT_ANISOTROPY_USED binormal, tangent, anisotropy, #endif @@ -2196,8 +2199,8 @@ void main() { rim_tint, #endif #ifdef LIGHT_CLEARCOAT_USED - clearcoat, clearcoat_roughness, normalize(normal_interp), -#endif + clearcoat, clearcoat_roughness, geo_normal, +#endif // LIGHT_CLEARCOAT_USED #ifdef LIGHT_ANISOTROPY_USED tangent, binormal, anisotropy, @@ -2448,8 +2451,8 @@ void main() { rim, rim_tint, #endif #ifdef LIGHT_CLEARCOAT_USED - clearcoat, clearcoat_roughness, normalize(normal_interp), -#endif + clearcoat, clearcoat_roughness, geo_normal, +#endif // LIGHT_CLEARCOAT_USED #ifdef LIGHT_ANISOTROPY_USED binormal, tangent, anisotropy, @@ -2481,8 +2484,8 @@ void main() { rim_tint, #endif #ifdef LIGHT_CLEARCOAT_USED - clearcoat, clearcoat_roughness, normalize(normal_interp), -#endif + clearcoat, clearcoat_roughness, geo_normal, +#endif // LIGHT_CLEARCOAT_USED #ifdef LIGHT_ANISOTROPY_USED binormal, tangent, anisotropy, #endif @@ -2511,8 +2514,8 @@ void main() { rim_tint, #endif #ifdef LIGHT_CLEARCOAT_USED - clearcoat, clearcoat_roughness, normalize(normal_interp), -#endif + clearcoat, clearcoat_roughness, geo_normal, +#endif // LIGHT_RIM_USED #ifdef LIGHT_ANISOTROPY_USED tangent, binormal, anisotropy, diff --git a/servers/rendering/renderer_rd/shaders/forward_clustered/scene_forward_clustered.glsl b/servers/rendering/renderer_rd/shaders/forward_clustered/scene_forward_clustered.glsl index c14ea34c8f0..43115e87479 100644 --- a/servers/rendering/renderer_rd/shaders/forward_clustered/scene_forward_clustered.glsl +++ b/servers/rendering/renderer_rd/shaders/forward_clustered/scene_forward_clustered.glsl @@ -457,13 +457,15 @@ void vertex_shader(vec3 vertex_input, vertex_interp = vertex; + // Normalize TBN vectors before interpolation, per MikkTSpace. + // See: http://www.mikktspace.com/ #ifdef NORMAL_USED - normal_interp = normal; + normal_interp = normalize(normal); #endif #ifdef TANGENT_USED - tangent_interp = tangent; - binormal_interp = binormal; + tangent_interp = normalize(tangent); + binormal_interp = normalize(binormal); #endif #ifdef MODE_RENDER_DEPTH @@ -1158,23 +1160,22 @@ void fragment_shader(in SceneData scene_data) { float alpha = float(instances.data[instance_index].flags >> INSTANCE_FLAGS_FADE_SHIFT) / float(255.0); #ifdef TANGENT_USED - vec3 binormal = normalize(binormal_interp); - vec3 tangent = normalize(tangent_interp); + vec3 binormal = binormal_interp; + vec3 tangent = tangent_interp; #else vec3 binormal = vec3(0.0); vec3 tangent = vec3(0.0); #endif #ifdef NORMAL_USED - vec3 normal = normalize(normal_interp); - + vec3 normal = normal_interp; #if defined(DO_SIDE_CHECK) if (!gl_FrontFacing) { normal = -normal; } -#endif - -#endif //NORMAL_USED +#endif // DO_SIDE_CHECK + vec3 geo_normal = normalize(normal); +#endif // NORMAL_USED #ifdef UV_USED vec2 uv = uv_interp; @@ -1292,21 +1293,22 @@ void fragment_shader(in SceneData scene_data) { #endif // !USE_SHADOW_TO_OPACITY -#ifdef NORMAL_MAP_USED - +#if defined(NORMAL_MAP_USED) normal_map.xy = normal_map.xy * 2.0 - 1.0; normal_map.z = sqrt(max(0.0, 1.0 - dot(normal_map.xy, normal_map.xy))); //always ignore Z, as it can be RG packed, Z may be pos/neg, etc. + // Tangent-space transformation is performed using unnormalized TBN vectors, per MikkTSpace. + // See: http://www.mikktspace.com/ normal = normalize(mix(normal, tangent * normal_map.x + binormal * normal_map.y + normal * normal_map.z, normal_map_depth)); - -#endif +#elif defined(NORMAL_USED) + normal = geo_normal; +#endif // NORMAL_MAP_USED #ifdef LIGHT_ANISOTROPY_USED if (anisotropy > 0.01) { - //rotation matrix - mat3 rot = mat3(tangent, binormal, normal); - //make local to space + mat3 rot = mat3(normalize(tangent), normalize(binormal), normal); + // Make local to space. tangent = normalize(rot * vec3(anisotropy_flow.x, anisotropy_flow.y, 0.0)); binormal = normalize(rot * vec3(-anisotropy_flow.y, anisotropy_flow.x, 0.0)); } @@ -1426,7 +1428,7 @@ void fragment_shader(in SceneData scene_data) { float fade = pow(1.0 - (uv_local.y > 0.0 ? uv_local.y : -uv_local.y), uv_local.y > 0.0 ? decals.data[decal_index].upper_fade : decals.data[decal_index].lower_fade); if (decals.data[decal_index].normal_fade > 0.0) { - fade *= smoothstep(decals.data[decal_index].normal_fade, 1.0, dot(normal_interp, decals.data[decal_index].normal) * 0.5 + 0.5); + fade *= smoothstep(decals.data[decal_index].normal_fade, 1.0, dot(geo_normal, decals.data[decal_index].normal) * 0.5 + 0.5); } //we need ddx/ddy for mipmaps, so simulate them @@ -1576,16 +1578,15 @@ void fragment_shader(in SceneData scene_data) { #ifdef LIGHT_CLEARCOAT_USED if (scene_data.use_reflection_cubemap) { - vec3 n = normalize(normal_interp); // We want to use geometric normal, not normal_map - float NoV = max(dot(n, view), 0.0001); - vec3 ref_vec = reflect(-view, n); + float NoV = max(dot(geo_normal, view), 0.0001); // We want to use geometric normal, not normal_map + vec3 ref_vec = reflect(-view, geo_normal); + ref_vec = mix(ref_vec, geo_normal, clearcoat_roughness * clearcoat_roughness); // The clear coat layer assumes an IOR of 1.5 (4% reflectance) float Fc = clearcoat * (0.04 + 0.96 * SchlickFresnel(NoV)); float attenuation = 1.0 - Fc; ambient_light *= attenuation; specular_light *= attenuation; - ref_vec = mix(ref_vec, n, clearcoat_roughness * clearcoat_roughness); float horizon = min(1.0 + dot(ref_vec, normal), 1.0); ref_vec = scene_data.radiance_inverse_xform * ref_vec; float roughness_lod = mix(0.001, 0.1, sqrt(clearcoat_roughness)) * MAX_ROUGHNESS_LOD; @@ -2026,7 +2027,7 @@ void fragment_shader(in SceneData scene_data) { if (directional_lights.data[i].shadow_opacity > 0.001) { float depth_z = -vertex.z; vec3 light_dir = directional_lights.data[i].direction; - vec3 base_normal_bias = normalize(normal_interp) * (1.0 - max(0.0, dot(light_dir, -normalize(normal_interp)))); + vec3 base_normal_bias = geo_normal * (1.0 - max(0.0, dot(light_dir, -geo_normal))); #define BIAS_FUNC(m_var, m_idx) \ m_var.xyz += light_dir * directional_lights.data[i].shadow_bias[m_idx]; \ @@ -2270,7 +2271,7 @@ void fragment_shader(in SceneData scene_data) { float depth_z = -vertex.z; if (depth_z < directional_lights.data[i].shadow_split_offsets.x) { - vec4 trans_vertex = vec4(vertex - normalize(normal_interp) * directional_lights.data[i].shadow_transmittance_bias.x, 1.0); + vec4 trans_vertex = vec4(vertex - geo_normal * directional_lights.data[i].shadow_transmittance_bias.x, 1.0); vec4 trans_coord = directional_lights.data[i].shadow_matrix1 * trans_vertex; trans_coord /= trans_coord.w; @@ -2280,7 +2281,7 @@ void fragment_shader(in SceneData scene_data) { transmittance_z = z - shadow_z; } else if (depth_z < directional_lights.data[i].shadow_split_offsets.y) { - vec4 trans_vertex = vec4(vertex - normalize(normal_interp) * directional_lights.data[i].shadow_transmittance_bias.y, 1.0); + vec4 trans_vertex = vec4(vertex - geo_normal * directional_lights.data[i].shadow_transmittance_bias.y, 1.0); vec4 trans_coord = directional_lights.data[i].shadow_matrix2 * trans_vertex; trans_coord /= trans_coord.w; @@ -2290,7 +2291,7 @@ void fragment_shader(in SceneData scene_data) { transmittance_z = z - shadow_z; } else if (depth_z < directional_lights.data[i].shadow_split_offsets.z) { - vec4 trans_vertex = vec4(vertex - normalize(normal_interp) * directional_lights.data[i].shadow_transmittance_bias.z, 1.0); + vec4 trans_vertex = vec4(vertex - geo_normal * directional_lights.data[i].shadow_transmittance_bias.z, 1.0); vec4 trans_coord = directional_lights.data[i].shadow_matrix3 * trans_vertex; trans_coord /= trans_coord.w; @@ -2299,9 +2300,8 @@ void fragment_shader(in SceneData scene_data) { float z = trans_coord.z * directional_lights.data[i].shadow_z_range.z; transmittance_z = z - shadow_z; - } else { - vec4 trans_vertex = vec4(vertex - normalize(normal_interp) * directional_lights.data[i].shadow_transmittance_bias.w, 1.0); + vec4 trans_vertex = vec4(vertex - geo_normal * directional_lights.data[i].shadow_transmittance_bias.w, 1.0); vec4 trans_coord = directional_lights.data[i].shadow_matrix4 * trans_vertex; trans_coord /= trans_coord.w; @@ -2365,8 +2365,8 @@ void fragment_shader(in SceneData scene_data) { rim, rim_tint, #endif #ifdef LIGHT_CLEARCOAT_USED - clearcoat, clearcoat_roughness, normalize(normal_interp), -#endif + clearcoat, clearcoat_roughness, geo_normal, +#endif // LIGHT_CLEARCOAT_USED #ifdef LIGHT_ANISOTROPY_USED binormal, tangent, anisotropy, @@ -2435,8 +2435,8 @@ void fragment_shader(in SceneData scene_data) { rim_tint, #endif #ifdef LIGHT_CLEARCOAT_USED - clearcoat, clearcoat_roughness, normalize(normal_interp), -#endif + clearcoat, clearcoat_roughness, geo_normal, +#endif // LIGHT_CLEARCOAT_USED #ifdef LIGHT_ANISOTROPY_USED tangent, binormal, anisotropy, #endif @@ -2503,8 +2503,8 @@ void fragment_shader(in SceneData scene_data) { rim_tint, #endif #ifdef LIGHT_CLEARCOAT_USED - clearcoat, clearcoat_roughness, normalize(normal_interp), -#endif + clearcoat, clearcoat_roughness, geo_normal, +#endif // LIGHT_CLEARCOAT_USED #ifdef LIGHT_ANISOTROPY_USED tangent, binormal, anisotropy, @@ -2553,7 +2553,7 @@ void fragment_shader(in SceneData scene_data) { vec3(0, -1, 0), vec3(0, 0, -1)); - vec3 cam_normal = mat3(scene_data.inv_view_matrix) * normalize(normal_interp); + vec3 cam_normal = mat3(scene_data.inv_view_matrix) * geo_normal; float closest_dist = -1e20; diff --git a/servers/rendering/renderer_rd/shaders/forward_mobile/scene_forward_mobile.glsl b/servers/rendering/renderer_rd/shaders/forward_mobile/scene_forward_mobile.glsl index a3c264b8236..800a95c5035 100644 --- a/servers/rendering/renderer_rd/shaders/forward_mobile/scene_forward_mobile.glsl +++ b/servers/rendering/renderer_rd/shaders/forward_mobile/scene_forward_mobile.glsl @@ -433,6 +433,9 @@ void main() { #endif vertex_interp = vertex; + + // Normalize TBN vectors before interpolation, per MikkTSpace. + // See: http://www.mikktspace.com/ #ifdef NORMAL_USED normal_interp = normalize(normal); #endif @@ -881,23 +884,22 @@ void main() { float alpha = 1.0; #if defined(TANGENT_USED) || defined(NORMAL_MAP_USED) || defined(LIGHT_ANISOTROPY_USED) - vec3 binormal = normalize(binormal_interp); - vec3 tangent = normalize(tangent_interp); -#else + vec3 binormal = binormal_interp; + vec3 tangent = tangent_interp; +#else // TANGENT_USED || NORMAL_MAP_USED || LIGHT_ANISOTROPY_USED vec3 binormal = vec3(0.0); vec3 tangent = vec3(0.0); #endif #ifdef NORMAL_USED - vec3 normal = normalize(normal_interp); - + vec3 normal = normal_interp; #if defined(DO_SIDE_CHECK) if (!gl_FrontFacing) { normal = -normal; } -#endif - -#endif //NORMAL_USED +#endif // DO_SIDE_CHECK + vec3 geo_normal = normalize(normal); +#endif // NORMAL_USED #ifdef UV_USED vec2 uv = uv_interp; @@ -1020,21 +1022,22 @@ void main() { #endif // !USE_SHADOW_TO_OPACITY -#ifdef NORMAL_MAP_USED - +#if defined(NORMAL_MAP_USED) normal_map.xy = normal_map.xy * 2.0 - 1.0; normal_map.z = sqrt(max(0.0, 1.0 - dot(normal_map.xy, normal_map.xy))); //always ignore Z, as it can be RG packed, Z may be pos/neg, etc. + // Tangent-space transformation is performed using unnormalized TBN vectors, per MikkTSpace. + // See: http://www.mikktspace.com/ normal = normalize(mix(normal, tangent * normal_map.x + binormal * normal_map.y + normal * normal_map.z, normal_map_depth)); - -#endif +#elif defined(NORMAL_USED) + normal = geo_normal; +#endif // NORMAL_MAP_USED #ifdef LIGHT_ANISOTROPY_USED if (anisotropy > 0.01) { - //rotation matrix - mat3 rot = mat3(tangent, binormal, normal); - //make local to space + mat3 rot = mat3(normalize(tangent), normalize(binormal), normal); + // Make local to space. tangent = normalize(rot * vec3(anisotropy_flow.x, anisotropy_flow.y, 0.0)); binormal = normalize(rot * vec3(-anisotropy_flow.y, anisotropy_flow.x, 0.0)); } @@ -1091,7 +1094,7 @@ void main() { float fade = pow(1.0 - (uv_local.y > 0.0 ? uv_local.y : -uv_local.y), uv_local.y > 0.0 ? decals.data[decal_index].upper_fade : decals.data[decal_index].lower_fade); if (decals.data[decal_index].normal_fade > 0.0) { - fade *= smoothstep(decals.data[decal_index].normal_fade, 1.0, dot(normal_interp, decals.data[decal_index].normal) * 0.5 + 0.5); + fade *= smoothstep(decals.data[decal_index].normal_fade, 1.0, dot(geo_normal, decals.data[decal_index].normal) * 0.5 + 0.5); } //we need ddx/ddy for mipmaps, so simulate them @@ -1238,10 +1241,9 @@ void main() { #ifdef LIGHT_CLEARCOAT_USED if (sc_scene_use_reflection_cubemap()) { - vec3 n = normalize(normal_interp); // We want to use geometric normal, not normal_map - float NoV = max(dot(n, view), 0.0001); - vec3 ref_vec = reflect(-view, n); - ref_vec = mix(ref_vec, n, clearcoat_roughness * clearcoat_roughness); + float NoV = max(dot(geo_normal, view), 0.0001); // We want to use geometric normal, not normal_map + vec3 ref_vec = reflect(-view, geo_normal); + ref_vec = mix(ref_vec, geo_normal, clearcoat_roughness * clearcoat_roughness); // The clear coat layer assumes an IOR of 1.5 (4% reflectance) float Fc = clearcoat * (0.04 + 0.96 * SchlickFresnel(NoV)); float attenuation = 1.0 - Fc; @@ -1481,7 +1483,7 @@ void main() { vec4 pssm_coord; float blur_factor; vec3 light_dir = directional_lights.data[i].direction; - vec3 base_normal_bias = normalize(normal_interp) * (1.0 - max(0.0, dot(light_dir, -normalize(normal_interp)))); + vec3 base_normal_bias = geo_normal * (1.0 - max(0.0, dot(light_dir, -geo_normal))); #define BIAS_FUNC(m_var, m_idx) \ m_var.xyz += light_dir * directional_lights.data[i].shadow_bias[m_idx]; \ @@ -1663,8 +1665,8 @@ void main() { rim, rim_tint, #endif #ifdef LIGHT_CLEARCOAT_USED - clearcoat, clearcoat_roughness, normalize(normal_interp), -#endif + clearcoat, clearcoat_roughness, geo_normal, +#endif // LIGHT_CLEARCOAT_USED #ifdef LIGHT_ANISOTROPY_USED binormal, tangent, anisotropy, #endif @@ -1694,8 +1696,8 @@ void main() { rim_tint, #endif #ifdef LIGHT_CLEARCOAT_USED - clearcoat, clearcoat_roughness, normalize(normal_interp), -#endif + clearcoat, clearcoat_roughness, geo_normal, +#endif // LIGHT_CLEARCOAT_USED #ifdef LIGHT_ANISOTROPY_USED tangent, binormal, anisotropy, @@ -1722,8 +1724,8 @@ void main() { rim_tint, #endif #ifdef LIGHT_CLEARCOAT_USED - clearcoat, clearcoat_roughness, normalize(normal_interp), -#endif + clearcoat, clearcoat_roughness, geo_normal, +#endif // LIGHT_CLEARCOAT_USED #ifdef LIGHT_ANISOTROPY_USED tangent, binormal, anisotropy,