@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
|
@@ -116,6 +116,7 @@ export const WATER_DEBUG_MODES = Object.freeze({
|
|
|
116
116
|
specular: 7,
|
|
117
117
|
fresnel: 8,
|
|
118
118
|
crest: 9,
|
|
119
|
+
shoreState: 10,
|
|
119
120
|
});
|
|
120
121
|
|
|
121
122
|
// The number of Gerstner components evaluated by the shader and the CPU
|
|
@@ -126,8 +127,9 @@ export const DEFAULT_WATER_SETTINGS = Object.freeze({
|
|
|
126
127
|
preset: 'lake',
|
|
127
128
|
colorTone: 'classic',
|
|
128
129
|
|
|
129
|
-
//
|
|
130
|
-
// steepness, and phase speed before the per-wave spectrum is
|
|
130
|
+
// Authored baseline: 0 = glassy mirror, 1 = storm swell. Scales wave
|
|
131
|
+
// amplitude, steepness, and phase speed before the per-wave spectrum is
|
|
132
|
+
// built. Scene weather may transiently override it on WaterSurface.
|
|
131
133
|
waveIntensity: 0.25,
|
|
132
134
|
waterLevel: 0.36,
|
|
133
135
|
|
|
@@ -150,6 +152,10 @@ export const DEFAULT_WATER_SETTINGS = Object.freeze({
|
|
|
150
152
|
// face above the rest waterline. Scales with total wave energy, so storms
|
|
151
153
|
// reach visibly further up the sand. 0 pins the waterline in place.
|
|
152
154
|
shorelineRunup: 0.6,
|
|
155
|
+
// Maximum horizontal run-up in meters. Individual wave-group events reach
|
|
156
|
+
// 80–100% of this bound, begin at the preceding rundown endpoint, and end
|
|
157
|
+
// at their own varied endpoint. 0 = automatic (energy-scaled run-up).
|
|
158
|
+
runupDistance: 0,
|
|
153
159
|
|
|
154
160
|
// Master switch for the breaker system: false removes the mesh and skips
|
|
155
161
|
// all rebuild work entirely (handy for A/B perf comparisons).
|
|
@@ -191,6 +197,11 @@ export const DEFAULT_WATER_SETTINGS = Object.freeze({
|
|
|
191
197
|
deepFadeDistance: 2.2,
|
|
192
198
|
opacity: 0.8,
|
|
193
199
|
refractionStrength: 0.35,
|
|
200
|
+
// Physical anchor for the stylized underside. 1.333 produces the familiar
|
|
201
|
+
// water-to-air Snell window and total-internal-reflection cutoff.
|
|
202
|
+
indexOfRefraction: 1.333,
|
|
203
|
+
underwaterTransmission: 1.0,
|
|
204
|
+
underwaterTintStrength: 0.35,
|
|
194
205
|
causticsStrength: 0.55,
|
|
195
206
|
causticsScale: 0.8,
|
|
196
207
|
causticsSpeed: 0.6,
|
|
@@ -198,13 +209,28 @@ export const DEFAULT_WATER_SETTINGS = Object.freeze({
|
|
|
198
209
|
// Foam.
|
|
199
210
|
foamColor: [0.94, 1.0, 0.99],
|
|
200
211
|
foamAmount: 1.0,
|
|
212
|
+
// Independent gain for the foam carried by the beach swash. Keeping this
|
|
213
|
+
// separate prevents stronger run-up foam from turning offshore contact
|
|
214
|
+
// bands and whitecaps into solid paint.
|
|
215
|
+
swashFoamAmount: 1.15,
|
|
216
|
+
// Stateful beach foam remains aerated for a few seconds, then converts to
|
|
217
|
+
// thinner residue instead of disappearing when the procedural cycle resets.
|
|
218
|
+
swashFoamLifetime: 4.0,
|
|
219
|
+
swashFoamResidueLifetime: 10.0,
|
|
220
|
+
// Wet sand remembers inundation much longer than the visible surface film.
|
|
221
|
+
// The short sheen is intentionally derived separately by the shore-state
|
|
222
|
+
// simulation, so a beach can stay dark without looking permanently glazed.
|
|
223
|
+
wetSandDryTime: 120.0,
|
|
224
|
+
wetSandDarkening: 0.58,
|
|
225
|
+
wetSandSheen: 0.78,
|
|
201
226
|
foamContactDistance: 0.4,
|
|
202
227
|
foamLineSpacing: 0.55,
|
|
203
228
|
foamNoiseScale: 0.6,
|
|
204
229
|
whitecapAmount: 0.05,
|
|
205
230
|
rippleFoamStrength: 0.8,
|
|
206
231
|
|
|
207
|
-
//
|
|
232
|
+
// Authored lighting/reflection fallback. A scene rig may transiently replace
|
|
233
|
+
// the sun and sky fields on WaterSurface without editing this water asset.
|
|
208
234
|
sunDirection: [0.35, 0.8, 0.45],
|
|
209
235
|
sunColor: [1.0, 0.96, 0.86],
|
|
210
236
|
specularStrength: 0.8,
|
|
@@ -304,6 +330,7 @@ const WATER_PRESETS = Object.freeze({
|
|
|
304
330
|
whitecapAmount: 0.22,
|
|
305
331
|
foamContactDistance: 0.6,
|
|
306
332
|
foamAmount: 1.3,
|
|
333
|
+
swashFoamAmount: 1.35,
|
|
307
334
|
rippleFoamStrength: 0.9,
|
|
308
335
|
breakerAmount: 0.55,
|
|
309
336
|
waveSetPeriod: 50,
|
|
@@ -328,6 +355,7 @@ const WATER_PRESETS = Object.freeze({
|
|
|
328
355
|
reflectionSoftness: 0.35,
|
|
329
356
|
whitecapAmount: 0.4,
|
|
330
357
|
foamAmount: 1.2,
|
|
358
|
+
swashFoamAmount: 1.25,
|
|
331
359
|
specularStretch: 0.5,
|
|
332
360
|
sparkleStrength: 0.65,
|
|
333
361
|
reflectionStrength: 0.6,
|
|
@@ -358,6 +386,7 @@ const WATER_PRESETS = Object.freeze({
|
|
|
358
386
|
reflectionSoftness: 0.3,
|
|
359
387
|
causticsStrength: 0.08,
|
|
360
388
|
foamAmount: 1.25,
|
|
389
|
+
swashFoamAmount: 1.45,
|
|
361
390
|
whitecapAmount: 0.55,
|
|
362
391
|
rippleFoamStrength: 1.0,
|
|
363
392
|
sunColor: [0.9, 0.88, 0.85],
|
|
@@ -544,6 +573,7 @@ export function createWaterSettings(options = {}) {
|
|
|
544
573
|
shoalingDepth: clampedNumber(source.shoalingDepth, base.shoalingDepth, 0.05, 12),
|
|
545
574
|
shorelineWaves: clampedNumber(source.shorelineWaves, base.shorelineWaves, 0, 1),
|
|
546
575
|
shorelineRunup: clampedNumber(source.shorelineRunup, base.shorelineRunup, 0, 3),
|
|
576
|
+
runupDistance: clampedNumber(source.runupDistance, base.runupDistance, 0, 15),
|
|
547
577
|
breakerEnabled: typeof source.breakerEnabled === 'boolean'
|
|
548
578
|
? source.breakerEnabled
|
|
549
579
|
: base.breakerEnabled !== false,
|
|
@@ -566,12 +596,33 @@ export function createWaterSettings(options = {}) {
|
|
|
566
596
|
deepFadeDistance: clampedNumber(tone.deepFadeDistance ?? source.deepFadeDistance, base.deepFadeDistance, 0.01, 120),
|
|
567
597
|
opacity: clampedNumber(source.opacity, base.opacity, 0, 1),
|
|
568
598
|
refractionStrength: clampedNumber(source.refractionStrength, base.refractionStrength, 0, 3),
|
|
599
|
+
indexOfRefraction: clampedNumber(
|
|
600
|
+
source.indexOfRefraction, base.indexOfRefraction, 1.0001, 1.8,
|
|
601
|
+
),
|
|
602
|
+
underwaterTransmission: clampedNumber(
|
|
603
|
+
source.underwaterTransmission, base.underwaterTransmission, 0, 1,
|
|
604
|
+
),
|
|
605
|
+
underwaterTintStrength: clampedNumber(
|
|
606
|
+
source.underwaterTintStrength, base.underwaterTintStrength, 0, 1,
|
|
607
|
+
),
|
|
569
608
|
causticsStrength: clampedNumber(tone.causticsStrength ?? source.causticsStrength, base.causticsStrength, 0, 4),
|
|
570
609
|
causticsScale: clampedNumber(source.causticsScale, base.causticsScale, 0.02, 12),
|
|
571
610
|
causticsSpeed: clampedNumber(source.causticsSpeed, base.causticsSpeed, 0, 8),
|
|
572
611
|
|
|
573
612
|
foamColor: colorArray(source.foamColor, base.foamColor),
|
|
574
613
|
foamAmount: clampedNumber(source.foamAmount, base.foamAmount, 0, 2),
|
|
614
|
+
swashFoamAmount: clampedNumber(source.swashFoamAmount, base.swashFoamAmount, 0, 2),
|
|
615
|
+
swashFoamLifetime: clampedNumber(
|
|
616
|
+
source.swashFoamLifetime, base.swashFoamLifetime, 0.25, 30,
|
|
617
|
+
),
|
|
618
|
+
swashFoamResidueLifetime: clampedNumber(
|
|
619
|
+
source.swashFoamResidueLifetime, base.swashFoamResidueLifetime, 0.5, 60,
|
|
620
|
+
),
|
|
621
|
+
wetSandDryTime: clampedNumber(source.wetSandDryTime, base.wetSandDryTime, 2, 600),
|
|
622
|
+
wetSandDarkening: clampedNumber(
|
|
623
|
+
source.wetSandDarkening, base.wetSandDarkening, 0, 1,
|
|
624
|
+
),
|
|
625
|
+
wetSandSheen: clampedNumber(source.wetSandSheen, base.wetSandSheen, 0, 1),
|
|
575
626
|
foamContactDistance: clampedNumber(source.foamContactDistance, base.foamContactDistance, 0.01, 8),
|
|
576
627
|
foamLineSpacing: clampedNumber(source.foamLineSpacing, base.foamLineSpacing, 0.05, 8),
|
|
577
628
|
foamNoiseScale: clampedNumber(source.foamNoiseScale, base.foamNoiseScale, 0.02, 12),
|
|
@@ -702,37 +753,289 @@ export function buildGerstnerWaves(settings) {
|
|
|
702
753
|
// accurate enough for buoyancy and interaction tests. chopWeight mirrors the
|
|
703
754
|
// shader's shallow-water spectrum filter: slots 0/1 (the dominant swell and
|
|
704
755
|
// its set beat partner) always pass at full strength, shorter cross chop
|
|
705
|
-
// fades toward the surf zone.
|
|
706
|
-
|
|
756
|
+
// fades toward the surf zone. The optional nearshore sample mirrors the
|
|
757
|
+
// vertex shader's depth-blended q(x,z) phase coordinate for slot 0 and, when
|
|
758
|
+
// the slot mask permits it, its authored same-direction beat partner in slot 1.
|
|
759
|
+
export function sampleGerstnerHeight(
|
|
760
|
+
waves,
|
|
761
|
+
x,
|
|
762
|
+
z,
|
|
763
|
+
time,
|
|
764
|
+
chopWeight = 1,
|
|
765
|
+
nearshore = null,
|
|
766
|
+
) {
|
|
767
|
+
const nearshoreBlend = THREE.MathUtils.clamp(Number(nearshore?.blend) || 0, 0, 1);
|
|
768
|
+
const nearshoreSlotMask = Number.isFinite(Number(nearshore?.slotMask))
|
|
769
|
+
? THREE.MathUtils.clamp(Number(nearshore.slotMask), 0, 2)
|
|
770
|
+
: 2;
|
|
707
771
|
let height = 0;
|
|
708
772
|
for (let i = 0; i < waves.length; i += 1) {
|
|
709
773
|
const wave = waves[i];
|
|
710
|
-
const
|
|
774
|
+
const baseCoordinate = wave.dirX * x + wave.dirZ * z;
|
|
775
|
+
const slotWeight = i === 0
|
|
776
|
+
? Math.min(nearshoreSlotMask, 1)
|
|
777
|
+
: i === 1
|
|
778
|
+
? Math.max(nearshoreSlotMask - 1, 0)
|
|
779
|
+
: 0;
|
|
780
|
+
const slotBlend = nearshoreBlend * slotWeight;
|
|
781
|
+
const phaseCoordinate = slotBlend > 0
|
|
782
|
+
? THREE.MathUtils.lerp(baseCoordinate, nearshore.phaseCoordinate, slotBlend)
|
|
783
|
+
: baseCoordinate;
|
|
784
|
+
const theta = wave.waveNumber * phaseCoordinate -
|
|
711
785
|
wave.omega * time + wave.phase;
|
|
712
786
|
height += wave.amplitude * (i < 2 ? 1 : chopWeight) * Math.sin(theta);
|
|
713
787
|
}
|
|
714
788
|
return height;
|
|
715
789
|
}
|
|
716
790
|
|
|
791
|
+
// CPU mirror of createWaterWavesChunk().gerstnerSwellHeight: only the two
|
|
792
|
+
// long components that survive into the surf zone. Swash samples this at a
|
|
793
|
+
// projected shoreline point to keep its edge connected across the beach.
|
|
794
|
+
export function sampleGerstnerSwellHeight(waves, x, z, time, nearshore = null) {
|
|
795
|
+
const nearshoreBlend = THREE.MathUtils.clamp(Number(nearshore?.blend) || 0, 0, 1);
|
|
796
|
+
const nearshoreSlotMask = Number.isFinite(Number(nearshore?.slotMask))
|
|
797
|
+
? THREE.MathUtils.clamp(Number(nearshore.slotMask), 0, 2)
|
|
798
|
+
: 2;
|
|
799
|
+
let height = 0;
|
|
800
|
+
for (let i = 0; i < Math.min(2, waves.length); i += 1) {
|
|
801
|
+
const wave = waves[i];
|
|
802
|
+
const baseCoordinate = wave.dirX * x + wave.dirZ * z;
|
|
803
|
+
const slotWeight = i === 0
|
|
804
|
+
? Math.min(nearshoreSlotMask, 1)
|
|
805
|
+
: Math.max(nearshoreSlotMask - 1, 0);
|
|
806
|
+
const slotBlend = nearshoreBlend * slotWeight;
|
|
807
|
+
const phaseCoordinate = slotBlend > 0
|
|
808
|
+
? THREE.MathUtils.lerp(baseCoordinate, nearshore.phaseCoordinate, slotBlend)
|
|
809
|
+
: baseCoordinate;
|
|
810
|
+
const theta = wave.waveNumber * phaseCoordinate -
|
|
811
|
+
wave.omega * time + wave.phase;
|
|
812
|
+
height += wave.amplitude * Math.sin(theta);
|
|
813
|
+
}
|
|
814
|
+
return height;
|
|
815
|
+
}
|
|
816
|
+
|
|
817
|
+
// 0..1 cycle of the primary crest at the rest shoreline. Zero is crest
|
|
818
|
+
// arrival; the shader uses the same cycle for the connected swash event.
|
|
819
|
+
export function samplePrimarySwellSequence(waves, time) {
|
|
820
|
+
const primary = waves?.[0];
|
|
821
|
+
if (!primary) return { cycle: 0, index: 0 };
|
|
822
|
+
const raw = (primary.omega * time - primary.phase + Math.PI * 0.5) / (Math.PI * 2);
|
|
823
|
+
const index = Math.floor(raw);
|
|
824
|
+
return { cycle: raw - index, index };
|
|
825
|
+
}
|
|
826
|
+
|
|
827
|
+
export function samplePrimarySwellCycle(waves, time) {
|
|
828
|
+
return samplePrimarySwellSequence(waves, time).cycle;
|
|
829
|
+
}
|
|
830
|
+
|
|
831
|
+
// One physical swash event: fast uprush, slower gravity-driven backwash.
|
|
832
|
+
// Unlike a signed sine, this never drains the sea below the rest shoreline.
|
|
833
|
+
export function shapeSwashProgress(cycle, uprushFraction = 0.34) {
|
|
834
|
+
const phase = ((Number(cycle) || 0) % 1 + 1) % 1;
|
|
835
|
+
const riseEnd = THREE.MathUtils.clamp(uprushFraction, 0.1, 0.8);
|
|
836
|
+
if (phase <= riseEnd) {
|
|
837
|
+
return Math.sin((phase / riseEnd) * Math.PI * 0.5);
|
|
838
|
+
}
|
|
839
|
+
const drain = (phase - riseEnd) / (1 - riseEnd);
|
|
840
|
+
return Math.sin((1 - drain) * Math.PI * 0.5);
|
|
841
|
+
}
|
|
842
|
+
|
|
843
|
+
function swashHash(value) {
|
|
844
|
+
let x = ((value * 0.1031) % 1 + 1) % 1;
|
|
845
|
+
x *= x + 33.33;
|
|
846
|
+
x *= x + x;
|
|
847
|
+
return ((x % 1) + 1) % 1;
|
|
848
|
+
}
|
|
849
|
+
|
|
850
|
+
// Low-frequency shoreline shape for one swash event. This is CPU-authored so
|
|
851
|
+
// the visible water, persistent foam pass, and gameplay queries all receive
|
|
852
|
+
// the same bounded tongue pattern without adding procedural noise to the
|
|
853
|
+
// private-memory-heavy visible fragment shader.
|
|
854
|
+
export function sampleSwashEventShape(cycleIndex) {
|
|
855
|
+
const index = Math.floor(Number(cycleIndex) || 0);
|
|
856
|
+
return {
|
|
857
|
+
phase: swashHash(index + 113.17) * Math.PI * 2,
|
|
858
|
+
frequency: THREE.MathUtils.lerp(0.085, 0.16, swashHash(index + 197.31)),
|
|
859
|
+
amplitude: THREE.MathUtils.lerp(0.55, 1.05, swashHash(index + 251.73)),
|
|
860
|
+
};
|
|
861
|
+
}
|
|
862
|
+
|
|
863
|
+
// Per-event forcing for an irregular swash train. A four-wave interpolated
|
|
864
|
+
// group term supplies the observed low-frequency envelope; the individual
|
|
865
|
+
// term keeps neighbouring bores from sharing one reach. Backwash strength is
|
|
866
|
+
// correlated with the event energy but retains its own variability.
|
|
867
|
+
function sampleSwashForcing(cycleIndex) {
|
|
868
|
+
const groupPosition = cycleIndex / 4;
|
|
869
|
+
const groupIndex = Math.floor(groupPosition);
|
|
870
|
+
const groupT = groupPosition - groupIndex;
|
|
871
|
+
const groupEase = groupT * groupT * (3 - 2 * groupT);
|
|
872
|
+
const group = THREE.MathUtils.lerp(
|
|
873
|
+
swashHash(groupIndex + 19.19),
|
|
874
|
+
swashHash(groupIndex + 20.19),
|
|
875
|
+
groupEase,
|
|
876
|
+
);
|
|
877
|
+
const individual = swashHash(cycleIndex + 7.73);
|
|
878
|
+
const baseRunupScale = 0.82 + group * 0.1 + individual * 0.08;
|
|
879
|
+
const normalizedRunup = THREE.MathUtils.clamp((baseRunupScale - 0.8) / 0.2, 0, 1);
|
|
880
|
+
const backwashStrength = THREE.MathUtils.clamp(
|
|
881
|
+
normalizedRunup * 0.62 + swashHash(cycleIndex + 71.37) * 0.38,
|
|
882
|
+
0,
|
|
883
|
+
1,
|
|
884
|
+
);
|
|
885
|
+
return { backwashStrength, baseRunupScale };
|
|
886
|
+
}
|
|
887
|
+
|
|
888
|
+
// Bounded stylized event statistics, informed by random-wave run-up and
|
|
889
|
+
// swash-interaction measurements: ordinary peaks cover 80–100% of the user
|
|
890
|
+
// reach, wave groups correlate several events, and a deep preceding rundown
|
|
891
|
+
// can lend a small amount of momentum to the next bore. The carry is capped
|
|
892
|
+
// at 2% of the authored reach (20 cm for the 10 m calibration beach), so it
|
|
893
|
+
// never overwhelms the event's own forcing. `rundownOffset` is metres relative
|
|
894
|
+
// to the still-water shoreline (negative = farther seaward).
|
|
895
|
+
export function sampleSwashCycleVariation(cycleIndex) {
|
|
896
|
+
const index = Math.floor(Number(cycleIndex) || 0);
|
|
897
|
+
const current = sampleSwashForcing(index);
|
|
898
|
+
const previous = sampleSwashForcing(index - 1);
|
|
899
|
+
const backwashCarry = Math.max(previous.backwashStrength - 0.5, 0) * 0.04;
|
|
900
|
+
return {
|
|
901
|
+
backwashStrength: current.backwashStrength,
|
|
902
|
+
backwashCarry,
|
|
903
|
+
baseRunupScale: current.baseRunupScale,
|
|
904
|
+
rundownOffset: THREE.MathUtils.lerp(0.35, -0.9, current.backwashStrength),
|
|
905
|
+
runupScale: THREE.MathUtils.clamp(current.baseRunupScale + backwashCarry, 0.8, 1),
|
|
906
|
+
};
|
|
907
|
+
}
|
|
908
|
+
|
|
909
|
+
// Continuous centerline position of the swash edge in metres along the beach.
|
|
910
|
+
// Event N begins exactly at event N-1's rundown endpoint, rises to its own
|
|
911
|
+
// varying inland maximum, then drains to a new endpoint without a reset jump.
|
|
912
|
+
export function sampleSwashDistance(waves, time, runupDistance, uprushFraction = 0.34) {
|
|
913
|
+
const sequence = samplePrimarySwellSequence(waves, time);
|
|
914
|
+
const current = sampleSwashCycleVariation(sequence.index);
|
|
915
|
+
const previous = sampleSwashCycleVariation(sequence.index - 1);
|
|
916
|
+
const progress = shapeSwashProgress(sequence.cycle, uprushFraction);
|
|
917
|
+
const peak = Math.max(Number(runupDistance) || 0, 0) * current.runupScale;
|
|
918
|
+
return sequence.cycle <= uprushFraction
|
|
919
|
+
? THREE.MathUtils.lerp(previous.rundownOffset, peak, progress)
|
|
920
|
+
: THREE.MathUtils.lerp(current.rundownOffset, peak, progress);
|
|
921
|
+
}
|
|
922
|
+
|
|
923
|
+
// One CPU-authored frame shared by the visible swash and the persistent
|
|
924
|
+
// shore-state pass. Keeping event identity, incidence, and derivatives here
|
|
925
|
+
// prevents a foam/wetness texture from becoming a second animation with a
|
|
926
|
+
// slightly different phase or direction.
|
|
927
|
+
export function sampleSwashFrameState(
|
|
928
|
+
waves,
|
|
929
|
+
time,
|
|
930
|
+
runupDistance = 0,
|
|
931
|
+
uprushFraction = 0.34,
|
|
932
|
+
) {
|
|
933
|
+
const sequence = samplePrimarySwellSequence(waves, time);
|
|
934
|
+
const current = sampleSwashCycleVariation(sequence.index);
|
|
935
|
+
const previous = sampleSwashCycleVariation(sequence.index - 1);
|
|
936
|
+
const edgeShape = sampleSwashEventShape(sequence.index);
|
|
937
|
+
const progress = shapeSwashProgress(sequence.cycle, uprushFraction);
|
|
938
|
+
const derivativeStep = 0.02;
|
|
939
|
+
const beforeSequence = samplePrimarySwellSequence(waves, time - derivativeStep);
|
|
940
|
+
const afterSequence = samplePrimarySwellSequence(waves, time + derivativeStep);
|
|
941
|
+
const progressSpeed = (
|
|
942
|
+
shapeSwashProgress(afterSequence.cycle, uprushFraction) -
|
|
943
|
+
shapeSwashProgress(beforeSequence.cycle, uprushFraction)
|
|
944
|
+
) / (derivativeStep * 2);
|
|
945
|
+
const maximumDistance = Math.max(Number(runupDistance) || 0, 0);
|
|
946
|
+
const edgeDistance = maximumDistance > 0
|
|
947
|
+
? sampleSwashDistance(waves, time, maximumDistance, uprushFraction)
|
|
948
|
+
: 0;
|
|
949
|
+
const edgeDistanceSpeed = maximumDistance > 0
|
|
950
|
+
? (
|
|
951
|
+
sampleSwashDistance(waves, time + derivativeStep, maximumDistance, uprushFraction) -
|
|
952
|
+
sampleSwashDistance(waves, time - derivativeStep, maximumDistance, uprushFraction)
|
|
953
|
+
) / (derivativeStep * 2)
|
|
954
|
+
: 0;
|
|
955
|
+
return {
|
|
956
|
+
cycle: sequence.cycle,
|
|
957
|
+
cycleSpeed: Math.max(Number(waves?.[0]?.omega) || 0, 0) / (Math.PI * 2),
|
|
958
|
+
edgeDistance,
|
|
959
|
+
edgeDistanceSpeed,
|
|
960
|
+
eventIndex: sequence.index,
|
|
961
|
+
isUprush: sequence.cycle < uprushFraction,
|
|
962
|
+
primaryDirectionX: waves?.[0]?.dirX ?? 0,
|
|
963
|
+
primaryDirectionZ: waves?.[0]?.dirZ ?? -1,
|
|
964
|
+
progress,
|
|
965
|
+
progressSpeed,
|
|
966
|
+
runupScale: current.runupScale,
|
|
967
|
+
startOffset: previous.rundownOffset,
|
|
968
|
+
endOffset: current.rundownOffset,
|
|
969
|
+
edgeShape,
|
|
970
|
+
};
|
|
971
|
+
}
|
|
972
|
+
|
|
973
|
+
// Connected oblique lip: the large term follows the incoming crest angle and
|
|
974
|
+
// small traveling scallops prevent a ruler-straight shoreline. A residual
|
|
975
|
+
// envelope at nominal rest/full reach represents the alongshore arrival lag;
|
|
976
|
+
// the centerline remains the 0..runupDistance calibration reference.
|
|
977
|
+
export function sampleSwashEdgeOffset(
|
|
978
|
+
x,
|
|
979
|
+
time,
|
|
980
|
+
progress,
|
|
981
|
+
waveDirectionX = 0,
|
|
982
|
+
cycle = 0,
|
|
983
|
+
edgeShape = null,
|
|
984
|
+
) {
|
|
985
|
+
const p = THREE.MathUtils.clamp(Number(progress) || 0, 0, 1);
|
|
986
|
+
const envelope = THREE.MathUtils.lerp(0.18, 1, Math.sin(Math.PI * p));
|
|
987
|
+
// A literal infinite oblique line grows without bound across a wide water
|
|
988
|
+
// tile. The old implementation then hard-clamped that line to the event's
|
|
989
|
+
// run-up maximum, pinning tens of metres of shore to one ruler-straight
|
|
990
|
+
// endpoint before releasing it a mesh column at a time. Soft-sign retains
|
|
991
|
+
// the incidence angle around the camera while approaching a finite offset
|
|
992
|
+
// smoothly, so it never creates a saturated plateau.
|
|
993
|
+
const incidenceSlope = THREE.MathUtils.clamp(
|
|
994
|
+
Number(waveDirectionX) * 0.52,
|
|
995
|
+
-0.2,
|
|
996
|
+
0.2,
|
|
997
|
+
);
|
|
998
|
+
const rawTilt = -x * incidenceSlope;
|
|
999
|
+
const maximumTilt = 2.0;
|
|
1000
|
+
const tilt = rawTilt / (1 + Math.abs(rawTilt) / maximumTilt);
|
|
1001
|
+
const scallop = (
|
|
1002
|
+
Math.sin(x * 0.32 - time * 0.35) - Math.sin(-time * 0.35) +
|
|
1003
|
+
(Math.sin(x * 0.91 + time * 0.18) - Math.sin(time * 0.18)) * 0.35
|
|
1004
|
+
) * 0.4;
|
|
1005
|
+
const shape = edgeShape ?? { phase: 0, frequency: 0.1, amplitude: 0 };
|
|
1006
|
+
const phase = Number(shape.phase) || 0;
|
|
1007
|
+
const frequency = THREE.MathUtils.clamp(Number(shape.frequency) || 0.1, 0.02, 0.5);
|
|
1008
|
+
const amplitude = THREE.MathUtils.clamp(Number(shape.amplitude) || 0, 0, 2.5);
|
|
1009
|
+
const secondaryPhase = phase * -0.71;
|
|
1010
|
+
const macroBase = (
|
|
1011
|
+
Math.sin(x * frequency + phase) - Math.sin(phase) +
|
|
1012
|
+
(Math.sin(x * frequency * 2.35 + secondaryPhase) - Math.sin(secondaryPhase)) * 0.42
|
|
1013
|
+
) * amplitude;
|
|
1014
|
+
const macroWave = Math.sin(THREE.MathUtils.clamp(Number(cycle) || 0, 0, 1) * Math.PI);
|
|
1015
|
+
const macroEnvelope = macroWave * macroWave;
|
|
1016
|
+
return (tilt + scallop) * envelope + macroBase * macroEnvelope;
|
|
1017
|
+
}
|
|
1018
|
+
|
|
717
1019
|
// --- Field schema -----------------------------------------------------------
|
|
718
1020
|
|
|
719
1021
|
export const WATER_SETTING_GROUPS = Object.freeze([
|
|
720
1022
|
Object.freeze({ id: 'waves', label: 'Waves', description: 'Gerstner swell and detail ripple shaping.' }),
|
|
721
1023
|
Object.freeze({ id: 'surface', label: 'Surface', description: 'Water body color, refraction, and caustics.' }),
|
|
722
1024
|
Object.freeze({ id: 'foam', label: 'Foam', description: 'Shoreline foam, whitecaps, and wake foam.' }),
|
|
723
|
-
Object.freeze({ id: 'lighting', label: 'Lighting', description: '
|
|
1025
|
+
Object.freeze({ id: 'lighting', label: 'Lighting', description: 'Authored fallback sun/sky plus water-specific glint, fresnel, and reflection response.' }),
|
|
724
1026
|
Object.freeze({ id: 'ripples', label: 'Ripples', description: 'Interactive ripple simulation response.' }),
|
|
725
1027
|
Object.freeze({ id: 'splashes', label: 'Splashes', description: 'Procedural splash droplets, spray, and rings.' }),
|
|
726
1028
|
Object.freeze({ id: 'quality', label: 'Quality', description: 'Shader quality tier gating caustics, sparkles, and noise octaves.' }),
|
|
727
1029
|
]);
|
|
728
1030
|
|
|
729
1031
|
const FIELD_METADATA = {
|
|
730
|
-
waveIntensity: { group: 'waves', label: 'Wave Intensity', min: 0, max: 1, step: 0.01, description: '
|
|
1032
|
+
waveIntensity: { group: 'waves', label: 'Wave Intensity', min: 0, max: 1, step: 0.01, description: 'Authored baseline from glassy mirror (0) to storm swell (1); scene weather can transiently modulate it without changing the preset.' },
|
|
731
1033
|
waterLevel: { group: 'waves', label: 'Water Level', min: 0, max: 4, step: 0.01, description: 'World-space rest height of the surface; waves and run-up displace around it.' },
|
|
732
1034
|
waveAmplitude: { group: 'waves', label: 'Wave Amplitude', min: 0, max: 5, step: 0.01, description: 'Largest wave amplitude in meters at full intensity; 5 gives a 10 m crest-to-trough swell.' },
|
|
733
1035
|
shoalingDepth: { group: 'waves', label: 'Shoaling Depth', min: 0.05, max: 12, step: 0.05, description: 'Column depth in meters at which waves reach full height; shallower water shrinks them (needs a bed height sampler).' },
|
|
734
1036
|
shorelineWaves: { group: 'waves', label: 'Shoreline Waves', min: 0, max: 1, step: 0.01, description: 'Fraction of wave height that keeps rolling through the shallows as surf before dying at the waterline.' },
|
|
735
1037
|
shorelineRunup: { group: 'waves', label: 'Shoreline Run-up', min: 0, max: 3, step: 0.05, description: 'How far incoming waves wash a thin foam film up the beach; reach scales with wave energy.' },
|
|
1038
|
+
runupDistance: { group: 'waves', label: 'Max Run-up Distance', min: 0, max: 15, step: 0.5, description: 'Maximum horizontal reach in meters. Wave groups vary each event from 80–100%, and each backwash hands its endpoint into the next uprush. 0 lets wave energy decide.' },
|
|
736
1039
|
breakerEnabled: { group: 'waves', label: 'Breakers On', type: 'boolean', description: 'Master switch for the breaker system; off removes the mesh and skips all breaker work (for perf A/B).' },
|
|
737
1040
|
breakerAmount: { group: 'waves', label: 'Surf Breakers', min: 0, max: 1, step: 0.01, description: 'Dedicated curling breaker shells along the break line; 0 disables the system (needs a bed height sampler).' },
|
|
738
1041
|
breakerCurl: { group: 'waves', label: 'Breaker Curl', min: 0, max: 1, step: 0.01, description: 'Lip pitch: 0 spills down the face, 1 curls a full surfable tunnel.' },
|
|
@@ -772,20 +1075,29 @@ const FIELD_METADATA = {
|
|
|
772
1075
|
deepFadeDistance: { group: 'surface', label: 'Deep Fade', min: 0.05, max: 24, step: 0.05, description: 'Additional depth where mid fades to the deep tint.' },
|
|
773
1076
|
opacity: { group: 'surface', label: 'Opacity', min: 0, max: 1, step: 0.01, description: 'Base transparency when no scene color grab pass is bound.' },
|
|
774
1077
|
refractionStrength: { group: 'surface', label: 'Refraction', min: 0, max: 2, step: 0.01, description: 'Screen-space distortion of the underwater scene.' },
|
|
1078
|
+
indexOfRefraction: { group: 'surface', label: 'Water IOR', min: 1.0001, max: 1.8, step: 0.001, description: 'Index of refraction used by the underwater Snell window and total internal reflection.' },
|
|
1079
|
+
underwaterTransmission: { group: 'surface', label: 'Underwater View', min: 0, max: 1, step: 0.01, description: 'Visibility of the real above-water scene through the surface from below.' },
|
|
1080
|
+
underwaterTintStrength: { group: 'surface', label: 'Underwater Tint', min: 0, max: 1, step: 0.01, description: 'Stylized water-color tint applied to the view through the surface.' },
|
|
775
1081
|
causticsStrength: { group: 'surface', label: 'Caustics', min: 0, max: 3, step: 0.01, description: 'Brightness of the procedural voronoi caustics on the bottom.' },
|
|
776
1082
|
causticsScale: { group: 'surface', label: 'Caustics Scale', min: 0.05, max: 8, step: 0.05, description: 'Spatial frequency of the caustic web.' },
|
|
777
1083
|
causticsSpeed: { group: 'surface', label: 'Caustics Speed', min: 0, max: 4, step: 0.01, description: 'Animation speed of the caustic web.' },
|
|
778
1084
|
|
|
779
1085
|
foamColor: { group: 'foam', label: 'Foam Color', type: 'color', description: 'Color of all foam: shoreline, whitecaps, wakes, and splashes.' },
|
|
780
|
-
foamAmount: { group: 'foam', label: 'Foam Amount', min: 0, max: 2, step: 0.01, description: '
|
|
1086
|
+
foamAmount: { group: 'foam', label: 'Foam Amount', min: 0, max: 2, step: 0.01, description: 'Offshore contact foam, whitecap, and wake gain.' },
|
|
1087
|
+
swashFoamAmount: { group: 'foam', label: 'Swash Foam', min: 0, max: 2, step: 0.01, description: 'Independent gain for torn foam carried up and back down the beach.' },
|
|
1088
|
+
swashFoamLifetime: { group: 'foam', label: 'Swash Foam Life (s)', min: 0.25, max: 30, step: 0.25, description: 'Seconds fresh aerated swash foam remains before thinning into residue.' },
|
|
1089
|
+
swashFoamResidueLifetime: { group: 'foam', label: 'Foam Residue Life (s)', min: 0.5, max: 60, step: 0.5, description: 'Seconds fragmented beach foam persists and drifts after the active front passes.' },
|
|
1090
|
+
wetSandDryTime: { group: 'foam', label: 'Wet Sand Drying (s)', min: 2, max: 600, step: 1, description: 'Seconds saturated sand takes to return to its dry color after the water retreats.' },
|
|
1091
|
+
wetSandDarkening: { group: 'foam', label: 'Wet Sand Darkening', min: 0, max: 1, step: 0.01, description: 'How strongly remembered moisture darkens exposed sand.' },
|
|
1092
|
+
wetSandSheen: { group: 'foam', label: 'Wet Sand Sheen', min: 0, max: 1, step: 0.01, description: 'Strength of the short-lived glossy water film left on freshly exposed sand.' },
|
|
781
1093
|
foamContactDistance: { group: 'foam', label: 'Contact Distance', min: 0.02, max: 4, step: 0.01, description: 'Depth difference covered by the solid contact foam band.' },
|
|
782
1094
|
foamLineSpacing: { group: 'foam', label: 'Line Spacing', min: 0.05, max: 4, step: 0.01, description: 'Spacing of the animated lapping foam lines off the shore.' },
|
|
783
1095
|
foamNoiseScale: { group: 'foam', label: 'Foam Noise Scale', min: 0.05, max: 8, step: 0.05, description: 'Breakup noise frequency for foam edges.' },
|
|
784
1096
|
whitecapAmount: { group: 'foam', label: 'Whitecaps', min: 0, max: 1, step: 0.01, description: 'Coverage of breaking crests on open water.' },
|
|
785
1097
|
rippleFoamStrength: { group: 'foam', label: 'Wake Foam', min: 0, max: 3, step: 0.01, description: 'Foam intensity left behind by interactive ripples and wakes.' },
|
|
786
1098
|
|
|
787
|
-
sunDirection: { group: 'lighting', label: 'Sun Direction', type: 'vector3', description: '
|
|
788
|
-
sunColor: { group: 'lighting', label: 'Sun Color', type: 'color', description: '
|
|
1099
|
+
sunDirection: { group: 'lighting', label: 'Sun Direction', type: 'vector3', description: 'Authored fallback direction toward the sun when no live scene-light override is connected.' },
|
|
1100
|
+
sunColor: { group: 'lighting', label: 'Sun Color', type: 'color', description: 'Authored fallback sun tint for glints, sparkles, and caustics; a live scene rig may replace it transiently.' },
|
|
789
1101
|
specularStrength: { group: 'lighting', label: 'Specular', min: 0, max: 3, step: 0.01, description: 'Toon sun-glint intensity.' },
|
|
790
1102
|
specularShininess: { group: 'lighting', label: 'Shininess', min: 4, max: 2000, step: 1, description: 'Glint tightness; higher is smaller and sharper.' },
|
|
791
1103
|
specularStretch: { group: 'lighting', label: 'Glint Stretch', min: 0, max: 0.95, step: 0.01, description: 'Elongates glints along the sun azimuth into a sparkling sun path.' },
|
|
@@ -798,8 +1110,8 @@ const FIELD_METADATA = {
|
|
|
798
1110
|
fresnelPower: { group: 'lighting', label: 'Fresnel Power', min: 0.5, max: 12, step: 0.1, description: 'Falloff of the fresnel band toward the horizon.' },
|
|
799
1111
|
fresnelBias: { group: 'lighting', label: 'Fresnel Bias', min: 0, max: 0.6, step: 0.01, description: 'Sky-tint floor at steep angles; higher reads more anime-blue.' },
|
|
800
1112
|
fresnelColor: { group: 'lighting', label: 'Fresnel Color', type: 'color', description: 'Additive rim tint at grazing angles.' },
|
|
801
|
-
skyZenithColor: { group: 'lighting', label: 'Sky Zenith', type: 'color', description: '
|
|
802
|
-
skyHorizonColor: { group: 'lighting', label: 'Sky Horizon', type: 'color', description: '
|
|
1113
|
+
skyZenithColor: { group: 'lighting', label: 'Sky Zenith', type: 'color', description: 'Authored fallback procedural sky-reflection color overhead when no live scene sky is connected.' },
|
|
1114
|
+
skyHorizonColor: { group: 'lighting', label: 'Sky Horizon', type: 'color', description: 'Authored fallback procedural sky-reflection color at the horizon when no live scene sky is connected.' },
|
|
803
1115
|
reflectionStrength: { group: 'lighting', label: 'Reflection', min: 0, max: 1.5, step: 0.01, description: 'Planar/sky reflection mix, weighted by fresnel.' },
|
|
804
1116
|
reflectionDistortion: { group: 'lighting', label: 'Reflection Ripple', min: 0, max: 0.3, step: 0.005, description: 'How much waves shatter the reflection.' },
|
|
805
1117
|
reflectionSoftness: { group: 'lighting', label: 'Reflection Softness', min: 0, max: 1, step: 0.01, description: 'Blends sharp planar reflections toward the soft procedural sky (milky anime look).' },
|