From 6437c5c59dfc1a21ea5ea21979c19fe13512a933 Mon Sep 17 00:00:00 2001 From: Arseny Kapoulkine Date: Wed, 17 Feb 2021 21:48:28 -0800 Subject: [PATCH 1/2] Replace perturbNormal implementation with a more robust version MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This change switches to a slightly different formulation using a cotangent frame described by Christian Schüler in "Normal Mapping Without Precomputed Tangents" follow-up blog post. This implementation is nicer as it has fewer opportunities to produce a NaN output given a degenerate input; it contains one division and one normalize at the end, and only division needs to be guarded against. As a result, when the UV mapping is degenerate within a given triangle, the resulting determinant is 0 and scale is set to 0 as well. Using Mali Offline Shader Compiler on a simple shader that samples a normal map and converts it to object space using this function, the resulting code is also slightly faster than before - 20.5 cycles vs 22.3 cycles. The resulting performance on a complete three.js shader is likely to be unaffected. --- .../normalmap_pars_fragment.glsl.js | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/src/renderers/shaders/ShaderChunk/normalmap_pars_fragment.glsl.js b/src/renderers/shaders/ShaderChunk/normalmap_pars_fragment.glsl.js index d3c3d19a66eaa8..15ec0edb096cef 100644 --- a/src/renderers/shaders/ShaderChunk/normalmap_pars_fragment.glsl.js +++ b/src/renderers/shaders/ShaderChunk/normalmap_pars_fragment.glsl.js @@ -14,8 +14,8 @@ export default /* glsl */` #if ! defined ( USE_TANGENT ) && ( defined ( TANGENTSPACE_NORMALMAP ) || defined ( USE_CLEARCOAT_NORMALMAP ) ) - // Per-Pixel Tangent Space Normal Mapping - // http://hacksoflife.blogspot.ch/2009/11/per-pixel-tangent-space-normal-mapping.html + // Normal Mapping Without Precomputed Tangents + // http://www.thetenthplanet.de/archives/1180 vec3 perturbNormal2Arb( vec3 eye_pos, vec3 surf_norm, vec3 mapN, float faceDirection ) { @@ -26,17 +26,18 @@ export default /* glsl */` vec2 st0 = dFdx( vUv.st ); vec2 st1 = dFdy( vUv.st ); - float scale = sign( st1.t * st0.s - st0.t * st1.s ); // we do not care about the magnitude + vec3 N = surf_norm; // normalized - vec3 S = normalize( ( q0 * st1.t - q1 * st0.t ) * scale ); - vec3 T = normalize( ( - q0 * st1.s + q1 * st0.s ) * scale ); - vec3 N = normalize( surf_norm ); + vec3 q1perp = cross( q1, N ); + vec3 q0perp = cross( N, q0 ); - mat3 tsn = mat3( S, T, N ); + vec3 T = q1perp * st0.x + q0perp * st1.x; + vec3 B = q1perp * st0.y + q0perp * st1.y; - mapN.xy *= faceDirection; + float det = max( dot(T,T), dot(B,B) ); + float scale = (det == 0.0) ? 0.0 : faceDirection * inversesqrt( det ); - return normalize( tsn * mapN ); + return normalize( T * (mapN.x * scale) + B * ( mapN.y * scale ) + N * mapN.z ); } From ab7458e32694ee1c3f7695559db0781d87bd36ec Mon Sep 17 00:00:00 2001 From: Arseny Kapoulkine Date: Wed, 17 Feb 2021 22:14:53 -0800 Subject: [PATCH 2/2] Fix code style in the shader --- .../shaders/ShaderChunk/normalmap_pars_fragment.glsl.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/renderers/shaders/ShaderChunk/normalmap_pars_fragment.glsl.js b/src/renderers/shaders/ShaderChunk/normalmap_pars_fragment.glsl.js index 15ec0edb096cef..24f84589d78d41 100644 --- a/src/renderers/shaders/ShaderChunk/normalmap_pars_fragment.glsl.js +++ b/src/renderers/shaders/ShaderChunk/normalmap_pars_fragment.glsl.js @@ -34,10 +34,10 @@ export default /* glsl */` vec3 T = q1perp * st0.x + q0perp * st1.x; vec3 B = q1perp * st0.y + q0perp * st1.y; - float det = max( dot(T,T), dot(B,B) ); - float scale = (det == 0.0) ? 0.0 : faceDirection * inversesqrt( det ); + float det = max( dot( T, T ), dot( B, B ) ); + float scale = ( det == 0.0 ) ? 0.0 : faceDirection * inversesqrt( det ); - return normalize( T * (mapN.x * scale) + B * ( mapN.y * scale ) + N * mapN.z ); + return normalize( T * ( mapN.x * scale ) + B * ( mapN.y * scale ) + N * mapN.z ); }