@multiplekex/shallot 0.2.3 → 0.2.5

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 (42) hide show
  1. package/package.json +1 -1
  2. package/src/extras/arrows/index.ts +3 -3
  3. package/src/extras/caustic.ts +37 -0
  4. package/src/extras/gradient/index.ts +63 -69
  5. package/src/extras/index.ts +3 -0
  6. package/src/extras/lines/index.ts +3 -3
  7. package/src/extras/skylab/index.ts +314 -0
  8. package/src/extras/text/font.ts +69 -14
  9. package/src/extras/text/index.ts +15 -7
  10. package/src/extras/text/sdf.ts +13 -2
  11. package/src/extras/water.ts +64 -0
  12. package/src/standard/defaults.ts +2 -0
  13. package/src/standard/index.ts +2 -0
  14. package/src/standard/raster/index.ts +517 -0
  15. package/src/standard/{render → raytracing}/bvh/blas.ts +3 -3
  16. package/src/standard/{render → raytracing}/bvh/tlas.ts +3 -0
  17. package/src/standard/{render → raytracing}/depth.ts +9 -9
  18. package/src/standard/raytracing/index.ts +380 -0
  19. package/src/standard/{render → raytracing}/instance.ts +3 -0
  20. package/src/standard/raytracing/shaders.ts +815 -0
  21. package/src/standard/{render → raytracing}/triangle.ts +1 -1
  22. package/src/standard/render/camera.ts +88 -80
  23. package/src/standard/render/index.ts +68 -208
  24. package/src/standard/render/indirect.ts +9 -10
  25. package/src/standard/render/mesh/index.ts +35 -166
  26. package/src/standard/render/overlay.ts +4 -4
  27. package/src/standard/render/pass.ts +1 -1
  28. package/src/standard/render/postprocess.ts +75 -50
  29. package/src/standard/render/scene.ts +28 -16
  30. package/src/standard/render/surface/compile.ts +6 -8
  31. package/src/standard/render/surface/noise.ts +15 -2
  32. package/src/standard/render/surface/shaders.ts +257 -0
  33. package/src/standard/render/surface/structs.ts +13 -6
  34. package/src/standard/render/forward/index.ts +0 -259
  35. package/src/standard/render/forward/raster.ts +0 -228
  36. package/src/standard/render/shaders.ts +0 -484
  37. package/src/standard/render/surface/wgsl.ts +0 -573
  38. /package/src/standard/{render → raytracing}/bvh/radix.ts +0 -0
  39. /package/src/standard/{render → raytracing}/bvh/structs.ts +0 -0
  40. /package/src/standard/{render → raytracing}/bvh/traverse.ts +0 -0
  41. /package/src/standard/{render → raytracing}/intersection.ts +0 -0
  42. /package/src/standard/{render → raytracing}/ray.ts +0 -0
@@ -5,6 +5,17 @@ fn hash2(p: vec2<f32>) -> f32 {
5
5
  return fract((p3.x + p3.y) * p3.z);
6
6
  }
7
7
 
