@call-me-sensei/toonlab 0.2.0 → 0.3.0
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/AGENTS.md +127 -4
- package/ATTRIBUTION.md +41 -3
- package/README.md +234 -34
- package/docs/characters.md +144 -0
- package/docs/debug-panel.md +126 -0
- package/docs/docs.css +589 -0
- package/docs/environment.md +186 -0
- package/docs/getting-started.md +180 -0
- package/docs/index.html +14 -0
- package/docs/lab-architecture.md +100 -0
- package/docs/lighting.md +825 -0
- package/docs/main.jsx +739 -0
- package/docs/mcp.md +97 -0
- package/docs/post-processing.md +98 -0
- package/docs/settings-reference.md +1878 -0
- package/docs/shader-constants.md +77 -0
- package/docs/sky.md +182 -0
- package/docs/style-labs.md +309 -0
- package/docs/texture-lab.md +135 -0
- package/docs/toon-shading.md +183 -0
- package/docs/tsl-conventions.md +167 -0
- package/docs/vegetation-sky.md +275 -0
- package/docs/water.md +430 -0
- package/docs/weather.md +200 -0
- package/docs/world-scale.md +111 -0
- package/mcp/server.mjs +610 -0
- package/mcp/style-lab-tools.mjs +371 -0
- package/mcp/vite-plugin.mjs +174 -0
- package/mcp/workspace.mjs +397 -0
- package/package.json +64 -5
- package/src/ambientfx/INTEGRATION.md +164 -0
- package/src/ambientfx/ambientFxPresets.js +83 -0
- package/src/ambientfx/ambientFxSettings.js +368 -0
- package/src/ambientfx/emitters.js +169 -0
- package/src/ambientfx/index.js +5 -0
- package/src/ambientfx/particleBackbone.js +493 -0
- package/src/ambientfx/stylizedAmbientFx.js +450 -0
- package/src/assetlib/ambientcg.js +117 -0
- package/src/assetlib/assetRef.js +91 -0
- package/src/assetlib/importedEntry.js +40 -0
- package/src/assetlib/index.js +23 -0
- package/src/assetlib/kaykit.js +192 -0
- package/src/assetlib/kaykitStaticIndex.js +700 -0
- package/src/assetlib/loadImported.js +143 -0
- package/src/assetlib/opensource3d.js +113 -0
- package/src/assetlib/polyhaven.js +182 -0
- package/src/assetlib/polypizza.js +115 -0
- package/src/assetlib/smithsonian.js +214 -0
- package/src/assetlib/sources.js +279 -0
- package/src/assetlib/zip.js +58 -0
- package/src/biome/biomeGenerator.js +385 -0
- package/src/biome/biomeRuntime.js +299 -0
- package/src/biome/index.js +2 -0
- package/src/buildinggen/buildingAsset.js +44 -0
- package/src/buildinggen/buildingGrammar.js +311 -0
- package/src/buildinggen/buildingMesh.js +451 -0
- package/src/buildinggen/buildingPresets.js +46 -0
- package/src/buildinggen/buildingRecipe.js +100 -0
- package/src/buildinggen/buildingSettings.js +238 -0
- package/src/buildinggen/index.js +6 -0
- package/src/camera/cameraDirector.js +157 -0
- package/src/camera/cameraGenerator.js +367 -0
- package/src/camera/cameraRig.js +570 -0
- package/src/camera/cameraSettings.js +236 -0
- package/src/camera/index.js +7 -0
- package/src/catalog/builtinEntries.js +245 -0
- package/src/catalog/catalog.js +210 -0
- package/src/catalog/index.js +3 -0
- package/src/catalog/manifest.js +84 -0
- package/src/core/generation.js +529 -0
- package/src/environment/environmentRigs.js +21 -1
- package/src/environment/environmentSettings.js +4 -0
- package/src/environment/environmentSunShadowPass.js +8 -0
- package/src/fauna/INTEGRATION.md +174 -0
- package/src/fauna/boids.js +861 -0
- package/src/fauna/faunaBodies.js +492 -0
- package/src/fauna/faunaPresets.js +52 -0
- package/src/fauna/faunaSettings.js +525 -0
- package/src/fauna/index.js +5 -0
- package/src/fauna/stylizedFauna.js +395 -0
- package/src/game-feel/gameFeelGenerator.js +402 -0
- package/src/game-feel/gameFeelRuntime.js +549 -0
- package/src/game-feel/index.js +2 -0
- package/src/index.js +17 -6
- package/src/lighting/colorIntensity.js +177 -0
- package/src/lighting/index.js +161 -0
- package/src/lighting/lightDescriptors.js +249 -0
- package/src/lighting/lightingCapabilities.js +79 -0
- package/src/lighting/lightingDocuments.js +247 -0
- package/src/lighting/lightingFixtures.js +446 -0
- package/src/lighting/lightingGenerator.js +449 -0
- package/src/lighting/lightingPresets.js +319 -0
- package/src/lighting/lightingRuntime.js +723 -0
- package/src/lighting/lightingStyle.js +386 -0
- package/src/lighting/lightingSystem.js +774 -0
- package/src/lighting/unrealExport.js +186 -0
- package/src/lighting/utils.js +87 -0
- package/src/motion/index.js +5 -0
- package/src/motion/motionClip.js +441 -0
- package/src/motion/motionController.js +628 -0
- package/src/motion/motionDocuments.js +225 -0
- package/src/motion/motionGraph.js +307 -0
- package/src/motion/motionSettings.js +222 -0
- package/src/pathgen/index.js +7 -0
- package/src/pathgen/pathBridge.js +232 -0
- package/src/pathgen/pathPresets.js +35 -0
- package/src/pathgen/pathRibbon.js +410 -0
- package/src/pathgen/pathRouter.js +380 -0
- package/src/pathgen/pathSettings.js +335 -0
- package/src/pathgen/pathTextures.js +123 -0
- package/src/pathgen/stylizedPaths.js +453 -0
- package/src/post/index.js +1 -0
- package/src/post/postGenerator.js +177 -0
- package/src/post/postProcessing.js +41 -0
- package/src/propgen/generatorsWave1.js +379 -0
- package/src/propgen/generatorsWave2.js +462 -0
- package/src/propgen/index.js +5 -0
- package/src/propgen/propAsset.js +323 -0
- package/src/propgen/propParts.js +170 -0
- package/src/propgen/propPlacement.js +459 -0
- package/src/propgen/propPresets.js +82 -0
- package/src/propgen/propSettings.js +395 -0
- package/src/shaders-tsl/chunks/projected-water-caustics.js +242 -0
- package/src/shaders-tsl/chunks/vegetation-style.js +360 -0
- package/src/shaders-tsl/chunks/water-shore-state.js +31 -0
- package/src/shaders-tsl/chunks/water-waves.js +90 -11
- package/src/shaders-tsl/environment.js +15 -1
- package/src/shaders-tsl/flower.js +279 -30
- package/src/shaders-tsl/grass.js +60 -33
- package/src/shaders-tsl/sky.js +125 -31
- package/src/shaders-tsl/tree-leaf.js +61 -25
- package/src/shaders-tsl/water-breaker.js +7 -4
- package/src/shaders-tsl/water-shore-state-simulation.js +523 -0
- package/src/shaders-tsl/water.js +439 -49
- package/src/shaders-tsl/woody-surface.js +154 -0
- package/src/sky/sceneOverrideLayers.js +10 -0
- package/src/sky/skyQuality.js +26 -0
- package/src/sky/stylizedSky.js +753 -45
- package/src/soundscape/index.js +4 -0
- package/src/soundscape/soundscapeGenerator.js +179 -0
- package/src/soundscape/soundscapeRuntime.js +806 -0
- package/src/soundscape/soundscapeSettings.js +292 -0
- package/src/styles/index.js +13 -0
- package/src/styles/styleBundle.js +325 -0
- package/src/stylizedTerrain.js +32 -2
- package/src/stylizedWorld.js +423 -20
- package/src/texgen/evaluateTexture.js +675 -0
- package/src/texgen/index.js +60 -0
- package/src/texgen/noise2.js +210 -0
- package/src/texgen/textureAi.js +436 -0
- package/src/texgen/textureGenerators.js +516 -0
- package/src/texgen/texturePresets.js +490 -0
- package/src/texgen/textureSettings.js +342 -0
- package/src/texgen/textureThree.js +59 -0
- package/src/vegetation/flowerSpecies.js +15 -3
- package/src/vegetation/grassPalettes.js +153 -0
- package/src/vegetation/index.js +6 -0
- package/src/vegetation/stylizedBush.js +2 -0
- package/src/vegetation/stylizedFlower.js +82 -0
- package/src/vegetation/stylizedFlowers.js +48 -7
- package/src/vegetation/stylizedForest.js +29 -1
- package/src/vegetation/stylizedGrass.js +291 -56
- package/src/vegetation/stylizedTree.js +38 -2
- package/src/vegetation/stylizedTreeFoliage.js +2 -1
- package/src/vegetation/vegetationShaders.js +1110 -0
- package/src/vfxgen/INTEGRATION.md +145 -0
- package/src/vfxgen/core/burstBackbone.js +380 -0
- package/src/vfxgen/core/projectileCore.js +92 -0
- package/src/vfxgen/core/spriteShapes.js +98 -0
- package/src/vfxgen/core/trailRibbon.js +272 -0
- package/src/vfxgen/core/vfxRandom.js +29 -0
- package/src/vfxgen/effects/emitHelpers.js +37 -0
- package/src/vfxgen/effects/magicEffects.js +162 -0
- package/src/vfxgen/effects/movementEffects.js +87 -0
- package/src/vfxgen/effects/weaponEffects.js +118 -0
- package/src/vfxgen/index.js +18 -0
- package/src/vfxgen/moves/moveController.js +146 -0
- package/src/vfxgen/moves/moveLibrary.js +335 -0
- package/src/vfxgen/vfxPresets.js +98 -0
- package/src/vfxgen/vfxSettings.js +384 -0
- package/src/vfxgen/vfxSystem.js +449 -0
- package/src/vfxgen/weapons/stylizedWeapons.js +137 -0
- package/src/villagegen/index.js +4 -0
- package/src/villagegen/stylizedVillage.js +490 -0
- package/src/villagegen/villageArchetypes.js +160 -0
- package/src/villagegen/villageNames.js +40 -0
- package/src/villagegen/villageSites.js +105 -0
- package/src/water/sceneOverrideLayers.js +23 -0
- package/src/water/water.js +5 -0
- package/src/water/waterBreakerSystem.js +15 -1
- package/src/water/waterCurrentField.js +447 -0
- package/src/water/waterMaterial.js +12 -0
- package/src/water/waterNearshorePhase.js +320 -0
- package/src/water/waterScenePasses.js +83 -30
- package/src/water/waterSettings.js +325 -13
- package/src/water/waterShoreMaterial.js +322 -0
- package/src/water/waterShoreStateField.js +605 -0
- package/src/water/waterSurface.js +797 -28
- package/src/weather/index.js +6 -0
- package/src/weather/weatherPrecipitation.js +221 -0
- package/src/weather/weatherPresets.js +258 -0
- package/src/weather/weatherSettings.js +269 -0
- package/src/weather/weatherSystem.js +871 -0
- package/src/worldMinimap.js +62 -0
- package/src/worldPresets.js +4 -1
|
@@ -0,0 +1,360 @@
|
|
|
1
|
+
// Shared vegetation style nodes. These uniforms describe the IP's rendering
|
|
2
|
+
// treatment; current sun, wind, weather amounts, albedo, and interaction stay
|
|
3
|
+
// in their own namespaces so applying a style cannot overwrite world state.
|
|
4
|
+
|
|
5
|
+
import * as THREE from 'three';
|
|
6
|
+
import {
|
|
7
|
+
abs,
|
|
8
|
+
cameraPosition,
|
|
9
|
+
clamp,
|
|
10
|
+
dot,
|
|
11
|
+
faceDirection,
|
|
12
|
+
mix,
|
|
13
|
+
normalize,
|
|
14
|
+
pow,
|
|
15
|
+
smoothstep,
|
|
16
|
+
uniform,
|
|
17
|
+
vec3,
|
|
18
|
+
} from 'three/tsl';
|
|
19
|
+
|
|
20
|
+
const FALLBACK = Object.freeze({
|
|
21
|
+
lighting: Object.freeze({
|
|
22
|
+
rimPower: 3,
|
|
23
|
+
rimStrength: 0.12,
|
|
24
|
+
shadowTint: Object.freeze([0.36, 0.4, 0.58]),
|
|
25
|
+
shadowTintStrength: 1,
|
|
26
|
+
skyFillStrength: 0.08,
|
|
27
|
+
sunTintStrength: 0.25,
|
|
28
|
+
}),
|
|
29
|
+
thinSurface: Object.freeze({
|
|
30
|
+
diffuseWrap: 0.5,
|
|
31
|
+
normalUpBias: 0,
|
|
32
|
+
transmissionPower: 3.5,
|
|
33
|
+
transmissionShadowFloor: 0.35,
|
|
34
|
+
transmissionStrength: 0.35,
|
|
35
|
+
twoSidedLighting: 1,
|
|
36
|
+
}),
|
|
37
|
+
weatherResponse: Object.freeze({
|
|
38
|
+
snowEdgeSoftness: 0.2,
|
|
39
|
+
snowShadowStrength: 0.65,
|
|
40
|
+
snowTint: Object.freeze([0.92, 0.96, 1]),
|
|
41
|
+
wetDarkening: 0.15,
|
|
42
|
+
wetDesaturation: 0.05,
|
|
43
|
+
wetHighlightStrength: 0.2,
|
|
44
|
+
}),
|
|
45
|
+
grass: Object.freeze({
|
|
46
|
+
backlitStrength: 0.4,
|
|
47
|
+
bandSoftness: 0.1,
|
|
48
|
+
bandThreshold: 0.49,
|
|
49
|
+
bendExponent: 2,
|
|
50
|
+
cloudShadowResponse: 0.35,
|
|
51
|
+
colorVariationStrength: 0.2,
|
|
52
|
+
gustSheenStrength: 0.22,
|
|
53
|
+
gustSheenThreshold: 0.78,
|
|
54
|
+
interactionResponse: 1,
|
|
55
|
+
rootOcclusionHeight: 0.62,
|
|
56
|
+
rootOcclusionStrength: 0.36,
|
|
57
|
+
sceneShadowResponse: 0.7,
|
|
58
|
+
shadowFloor: 0.35,
|
|
59
|
+
tipGradientEnd: 0.95,
|
|
60
|
+
tipGradientStart: 0.1,
|
|
61
|
+
}),
|
|
62
|
+
foliage: Object.freeze({
|
|
63
|
+
backlitStrength: 0.35,
|
|
64
|
+
bandSoftness: 0.18,
|
|
65
|
+
bandThreshold: 0.47,
|
|
66
|
+
cardVariationStrength: 0.16,
|
|
67
|
+
cloudShadowResponse: 0,
|
|
68
|
+
crestSoftness: 0.12,
|
|
69
|
+
crestThreshold: 0.72,
|
|
70
|
+
crownOcclusionStrength: 0.2,
|
|
71
|
+
sceneShadowResponse: 0.55,
|
|
72
|
+
spriteLuminanceStrength: 0.36,
|
|
73
|
+
transmissionPowerMultiplier: 1,
|
|
74
|
+
}),
|
|
75
|
+
flower: Object.freeze({
|
|
76
|
+
backlitStrength: 0.35,
|
|
77
|
+
bandSoftness: 0.1,
|
|
78
|
+
bandThreshold: 0.5,
|
|
79
|
+
centerLightResponse: 0.8,
|
|
80
|
+
centerShadowResponse: 1,
|
|
81
|
+
cupDarkeningStrength: 0.1,
|
|
82
|
+
petalTransmissionMultiplier: 1,
|
|
83
|
+
sceneShadowResponse: 0.85,
|
|
84
|
+
unlitPetalLift: 0.35,
|
|
85
|
+
}),
|
|
86
|
+
bark: Object.freeze({
|
|
87
|
+
bandCount: 3,
|
|
88
|
+
bandSoftness: 0,
|
|
89
|
+
rimStrength: 0,
|
|
90
|
+
shadowFloor: 0.35,
|
|
91
|
+
skyFillStrength: 0.04,
|
|
92
|
+
specularStrength: 0,
|
|
93
|
+
sunTintStrength: 0.15,
|
|
94
|
+
verticalShadeStrength: 0,
|
|
95
|
+
}),
|
|
96
|
+
stem: Object.freeze({
|
|
97
|
+
bandCount: 3,
|
|
98
|
+
bandSoftness: 0.08,
|
|
99
|
+
shadowFloor: 0.42,
|
|
100
|
+
skyFillStrength: 0.06,
|
|
101
|
+
transmissionStrength: 0.08,
|
|
102
|
+
rimStrength: 0.02,
|
|
103
|
+
}),
|
|
104
|
+
});
|
|
105
|
+
|
|
106
|
+
function sourceSettings(profile) {
|
|
107
|
+
return profile?.settings && typeof profile.settings === 'object' ? profile.settings : (profile ?? {});
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
function value(settings, group, key) {
|
|
111
|
+
return settings?.[group]?.[key] ?? FALLBACK[group][key];
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
function color(settings, group, key) {
|
|
115
|
+
const rgb = value(settings, group, key);
|
|
116
|
+
return new THREE.Color().setRGB(rgb[0], rgb[1], rgb[2], THREE.SRGBColorSpace);
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
/** Uniform contract shared by every vegetation material role. */
|
|
120
|
+
export function createVegetationStyleUniforms(profile = {}, role = null) {
|
|
121
|
+
const settings = sourceSettings(profile);
|
|
122
|
+
const roles = new Set((Array.isArray(role) ? role : [role]).filter(Boolean));
|
|
123
|
+
const hasRole = (...candidates) => candidates.some((candidate) => roles.has(candidate));
|
|
124
|
+
const u = {
|
|
125
|
+
uSnowCover: uniform(0),
|
|
126
|
+
uSnowRetention: uniform(1),
|
|
127
|
+
uStyleLightingRimPower: uniform(value(settings, 'lighting', 'rimPower')),
|
|
128
|
+
uStyleLightingRimStrength: uniform(value(settings, 'lighting', 'rimStrength')),
|
|
129
|
+
uStyleLightingShadowTint: uniform(color(settings, 'lighting', 'shadowTint')),
|
|
130
|
+
uStyleLightingShadowTintStrength: uniform(value(settings, 'lighting', 'shadowTintStrength')),
|
|
131
|
+
uStyleLightingSkyFillStrength: uniform(value(settings, 'lighting', 'skyFillStrength')),
|
|
132
|
+
uStyleLightingSunTintStrength: uniform(value(settings, 'lighting', 'sunTintStrength')),
|
|
133
|
+
uStyleWeatherResponseSnowEdgeSoftness: uniform(value(settings, 'weatherResponse', 'snowEdgeSoftness')),
|
|
134
|
+
uStyleWeatherResponseSnowShadowStrength: uniform(value(settings, 'weatherResponse', 'snowShadowStrength')),
|
|
135
|
+
uStyleWeatherResponseSnowTint: uniform(color(settings, 'weatherResponse', 'snowTint')),
|
|
136
|
+
uStyleWeatherResponseWetDarkening: uniform(value(settings, 'weatherResponse', 'wetDarkening')),
|
|
137
|
+
uStyleWeatherResponseWetDesaturation: uniform(value(settings, 'weatherResponse', 'wetDesaturation')),
|
|
138
|
+
uStyleWeatherResponseWetHighlightStrength: uniform(value(settings, 'weatherResponse', 'wetHighlightStrength')),
|
|
139
|
+
uWetness: uniform(0),
|
|
140
|
+
uWetnessResponse: uniform(1),
|
|
141
|
+
};
|
|
142
|
+
|
|
143
|
+
if (hasRole('grassBlade', 'foliageCard', 'flowerPetal')) Object.assign(u, {
|
|
144
|
+
uStyleThinSurfaceDiffuseWrap: uniform(value(settings, 'thinSurface', 'diffuseWrap')),
|
|
145
|
+
uStyleThinSurfaceNormalUpBias: uniform(value(settings, 'thinSurface', 'normalUpBias')),
|
|
146
|
+
uStyleThinSurfaceTransmissionPower: uniform(value(settings, 'thinSurface', 'transmissionPower')),
|
|
147
|
+
uStyleThinSurfaceTransmissionShadowFloor: uniform(value(settings, 'thinSurface', 'transmissionShadowFloor')),
|
|
148
|
+
uStyleThinSurfaceTransmissionStrength: uniform(value(settings, 'thinSurface', 'transmissionStrength')),
|
|
149
|
+
uStyleThinSurfaceTwoSidedLighting: uniform(value(settings, 'thinSurface', 'twoSidedLighting')),
|
|
150
|
+
});
|
|
151
|
+
if (hasRole('grassBlade')) Object.assign(u, {
|
|
152
|
+
uStyleGrassBacklitStrength: uniform(value(settings, 'grass', 'backlitStrength')),
|
|
153
|
+
uStyleGrassBandSoftness: uniform(value(settings, 'grass', 'bandSoftness')),
|
|
154
|
+
uStyleGrassBandThreshold: uniform(value(settings, 'grass', 'bandThreshold')),
|
|
155
|
+
uStyleGrassBendExponent: uniform(value(settings, 'grass', 'bendExponent')),
|
|
156
|
+
uStyleGrassCloudShadowResponse: uniform(value(settings, 'grass', 'cloudShadowResponse')),
|
|
157
|
+
uStyleGrassColorVariationStrength: uniform(value(settings, 'grass', 'colorVariationStrength')),
|
|
158
|
+
uStyleGrassGustSheenStrength: uniform(value(settings, 'grass', 'gustSheenStrength')),
|
|
159
|
+
uStyleGrassGustSheenThreshold: uniform(value(settings, 'grass', 'gustSheenThreshold')),
|
|
160
|
+
uStyleGrassInteractionResponse: uniform(value(settings, 'grass', 'interactionResponse')),
|
|
161
|
+
uStyleGrassRootOcclusionHeight: uniform(value(settings, 'grass', 'rootOcclusionHeight')),
|
|
162
|
+
uStyleGrassRootOcclusionStrength: uniform(value(settings, 'grass', 'rootOcclusionStrength')),
|
|
163
|
+
uStyleGrassSceneShadowResponse: uniform(value(settings, 'grass', 'sceneShadowResponse')),
|
|
164
|
+
uStyleGrassShadowFloor: uniform(value(settings, 'grass', 'shadowFloor')),
|
|
165
|
+
uStyleGrassTipGradientEnd: uniform(value(settings, 'grass', 'tipGradientEnd')),
|
|
166
|
+
uStyleGrassTipGradientStart: uniform(value(settings, 'grass', 'tipGradientStart')),
|
|
167
|
+
});
|
|
168
|
+
if (hasRole('foliageCard')) Object.assign(u, {
|
|
169
|
+
uStyleFoliageBacklitStrength: uniform(value(settings, 'foliage', 'backlitStrength')),
|
|
170
|
+
uStyleFoliageBandSoftness: uniform(value(settings, 'foliage', 'bandSoftness')),
|
|
171
|
+
uStyleFoliageBandThreshold: uniform(value(settings, 'foliage', 'bandThreshold')),
|
|
172
|
+
uStyleFoliageCardVariationStrength: uniform(value(settings, 'foliage', 'cardVariationStrength')),
|
|
173
|
+
uStyleFoliageCloudShadowResponse: uniform(value(settings, 'foliage', 'cloudShadowResponse')),
|
|
174
|
+
uStyleFoliageCrestSoftness: uniform(value(settings, 'foliage', 'crestSoftness')),
|
|
175
|
+
uStyleFoliageCrestThreshold: uniform(value(settings, 'foliage', 'crestThreshold')),
|
|
176
|
+
uStyleFoliageCrownOcclusionStrength: uniform(value(settings, 'foliage', 'crownOcclusionStrength')),
|
|
177
|
+
uStyleFoliageSceneShadowResponse: uniform(value(settings, 'foliage', 'sceneShadowResponse')),
|
|
178
|
+
uStyleFoliageSpriteLuminanceStrength: uniform(value(settings, 'foliage', 'spriteLuminanceStrength')),
|
|
179
|
+
uStyleFoliageTransmissionPowerMultiplier: uniform(value(settings, 'foliage', 'transmissionPowerMultiplier')),
|
|
180
|
+
});
|
|
181
|
+
if (hasRole('flowerPetal', 'flowerCenter')) Object.assign(u, {
|
|
182
|
+
uStyleFlowerBandSoftness: uniform(value(settings, 'flower', 'bandSoftness')),
|
|
183
|
+
uStyleFlowerBandThreshold: uniform(value(settings, 'flower', 'bandThreshold')),
|
|
184
|
+
uStyleFlowerSceneShadowResponse: uniform(value(settings, 'flower', 'sceneShadowResponse')),
|
|
185
|
+
});
|
|
186
|
+
if (hasRole('flowerPetal')) Object.assign(u, {
|
|
187
|
+
uStyleFlowerBacklitStrength: uniform(value(settings, 'flower', 'backlitStrength')),
|
|
188
|
+
uStyleFlowerCupDarkeningStrength: uniform(value(settings, 'flower', 'cupDarkeningStrength')),
|
|
189
|
+
uStyleFlowerPetalTransmissionMultiplier: uniform(value(settings, 'flower', 'petalTransmissionMultiplier')),
|
|
190
|
+
uStyleFlowerUnlitPetalLift: uniform(value(settings, 'flower', 'unlitPetalLift')),
|
|
191
|
+
});
|
|
192
|
+
if (hasRole('flowerCenter')) Object.assign(u, {
|
|
193
|
+
uStyleFlowerCenterLightResponse: uniform(value(settings, 'flower', 'centerLightResponse')),
|
|
194
|
+
uStyleFlowerCenterShadowResponse: uniform(value(settings, 'flower', 'centerShadowResponse')),
|
|
195
|
+
});
|
|
196
|
+
if (hasRole('herbaceousStem')) Object.assign(u, {
|
|
197
|
+
uStyleStemBandCount: uniform(value(settings, 'stem', 'bandCount')),
|
|
198
|
+
uStyleStemBandSoftness: uniform(value(settings, 'stem', 'bandSoftness')),
|
|
199
|
+
uStyleStemShadowFloor: uniform(value(settings, 'stem', 'shadowFloor')),
|
|
200
|
+
uStyleStemRimStrength: uniform(value(settings, 'stem', 'rimStrength')),
|
|
201
|
+
uStyleStemSkyFillStrength: uniform(value(settings, 'stem', 'skyFillStrength')),
|
|
202
|
+
uStyleStemTransmissionStrength: uniform(value(settings, 'stem', 'transmissionStrength')),
|
|
203
|
+
});
|
|
204
|
+
if (hasRole('woodySurface')) Object.assign(u, {
|
|
205
|
+
uStyleBarkBandCount: uniform(value(settings, 'bark', 'bandCount')),
|
|
206
|
+
uStyleBarkBandSoftness: uniform(value(settings, 'bark', 'bandSoftness')),
|
|
207
|
+
uStyleBarkRimStrength: uniform(value(settings, 'bark', 'rimStrength')),
|
|
208
|
+
uStyleBarkShadowFloor: uniform(value(settings, 'bark', 'shadowFloor')),
|
|
209
|
+
uStyleBarkSkyFillStrength: uniform(value(settings, 'bark', 'skyFillStrength')),
|
|
210
|
+
uStyleBarkSpecularStrength: uniform(value(settings, 'bark', 'specularStrength')),
|
|
211
|
+
uStyleBarkSunTintStrength: uniform(value(settings, 'bark', 'sunTintStrength')),
|
|
212
|
+
uStyleBarkVerticalShadeStrength: uniform(value(settings, 'bark', 'verticalShadeStrength')),
|
|
213
|
+
});
|
|
214
|
+
return u;
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
export function vegetationBand(wrap, threshold, softness) {
|
|
218
|
+
const half = softness.mul(0.5);
|
|
219
|
+
return smoothstep(threshold.sub(half), threshold.add(half), wrap);
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
export function vegetationVisibility(
|
|
223
|
+
sceneShadow,
|
|
224
|
+
cloudShadow,
|
|
225
|
+
sceneShadowResponse,
|
|
226
|
+
cloudShadowResponse = 1,
|
|
227
|
+
) {
|
|
228
|
+
const scene = mix(1.0, sceneShadow, sceneShadowResponse);
|
|
229
|
+
const cloud = mix(1.0, cloudShadow, cloudShadowResponse);
|
|
230
|
+
return scene.mul(cloud);
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
/** Common thin-surface lighting. Family shaders can layer role bands on top. */
|
|
234
|
+
export function shadeVegetationSurface({
|
|
235
|
+
baseColor,
|
|
236
|
+
bandOverride = null,
|
|
237
|
+
bandSoftness,
|
|
238
|
+
bandThreshold,
|
|
239
|
+
cloudShadow,
|
|
240
|
+
u,
|
|
241
|
+
diffuseWrap = u.uStyleThinSurfaceDiffuseWrap,
|
|
242
|
+
normal,
|
|
243
|
+
normalUpBias = u.uStyleThinSurfaceNormalUpBias,
|
|
244
|
+
materialShadowColor = null,
|
|
245
|
+
sceneShadow,
|
|
246
|
+
sceneShadowResponse,
|
|
247
|
+
cloudShadowResponse = 1,
|
|
248
|
+
shadowFloor,
|
|
249
|
+
skyColor,
|
|
250
|
+
sunColor,
|
|
251
|
+
sunDirection,
|
|
252
|
+
rimStrength = u.uStyleLightingRimStrength,
|
|
253
|
+
transmissionMultiplier = 1,
|
|
254
|
+
transmissionPower = u.uStyleThinSurfaceTransmissionPower,
|
|
255
|
+
transmissionShadowFloor = u.uStyleThinSurfaceTransmissionShadowFloor,
|
|
256
|
+
transmissionStrength = u.uStyleThinSurfaceTransmissionStrength,
|
|
257
|
+
twoSidedLighting = u.uStyleThinSurfaceTwoSidedLighting,
|
|
258
|
+
skyFillStrength = u.uStyleLightingSkyFillStrength,
|
|
259
|
+
worldPosition,
|
|
260
|
+
}) {
|
|
261
|
+
const doubleSidedNormal = normal.mul(faceDirection);
|
|
262
|
+
const faceAwareNormal = mix(normal, doubleSidedNormal, twoSidedLighting);
|
|
263
|
+
const styledNormal = normalize(mix(
|
|
264
|
+
faceAwareNormal,
|
|
265
|
+
vec3(0, 1, 0),
|
|
266
|
+
normalUpBias,
|
|
267
|
+
));
|
|
268
|
+
const sun = normalize(sunDirection);
|
|
269
|
+
const wrap = dot(styledNormal, sun)
|
|
270
|
+
.mul(diffuseWrap.oneMinus())
|
|
271
|
+
.add(diffuseWrap);
|
|
272
|
+
const visibility = vegetationVisibility(
|
|
273
|
+
sceneShadow,
|
|
274
|
+
cloudShadow,
|
|
275
|
+
sceneShadowResponse,
|
|
276
|
+
cloudShadowResponse,
|
|
277
|
+
).toVar();
|
|
278
|
+
const band = (bandOverride ?? vegetationBand(wrap, bandThreshold, bandSoftness))
|
|
279
|
+
.mul(visibility).toVar();
|
|
280
|
+
const colorNode = baseColor.mul(mix(shadowFloor, 1.0, band)).toVar();
|
|
281
|
+
|
|
282
|
+
// An asset palette may carry its own shadow tone (grass and tree foliage
|
|
283
|
+
// are the common cases). It establishes the material identity in shadow;
|
|
284
|
+
// the IP-wide treatment tint below still layers over every material.
|
|
285
|
+
const materialShadowAmount = visibility.oneMinus();
|
|
286
|
+
if (materialShadowColor) {
|
|
287
|
+
colorNode.assign(mix(
|
|
288
|
+
colorNode,
|
|
289
|
+
materialShadowColor,
|
|
290
|
+
clamp(materialShadowAmount, 0, 1),
|
|
291
|
+
));
|
|
292
|
+
}
|
|
293
|
+
const shadowAmount = materialShadowAmount.mul(u.uStyleLightingShadowTintStrength);
|
|
294
|
+
colorNode.mulAssign(mix(
|
|
295
|
+
vec3(1),
|
|
296
|
+
u.uStyleLightingShadowTint,
|
|
297
|
+
clamp(shadowAmount, 0, 1),
|
|
298
|
+
));
|
|
299
|
+
colorNode.mulAssign(mix(vec3(1), sunColor, band.mul(u.uStyleLightingSunTintStrength)));
|
|
300
|
+
colorNode.addAssign(skyColor.mul(band.oneMinus()).mul(skyFillStrength));
|
|
301
|
+
|
|
302
|
+
const viewDirection = normalize(cameraPosition.sub(worldPosition));
|
|
303
|
+
const rim = pow(
|
|
304
|
+
clamp(abs(dot(styledNormal, viewDirection)).oneMinus(), 0, 1),
|
|
305
|
+
u.uStyleLightingRimPower,
|
|
306
|
+
);
|
|
307
|
+
colorNode.addAssign(skyColor.mul(rim).mul(rimStrength));
|
|
308
|
+
const transmission = pow(
|
|
309
|
+
clamp(dot(viewDirection, sun.negate()), 0, 1),
|
|
310
|
+
transmissionPower,
|
|
311
|
+
).mul(transmissionStrength).mul(transmissionMultiplier);
|
|
312
|
+
colorNode.addAssign(
|
|
313
|
+
sunColor.mul(transmission).mul(mix(
|
|
314
|
+
transmissionShadowFloor,
|
|
315
|
+
1,
|
|
316
|
+
visibility,
|
|
317
|
+
)),
|
|
318
|
+
);
|
|
319
|
+
|
|
320
|
+
const wet = clamp(u.uWetness.mul(u.uWetnessResponse), 0, 1).toVar();
|
|
321
|
+
colorNode.mulAssign(wet.mul(u.uStyleWeatherResponseWetDarkening).oneMinus());
|
|
322
|
+
const luminance = dot(colorNode, vec3(0.299, 0.587, 0.114));
|
|
323
|
+
colorNode.assign(mix(
|
|
324
|
+
colorNode,
|
|
325
|
+
vec3(luminance),
|
|
326
|
+
wet.mul(u.uStyleWeatherResponseWetDesaturation),
|
|
327
|
+
));
|
|
328
|
+
colorNode.addAssign(
|
|
329
|
+
sunColor.mul(wet).mul(u.uStyleWeatherResponseWetHighlightStrength)
|
|
330
|
+
.mul(rim.mul(0.5).add(0.08)),
|
|
331
|
+
);
|
|
332
|
+
|
|
333
|
+
const snowSoftness = u.uStyleWeatherResponseSnowEdgeSoftness.max(0.001);
|
|
334
|
+
const snowEdge = snowSoftness.mul(0.5);
|
|
335
|
+
const snowFacing = smoothstep(
|
|
336
|
+
snowSoftness.mul(0.25).sub(snowEdge),
|
|
337
|
+
snowSoftness.mul(0.25).add(snowEdge),
|
|
338
|
+
styledNormal.y,
|
|
339
|
+
);
|
|
340
|
+
const snow = clamp(u.uSnowCover.mul(u.uSnowRetention).mul(snowFacing), 0, 1);
|
|
341
|
+
const snowTint = u.uStyleWeatherResponseSnowTint.mul(mix(
|
|
342
|
+
u.uStyleWeatherResponseSnowShadowStrength,
|
|
343
|
+
1,
|
|
344
|
+
visibility,
|
|
345
|
+
));
|
|
346
|
+
colorNode.assign(mix(colorNode, snowTint, snow));
|
|
347
|
+
|
|
348
|
+
return { band, color: colorNode, normal: styledNormal, visibility, viewDirection };
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
export function tagVegetationRole(material, role, variant) {
|
|
352
|
+
const roles = Object.freeze([...new Set((Array.isArray(role) ? role : [role]).filter(Boolean))]);
|
|
353
|
+
material.userData.toonlabVegetation = Object.freeze({
|
|
354
|
+
role: roles[0] ?? '',
|
|
355
|
+
roles,
|
|
356
|
+
variant,
|
|
357
|
+
version: 1,
|
|
358
|
+
});
|
|
359
|
+
return material;
|
|
360
|
+
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
// World-anchored persistent shoreline state shared by water foam and wet
|
|
2
|
+
// terrain materials. RGBA = moisture, surface film, active foam, residue.
|
|
3
|
+
|
|
4
|
+
import {
|
|
5
|
+
clamp,
|
|
6
|
+
smoothstep,
|
|
7
|
+
vec2,
|
|
8
|
+
} from 'three/tsl';
|
|
9
|
+
|
|
10
|
+
export function createWaterShoreStateChunk({ u }) {
|
|
11
|
+
const shoreStateUv = (worldXZ) => worldXZ
|
|
12
|
+
.sub(u.uShoreStateRegion.xy)
|
|
13
|
+
.div(u.uShoreStateRegion.zw.mul(2.0))
|
|
14
|
+
.add(0.5);
|
|
15
|
+
|
|
16
|
+
const shoreStateSample = (worldXZ) => {
|
|
17
|
+
const fieldUv = shoreStateUv(worldXZ).toVar();
|
|
18
|
+
// Fade over two percent of the atlas instead of revealing a rectangular
|
|
19
|
+
// edge when a world uses a finite shore-state tile.
|
|
20
|
+
const border = smoothstep(vec2(0.0), vec2(0.02), fieldUv)
|
|
21
|
+
.mul(smoothstep(vec2(0.98), vec2(1.0), fieldUv).oneMinus());
|
|
22
|
+
const mask = border.x.mul(border.y).mul(u.uUseShoreState).toVar();
|
|
23
|
+
return u.uShoreStateMap
|
|
24
|
+
.sample(clamp(fieldUv, vec2(0.0), vec2(1.0)))
|
|
25
|
+
.level(0)
|
|
26
|
+
.mul(mask)
|
|
27
|
+
.toVar();
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
return { shoreStateSample, shoreStateUv };
|
|
31
|
+
}
|
|
@@ -13,6 +13,8 @@
|
|
|
13
13
|
// The GLSL `i < 2 ? 1.0 : chopWeight` slot filter is a JS-side ternary per
|
|
14
14
|
// unrolled iteration — slots 0/1 (the dominant swell / set-beat pair) always
|
|
15
15
|
// pass at full strength, the shorter cross chop fades toward the surf zone.
|
|
16
|
+
// That amplitude filter is independent of nearshore phase applicability:
|
|
17
|
+
// slot 1 only shares q when WaterSurface marks it as an authored beat partner.
|
|
16
18
|
|
|
17
19
|
import {
|
|
18
20
|
clamp,
|
|
@@ -20,6 +22,8 @@ import {
|
|
|
20
22
|
cos,
|
|
21
23
|
dot,
|
|
22
24
|
float,
|
|
25
|
+
fract,
|
|
26
|
+
mix,
|
|
23
27
|
normalize,
|
|
24
28
|
sin,
|
|
25
29
|
vec3,
|
|
@@ -28,9 +32,42 @@ import {
|
|
|
28
32
|
export function createWaterWavesChunk({ wavesA, wavesB, waveCount }) {
|
|
29
33
|
const count = Math.max(1, Math.trunc(waveCount));
|
|
30
34
|
|
|
35
|
+
// The primary swell and an eligible set-beat partner can replace their
|
|
36
|
+
// deep-water plane coordinate with a CPU-baked mild-slope phase field.
|
|
37
|
+
// `waveVector` is grad(q), in
|
|
38
|
+
// multiples of the offshore wave number; it therefore supplies both the
|
|
39
|
+
// refracted propagation direction and the shortened shallow wavelength.
|
|
40
|
+
// This is a JS-side branch while the node graph is built, so the six short
|
|
41
|
+
// cross-wave slots keep their original graph and cost.
|
|
42
|
+
const spatialPhaseFrame = (slot, direction, restXZ, nearshore) => {
|
|
43
|
+
const baseCoordinate = dot(direction, restXZ);
|
|
44
|
+
if (!nearshore || slot >= 2) {
|
|
45
|
+
return {
|
|
46
|
+
coordinate: baseCoordinate,
|
|
47
|
+
gradient: direction,
|
|
48
|
+
orbitDirection: direction,
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
// slotMask: 0 = disabled, 1 = primary only, 2 = same-direction beat pair.
|
|
52
|
+
// Encoding both applicability bits in the existing attribute channel
|
|
53
|
+
// avoids another varying/uniform and keeps cross-running slot 1 on its
|
|
54
|
+
// authored plane phase when Wave Set Strength is zero.
|
|
55
|
+
const slotMask = nearshore.slotMask ?? float(2.0);
|
|
56
|
+
const slotWeight = slot === 0
|
|
57
|
+
? clamp(slotMask, 0.0, 1.0)
|
|
58
|
+
: clamp(slotMask.sub(1.0), 0.0, 1.0);
|
|
59
|
+
const blend = nearshore.blend.mul(slotWeight).toVar();
|
|
60
|
+
const gradient = mix(direction, nearshore.waveVector, blend).toVar();
|
|
61
|
+
return {
|
|
62
|
+
coordinate: mix(baseCoordinate, nearshore.phaseCoordinate, blend),
|
|
63
|
+
gradient,
|
|
64
|
+
orbitDirection: normalize(gradient),
|
|
65
|
+
};
|
|
66
|
+
};
|
|
67
|
+
|
|
31
68
|
// vec3 displacement of the rest position; chopWeight ∈ [0,1] filters the
|
|
32
69
|
// short cross-running components (1 = full open-water spectrum).
|
|
33
|
-
const gerstnerDisplacementFiltered = (restXZ, time, chopWeight) => {
|
|
70
|
+
const gerstnerDisplacementFiltered = (restXZ, time, chopWeight, nearshore = null) => {
|
|
34
71
|
const displacement = vec3(0.0).toVar();
|
|
35
72
|
for (let i = 0; i < count; i += 1) {
|
|
36
73
|
const a = wavesA.element(i).toVar();
|
|
@@ -41,20 +78,48 @@ export function createWaterWavesChunk({ wavesA, wavesB, waveCount }) {
|
|
|
41
78
|
const amplitude = (i < 2 ? b.x : b.x.mul(chopWeight)).toVar();
|
|
42
79
|
const phase = b.y;
|
|
43
80
|
const steepness = b.z;
|
|
44
|
-
const
|
|
81
|
+
const phaseFrame = spatialPhaseFrame(i, direction, restXZ, nearshore);
|
|
82
|
+
const theta = waveNumber.mul(phaseFrame.coordinate)
|
|
83
|
+
.sub(omega.mul(time)).add(phase).toVar();
|
|
45
84
|
const sinTheta = sin(theta);
|
|
46
85
|
const cosTheta = cos(theta);
|
|
47
86
|
displacement.y.addAssign(amplitude.mul(sinTheta));
|
|
48
|
-
displacement.xz.addAssign(
|
|
87
|
+
displacement.xz.addAssign(
|
|
88
|
+
phaseFrame.orbitDirection.mul(steepness.mul(amplitude).mul(cosTheta)),
|
|
89
|
+
);
|
|
49
90
|
}
|
|
50
91
|
return displacement;
|
|
51
92
|
};
|
|
52
93
|
|
|
53
94
|
const gerstnerDisplacement = (restXZ, time) => gerstnerDisplacementFiltered(restXZ, time, float(1.0));
|
|
54
95
|
|
|
96
|
+
// Vertical signal of the two long swell slots that survive into the surf
|
|
97
|
+
// zone. Swash evaluates this at the projected rest shoreline rather than at
|
|
98
|
+
// every beach vertex, so one arriving swell produces one connected edge.
|
|
99
|
+
const gerstnerSwellHeight = (restXZ, time, nearshore = null) => {
|
|
100
|
+
const height = float(0.0).toVar();
|
|
101
|
+
for (let i = 0; i < Math.min(2, count); i += 1) {
|
|
102
|
+
const a = wavesA.element(i).toVar();
|
|
103
|
+
const b = wavesB.element(i).toVar();
|
|
104
|
+
const phaseFrame = spatialPhaseFrame(i, a.xy, restXZ, nearshore);
|
|
105
|
+
const theta = a.w.mul(phaseFrame.coordinate).sub(a.z.mul(time)).add(b.y).toVar();
|
|
106
|
+
height.addAssign(b.x.mul(sin(theta)));
|
|
107
|
+
}
|
|
108
|
+
return height;
|
|
109
|
+
};
|
|
110
|
+
|
|
111
|
+
// 0..1 cycle of the dominant crest at the rest shoreline. Cycle zero is
|
|
112
|
+
// the instant that crest reaches the shoreline, so the breaker can hand
|
|
113
|
+
// directly into uprush instead of looking like an unrelated animation.
|
|
114
|
+
const primarySwellCycle = (time) => {
|
|
115
|
+
const a = wavesA.element(0).toVar();
|
|
116
|
+
const b = wavesB.element(0).toVar();
|
|
117
|
+
return fract(a.z.mul(time).sub(b.y).add(Math.PI * 0.5).div(Math.PI * 2));
|
|
118
|
+
};
|
|
119
|
+
|
|
55
120
|
// Analytic surface frame at the rest position plus the 0..1 crest factor
|
|
56
121
|
// used for whitecap foam (GLSL `out float crest` → returned alongside).
|
|
57
|
-
const gerstnerNormalFiltered = (restXZ, time, chopWeight) => {
|
|
122
|
+
const gerstnerNormalFiltered = (restXZ, time, chopWeight, nearshore = null) => {
|
|
58
123
|
const tangent = vec3(1.0, 0.0, 0.0).toVar();
|
|
59
124
|
const bitangent = vec3(0.0, 0.0, 1.0).toVar();
|
|
60
125
|
const crestSum = float(0.0).toVar();
|
|
@@ -69,16 +134,28 @@ export function createWaterWavesChunk({ wavesA, wavesB, waveCount }) {
|
|
|
69
134
|
const phase = b.y;
|
|
70
135
|
const steepness = b.z;
|
|
71
136
|
const crestWeight = b.w;
|
|
72
|
-
const
|
|
137
|
+
const phaseFrame = spatialPhaseFrame(i, direction, restXZ, nearshore);
|
|
138
|
+
const theta = waveNumber.mul(phaseFrame.coordinate)
|
|
139
|
+
.sub(omega.mul(time)).add(phase).toVar();
|
|
73
140
|
const sinTheta = sin(theta).toVar();
|
|
74
141
|
const cosTheta = cos(theta).toVar();
|
|
75
142
|
const waveKA = waveNumber.mul(amplitude).toVar();
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
tangent.
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
143
|
+
const phaseGradient = phaseFrame.gradient;
|
|
144
|
+
const orbitDirection = phaseFrame.orbitDirection;
|
|
145
|
+
tangent.x.subAssign(
|
|
146
|
+
steepness.mul(orbitDirection.x).mul(phaseGradient.x).mul(waveKA).mul(sinTheta),
|
|
147
|
+
);
|
|
148
|
+
tangent.y.addAssign(phaseGradient.x.mul(waveKA).mul(cosTheta));
|
|
149
|
+
tangent.z.subAssign(
|
|
150
|
+
steepness.mul(orbitDirection.y).mul(phaseGradient.x).mul(waveKA).mul(sinTheta),
|
|
151
|
+
);
|
|
152
|
+
bitangent.x.subAssign(
|
|
153
|
+
steepness.mul(orbitDirection.x).mul(phaseGradient.y).mul(waveKA).mul(sinTheta),
|
|
154
|
+
);
|
|
155
|
+
bitangent.y.addAssign(phaseGradient.y.mul(waveKA).mul(cosTheta));
|
|
156
|
+
bitangent.z.subAssign(
|
|
157
|
+
steepness.mul(orbitDirection.y).mul(phaseGradient.y).mul(waveKA).mul(sinTheta),
|
|
158
|
+
);
|
|
82
159
|
const crestPeak = sinTheta.mul(0.5).add(0.5).toVar();
|
|
83
160
|
crestSum.addAssign(crestWeight.mul(amplitude).mul(crestPeak).mul(crestPeak));
|
|
84
161
|
crestTotal.addAssign(crestWeight.mul(amplitude));
|
|
@@ -96,5 +173,7 @@ export function createWaterWavesChunk({ wavesA, wavesB, waveCount }) {
|
|
|
96
173
|
gerstnerDisplacementFiltered,
|
|
97
174
|
gerstnerNormal,
|
|
98
175
|
gerstnerNormalFiltered,
|
|
176
|
+
gerstnerSwellHeight,
|
|
177
|
+
primarySwellCycle,
|
|
99
178
|
};
|
|
100
179
|
}
|
|
@@ -80,6 +80,7 @@ import { applySaturation, envLuma, windowPaneMask } from './chunks/environment-c
|
|
|
80
80
|
import { environmentDebugColor } from './chunks/environment-debug.js';
|
|
81
81
|
import { createEnvironmentLightingChunk } from './chunks/environment-lighting.js';
|
|
82
82
|
import { sampleEnvironmentSunShadow } from './chunks/environment-sun-shadow.js';
|
|
83
|
+
import { projectedWaterCaustics } from './chunks/projected-water-caustics.js';
|
|
83
84
|
import { stylizedCloudShadow } from './chunks/stylized-cloud-shadow.js';
|
|
84
85
|
import { toonSceneLights } from './chunks/character-scene-lights.js';
|
|
85
86
|
|
|
@@ -162,6 +163,11 @@ export function createEnvironmentNodeMaterial({
|
|
|
162
163
|
lightingInfluence: uniform(hasSun ? 1.0 : 0.36),
|
|
163
164
|
saturation: uniform(1.12),
|
|
164
165
|
shadowLift: uniform(hasSun ? 0.0 : 0.66),
|
|
166
|
+
// How much of the CAST sun shadow applies to direct light. 1 = classic
|
|
167
|
+
// full-depth shadows. Worlds whose look runs near-zero ambient (the
|
|
168
|
+
// vivid outdoor presets) crush building-scale shadow masses to black at
|
|
169
|
+
// 1.0 — ~0.7 keeps them as painted mid-tones instead.
|
|
170
|
+
sunShadowStrength: uniform(1.0),
|
|
165
171
|
skyTintStrength: uniform(0.24),
|
|
166
172
|
emissiveStrength: uniform(hasSun ? 0.0 : isEmissive ? 0.55 : 0.0),
|
|
167
173
|
sunBoost: uniform(hasSun ? 0.08 : 0.0),
|
|
@@ -526,7 +532,7 @@ export function createEnvironmentNodeMaterial({
|
|
|
526
532
|
const sampledSunShadow = sampleEnvironmentSunShadow(vWorldPosition).toVar();
|
|
527
533
|
const sunlightVisibility = float(1.0).toVar();
|
|
528
534
|
If(u.enableShadowMask.greaterThan(0.5), () => {
|
|
529
|
-
sunlightVisibility.assign(sampledSunShadow);
|
|
535
|
+
sunlightVisibility.assign(mix(1.0, sampledSunShadow, u.sunShadowStrength));
|
|
530
536
|
});
|
|
531
537
|
// Drifting procedural cloud shadows over outdoor terrain; strength
|
|
532
538
|
// defaults to 0 so indoor scenes are untouched.
|
|
@@ -688,6 +694,14 @@ export function createEnvironmentNodeMaterial({
|
|
|
688
694
|
});
|
|
689
695
|
}
|
|
690
696
|
|
|
697
|
+
// When the camera is below an active WaterSurface, the seabed is drawn
|
|
698
|
+
// directly instead of being seen through the water material. Receive the
|
|
699
|
+
// same moving sunlight here; the shared updater keeps this inert above
|
|
700
|
+
// water, where the surface shader already composites its own caustics.
|
|
701
|
+
color.addAssign(
|
|
702
|
+
projectedWaterCaustics(vWorldPosition, normalWorld).mul(sunlightVisibility),
|
|
703
|
+
);
|
|
704
|
+
|
|
691
705
|
color.assign(applySaturation(color.mul(u.exposure), u.saturation));
|
|
692
706
|
|
|
693
707
|
// World-height fog: dense near the environment floor, fading with height.
|