@call-me-sensei/toonlab 0.2.0 → 0.3.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/AGENTS.md +127 -4
- package/ATTRIBUTION.md +41 -3
- package/README.md +231 -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 +752 -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,322 @@
|
|
|
1
|
+
import * as THREE from 'three';
|
|
2
|
+
|
|
3
|
+
import { MeshPhysicalNodeMaterial } from 'three/webgpu';
|
|
4
|
+
import {
|
|
5
|
+
clamp,
|
|
6
|
+
float,
|
|
7
|
+
luminance,
|
|
8
|
+
max,
|
|
9
|
+
mix,
|
|
10
|
+
normalMap as normalMapNode,
|
|
11
|
+
normalWorld,
|
|
12
|
+
positionWorld,
|
|
13
|
+
smoothstep,
|
|
14
|
+
step,
|
|
15
|
+
texture,
|
|
16
|
+
uniform,
|
|
17
|
+
uv,
|
|
18
|
+
vec2,
|
|
19
|
+
vec3,
|
|
20
|
+
vertexColor,
|
|
21
|
+
} from 'three/tsl';
|
|
22
|
+
|
|
23
|
+
import { projectedWaterCaustics } from '../shaders-tsl/chunks/projected-water-caustics.js';
|
|
24
|
+
|
|
25
|
+
const DEFAULT_FOAM_COLOR = Object.freeze([0.94, 0.98, 1.0]);
|
|
26
|
+
const DEFAULT_WET_DARKENING = 0.3;
|
|
27
|
+
const DEFAULT_WET_ROUGHNESS = 0.28;
|
|
28
|
+
const DEFAULT_WET_CLEARCOAT = 0.68;
|
|
29
|
+
const DEFAULT_FOAM_AMOUNT = 1;
|
|
30
|
+
const DRY_ROUGHNESS = 0.95;
|
|
31
|
+
|
|
32
|
+
const regionScratch = new THREE.Vector4();
|
|
33
|
+
let fallbackStateTexture = null;
|
|
34
|
+
|
|
35
|
+
function getFallbackStateTexture() {
|
|
36
|
+
if (!fallbackStateTexture) {
|
|
37
|
+
// R moisture, G surface film, B active foam, A stranded residue.
|
|
38
|
+
fallbackStateTexture = new THREE.DataTexture(
|
|
39
|
+
new Uint8Array([0, 0, 0, 0]),
|
|
40
|
+
1,
|
|
41
|
+
1,
|
|
42
|
+
THREE.RGBAFormat,
|
|
43
|
+
);
|
|
44
|
+
fallbackStateTexture.name = 'waterShoreStateFallback';
|
|
45
|
+
fallbackStateTexture.colorSpace = THREE.NoColorSpace;
|
|
46
|
+
fallbackStateTexture.minFilter = THREE.LinearFilter;
|
|
47
|
+
fallbackStateTexture.magFilter = THREE.LinearFilter;
|
|
48
|
+
fallbackStateTexture.wrapS = THREE.ClampToEdgeWrapping;
|
|
49
|
+
fallbackStateTexture.wrapT = THREE.ClampToEdgeWrapping;
|
|
50
|
+
fallbackStateTexture.generateMipmaps = false;
|
|
51
|
+
fallbackStateTexture.needsUpdate = true;
|
|
52
|
+
}
|
|
53
|
+
return fallbackStateTexture;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
function resolveStateTexture(stateField) {
|
|
57
|
+
const candidate = stateField?.texture
|
|
58
|
+
?? stateField?.stateTexture
|
|
59
|
+
?? stateField?.renderTarget?.texture
|
|
60
|
+
?? stateField?.target?.texture;
|
|
61
|
+
return candidate?.isTexture ? candidate : getFallbackStateTexture();
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
function copyRegion(out, value) {
|
|
65
|
+
if (value?.isVector4) return out.copy(value);
|
|
66
|
+
if (Array.isArray(value)) {
|
|
67
|
+
return out.set(value[0] ?? 0, value[1] ?? 0, value[2] ?? 0.5, value[3] ?? 0.5);
|
|
68
|
+
}
|
|
69
|
+
if (value && typeof value === 'object') {
|
|
70
|
+
const width = Number(value.width);
|
|
71
|
+
const depth = Number(value.depth);
|
|
72
|
+
const halfWidth = value.halfWidth ?? value.halfSizeX
|
|
73
|
+
?? (Number.isFinite(width) ? width * 0.5 : 0.5);
|
|
74
|
+
const halfDepth = value.halfDepth ?? value.halfSizeZ
|
|
75
|
+
?? (Number.isFinite(depth) ? depth * 0.5 : 0.5);
|
|
76
|
+
return out.set(
|
|
77
|
+
value.centerX ?? value.x ?? 0,
|
|
78
|
+
value.centerZ ?? value.z ?? 0,
|
|
79
|
+
halfWidth,
|
|
80
|
+
halfDepth,
|
|
81
|
+
);
|
|
82
|
+
}
|
|
83
|
+
return out.set(0, 0, 0.5, 0.5);
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
function resolveStateRegion(stateField, out) {
|
|
87
|
+
if (!stateField) return out.set(0, 0, 0.5, 0.5);
|
|
88
|
+
if (typeof stateField.getRegion === 'function') {
|
|
89
|
+
out.set(0, 0, 0.5, 0.5);
|
|
90
|
+
const result = stateField.getRegion(out);
|
|
91
|
+
return result && result !== out ? copyRegion(out, result) : out;
|
|
92
|
+
}
|
|
93
|
+
if (stateField.region || stateField.worldRegion) {
|
|
94
|
+
return copyRegion(out, stateField.region ?? stateField.worldRegion);
|
|
95
|
+
}
|
|
96
|
+
return out.set(
|
|
97
|
+
stateField.centerX ?? 0,
|
|
98
|
+
stateField.centerZ ?? 0,
|
|
99
|
+
Math.max((stateField.worldWidth ?? stateField.width ?? 1) * 0.5, 1e-3),
|
|
100
|
+
Math.max((stateField.worldDepth ?? stateField.depth ?? 1) * 0.5, 1e-3),
|
|
101
|
+
);
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
function setSrgbColor(uniformNode, value) {
|
|
105
|
+
if (value === undefined) return;
|
|
106
|
+
if (value?.isColor) {
|
|
107
|
+
uniformNode.value.copy(value);
|
|
108
|
+
return;
|
|
109
|
+
}
|
|
110
|
+
if (Array.isArray(value)) {
|
|
111
|
+
uniformNode.value.setRGB(
|
|
112
|
+
value[0] ?? 1,
|
|
113
|
+
value[1] ?? 1,
|
|
114
|
+
value[2] ?? 1,
|
|
115
|
+
THREE.SRGBColorSpace,
|
|
116
|
+
);
|
|
117
|
+
return;
|
|
118
|
+
}
|
|
119
|
+
uniformNode.value.set(value);
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
function setFiniteClamped(uniformNode, value, min, maximum) {
|
|
123
|
+
if (!Number.isFinite(value)) return;
|
|
124
|
+
uniformNode.value = THREE.MathUtils.clamp(value, min, maximum);
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
/**
|
|
128
|
+
* Refreshes the live state binding and appearance controls of a persistent
|
|
129
|
+
* shore material. Call once after each shore-state ping-pong swap; changing a
|
|
130
|
+
* TextureNode's value updates the binding without rebuilding the node graph.
|
|
131
|
+
*/
|
|
132
|
+
export function updateWaterShoreMaterial(material, {
|
|
133
|
+
stateField,
|
|
134
|
+
foamColor,
|
|
135
|
+
foamAmount,
|
|
136
|
+
wetDarkening,
|
|
137
|
+
wetRoughness,
|
|
138
|
+
wetClearcoat,
|
|
139
|
+
} = {}) {
|
|
140
|
+
const uniforms = material?.uniforms;
|
|
141
|
+
if (!uniforms?.uShoreStateMap) return material;
|
|
142
|
+
|
|
143
|
+
if (stateField !== undefined) material.userData.waterShoreStateField = stateField;
|
|
144
|
+
const field = material.userData.waterShoreStateField ?? null;
|
|
145
|
+
uniforms.uShoreStateMap.value = resolveStateTexture(field);
|
|
146
|
+
resolveStateRegion(field, regionScratch);
|
|
147
|
+
uniforms.uShoreStateRegion.value.copy(regionScratch);
|
|
148
|
+
|
|
149
|
+
setSrgbColor(uniforms.uShoreFoamColor, foamColor);
|
|
150
|
+
setFiniteClamped(uniforms.uShoreFoamAmount, foamAmount, 0, 2);
|
|
151
|
+
setFiniteClamped(uniforms.uShoreWetDarkening, wetDarkening, 0, 1);
|
|
152
|
+
setFiniteClamped(uniforms.uShoreWetRoughness, wetRoughness, 0.04, 1);
|
|
153
|
+
setFiniteClamped(uniforms.uShoreWetClearcoat, wetClearcoat, 0, 1);
|
|
154
|
+
return material;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
/**
|
|
158
|
+
* Creates a lit wettable ground material for shoreline meshes whose base
|
|
159
|
+
* albedo lives in the geometry's `color` attribute.
|
|
160
|
+
*
|
|
161
|
+
* Packed state channels:
|
|
162
|
+
* - R: persistent sediment moisture
|
|
163
|
+
* - G: short-lived surface water film
|
|
164
|
+
* - B: active aerated foam
|
|
165
|
+
* - A: stranded/drying foam residue
|
|
166
|
+
*
|
|
167
|
+
* The vertex color is sampled explicitly so foam can blend toward its actual
|
|
168
|
+
* albedo instead of merely multiplying dark wet sand. Automatic vertex-color
|
|
169
|
+
* modulation is disabled below to avoid applying the base twice.
|
|
170
|
+
*/
|
|
171
|
+
export function createWaterShoreMaterial({
|
|
172
|
+
albedoMap = null,
|
|
173
|
+
armMap = null,
|
|
174
|
+
normalMap = null,
|
|
175
|
+
textureRepeat = 1,
|
|
176
|
+
stateField = null,
|
|
177
|
+
foamColor = DEFAULT_FOAM_COLOR,
|
|
178
|
+
foamAmount = DEFAULT_FOAM_AMOUNT,
|
|
179
|
+
wetDarkening = DEFAULT_WET_DARKENING,
|
|
180
|
+
wetRoughness = DEFAULT_WET_ROUGHNESS,
|
|
181
|
+
wetClearcoat = DEFAULT_WET_CLEARCOAT,
|
|
182
|
+
} = {}) {
|
|
183
|
+
const uniforms = {
|
|
184
|
+
uShoreStateMap: texture(getFallbackStateTexture()),
|
|
185
|
+
// (centerX, centerZ, halfWidth, halfDepth), matching the ripple-region
|
|
186
|
+
// convention used by the rest of the water system.
|
|
187
|
+
uShoreStateRegion: uniform(new THREE.Vector4(0, 0, 0.5, 0.5)),
|
|
188
|
+
uShoreFoamColor: uniform(new THREE.Color(1, 1, 1)),
|
|
189
|
+
uShoreFoamAmount: uniform(DEFAULT_FOAM_AMOUNT),
|
|
190
|
+
uShoreWetDarkening: uniform(DEFAULT_WET_DARKENING),
|
|
191
|
+
uShoreWetRoughness: uniform(DEFAULT_WET_ROUGHNESS),
|
|
192
|
+
uShoreWetClearcoat: uniform(DEFAULT_WET_CLEARCOAT),
|
|
193
|
+
};
|
|
194
|
+
|
|
195
|
+
const fieldUv = positionWorld.xz
|
|
196
|
+
.sub(uniforms.uShoreStateRegion.xy)
|
|
197
|
+
.div(max(uniforms.uShoreStateRegion.zw.mul(2.0), vec2(1e-3)))
|
|
198
|
+
.add(0.5);
|
|
199
|
+
const inside = step(0.0, fieldUv.x).mul(step(fieldUv.x, 1.0))
|
|
200
|
+
.mul(step(0.0, fieldUv.y)).mul(step(fieldUv.y, 1.0));
|
|
201
|
+
// State render targets have no mip chain. Explicit level zero is required
|
|
202
|
+
// by WGSL in non-uniform control flow and is harmless on the GLSL builder.
|
|
203
|
+
const state = uniforms.uShoreStateMap
|
|
204
|
+
.sample(clamp(fieldUv, vec2(0.0), vec2(1.0)))
|
|
205
|
+
.level(0)
|
|
206
|
+
.mul(inside);
|
|
207
|
+
|
|
208
|
+
const moisture = clamp(state.r, 0.0, 1.0);
|
|
209
|
+
const film = clamp(state.g, 0.0, 1.0);
|
|
210
|
+
const foamAmountNode = clamp(uniforms.uShoreFoamAmount, 0.0, 2.0);
|
|
211
|
+
const activeFoam = clamp(state.b, 0.0, 1.0);
|
|
212
|
+
const residue = clamp(state.a, 0.0, 1.0);
|
|
213
|
+
const presentedActiveFoam = activeFoam.mul(foamAmountNode);
|
|
214
|
+
const presentedResidue = residue.mul(foamAmountNode);
|
|
215
|
+
|
|
216
|
+
// Optional tiling detail lets authored lab/showcase terrain use this same
|
|
217
|
+
// persistent wet-sand material without weakening its vertex-painted depth
|
|
218
|
+
// tint. ARM follows the Poly Haven convention (AO, roughness, metalness).
|
|
219
|
+
// The maps are fixed when the graph is created; wetness/foam remain live.
|
|
220
|
+
const hasTiledMaps = albedoMap?.isTexture || armMap?.isTexture || normalMap?.isTexture;
|
|
221
|
+
const tiledUv = hasTiledMaps
|
|
222
|
+
? uv().mul(Math.max(Number(textureRepeat) || 1, 1e-3))
|
|
223
|
+
: null;
|
|
224
|
+
const vertexBaseColor = vertexColor().rgb;
|
|
225
|
+
const armSample = armMap?.isTexture ? texture(armMap).sample(tiledUv) : null;
|
|
226
|
+
const albedoSample = albedoMap?.isTexture
|
|
227
|
+
? texture(albedoMap).sample(tiledUv).rgb
|
|
228
|
+
: null;
|
|
229
|
+
const albedoLuminance = albedoSample ? luminance(albedoSample) : null;
|
|
230
|
+
const sandContrast = albedoLuminance
|
|
231
|
+
? clamp(albedoLuminance.mul(1.7).add(0.48), 0.72, 1.24)
|
|
232
|
+
: null;
|
|
233
|
+
const sandChroma = albedoSample
|
|
234
|
+
? albedoSample.div(max(albedoLuminance, float(0.08)))
|
|
235
|
+
: null;
|
|
236
|
+
const dryBaseColor = albedoSample
|
|
237
|
+
? mix(
|
|
238
|
+
vertexBaseColor,
|
|
239
|
+
clamp(
|
|
240
|
+
vertexBaseColor
|
|
241
|
+
.mul(sandContrast)
|
|
242
|
+
.mul(mix(vec3(1.0), sandChroma, 0.18)),
|
|
243
|
+
0.0,
|
|
244
|
+
1.0,
|
|
245
|
+
),
|
|
246
|
+
0.86,
|
|
247
|
+
)
|
|
248
|
+
: vertexBaseColor;
|
|
249
|
+
const dryRoughness = armSample
|
|
250
|
+
? mix(float(0.82), float(1.0), armSample.g)
|
|
251
|
+
: float(DRY_ROUGHNESS);
|
|
252
|
+
|
|
253
|
+
// Damp sand darkens substantially but remains saturated. Exposed active
|
|
254
|
+
// foam must still read after the thin water geometry has retreated, so blend
|
|
255
|
+
// toward the physical foam albedo; residue receives a quieter contribution.
|
|
256
|
+
const wetScale = float(1.0).sub(moisture.mul(uniforms.uShoreWetDarkening));
|
|
257
|
+
const wetBaseColor = dryBaseColor.mul(wetScale);
|
|
258
|
+
// The water mesh is deliberately clipped at the signed swash head. The
|
|
259
|
+
// same temporal state extends about one source-cell onto exposed wet sand,
|
|
260
|
+
// so render coherent active foam here as the thin sand-side half of the
|
|
261
|
+
// shoreline lace. Without this shared fringe the water material can only
|
|
262
|
+
// show the wet-side half and every raft looks cut off by the mesh edge.
|
|
263
|
+
// Keep a firm onset so weak advected state still cannot become the broad
|
|
264
|
+
// grey clouds that an earlier raw-state blend produced.
|
|
265
|
+
const visibleActiveFoam = smoothstep(0.46, 0.68, activeFoam);
|
|
266
|
+
const visibleResidue = smoothstep(0.3, 0.68, residue);
|
|
267
|
+
const foamCoverage = clamp(
|
|
268
|
+
visibleActiveFoam.mul(0.72).add(visibleResidue.mul(0.12)).mul(foamAmountNode),
|
|
269
|
+
0.0,
|
|
270
|
+
0.74,
|
|
271
|
+
);
|
|
272
|
+
const shoreColor = mix(wetBaseColor, uniforms.uShoreFoamColor, foamCoverage);
|
|
273
|
+
|
|
274
|
+
// Saturated grains remain mostly diffuse; the fresh micron-thin film owns
|
|
275
|
+
// the sharp gloss. Aerated/stranded foam pushes the lobe back toward matte.
|
|
276
|
+
const gloss = max(film, moisture.mul(0.24));
|
|
277
|
+
const foamRoughness = clamp(
|
|
278
|
+
presentedActiveFoam.mul(0.72).add(presentedResidue.mul(0.32)),
|
|
279
|
+
0.0,
|
|
280
|
+
1.0,
|
|
281
|
+
);
|
|
282
|
+
const roughness = mix(dryRoughness, uniforms.uShoreWetRoughness, gloss);
|
|
283
|
+
const clearcoatOcclusion = clamp(
|
|
284
|
+
presentedActiveFoam.mul(0.72).add(presentedResidue.mul(0.18)),
|
|
285
|
+
0.0,
|
|
286
|
+
0.88,
|
|
287
|
+
)
|
|
288
|
+
.oneMinus();
|
|
289
|
+
|
|
290
|
+
const material = new MeshPhysicalNodeMaterial({ vertexColors: false });
|
|
291
|
+
material.name = 'WaterShoreMaterial';
|
|
292
|
+
// Fold the underwater light into albedo instead of assigning emissiveNode.
|
|
293
|
+
// Some node backends replace the normal lit path when an otherwise-zero
|
|
294
|
+
// custom emissive graph is present, which made the dry beach charcoal while
|
|
295
|
+
// the camera was above water. The shared enabled uniform keeps this addend
|
|
296
|
+
// exactly zero outside the underwater pass.
|
|
297
|
+
material.colorNode = shoreColor.add(projectedWaterCaustics(positionWorld, normalWorld));
|
|
298
|
+
material.metalnessNode = float(0.0);
|
|
299
|
+
material.roughnessNode = mix(roughness, float(0.88), foamRoughness);
|
|
300
|
+
if (armSample) material.aoNode = mix(float(0.9), float(1.0), armSample.r);
|
|
301
|
+
if (normalMap?.isTexture) {
|
|
302
|
+
material.normalNode = normalMapNode(
|
|
303
|
+
texture(normalMap).sample(tiledUv).rgb,
|
|
304
|
+
vec2(0.24, 0.24),
|
|
305
|
+
);
|
|
306
|
+
}
|
|
307
|
+
material.clearcoatNode = film.mul(uniforms.uShoreWetClearcoat).mul(clearcoatOcclusion);
|
|
308
|
+
material.clearcoatRoughnessNode = mix(0.32, 0.1, film);
|
|
309
|
+
material.specularIntensityNode = mix(0.5, 1.0, film);
|
|
310
|
+
material.uniforms = uniforms;
|
|
311
|
+
material.userData.waterShoreMaterial = true;
|
|
312
|
+
material.userData.waterShoreStateField = stateField;
|
|
313
|
+
|
|
314
|
+
return updateWaterShoreMaterial(material, {
|
|
315
|
+
stateField,
|
|
316
|
+
foamColor,
|
|
317
|
+
foamAmount,
|
|
318
|
+
wetDarkening,
|
|
319
|
+
wetRoughness,
|
|
320
|
+
wetClearcoat,
|
|
321
|
+
});
|
|
322
|
+
}
|