| 220 | |
| 221 | let mossMaps = []; // rebuilt by loadMossTextures() |
| 222 | function loadMossTextures() { |
| 223 | const make = (suffix) => { |
| 224 | const tex = loader.load('/Moss002_1K-JPG_' + suffix + '.jpg'); |
| 225 | // Sampled through custom uniforms, so tiling is driven by uMossTextureScale |
| 226 | // (world-space UVs) rather than the texture's own repeat. |
| 227 | tex.wrapS = tex.wrapT = THREE.RepeatWrapping; |
| 228 | tex.anisotropy = maxAnisotropy; |
| 229 | return tex; |
| 230 | }; |
| 231 | for (const m of mossMaps) m.dispose(); |
| 232 | mossUniforms.uMossMap.value = make('Color'); // decoded to linear in-shader |
| 233 | mossUniforms.uMossRoughnessMap.value = make('Roughness'); |
| 234 | mossUniforms.uMossNormalMap.value = make('NormalGL'); |
| 235 | mossUniforms.uMossAoMap.value = make('AmbientOcclusion'); |
| 236 | mossMaps = [ |
| 237 | mossUniforms.uMossMap.value, |
| 238 | mossUniforms.uMossRoughnessMap.value, |
| 239 | mossUniforms.uMossNormalMap.value, |
| 240 | mossUniforms.uMossAoMap.value, |
| 241 | ]; |
| 242 | } |
| 243 | |
| 244 | // Pure noise helpers (no uniforms). |
| 245 | const NOISE_FUNCTIONS = /* glsl */ ` |