@call-me-sensei/toonlab 0.4.21 → 0.4.23

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.
Files changed (129) hide show
  1. package/NPM-LIBRARY.md +87 -27
  2. package/README.md +11 -10
  3. package/agents/PROMPTS.md +17 -3
  4. package/agents/README.md +13 -3
  5. package/agents/claude/CLAUDE.md +3 -0
  6. package/agents/codex/AGENTS.md +112 -159
  7. package/agents/cursor/toonlab.mdc +7 -3
  8. package/agents/examples/game-foundation.mjs +104 -0
  9. package/agents/references/game-lifecycle.md +57 -0
  10. package/agents/references/runtime-entry-points.md +26 -0
  11. package/agents/references/style-bundles.md +4 -1
  12. package/agents/skills/claude/asset-sourcing/SKILL.md +7 -4
  13. package/agents/skills/claude/environment/SKILL.md +4 -0
  14. package/agents/skills/claude/game-dev/SKILL.md +63 -87
  15. package/agents/skills/claude/lighting/SKILL.md +36 -0
  16. package/agents/skills/claude/post-processing/SKILL.md +8 -3
  17. package/agents/skills/claude/rock-ground-shaders/SKILL.md +10 -15
  18. package/agents/skills/claude/rockgen/SKILL.md +52 -21
  19. package/agents/skills/claude/scene-style-application/SKILL.md +4 -0
  20. package/agents/skills/claude/style-presets/SKILL.md +11 -8
  21. package/agents/skills/claude/toon-shading/SKILL.md +4 -0
  22. package/agents/skills/claude/vegetation-sky/SKILL.md +13 -7
  23. package/agents/skills/claude/visual-verification/SKILL.md +45 -119
  24. package/agents/skills/claude/water/SKILL.md +6 -2
  25. package/agents/skills/codex/asset-sourcing/SKILL.md +7 -4
  26. package/agents/skills/codex/environment/SKILL.md +4 -0
  27. package/agents/skills/codex/game-dev/SKILL.md +63 -87
  28. package/agents/skills/codex/lighting/SKILL.md +36 -0
  29. package/agents/skills/codex/post-processing/SKILL.md +8 -3
  30. package/agents/skills/codex/rock-ground-shaders/SKILL.md +10 -15
  31. package/agents/skills/codex/rockgen/SKILL.md +52 -21
  32. package/agents/skills/codex/scene-style-application/SKILL.md +4 -0
  33. package/agents/skills/codex/style-presets/SKILL.md +11 -8
  34. package/agents/skills/codex/toon-shading/SKILL.md +4 -0
  35. package/agents/skills/codex/vegetation-sky/SKILL.md +13 -7
  36. package/agents/skills/codex/visual-verification/SKILL.md +45 -119
  37. package/agents/skills/codex/water/SKILL.md +6 -2
  38. package/database/apply-one-catalog-seed.mjs +63 -0
  39. package/database/providers.mjs +1 -0
  40. package/database/seeds/catalog/0005_2026-08-c8-first12.sql +1101 -0
  41. package/database/seeds/catalog/0006_2026-09-c8-first100.sql +14245 -0
  42. package/database/seeds/catalog/0007_2026-09-c8-first100-primary-model.sql +14845 -0
  43. package/mcp/lab-management.mjs +11 -8
  44. package/mcp/public-catalog.mjs +0 -16
  45. package/mcp/tree-lab-contract.mjs +2381 -0
  46. package/mcp/vite-plugin.mjs +5 -1
  47. package/package.json +29 -18
  48. package/src/catalog/officialCatalog.js +1 -0
  49. package/src/catalog/officialCatalogAssetRuntime.js +98 -1
  50. package/src/catalog/officialCatalogLod.js +160 -16
  51. package/src/catalog/officialCatalogPlacement.js +52 -12
  52. package/src/catalog/officialCatalogProvider.js +79 -8
  53. package/src/catalog/officialCatalogRockPackage.js +172 -0
  54. package/src/cloud/cloudReprojection.js +40 -7
  55. package/src/cloud/cloudVolume.js +44 -7
  56. package/src/cloud/noise/baseShapeVolume.js +8 -6
  57. package/src/cloud/noise/cirrusMap.js +2 -1
  58. package/src/cloud/noise/curlNoise.js +2 -1
  59. package/src/cloud/noise/erosionVolume.js +2 -1
  60. package/src/cloud/noise/textureCache.js +77 -0
  61. package/src/cloud/noise/weatherMap.js +2 -1
  62. package/src/core/sha256.js +83 -0
  63. package/src/ground-shader/groundShaderMaterial.js +12 -3
  64. package/src/rock-shader/rockMaterial.js +205 -28
  65. package/src/rock-shader/rockShaderRuntime.js +17 -1
  66. package/src/rock-shader/rockShaderSettings.js +16 -0
  67. package/src/rockgen/index.js +9 -0
  68. package/src/rockgen/lod/index.js +1 -0
  69. package/src/rockgen/lod/rockDenseFieldPolicy.js +143 -0
  70. package/src/rockgen/rockDocument.js +899 -35
  71. package/src/rockgen/surface/c7GeologySurface.js +39 -9
  72. package/src/rockgen/surface/naturalRockSurface.js +1948 -0
  73. package/src/shaders-tsl/chunks/projected-water-caustics.js +14 -2
  74. package/src/shaders-tsl/chunks/water-foam.js +6 -6
  75. package/src/shaders-tsl/chunks/water-lighting.js +28 -5
  76. package/src/shaders-tsl/chunks/water-shore-state.js +5 -2
  77. package/src/shaders-tsl/chunks/water-waves.js +10 -5
  78. package/src/shaders-tsl/flower.js +7 -4
  79. package/src/shaders-tsl/grass.js +4 -3
  80. package/src/shaders-tsl/water-shore-state-simulation.js +3 -2
  81. package/src/shaders-tsl/water.js +209 -41
  82. package/src/sky/skySystem.js +30 -7
  83. package/src/toon/settings/perspectiveRemovalSettings.js +1 -1
  84. package/src/toon/toonSettings.js +74 -0
  85. package/src/vegetation/stylizedFlower.js +6 -0
  86. package/src/version.js +1 -1
  87. package/src/water/waterDetailSpectrum.js +91 -0
  88. package/src/water/waterDynamics.js +422 -0
  89. package/src/water/waterFoamParticles.js +196 -0
  90. package/src/water/waterFoamTexture.js +57 -0
  91. package/src/water/waterHydrodynamics.js +209 -0
  92. package/src/water/waterMaterial.js +1 -0
  93. package/src/water/waterRenderExtension.js +107 -0
  94. package/src/water/waterScenePasses.js +14 -6
  95. package/src/water/waterSettings.js +53 -26
  96. package/src/water/waterShoreMaterial.js +38 -14
  97. package/src/water/waterSpectralOcean.js +137 -0
  98. package/src/water/waterStageSettings.js +19 -1
  99. package/src/water/waterSurface.js +62 -18
  100. package/src/water/waterUnderwaterAtmosphere.js +30 -3
  101. package/types/catalog/officialCatalog.d.ts +1 -0
  102. package/types/catalog/officialCatalogPlacement.d.ts +4 -0
  103. package/types/catalog/officialCatalogRockPackage.d.ts +68 -0
  104. package/types/cloud/noise/baseShapeVolume.d.ts +1 -1
  105. package/types/cloud/noise/textureCache.d.ts +12 -0
  106. package/types/core/sha256.d.ts +2 -0
  107. package/types/index.d.ts +30 -0
  108. package/types/rock-shader/rockMaterial.d.ts +3 -0
  109. package/types/rock-shader/rockShaderSettings.d.ts +2 -0
  110. package/types/rockgen/index.d.ts +1 -0
  111. package/types/rockgen/lod/index.d.ts +1 -0
  112. package/types/rockgen/lod/rockDenseFieldPolicy.d.ts +181 -0
  113. package/types/rockgen/rockDocument.d.ts +654 -71
  114. package/types/rockgen/surface/naturalRockSurface.d.ts +100 -0
  115. package/types/shaders-tsl/chunks/projected-water-caustics.d.ts +7 -1
  116. package/types/shaders-tsl/chunks/water-lighting.d.ts +4 -3
  117. package/types/shaders-tsl/water.d.ts +2 -1
  118. package/types/version.d.ts +1 -1
  119. package/types/water/waterDetailSpectrum.d.ts +13 -0
  120. package/types/water/waterDynamics.d.ts +89 -0
  121. package/types/water/waterFoamParticles.d.ts +32 -0
  122. package/types/water/waterFoamTexture.d.ts +1 -0
  123. package/types/water/waterHydrodynamics.d.ts +49 -0
  124. package/types/water/waterRenderExtension.d.ts +16 -0
  125. package/types/water/waterScenePasses.d.ts +1 -1
  126. package/types/water/waterSettings.d.ts +1 -0
  127. package/types/water/waterSpectralOcean.d.ts +17 -0
  128. package/types/water/waterStageSettings.d.ts +1 -1
  129. package/types/water/waterUnderwaterAtmosphere.d.ts +19 -4
