@plasius/gpu-world-generator 0.0.4
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/LICENSE +203 -0
- package/README.md +73 -0
- package/dist/field.wgsl +225 -0
- package/dist/fractal-prepass.wgsl +290 -0
- package/dist/index.cjs +1942 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +447 -0
- package/dist/index.d.ts +447 -0
- package/dist/index.js +1848 -0
- package/dist/index.js.map +1 -0
- package/dist/terrain.wgsl +451 -0
- package/docs/adrs/adr-0001-package-scope.md +18 -0
- package/docs/adrs/adr-0002-world-tiling-lod-stitching.md +28 -0
- package/docs/adrs/adr-0003-terrain-generation-style-mixing.md +21 -0
- package/docs/adrs/index.md +5 -0
- package/docs/biomes.md +206 -0
- package/docs/lod-zoning.md +22 -0
- package/docs/plan.md +107 -0
- package/docs/procedural-surface.md +73 -0
- package/docs/resources.md +55 -0
- package/package.json +53 -0
- package/src/biomes/temperate.ts +387 -0
- package/src/field.wgsl +225 -0
- package/src/fields.ts +321 -0
- package/src/fractal-prepass.ts +237 -0
- package/src/fractal-prepass.wgsl +290 -0
- package/src/generator.ts +106 -0
- package/src/hex.ts +54 -0
- package/src/index.ts +11 -0
- package/src/mesh.ts +285 -0
- package/src/perf-monitor.ts +133 -0
- package/src/shaders/demo-terrain.wgsl +193 -0
- package/src/shaders/demo-trees.wgsl +172 -0
- package/src/shaders/demo-water-sim.wgsl +361 -0
- package/src/shaders/library/common.wgsl +38 -0
- package/src/shaders/library/materials.wgsl +175 -0
- package/src/terrain.wgsl +451 -0
- package/src/tile-cache.ts +274 -0
- package/src/tiles.ts +417 -0
- package/src/types.ts +220 -0
- package/src/wgsl/field.job.wgsl +225 -0
- package/src/wgsl/terrain.job.wgsl +451 -0
- package/src/wgsl.ts +77 -0
|
@@ -0,0 +1,172 @@
|
|
|
1
|
+
struct TreeUniforms {
|
|
2
|
+
viewProj: mat4x4<f32>,
|
|
3
|
+
cameraPos: vec4<f32>,
|
|
4
|
+
cameraRight: vec4<f32>,
|
|
5
|
+
cameraUp: vec4<f32>,
|
|
6
|
+
season: vec4<f32>,
|
|
7
|
+
wind: vec4<f32>,
|
|
8
|
+
};
|
|
9
|
+
|
|
10
|
+
struct TreeInstance {
|
|
11
|
+
posHeight: vec4<f32>,
|
|
12
|
+
canopySeed: vec4<f32>,
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
struct VSOut {
|
|
16
|
+
@builtin(position) position: vec4<f32>,
|
|
17
|
+
@location(0) uv: vec2<f32>,
|
|
18
|
+
@location(1) baseColor: vec3<f32>,
|
|
19
|
+
@location(2) trunkColor: vec3<f32>,
|
|
20
|
+
@location(3) height: f32,
|
|
21
|
+
@location(4) leafiness: f32,
|
|
22
|
+
@location(5) deciduous: f32,
|
|
23
|
+
@location(6) lod: f32,
|
|
24
|
+
@location(7) worldPos: vec3<f32>,
|
|
25
|
+
@location(8) seed: f32,
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
@group(0) @binding(0) var<uniform> scene: TreeUniforms;
|
|
29
|
+
@group(0) @binding(1) var<storage, read> trees: array<TreeInstance>;
|
|
30
|
+
|
|
31
|
+
fn quad_uv(vid: u32) -> vec2<f32> {
|
|
32
|
+
let uv = array<vec2<f32>, 6>(
|
|
33
|
+
vec2<f32>(0.0, 0.0),
|
|
34
|
+
vec2<f32>(1.0, 0.0),
|
|
35
|
+
vec2<f32>(1.0, 1.0),
|
|
36
|
+
vec2<f32>(0.0, 0.0),
|
|
37
|
+
vec2<f32>(1.0, 1.0),
|
|
38
|
+
vec2<f32>(0.0, 1.0)
|
|
39
|
+
);
|
|
40
|
+
return uv[vid];
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
fn mandelbrot(c: vec2<f32>, iterations: u32) -> f32 {
|
|
44
|
+
var z = vec2<f32>(0.0);
|
|
45
|
+
var i: u32 = 0u;
|
|
46
|
+
loop {
|
|
47
|
+
if (i >= iterations) {
|
|
48
|
+
break;
|
|
49
|
+
}
|
|
50
|
+
let x = z.x * z.x - z.y * z.y + c.x;
|
|
51
|
+
let y = 2.0 * z.x * z.y + c.y;
|
|
52
|
+
z = vec2<f32>(x, y);
|
|
53
|
+
if (dot(z, z) > 4.0) {
|
|
54
|
+
break;
|
|
55
|
+
}
|
|
56
|
+
i = i + 1u;
|
|
57
|
+
}
|
|
58
|
+
return f32(i) / f32(iterations);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
@vertex
|
|
62
|
+
fn vs_main(@builtin(vertex_index) vid: u32, @builtin(instance_index) iid: u32) -> VSOut {
|
|
63
|
+
let data = trees[iid];
|
|
64
|
+
let pos = data.posHeight.xyz;
|
|
65
|
+
let height = data.posHeight.w;
|
|
66
|
+
let canopy = data.canopySeed.x;
|
|
67
|
+
let seed = data.canopySeed.y;
|
|
68
|
+
let deciduous = data.canopySeed.z;
|
|
69
|
+
let leafiness = data.canopySeed.w;
|
|
70
|
+
|
|
71
|
+
let quadIndex = vid / 6u;
|
|
72
|
+
let localVid = vid % 6u;
|
|
73
|
+
let uv = quad_uv(localVid);
|
|
74
|
+
let right = scene.cameraRight.xyz;
|
|
75
|
+
let up = scene.cameraUp.xyz;
|
|
76
|
+
let forward = normalize(cross(right, up));
|
|
77
|
+
let diag = normalize(right + forward);
|
|
78
|
+
let anti = normalize(right - forward);
|
|
79
|
+
var basis = right;
|
|
80
|
+
if (quadIndex == 1u) {
|
|
81
|
+
basis = forward;
|
|
82
|
+
} else if (quadIndex == 2u) {
|
|
83
|
+
basis = diag;
|
|
84
|
+
} else if (quadIndex == 3u) {
|
|
85
|
+
basis = anti;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
let dist = length(scene.cameraPos.xyz - pos);
|
|
89
|
+
let lod = clamp((dist - 6.0) / 20.0, 0.0, 1.0);
|
|
90
|
+
let lodScale = mix(1.0, 0.6, lod);
|
|
91
|
+
|
|
92
|
+
let partIndex = quadIndex / 4u;
|
|
93
|
+
var localY = uv.y;
|
|
94
|
+
if (partIndex == 0u) {
|
|
95
|
+
localY = uv.y * 0.55;
|
|
96
|
+
} else if (partIndex == 1u) {
|
|
97
|
+
localY = 0.5 + uv.y * 0.45;
|
|
98
|
+
} else {
|
|
99
|
+
localY = 0.78 + uv.y * 0.35;
|
|
100
|
+
}
|
|
101
|
+
let canopyScale = mix(0.5, 1.0, f32(partIndex) / 2.0);
|
|
102
|
+
|
|
103
|
+
var out: VSOut;
|
|
104
|
+
let basePos = pos + basis * (uv.x - 0.5) * canopy * 1.2 * canopyScale * lodScale + up * (localY) * height * lodScale;
|
|
105
|
+
let windDir = normalize(scene.wind.xyz);
|
|
106
|
+
let time = scene.wind.w;
|
|
107
|
+
let windStrength = scene.season.y;
|
|
108
|
+
let gustiness = scene.season.z;
|
|
109
|
+
let heightFactor = clamp(localY, 0.0, 1.0);
|
|
110
|
+
let swayBase = smoothstep(0.15, 0.95, heightFactor);
|
|
111
|
+
let sway = sin(time * (0.6 + gustiness) + seed * 6.1 + uv.y * 2.1) * windStrength * swayBase;
|
|
112
|
+
let flutter = sin(time * 1.7 + seed * 3.3 + uv.x * 6.0) * windStrength * 0.2 * leafiness;
|
|
113
|
+
let windOffset = windDir * (sway + flutter) * (1.0 - lod * 0.6);
|
|
114
|
+
let worldPos = basePos + windOffset;
|
|
115
|
+
out.position = scene.viewProj * vec4<f32>(worldPos, 1.0);
|
|
116
|
+
out.uv = uv;
|
|
117
|
+
out.baseColor = vec3<f32>(0.18, 0.48, 0.2);
|
|
118
|
+
out.trunkColor = vec3<f32>(0.25, 0.16, 0.08);
|
|
119
|
+
out.height = height;
|
|
120
|
+
out.leafiness = leafiness;
|
|
121
|
+
out.deciduous = deciduous;
|
|
122
|
+
out.lod = lod;
|
|
123
|
+
out.worldPos = worldPos;
|
|
124
|
+
out.seed = seed;
|
|
125
|
+
return out;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
@fragment
|
|
129
|
+
fn fs_main(input: VSOut) -> @location(0) vec4<f32> {
|
|
130
|
+
let uv = input.uv;
|
|
131
|
+
let trunkHeight = 0.42;
|
|
132
|
+
let trunkWidth = mix(0.18, 0.08, uv.y / trunkHeight);
|
|
133
|
+
let centerX = abs(uv.x - 0.5);
|
|
134
|
+
|
|
135
|
+
if (uv.y <= trunkHeight && centerX < trunkWidth) {
|
|
136
|
+
return vec4<f32>(input.trunkColor, 1.0);
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
if (uv.y > trunkHeight && uv.y < 0.7) {
|
|
140
|
+
let branchWave = sin((uv.y + input.seed * 1.7) * 12.0) * 0.5 + 0.5;
|
|
141
|
+
let branchOffset = (branchWave - 0.5) * 0.35;
|
|
142
|
+
let branchWidth = mix(0.12, 0.04, uv.y);
|
|
143
|
+
if (abs(uv.x - (0.5 + branchOffset)) < branchWidth) {
|
|
144
|
+
return vec4<f32>(input.trunkColor * 0.9, 1.0);
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
let canopyCenter = vec2<f32>(0.5, 0.7);
|
|
149
|
+
let rel = uv - canopyCenter;
|
|
150
|
+
let radius = length(rel);
|
|
151
|
+
|
|
152
|
+
let iterCount = u32(round(mix(20.0, 6.0, input.lod)));
|
|
153
|
+
let c = rel * 2.2 + vec2<f32>(input.seed * 0.35, input.seed * -0.22);
|
|
154
|
+
let fract = mandelbrot(c, iterCount);
|
|
155
|
+
let leafMask = smoothstep(0.85, 0.35, radius + fract * 0.2);
|
|
156
|
+
|
|
157
|
+
let season = scene.season.x;
|
|
158
|
+
var leafAlpha = leafMask * input.leafiness * mix(1.0, 0.6, input.lod);
|
|
159
|
+
if (input.deciduous > 0.5) {
|
|
160
|
+
leafAlpha = leafAlpha * mix(1.0, 0.35, season);
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
if (leafAlpha < 0.02) {
|
|
164
|
+
discard;
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
let summer = vec3<f32>(0.2, 0.55, 0.24);
|
|
168
|
+
let autumn = vec3<f32>(0.72, 0.38, 0.18);
|
|
169
|
+
let leafColor = mix(summer, autumn, season * input.deciduous);
|
|
170
|
+
|
|
171
|
+
return vec4<f32>(leafColor, leafAlpha);
|
|
172
|
+
}
|
|
@@ -0,0 +1,361 @@
|
|
|
1
|
+
struct SimParams {
|
|
2
|
+
sim0: vec4<f32>, // origin.x, origin.z, invSize, gridSize
|
|
3
|
+
sim1: vec4<f32>, // dt, flipRatio, particleCount, pad
|
|
4
|
+
sim2: vec4<f32>, // velScale, weightScale, heightScale, densityScale
|
|
5
|
+
sim3: vec4<f32>, // damping, bounce, pad, pad
|
|
6
|
+
};
|
|
7
|
+
|
|
8
|
+
struct GridAccum {
|
|
9
|
+
velX: atomic<i32>,
|
|
10
|
+
velY: atomic<i32>,
|
|
11
|
+
weight: atomic<i32>,
|
|
12
|
+
pad: atomic<i32>,
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
@group(0) @binding(0) var<uniform> params: SimParams;
|
|
16
|
+
@group(0) @binding(1) var<storage, read_write> particles: array<vec4<f32>>;
|
|
17
|
+
@group(0) @binding(2) var<storage, read_write> gridAccum: array<GridAccum>;
|
|
18
|
+
@group(0) @binding(3) var<storage, read_write> gridVel: array<vec2<f32>>;
|
|
19
|
+
@group(0) @binding(4) var<storage, read_write> gridVelPrev: array<vec2<f32>>;
|
|
20
|
+
@group(0) @binding(5) var<storage, read_write> gridDivergence: array<f32>;
|
|
21
|
+
@group(0) @binding(6) var<storage, read_write> gridPressure: array<f32>;
|
|
22
|
+
@group(0) @binding(7) var<storage, read_write> gridPressurePrev: array<f32>;
|
|
23
|
+
@group(0) @binding(8) var<storage, read_write> gridHeight: array<f32>;
|
|
24
|
+
|
|
25
|
+
fn grid_size() -> i32 {
|
|
26
|
+
return i32(params.sim0.w);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
fn grid_count() -> i32 {
|
|
30
|
+
let size = grid_size();
|
|
31
|
+
return size * size;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
fn grid_index(ix: i32, iy: i32, size: i32) -> i32 {
|
|
35
|
+
return iy * size + ix;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
fn clamp_cell(ix: i32, size: i32) -> i32 {
|
|
39
|
+
return clamp(ix, 0, size - 1);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
fn sample_grid_vel_current(uv: vec2<f32>) -> vec2<f32> {
|
|
43
|
+
let size = grid_size();
|
|
44
|
+
let grid_max = f32(size - 1);
|
|
45
|
+
let gx = uv.x * grid_max;
|
|
46
|
+
let gy = uv.y * grid_max;
|
|
47
|
+
let ix = i32(floor(gx));
|
|
48
|
+
let iy = i32(floor(gy));
|
|
49
|
+
let fx = fract(gx);
|
|
50
|
+
let fy = fract(gy);
|
|
51
|
+
let ix0 = clamp(ix, 0, size - 1);
|
|
52
|
+
let iy0 = clamp(iy, 0, size - 1);
|
|
53
|
+
let ix1 = clamp(ix + 1, 0, size - 1);
|
|
54
|
+
let iy1 = clamp(iy + 1, 0, size - 1);
|
|
55
|
+
let idx00 = grid_index(ix0, iy0, size);
|
|
56
|
+
let idx10 = grid_index(ix1, iy0, size);
|
|
57
|
+
let idx01 = grid_index(ix0, iy1, size);
|
|
58
|
+
let idx11 = grid_index(ix1, iy1, size);
|
|
59
|
+
let v00 = gridVel[idx00];
|
|
60
|
+
let v10 = gridVel[idx10];
|
|
61
|
+
let v01 = gridVel[idx01];
|
|
62
|
+
let v11 = gridVel[idx11];
|
|
63
|
+
let vx0 = mix(v00, v10, fx);
|
|
64
|
+
let vx1 = mix(v01, v11, fx);
|
|
65
|
+
return mix(vx0, vx1, fy);
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
fn sample_grid_vel_prev(uv: vec2<f32>) -> vec2<f32> {
|
|
69
|
+
let size = grid_size();
|
|
70
|
+
let grid_max = f32(size - 1);
|
|
71
|
+
let gx = uv.x * grid_max;
|
|
72
|
+
let gy = uv.y * grid_max;
|
|
73
|
+
let ix = i32(floor(gx));
|
|
74
|
+
let iy = i32(floor(gy));
|
|
75
|
+
let fx = fract(gx);
|
|
76
|
+
let fy = fract(gy);
|
|
77
|
+
let ix0 = clamp(ix, 0, size - 1);
|
|
78
|
+
let iy0 = clamp(iy, 0, size - 1);
|
|
79
|
+
let ix1 = clamp(ix + 1, 0, size - 1);
|
|
80
|
+
let iy1 = clamp(iy + 1, 0, size - 1);
|
|
81
|
+
let idx00 = grid_index(ix0, iy0, size);
|
|
82
|
+
let idx10 = grid_index(ix1, iy0, size);
|
|
83
|
+
let idx01 = grid_index(ix0, iy1, size);
|
|
84
|
+
let idx11 = grid_index(ix1, iy1, size);
|
|
85
|
+
let v00 = gridVelPrev[idx00];
|
|
86
|
+
let v10 = gridVelPrev[idx10];
|
|
87
|
+
let v01 = gridVelPrev[idx01];
|
|
88
|
+
let v11 = gridVelPrev[idx11];
|
|
89
|
+
let vx0 = mix(v00, v10, fx);
|
|
90
|
+
let vx1 = mix(v01, v11, fx);
|
|
91
|
+
return mix(vx0, vx1, fy);
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
fn particle_uv(pos: vec2<f32>) -> vec2<f32> {
|
|
95
|
+
let origin = params.sim0.xy;
|
|
96
|
+
let invSize = params.sim0.z;
|
|
97
|
+
return clamp((pos - origin) * invSize, vec2<f32>(0.0), vec2<f32>(1.0));
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
@compute @workgroup_size(128)
|
|
101
|
+
fn copy_grid(@builtin(global_invocation_id) gid: vec3<u32>) {
|
|
102
|
+
let idx = i32(gid.x);
|
|
103
|
+
let count = grid_count();
|
|
104
|
+
if (idx >= count) {
|
|
105
|
+
return;
|
|
106
|
+
}
|
|
107
|
+
gridVelPrev[idx] = gridVel[idx];
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
@compute @workgroup_size(128)
|
|
111
|
+
fn clear_grid(@builtin(global_invocation_id) gid: vec3<u32>) {
|
|
112
|
+
let idx = i32(gid.x);
|
|
113
|
+
let count = grid_count();
|
|
114
|
+
if (idx >= count) {
|
|
115
|
+
return;
|
|
116
|
+
}
|
|
117
|
+
atomicStore(&gridAccum[idx].velX, 0);
|
|
118
|
+
atomicStore(&gridAccum[idx].velY, 0);
|
|
119
|
+
atomicStore(&gridAccum[idx].weight, 0);
|
|
120
|
+
atomicStore(&gridAccum[idx].pad, 0);
|
|
121
|
+
gridVel[idx] = vec2<f32>(0.0);
|
|
122
|
+
gridDivergence[idx] = 0.0;
|
|
123
|
+
gridPressure[idx] = 0.0;
|
|
124
|
+
gridPressurePrev[idx] = 0.0;
|
|
125
|
+
gridHeight[idx] = 0.0;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
@compute @workgroup_size(128)
|
|
129
|
+
fn particles_to_grid(@builtin(global_invocation_id) gid: vec3<u32>) {
|
|
130
|
+
let idx = i32(gid.x);
|
|
131
|
+
let count = i32(params.sim1.z);
|
|
132
|
+
if (idx >= count) {
|
|
133
|
+
return;
|
|
134
|
+
}
|
|
135
|
+
let pos = particles[idx].xy;
|
|
136
|
+
let vel = particles[idx].zw;
|
|
137
|
+
let size = grid_size();
|
|
138
|
+
let grid_max = f32(size - 1);
|
|
139
|
+
let uv = particle_uv(pos);
|
|
140
|
+
let gx = uv.x * grid_max;
|
|
141
|
+
let gy = uv.y * grid_max;
|
|
142
|
+
let ix = i32(floor(gx));
|
|
143
|
+
let iy = i32(floor(gy));
|
|
144
|
+
let fx = fract(gx);
|
|
145
|
+
let fy = fract(gy);
|
|
146
|
+
let ix0 = clamp(ix, 0, size - 2);
|
|
147
|
+
let iy0 = clamp(iy, 0, size - 2);
|
|
148
|
+
let ix1 = ix0 + 1;
|
|
149
|
+
let iy1 = iy0 + 1;
|
|
150
|
+
|
|
151
|
+
let w00 = (1.0 - fx) * (1.0 - fy);
|
|
152
|
+
let w10 = fx * (1.0 - fy);
|
|
153
|
+
let w01 = (1.0 - fx) * fy;
|
|
154
|
+
let w11 = fx * fy;
|
|
155
|
+
|
|
156
|
+
let velScale = params.sim2.x;
|
|
157
|
+
let weightScale = params.sim2.y;
|
|
158
|
+
|
|
159
|
+
let v00x = i32(round(vel.x * velScale * w00));
|
|
160
|
+
let v00y = i32(round(vel.y * velScale * w00));
|
|
161
|
+
let v10x = i32(round(vel.x * velScale * w10));
|
|
162
|
+
let v10y = i32(round(vel.y * velScale * w10));
|
|
163
|
+
let v01x = i32(round(vel.x * velScale * w01));
|
|
164
|
+
let v01y = i32(round(vel.y * velScale * w01));
|
|
165
|
+
let v11x = i32(round(vel.x * velScale * w11));
|
|
166
|
+
let v11y = i32(round(vel.y * velScale * w11));
|
|
167
|
+
|
|
168
|
+
let w00i = i32(round(weightScale * w00));
|
|
169
|
+
let w10i = i32(round(weightScale * w10));
|
|
170
|
+
let w01i = i32(round(weightScale * w01));
|
|
171
|
+
let w11i = i32(round(weightScale * w11));
|
|
172
|
+
|
|
173
|
+
let idx00 = grid_index(ix0, iy0, size);
|
|
174
|
+
let idx10 = grid_index(ix1, iy0, size);
|
|
175
|
+
let idx01 = grid_index(ix0, iy1, size);
|
|
176
|
+
let idx11 = grid_index(ix1, iy1, size);
|
|
177
|
+
|
|
178
|
+
atomicAdd(&gridAccum[idx00].velX, v00x);
|
|
179
|
+
atomicAdd(&gridAccum[idx00].velY, v00y);
|
|
180
|
+
atomicAdd(&gridAccum[idx00].weight, w00i);
|
|
181
|
+
|
|
182
|
+
atomicAdd(&gridAccum[idx10].velX, v10x);
|
|
183
|
+
atomicAdd(&gridAccum[idx10].velY, v10y);
|
|
184
|
+
atomicAdd(&gridAccum[idx10].weight, w10i);
|
|
185
|
+
|
|
186
|
+
atomicAdd(&gridAccum[idx01].velX, v01x);
|
|
187
|
+
atomicAdd(&gridAccum[idx01].velY, v01y);
|
|
188
|
+
atomicAdd(&gridAccum[idx01].weight, w01i);
|
|
189
|
+
|
|
190
|
+
atomicAdd(&gridAccum[idx11].velX, v11x);
|
|
191
|
+
atomicAdd(&gridAccum[idx11].velY, v11y);
|
|
192
|
+
atomicAdd(&gridAccum[idx11].weight, w11i);
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
@compute @workgroup_size(128)
|
|
196
|
+
fn normalize_grid(@builtin(global_invocation_id) gid: vec3<u32>) {
|
|
197
|
+
let idx = i32(gid.x);
|
|
198
|
+
let count = grid_count();
|
|
199
|
+
if (idx >= count) {
|
|
200
|
+
return;
|
|
201
|
+
}
|
|
202
|
+
let weightScale = params.sim2.y;
|
|
203
|
+
let velScale = params.sim2.x;
|
|
204
|
+
let damping = params.sim3.x;
|
|
205
|
+
let heightScale = params.sim2.z;
|
|
206
|
+
let densityScale = max(params.sim2.w, 1e-3);
|
|
207
|
+
|
|
208
|
+
let w = f32(atomicLoad(&gridAccum[idx].weight)) / weightScale;
|
|
209
|
+
var vel = vec2<f32>(0.0);
|
|
210
|
+
if (w > 0.0) {
|
|
211
|
+
let vx = f32(atomicLoad(&gridAccum[idx].velX)) / (velScale * w);
|
|
212
|
+
let vy = f32(atomicLoad(&gridAccum[idx].velY)) / (velScale * w);
|
|
213
|
+
vel = vec2<f32>(vx, vy);
|
|
214
|
+
}
|
|
215
|
+
vel = vel * (1.0 - damping);
|
|
216
|
+
gridVel[idx] = vel;
|
|
217
|
+
let height = clamp(w / densityScale, 0.0, 1.0) * heightScale;
|
|
218
|
+
gridHeight[idx] = height;
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
@compute @workgroup_size(128)
|
|
222
|
+
fn compute_divergence(@builtin(global_invocation_id) gid: vec3<u32>) {
|
|
223
|
+
let idx = i32(gid.x);
|
|
224
|
+
let size = grid_size();
|
|
225
|
+
let count = size * size;
|
|
226
|
+
if (idx >= count) {
|
|
227
|
+
return;
|
|
228
|
+
}
|
|
229
|
+
let ix = idx % size;
|
|
230
|
+
let iy = idx / size;
|
|
231
|
+
let invCell = params.sim0.z * (f32(size - 1));
|
|
232
|
+
|
|
233
|
+
var left = vec2<f32>(0.0);
|
|
234
|
+
var right = vec2<f32>(0.0);
|
|
235
|
+
var down = vec2<f32>(0.0);
|
|
236
|
+
var up = vec2<f32>(0.0);
|
|
237
|
+
if (ix > 0) {
|
|
238
|
+
left = gridVel[grid_index(ix - 1, iy, size)];
|
|
239
|
+
}
|
|
240
|
+
if (ix < size - 1) {
|
|
241
|
+
right = gridVel[grid_index(ix + 1, iy, size)];
|
|
242
|
+
}
|
|
243
|
+
if (iy > 0) {
|
|
244
|
+
down = gridVel[grid_index(ix, iy - 1, size)];
|
|
245
|
+
}
|
|
246
|
+
if (iy < size - 1) {
|
|
247
|
+
up = gridVel[grid_index(ix, iy + 1, size)];
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
let div = (right.x - left.x + up.y - down.y) * 0.5 * invCell;
|
|
251
|
+
gridDivergence[idx] = div;
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
@compute @workgroup_size(128)
|
|
255
|
+
fn pressure_jacobi(@builtin(global_invocation_id) gid: vec3<u32>) {
|
|
256
|
+
let idx = i32(gid.x);
|
|
257
|
+
let size = grid_size();
|
|
258
|
+
let count = size * size;
|
|
259
|
+
if (idx >= count) {
|
|
260
|
+
return;
|
|
261
|
+
}
|
|
262
|
+
let ix = idx % size;
|
|
263
|
+
let iy = idx / size;
|
|
264
|
+
|
|
265
|
+
var pL = 0.0;
|
|
266
|
+
var pR = 0.0;
|
|
267
|
+
var pB = 0.0;
|
|
268
|
+
var pT = 0.0;
|
|
269
|
+
if (ix > 0) {
|
|
270
|
+
pL = gridPressurePrev[grid_index(ix - 1, iy, size)];
|
|
271
|
+
}
|
|
272
|
+
if (ix < size - 1) {
|
|
273
|
+
pR = gridPressurePrev[grid_index(ix + 1, iy, size)];
|
|
274
|
+
}
|
|
275
|
+
if (iy > 0) {
|
|
276
|
+
pB = gridPressurePrev[grid_index(ix, iy - 1, size)];
|
|
277
|
+
}
|
|
278
|
+
if (iy < size - 1) {
|
|
279
|
+
pT = gridPressurePrev[grid_index(ix, iy + 1, size)];
|
|
280
|
+
}
|
|
281
|
+
let div = gridDivergence[idx];
|
|
282
|
+
gridPressure[idx] = (pL + pR + pB + pT - div) * 0.25;
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
@compute @workgroup_size(128)
|
|
286
|
+
fn project_grid(@builtin(global_invocation_id) gid: vec3<u32>) {
|
|
287
|
+
let idx = i32(gid.x);
|
|
288
|
+
let size = grid_size();
|
|
289
|
+
let count = size * size;
|
|
290
|
+
if (idx >= count) {
|
|
291
|
+
return;
|
|
292
|
+
}
|
|
293
|
+
let ix = idx % size;
|
|
294
|
+
let iy = idx / size;
|
|
295
|
+
let invCell = params.sim0.z * (f32(size - 1));
|
|
296
|
+
|
|
297
|
+
var pL = 0.0;
|
|
298
|
+
var pR = 0.0;
|
|
299
|
+
var pB = 0.0;
|
|
300
|
+
var pT = 0.0;
|
|
301
|
+
if (ix > 0) {
|
|
302
|
+
pL = gridPressure[grid_index(ix - 1, iy, size)];
|
|
303
|
+
}
|
|
304
|
+
if (ix < size - 1) {
|
|
305
|
+
pR = gridPressure[grid_index(ix + 1, iy, size)];
|
|
306
|
+
}
|
|
307
|
+
if (iy > 0) {
|
|
308
|
+
pB = gridPressure[grid_index(ix, iy - 1, size)];
|
|
309
|
+
}
|
|
310
|
+
if (iy < size - 1) {
|
|
311
|
+
pT = gridPressure[grid_index(ix, iy + 1, size)];
|
|
312
|
+
}
|
|
313
|
+
var vel = gridVel[idx];
|
|
314
|
+
vel.x = vel.x - (pR - pL) * 0.5 * invCell;
|
|
315
|
+
vel.y = vel.y - (pT - pB) * 0.5 * invCell;
|
|
316
|
+
gridVel[idx] = vel;
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
@compute @workgroup_size(128)
|
|
320
|
+
fn update_particles(@builtin(global_invocation_id) gid: vec3<u32>) {
|
|
321
|
+
let idx = i32(gid.x);
|
|
322
|
+
let count = i32(params.sim1.z);
|
|
323
|
+
if (idx >= count) {
|
|
324
|
+
return;
|
|
325
|
+
}
|
|
326
|
+
let dt = params.sim1.x;
|
|
327
|
+
let flipRatio = params.sim1.y;
|
|
328
|
+
let bounce = params.sim3.y;
|
|
329
|
+
let origin = params.sim0.xy;
|
|
330
|
+
let size = 1.0 / params.sim0.z;
|
|
331
|
+
|
|
332
|
+
var pos = particles[idx].xy;
|
|
333
|
+
var vel = particles[idx].zw;
|
|
334
|
+
let uv = particle_uv(pos);
|
|
335
|
+
|
|
336
|
+
let pic = sample_grid_vel_current(uv);
|
|
337
|
+
let prev = sample_grid_vel_prev(uv);
|
|
338
|
+
let flip = vel + (pic - prev);
|
|
339
|
+
vel = mix(pic, flip, flipRatio);
|
|
340
|
+
|
|
341
|
+
pos = pos + vel * dt;
|
|
342
|
+
|
|
343
|
+
let minPos = origin;
|
|
344
|
+
let maxPos = origin + vec2<f32>(size);
|
|
345
|
+
if (pos.x < minPos.x) {
|
|
346
|
+
pos.x = minPos.x;
|
|
347
|
+
vel.x = -vel.x * bounce;
|
|
348
|
+
} else if (pos.x > maxPos.x) {
|
|
349
|
+
pos.x = maxPos.x;
|
|
350
|
+
vel.x = -vel.x * bounce;
|
|
351
|
+
}
|
|
352
|
+
if (pos.y < minPos.y) {
|
|
353
|
+
pos.y = minPos.y;
|
|
354
|
+
vel.y = -vel.y * bounce;
|
|
355
|
+
} else if (pos.y > maxPos.y) {
|
|
356
|
+
pos.y = maxPos.y;
|
|
357
|
+
vel.y = -vel.y * bounce;
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
particles[idx] = vec4<f32>(pos, vel);
|
|
361
|
+
}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
const PI: f32 = 3.1415926535;
|
|
2
|
+
|
|
3
|
+
fn hash12(p: vec2<f32>) -> f32 {
|
|
4
|
+
let h = dot(p, vec2<f32>(127.1, 311.7));
|
|
5
|
+
return fract(sin(h) * 43758.5453123);
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
fn noise2(p: vec2<f32>) -> f32 {
|
|
9
|
+
let i = floor(p);
|
|
10
|
+
let f = fract(p);
|
|
11
|
+
let a = hash12(i);
|
|
12
|
+
let b = hash12(i + vec2<f32>(1.0, 0.0));
|
|
13
|
+
let c = hash12(i + vec2<f32>(0.0, 1.0));
|
|
14
|
+
let d = hash12(i + vec2<f32>(1.0, 1.0));
|
|
15
|
+
let u = f * f * (3.0 - 2.0 * f);
|
|
16
|
+
return mix(a, b, u.x) + (c - a) * u.y * (1.0 - u.x) + (d - b) * u.x * u.y;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
fn fbm2(p: vec2<f32>) -> f32 {
|
|
20
|
+
var value = 0.0;
|
|
21
|
+
var amp = 0.5;
|
|
22
|
+
var freq = 1.0;
|
|
23
|
+
var coord = p;
|
|
24
|
+
value = value + amp * noise2(coord * freq);
|
|
25
|
+
amp = amp * 0.5;
|
|
26
|
+
freq = freq * 2.0;
|
|
27
|
+
coord = coord + vec2<f32>(1.7, 9.2);
|
|
28
|
+
value = value + amp * noise2(coord * freq);
|
|
29
|
+
amp = amp * 0.5;
|
|
30
|
+
freq = freq * 2.0;
|
|
31
|
+
coord = coord + vec2<f32>(8.3, 2.8);
|
|
32
|
+
value = value + amp * noise2(coord * freq);
|
|
33
|
+
return value;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
fn clamp01(v: f32) -> f32 {
|
|
37
|
+
return clamp(v, 0.0, 1.0);
|
|
38
|
+
}
|