@call-me-sensei/toonlab 0.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/ATTRIBUTION.md +40 -0
- package/LICENSE +21 -0
- package/README.md +125 -0
- package/package.json +97 -0
- package/src/character/characterRig.js +353 -0
- package/src/character/freestyleSwimClip.js +541 -0
- package/src/character/index.js +4 -0
- package/src/character/modelLoader.js +433 -0
- package/src/core/materialRoles.js +438 -0
- package/src/core/presetDocuments.js +110 -0
- package/src/core/shaderBackend.js +24 -0
- package/src/debrisgen/debrisFields.js +544 -0
- package/src/debrisgen/debrisGenerator.js +1929 -0
- package/src/debrisgen/debrisPalettes.js +71 -0
- package/src/debrisgen/debrisPhysics.js +198 -0
- package/src/debrisgen/debrisPresets.js +155 -0
- package/src/debrisgen/debrisSettings.js +333 -0
- package/src/debrisgen/debrisTextures.js +380 -0
- package/src/debrisgen/index.js +5 -0
- package/src/debug/fieldValues.js +95 -0
- package/src/debug/index.js +7 -0
- package/src/debug/settingsPanel.js +170 -0
- package/src/environment/environmentAmbientProbe.js +160 -0
- package/src/environment/environmentMaterialAdapter.js +336 -0
- package/src/environment/environmentMaterialClassifier.js +178 -0
- package/src/environment/environmentPlanarReflection.js +201 -0
- package/src/environment/environmentPresets.js +443 -0
- package/src/environment/environmentRigs.js +684 -0
- package/src/environment/environmentSettings.js +504 -0
- package/src/environment/environmentShaderMaterials.js +318 -0
- package/src/environment/environmentSunShadowPass.js +268 -0
- package/src/environment/environmentTextureResolver.js +161 -0
- package/src/environment/environmentTimeOfDay.js +167 -0
- package/src/environment/environmentVertexAo.js +188 -0
- package/src/environment/index.js +8 -0
- package/src/environment/scanAssetStylize.js +103 -0
- package/src/index.js +21 -0
- package/src/loaders/index.js +1 -0
- package/src/post/index.js +2 -0
- package/src/post/postProcessing.js +1087 -0
- package/src/rockgen/export/glbExport.js +177 -0
- package/src/rockgen/heightfield/heightfieldErosion.js +7 -0
- package/src/rockgen/heightfield/heightfieldPatch.js +183 -0
- package/src/rockgen/heightfield/stylizedErosionSim.js +356 -0
- package/src/rockgen/index.js +14 -0
- package/src/rockgen/mesh/meshAttributes.js +374 -0
- package/src/rockgen/mesh/meshDocument.js +158 -0
- package/src/rockgen/mesh/surfaceNets.js +329 -0
- package/src/rockgen/noise/cellularNoise3.js +92 -0
- package/src/rockgen/noise/prng.js +62 -0
- package/src/rockgen/noise/simplexNoise3.js +89 -0
- package/src/rockgen/noise/valueNoise3.js +87 -0
- package/src/rockgen/rockDocument.js +255 -0
- package/src/rockgen/rockHelpers.js +8 -0
- package/src/rockgen/rockgenPresets.js +559 -0
- package/src/rockgen/rockgenSettings.js +756 -0
- package/src/rockgen/sdf/fieldCompiler.js +544 -0
- package/src/rockgen/sdf/sculptEdits.js +66 -0
- package/src/rockgen/sdf/sdfModifiers.js +45 -0
- package/src/rockgen/sdf/sdfOps.js +37 -0
- package/src/rockgen/sdf/sdfPrimitives.js +88 -0
- package/src/shaders-tsl/anime.js +963 -0
- package/src/shaders-tsl/chunks/character-color.js +64 -0
- package/src/shaders-tsl/chunks/character-highlights.js +174 -0
- package/src/shaders-tsl/chunks/character-lighting.js +309 -0
- package/src/shaders-tsl/chunks/character-material-maps.js +146 -0
- package/src/shaders-tsl/chunks/character-roles.js +52 -0
- package/src/shaders-tsl/chunks/character-scene-lights.js +270 -0
- package/src/shaders-tsl/chunks/character-shadow-color.js +61 -0
- package/src/shaders-tsl/chunks/character-skinning.js +140 -0
- package/src/shaders-tsl/chunks/environment-color.js +47 -0
- package/src/shaders-tsl/chunks/environment-debug.js +79 -0
- package/src/shaders-tsl/chunks/environment-lighting.js +260 -0
- package/src/shaders-tsl/chunks/environment-sun-shadow.js +87 -0
- package/src/shaders-tsl/chunks/foliage-fog.js +60 -0
- package/src/shaders-tsl/chunks/pass-depth-color.js +89 -0
- package/src/shaders-tsl/chunks/stylized-cloud-shadow.js +70 -0
- package/src/shaders-tsl/chunks/water-color.js +190 -0
- package/src/shaders-tsl/chunks/water-common.js +124 -0
- package/src/shaders-tsl/chunks/water-foam.js +93 -0
- package/src/shaders-tsl/chunks/water-lighting.js +116 -0
- package/src/shaders-tsl/chunks/water-ripple.js +64 -0
- package/src/shaders-tsl/chunks/water-waves.js +100 -0
- package/src/shaders-tsl/environment-ao-overlay.js +60 -0
- package/src/shaders-tsl/environment.js +680 -0
- package/src/shaders-tsl/flower.js +267 -0
- package/src/shaders-tsl/grass.js +193 -0
- package/src/shaders-tsl/post-composite.js +461 -0
- package/src/shaders-tsl/sky.js +175 -0
- package/src/shaders-tsl/tree-leaf.js +264 -0
- package/src/shaders-tsl/water-breaker.js +423 -0
- package/src/shaders-tsl/water-kelp.js +171 -0
- package/src/shaders-tsl/water-rain.js +107 -0
- package/src/shaders-tsl/water-simulation.js +140 -0
- package/src/shaders-tsl/water-splash.js +298 -0
- package/src/shaders-tsl/water.js +509 -0
- package/src/sky/index.js +2 -0
- package/src/sky/stylizedSky.js +341 -0
- package/src/toon/characterRenderPasses.js +646 -0
- package/src/toon/index.js +4 -0
- package/src/toon/settings/alphaSettings.js +170 -0
- package/src/toon/settings/averageShadowSettings.js +181 -0
- package/src/toon/settings/baseTextureSettings.js +133 -0
- package/src/toon/settings/celShadeSettings.js +96 -0
- package/src/toon/settings/contactShadowSettings.js +114 -0
- package/src/toon/settings/eyeHighlightSettings.js +187 -0
- package/src/toon/settings/faceLightingSettings.js +135 -0
- package/src/toon/settings/furSettings.js +112 -0
- package/src/toon/settings/glitterSettings.js +102 -0
- package/src/toon/settings/hairHighlightSettings.js +325 -0
- package/src/toon/settings/indirectLightSettings.js +244 -0
- package/src/toon/settings/localLightSettings.js +176 -0
- package/src/toon/settings/materialMapSettings.js +299 -0
- package/src/toon/settings/outlineSettings.js +263 -0
- package/src/toon/settings/perspectiveRemovalSettings.js +60 -0
- package/src/toon/settings/rimLightSettings.js +268 -0
- package/src/toon/settings/sceneShadowSettings.js +147 -0
- package/src/toon/settings/selfShadowSettings.js +221 -0
- package/src/toon/settings/shadowColorSettings.js +238 -0
- package/src/toon/settings/skinToneSettings.js +150 -0
- package/src/toon/settings/specularSettings.js +314 -0
- package/src/toon/settings/stickerSettings.js +102 -0
- package/src/toon/toonMaterialAdapter.js +2235 -0
- package/src/toon/toonSettings.js +860 -0
- package/src/vegetation/flowerSpecies.js +260 -0
- package/src/vegetation/index.js +10 -0
- package/src/vegetation/stylizedBush.js +136 -0
- package/src/vegetation/stylizedFlower.js +213 -0
- package/src/vegetation/stylizedFlowers.js +297 -0
- package/src/vegetation/stylizedGrass.js +491 -0
- package/src/vegetation/stylizedTree.js +2449 -0
- package/src/vegetation/stylizedTreeFoliage.js +691 -0
- package/src/vegetation/treeExport.js +300 -0
- package/src/vegetation/treeRecipe.js +647 -0
- package/src/water/index.js +2 -0
- package/src/water/water.js +20 -0
- package/src/water/waterBreakerSystem.js +591 -0
- package/src/water/waterInteraction.js +193 -0
- package/src/water/waterMaterial.js +263 -0
- package/src/water/waterRain.js +136 -0
- package/src/water/waterRippleSimulation.js +227 -0
- package/src/water/waterScenePasses.js +415 -0
- package/src/water/waterSettings.js +1138 -0
- package/src/water/waterSplashSystem.js +251 -0
- package/src/water/waterSurface.js +475 -0
- package/src/water/waterVegetation.js +83 -0
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
// TSL port of src/shaders/waterKelp.vert.glsl + waterKelp.frag.glsl —
|
|
2
|
+
// instanced underwater kelp blades swaying with the water flow.
|
|
3
|
+
//
|
|
4
|
+
// getShadowMask() → sampleEnvironmentSunShadow(worldPos) (shared sun-shadow
|
|
5
|
+
// pass; inert 1.0 until it runs). The material also exposes
|
|
6
|
+
// userData.createDepthColorVariant() so depth-as-color passes (the water grab
|
|
7
|
+
// pass on the node backends, the sun-shadow pass) see the blades exactly
|
|
8
|
+
// where the color pass draws them — same sway, same edge cutout — mirroring
|
|
9
|
+
// the tree-leaf convention. Varyings are created per material variant
|
|
10
|
+
// (tree-leaf pattern): node graphs may share uniforms, but each material owns
|
|
11
|
+
// its varyings.
|
|
12
|
+
//
|
|
13
|
+
// TODO(tsl Phase 7): fog ordering — GLSL fogs after colorspace; scene fog is
|
|
14
|
+
// disabled here until the fog phase resolves the ordering.
|
|
15
|
+
|
|
16
|
+
import * as THREE from 'three';
|
|
17
|
+
import {
|
|
18
|
+
abs,
|
|
19
|
+
attribute,
|
|
20
|
+
cameraFar,
|
|
21
|
+
cameraNear,
|
|
22
|
+
cameraProjectionMatrix,
|
|
23
|
+
cameraViewMatrix,
|
|
24
|
+
cos,
|
|
25
|
+
Discard,
|
|
26
|
+
dot,
|
|
27
|
+
float,
|
|
28
|
+
Fn,
|
|
29
|
+
mix,
|
|
30
|
+
modelWorldMatrix,
|
|
31
|
+
normalize,
|
|
32
|
+
positionGeometry,
|
|
33
|
+
pow,
|
|
34
|
+
select,
|
|
35
|
+
sin,
|
|
36
|
+
smoothstep,
|
|
37
|
+
uniform,
|
|
38
|
+
uv,
|
|
39
|
+
varying,
|
|
40
|
+
vec2,
|
|
41
|
+
vec3,
|
|
42
|
+
vec4,
|
|
43
|
+
viewZToOrthographicDepth,
|
|
44
|
+
viewZToPerspectiveDepth,
|
|
45
|
+
} from 'three/tsl';
|
|
46
|
+
import { NodeMaterial } from 'three/webgpu';
|
|
47
|
+
|
|
48
|
+
import { sampleEnvironmentSunShadow } from './chunks/environment-sun-shadow.js';
|
|
49
|
+
import { waterToonStep, waterValueNoise } from './chunks/water-common.js';
|
|
50
|
+
|
|
51
|
+
export function createWaterKelpNodeMaterial({
|
|
52
|
+
swayAmplitude = 0.24,
|
|
53
|
+
shadowStrength = 0.55,
|
|
54
|
+
} = {}) {
|
|
55
|
+
const u = {
|
|
56
|
+
uTime: uniform(0),
|
|
57
|
+
uSceneShadowStrength: uniform(shadowStrength),
|
|
58
|
+
uFlowDirection: uniform(new THREE.Vector2(1, 0)),
|
|
59
|
+
uFlowSpeed: uniform(0.3),
|
|
60
|
+
uSwayAmplitude: uniform(swayAmplitude),
|
|
61
|
+
uKelpColor: uniform(new THREE.Color()),
|
|
62
|
+
uKelpShadeColor: uniform(new THREE.Color()),
|
|
63
|
+
};
|
|
64
|
+
|
|
65
|
+
const vUv = uv();
|
|
66
|
+
|
|
67
|
+
// Shared vertex displacement, parameterized by the owning material's
|
|
68
|
+
// varyings (waterKelp.vert.glsl).
|
|
69
|
+
const buildVertexNode = ({ vSeed, vWorldPosition }) => Fn(() => {
|
|
70
|
+
const iOrigin = attribute('iOrigin', 'vec3');
|
|
71
|
+
// iInfo = (height, phase, width, yaw)
|
|
72
|
+
const iInfo = attribute('iInfo', 'vec4');
|
|
73
|
+
|
|
74
|
+
vSeed.assign(iInfo.y);
|
|
75
|
+
|
|
76
|
+
const heightFraction = vUv.y.toVar();
|
|
77
|
+
const bladeHeight = iInfo.x.toVar();
|
|
78
|
+
const yaw = iInfo.w;
|
|
79
|
+
const facing = vec2(cos(yaw), sin(yaw)).toVar();
|
|
80
|
+
|
|
81
|
+
const flow = normalize(u.uFlowDirection.add(vec2(1e-4, 0.0))).toVar();
|
|
82
|
+
const bendPhase = u.uTime.mul(u.uFlowSpeed.mul(0.9).add(0.9)).add(iInfo.y.mul(6.28))
|
|
83
|
+
.add(dot(iOrigin.xz, vec2(0.4, 0.31))).toVar();
|
|
84
|
+
const bend = sin(bendPhase).mul(0.5).add(0.5).add(u.uFlowSpeed.mul(0.35)).mul(u.uSwayAmplitude).toVar();
|
|
85
|
+
const wiggle = vec2(flow.y.negate(), flow.x)
|
|
86
|
+
.mul(sin(bendPhase.mul(2.6).add(heightFraction.mul(3.2)))).mul(0.14).mul(u.uSwayAmplitude).toVar();
|
|
87
|
+
const curve = pow(heightFraction, 1.55).toVar();
|
|
88
|
+
|
|
89
|
+
const bladePosition = vec3(iOrigin).toVar();
|
|
90
|
+
bladePosition.xz.addAssign(facing.mul(positionGeometry.x.mul(iInfo.z)));
|
|
91
|
+
bladePosition.y.addAssign(heightFraction.mul(bladeHeight));
|
|
92
|
+
bladePosition.xz.addAssign(flow.mul(bend).add(wiggle).mul(curve).mul(bladeHeight));
|
|
93
|
+
bladePosition.y.subAssign(bend.mul(curve).mul(curve).mul(bladeHeight).mul(0.2));
|
|
94
|
+
|
|
95
|
+
const worldPosition = modelWorldMatrix.mul(vec4(bladePosition, 1.0)).toVar();
|
|
96
|
+
vWorldPosition.assign(worldPosition.xyz);
|
|
97
|
+
return cameraProjectionMatrix.mul(cameraViewMatrix).mul(worldPosition);
|
|
98
|
+
})();
|
|
99
|
+
|
|
100
|
+
// Shared blade silhouette cutout (color + depth variant); returns the
|
|
101
|
+
// distance-from-midrib term the color stage keeps using.
|
|
102
|
+
const bladeCutout = (vSeed) => {
|
|
103
|
+
// Tapered silhouette with a wavy edge; widest near the base.
|
|
104
|
+
const edgeWave = sin(vUv.y.mul(9.0).add(vSeed.mul(37.0))).mul(0.22).add(0.78);
|
|
105
|
+
const halfWidth = vUv.y.mul(0.62).oneMinus().mul(0.5).mul(edgeWave);
|
|
106
|
+
const distanceFromMid = abs(vUv.x.sub(0.5)).toVar();
|
|
107
|
+
Discard(distanceFromMid.greaterThan(halfWidth));
|
|
108
|
+
|
|
109
|
+
const tipFade = smoothstep(0.93, 1.0, vUv.y).oneMinus();
|
|
110
|
+
Discard(tipFade.lessThan(0.4));
|
|
111
|
+
return distanceFromMid;
|
|
112
|
+
};
|
|
113
|
+
|
|
114
|
+
const material = new NodeMaterial();
|
|
115
|
+
material.name = 'WaterKelp';
|
|
116
|
+
material.lights = false;
|
|
117
|
+
// TODO(tsl Phase 7): fog ordering (see module header).
|
|
118
|
+
material.fog = false;
|
|
119
|
+
material.side = THREE.DoubleSide;
|
|
120
|
+
|
|
121
|
+
const vSeed = varying(float(), 'vKelpSeed');
|
|
122
|
+
const vWorldPosition = varying(vec3(), 'vKelpWorldPosition');
|
|
123
|
+
material.vertexNode = buildVertexNode({ vSeed, vWorldPosition });
|
|
124
|
+
|
|
125
|
+
material.fragmentNode = Fn(() => {
|
|
126
|
+
const distanceFromMid = bladeCutout(vSeed);
|
|
127
|
+
|
|
128
|
+
const shadeNoise = waterValueNoise(vec2(vUv.x.mul(3.0), vUv.y.mul(5.0)).add(vSeed.mul(19.0)));
|
|
129
|
+
const lit = waterToonStep(0.5, 0.09, shadeNoise.add(vUv.y.mul(0.42)));
|
|
130
|
+
const color = mix(u.uKelpShadeColor, u.uKelpColor, lit).toVar();
|
|
131
|
+
// Darker midrib line.
|
|
132
|
+
color.mulAssign(smoothstep(0.02, 0.05, distanceFromMid).oneMinus().mul(0.28).oneMinus());
|
|
133
|
+
|
|
134
|
+
// Scene shadows filtering down through the water.
|
|
135
|
+
const sceneShadow = mix(1.0, sampleEnvironmentSunShadow(vWorldPosition), u.uSceneShadowStrength);
|
|
136
|
+
color.mulAssign(mix(vec3(0.52, 0.58, 0.68), vec3(1.0), sceneShadow));
|
|
137
|
+
|
|
138
|
+
return vec4(color, 1.0);
|
|
139
|
+
})();
|
|
140
|
+
|
|
141
|
+
material.uniforms = u;
|
|
142
|
+
material.userData.isToonNodeMaterial = true;
|
|
143
|
+
|
|
144
|
+
// Depth-color variant: same sway + cutout, linear window depth as color
|
|
145
|
+
// (pass-depth-color convention; the pass's own camera feeds cameraNear/Far).
|
|
146
|
+
material.userData.createDepthColorVariant = () => {
|
|
147
|
+
const depthMaterial = new NodeMaterial();
|
|
148
|
+
depthMaterial.name = 'WaterKelpDepth';
|
|
149
|
+
depthMaterial.lights = false;
|
|
150
|
+
depthMaterial.fog = false;
|
|
151
|
+
depthMaterial.side = THREE.DoubleSide;
|
|
152
|
+
depthMaterial.isShadowPassMaterial = true; // see pass-depth-color.js
|
|
153
|
+
const dSeed = varying(float(), 'vKelpDepthSeed');
|
|
154
|
+
const dWorldPosition = varying(vec3(), 'vKelpDepthWorldPosition');
|
|
155
|
+
depthMaterial.vertexNode = buildVertexNode({ vSeed: dSeed, vWorldPosition: dWorldPosition });
|
|
156
|
+
depthMaterial.fragmentNode = Fn(() => {
|
|
157
|
+
bladeCutout(dSeed);
|
|
158
|
+
const viewZ = cameraViewMatrix.mul(vec4(dWorldPosition, 1.0)).z;
|
|
159
|
+
const orthographic = cameraProjectionMatrix.element(3).w.equal(1.0);
|
|
160
|
+
const depth01 = select(
|
|
161
|
+
orthographic,
|
|
162
|
+
viewZToOrthographicDepth(viewZ, cameraNear, cameraFar),
|
|
163
|
+
viewZToPerspectiveDepth(viewZ, cameraNear, cameraFar),
|
|
164
|
+
);
|
|
165
|
+
return vec4(vec3(depth01), 1.0);
|
|
166
|
+
})();
|
|
167
|
+
return depthMaterial;
|
|
168
|
+
};
|
|
169
|
+
|
|
170
|
+
return material;
|
|
171
|
+
}
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
// TSL port of src/shaders/waterRain.vert.glsl + waterRain.frag.glsl —
|
|
2
|
+
// GPU-looping rain streak particles. Each particle owns a static seed; its
|
|
3
|
+
// full trajectory is derived from time, so rain runs forever with zero
|
|
4
|
+
// per-frame buffer writes.
|
|
5
|
+
//
|
|
6
|
+
// WGSL has no gl_PointSize, so the streak POINTS become instanced billboard
|
|
7
|
+
// quads with the exact pixel-size math in clip space (dust-motes pattern,
|
|
8
|
+
// docs/tsl-conventions.md). The quad uv is flipped to gl_PointCoord
|
|
9
|
+
// orientation for the bar shape (symmetric here, kept for exactness).
|
|
10
|
+
|
|
11
|
+
import * as THREE from 'three';
|
|
12
|
+
import {
|
|
13
|
+
abs,
|
|
14
|
+
attribute,
|
|
15
|
+
cameraProjectionMatrix,
|
|
16
|
+
cameraViewMatrix,
|
|
17
|
+
Discard,
|
|
18
|
+
float,
|
|
19
|
+
Fn,
|
|
20
|
+
max,
|
|
21
|
+
mod,
|
|
22
|
+
positionGeometry,
|
|
23
|
+
smoothstep,
|
|
24
|
+
uniform,
|
|
25
|
+
uv,
|
|
26
|
+
varying,
|
|
27
|
+
vec2,
|
|
28
|
+
vec3,
|
|
29
|
+
vec4,
|
|
30
|
+
viewportSize,
|
|
31
|
+
} from 'three/tsl';
|
|
32
|
+
import { NodeMaterial } from 'three/webgpu';
|
|
33
|
+
|
|
34
|
+
export function createWaterRainNodeMaterial({
|
|
35
|
+
areaSize = 30,
|
|
36
|
+
fallHeight = 16,
|
|
37
|
+
speed = 16,
|
|
38
|
+
streakLength = 0.42,
|
|
39
|
+
wind = [2.2, 0.8],
|
|
40
|
+
color = [0.8, 0.88, 0.95],
|
|
41
|
+
opacity = 0.34,
|
|
42
|
+
} = {}) {
|
|
43
|
+
const u = {
|
|
44
|
+
uTime: uniform(0),
|
|
45
|
+
uCenter: uniform(new THREE.Vector3()),
|
|
46
|
+
uArea: uniform(new THREE.Vector2(areaSize * 0.5, areaSize * 0.5)),
|
|
47
|
+
uFallHeight: uniform(fallHeight),
|
|
48
|
+
uSpeed: uniform(speed),
|
|
49
|
+
uWind: uniform(new THREE.Vector2(wind[0], wind[1])),
|
|
50
|
+
uStreakLength: uniform(streakLength),
|
|
51
|
+
uPointScale: uniform(540),
|
|
52
|
+
uPixelRatio: uniform(1),
|
|
53
|
+
uColor: uniform(new THREE.Color().setRGB(color[0], color[1], color[2], THREE.SRGBColorSpace)),
|
|
54
|
+
uOpacity: uniform(opacity),
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
const material = new NodeMaterial();
|
|
58
|
+
material.name = 'WaterRain';
|
|
59
|
+
material.lights = false;
|
|
60
|
+
material.fog = false;
|
|
61
|
+
material.transparent = true;
|
|
62
|
+
material.depthWrite = false;
|
|
63
|
+
|
|
64
|
+
const vFade = varying(float(), 'vRainFade');
|
|
65
|
+
|
|
66
|
+
material.vertexNode = Fn(() => {
|
|
67
|
+
// aSeed = (unitX, unitZ, phase, jitter)
|
|
68
|
+
const aSeed = attribute('aSeed', 'vec4');
|
|
69
|
+
|
|
70
|
+
const speedValue = u.uSpeed.mul(aSeed.w.mul(0.5).add(0.75)).toVar();
|
|
71
|
+
const cycle = u.uFallHeight.div(speedValue).toVar();
|
|
72
|
+
const t = mod(u.uTime.add(aSeed.z.mul(cycle)), cycle).toVar();
|
|
73
|
+
const y = u.uFallHeight.sub(speedValue.mul(t)).toVar();
|
|
74
|
+
|
|
75
|
+
const xz = aSeed.xy.mul(2.0).sub(1.0).mul(u.uArea).add(u.uWind.mul(t)).toVar();
|
|
76
|
+
xz.assign(mod(xz.add(u.uArea), u.uArea.mul(2.0)).sub(u.uArea));
|
|
77
|
+
|
|
78
|
+
const worldPosition = u.uCenter.add(vec3(xz.x, y, xz.y)).toVar();
|
|
79
|
+
const mvPosition = cameraViewMatrix.mul(vec4(worldPosition, 1.0)).toVar();
|
|
80
|
+
|
|
81
|
+
vFade.assign(smoothstep(0.0, 0.5, y)
|
|
82
|
+
.mul(smoothstep(u.uFallHeight.mul(0.85), u.uFallHeight, y).oneMinus()));
|
|
83
|
+
|
|
84
|
+
// gl_PointSize equivalent, in device pixels.
|
|
85
|
+
const pixels = u.uStreakLength.mul(u.uPointScale).mul(u.uPixelRatio)
|
|
86
|
+
.div(max(mvPosition.z.negate(), 0.5)).toVar();
|
|
87
|
+
const clipPosition = cameraProjectionMatrix.mul(mvPosition).toVar();
|
|
88
|
+
clipPosition.xy.addAssign(
|
|
89
|
+
positionGeometry.xy.mul(pixels).mul(float(2.0).div(viewportSize)).mul(clipPosition.w),
|
|
90
|
+
);
|
|
91
|
+
return clipPosition;
|
|
92
|
+
})();
|
|
93
|
+
|
|
94
|
+
material.fragmentNode = Fn(() => {
|
|
95
|
+
// gl_PointCoord replacement (y flipped), remapped to [-1, 1].
|
|
96
|
+
const point = vec2(uv().x, uv().y.oneMinus()).mul(2.0).sub(1.0).toVar();
|
|
97
|
+
const bar = smoothstep(0.05, 0.18, abs(point.x)).oneMinus()
|
|
98
|
+
.mul(smoothstep(0.65, 1.0, abs(point.y)).oneMinus());
|
|
99
|
+
const alpha = bar.mul(u.uOpacity).mul(vFade).toVar();
|
|
100
|
+
Discard(alpha.lessThan(0.02));
|
|
101
|
+
return vec4(u.uColor, alpha);
|
|
102
|
+
})();
|
|
103
|
+
|
|
104
|
+
material.uniforms = u;
|
|
105
|
+
material.userData.isToonNodeMaterial = true;
|
|
106
|
+
return material;
|
|
107
|
+
}
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
// TSL port of src/shaders/waterSimulation.vert.glsl + waterSimulation.frag.glsl
|
|
2
|
+
// — the fullscreen ripple heightfield simulation material (ping-pong render
|
|
3
|
+
// targets, decision D3: stays fragment-based, no compute).
|
|
4
|
+
// State texture: R = height, G = vertical velocity, B = foam energy.
|
|
5
|
+
//
|
|
6
|
+
// Porting notes (docs/tsl-conventions.md):
|
|
7
|
+
// - The GLSL early return (region shift moving the source uv off the state)
|
|
8
|
+
// becomes var + If — the fragment root stays a plain vec4 var.
|
|
9
|
+
// - uPrevState is the RT being ping-ponged: every sample is .level(0).
|
|
10
|
+
// - The impulse loop (WATER_SIM_MAX_IMPULSES) unrolls at graph build; the
|
|
11
|
+
// `i >= uImpulseCount break` / `distanceSq > 4 continue` guards become
|
|
12
|
+
// nested Ifs (impulses are packed from slot 0, so the guard is equivalent).
|
|
13
|
+
// - uImpulses is a uniformArray (vec4 slots, GLSL layout preserved).
|
|
14
|
+
|
|
15
|
+
import * as THREE from 'three';
|
|
16
|
+
import {
|
|
17
|
+
abs,
|
|
18
|
+
dot,
|
|
19
|
+
exp,
|
|
20
|
+
clamp,
|
|
21
|
+
Fn,
|
|
22
|
+
If,
|
|
23
|
+
max,
|
|
24
|
+
min,
|
|
25
|
+
mix,
|
|
26
|
+
positionGeometry,
|
|
27
|
+
pow,
|
|
28
|
+
smoothstep,
|
|
29
|
+
texture,
|
|
30
|
+
uniform,
|
|
31
|
+
uniformArray,
|
|
32
|
+
uv,
|
|
33
|
+
vec2,
|
|
34
|
+
vec4,
|
|
35
|
+
} from 'three/tsl';
|
|
36
|
+
import { NodeMaterial } from 'three/webgpu';
|
|
37
|
+
|
|
38
|
+
import { waterArrayUniformEntry } from './water.js';
|
|
39
|
+
|
|
40
|
+
let fallbackStateTexture = null;
|
|
41
|
+
function getFallbackStateTexture() {
|
|
42
|
+
if (!fallbackStateTexture) {
|
|
43
|
+
fallbackStateTexture = new THREE.DataTexture(new Uint8Array([0, 0, 0, 255]), 1, 1, THREE.RGBAFormat);
|
|
44
|
+
fallbackStateTexture.needsUpdate = true;
|
|
45
|
+
}
|
|
46
|
+
return fallbackStateTexture;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
export function createWaterSimulationNodeMaterial({ maxImpulses, resolution }) {
|
|
50
|
+
// uImpulses[i] = (centerU, centerV, worldRadius, strength)
|
|
51
|
+
const impulsesNode = uniformArray(
|
|
52
|
+
Array.from({ length: maxImpulses }, () => new THREE.Vector4()), 'vec4',
|
|
53
|
+
);
|
|
54
|
+
const u = {
|
|
55
|
+
uPrevState: texture(getFallbackStateTexture()),
|
|
56
|
+
uTexel: uniform(new THREE.Vector2(1 / resolution, 1 / resolution)),
|
|
57
|
+
uDelta: uniform(1 / 120),
|
|
58
|
+
uPropagation: uniform(1),
|
|
59
|
+
uDamping: uniform(0.985),
|
|
60
|
+
uHeightRelaxation: uniform(0.9985),
|
|
61
|
+
uFoamDecay: uniform(0.94),
|
|
62
|
+
uFoamGain: uniform(2.4),
|
|
63
|
+
uRegionShiftTexels: uniform(new THREE.Vector2(0, 0)),
|
|
64
|
+
uRegionWorldSize: uniform(new THREE.Vector2(20, 20)),
|
|
65
|
+
uImpulseCount: uniform(0, 'int'),
|
|
66
|
+
// Array uniform wrapper: `.value[i].set(...)` writes land on the node's
|
|
67
|
+
// authoring array (see waterArrayUniformEntry).
|
|
68
|
+
uImpulses: waterArrayUniformEntry(impulsesNode),
|
|
69
|
+
};
|
|
70
|
+
|
|
71
|
+
const material = new NodeMaterial();
|
|
72
|
+
material.name = 'WaterRippleSimulation';
|
|
73
|
+
material.lights = false;
|
|
74
|
+
material.fog = false;
|
|
75
|
+
material.depthTest = false;
|
|
76
|
+
material.depthWrite = false;
|
|
77
|
+
|
|
78
|
+
// Fullscreen pass: clip position straight from the quad's xy.
|
|
79
|
+
material.vertexNode = Fn(() => vec4(positionGeometry.xy, 0.0, 1.0))();
|
|
80
|
+
|
|
81
|
+
const vUv = uv();
|
|
82
|
+
|
|
83
|
+
material.fragmentNode = Fn(() => {
|
|
84
|
+
const result = vec4(0.0, 0.0, 0.0, 1.0).toVar();
|
|
85
|
+
|
|
86
|
+
// Texel-aligned region shift so the simulation window can follow a
|
|
87
|
+
// character while ripples stay put in world space.
|
|
88
|
+
const sourceUv = vUv.add(u.uRegionShiftTexels.mul(u.uTexel)).toVar();
|
|
89
|
+
const inRange = sourceUv.x.greaterThanEqual(0.0).and(sourceUv.x.lessThanEqual(1.0))
|
|
90
|
+
.and(sourceUv.y.greaterThanEqual(0.0)).and(sourceUv.y.lessThanEqual(1.0));
|
|
91
|
+
If(inRange, () => {
|
|
92
|
+
const state = u.uPrevState.sample(sourceUv).level(0).rgb.toVar();
|
|
93
|
+
const height = state.r.toVar();
|
|
94
|
+
const velocity = state.g.toVar();
|
|
95
|
+
const foam = state.b.toVar();
|
|
96
|
+
|
|
97
|
+
const left = u.uPrevState.sample(sourceUv.sub(vec2(u.uTexel.x, 0.0))).level(0).r;
|
|
98
|
+
const right = u.uPrevState.sample(sourceUv.add(vec2(u.uTexel.x, 0.0))).level(0).r;
|
|
99
|
+
const down = u.uPrevState.sample(sourceUv.sub(vec2(0.0, u.uTexel.y))).level(0).r;
|
|
100
|
+
const up = u.uPrevState.sample(sourceUv.add(vec2(0.0, u.uTexel.y))).level(0).r;
|
|
101
|
+
const laplacian = left.add(right).add(up).add(down).sub(height.mul(4.0));
|
|
102
|
+
|
|
103
|
+
velocity.addAssign(laplacian.mul(u.uPropagation).mul(u.uDelta));
|
|
104
|
+
velocity.mulAssign(pow(u.uDamping, u.uDelta.mul(60.0)));
|
|
105
|
+
|
|
106
|
+
for (let i = 0; i < maxImpulses; i += 1) {
|
|
107
|
+
If(u.uImpulseCount.greaterThan(i), () => {
|
|
108
|
+
const impulse = impulsesNode.element(i).toVar();
|
|
109
|
+
const delta = vUv.sub(impulse.xy).mul(u.uRegionWorldSize).div(max(impulse.z, 1e-4));
|
|
110
|
+
const distanceSq = dot(delta, delta).toVar();
|
|
111
|
+
If(distanceSq.lessThanEqual(4.0), () => {
|
|
112
|
+
const falloff = exp(distanceSq.mul(-3.0)).toVar();
|
|
113
|
+
velocity.addAssign(impulse.w.mul(falloff));
|
|
114
|
+
height.subAssign(impulse.w.mul(falloff).mul(0.12));
|
|
115
|
+
});
|
|
116
|
+
});
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
height.addAssign(velocity.mul(u.uDelta));
|
|
120
|
+
height.mulAssign(pow(u.uHeightRelaxation, u.uDelta.mul(60.0)));
|
|
121
|
+
|
|
122
|
+
// Absorbing border so waves never bounce off the region edge.
|
|
123
|
+
const borderDistance = min(vUv, vUv.oneMinus()).toVar();
|
|
124
|
+
const border = smoothstep(0.0, 0.05, min(borderDistance.x, borderDistance.y)).toVar();
|
|
125
|
+
height.mulAssign(mix(0.9, 1.0, border));
|
|
126
|
+
velocity.mulAssign(mix(0.82, 1.0, border));
|
|
127
|
+
|
|
128
|
+
foam.assign(foam.mul(pow(u.uFoamDecay, u.uDelta.mul(60.0)))
|
|
129
|
+
.add(abs(velocity).mul(u.uFoamGain).mul(u.uDelta)));
|
|
130
|
+
foam.assign(clamp(foam, 0.0, 1.5));
|
|
131
|
+
|
|
132
|
+
result.assign(vec4(height, velocity, foam, 1.0));
|
|
133
|
+
});
|
|
134
|
+
|
|
135
|
+
return result;
|
|
136
|
+
})();
|
|
137
|
+
|
|
138
|
+
material.uniforms = u;
|
|
139
|
+
return material;
|
|
140
|
+
}
|