@@ -104,6 +104,9 @@ const causticTexture = createCausticCellTexture();
104
104
 
105
105
  export const projectedWaterCausticUniforms = {
106
106
  map: texture(causticTexture),
107
+ surfaceMap: texture(causticTexture),
108
+ useSurfaceMap: uniform(0),
109
+ surfaceRegion: uniform(new THREE.Vector4(0,0,1,1)),
107
110
  enabled: uniform(0.0),
108
111
  time: uniform(0.0),
109
112
  waterLevel: uniform(0.0),
@@ -147,9 +150,14 @@ export function updateProjectedWaterCaustics({
147
150
  flowDirection,
148
151
  waveDistortion,
149
152
  depthAttenuation,
153
+ surfaceMap = null,
154
+ surfaceRegion = null,
150
155
  } = {}) {
151
156
  const u = projectedWaterCausticUniforms;
152
157
  u.enabled.value = enabled ? 1 : 0;
158
+ u.useSurfaceMap.value = surfaceMap ? 1 : 0;
159
+ if (surfaceMap) u.surfaceMap.value = surfaceMap;
160
+ if (surfaceRegion) u.surfaceRegion.value.copy(surfaceRegion);
153
161
  if (Number.isFinite(time)) u.time.value = time;
154
162
  if (Number.isFinite(waterLevel)) u.waterLevel.value = waterLevel;
155
163
  if ([centerX, centerZ, halfWidth, halfDepth].every(Number.isFinite)) {
@@ -227,13 +235,17 @@ export function projectedWaterCaustics(worldPosition, worldNormal) {
227
235
  const cellA = u.map.sample(uvA).level(0).r;
228
236
  const cellB = u.map.sample(uvB).level(0).r;
229
237
  const web = min(cellA, cellB).oneMinus();
230
- const strands = smoothstep(0.2, 0.9, web).pow(1.7);
238
+ const fieldUv = worldPosition.xz.sub(u.surfaceRegion.xy).div(u.surfaceRegion.zw.mul(2.0)).add(0.5);
239
+ const edge = smoothstep(vec2(0.0), vec2(0.08), fieldUv)
240
+ .mul(smoothstep(vec2(0.92), vec2(1.0), fieldUv).oneMinus());
241
+ const solvedLight = u.surfaceMap.sample(clamp(fieldUv, vec2(0.0), vec2(1.0))).level(0).r.mul(edge.x).mul(edge.y);
242
+ const strands = mix(smoothstep(0.2, 0.9, web).pow(1.7), solvedLight, u.useSurfaceMap);
231
243
  const pulse = sin(phase.mul(0.9).add(worldPosition.x.mul(0.11)))
232
244
  .mul(0.5).add(0.5);
233
245
  return u.color
234
246
  .mul(u.intensity)
235
247
  .mul(strands)
236
- .mul(mix(0.76, 1.0, pulse))
248
+ .mul(mix(mix(0.76, 1.0, pulse), 1.0, u.useSurfaceMap))
237
249
  .mul(submerged)
238
250
  .mul(depthFade)
239
251
  .mul(receivingFace)
@@ -26,8 +26,8 @@ export function createWaterFoamChunk({ u, foamOctaves }) {
26
26
  const shoreFoam = (columnDepth, viewDepthDiff, restXZ, time, pierce) => {
27
27
  const result = float(0.0).toVar();
28
28
  If(u.uUseSceneDepth.greaterThanEqual(0.5), () => {
29
- const flowOffset = u.uFlowDirection.mul(time).mul(u.uFlowSpeed).mul(0.35).toVar();
30
- const breakup = waterFbm(restXZ.mul(u.uFoamNoiseScale).add(flowOffset), foamOctaves).toVar();
29
+ const flowOffset = u.uFlowDirection.mul(time).mul(u.uFlowSpeed).toVar();
30
+ const breakup = waterFbm(restXZ.sub(flowOffset).mul(u.uFoamNoiseScale), foamOctaves).toVar();
31
31
 
32
32
  // A tight ungated core keeps a crisp waterline rim on any contact.
33
33
  const contactTight = smoothstep(0.0, min(u.uFoamContactDistance.mul(0.5), 0.07), columnDepth).oneMinus();
@@ -51,7 +51,7 @@ export function createWaterFoamChunk({ u, foamOctaves }) {
51
51
  const line = smoothstep(0.8, 0.93, lineBand)
52
52
  .mul(smoothstep(0.93, 1.0, lineBand).oneMinus()).toVar();
53
53
  const lineFalloff = smoothstep(1.3, 2.4, columnDepth.div(max(u.uFoamLineSpacing, 1e-3))).oneMinus();
54
- const dashes = waterValueNoise(restXZ.mul(u.uFoamNoiseScale).mul(2.6).add(flowOffset.mul(1.7)));
54
+ const dashes = waterValueNoise(restXZ.sub(flowOffset).mul(u.uFoamNoiseScale).mul(2.6));
55
55
  line.mulAssign(lineFalloff.mul(smoothstep(0.36, 0.6, breakup)).mul(smoothstep(0.3, 0.55, dashes)));
56
56
 
57
57
  result.assign(clamp(
@@ -68,8 +68,8 @@ export function createWaterFoamChunk({ u, foamOctaves }) {
68
68
  const whitecaps = (crest, gerstnerNormalY, restXZ, time) => {
69
69
  const result = float(0.0).toVar();
70
70
  If(u.uWhitecapAmount.greaterThan(0.002), () => {
71
- const drift = u.uFlowDirection.mul(time).mul(u.uFlowSpeed.mul(0.4).add(0.35));
72
- const patches = waterFbm(restXZ.mul(u.uFoamNoiseScale).mul(0.6).add(drift), foamOctaves).toVar();
71
+ const drift = u.uFlowDirection.mul(time).mul(u.uFlowSpeed);
72
+ const patches = waterFbm(restXZ.sub(drift).mul(u.uFoamNoiseScale).mul(0.6), foamOctaves).toVar();
73
73
  const steepness = clamp(gerstnerNormalY.oneMinus().mul(9.0), 0.0, 1.0);
74
74
  const energy = crest.mul(steepness.mul(0.45).add(0.55)).mul(patches.mul(0.85).add(0.45)).toVar();
75
75
  const threshold = mix(float(0.985), float(0.32), clamp(u.uWhitecapAmount, 0.0, 1.0)).toVar();
@@ -82,7 +82,7 @@ export function createWaterFoamChunk({ u, foamOctaves }) {
82
82
  // foam edges read as hand-drawn dots rather than a soft alpha gradient.
83
83
  const foamShape = (rawMask, restXZ, time) => {
84
84
  const dotNoise = waterValueNoise(
85
- restXZ.mul(u.uFoamNoiseScale).mul(5.2).add(u.uFlowDirection.mul(time).mul(u.uFlowSpeed).mul(0.6)),
85
+ restXZ.sub(u.uFlowDirection.mul(time).mul(u.uFlowSpeed)).mul(u.uFoamNoiseScale).mul(5.2),
86
86
  );
87
87
  const solid = waterToonStep(0.56, 0.12, rawMask);
88
88
  const dots = waterToonStep(0.74, 0.06, rawMask.mul(dotNoise.mul(0.75).add(0.35)));
@@ -10,6 +10,7 @@
10
10
 
11
11
  import {
12
12
  clamp,
13
+ float,
13
14
  dot,
14
15
  If,
15
16
  fract,
@@ -27,21 +28,43 @@ import {
27
28
 
28
29
  import { waterHash12, waterToonStep, waterVoronoi2 } from './water-common.js';
29
30
 
30
- export function createWaterLightingChunk({ u }) {
31
+ export function createWaterLightingChunk({ u, physicalSurface = false }) {
32
+ const reference = u.uReferenceView ?? float(0.0);
31
33
  // Schlick-style fresnel with a stylized floor: uFresnelBias keeps some sky
32
34
  // reflection alive even looking straight down.
33
35
  const fresnelFactor = (viewDir, surfaceNormal) => {
34
36
  const facing = clamp(dot(viewDir, surfaceNormal), 0.0, 1.0);
35
- return clamp(
37
+ const stylized = clamp(
36
38
  u.uFresnelBias.add(pow(facing.oneMinus(), u.uFresnelPower).mul(u.uFresnelStrength)),
37
39
  0.0,
38
40
  1.0,
39
41
  );
42
+ const eta = u.uIndexOfRefraction ?? float(1.333);
43
+ const f0 = eta.sub(1.0).div(eta.add(1.0)).pow2();
44
+ const physical = f0.add(f0.oneMinus().mul(pow(facing.oneMinus(), 5.0)));
45
+ return physicalSurface ? physical : mix(stylized, physical, reference);
40
46
  };
41
47
 
42
48
  // Toon-banded Blinn-Phong glint. The normal is anisotropically squashed
43
49
  // along the sun azimuth so low sun angles draw the long sparkling sun path.
44
50
  const specular = (viewDir, surfaceNormal, shadowFactor) => {
51
+ if (physicalSurface) {
52
+ const half = normalize(viewDir.add(u.uSunDirection));
53
+ const nv = max(dot(surfaceNormal, viewDir), 0.001);
54
+ const nl = max(dot(surfaceNormal, u.uSunDirection), 0.0);
55
+ const nh = max(dot(surfaceNormal, half), 0.0);
56
+ const vh = max(dot(viewDir, half), 0.0);
57
+ const roughness = clamp(float(2.0).div(u.uSpecularShininess.add(2.0)).sqrt(), 0.055, 0.35);
58
+ const alpha2 = roughness.mul(roughness).toVar();
59
+ const denominator = nh.mul(nh).mul(alpha2.sub(1.0)).add(1.0);
60
+ const distribution = alpha2.div(max(denominator.mul(denominator).mul(Math.PI), 1e-5));
61
+ const masking = (cosine) => cosine.mul(2.0).div(max(cosine.add(alpha2.add(alpha2.oneMinus().mul(cosine.mul(cosine))).sqrt()), 1e-4));
62
+ const eta = u.uIndexOfRefraction ?? float(1.333);
63
+ const f0 = eta.sub(1.0).div(eta.add(1.0)).pow2();
64
+ const fresnel = f0.add(f0.oneMinus().mul(vh.oneMinus().pow(5)));
65
+ const specularBRDF = distribution.mul(masking(nv)).mul(masking(nl)).mul(fresnel).div(max(nv.mul(4.0), 1e-4));
66
+ return u.uSunColor.mul(specularBRDF).mul(u.uSpecularStrength).mul(shadowFactor);
67
+ }
45
68
  const sunAzimuth = normalize(u.uSunDirection.xz.add(vec2(1e-5, 0.0))).toVar();
46
69
  const sunPerp = vec2(sunAzimuth.y.negate(), sunAzimuth.x).toVar();
47
70
  const stretched = sunAzimuth.mul(dot(surfaceNormal.xz, sunAzimuth))
@@ -52,7 +75,7 @@ export function createWaterLightingChunk({ u }) {
52
75
  const alignment = pow(max(dot(glintNormal, halfDir), 0.0), u.uSpecularShininess).toVar();
53
76
  const core = waterToonStep(0.5, 0.1, alignment);
54
77
  const halo = waterToonStep(0.18, 0.16, alignment).mul(0.3);
55
- return u.uSunColor.mul(u.uSpecularStrength).mul(core.add(halo)).mul(shadowFactor);
78
+ return u.uSunColor.mul(u.uSpecularStrength).mul(mix(core.add(halo), alignment, reference)).mul(shadowFactor);
56
79
  };
57
80
 
58
81
  // Twinkling star glints: sparse voronoi feature points with per-cell phase.
@@ -78,7 +101,7 @@ export function createWaterLightingChunk({ u }) {
78
101
  .mul(alignment).mul(distanceFade).mul(3.2),
79
102
  );
80
103
  });
81
- return result;
104
+ return result.mul(reference.oneMinus());
82
105
  };
83
106
 
84
107
  // Procedural sky used when no planar reflection target is bound: a vertical
@@ -105,7 +128,7 @@ export function createWaterLightingChunk({ u }) {
105
128
  const planar = u.uReflectionMap.sample(reflectionUv).level(0).rgb;
106
129
  // Milky painterly reflections: soften the mirror image toward the
107
130
  // smooth procedural sky instead of showing a sharp flipped scene.
108
- result.assign(mix(planar, fallback, clamp(u.uReflectionSoftness, 0.0, 1.0)));
131
+ result.assign(mix(planar, fallback, clamp(u.uReflectionSoftness, 0.0, 1.0).mul(reference.oneMinus()).mul(physicalSurface ? 0.25 : 1.0)));
109
132
  });
110
133
  });
111
134
  });
@@ -3,6 +3,8 @@
3
3
 
4
4
  import {
5
5
  clamp,
6
+ float,
7
+ mix,
6
8
  smoothstep,
7
9
  vec2,
8
10
  } from 'three/tsl';
@@ -19,7 +21,8 @@ export function createWaterShoreStateChunk({ u }) {
19
21
  // edge when a world uses a finite shore-state tile.
20
22
  const border = smoothstep(vec2(0.0), vec2(0.02), fieldUv)
21
23
  .mul(smoothstep(vec2(0.98), vec2(1.0), fieldUv).oneMinus());
22
- const mask = border.x.mul(border.y).mul(u.uUseShoreState).toVar();
24
+ const side = mix(border.x,1.0,u.uContinueShoreAlongX ?? float(0));
25
+ const mask = side.mul(border.y).mul(u.uUseShoreState).toVar();
23
26
  return u.uShoreStateMap
24
27
  .sample(clamp(fieldUv, vec2(0.0), vec2(1.0)))
25
28
  .level(0)
@@ -31,7 +34,7 @@ export function createWaterShoreStateChunk({ u }) {
31
34
  const fieldUv = shoreStateUv(worldXZ).toVar();
32
35
  const border = smoothstep(vec2(0.0), vec2(0.02), fieldUv)
33
36
  .mul(smoothstep(vec2(0.98), vec2(1.0), fieldUv).oneMinus());
34
- return border.x.mul(border.y).mul(u.uUseShoreState).toVar();
37
+ return mix(border.x,1.0,u.uContinueShoreAlongX ?? float(0)).mul(border.y).mul(u.uUseShoreState).toVar();
35
38
  };
36
39
 
37
40
  return { shoreStateCoverage, shoreStateSample, shoreStateUv };
@@ -24,6 +24,8 @@ import {
24
24
  float,
25
25
  fract,
26
26
  mix,
27
+ length,
28
+ max,
27
29
  normalize,
28
30
  sin,
29
31
  vec3,
@@ -85,7 +87,7 @@ export function createWaterWavesChunk({ wavesA, wavesB, waveCount }) {
85
87
  const cosTheta = cos(theta);
86
88
  displacement.y.addAssign(amplitude.mul(sinTheta));
87
89
  displacement.xz.addAssign(
88
- phaseFrame.orbitDirection.mul(steepness.mul(amplitude).mul(cosTheta)),
90
+ phaseFrame.orbitDirection.mul(steepness.div(max(length(phaseFrame.gradient), 1.0)).mul(amplitude).mul(cosTheta)),
89
91
  );
90
92
  }
91
93
  return displacement;
@@ -139,22 +141,25 @@ export function createWaterWavesChunk({ wavesA, wavesB, waveCount }) {
139
141
  .sub(omega.mul(time)).add(phase).toVar();
140
142
  const sinTheta = sin(theta).toVar();
141
143
  const cosTheta = cos(theta).toVar();
144
+ // Shortening wavelength must not amplify horizontal compression.
145
+ // Match the orbital reduction in the displacement evaluation.
146
+ const localSteepness = steepness.div(max(length(phaseFrame.gradient), 1.0));
142
147
  const waveKA = waveNumber.mul(amplitude).toVar();
143
148
  const phaseGradient = phaseFrame.gradient;
144
149
  const orbitDirection = phaseFrame.orbitDirection;
145
150
  tangent.x.subAssign(
146
- steepness.mul(orbitDirection.x).mul(phaseGradient.x).mul(waveKA).mul(sinTheta),
151
+ localSteepness.mul(orbitDirection.x).mul(phaseGradient.x).mul(waveKA).mul(sinTheta),
147
152
  );
148
153
  tangent.y.addAssign(phaseGradient.x.mul(waveKA).mul(cosTheta));
149
154
  tangent.z.subAssign(
150
- steepness.mul(orbitDirection.y).mul(phaseGradient.x).mul(waveKA).mul(sinTheta),
155
+ localSteepness.mul(orbitDirection.y).mul(phaseGradient.x).mul(waveKA).mul(sinTheta),
151
156
  );
152
157
  bitangent.x.subAssign(
153
- steepness.mul(orbitDirection.x).mul(phaseGradient.y).mul(waveKA).mul(sinTheta),
158
+ localSteepness.mul(orbitDirection.x).mul(phaseGradient.y).mul(waveKA).mul(sinTheta),
154
159
  );
155
160
  bitangent.y.addAssign(phaseGradient.y.mul(waveKA).mul(cosTheta));
156
161
  bitangent.z.subAssign(
157
- steepness.mul(orbitDirection.y).mul(phaseGradient.y).mul(waveKA).mul(sinTheta),
162
+ localSteepness.mul(orbitDirection.y).mul(phaseGradient.y).mul(waveKA).mul(sinTheta),
158
163
  );
159
164
  const crestPeak = sinTheta.mul(0.5).add(0.5).toVar();
160
165
  crestSum.addAssign(crestWeight.mul(amplitude).mul(crestPeak).mul(crestPeak));
@@ -112,6 +112,8 @@ function shadeFlowerHead({
112
112
  );
113
113
  const styledPetalColor = petalBaseColor.mul(flowerTint);
114
114
  const styledCenterColor = centerBaseColor.mul(flowerTint);
115
+ // Shadow-side lift is reflected light, distinct from authored emission.
116
+ const sceneFill = clamp(u.uSunIntensity.add(u.uSkyIntensity).mul(0.5), 0, 1.5);
115
117
  const petal = shadeVegetationSurface({
116
118
  baseColor: styledPetalColor,
117
119
  bandSoftness: u.uStyleFlowerBandSoftness,
@@ -133,7 +135,8 @@ function shadeFlowerHead({
133
135
  worldPosition,
134
136
  });
135
137
  petal.color.addAssign(
136
- styledPetalColor.mul(petal.band.oneMinus()).mul(u.uStyleFlowerUnlitPetalLift),
138
+ styledPetalColor.mul(petal.band.oneMinus()).mul(u.uStyleFlowerUnlitPetalLift)
139
+ .mul(sceneFill),
137
140
  );
138
141
 
139
142
  // Centers are opaque botanical structures, not thin petals. They share the
@@ -163,7 +166,7 @@ function shadeFlowerHead({
163
166
  worldPosition,
164
167
  });
165
168
  const centerColor = mix(
166
- styledCenterColor,
169
+ styledCenterColor.mul(sceneFill),
167
170
  center.color,
168
171
  u.uStyleFlowerCenterLightResponse,
169
172
  );
@@ -174,7 +177,7 @@ function shadeFlowerHead({
174
177
  const highlight = pow(
175
178
  clamp(dot(normal, halfVector), 0, 1),
176
179
  highlightPower,
177
- ).mul(sceneShadow).mul(u.uStyleFlowerSpecularStrength);
180
+ ).mul(sceneShadow).mul(u.uStyleFlowerSpecularStrength).mul(u.uSunIntensity);
178
181
  result.addAssign(u.uSunColor.mul(highlight));
179
182
  result.addAssign(
180
183
  mix(styledPetalColor, styledCenterColor, centerMask)
@@ -549,7 +552,7 @@ export function createFlowerStemNodeMaterial({
549
552
  clamp(dot(normal, halfVector), 0, 1),
550
553
  highlightPower,
551
554
  ).mul(u.uStyleStemSpecularStrength);
552
- shaded.color.addAssign(u.uSunColor.mul(highlight));
555
+ shaded.color.addAssign(u.uSunColor.mul(highlight).mul(u.uSunIntensity));
553
556
  shaded.color.addAssign(stemColor.mul(u.uStyleStemEmissiveStrength));
554
557
  return vec4(shaded.color, 1.0);
555
558
  })();
@@ -337,7 +337,8 @@ export function createGrassNodeMaterial(
337
337
 
338
338
  const sheen = smoothstep(u.uStyleGrassGustSheenThreshold, 1.0, vGust).mul(tipMix);
339
339
  shaded.color.addAssign(
340
- u.uSunColor.mul(sheen).mul(u.uStyleGrassGustSheenStrength).mul(shaded.band),
340
+ u.uSunColor.mul(sheen).mul(u.uStyleGrassGustSheenStrength).mul(shaded.band)
341
+ .mul(u.uSunIntensity),
341
342
  );
342
343
  const viewDirection = normalize(cameraPosition.sub(vWorldPosition));
343
344
  const halfVector = normalize(sunDirection.add(viewDirection));
@@ -345,7 +346,7 @@ export function createGrassNodeMaterial(
345
346
  const highlight = pow(
346
347
  clamp(dot(normal, halfVector), 0, 1),
347
348
  highlightPower,
348
- ).mul(sceneShadow).mul(u.uStyleGrassSpecularStrength);
349
+ ).mul(sceneShadow).mul(u.uStyleGrassSpecularStrength).mul(u.uSunIntensity);
349
350
  shaded.color.addAssign(u.uSunColor.mul(highlight));
350
351
  shaded.color.addAssign(color.mul(u.uStyleGrassEmissiveStrength));
351
352
 
@@ -368,7 +369,7 @@ export function createGrassNodeMaterial(
368
369
  // flat ground albedo here would undo the shared scene-light response a
369
370
  // second time.
370
371
  const washTarget = shaded.color.mul(1.18).add(
371
- u.uSunColor.mul(0.035).mul(groundCoverage.oneMinus()),
372
+ u.uSunColor.mul(0.035).mul(groundCoverage.oneMinus()).mul(u.uSunIntensity),
372
373
  );
373
374
  shaded.color.assign(mix(shaded.color, washTarget, u.uWashLift));
374
375
  const strokeAlpha = mix(
@@ -277,12 +277,13 @@ export function createWaterShoreStateSimulationNodeMaterial({
277
277
  )
278
278
  .mul(u.uSwashEdgeShape.z)
279
279
  .toVar();
280
+ const swashActivity = smoothstep(0.0, 0.05, u.uWaveEnergy).toVar();
280
281
  const edgeOffset = edgeTilt.add(edgeScallop).mul(edgeEnvelope)
281
- .add(macroBase.mul(macroEnvelope)).toVar();
282
+ .add(macroBase.mul(macroEnvelope)).mul(swashActivity).toVar();
282
283
  const edgeOffsetSpeed = scallopSpeed.mul(edgeEnvelope)
283
284
  .add(edgeTilt.add(edgeScallop).mul(envelopeSpeed))
284
285
  .add(macroBase.mul(macroEnvelopeSpeed))
285
- .toVar();
286
+ .mul(swashActivity).toVar();
286
287
 
287
288
  // In automatic mode the horizontal distance contains 1/slope, so its
288
289
  // vertical head reduces to shorelineRunup * energy * progress.