From 2c2b24db37e22d26d2adc8798b0c37bb8c238212 Mon Sep 17 00:00:00 2001 From: Martin Valigursky <59932779+mvaligursky@users.noreply.github.com> Date: Wed, 26 Feb 2025 16:24:35 +0000 Subject: [PATCH] Lit/Standard material vertex shader is now a chunk driven by defines (#7381) Co-authored-by: Martin Valigursky --- src/core/preprocessor.js | 2 +- src/framework/lightmapper/lightmapper.js | 3 +- .../shader-lib/chunks-wgsl/chunks-wgsl.js | 16 +- .../shader-lib/chunks/chunk-validation.js | 7 +- src/scene/shader-lib/chunks/chunks.js | 16 +- src/scene/shader-lib/chunks/lit/vert/base.js | 11 - .../chunks/lit/vert/baseNineSliced.js | 10 - src/scene/shader-lib/chunks/lit/vert/end.js | 2 - .../shader-lib/chunks/lit/vert/litMain.js | 104 ++++++++ src/scene/shader-lib/chunks/lit/vert/start.js | 4 - .../shader-lib/chunks/lit/vert/uvTransform.js | 7 + .../chunks/lit/vert/uvTransformUniforms.js | 5 + src/scene/shader-lib/programs/lit-shader.js | 244 ++++++++---------- src/scene/shader-lib/programs/lit.js | 6 +- src/scene/shader-lib/programs/standard.js | 58 +++-- 15 files changed, 274 insertions(+), 221 deletions(-) delete mode 100644 src/scene/shader-lib/chunks/lit/vert/base.js delete mode 100644 src/scene/shader-lib/chunks/lit/vert/baseNineSliced.js delete mode 100644 src/scene/shader-lib/chunks/lit/vert/end.js create mode 100644 src/scene/shader-lib/chunks/lit/vert/litMain.js delete mode 100644 src/scene/shader-lib/chunks/lit/vert/start.js create mode 100644 src/scene/shader-lib/chunks/lit/vert/uvTransform.js create mode 100644 src/scene/shader-lib/chunks/lit/vert/uvTransformUniforms.js diff --git a/src/core/preprocessor.js b/src/core/preprocessor.js index 37b32adb0a3..1315650907a 100644 --- a/src/core/preprocessor.js +++ b/src/core/preprocessor.js @@ -152,7 +152,7 @@ class Preprocessor { // replace all instances of the injected defines with the value itself const lines = source.split('\n'); injectDefines.forEach((value, key) => { - const regex = new RegExp(`\\b${key}\\b`, 'g'); + const regex = new RegExp(key, 'g'); for (let i = 0; i < lines.length; i++) { // replace them on lines that do not contain a preprocessor directive (the define itself for example) diff --git a/src/framework/lightmapper/lightmapper.js b/src/framework/lightmapper/lightmapper.js index 96e806ca6a5..6ff31499fbd 100644 --- a/src/framework/lightmapper/lightmapper.js +++ b/src/framework/lightmapper/lightmapper.js @@ -236,8 +236,7 @@ class Lightmapper { const material = new StandardMaterial(); material.name = `lmMaterial-pass:${pass}-ambient:${addAmbient}`; material.chunks.APIVersion = CHUNKAPI_1_65; - const transformDefines = '#define UV1LAYOUT\n'; - material.chunks.transformVS = transformDefines + shaderChunks.transformVS; // draw into UV1 texture space + material.setDefine('UV1LAYOUT', ''); // draw into UV1 texture space if (pass === PASS_COLOR) { let bakeLmEndChunk = shaderChunksLightmapper.bakeLmEndPS; // encode to RGBM diff --git a/src/scene/shader-lib/chunks-wgsl/chunks-wgsl.js b/src/scene/shader-lib/chunks-wgsl/chunks-wgsl.js index 878360fa17e..5b7eb59b185 100644 --- a/src/scene/shader-lib/chunks-wgsl/chunks-wgsl.js +++ b/src/scene/shader-lib/chunks-wgsl/chunks-wgsl.js @@ -10,9 +10,7 @@ // import aoSpecOccConstSimplePS from './lit/frag/aoSpecOccConstSimple.js'; // import aoSpecOccSimplePS from './lit/frag/aoSpecOccSimple.js'; // import basePS from './lit/frag/base.js'; -// import baseVS from './lit/vert/base.js'; // import baseNineSlicedPS from './lit/frag/baseNineSliced.js'; -// import baseNineSlicedVS from './lit/vert/baseNineSliced.js'; // import baseNineSlicedTiledPS from './lit/frag/baseNineSlicedTiled.js'; // import bayerPS from './common/frag/bayer.js'; // import blurVSMPS from './lit/frag/blurVSM.js'; @@ -37,7 +35,6 @@ import decodePS from './common/frag/decode.js'; // import emissivePS from './standard/frag/emissive.js'; // import encodePS from './common/frag/encode.js'; // import endPS from './lit/frag/end.js'; -// import endVS from './lit/vert/end.js'; import envAtlasPS from './common/frag/envAtlas.js'; // import envConstPS from './common/frag/envConst.js'; import envMultiplyPS from './common/frag/envMultiply.js'; @@ -83,6 +80,7 @@ import immediateLineVS from './internal/vert/immediateLine.js'; // import lightSpecularBlinnPS from './lit/frag/lightSpecularBlinn.js'; // import lightSheenPS from './lit/frag/lightSheen.js'; // import linearizeDepthPS from './common/frag/linearizeDepth.js'; +// import litMainVS from './lit/vert/litMain.js'; // import litShaderArgsPS from './standard/frag/litShaderArgs.js'; // import ltcPS from './lit/frag/ltc.js'; // import metalnessPS from './standard/frag/metalness.js'; @@ -176,7 +174,6 @@ import sphericalPS from './common/frag/spherical.js'; // import specularityFactorPS from './standard/frag/specularityFactor.js'; // import spotPS from './lit/frag/spot.js'; // import startPS from './lit/frag/start.js'; -// import startVS from './lit/vert/start.js'; // import startNineSlicedPS from './lit/frag/startNineSliced.js'; // import startNineSlicedTiledPS from './lit/frag/startNineSlicedTiled.js'; // import tangentBinormalVS from './lit/vert/tangentBinormal.js'; @@ -199,8 +196,9 @@ import tonemappingNonePS from './common/frag/tonemapping/tonemappingNone.js'; // import twoSidedLightingPS from './lit/frag/twoSidedLighting.js'; // import uv0VS from './lit/vert/uv0.js'; // import uv1VS from './lit/vert/uv1.js'; +// import uvTransformPS from './lit/vert/uvTransform.js'; +// import uvTransformUniformsPS from './lit/vert/uvTransformUniforms.js'; // import viewDirPS from './lit/frag/viewDir.js'; -// import viewNormalVS from './lit/vert/viewNormal.js'; // import webgpuPS from '../../../platform/graphics/shader-chunks/frag/webgpu.js'; // import webgpuVS from '../../../platform/graphics/shader-chunks/vert/webgpu.js'; @@ -223,9 +221,7 @@ const shaderChunksWGSL = { // aoSpecOccConstSimplePS, // aoSpecOccSimplePS, // basePS, - // baseVS, // baseNineSlicedPS, - // baseNineSlicedVS, // baseNineSlicedTiledPS, // bayerPS, // blurVSMPS, @@ -250,7 +246,6 @@ const shaderChunksWGSL = { // emissivePS, // encodePS, // endPS, - // endVS, envAtlasPS, // envConstPS, envMultiplyPS, @@ -297,6 +292,7 @@ const shaderChunksWGSL = { // lightSpecularBlinnPS, // lightSheenPS, // linearizeDepthPS, + // litMainVS, // litShaderArgsPS, // ltcPS, // metalnessPS, @@ -390,7 +386,6 @@ const shaderChunksWGSL = { // specularityFactorPS, // spotPS, // startPS, - // startVS, // startNineSlicedPS, // startNineSlicedTiledPS, // tangentBinormalVS, @@ -413,8 +408,9 @@ const shaderChunksWGSL = { // twoSidedLightingPS, // uv0VS, // uv1VS, + // uvTransformPS, + // uvTransformUniformsPS, // viewDirPS, - // viewNormalVS, // webgpuPS, // webgpuVS }; diff --git a/src/scene/shader-lib/chunks/chunk-validation.js b/src/scene/shader-lib/chunks/chunk-validation.js index 26dfca71dde..d8ac4f1b0ed 100644 --- a/src/scene/shader-lib/chunks/chunk-validation.js +++ b/src/scene/shader-lib/chunks/chunk-validation.js @@ -115,7 +115,12 @@ const removedChunks = { shadowEVSMnPS: CHUNKAPI_2_6, shadowVSM_commonPS: CHUNKAPI_2_6, shadowStandardPS: CHUNKAPI_2_6, - shadowStandardGL2PS: CHUNKAPI_2_6 + shadowStandardGL2PS: CHUNKAPI_2_6, + startVS: CHUNKAPI_2_6, + endVS: CHUNKAPI_2_6, + baseVS: CHUNKAPI_2_6, + baseNineSlicedVS: CHUNKAPI_2_6, + viewNormalVS: 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 bb7117cd58f..88dcfb3adc2 100644 --- a/src/scene/shader-lib/chunks/chunks.js +++ b/src/scene/shader-lib/chunks/chunks.js @@ -10,9 +10,7 @@ import aoSpecOccConstPS from './lit/frag/aoSpecOccConst.js'; import aoSpecOccConstSimplePS from './lit/frag/aoSpecOccConstSimple.js'; import aoSpecOccSimplePS from './lit/frag/aoSpecOccSimple.js'; import basePS from './lit/frag/base.js'; -import baseVS from './lit/vert/base.js'; import baseNineSlicedPS from './lit/frag/baseNineSliced.js'; -import baseNineSlicedVS from './lit/vert/baseNineSliced.js'; import baseNineSlicedTiledPS from './lit/frag/baseNineSlicedTiled.js'; import bayerPS from './common/frag/bayer.js'; import blurVSMPS from './lit/frag/blurVSM.js'; @@ -37,7 +35,6 @@ import diffuseDetailMapPS from './standard/frag/diffuseDetailMap.js'; import emissivePS from './standard/frag/emissive.js'; import encodePS from './common/frag/encode.js'; import endPS from './lit/frag/end.js'; -import endVS from './lit/vert/end.js'; import envAtlasPS from './common/frag/envAtlas.js'; import envConstPS from './common/frag/envConst.js'; import envMultiplyPS from './common/frag/envMultiply.js'; @@ -83,6 +80,7 @@ import lightSpecularAnisoGGXPS from './lit/frag/lightSpecularAnisoGGX.js'; import lightSpecularBlinnPS from './lit/frag/lightSpecularBlinn.js'; import lightSheenPS from './lit/frag/lightSheen.js'; import linearizeDepthPS from './common/frag/linearizeDepth.js'; +import litMainVS from './lit/vert/litMain.js'; import litShaderArgsPS from './standard/frag/litShaderArgs.js'; import ltcPS from './lit/frag/ltc.js'; import metalnessPS from './standard/frag/metalness.js'; @@ -176,7 +174,6 @@ import sphericalPS from './common/frag/spherical.js'; import specularityFactorPS from './standard/frag/specularityFactor.js'; import spotPS from './lit/frag/spot.js'; import startPS from './lit/frag/start.js'; -import startVS from './lit/vert/start.js'; import startNineSlicedPS from './lit/frag/startNineSliced.js'; import startNineSlicedTiledPS from './lit/frag/startNineSlicedTiled.js'; import tangentBinormalVS from './lit/vert/tangentBinormal.js'; @@ -199,8 +196,9 @@ import transmissionPS from './standard/frag/transmission.js'; import twoSidedLightingPS from './lit/frag/twoSidedLighting.js'; import uv0VS from './lit/vert/uv0.js'; import uv1VS from './lit/vert/uv1.js'; +import uvTransformPS from './lit/vert/uvTransform.js'; +import uvTransformUniformsPS from './lit/vert/uvTransformUniforms.js'; import viewDirPS from './lit/frag/viewDir.js'; -import viewNormalVS from './lit/vert/viewNormal.js'; import webgpuPS from '../../../platform/graphics/shader-chunks/frag/webgpu.js'; import webgpuVS from '../../../platform/graphics/shader-chunks/vert/webgpu.js'; @@ -223,9 +221,7 @@ const shaderChunks = { aoSpecOccConstSimplePS, aoSpecOccSimplePS, basePS, - baseVS, baseNineSlicedPS, - baseNineSlicedVS, baseNineSlicedTiledPS, bayerPS, blurVSMPS, @@ -250,7 +246,6 @@ const shaderChunks = { emissivePS, encodePS, endPS, - endVS, envAtlasPS, envConstPS, envMultiplyPS, @@ -297,6 +292,7 @@ const shaderChunks = { lightSpecularBlinnPS, lightSheenPS, linearizeDepthPS, + litMainVS, litShaderArgsPS, ltcPS, metalnessPS, @@ -390,7 +386,6 @@ const shaderChunks = { specularityFactorPS, spotPS, startPS, - startVS, startNineSlicedPS, startNineSlicedTiledPS, tangentBinormalVS, @@ -413,8 +408,9 @@ const shaderChunks = { twoSidedLightingPS, uv0VS, uv1VS, + uvTransformPS, + uvTransformUniformsPS, viewDirPS, - viewNormalVS, webgpuPS, webgpuVS }; diff --git a/src/scene/shader-lib/chunks/lit/vert/base.js b/src/scene/shader-lib/chunks/lit/vert/base.js deleted file mode 100644 index 88ef7765453..00000000000 --- a/src/scene/shader-lib/chunks/lit/vert/base.js +++ /dev/null @@ -1,11 +0,0 @@ -export default /* glsl */` -attribute vec4 vertex_tangent; -attribute vec2 vertex_texCoord0; -attribute vec2 vertex_texCoord1; -attribute vec4 vertex_color; - -vec3 dPositionW; -mat4 dModelMatrix; - -#include "transformCoreVS" -`; diff --git a/src/scene/shader-lib/chunks/lit/vert/baseNineSliced.js b/src/scene/shader-lib/chunks/lit/vert/baseNineSliced.js deleted file mode 100644 index e85eefc2fd1..00000000000 --- a/src/scene/shader-lib/chunks/lit/vert/baseNineSliced.js +++ /dev/null @@ -1,10 +0,0 @@ -export default /* glsl */` -#define NINESLICED - -varying vec2 vMask; -varying vec2 vTiledUv; - -uniform mediump vec4 innerOffset; -uniform mediump vec2 outerScale; -uniform mediump vec4 atlasRect; -`; diff --git a/src/scene/shader-lib/chunks/lit/vert/end.js b/src/scene/shader-lib/chunks/lit/vert/end.js deleted file mode 100644 index 30f08eb9bee..00000000000 --- a/src/scene/shader-lib/chunks/lit/vert/end.js +++ /dev/null @@ -1,2 +0,0 @@ -export default /* glsl */` -`; diff --git a/src/scene/shader-lib/chunks/lit/vert/litMain.js b/src/scene/shader-lib/chunks/lit/vert/litMain.js new file mode 100644 index 00000000000..eb7c39a90af --- /dev/null +++ b/src/scene/shader-lib/chunks/lit/vert/litMain.js @@ -0,0 +1,104 @@ +// main shader of the lit vertex shader +export default /* glsl */` +#ifdef VERTEX_COLOR + attribute vec4 vertex_color; +#endif + +vec3 dPositionW; +mat4 dModelMatrix; + +#ifdef UV0 + attribute vec2 vertex_texCoord0; + #include "uv0VS" +#endif + +#ifdef UV1 + attribute vec2 vertex_texCoord1; + #include "uv1VS" +#endif + +#include "transformCoreVS" + +#ifdef NINESLICED + + varying vec2 vMask; + varying vec2 vTiledUv; + + uniform mediump vec4 innerOffset; + uniform mediump vec2 outerScale; + uniform mediump vec4 atlasRect; + +#endif + +#ifdef LINEAR_DEPTH + #ifndef VIEWMATRIX + #define VIEWMATRIX + uniform mat4 matrix_view; + #endif +#endif + +#include "transformVS" + +#ifdef NORMALS + #include "normalCoreVS" + #include "normalVS" +#endif + +#ifdef TANGENTS + attribute vec4 vertex_tangent; + #include "tangentBinormalVS" +#endif + +// expand uniforms for uv transforms +#include "uvTransformUniformsPS, UV_TRANSFORMS_COUNT" + +#ifdef MSDF + #include "msdfVS" +#endif + +void main(void) { + gl_Position = getPosition(); + vPositionW = getWorldPosition(); + + #ifdef NORMALS + vNormalW = getNormal(); + #endif + + #ifdef TANGENTS + vTangentW = getTangent(); + vBinormalW = getBinormal(); + #elif defined(GGX_SPECULAR) + vObjectSpaceUpW = normalize(dNormalMatrix * vec3(0, 1, 0)); + #endif + + #ifdef UV0 + vec2 uv0 = getUv0(); + #ifdef UV0_UNMODIFIED + vUv0 = uv0; + #endif + #endif + + #ifdef UV1 + vec2 uv1 = getUv1(); + #ifdef UV1_UNMODIFIED + vUv1 = uv1; + #endif + #endif + + // expand code for uv transforms + #include "uvTransformPS, UV_TRANSFORMS_COUNT" + + #ifdef VERTEX_COLOR + vVertexColor = vertex_color; + #endif + + #ifdef LINEAR_DEPTH + // linear depth from the worldPosition, see getLinearDepth + vLinearDepth = -(matrix_view * vec4(vPositionW, 1.0)).z; + #endif + + #ifdef MSDF + unpackMsdfParams(); + #endif +} +`; diff --git a/src/scene/shader-lib/chunks/lit/vert/start.js b/src/scene/shader-lib/chunks/lit/vert/start.js deleted file mode 100644 index 54dd1429aa6..00000000000 --- a/src/scene/shader-lib/chunks/lit/vert/start.js +++ /dev/null @@ -1,4 +0,0 @@ -export default /* glsl */` -void main(void) { - gl_Position = getPosition(); -`; diff --git a/src/scene/shader-lib/chunks/lit/vert/uvTransform.js b/src/scene/shader-lib/chunks/lit/vert/uvTransform.js new file mode 100644 index 00000000000..06957fdbd9b --- /dev/null +++ b/src/scene/shader-lib/chunks/lit/vert/uvTransform.js @@ -0,0 +1,7 @@ +// chunk that generates uv coordinate transformed by uv transform matrix +export default /* glsl */` +vUV_INJECT_TRANSFORM_UV_{i}__INJECT_TRANSFORM_ID_{i} = vec2( + dot(vec3(uv_INJECT_TRANSFORM_UV_{i}, 1), _INJECT_TRANSFORM_NAME_{i}0), + dot(vec3(uv_INJECT_TRANSFORM_UV_{i}, 1), _INJECT_TRANSFORM_NAME_{i}1) +); +`; diff --git a/src/scene/shader-lib/chunks/lit/vert/uvTransformUniforms.js b/src/scene/shader-lib/chunks/lit/vert/uvTransformUniforms.js new file mode 100644 index 00000000000..7f5212f7de8 --- /dev/null +++ b/src/scene/shader-lib/chunks/lit/vert/uvTransformUniforms.js @@ -0,0 +1,5 @@ +// uniform declaration for uv transform matrix, 3x2 matrix +export default /* glsl */` + uniform vec3 _INJECT_TRANSFORM_NAME_{i}0; + uniform vec3 _INJECT_TRANSFORM_NAME_{i}1; +`; diff --git a/src/scene/shader-lib/programs/lit-shader.js b/src/scene/shader-lib/programs/lit-shader.js index a5f04beff87..53e21a01f00 100644 --- a/src/scene/shader-lib/programs/lit-shader.js +++ b/src/scene/shader-lib/programs/lit-shader.js @@ -40,20 +40,21 @@ const builtinAttributes = { vertex_boneIndices: SEMANTIC_BLENDINDICES }; -const builtinVaryings = { - vVertexColor: 'vec4', - vPositionW: 'vec3', - vNormalV: 'vec3', - vNormalW: 'vec3', - vTangentW: 'vec3', - vBinormalW: 'vec3', - vObjectSpaceUpW: 'vec3', - vUv0: 'vec2', - vUv1: 'vec2', - vLinearDepth: 'float' -}; +export const varyingsWGSLTypes = new Map([ + ['vec4', 'vec4f'], + ['vec3', 'vec3f'], + ['vec2', 'vec2f'], + ['float', 'f32'] +]); class LitShader { + /** + * Shader code representing varyings. + * + * @type {string} + */ + varyingsCode = ''; + /** * @param {GraphicsDevice} device - The graphics device. * @param {LitShaderOptions} options - The lit options. @@ -116,8 +117,6 @@ class LitShader { this.needsTransforms = options.useDynamicRefraction; // generated by vshader - this.varyings = ''; - this.varyingDefines = ''; this.vshader = null; // supplied by caller @@ -135,29 +134,6 @@ class LitShader { this.fshader = null; } - _vsAddBaseCode(code, chunks, options) { - code += chunks.baseVS; - if (options.nineSlicedMode === SPRITE_RENDERMODE_SLICED || - options.nineSlicedMode === SPRITE_RENDERMODE_TILED) { - code += chunks.baseNineSlicedVS; - } - return code; - } - - _setMapTransform(codes, name, id, uv) { - const checkId = id + uv * 100; - if (!codes[3][checkId]) { - // upload a 3x2 matrix and manually perform the multiplication - const varName = `texture_${name}MapTransform`; - codes[0] += `uniform vec3 ${varName}0;\n`; - codes[0] += `uniform vec3 ${varName}1;\n`; - codes[1] += `varying vec2 vUV${uv}_${id};\n`; - codes[2] += ` vUV${uv}_${id} = vec2(dot(vec3(uv${uv}, 1), ${varName}0), dot(vec3(uv${uv}, 1), ${varName}1));\n`; - codes[3][checkId] = true; - } - return codes; - } - // Add "Base" Code section to fragment shader. _fsGetBaseCode() { const options = this.options; @@ -195,163 +171,152 @@ class LitShader { } } + /** + * The function generates defines for the lit vertex shader, and handles required attributes and + * varyings. The source code of the shader is supplied by litMainVS chunk. + * + * @param {any} useUv - Info about used UVs. + * @param {any} useUnmodifiedUv - Info about used unmodified UVs. + * @param {any} mapTransforms - Info about used texture transforms. + */ generateVertexShader(useUv, useUnmodifiedUv, mapTransforms) { - const device = this.device; - const options = this.options; - const chunks = this.chunks; - let code = ''; - let codeBody = ''; - let codeDefines = ''; + const { options, vDefines, attributes, chunks } = this; - code = this._vsAddBaseCode(code, chunks, options); + // varyings + const varyings = new Map(); + varyings.set('vPositionW', 'vec3'); - codeBody += ' vPositionW = getWorldPosition();\n'; + if (options.nineSlicedMode === SPRITE_RENDERMODE_SLICED || options.nineSlicedMode === SPRITE_RENDERMODE_TILED) { + vDefines.set('NINESLICED', true); + } if (this.options.linearDepth) { - codeDefines += ` - #ifndef VIEWMATRIX - #define VIEWMATRIX - uniform mat4 matrix_view; - #endif - `; - codeBody += ` - // linear depth from the worldPosition, see getLinearDepth - vLinearDepth = -(matrix_view * vec4(vPositionW, 1.0)).z; - `; + vDefines.set('LINEAR_DEPTH', true); + varyings.set('vLinearDepth', 'float'); } + if (this.needsNormal) vDefines.set('NORMALS', true); + if (this.options.useInstancing) { // only attach these if the default instancing chunk is used, otherwise it is expected // for the user to provide required attributes using material.setAttribute if (this.chunks.transformInstancingVS === shaderChunks.transformInstancingVS) { - this.attributes.instance_line1 = SEMANTIC_ATTR12; - this.attributes.instance_line2 = SEMANTIC_ATTR13; - this.attributes.instance_line3 = SEMANTIC_ATTR14; - this.attributes.instance_line4 = SEMANTIC_ATTR15; + attributes.instance_line1 = SEMANTIC_ATTR12; + attributes.instance_line2 = SEMANTIC_ATTR13; + attributes.instance_line3 = SEMANTIC_ATTR14; + attributes.instance_line4 = SEMANTIC_ATTR15; } } - code += chunks.transformVS; - if (this.needsNormal) { - code += chunks.normalCoreVS; - code += chunks.normalVS; - } + attributes.vertex_normal = SEMANTIC_NORMAL; + varyings.set('vNormalW', 'vec3'); - if (this.needsNormal) { - this.attributes.vertex_normal = SEMANTIC_NORMAL; - codeBody += ' vNormalW = getNormal();\n'; + if (options.hasTangents && (options.useHeights || options.useNormals || options.enableGGXSpecular)) { - if (options.reflectionSource === 'sphereMap' && device.fragmentUniformsCount <= 16) { - code += chunks.viewNormalVS; - codeBody += ' vNormalV = getViewNormal();\n'; - } + vDefines.set('TANGENTS', true); + attributes.vertex_tangent = SEMANTIC_TANGENT; + varyings.set('vTangentW', 'vec3'); + varyings.set('vBinormalW', 'vec3'); - if (options.hasTangents && (options.useHeights || options.useNormals || options.enableGGXSpecular)) { - this.attributes.vertex_tangent = SEMANTIC_TANGENT; - code += chunks.tangentBinormalVS; - codeBody += ' vTangentW = getTangent();\n'; - codeBody += ' vBinormalW = getBinormal();\n'; } else if (options.enableGGXSpecular) { - codeBody += ' vObjectSpaceUpW = normalize(dNormalMatrix * vec3(0, 1, 0));\n'; + + vDefines.set('GGX_SPECULAR', true); + varyings.set('vObjectSpaceUpW', 'vec3'); } } const maxUvSets = 2; - for (let i = 0; i < maxUvSets; i++) { if (useUv[i]) { - this.attributes[`vertex_texCoord${i}`] = `TEXCOORD${i}`; - code += chunks[`uv${i}VS`]; - codeBody += ` vec2 uv${i} = getUv${i}();\n`; + vDefines.set(`UV${i}`, true); + attributes[`vertex_texCoord${i}`] = `TEXCOORD${i}`; } if (useUnmodifiedUv[i]) { - codeBody += ` vUv${i} = uv${i};\n`; + vDefines.set(`UV${i}_UNMODIFIED`, true); + varyings.set(`vUv${i}`, 'vec2'); } } - const codes = [code, this.varyings, codeBody, []]; - + // prepare defines for texture transforms + let numTransforms = 0; + const transformDone = new Set(); mapTransforms.forEach((mapTransform) => { - this._setMapTransform(codes, mapTransform.name, mapTransform.id, mapTransform.uv); + + const { id, uv, name } = mapTransform; + const checkId = id + uv * 100; // make sure each UV set is transformed by each unique transform only once + + if (!transformDone.has(checkId)) { + transformDone.add(checkId); + + // register the varying + varyings.set(`vUV${uv}_${id}`, 'vec2'); + + // defines used by the included chunks + const varName = `texture_${name}MapTransform`; + vDefines.set(`_INJECT_TRANSFORM_NAME_${numTransforms}`, varName); + vDefines.set(`_INJECT_TRANSFORM_UV_${numTransforms}`, uv); + vDefines.set(`_INJECT_TRANSFORM_ID_${numTransforms}`, id); + + numTransforms++; + } }); - code = codes[0]; - this.varyings = codes[1]; - codeBody = codes[2]; + // number of transforms, this drives the looped includes + vDefines.set('UV_TRANSFORMS_COUNT', numTransforms); if (options.vertexColors) { - this.attributes.vertex_color = SEMANTIC_COLOR; - codeBody += ' vVertexColor = vertex_color;\n'; + attributes.vertex_color = SEMANTIC_COLOR; + vDefines.set('VERTEX_COLOR', true); + varyings.set('vVertexColor', 'vec4'); } if (options.useMsdf && options.msdfTextAttribute) { - this.attributes.vertex_outlineParameters = SEMANTIC_ATTR8; - this.attributes.vertex_shadowParameters = SEMANTIC_ATTR9; - - codeBody += ' unpackMsdfParams();\n'; - - code += chunks.msdfVS; + attributes.vertex_outlineParameters = SEMANTIC_ATTR8; + attributes.vertex_shadowParameters = SEMANTIC_ATTR9; + vDefines.set('MSDF', true); } // morphing if (options.useMorphPosition || options.useMorphNormal) { - codeDefines += '#define MORPHING\n'; - - if (options.useMorphTextureBasedInt) { - codeDefines += '#define MORPHING_INT\n'; - } - - if (options.useMorphPosition) { - codeDefines += '#define MORPHING_POSITION\n'; - } - - if (options.useMorphNormal) { - codeDefines += '#define MORPHING_NORMAL\n'; - } + vDefines.set('MORPHING', true); + if (options.useMorphTextureBasedInt) vDefines.set('MORPHING_INT', true); + if (options.useMorphPosition) vDefines.set('MORPHING_POSITION', true); + if (options.useMorphNormal) vDefines.set('MORPHING_NORMAL', true); // vertex ids attributes - this.attributes.morph_vertex_id = SEMANTIC_ATTR15; + attributes.morph_vertex_id = SEMANTIC_ATTR15; } if (options.skin) { - this.attributes.vertex_boneIndices = SEMANTIC_BLENDINDICES; + attributes.vertex_boneIndices = SEMANTIC_BLENDINDICES; if (options.batch) { - codeDefines += '#define BATCH\n'; + vDefines.set('BATCH', true); } else { - this.attributes.vertex_boneWeights = SEMANTIC_BLENDWEIGHT; - codeDefines += '#define SKIN\n'; + attributes.vertex_boneWeights = SEMANTIC_BLENDWEIGHT; + vDefines.set('SKIN', true); } - } else if (options.useInstancing) { - codeDefines += '#define INSTANCING\n'; - } - if (options.screenSpace) { - codeDefines += '#define SCREENSPACE\n'; - } - if (options.pixelSnap) { - codeDefines += '#define PIXELSNAP\n'; } - code += '\n'; - code += chunks.startVS; - code += codeBody; - code += chunks.endVS; - code += '}'; - - // build varyings - Object.keys(builtinVaryings).forEach((v) => { - if (code.indexOf(v) >= 0) { - this.varyings += `varying ${builtinVaryings[v]} ${v};\n`; - this.varyingDefines += `#define VARYING_${v.toUpperCase()}\n`; - } + if (options.useInstancing) vDefines.set('INSTANCING', true); + if (options.screenSpace) vDefines.set('SCREENSPACE', true); + if (options.pixelSnap) vDefines.set('PIXELSNAP', true); + + // generate varyings code + varyings.forEach((type, name) => { + vDefines.set(`VARYING_${name.toUpperCase()}`, true); + const generateWgsl = false; // when we switch generation to WGSL on WebGPU + this.varyingsCode += generateWgsl ? + `varying ${name}: ${varyingsWGSLTypes.get(type)};\n` : + `varying ${type} ${name};\n`; }); - this.vshader = codeDefines + this.varyings + code; + this.vshader = this.varyingsCode + chunks.litMainVS; } _fsGetBeginCode() { @@ -390,8 +355,7 @@ class LitShader { const code = ` ${this._fsGetBeginCode()} - ${this.varyings} - ${this.varyingDefines} + ${this.varyingsCode} ${this.frontendDecl} ${this.frontendCode} @@ -1359,8 +1323,7 @@ class LitShader { if (mergedCode.includes('sReflection')) structCode += 'vec3 sReflection;\n'; const result = this._fsGetBeginCode() + - this.varyings + - this.varyingDefines + + this.varyingsCode + this._fsGetBaseCode() + structCode + this.frontendDecl + @@ -1390,8 +1353,7 @@ class LitShader { ${this.device.textureFloatRenderable ? '#define TEXTURE_FLOAT_RENDERABLE' : ''} ${this._fsGetBeginCode()} - ${this.varyings} - ${this.varyingDefines} + ${this.varyingsCode} ${this.frontendDecl} ${this.frontendCode} diff --git a/src/scene/shader-lib/programs/lit.js b/src/scene/shader-lib/programs/lit.js index d015b2ba165..09d66377dc9 100644 --- a/src/scene/shader-lib/programs/lit.js +++ b/src/scene/shader-lib/programs/lit.js @@ -42,16 +42,16 @@ class ShaderGeneratorLit extends ShaderGenerator { decl.append(litShader.chunks.litShaderArgsPS); code.append(options.shaderChunk); - const usedUvSets = options.usedUvs || [true]; - const mapTransforms = []; - const definitionOptions = { name: 'LitShader', shaderLanguage: SHADERLANGUAGE_GLSL, tag: litShader.shaderPassInfo.isForward ? SHADERTAG_MATERIAL : undefined }; + const usedUvSets = options.usedUvs || [true]; + const mapTransforms = []; litShader.generateVertexShader(usedUvSets, usedUvSets, mapTransforms); + litShader.generateFragmentShader(decl.code, code.code, 'vUv0'); const includes = new Map(Object.entries({ diff --git a/src/scene/shader-lib/programs/standard.js b/src/scene/shader-lib/programs/standard.js index 210047a0a96..2cdf7bedbf7 100644 --- a/src/scene/shader-lib/programs/standard.js +++ b/src/scene/shader-lib/programs/standard.js @@ -204,50 +204,39 @@ class ShaderGeneratorStandard extends ShaderGenerator { } } - /** - * @param {GraphicsDevice} device - The graphics device. - * @param {StandardMaterialOptions} options - The create options. - * @returns {object} Returns the created shader definition. - */ - createShaderDefinition(device, options) { - - const shaderPassInfo = ShaderPass.get(device).getByIndex(options.litOptions.pass); - const isForwardPass = shaderPassInfo.isForward; - const litShader = new LitShader(device, options.litOptions); + createVertexShader(litShader, options) { - // generate vertex shader const useUv = []; const useUnmodifiedUv = []; const mapTransforms = []; const maxUvSets = 2; - const textureMapping = {}; for (const p in _matTex2D) { - const mname = `${p}Map`; + const mapName = `${p}Map`; if (options[`${p}VertexColor`]) { - const cname = `${p}VertexColorChannel`; - options[cname] = this._correctChannel(p, options[cname], _matTex2D); + const colorChannelName = `${p}VertexColorChannel`; + options[colorChannelName] = this._correctChannel(p, options[colorChannelName], _matTex2D); } - if (options[mname]) { - const cname = `${mname}Channel`; - const tname = `${mname}Transform`; - const uname = `${mname}Uv`; + if (options[mapName]) { + const channelName = `${mapName}Channel`; + const transformName = `${mapName}Transform`; + const uvName = `${mapName}Uv`; - options[uname] = Math.min(options[uname], maxUvSets - 1); - options[cname] = this._correctChannel(p, options[cname], _matTex2D); + options[uvName] = Math.min(options[uvName], maxUvSets - 1); + options[channelName] = this._correctChannel(p, options[channelName], _matTex2D); - const uvSet = options[uname]; + const uvSet = options[uvName]; useUv[uvSet] = true; - useUnmodifiedUv[uvSet] = useUnmodifiedUv[uvSet] || (options[mname] && !options[tname]); + useUnmodifiedUv[uvSet] = useUnmodifiedUv[uvSet] || (options[mapName] && !options[transformName]); // create map transforms - if (options[tname]) { + if (options[transformName]) { mapTransforms.push({ name: p, - id: options[tname], - uv: options[uname] + id: options[transformName], + uv: options[uvName] }); } } @@ -259,8 +248,25 @@ class ShaderGeneratorStandard extends ShaderGenerator { } litShader.generateVertexShader(useUv, useUnmodifiedUv, mapTransforms); + } + + /** + * @param {GraphicsDevice} device - The graphics device. + * @param {StandardMaterialOptions} options - The create options. + * @returns {object} Returns the created shader definition. + */ + createShaderDefinition(device, options) { + + const shaderPassInfo = ShaderPass.get(device).getByIndex(options.litOptions.pass); + const isForwardPass = shaderPassInfo.isForward; + const litShader = new LitShader(device, options.litOptions); + + // generate vertex shader + this.createVertexShader(litShader, options); // handle fragment shader + const textureMapping = {}; + options.litOptions.fresnelModel = (options.litOptions.fresnelModel === 0) ? FRESNEL_SCHLICK : options.litOptions.fresnelModel; const decl = new ChunkBuilder();