@call-me-sensei/toonlab 0.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/ATTRIBUTION.md +40 -0
- package/LICENSE +21 -0
- package/README.md +125 -0
- package/package.json +97 -0
- package/src/character/characterRig.js +353 -0
- package/src/character/freestyleSwimClip.js +541 -0
- package/src/character/index.js +4 -0
- package/src/character/modelLoader.js +433 -0
- package/src/core/materialRoles.js +438 -0
- package/src/core/presetDocuments.js +110 -0
- package/src/core/shaderBackend.js +24 -0
- package/src/debrisgen/debrisFields.js +544 -0
- package/src/debrisgen/debrisGenerator.js +1929 -0
- package/src/debrisgen/debrisPalettes.js +71 -0
- package/src/debrisgen/debrisPhysics.js +198 -0
- package/src/debrisgen/debrisPresets.js +155 -0
- package/src/debrisgen/debrisSettings.js +333 -0
- package/src/debrisgen/debrisTextures.js +380 -0
- package/src/debrisgen/index.js +5 -0
- package/src/debug/fieldValues.js +95 -0
- package/src/debug/index.js +7 -0
- package/src/debug/settingsPanel.js +170 -0
- package/src/environment/environmentAmbientProbe.js +160 -0
- package/src/environment/environmentMaterialAdapter.js +336 -0
- package/src/environment/environmentMaterialClassifier.js +178 -0
- package/src/environment/environmentPlanarReflection.js +201 -0
- package/src/environment/environmentPresets.js +443 -0
- package/src/environment/environmentRigs.js +684 -0
- package/src/environment/environmentSettings.js +504 -0
- package/src/environment/environmentShaderMaterials.js +318 -0
- package/src/environment/environmentSunShadowPass.js +268 -0
- package/src/environment/environmentTextureResolver.js +161 -0
- package/src/environment/environmentTimeOfDay.js +167 -0
- package/src/environment/environmentVertexAo.js +188 -0
- package/src/environment/index.js +8 -0
- package/src/environment/scanAssetStylize.js +103 -0
- package/src/index.js +21 -0
- package/src/loaders/index.js +1 -0
- package/src/post/index.js +2 -0
- package/src/post/postProcessing.js +1087 -0
- package/src/rockgen/export/glbExport.js +177 -0
- package/src/rockgen/heightfield/heightfieldErosion.js +7 -0
- package/src/rockgen/heightfield/heightfieldPatch.js +183 -0
- package/src/rockgen/heightfield/stylizedErosionSim.js +356 -0
- package/src/rockgen/index.js +14 -0
- package/src/rockgen/mesh/meshAttributes.js +374 -0
- package/src/rockgen/mesh/meshDocument.js +158 -0
- package/src/rockgen/mesh/surfaceNets.js +329 -0
- package/src/rockgen/noise/cellularNoise3.js +92 -0
- package/src/rockgen/noise/prng.js +62 -0
- package/src/rockgen/noise/simplexNoise3.js +89 -0
- package/src/rockgen/noise/valueNoise3.js +87 -0
- package/src/rockgen/rockDocument.js +255 -0
- package/src/rockgen/rockHelpers.js +8 -0
- package/src/rockgen/rockgenPresets.js +559 -0
- package/src/rockgen/rockgenSettings.js +756 -0
- package/src/rockgen/sdf/fieldCompiler.js +544 -0
- package/src/rockgen/sdf/sculptEdits.js +66 -0
- package/src/rockgen/sdf/sdfModifiers.js +45 -0
- package/src/rockgen/sdf/sdfOps.js +37 -0
- package/src/rockgen/sdf/sdfPrimitives.js +88 -0
- package/src/shaders-tsl/anime.js +963 -0
- package/src/shaders-tsl/chunks/character-color.js +64 -0
- package/src/shaders-tsl/chunks/character-highlights.js +174 -0
- package/src/shaders-tsl/chunks/character-lighting.js +309 -0
- package/src/shaders-tsl/chunks/character-material-maps.js +146 -0
- package/src/shaders-tsl/chunks/character-roles.js +52 -0
- package/src/shaders-tsl/chunks/character-scene-lights.js +270 -0
- package/src/shaders-tsl/chunks/character-shadow-color.js +61 -0
- package/src/shaders-tsl/chunks/character-skinning.js +140 -0
- package/src/shaders-tsl/chunks/environment-color.js +47 -0
- package/src/shaders-tsl/chunks/environment-debug.js +79 -0
- package/src/shaders-tsl/chunks/environment-lighting.js +260 -0
- package/src/shaders-tsl/chunks/environment-sun-shadow.js +87 -0
- package/src/shaders-tsl/chunks/foliage-fog.js +60 -0
- package/src/shaders-tsl/chunks/pass-depth-color.js +89 -0
- package/src/shaders-tsl/chunks/stylized-cloud-shadow.js +70 -0
- package/src/shaders-tsl/chunks/water-color.js +190 -0
- package/src/shaders-tsl/chunks/water-common.js +124 -0
- package/src/shaders-tsl/chunks/water-foam.js +93 -0
- package/src/shaders-tsl/chunks/water-lighting.js +116 -0
- package/src/shaders-tsl/chunks/water-ripple.js +64 -0
- package/src/shaders-tsl/chunks/water-waves.js +100 -0
- package/src/shaders-tsl/environment-ao-overlay.js +60 -0
- package/src/shaders-tsl/environment.js +680 -0
- package/src/shaders-tsl/flower.js +267 -0
- package/src/shaders-tsl/grass.js +193 -0
- package/src/shaders-tsl/post-composite.js +461 -0
- package/src/shaders-tsl/sky.js +175 -0
- package/src/shaders-tsl/tree-leaf.js +264 -0
- package/src/shaders-tsl/water-breaker.js +423 -0
- package/src/shaders-tsl/water-kelp.js +171 -0
- package/src/shaders-tsl/water-rain.js +107 -0
- package/src/shaders-tsl/water-simulation.js +140 -0
- package/src/shaders-tsl/water-splash.js +298 -0
- package/src/shaders-tsl/water.js +509 -0
- package/src/sky/index.js +2 -0
- package/src/sky/stylizedSky.js +341 -0
- package/src/toon/characterRenderPasses.js +646 -0
- package/src/toon/index.js +4 -0
- package/src/toon/settings/alphaSettings.js +170 -0
- package/src/toon/settings/averageShadowSettings.js +181 -0
- package/src/toon/settings/baseTextureSettings.js +133 -0
- package/src/toon/settings/celShadeSettings.js +96 -0
- package/src/toon/settings/contactShadowSettings.js +114 -0
- package/src/toon/settings/eyeHighlightSettings.js +187 -0
- package/src/toon/settings/faceLightingSettings.js +135 -0
- package/src/toon/settings/furSettings.js +112 -0
- package/src/toon/settings/glitterSettings.js +102 -0
- package/src/toon/settings/hairHighlightSettings.js +325 -0
- package/src/toon/settings/indirectLightSettings.js +244 -0
- package/src/toon/settings/localLightSettings.js +176 -0
- package/src/toon/settings/materialMapSettings.js +299 -0
- package/src/toon/settings/outlineSettings.js +263 -0
- package/src/toon/settings/perspectiveRemovalSettings.js +60 -0
- package/src/toon/settings/rimLightSettings.js +268 -0
- package/src/toon/settings/sceneShadowSettings.js +147 -0
- package/src/toon/settings/selfShadowSettings.js +221 -0
- package/src/toon/settings/shadowColorSettings.js +238 -0
- package/src/toon/settings/skinToneSettings.js +150 -0
- package/src/toon/settings/specularSettings.js +314 -0
- package/src/toon/settings/stickerSettings.js +102 -0
- package/src/toon/toonMaterialAdapter.js +2235 -0
- package/src/toon/toonSettings.js +860 -0
- package/src/vegetation/flowerSpecies.js +260 -0
- package/src/vegetation/index.js +10 -0
- package/src/vegetation/stylizedBush.js +136 -0
- package/src/vegetation/stylizedFlower.js +213 -0
- package/src/vegetation/stylizedFlowers.js +297 -0
- package/src/vegetation/stylizedGrass.js +491 -0
- package/src/vegetation/stylizedTree.js +2449 -0
- package/src/vegetation/stylizedTreeFoliage.js +691 -0
- package/src/vegetation/treeExport.js +300 -0
- package/src/vegetation/treeRecipe.js +647 -0
- package/src/water/index.js +2 -0
- package/src/water/water.js +20 -0
- package/src/water/waterBreakerSystem.js +591 -0
- package/src/water/waterInteraction.js +193 -0
- package/src/water/waterMaterial.js +263 -0
- package/src/water/waterRain.js +136 -0
- package/src/water/waterRippleSimulation.js +227 -0
- package/src/water/waterScenePasses.js +415 -0
- package/src/water/waterSettings.js +1138 -0
- package/src/water/waterSplashSystem.js +251 -0
- package/src/water/waterSurface.js +475 -0
- package/src/water/waterVegetation.js +83 -0
|
@@ -0,0 +1,680 @@
|
|
|
1
|
+
// TSL port of src/shaders/environment.vert.glsl + environment.frag.glsl — the
|
|
2
|
+
// stitched environment surface shader. Consumed by environmentShaderMaterials'
|
|
3
|
+
// createEnvironmentMaterial on the TSL backend; receives the same resolved
|
|
4
|
+
// role/texture decisions the GLSL ShaderMaterial uniform block is built from,
|
|
5
|
+
// and exposes `.uniforms` under the exact GLSL uniform names so every
|
|
6
|
+
// adapter/HUD/settings write-through works unchanged on both backends.
|
|
7
|
+
//
|
|
8
|
+
// Porting notes (docs/tsl-conventions.md):
|
|
9
|
+
// - Optional samplers are gated by the same conditions as the GLSL defines
|
|
10
|
+
// (JS guards instead of #ifdef), so unused maps never join the graph and
|
|
11
|
+
// ANGLE's 16-sampler budget is respected on the fallback backend. The
|
|
12
|
+
// texture *uniform slots* still exist for every map (fallback-backed), so
|
|
13
|
+
// `.value` write-throughs keep working exactly like the classic uniforms.
|
|
14
|
+
// - GLSL float-uniform "bools" (enableX > 0.5) stay float uniforms with the
|
|
15
|
+
// same comparisons.
|
|
16
|
+
// - vWorldNormal is ported faithfully as three's VIEW-space normal
|
|
17
|
+
// (normalize(transformedNormal) in the GLSL vertex stage) — the mixed-space
|
|
18
|
+
// lighting quirk is part of the approved baseline look.
|
|
19
|
+
// - Scene lights come from chunks/character-scene-lights.js (shared uniforms
|
|
20
|
+
// mirrored from the scene each frame by the converted meshes'
|
|
21
|
+
// onBeforeRender) instead of <lights_pars_begin>.
|
|
22
|
+
// - getShadowMask() maps to sampleEnvironmentSunShadow(vWorldPosition)
|
|
23
|
+
// (chunks/environment-sun-shadow.js) — inert (mask = 1) until the dedicated
|
|
24
|
+
// sun-shadow pass fills the shared uniforms.
|
|
25
|
+
// - Debug views are always compiled in (they add zero bindings); the GLSL
|
|
26
|
+
// ENV_DEBUG_VIEWS define flip becomes a pure envDebugMode uniform write.
|
|
27
|
+
// - Scene-wide state (clock, cloud shadows, openings, probe, debug mode,
|
|
28
|
+
// planar reflection pass output) lives in module-level shared uniform nodes
|
|
29
|
+
// mirroring environmentShaderMaterials' environmentSharedUniforms.
|
|
30
|
+
|
|
31
|
+
import * as THREE from 'three';
|
|
32
|
+
import {
|
|
33
|
+
attribute,
|
|
34
|
+
cameraFar,
|
|
35
|
+
cameraNear,
|
|
36
|
+
cameraPosition,
|
|
37
|
+
cameraProjectionMatrix,
|
|
38
|
+
cameraViewMatrix,
|
|
39
|
+
clamp,
|
|
40
|
+
Discard,
|
|
41
|
+
distance,
|
|
42
|
+
dot,
|
|
43
|
+
exp,
|
|
44
|
+
float,
|
|
45
|
+
Fn,
|
|
46
|
+
frontFacing,
|
|
47
|
+
If,
|
|
48
|
+
length,
|
|
49
|
+
max,
|
|
50
|
+
mix,
|
|
51
|
+
normalize,
|
|
52
|
+
normalLocal,
|
|
53
|
+
positionWorld,
|
|
54
|
+
pow,
|
|
55
|
+
select,
|
|
56
|
+
smoothstep,
|
|
57
|
+
texture,
|
|
58
|
+
transformNormalToView,
|
|
59
|
+
uniform,
|
|
60
|
+
uniformArray,
|
|
61
|
+
uv,
|
|
62
|
+
varying,
|
|
63
|
+
vec2,
|
|
64
|
+
vec3,
|
|
65
|
+
vec4,
|
|
66
|
+
vertexColor,
|
|
67
|
+
viewZToOrthographicDepth,
|
|
68
|
+
viewZToPerspectiveDepth,
|
|
69
|
+
} from 'three/tsl';
|
|
70
|
+
import { NodeMaterial } from 'three/webgpu';
|
|
71
|
+
|
|
72
|
+
import {
|
|
73
|
+
fallbackEnvironmentBlackTexture,
|
|
74
|
+
fallbackEnvironmentNormalTexture,
|
|
75
|
+
fallbackEnvironmentWhiteTexture,
|
|
76
|
+
} from '../environment/environmentTextureResolver.js';
|
|
77
|
+
import { applySaturation, envLuma, windowPaneMask } from './chunks/environment-color.js';
|
|
78
|
+
import { environmentDebugColor } from './chunks/environment-debug.js';
|
|
79
|
+
import { createEnvironmentLightingChunk } from './chunks/environment-lighting.js';
|
|
80
|
+
import { sampleEnvironmentSunShadow } from './chunks/environment-sun-shadow.js';
|
|
81
|
+
import { stylizedCloudShadow } from './chunks/stylized-cloud-shadow.js';
|
|
82
|
+
import { toonSceneLights } from './chunks/character-scene-lights.js';
|
|
83
|
+
|
|
84
|
+
// Scene-wide uniform nodes shared by reference across every environment node
|
|
85
|
+
// material — the TSL mirror of environmentSharedUniforms (same names, same
|
|
86
|
+
// defaults). environmentShaderMaterials' setters write both stores; the
|
|
87
|
+
// per-material `.uniforms` maps register these shared nodes so material-walk
|
|
88
|
+
// writes (applyEnvironmentSettingsToMaterial cloudShadow* etc.) hit the
|
|
89
|
+
// scene-wide values exactly like the classic shared-by-reference uniforms.
|
|
90
|
+
export const environmentSharedUniformNodes = {
|
|
91
|
+
time: uniform(0.0),
|
|
92
|
+
cloudShadowStrength: uniform(0.0),
|
|
93
|
+
cloudShadowCoverage: uniform(0.45),
|
|
94
|
+
cloudShadowScale: uniform(0.012),
|
|
95
|
+
cloudShadowVelocity: uniform(new THREE.Vector2(0.02, 0.006)),
|
|
96
|
+
envDebugMode: uniform(0.0),
|
|
97
|
+
environmentOpenings: uniformArray(
|
|
98
|
+
Array.from({ length: 4 }, () => new THREE.Vector4()),
|
|
99
|
+
'vec4',
|
|
100
|
+
),
|
|
101
|
+
environmentOpeningCount: uniform(0.0),
|
|
102
|
+
ambientProbe: uniformArray(
|
|
103
|
+
Array.from({ length: 6 }, () => new THREE.Color(1, 1, 1)),
|
|
104
|
+
'color',
|
|
105
|
+
),
|
|
106
|
+
planarReflectionMap: texture(fallbackEnvironmentBlackTexture),
|
|
107
|
+
planarReflectionMatrix: uniform(new THREE.Matrix4()),
|
|
108
|
+
};
|
|
109
|
+
|
|
110
|
+
function max3(color) {
|
|
111
|
+
return max(color.r, max(color.g, color.b));
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
/**
|
|
115
|
+
* Builds the environment NodeMaterial. Value defaults mirror the classic
|
|
116
|
+
* ShaderMaterial uniform block in environmentShaderMaterials.js line for
|
|
117
|
+
* line; `flags` mirrors the USE_ENV_* defines the classic factory sets.
|
|
118
|
+
*/
|
|
119
|
+
export function createEnvironmentNodeMaterial({
|
|
120
|
+
alphaBlend = false,
|
|
121
|
+
alphaCutoff = -1.0,
|
|
122
|
+
baseColor = new THREE.Color(1, 1, 1),
|
|
123
|
+
environmentBox = null,
|
|
124
|
+
flags,
|
|
125
|
+
hasSun = false,
|
|
126
|
+
isEmissive = false,
|
|
127
|
+
isFoliage = false,
|
|
128
|
+
isGlossFloor = false,
|
|
129
|
+
opacity = 1,
|
|
130
|
+
side = THREE.DoubleSide,
|
|
131
|
+
textureSet,
|
|
132
|
+
}) {
|
|
133
|
+
const shared = environmentSharedUniformNodes;
|
|
134
|
+
|
|
135
|
+
// ---- Uniforms (GLSL names; UniformNodes expose `.value` like
|
|
136
|
+
// ShaderMaterial uniform entries). Values mirror the classic block. ----
|
|
137
|
+
const u = {
|
|
138
|
+
baseMap: texture(textureSet.baseMap ?? fallbackEnvironmentWhiteTexture),
|
|
139
|
+
// Applies the base map's repeat/offset/rotation (THREE.Texture.matrix)
|
|
140
|
+
// like the built-in materials' mapTransform; identity when unset.
|
|
141
|
+
baseMapTransform: uniform(new THREE.Matrix3()),
|
|
142
|
+
alphaMap: texture(textureSet.alphaMap ?? fallbackEnvironmentWhiteTexture),
|
|
143
|
+
packedMap: texture(textureSet.packedMap ?? fallbackEnvironmentBlackTexture),
|
|
144
|
+
baseColor: uniform(baseColor.clone?.() ?? new THREE.Color(baseColor)),
|
|
145
|
+
opacity: uniform(opacity),
|
|
146
|
+
alphaCutoff: uniform(alphaCutoff),
|
|
147
|
+
useAlphaMap: uniform(textureSet.alphaMap ? 1.0 : 0.0),
|
|
148
|
+
alphaFromLuminance: uniform(textureSet.alphaFromLuminance ? 1.0 : 0.0),
|
|
149
|
+
foliageCutout: uniform(isFoliage ? 1.0 : 0.0),
|
|
150
|
+
usePackedMap: uniform(textureSet.packedMap ? 1.0 : 0.0),
|
|
151
|
+
windowCutout: uniform(0.0),
|
|
152
|
+
shadeStrength: uniform(hasSun ? 0.95 : 0.42),
|
|
153
|
+
shadeSoftness: uniform(hasSun ? 0.18 : 0.38),
|
|
154
|
+
ambientStrength: uniform(hasSun ? 0.0 : 0.98),
|
|
155
|
+
ambientLightInfluence: uniform(0.22),
|
|
156
|
+
directLightStrength: uniform(hasSun ? 1.35 : 0.42),
|
|
157
|
+
pointLightStrength: uniform(hasSun ? 0.72 : 0.18),
|
|
158
|
+
spotLightStrength: uniform(0.0),
|
|
159
|
+
exposure: uniform(hasSun ? 0.9 : 1.0),
|
|
160
|
+
lightingInfluence: uniform(hasSun ? 1.0 : 0.36),
|
|
161
|
+
saturation: uniform(1.12),
|
|
162
|
+
shadowLift: uniform(hasSun ? 0.0 : 0.66),
|
|
163
|
+
skyTintStrength: uniform(0.24),
|
|
164
|
+
emissiveStrength: uniform(hasSun ? 0.0 : isEmissive ? 0.55 : 0.0),
|
|
165
|
+
sunBoost: uniform(hasSun ? 0.08 : 0.0),
|
|
166
|
+
environmentCenter: uniform(environmentBox?.getCenter(new THREE.Vector3()) ?? new THREE.Vector3()),
|
|
167
|
+
environmentSize: uniform(environmentBox?.getSize(new THREE.Vector3()) ?? new THREE.Vector3(1, 1, 1)),
|
|
168
|
+
leftSideShadow: uniform(hasSun ? 0.92 : 0.0),
|
|
169
|
+
leftSideShadowColor: uniform(new THREE.Color(0.34, 0.29, 0.25)),
|
|
170
|
+
packedOcclusionStrength: uniform(0.22),
|
|
171
|
+
bakedGlowStrength: uniform(0.05),
|
|
172
|
+
shadowTintColor: uniform(new THREE.Color(0.86, 0.82, 0.78)),
|
|
173
|
+
skyGroundTint: uniform(new THREE.Color(1.05, 0.97, 0.9)),
|
|
174
|
+
skyTopTint: uniform(new THREE.Color(0.86, 0.96, 1.08)),
|
|
175
|
+
sunBoostColor: uniform(new THREE.Color(1.0, 0.78, 0.42)),
|
|
176
|
+
|
|
177
|
+
normalMapTex: texture(textureSet.normalMap ?? fallbackEnvironmentNormalTexture),
|
|
178
|
+
normalMapStrength: uniform(0.8),
|
|
179
|
+
normalMapScale: uniform(textureSet.normalScale ?? new THREE.Vector2(1, 1)),
|
|
180
|
+
aoMapTex: texture(textureSet.aoMap ?? fallbackEnvironmentWhiteTexture),
|
|
181
|
+
aoMapStrength: uniform(THREE.MathUtils.clamp(textureSet.aoMapIntensity ?? 1, 0, 2)),
|
|
182
|
+
aoWarmth: uniform(0.55),
|
|
183
|
+
lightMapTex: texture(textureSet.lightMap ?? fallbackEnvironmentBlackTexture),
|
|
184
|
+
lightMapStrength: uniform(THREE.MathUtils.clamp(textureSet.lightMapIntensity ?? 1, 0, 2)),
|
|
185
|
+
lightMapLift: uniform(0.1),
|
|
186
|
+
emissiveMapTex: texture(textureSet.emissiveMap ?? fallbackEnvironmentBlackTexture),
|
|
187
|
+
emissiveMapColor: uniform(textureSet.emissiveColor ?? new THREE.Color(1, 1, 1)),
|
|
188
|
+
// Sunlight dominates emissives by day; lamps carry the room at night.
|
|
189
|
+
emissiveMapStrength: uniform(hasSun ? 0.25 : 1.0),
|
|
190
|
+
vertexAoStrength: uniform(0.85),
|
|
191
|
+
untexturedGradientStrength: uniform(0.4),
|
|
192
|
+
specularStrength: uniform(isGlossFloor ? 0.35 : 0.0),
|
|
193
|
+
specularShininess: uniform(isGlossFloor ? 48 : 32),
|
|
194
|
+
specularSoftness: uniform(0.22),
|
|
195
|
+
specularColor: uniform(new THREE.Color(1, 1, 1)),
|
|
196
|
+
interiorOcclusionStrength: uniform(0.0),
|
|
197
|
+
interiorOcclusionColor: uniform(new THREE.Color(0.34, 0.29, 0.25)),
|
|
198
|
+
ambientProbeBlend: uniform(0.0),
|
|
199
|
+
heightFogDensity: uniform(0.0),
|
|
200
|
+
heightFogFalloff: uniform(6.0),
|
|
201
|
+
heightFogColor: uniform(new THREE.Color(0.75, 0.82, 0.92)),
|
|
202
|
+
planarReflectionStrength: uniform(isGlossFloor ? 0.3 : 0.0),
|
|
203
|
+
planarReflectionFresnel: uniform(2.4),
|
|
204
|
+
|
|
205
|
+
enableAlphaMap: uniform(1.0),
|
|
206
|
+
enableAlphaCutout: uniform(1.0),
|
|
207
|
+
enableFoliageCutout: uniform(1.0),
|
|
208
|
+
enablePackedMap: uniform(1.0),
|
|
209
|
+
enableWindowCutout: uniform(1.0),
|
|
210
|
+
enableDirectionalLights: uniform(1.0),
|
|
211
|
+
enablePointLights: uniform(1.0),
|
|
212
|
+
enableSpotLights: uniform(1.0),
|
|
213
|
+
enableShadowMask: uniform(1.0),
|
|
214
|
+
enableAmbientLight: uniform(1.0),
|
|
215
|
+
enableSkyTint: uniform(1.0),
|
|
216
|
+
enableLeftSideShadow: uniform(1.0),
|
|
217
|
+
enableEmissive: uniform(1.0),
|
|
218
|
+
enableSunBoost: uniform(1.0),
|
|
219
|
+
enableNormalMap: uniform(1.0),
|
|
220
|
+
enableAoMap: uniform(1.0),
|
|
221
|
+
enableLightMap: uniform(1.0),
|
|
222
|
+
enableVertexAo: uniform(1.0),
|
|
223
|
+
enableUntexturedGradient: uniform(1.0),
|
|
224
|
+
enableSpecular: uniform(1.0),
|
|
225
|
+
enableInteriorOcclusion: uniform(1.0),
|
|
226
|
+
enableAmbientProbe: uniform(1.0),
|
|
227
|
+
enableHeightFog: uniform(1.0),
|
|
228
|
+
enablePlanarReflection: uniform(1.0),
|
|
229
|
+
|
|
230
|
+
// Scene-wide state, shared by reference (the classic factory swaps these
|
|
231
|
+
// slots for the shared uniform objects the same way).
|
|
232
|
+
time: shared.time,
|
|
233
|
+
cloudShadowStrength: shared.cloudShadowStrength,
|
|
234
|
+
cloudShadowCoverage: shared.cloudShadowCoverage,
|
|
235
|
+
cloudShadowScale: shared.cloudShadowScale,
|
|
236
|
+
cloudShadowVelocity: shared.cloudShadowVelocity,
|
|
237
|
+
envDebugMode: shared.envDebugMode,
|
|
238
|
+
environmentOpenings: shared.environmentOpenings,
|
|
239
|
+
environmentOpeningCount: shared.environmentOpeningCount,
|
|
240
|
+
ambientProbe: shared.ambientProbe,
|
|
241
|
+
planarReflectionMap: shared.planarReflectionMap,
|
|
242
|
+
planarReflectionMatrix: shared.planarReflectionMatrix,
|
|
243
|
+
};
|
|
244
|
+
|
|
245
|
+
// Texture nodes referenced by the graph — only flag-gated maps are ever
|
|
246
|
+
// sampled, so absent maps never claim a texture unit.
|
|
247
|
+
const tex = {
|
|
248
|
+
alphaMap: u.alphaMap,
|
|
249
|
+
aoMapTex: u.aoMapTex,
|
|
250
|
+
baseMap: u.baseMap,
|
|
251
|
+
emissiveMapTex: u.emissiveMapTex,
|
|
252
|
+
lightMapTex: u.lightMapTex,
|
|
253
|
+
normalMapTex: u.normalMapTex,
|
|
254
|
+
packedMap: u.packedMap,
|
|
255
|
+
planarReflectionMap: shared.planarReflectionMap,
|
|
256
|
+
};
|
|
257
|
+
|
|
258
|
+
const material = new NodeMaterial();
|
|
259
|
+
material.name = 'ToonEnvironmentNode';
|
|
260
|
+
material.lights = false;
|
|
261
|
+
// TODO(tsl Phase 7): fog ordering — the GLSL applies scene fog AFTER the
|
|
262
|
+
// colorspace conversion (fog_fragment follows colorspace_fragment); the
|
|
263
|
+
// node pipeline would apply it before. Scene fog is disabled here until the
|
|
264
|
+
// fog phase resolves the ordering (the Phase 3 gate scene has no scene.fog).
|
|
265
|
+
material.fog = false;
|
|
266
|
+
material.side = side;
|
|
267
|
+
material.transparent = alphaBlend;
|
|
268
|
+
material.depthWrite = !alphaBlend;
|
|
269
|
+
material.alphaTest = 0; // alpha cutout is in-graph (alphaCutoff), like the GLSL
|
|
270
|
+
material.vertexColors = Boolean(flags.useVertexColors);
|
|
271
|
+
// Debug views are compiled in on this backend; the marker keeps
|
|
272
|
+
// setEnvironmentDebugOutput's define-flip loop from forcing a recompile.
|
|
273
|
+
material.defines = { ENV_DEBUG_VIEWS: 1 };
|
|
274
|
+
|
|
275
|
+
// ---- Vertex stage ----
|
|
276
|
+
// Varyings are declared up front and assigned imperatively inside the
|
|
277
|
+
// single vertexNode Fn (docs/tsl-conventions.md gotcha 4).
|
|
278
|
+
const vUv = varying(vec2(), 'vEnvUv');
|
|
279
|
+
const vUv2 = flags.useUv2 ? varying(vec2(), 'vEnvUv2') : null;
|
|
280
|
+
const vEnvVertexAo = flags.hasVertexAo ? varying(float(), 'vEnvVertexAo') : null;
|
|
281
|
+
const vWorldNormal = varying(vec3(), 'vEnvWorldNormal');
|
|
282
|
+
const vWorldPosition = varying(vec3(), 'vEnvWorldPosition');
|
|
283
|
+
const vViewPosition = varying(vec3(), 'vEnvViewPosition');
|
|
284
|
+
const vRoomUv = varying(vec2(), 'vEnvRoomUv');
|
|
285
|
+
|
|
286
|
+
material.vertexNode = Fn(() => {
|
|
287
|
+
vUv.assign(u.baseMapTransform.mul(vec3(uv(), 1.0)).xy);
|
|
288
|
+
if (flags.useUv2) vUv2.assign(attribute('uv1', 'vec2'));
|
|
289
|
+
if (flags.hasVertexAo) vEnvVertexAo.assign(attribute('envVertexAo', 'float'));
|
|
290
|
+
|
|
291
|
+
// GLSL: vWorldNormal = normalize(transformedNormal) — three's VIEW-space
|
|
292
|
+
// normal (see the quirk note in the header).
|
|
293
|
+
vWorldNormal.assign(normalize(transformNormalToView(normalLocal)));
|
|
294
|
+
|
|
295
|
+
const worldPosition = positionWorld.toVar();
|
|
296
|
+
vWorldPosition.assign(worldPosition);
|
|
297
|
+
// Room-normalized position for the side-shadow gradient; linear in world
|
|
298
|
+
// space, so interpolating it is exact and the fragment stage skips the
|
|
299
|
+
// per-pixel divide.
|
|
300
|
+
vRoomUv.assign(
|
|
301
|
+
worldPosition.sub(u.environmentCenter)
|
|
302
|
+
.div(max(u.environmentSize, vec3(0.001))).xy.add(0.5),
|
|
303
|
+
);
|
|
304
|
+
|
|
305
|
+
const mvPosition = cameraViewMatrix.mul(vec4(worldPosition, 1.0));
|
|
306
|
+
vViewPosition.assign(mvPosition.xyz.negate());
|
|
307
|
+
return cameraProjectionMatrix.mul(mvPosition);
|
|
308
|
+
})();
|
|
309
|
+
|
|
310
|
+
const shadowSideFor = {
|
|
311
|
+
[THREE.FrontSide]: THREE.BackSide,
|
|
312
|
+
[THREE.BackSide]: THREE.FrontSide,
|
|
313
|
+
[THREE.DoubleSide]: THREE.DoubleSide,
|
|
314
|
+
};
|
|
315
|
+
|
|
316
|
+
material.userData.createDepthColorVariant = () => {
|
|
317
|
+
const depthMaterial = new NodeMaterial();
|
|
318
|
+
depthMaterial.name = `${material.name || 'ToonEnvironmentNode'}Depth`;
|
|
319
|
+
depthMaterial.lights = false;
|
|
320
|
+
depthMaterial.fog = false;
|
|
321
|
+
depthMaterial.side = material.shadowSide ?? shadowSideFor[material.side] ?? THREE.DoubleSide;
|
|
322
|
+
depthMaterial.transparent = false;
|
|
323
|
+
depthMaterial.depthWrite = true;
|
|
324
|
+
depthMaterial.depthTest = material.depthTest;
|
|
325
|
+
depthMaterial.vertexColors = material.vertexColors;
|
|
326
|
+
depthMaterial.isShadowPassMaterial = true;
|
|
327
|
+
depthMaterial.vertexNode = material.vertexNode;
|
|
328
|
+
depthMaterial.fragmentNode = Fn(() => {
|
|
329
|
+
const texel = tex.baseMap.sample(vUv).toVar();
|
|
330
|
+
if (flags.useVertexColors) {
|
|
331
|
+
const vColor = vertexColor();
|
|
332
|
+
texel.a.mulAssign(vColor.a);
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
Discard(
|
|
336
|
+
u.enableFoliageCutout.greaterThan(0.5)
|
|
337
|
+
.and(u.foliageCutout.greaterThan(0.5))
|
|
338
|
+
.and(max3(texel.rgb).lessThan(0.11)),
|
|
339
|
+
);
|
|
340
|
+
const paneMask = windowPaneMask(texel.rgb).toVar();
|
|
341
|
+
Discard(
|
|
342
|
+
u.enableWindowCutout.greaterThan(0.5)
|
|
343
|
+
.and(u.windowCutout.greaterThan(0.5))
|
|
344
|
+
.and(paneMask.greaterThan(0.44)),
|
|
345
|
+
);
|
|
346
|
+
|
|
347
|
+
const alpha = texel.a.mul(u.opacity).toVar();
|
|
348
|
+
if (flags.hasAlphaMap) {
|
|
349
|
+
If(u.enableAlphaMap.greaterThan(0.5).and(u.useAlphaMap.greaterThan(0.5)), () => {
|
|
350
|
+
const alphaTexel = tex.alphaMap.sample(vUv).toVar();
|
|
351
|
+
const textureAlpha = select(
|
|
352
|
+
u.alphaFromLuminance.greaterThan(0.5),
|
|
353
|
+
max3(alphaTexel.rgb),
|
|
354
|
+
alphaTexel.a,
|
|
355
|
+
);
|
|
356
|
+
alpha.mulAssign(textureAlpha);
|
|
357
|
+
});
|
|
358
|
+
}
|
|
359
|
+
Discard(
|
|
360
|
+
u.enableAlphaCutout.greaterThan(0.5)
|
|
361
|
+
.and(u.alphaCutoff.greaterThanEqual(0.0))
|
|
362
|
+
.and(alpha.lessThan(u.alphaCutoff)),
|
|
363
|
+
);
|
|
364
|
+
|
|
365
|
+
const orthographic = cameraProjectionMatrix.element(3).w.equal(1.0);
|
|
366
|
+
const viewZ = vViewPosition.z.negate();
|
|
367
|
+
const depth01 = select(
|
|
368
|
+
orthographic,
|
|
369
|
+
viewZToOrthographicDepth(viewZ, cameraNear, cameraFar),
|
|
370
|
+
viewZToPerspectiveDepth(viewZ, cameraNear, cameraFar),
|
|
371
|
+
);
|
|
372
|
+
return vec4(vec3(depth01), 1.0);
|
|
373
|
+
})();
|
|
374
|
+
return depthMaterial;
|
|
375
|
+
};
|
|
376
|
+
|
|
377
|
+
// ---- Fragment stage ----
|
|
378
|
+
const lighting = createEnvironmentLightingChunk({
|
|
379
|
+
cameraViewMatrixNode: cameraViewMatrix,
|
|
380
|
+
flags,
|
|
381
|
+
tex,
|
|
382
|
+
u,
|
|
383
|
+
});
|
|
384
|
+
|
|
385
|
+
// #ifdef ENV_AO_UV2 / ENV_LIGHT_UV2 — compile-time uv channel selection.
|
|
386
|
+
const envAoUv = flags.aoUsesUv2 && vUv2 ? vUv2 : vUv;
|
|
387
|
+
const envLightUv = flags.lightUsesUv2 && vUv2 ? vUv2 : vUv;
|
|
388
|
+
|
|
389
|
+
material.fragmentNode = Fn(() => {
|
|
390
|
+
const texel = tex.baseMap.sample(vUv).toVar();
|
|
391
|
+
if (flags.useVertexColors) {
|
|
392
|
+
// USE_COLOR / USE_COLOR_ALPHA: vertexColor() yields w = 1 for vec3
|
|
393
|
+
// color attributes, so the alpha multiply is a no-op exactly when the
|
|
394
|
+
// classic pipeline compiled without USE_COLOR_ALPHA.
|
|
395
|
+
const vColor = vertexColor();
|
|
396
|
+
texel.rgb.mulAssign(vColor.rgb);
|
|
397
|
+
texel.a.mulAssign(vColor.a);
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
Discard(
|
|
401
|
+
u.enableFoliageCutout.greaterThan(0.5)
|
|
402
|
+
.and(u.foliageCutout.greaterThan(0.5))
|
|
403
|
+
.and(max3(texel.rgb).lessThan(0.11)),
|
|
404
|
+
);
|
|
405
|
+
const paneMask = windowPaneMask(texel.rgb).toVar();
|
|
406
|
+
Discard(
|
|
407
|
+
u.enableWindowCutout.greaterThan(0.5)
|
|
408
|
+
.and(u.windowCutout.greaterThan(0.5))
|
|
409
|
+
.and(paneMask.greaterThan(0.44)),
|
|
410
|
+
);
|
|
411
|
+
|
|
412
|
+
const alpha = texel.a.mul(u.opacity).toVar();
|
|
413
|
+
if (flags.hasAlphaMap) {
|
|
414
|
+
If(u.enableAlphaMap.greaterThan(0.5).and(u.useAlphaMap.greaterThan(0.5)), () => {
|
|
415
|
+
const alphaTexel = tex.alphaMap.sample(vUv).toVar();
|
|
416
|
+
const textureAlpha = select(
|
|
417
|
+
u.alphaFromLuminance.greaterThan(0.5),
|
|
418
|
+
max3(alphaTexel.rgb),
|
|
419
|
+
alphaTexel.a,
|
|
420
|
+
);
|
|
421
|
+
alpha.mulAssign(textureAlpha);
|
|
422
|
+
});
|
|
423
|
+
}
|
|
424
|
+
Discard(
|
|
425
|
+
u.enableAlphaCutout.greaterThan(0.5)
|
|
426
|
+
.and(u.alphaCutoff.greaterThanEqual(0.0))
|
|
427
|
+
.and(alpha.lessThan(u.alphaCutoff)),
|
|
428
|
+
);
|
|
429
|
+
|
|
430
|
+
const albedo = pow(max(texel.rgb.mul(u.baseColor), vec3(0.0)), vec3(0.92)).toVar();
|
|
431
|
+
|
|
432
|
+
const N = normalize(vWorldNormal)
|
|
433
|
+
.mul(select(frontFacing, float(1.0), float(-1.0)))
|
|
434
|
+
.toVar();
|
|
435
|
+
if (flags.hasNormalMap) {
|
|
436
|
+
If(u.enableNormalMap.greaterThan(0.5).and(u.normalMapStrength.greaterThan(0.0)), () => {
|
|
437
|
+
N.assign(lighting.perturbEnvironmentNormal(N, vWorldPosition, vUv));
|
|
438
|
+
});
|
|
439
|
+
}
|
|
440
|
+
|
|
441
|
+
const upward = N.y.mul(0.5).add(0.5);
|
|
442
|
+
const coolSkyTint = mix(u.skyGroundTint, u.skyTopTint, upward);
|
|
443
|
+
|
|
444
|
+
if (flags.hasUntextured) {
|
|
445
|
+
// Designed flat-color response: walls fall off gently toward the floor
|
|
446
|
+
// and up-facing surfaces pick up a hint of sky.
|
|
447
|
+
If(u.enableUntexturedGradient.greaterThan(0.5), () => {
|
|
448
|
+
const heightT = clamp(vRoomUv.y, 0.0, 1.0);
|
|
449
|
+
const floorDarkening = u.untexturedGradientStrength.mul(0.5)
|
|
450
|
+
.mul(smoothstep(0.0, 0.55, heightT).oneMinus())
|
|
451
|
+
.oneMinus();
|
|
452
|
+
albedo.mulAssign(floorDarkening);
|
|
453
|
+
albedo.mulAssign(mix(vec3(1.0), coolSkyTint, u.untexturedGradientStrength.mul(0.35)));
|
|
454
|
+
});
|
|
455
|
+
}
|
|
456
|
+
|
|
457
|
+
const directional = lighting.evaluateDirectionalLight(N);
|
|
458
|
+
const directLight = directional.light.mul(u.directLightStrength).toVar();
|
|
459
|
+
const strongestLight = directional.strongest;
|
|
460
|
+
|
|
461
|
+
// getShadowMask() → shared sun-shadow sampler, fed by
|
|
462
|
+
// environmentSunShadowPass on the node backends (mask = 1 until it runs).
|
|
463
|
+
// The Fn call is hoisted to statement level per the conventions doc's
|
|
464
|
+
// hoisting guidance (docs/tsl-conventions.md, select()/If call hazards);
|
|
465
|
+
// the enableShadowMask ternary becomes an If()-assigned var.
|
|
466
|
+
const sampledSunShadow = sampleEnvironmentSunShadow(vWorldPosition).toVar();
|
|
467
|
+
const sunlightVisibility = float(1.0).toVar();
|
|
468
|
+
If(u.enableShadowMask.greaterThan(0.5), () => {
|
|
469
|
+
sunlightVisibility.assign(sampledSunShadow);
|
|
470
|
+
});
|
|
471
|
+
// Drifting procedural cloud shadows over outdoor terrain; strength
|
|
472
|
+
// defaults to 0 so indoor scenes are untouched.
|
|
473
|
+
sunlightVisibility.mulAssign(stylizedCloudShadow(
|
|
474
|
+
vWorldPosition.xz, u.time,
|
|
475
|
+
u.cloudShadowStrength, u.cloudShadowCoverage, u.cloudShadowScale, u.cloudShadowVelocity,
|
|
476
|
+
));
|
|
477
|
+
directLight.mulAssign(sunlightVisibility);
|
|
478
|
+
strongestLight.mulAssign(sunlightVisibility);
|
|
479
|
+
|
|
480
|
+
const geometryPosition = vViewPosition.negate(); // GLSL passes -vViewPosition
|
|
481
|
+
const point = lighting.evaluatePointLights(N, geometryPosition);
|
|
482
|
+
const pointLight = point.light.mul(u.pointLightStrength).toVar();
|
|
483
|
+
const spot = lighting.evaluateSpotLights(N, geometryPosition);
|
|
484
|
+
const spotLight = spot.light.mul(u.spotLightStrength).toVar();
|
|
485
|
+
strongestLight.assign(max(strongestLight, point.strongest.mul(0.35)));
|
|
486
|
+
strongestLight.assign(max(strongestLight, spot.strongest.mul(0.35)));
|
|
487
|
+
|
|
488
|
+
const ambientBase = mix(vec3(1.0), toonSceneLights.ambientLightColor, u.ambientLightInfluence).toVar();
|
|
489
|
+
// Ambient probe reshapes ambient color toward the room's own palette;
|
|
490
|
+
// ambientStrength still owns the magnitude.
|
|
491
|
+
If(u.enableAmbientProbe.greaterThan(0.5).and(u.ambientProbeBlend.greaterThan(0.0)), () => {
|
|
492
|
+
ambientBase.assign(mix(ambientBase, lighting.environmentProbeIrradiance(N), u.ambientProbeBlend));
|
|
493
|
+
});
|
|
494
|
+
const ambient = ambientBase.mul(u.ambientStrength).mul(u.enableAmbientLight).toVar();
|
|
495
|
+
ambient.mulAssign(mix(vec3(1.0), coolSkyTint, u.skyTintStrength.mul(u.enableSkyTint)));
|
|
496
|
+
|
|
497
|
+
const dbgBakedGi = vec3(0.0).toVar();
|
|
498
|
+
if (flags.hasLightMap) {
|
|
499
|
+
// Baked GI replaces the flat ambient where a lightmap exists.
|
|
500
|
+
If(u.enableLightMap.greaterThan(0.5).and(u.lightMapStrength.greaterThan(0.0)), () => {
|
|
501
|
+
const baked = tex.lightMapTex.sample(envLightUv).rgb.mul(u.lightMapStrength).toVar();
|
|
502
|
+
baked.assign(mix(u.shadowTintColor.mul(baked), baked, smoothstep(0.0, 0.62, envLuma(baked))));
|
|
503
|
+
baked.assign(max(baked, vec3(u.lightMapLift)));
|
|
504
|
+
dbgBakedGi.assign(baked);
|
|
505
|
+
ambient.assign(mix(ambient, baked, clamp(u.lightMapStrength, 0.0, 1.0)));
|
|
506
|
+
strongestLight.assign(max(strongestLight, smoothstep(0.55, 1.0, envLuma(baked))));
|
|
507
|
+
});
|
|
508
|
+
}
|
|
509
|
+
|
|
510
|
+
// Baked occlusion multiplies indirect light only, tinted warm so shade
|
|
511
|
+
// reads painted rather than gray.
|
|
512
|
+
const aoMul = vec3(1.0).toVar();
|
|
513
|
+
const warmOcclusionFloor = mix(vec3(0.0), u.shadowTintColor, u.aoWarmth);
|
|
514
|
+
const dbgVertexAo = float(1.0).toVar();
|
|
515
|
+
if (flags.hasAoMap) {
|
|
516
|
+
If(u.enableAoMap.greaterThan(0.5).and(u.aoMapStrength.greaterThan(0.0)), () => {
|
|
517
|
+
const aoSample = tex.aoMapTex.sample(envAoUv).r;
|
|
518
|
+
const ao = mix(1.0, aoSample, u.aoMapStrength);
|
|
519
|
+
aoMul.mulAssign(mix(warmOcclusionFloor, vec3(1.0), ao));
|
|
520
|
+
});
|
|
521
|
+
}
|
|
522
|
+
if (flags.hasVertexAo) {
|
|
523
|
+
If(u.enableVertexAo.greaterThan(0.5).and(u.vertexAoStrength.greaterThan(0.0)), () => {
|
|
524
|
+
dbgVertexAo.assign(vEnvVertexAo);
|
|
525
|
+
const vao = mix(1.0, vEnvVertexAo, u.vertexAoStrength);
|
|
526
|
+
aoMul.mulAssign(mix(warmOcclusionFloor, vec3(1.0), vao));
|
|
527
|
+
});
|
|
528
|
+
}
|
|
529
|
+
ambient.mulAssign(aoMul);
|
|
530
|
+
|
|
531
|
+
const litColor = albedo.mul(ambient.add(directLight).add(pointLight).add(spotLight)).toVar();
|
|
532
|
+
|
|
533
|
+
const dbgSpecular = vec3(0.0).toVar();
|
|
534
|
+
If(u.enableSpecular.greaterThan(0.5).and(u.specularStrength.greaterThan(0.0)), () => {
|
|
535
|
+
dbgSpecular.assign(
|
|
536
|
+
lighting.evaluateEnvironmentSpecular(N, geometryPosition, sunlightVisibility)
|
|
537
|
+
.mul(u.specularStrength).mul(u.specularColor),
|
|
538
|
+
);
|
|
539
|
+
litColor.addAssign(dbgSpecular);
|
|
540
|
+
});
|
|
541
|
+
|
|
542
|
+
const color = mix(albedo, litColor, u.lightingInfluence).toVar();
|
|
543
|
+
const sunBand = smoothstep(0.45, 0.9, strongestLight);
|
|
544
|
+
color.addAssign(albedo.mul(u.sunBoostColor).mul(sunBand).mul(u.sunBoost).mul(u.enableSunBoost));
|
|
545
|
+
|
|
546
|
+
// Legacy Liyue-tuned left-edge gradient; superseded by openings-based
|
|
547
|
+
// interior occlusion but preserved for the approved room look.
|
|
548
|
+
const leftDarkness = smoothstep(0.16, 0.72, vRoomUv.x).oneMinus()
|
|
549
|
+
.mul(smoothstep(0.78, 1.08, vRoomUv.y).oneMinus())
|
|
550
|
+
.toVar();
|
|
551
|
+
color.mulAssign(mix(
|
|
552
|
+
vec3(1.0),
|
|
553
|
+
u.leftSideShadowColor,
|
|
554
|
+
leftDarkness.mul(u.leftSideShadow).mul(u.enableLeftSideShadow),
|
|
555
|
+
));
|
|
556
|
+
|
|
557
|
+
// Interiors darken away from registered openings; inert with no openings.
|
|
558
|
+
const dbgInteriorDark = float(0.0).toVar();
|
|
559
|
+
If(
|
|
560
|
+
u.enableInteriorOcclusion.greaterThan(0.5)
|
|
561
|
+
.and(u.interiorOcclusionStrength.greaterThan(0.0))
|
|
562
|
+
.and(u.environmentOpeningCount.greaterThan(0.5)),
|
|
563
|
+
() => {
|
|
564
|
+
const openingLight = float(0.0).toVar();
|
|
565
|
+
for (let i = 0; i < 4; i += 1) {
|
|
566
|
+
If(float(i).lessThan(u.environmentOpeningCount), () => {
|
|
567
|
+
const opening = u.environmentOpenings.element(i);
|
|
568
|
+
const reach = max(opening.w, 0.001);
|
|
569
|
+
const dist = distance(vWorldPosition, opening.xyz);
|
|
570
|
+
openingLight.assign(max(
|
|
571
|
+
openingLight,
|
|
572
|
+
smoothstep(reach.mul(0.35), reach.mul(1.45), dist).oneMinus(),
|
|
573
|
+
));
|
|
574
|
+
});
|
|
575
|
+
}
|
|
576
|
+
dbgInteriorDark.assign(openingLight.oneMinus().mul(u.interiorOcclusionStrength));
|
|
577
|
+
color.mulAssign(mix(vec3(1.0), u.interiorOcclusionColor, dbgInteriorDark));
|
|
578
|
+
},
|
|
579
|
+
);
|
|
580
|
+
|
|
581
|
+
if (flags.hasPackedMap) {
|
|
582
|
+
If(u.enablePackedMap.greaterThan(0.5).and(u.usePackedMap.greaterThan(0.5)), () => {
|
|
583
|
+
const packed = tex.packedMap.sample(vUv).rgb.toVar();
|
|
584
|
+
const packedBrightness = max3(packed);
|
|
585
|
+
const materialOcclusion = mix(1.0, clamp(packed.r.mul(1.18), 0.62, 1.08), u.packedOcclusionStrength);
|
|
586
|
+
const bakedGlow = smoothstep(0.62, 0.96, max(packed.r, packed.g));
|
|
587
|
+
color.mulAssign(materialOcclusion);
|
|
588
|
+
color.addAssign(albedo.mul(bakedGlow).mul(u.bakedGlowStrength));
|
|
589
|
+
strongestLight.assign(max(strongestLight, smoothstep(0.58, 1.0, packedBrightness).mul(0.58)));
|
|
590
|
+
});
|
|
591
|
+
}
|
|
592
|
+
|
|
593
|
+
const liftedShadow = max(strongestLight, u.shadowLift);
|
|
594
|
+
color.mulAssign(mix(u.shadowTintColor, vec3(1.0), liftedShadow));
|
|
595
|
+
|
|
596
|
+
const emissiveMask = float(0.0).toVar();
|
|
597
|
+
if (flags.hasEmissiveMap) {
|
|
598
|
+
// A real emissive map replaces the bright-albedo luminance guess.
|
|
599
|
+
const emissiveTexel = tex.emissiveMapTex.sample(vUv).rgb
|
|
600
|
+
.mul(u.emissiveMapColor).mul(u.emissiveMapStrength);
|
|
601
|
+
color.addAssign(emissiveTexel.mul(u.enableEmissive));
|
|
602
|
+
emissiveMask.assign(envLuma(emissiveTexel));
|
|
603
|
+
} else {
|
|
604
|
+
emissiveMask.assign(smoothstep(0.68, 1.0, max3(albedo)));
|
|
605
|
+
color.addAssign(albedo.mul(emissiveMask).mul(u.emissiveStrength).mul(u.enableEmissive));
|
|
606
|
+
emissiveMask.mulAssign(u.emissiveStrength);
|
|
607
|
+
}
|
|
608
|
+
|
|
609
|
+
if (flags.hasPlanarReflection) {
|
|
610
|
+
// Mirrored-scene reflection for glossy floors; fresnel keeps it
|
|
611
|
+
// strongest at grazing angles like lacquer.
|
|
612
|
+
If(u.enablePlanarReflection.greaterThan(0.5).and(u.planarReflectionStrength.greaterThan(0.0)), () => {
|
|
613
|
+
const reflectionUv = u.planarReflectionMatrix.mul(vec4(vWorldPosition, 1.0));
|
|
614
|
+
// texture2DProj(map, uv4) — divide by w; RT-fed textures sample at
|
|
615
|
+
// explicit level 0 (docs/tsl-conventions.md gotcha 9).
|
|
616
|
+
const reflection = tex.planarReflectionMap
|
|
617
|
+
.sample(reflectionUv.xy.div(reflectionUv.w)).level(0).rgb;
|
|
618
|
+
const toCamera = normalize(cameraPosition.sub(vWorldPosition));
|
|
619
|
+
const fresnel = pow(
|
|
620
|
+
clamp(dot(N, toCamera), 0.0, 1.0).oneMinus(),
|
|
621
|
+
max(u.planarReflectionFresnel, 0.001),
|
|
622
|
+
);
|
|
623
|
+
color.assign(mix(
|
|
624
|
+
color,
|
|
625
|
+
reflection,
|
|
626
|
+
clamp(u.planarReflectionStrength.mul(mix(0.16, 1.0, fresnel)), 0.0, 1.0),
|
|
627
|
+
));
|
|
628
|
+
});
|
|
629
|
+
}
|
|
630
|
+
|
|
631
|
+
color.assign(applySaturation(color.mul(u.exposure), u.saturation));
|
|
632
|
+
|
|
633
|
+
// World-height fog: dense near the environment floor, fading with height.
|
|
634
|
+
If(u.enableHeightFog.greaterThan(0.5).and(u.heightFogDensity.greaterThan(0.0)), () => {
|
|
635
|
+
const floorY = u.environmentCenter.y.sub(u.environmentSize.y.mul(0.5));
|
|
636
|
+
const heightFalloff = exp(
|
|
637
|
+
max(vWorldPosition.y.sub(floorY), 0.0).div(max(u.heightFogFalloff, 0.001)).negate(),
|
|
638
|
+
);
|
|
639
|
+
const depthTerm = exp(length(vViewPosition).mul(u.heightFogDensity).negate()).oneMinus();
|
|
640
|
+
color.assign(mix(color, u.heightFogColor, clamp(depthTerm.mul(heightFalloff), 0.0, 1.0)));
|
|
641
|
+
});
|
|
642
|
+
|
|
643
|
+
const finalColor = vec4(max(color, vec3(0.0)), alpha);
|
|
644
|
+
|
|
645
|
+
// Debug table (masked sum). envDebugMode 0 matches no entry, so the
|
|
646
|
+
// result is exactly finalColor during normal rendering — the ENV_DEBUG_VIEWS
|
|
647
|
+
// define flip becomes a pure uniform write on this backend.
|
|
648
|
+
return environmentDebugColor({
|
|
649
|
+
albedo,
|
|
650
|
+
alpha,
|
|
651
|
+
ambient,
|
|
652
|
+
aoMul,
|
|
653
|
+
bakedGi: dbgBakedGi,
|
|
654
|
+
directLight,
|
|
655
|
+
emissiveMask,
|
|
656
|
+
finalColor,
|
|
657
|
+
litColor,
|
|
658
|
+
mode: u.envDebugMode,
|
|
659
|
+
normal: N,
|
|
660
|
+
pointLight,
|
|
661
|
+
roomOcclusion: clamp(
|
|
662
|
+
dbgInteriorDark.add(leftDarkness.mul(u.leftSideShadow).mul(u.enableLeftSideShadow)),
|
|
663
|
+
0.0,
|
|
664
|
+
1.0,
|
|
665
|
+
),
|
|
666
|
+
specular: dbgSpecular,
|
|
667
|
+
spotLight,
|
|
668
|
+
sunlightVisibility,
|
|
669
|
+
vertexAo: dbgVertexAo,
|
|
670
|
+
windowMask: paneMask,
|
|
671
|
+
});
|
|
672
|
+
})();
|
|
673
|
+
|
|
674
|
+
// Same-name uniform slots (ShaderMaterial-compatible `.value` access).
|
|
675
|
+
material.uniforms = u;
|
|
676
|
+
material.userData.isEnvironmentNodeMaterial = true;
|
|
677
|
+
material.userData.environmentTslFlags = { ...flags };
|
|
678
|
+
|
|
679
|
+
return material;
|
|
680
|
+
}
|