8
+ fn value2d(p: vec2f, seed: vec2f) -> f32 {
9
+ let i = floor(p);
10
+ let f = fract(p);
11
+ let u = f * f * (3.0 - 2.0 * f);
12
+ return mix(
13
+ mix(fract(sin(dot(i, seed)) * 43758.5) * 2.0 - 1.0,
14
+ fract(sin(dot(i + vec2(1.0, 0.0), seed)) * 43758.5) * 2.0 - 1.0, u.x),
15
+ mix(fract(sin(dot(i + vec2(0.0, 1.0), seed)) * 43758.5) * 2.0 - 1.0,
16
+ fract(sin(dot(i + vec2(1.0, 1.0), seed)) * 43758.5) * 2.0 - 1.0, u.x), u.y);
17
+ }
18
+
8
19
  fn simplex2(p: vec2<f32>) -> f32 {
9
20
  let K1 = 0.366025404;
10
21
  let K2 = 0.211324865;
@@ -28,13 +39,15 @@ fn simplex2(p: vec2<f32>) -> f32 {
28
39
  return dot(h4, n) * 70.0;
29
40
  }
30
41
 
31
- fn fbm2(p: vec2<f32>, octaves: i32) -> f32 {
42
+ const FBM2_OCTAVES = 5;
43
+
44
+ fn fbm2(p: vec2<f32>) -> f32 {
32
45
  var value = 0.0;
33
46
  var amplitude = 0.5;
34
47
  var frequency = 1.0;
35
48
  var pos = p;
36
49
 
37
- for (var i = 0; i < octaves; i++) {
50
+ for (var i = 0; i < FBM2_OCTAVES; i++) {
38
51
  value += amplitude * simplex2(pos * frequency);
39
52
  amplitude *= 0.5;
40
53
  frequency *= 2.0;
@@ -0,0 +1,257 @@
1
+ export function compileVertexBody(vertex?: string): string {
2
+ return vertex
3
+ ? `var pos = worldPos;
4
+ ${vertex}
5
+ return pos;`
6
+ : "return worldPos;";
7
+ }
8
+
9
+ export const STARS_WGSL = /* wgsl */ `
10
+ fn hashStar(p: vec2<f32>) -> f32 {
11
+ var p3 = fract(vec3(p.x, p.y, p.x) * 0.1031);
12
+ p3 += dot(p3, p3.yzx + 33.33);
13
+ return fract((p3.x + p3.y) * p3.z);
14
+ }
15
+
16
+ fn hash2Star(p: vec2<f32>) -> vec2<f32> {
17
+ var p3 = fract(vec3(p.x, p.y, p.x) * vec3(0.1031, 0.1030, 0.0973));
18
+ p3 += dot(p3, p3.yzx + 33.33);
19
+ return fract((p3.xx + p3.yz) * p3.zy);
20
+ }
21
+
22
+ fn sampleStars(dir: vec3<f32>) -> vec3<f32> {
23
+ if (sky.starParams.z <= 0.0 || dir.y < 0.0) {
24
+ return vec3(0.0);
25
+ }
26
+
27
+ let theta = atan2(dir.z, dir.x);
28
+ let phi = asin(clamp(dir.y, -1.0, 1.0));
29
+
30
+ let gridSize = mix(20.0, 100.0, sky.starParams.y);
31
+ let cell = vec2(theta * gridSize / 3.14159, phi * gridSize / 1.5708);
32
+ let cellId = floor(cell);
33
+ let cellFract = fract(cell);
34
+
35
+ var starColor = vec3(0.0);
36
+
37
+ for (var dy = -1; dy <= 1; dy++) {
38
+ for (var dx = -1; dx <= 1; dx++) {
39
+ let neighbor = cellId + vec2(f32(dx), f32(dy));
40
+ let starHash = hashStar(neighbor);
41
+
42
+ if (starHash > sky.starParams.y * 0.7) {
43
+ continue;
44
+ }
45
+
46
+ let starPos = hash2Star(neighbor);
47
+ let starCenter = neighbor + starPos;
48
+ let dist = length(cell - starCenter);
49
+
50
+ let brightness = hashStar(neighbor + vec2(100.0, 100.0));
51
+ let radius = 0.02 + brightness * 0.03;
52
+
53
+ if (dist < radius) {
54
+ let twinkle = 0.8 + 0.2 * sin(brightness * 100.0);
55
+ let intensity = sky.starParams.x * brightness * twinkle;
56
+ let falloff = 1.0 - smoothstep(0.0, radius, dist);
57
+
58
+ let temp = hashStar(neighbor + vec2(200.0, 200.0));
59
+ let tint = mix(vec3(1.0, 0.9, 0.8), vec3(0.8, 0.9, 1.0), temp);
60
+
61
+ starColor = max(starColor, tint * intensity * falloff);
62
+ }
63
+ }
64
+ }
65
+
66
+ return starColor;
67
+ }
68
+ `;
69
+
70
+ export { NOISE_WGSL } from "./noise";
71
+
72
+ export const MOON_WGSL = /* wgsl */ `
73
+ fn sampleMoon(dir: vec3<f32>) -> vec3<f32> {
74
+ if (sky.moonParams.z <= 0.0) {
75
+ return vec3(0.0);
76
+ }
77
+
78
+ let moonDir = sky.moonDirection.xyz;
79
+ let moonDot = dot(dir, moonDir);
80
+
81
+ let moonSize = 0.9995;
82
+ let moonColor = vec3(0.9, 0.9, 0.85);
83
+ let edgeWidth = 0.0003;
84
+ let glowStart = 0.999;
85
+
86
+ if (moonDot > moonSize - edgeWidth) {
87
+ let toCenter = dir - moonDir * moonDot;
88
+ let diskRight = normalize(cross(moonDir, vec3(0.0, 1.0, 0.0)));
89
+ let diskUp = cross(diskRight, moonDir);
90
+
91
+ let diskRadius = sqrt(1.0 - moonSize * moonSize);
92
+ let u = dot(toCenter, diskRight) / diskRadius;
93
+ let v = dot(toCenter, diskUp) / diskRadius;
94
+
95
+ let r2 = u * u + v * v;
96
+ let z = sqrt(max(0.0, 1.0 - r2));
97
+
98
+ let diskEdge = smoothstep(1.0 + edgeWidth / diskRadius, 1.0 - edgeWidth / diskRadius, sqrt(r2));
99
+
100
+ let limb = pow(z, 0.6);
101
+
102
+ let cellU = u * 8.0;
103
+ let cellV = v * 8.0;
104
+ let craterNoise = hashStar(floor(vec2(cellU, cellV)) + vec2(50.0, 50.0));
105
+ let surfaceVariation = 0.85 + 0.15 * craterNoise;
106
+
107
+ let phase = sky.moonParams.x;
108
+ let sunAngle = phase * 6.28318;
109
+ let sunLocalX = sin(sunAngle);
110
+ let sunLocalZ = -cos(sunAngle);
111
+
112
+ let illumination = u * sunLocalX + z * sunLocalZ;
113
+ let lit = smoothstep(-0.05, 0.05, illumination);
114
+
115
+ let earthshine = vec3(0.06, 0.07, 0.1);
116
+ let dayColor = moonColor * surfaceVariation * limb;
117
+ let surfaceColor = mix(earthshine * limb, dayColor, lit);
118
+
119
+ return surfaceColor * diskEdge * smoothstep(0.15, 0.3, sky.moonParams.y);
120
+ } else {
121
+ let glowFalloff = max(0.0, moonDot - glowStart) / (moonSize - edgeWidth - glowStart);
122
+ let glow = pow(glowFalloff, 2.0) * sky.moonParams.y;
123
+ return moonColor * glow * 0.3;
124
+ }
125
+ }
126
+ `;
127
+
128
+ export const CLOUDS_WGSL = /* wgsl */ `
129
+ fn sampleClouds(dir: vec3<f32>) -> vec4<f32> {
130
+ if (sky.cloudParams.w <= 0.0 || dir.y < 0.01) {
131
+ return vec4(0.0);
132
+ }
133
+
134
+ let t = sky.cloudParams.z / max(dir.y, 0.001);
135
+ let uv = dir.xz * t;
136
+
137
+ var n = fbm2(uv);
138
+
139
+ let coverage = sky.cloudParams.x;
140
+ let density = sky.cloudParams.y;
141
+ n = smoothstep(1.0 - coverage, 1.0, n * 0.5 + 0.5) * density;
142
+
143
+ n *= smoothstep(0.0, 0.15, dir.y);
144
+
145
+ return vec4(sky.cloudColor.rgb, n);
146
+ }
147
+ `;
148
+
149
+ const DEG_TO_RAD = Math.PI / 180;
150
+
151
+ export const SKY_DIR_WGSL = /* wgsl */ `
152
+ const DEG_TO_RAD: f32 = ${DEG_TO_RAD};
153
+
154
+ fn computeSkyDir(screenX: f32, screenY: f32) -> vec3<f32> {
155
+ let width = scene.viewport.x;
156
+ let height = scene.viewport.y;
157
+
158
+ let ndcX = screenX * 2.0 - 1.0;
159
+ let ndcY = 1.0 - screenY * 2.0;
160
+
161
+ let aspect = width / height;
162
+
163
+ let cameraWorld = scene.cameraWorld;
164
+ let r00 = cameraWorld[0][0]; let r10 = cameraWorld[0][1]; let r20 = cameraWorld[0][2];
165
+ let r01 = cameraWorld[1][0]; let r11 = cameraWorld[1][1]; let r21 = cameraWorld[1][2];
166
+ let r02 = cameraWorld[2][0]; let r12 = cameraWorld[2][1]; let r22 = cameraWorld[2][2];
167
+
168
+ let skyFov = select(scene.fov, 60.0, scene.cameraMode > 0.5);
169
+ let tanHalfFov = tan((skyFov * DEG_TO_RAD) / 2.0);
170
+ let camDirX = ndcX * aspect * tanHalfFov;
171
+ let camDirY = ndcY * tanHalfFov;
172
+ let camDirZ = -1.0;
173
+ var dirX = r00 * camDirX + r01 * camDirY + r02 * camDirZ;
174
+ var dirY = r10 * camDirX + r11 * camDirY + r12 * camDirZ;
175
+ var dirZ = r20 * camDirX + r21 * camDirY + r22 * camDirZ;
176
+ let len = sqrt(dirX * dirX + dirY * dirY + dirZ * dirZ);
177
+ dirX /= len; dirY /= len; dirZ /= len;
178
+ return vec3(dirX, dirY, dirZ);
179
+ }
180
+ `;
181
+
182
+ export const SKY_WGSL = /* wgsl */ `
183
+ ${STARS_WGSL}
184
+ ${MOON_WGSL}
185
+ ${CLOUDS_WGSL}
186
+
187
+ fn sampleSky(dir: vec3<f32>) -> vec3<f32> {
188
+ if (sky.skyZenith.a <= 0.0) {
189
+ return scene.clearColor.rgb;
190
+ }
191
+
192
+ let t = pow(clamp(dir.y, 0.0, 1.0), 0.25);
193
+ var color = mix(sky.skyHorizon.rgb, sky.skyZenith.rgb, t);
194
+
195
+ let horizonBand = 1.0 - abs(dir.y);
196
+ let horizonBlend = pow(horizonBand, 32.0);
197
+ color = mix(color, vec3(1.09), horizonBlend);
198
+
199
+ color += sampleStars(dir);
200
+
201
+ let clouds = sampleClouds(dir);
202
+ color = mix(color, clouds.rgb, clouds.a);
203
+
204
+ let moonContrib = sampleMoon(dir);
205
+ color += moonContrib * (1.0 - clouds.a * 0.7);
206
+
207
+ let sunDir = -scene.sunDirection.xyz;
208
+ let sunDot = dot(dir, sunDir);
209
+
210
+ let sunVisualColor = select(scene.sunColor.rgb, sky.sunVisualColor.rgb, sky.sunParams.z > 0.5);
211
+
212
+ let glowParam = sky.sunParams.y;
213
+
214
+ let baseSunSize = 0.9995;
215
+ let sunSizeParam = sky.sunParams.x;
216
+ let sunThreshold = 1.0 - (1.0 - baseSunSize) * sunSizeParam;
217
+ let sunEdgeWidth = (1.0 - sunThreshold) * 0.15;
218
+
219
+ let diskBlend = smoothstep(sunThreshold - sunEdgeWidth, sunThreshold + sunEdgeWidth, sunDot);
220
+ if (diskBlend > 0.0 && glowParam > 0.0) {
221
+ let radial = saturate((sunDot - sunThreshold) / (1.0 - sunThreshold));
222
+ let limbDarken = mix(0.7, 1.0, pow(radial, 0.5));
223
+ color += sunVisualColor * limbDarken * diskBlend * smoothstep(0.0, 0.3, glowParam);
224
+ }
225
+
226
+ let glow = max(0.0, sunDot);
227
+ let innerGlow = pow(glow, 32.0) * glowParam * 1.5;
228
+ let outerGlow = pow(glow, 3.0) * glowParam * 0.15;
229
+ color += sunVisualColor * (innerGlow + outerGlow);
230
+
231
+ if (sky.hazeDensity > 0.0) {
232
+ let horizonFactor = 1.0 - clamp(dir.y, 0.0, 1.0);
233
+ let hazeAmount = pow(horizonFactor, 2.0) * saturate(sky.hazeDensity * 5.0);
234
+ color = mix(color, sky.hazeColor.rgb, hazeAmount);
235
+ }
236
+
237
+ return color;
238
+ }
239
+ `;
240
+
241
+ export const HAZE_WGSL = /* wgsl */ `
242
+ fn applyHaze(color: vec3<f32>, dist: f32) -> vec3<f32> {
243
+ if (sky.hazeDensity <= 0.0) {
244
+ return color;
245
+ }
246
+ let haze = 1.0 - exp(-sky.hazeDensity * dist);
247
+ return mix(color, sky.hazeColor.rgb, haze);
248
+ }
249
+ `;
250
+
251
+ export const WGSL_LIGHTING_CALC = /* wgsl */ `
252
+ let NdotL = max(dot(surface.normal, -scene.sunDirection.xyz), 0.0);
253
+ let ambient = scene.ambientColor.rgb * scene.ambientColor.a;
254
+ let sunDiffuse = scene.sunColor.rgb * NdotL;
255
+ let diffuseWeight = 1.0 - surface.metallic;
256
+ let lighting = (ambient + sunDiffuse) * diffuseWeight;
257
+ `;
@@ -28,6 +28,15 @@ struct Scene {
28
28
  reflectionDepth: u32,
29
29
  refractionDepth: u32,
30
30
  instanceCount: u32,
31
+ time: f32,
32
+ _pad0: f32,
33
+ _pad1: f32,
34
+ _pad2: f32,
35
+ frustumPlanes: array<vec4<f32>, 6>,
36
+ }`;
37
+
38
+ export const SKY_STRUCT_WGSL = /* wgsl */ `
39
+ struct Sky {
31
40
  hazeDensity: f32,
32
41
  _pad2: f32,
33
42
  _pad3: f32,
@@ -35,7 +44,6 @@ struct Scene {
35
44
  hazeColor: vec4<f32>,
36
45
  skyZenith: vec4<f32>,
37
46
  skyHorizon: vec4<f32>,
38
- frustumPlanes: array<vec4<f32>, 6>,
39
47
  moonParams: vec4<f32>,
40
48
  moonDirection: vec4<f32>,
41
49
  starParams: vec4<f32>,
@@ -80,12 +88,11 @@ struct FragmentOutput {
80
88
 
81
89
  ${SCENE_STRUCT_WGSL}
82
90
 
91
+ ${DATA_STRUCT_WGSL}
92
+
83
93
  @group(0) @binding(0) var<uniform> scene: Scene;
84
94
  @group(0) @binding(1) var<storage, read> entityIds: array<u32>;
85
95
  @group(0) @binding(2) var<storage, read> matrices: array<mat4x4<f32>>;
86
- @group(0) @binding(3) var<storage, read> colors: array<vec4<f32>>;
87
- @group(0) @binding(4) var<storage, read> sizes: array<vec4<f32>>;
88
- @group(0) @binding(5) var<storage, read> pbr: array<vec4<f32>>;
89
- @group(0) @binding(6) var<storage, read> emission: array<vec4<f32>>;
90
- @group(0) @binding(7) var<storage, read> shapes: array<u32>;
96
+ @group(0) @binding(3) var<storage, read> sizes: array<vec4<f32>>;
97
+ @group(0) @binding(4) var<storage, read> data: array<Data>;
91
98
  `;
@@ -1,259 +0,0 @@
1
- import type { ComputeNode, ExecutionContext } from "../../compute";
2
- import type { SurfaceData } from "../surface";
3
- import type { Batch } from "../mesh";
4
- import { COLOR_FORMAT } from "../scene";
5
- import { compileUberShader } from "../shaders";
6
- import { createRasterPipeline, createSkyPipeline } from "./raster";
7
-
8
- export interface ForwardConfig {
9
- scene: GPUBuffer;
10
- matrices: GPUBuffer;
11
- colors: GPUBuffer;
12
- sizes: GPUBuffer;
13
- pbr: GPUBuffer;
14
- emission: GPUBuffer;
15
- shapes: GPUBuffer;
16
- surfaces: GPUBuffer;
17
- data: GPUBuffer;
18
- getSurfaces: () => SurfaceData[];
19
- getRaytracing: () => boolean;
20
- getClearColor: () => { r: number; g: number; b: number };
21
- getSky: () => boolean;
22
- acquire?: (message?: string) => (() => void) | undefined;
23
- batches: () => (Batch | null)[];
24
- tlasNodes: GPUBuffer;
25
- tlasInstanceIds: GPUBuffer;
26
- blasNodes: GPUBuffer;
27
- blasTriIds: GPUBuffer;
28
- blasTriangles: GPUBuffer;
29
- blasMeta: GPUBuffer;
30
- instanceInverses: GPUBuffer;
31
- }
32
-
33
- export type ForwardNode = ComputeNode;
34
-
35
- export function createForwardNode(config: ForwardConfig): ForwardNode {
36
- let rasterPipeline: GPURenderPipeline | null = null;
37
- let rtPipeline: GPUComputePipeline | null = null;
38
- let rtCompiling = false;
39
- let skyPipeline: GPURenderPipeline | null = null;
40
-
41
- function executeRT(ctx: ExecutionContext): void {
42
- const { device, encoder } = ctx;
43
-
44
- const colorView = ctx.getTextureView("color");
45
- const linearDepthView = ctx.getTextureView("linear-depth");
46
- const eidView = ctx.getTextureView("eid");
47
-
48
- if (!colorView || !linearDepthView || !eidView || !rtPipeline) {
49
- return;
50
- }
51
-
52
- const bindGroup0 = device.createBindGroup({
53
- layout: rtPipeline.getBindGroupLayout(0),
54
- entries: [
55
- { binding: 0, resource: { buffer: config.scene } },
56
- { binding: 1, resource: { buffer: config.data } },
57
- { binding: 2, resource: colorView },
58
- { binding: 3, resource: linearDepthView },
59
- { binding: 4, resource: eidView },
60
- ],
61
- });
62
-
63
- const bindGroup1 = device.createBindGroup({
64
- layout: rtPipeline.getBindGroupLayout(1),
65
- entries: [
66
- { binding: 0, resource: { buffer: config.tlasNodes } },
67
- { binding: 1, resource: { buffer: config.tlasInstanceIds } },
68
- { binding: 2, resource: { buffer: config.blasNodes } },
69
- { binding: 3, resource: { buffer: config.blasTriIds } },
70
- { binding: 4, resource: { buffer: config.blasTriangles } },
71
- { binding: 5, resource: { buffer: config.blasMeta } },
72
- { binding: 6, resource: { buffer: config.instanceInverses } },
73
- ],
74
- });
75
-
76
- const colorTexture = ctx.getTexture("color");
77
- if (!colorTexture) return;
78
-
79
- const width = colorTexture.width;
80
- const height = colorTexture.height;
81
-
82
- const pass = encoder.beginComputePass();
83
- pass.setPipeline(rtPipeline);
84
- pass.setBindGroup(0, bindGroup0);
85
- pass.setBindGroup(1, bindGroup1);
86
- pass.dispatchWorkgroups(Math.ceil(width / 8), Math.ceil(height / 8));
87
- pass.end();
88
- }
89
-
90
- function executeRaster(ctx: ExecutionContext): void {
91
- const { device, encoder } = ctx;
92
-
93
- const colorView = ctx.getTextureView("color");
94
- const eidView = ctx.getTextureView("eid");
95
- const depthView = ctx.getTextureView("depth");
96
-
97
- if (!colorView || !eidView || !depthView || !rasterPipeline) {
98
- return;
99
- }
100
-
101
- const batches = config.batches();
102
- const colorTexture = ctx.getTexture("color");
103
- if (!colorTexture) return;
104
-
105
- const clearColor = config.getClearColor();
106
- const hasSky = config.getSky() && skyPipeline;
107
-
108
- if (hasSky) {
109
- const skyPass = encoder.beginRenderPass({
110
- colorAttachments: [
111
- {
112
- view: colorView as GPUTextureView,
113
- clearValue: { r: clearColor.r, g: clearColor.g, b: clearColor.b, a: 1 },
114
- loadOp: "clear" as const,
115
- storeOp: "store" as const,
116
- },
117
- ],
118
- depthStencilAttachment: {
119
- view: depthView as GPUTextureView,
120
- depthClearValue: 1.0,
121
- depthLoadOp: "clear" as const,
122
- depthStoreOp: "store" as const,
123
- },
124
- });
125
-
126
- const skyBindGroup = device.createBindGroup({
127
- layout: skyPipeline!.getBindGroupLayout(0),
128
- entries: [{ binding: 0, resource: { buffer: config.scene } }],
129
- });
130
-
131
- skyPass.setPipeline(skyPipeline!);
132
- skyPass.setBindGroup(0, skyBindGroup);
133
- skyPass.draw(3);
134
- skyPass.end();
135
- }
136
-
137
- const pass = encoder.beginRenderPass({
138
- colorAttachments: [
139
- {
140
- view: colorView as GPUTextureView,
141
- clearValue: { r: clearColor.r, g: clearColor.g, b: clearColor.b, a: 1 },
142
- loadOp: hasSky ? ("load" as const) : ("clear" as const),
143
- storeOp: "store" as const,
144
- },
145
- {
146
- view: eidView as GPUTextureView,
147
- clearValue: { r: 0, g: 0, b: 0, a: 0 },
148
- loadOp: "clear" as const,
149
- storeOp: "store" as const,
150
- },
151
- ],
152
- depthStencilAttachment: {
153
- view: depthView as GPUTextureView,
154
- depthClearValue: 1.0,
155
- depthLoadOp: hasSky ? ("load" as const) : ("clear" as const),
156
- depthStoreOp: "store" as const,
157
- },
158
- });
159
-
160
- pass.setPipeline(rasterPipeline);
161
-
162
- for (const batch of batches) {
163
- if (!batch || batch.count === 0) continue;
164
-
165
- const batchBindGroup = device.createBindGroup({
166
- layout: rasterPipeline.getBindGroupLayout(0),
167
- entries: [
168
- { binding: 0, resource: { buffer: config.scene } },
169
- { binding: 1, resource: { buffer: batch.entityIds } },
170
- { binding: 2, resource: { buffer: config.matrices } },
171
- { binding: 3, resource: { buffer: config.colors } },
172
- { binding: 4, resource: { buffer: config.sizes } },
173
- { binding: 5, resource: { buffer: config.pbr } },
174
- { binding: 6, resource: { buffer: config.emission } },
175
- { binding: 7, resource: { buffer: config.shapes } },
176
- { binding: 8, resource: { buffer: config.surfaces } },
177
- ],
178
- });
179
-
180
- pass.setBindGroup(0, batchBindGroup);
181
- pass.setVertexBuffer(0, batch.buffers.vertex);
182
- pass.setIndexBuffer(batch.buffers.index, "uint16");
183
- pass.drawIndexed(batch.buffers.indexCount, batch.count);
184
- }
185
-
186
- pass.end();
187
- }
188
-
189
- return {
190
- id: "forward",
191
- inputs: [{ id: "tlas-bvh-nodes", access: "read" as const }],
192
- outputs: [
193
- { id: "color", access: "write" },
194
- { id: "linear-depth", access: "write" },
195
- { id: "eid", access: "write" },
196
- ],
197
-
198
- async prepare(device: GPUDevice) {
199
- const surfaces = config.getSurfaces();
200
- rasterPipeline = await createRasterPipeline(device, surfaces, COLOR_FORMAT);
201
- skyPipeline = await createSkyPipeline(device, COLOR_FORMAT);
202
-
203
- if (config.getRaytracing()) {
204
- rtCompiling = true;
205
- const release = config.acquire?.("compiling shaders");
206
- createRTPipeline(device, surfaces)
207
- .then((p) => {
208
- rtPipeline = p;
209
- })
210
- .finally(() => {
211
- rtCompiling = false;
212
- release?.();
213
- });
214
- }
215
- },
216
-
217
- execute(ctx: ExecutionContext) {
218
- const wantRT = config.getRaytracing();
219
-
220
- if (wantRT && !rtPipeline && !rtCompiling) {
221
- rtCompiling = true;
222
- const release = config.acquire?.("compiling shaders");
223
- const surfaces = config.getSurfaces();
224
- createRTPipeline(ctx.device, surfaces)
225
- .then((p) => {
226
- rtPipeline = p;
227
- })
228
- .finally(() => {
229
- rtCompiling = false;
230
- release?.();
231
- });
232
- }
233
-
234
- if (wantRT && rtPipeline) {
235
- executeRT(ctx);
236
- } else {
237
- executeRaster(ctx);
238
- }
239
- },
240
- };
241
- }
242
-
243
- export function compileRTShader(surfaces: SurfaceData[]): string {
244
- return compileUberShader(surfaces);
245
- }
246
-
247
- export async function createRTPipeline(
248
- device: GPUDevice,
249
- surfaces: SurfaceData[]
250
- ): Promise<GPUComputePipeline> {
251
- const code = compileRTShader(surfaces);
252
- const module = device.createShaderModule({ code });
253
- return device.createComputePipelineAsync({
254
- layout: "auto",
255
- compute: { module, entryPoint: "main" },
256
- });
257
- }
258
-
259
- export { createRasterPipeline, compileRasterShader } from "./raster";