diff --git a/src/scene/shader-lib/chunks-wgsl/chunks-wgsl.js b/src/scene/shader-lib/chunks-wgsl/chunks-wgsl.js index 419db4a0148..64389782cc4 100644 --- a/src/scene/shader-lib/chunks-wgsl/chunks-wgsl.js +++ b/src/scene/shader-lib/chunks-wgsl/chunks-wgsl.js @@ -177,8 +177,6 @@ import sphericalPS from './common/frag/spherical.js'; // import startNineSlicedTiledPS from './lit/frag/startNineSlicedTiled.js'; // import tangentBinormalVS from './lit/vert/tangentBinormal.js'; // import TBNPS from './lit/frag/TBN.js'; -// import TBNderivativePS from './lit/frag/TBNderivative.js'; -// import TBNObjectSpacePS from './lit/frag/TBNObjectSpace.js'; // import thicknessPS from './standard/frag/thickness.js'; import tonemappingPS from './common/frag/tonemapping/tonemapping.js'; import tonemappingAcesPS from './common/frag/tonemapping/tonemappingAces.js'; @@ -388,8 +386,6 @@ const shaderChunksWGSL = { // startNineSlicedTiledPS, // tangentBinormalVS, // TBNPS, - // TBNderivativePS, - // TBNObjectSpacePS, // thicknessPS, tonemappingPS, tonemappingAcesPS, diff --git a/src/scene/shader-lib/chunks/chunk-validation.js b/src/scene/shader-lib/chunks/chunk-validation.js index 48825fa1ba1..886d4a77b69 100644 --- a/src/scene/shader-lib/chunks/chunk-validation.js +++ b/src/scene/shader-lib/chunks/chunk-validation.js @@ -65,8 +65,6 @@ const chunkVersions = { shadowEVSMPS: CHUNKAPI_1_62, spotPS: CHUNKAPI_1_62, TBNPS: CHUNKAPI_1_62, - TBNObjectSpacePS: CHUNKAPI_1_62, - TBNderivativePS: CHUNKAPI_1_62, endPS: CHUNKAPI_1_65, metalnessModulatePS: CHUNKAPI_1_65, @@ -120,7 +118,9 @@ const removedChunks = { baseVS: CHUNKAPI_2_6, baseNineSlicedVS: CHUNKAPI_2_6, viewNormalVS: CHUNKAPI_2_6, - lightmapDirAddPS: CHUNKAPI_2_6 + lightmapDirAddPS: CHUNKAPI_2_6, + TBNObjectSpacePS: CHUNKAPI_2_6, + TBNderivativePS: CHUNKAPI_2_6 }; // compare two "major.minor" semantic version strings and return true if a is a smaller version than b. diff --git a/src/scene/shader-lib/chunks/chunks.js b/src/scene/shader-lib/chunks/chunks.js index 236c61a1f31..f1fec456d3f 100644 --- a/src/scene/shader-lib/chunks/chunks.js +++ b/src/scene/shader-lib/chunks/chunks.js @@ -177,8 +177,6 @@ import startNineSlicedPS from './lit/frag/startNineSliced.js'; import startNineSlicedTiledPS from './lit/frag/startNineSlicedTiled.js'; import tangentBinormalVS from './lit/vert/tangentBinormal.js'; import TBNPS from './lit/frag/TBN.js'; -import TBNderivativePS from './lit/frag/TBNderivative.js'; -import TBNObjectSpacePS from './lit/frag/TBNObjectSpace.js'; import thicknessPS from './standard/frag/thickness.js'; import tonemappingPS from './common/frag/tonemapping/tonemapping.js'; import tonemappingAcesPS from './common/frag/tonemapping/tonemappingAces.js'; @@ -388,8 +386,6 @@ const shaderChunks = { startNineSlicedTiledPS, tangentBinormalVS, TBNPS, - TBNderivativePS, - TBNObjectSpacePS, thicknessPS, tonemappingPS, tonemappingAcesPS, diff --git a/src/scene/shader-lib/chunks/lit/frag/TBN.js b/src/scene/shader-lib/chunks/lit/frag/TBN.js index 8ac6a907f0f..0e86c553d3d 100644 --- a/src/scene/shader-lib/chunks/lit/frag/TBN.js +++ b/src/scene/shader-lib/chunks/lit/frag/TBN.js @@ -1,5 +1,72 @@ export default /* glsl */` + +#ifdef LIT_TANGENTS + #define TBN_TANGENTS +#else + #if defined(LIT_USE_NORMALS) || defined(LIT_USE_CLEARCOAT_NORMALS) + #define TBN_DERIVATIVES + #endif +#endif + +#if defined(TBN_DERIVATIVES) + uniform float tbnBasis; +#endif + void getTBN(vec3 tangent, vec3 binormal, vec3 normal) { - dTBN = mat3(normalize(tangent), normalize(binormal), normalize(normal)); + + #ifdef TBN_TANGENTS // tangents / binormals based TBN + + dTBN = mat3(normalize(tangent), normalize(binormal), normalize(normal)); + + #elif defined(TBN_DERIVATIVES) // derivatives based TBN + + vec2 uv = {lightingUv}; + + // get edge vectors of the pixel triangle + vec3 dp1 = dFdx( vPositionW ); + vec3 dp2 = dFdy( vPositionW ); + vec2 duv1 = dFdx( uv ); + vec2 duv2 = dFdy( uv ); + + // solve the linear system + vec3 dp2perp = cross( dp2, normal ); + vec3 dp1perp = cross( normal, dp1 ); + vec3 T = dp2perp * duv1.x + dp1perp * duv2.x; + vec3 B = dp2perp * duv1.y + dp1perp * duv2.y; + + // construct a scale-invariant frame + float denom = max( dot(T,T), dot(B,B) ); + float invmax = (denom == 0.0) ? 0.0 : tbnBasis / sqrt( denom ); + dTBN = mat3(T * invmax, -B * invmax, normal ); + + #else // object space TBN + + vec3 B = cross(normal, vObjectSpaceUpW); + vec3 T = cross(normal, B); + + if (dot(B,B)==0.0) // deal with case when vObjectSpaceUpW normal are parallel + { + float major=max(max(normal.x, normal.y), normal.z); + + if (normal.x == major) + { + B = cross(normal, vec3(0,1,0)); + T = cross(normal, B); + } + else if (normal.y == major) + { + B = cross(normal, vec3(0,0,1)); + T = cross(normal, B); + } + else if (normal.z == major) + { + B = cross(normal, vec3(1,0,0)); + T = cross(normal, B); + } + } + + dTBN = mat3(normalize(T), normalize(B), normalize(normal)); + + #endif } `; diff --git a/src/scene/shader-lib/chunks/lit/frag/TBNObjectSpace.js b/src/scene/shader-lib/chunks/lit/frag/TBNObjectSpace.js deleted file mode 100644 index 29cf15f3c47..00000000000 --- a/src/scene/shader-lib/chunks/lit/frag/TBNObjectSpace.js +++ /dev/null @@ -1,30 +0,0 @@ -export default /* glsl */` -void getTBN(vec3 tangent, vec3 binormal, vec3 normal) { - - vec3 B = cross(normal, vObjectSpaceUpW); - vec3 T = cross(normal, B); - - if (dot(B,B)==0.0) // deal with case when vObjectSpaceUpW normal are parallel - { - float major=max(max(normal.x, normal.y), normal.z); - - if (normal.x == major) - { - B=cross(normal, vec3(0,1,0)); - T=cross(normal, B); - } - else if (normal.y == major) - { - B=cross(normal, vec3(0,0,1)); - T=cross(normal, B); - } - else if (normal.z == major) - { - B=cross(normal, vec3(1,0,0)); - T=cross(normal, B); - } - } - - dTBN = mat3(normalize(T), normalize(B), normalize(normal)); -} -`; diff --git a/src/scene/shader-lib/chunks/lit/frag/TBNderivative.js b/src/scene/shader-lib/chunks/lit/frag/TBNderivative.js deleted file mode 100644 index f7971990f01..00000000000 --- a/src/scene/shader-lib/chunks/lit/frag/TBNderivative.js +++ /dev/null @@ -1,25 +0,0 @@ -export default /* glsl */` -uniform float tbnBasis; - -// http://www.thetenthplanet.de/archives/1180 -void getTBN(vec3 tangent, vec3 binormal, vec3 normal) { - vec2 uv = $UV; - - // get edge vectors of the pixel triangle - vec3 dp1 = dFdx( vPositionW ); - vec3 dp2 = dFdy( vPositionW ); - vec2 duv1 = dFdx( uv ); - vec2 duv2 = dFdy( uv ); - - // solve the linear system - vec3 dp2perp = cross( dp2, normal ); - vec3 dp1perp = cross( normal, dp1 ); - vec3 T = dp2perp * duv1.x + dp1perp * duv2.x; - vec3 B = dp2perp * duv1.y + dp1perp * duv2.y; - - // construct a scale-invariant frame - float denom = max( dot(T,T), dot(B,B) ); - float invmax = (denom == 0.0) ? 0.0 : tbnBasis / sqrt( denom ); - dTBN = mat3(T * invmax, -B * invmax, normal ); -} -`; diff --git a/src/scene/shader-lib/programs/lit-shader.js b/src/scene/shader-lib/programs/lit-shader.js index 7f7adb3dc87..3864e168246 100644 --- a/src/scene/shader-lib/programs/lit-shader.js +++ b/src/scene/shader-lib/programs/lit-shader.js @@ -505,6 +505,8 @@ class LitShader { const backend = new ChunkBuilder(); const code = new ChunkBuilder(); + const hasTBN = this.needsNormal && (options.useNormals || options.useClearCoatNormals || (options.enableGGXSpecular && !options.useHeights)); + if (options.useSpecular) { this.fDefineSet(true, 'LIT_SPECULAR'); this.fDefineSet(this.reflections, 'LIT_REFLECTIONS'); @@ -522,6 +524,18 @@ class LitShader { this.fDefineSet(options.twoSidedLighting, 'LIT_TWO_SIDED_LIGHTING'); this.fDefineSet(options.lightMapEnabled, 'LIT_LIGHTMAP'); this.fDefineSet(options.dirLightMapEnabled, 'LIT_DIR_LIGHTMAP'); + this.fDefineSet(hasTBN, 'LIT_TBN'); + this.fDefineSet(options.hasTangents, 'LIT_TANGENTS'); + this.fDefineSet(options.useNormals, 'LIT_USE_NORMALS'); + this.fDefineSet(options.useClearCoatNormals, 'LIT_USE_CLEARCOAT_NORMALS'); + + // injection defines + this.fDefineSet(true, '{lightingUv}', this.lightingUv ?? ''); // example: vUV0_1 + + // globals + decl.append(` + mat3 dTBN; + `); // FRAGMENT SHADER INPUTS: UNIFORMS @@ -555,33 +569,23 @@ class LitShader { #include "lightingPS" `); - // TBN - const hasTBN = this.needsNormal && (options.useNormals || options.useClearCoatNormals || (options.enableGGXSpecular && !options.useHeights)); + func.append(` - if (hasTBN) { - if (options.hasTangents) { - func.append(chunks.TBNPS); - } else { - if (options.useNormals || options.useClearCoatNormals) { - func.append(chunks.TBNderivativePS.replace(/\$UV/g, this.lightingUv)); - } else { - func.append(chunks.TBNObjectSpacePS); - } - } + // TBN + #ifdef LIT_TBN + #include "TBNPS" - func.append(` #ifdef LIT_TWO_SIDED_LIGHTING #include "twoSidedLightingPS" #endif - `); - } + #endif - func.append(` #include "sphericalPS" #include "decodePS" #include "gammaPS" #include "tonemappingPS" #include "fogPS" + `); // frontend @@ -800,16 +804,17 @@ class LitShader { code.append(' dBinormalW = vBinormalW;'); } - code.append(' getViewDir();'); - if (hasTBN) { - code.append(` + code.append(` + getViewDir(); + + #ifdef LIT_TBN getTBN(dTangentW, dBinormalW, dVertexNormalW); #ifdef LIT_TWO_SIDED_LIGHTING handleTwoSidedLighting(); #endif - `); - } + #endif + `); } // invoke frontend functions @@ -1354,7 +1359,6 @@ class LitShader { const mergedCode = decl.code + func.code + code.code; // Light inputs - if (mergedCode.includes('dTBN')) structCode += 'mat3 dTBN;\n'; if (mergedCode.includes('dVertexNormalW')) structCode += 'vec3 dVertexNormalW;\n'; if (mergedCode.includes('dTangentW')) structCode += 'vec3 dTangentW;\n'; if (mergedCode.includes('dBinormalW')) structCode += 'vec3 dBinormalW;\n';