Переглянути джерело

Replace perturbNormal implementation with a more robust version

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.
Arseny Kapoulkine 4 роки тому
батько
коміт
6437c5c59d

+ 10 - 9
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 );
 
 	}