@multiplekex/shallot 0.2.5 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/src/core/component.ts +1 -1
- package/src/core/index.ts +1 -13
- package/src/core/math.ts +186 -0
- package/src/core/state.ts +1 -1
- package/src/core/xml.ts +56 -41
- package/src/extras/orbit/index.ts +1 -1
- package/src/extras/text/index.ts +10 -65
- package/src/extras/{water.ts → water/index.ts} +59 -4
- package/src/standard/raster/batch.ts +149 -0
- package/src/standard/raster/forward.ts +832 -0
- package/src/standard/raster/index.ts +146 -472
- package/src/standard/raster/shadow.ts +408 -0
- package/src/standard/raytracing/bvh/blas.ts +335 -87
- package/src/standard/raytracing/bvh/radix.ts +225 -228
- package/src/standard/raytracing/bvh/refit.ts +711 -0
- package/src/standard/raytracing/bvh/structs.ts +0 -55
- package/src/standard/raytracing/bvh/tlas.ts +153 -141
- package/src/standard/raytracing/bvh/traverse.ts +72 -64
- package/src/standard/raytracing/index.ts +233 -204
- package/src/standard/raytracing/instance.ts +30 -18
- package/src/standard/raytracing/ray.ts +1 -1
- package/src/standard/raytracing/shaders.ts +23 -40
- package/src/standard/render/camera.ts +10 -28
- package/src/standard/render/data.ts +1 -1
- package/src/standard/render/index.ts +68 -12
- package/src/standard/render/light.ts +2 -2
- package/src/standard/render/mesh.ts +404 -0
- package/src/standard/render/overlay.ts +5 -2
- package/src/standard/render/postprocess.ts +263 -267
- package/src/standard/render/surface/index.ts +81 -12
- package/src/standard/render/surface/shaders.ts +265 -11
- package/src/standard/render/surface/structs.ts +10 -0
- package/src/standard/tween/tween.ts +44 -115
- package/src/standard/render/mesh/box.ts +0 -20
- package/src/standard/render/mesh/index.ts +0 -315
- package/src/standard/render/mesh/plane.ts +0 -11
- package/src/standard/render/mesh/sphere.ts +0 -40
- package/src/standard/render/mesh/unified.ts +0 -96
- package/src/standard/render/surface/compile.ts +0 -65
- package/src/standard/render/surface/noise.ts +0 -58
|
@@ -1,473 +1,52 @@
|
|
|
1
|
-
import type { Plugin, State
|
|
2
|
-
import { resource } from "../../core";
|
|
1
|
+
import type { Plugin, State } from "../../core";
|
|
3
2
|
import { Compute } from "../compute";
|
|
4
|
-
import type { ComputeNode
|
|
3
|
+
import type { ComputeNode } from "../compute";
|
|
5
4
|
import { WorldTransform } from "../transforms";
|
|
6
|
-
import { Camera, getClearColor, hasSkyComponent } from "../render/camera";
|
|
7
|
-
import {
|
|
8
|
-
|
|
9
|
-
collectBatches,
|
|
10
|
-
updateBatches,
|
|
11
|
-
MAX_BATCH_SLOTS,
|
|
12
|
-
type Batch,
|
|
13
|
-
type MeshBuffers,
|
|
14
|
-
type BatchEntities,
|
|
15
|
-
} from "../render/mesh";
|
|
5
|
+
import { Camera, Shadows, getClearColor, hasSkyComponent } from "../render/camera";
|
|
6
|
+
import { DirectionalLight, normalizeDirection } from "../render/light";
|
|
7
|
+
import { Mesh, MeshColors } from "../render/mesh";
|
|
16
8
|
import { Surface, SurfaceType, getDefaultAllSurfaces } from "../render/surface";
|
|
17
|
-
import
|
|
18
|
-
import {
|
|
9
|
+
import { Render, RenderPlugin, registerShadowSettings } from "../render";
|
|
10
|
+
import { setTraits } from "../../core/component";
|
|
19
11
|
import {
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
} from "
|
|
26
|
-
import {
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
export
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
indirect: GPUBuffer;
|
|
35
|
-
batches: (Batch | null)[];
|
|
36
|
-
batchEntities: BatchEntities;
|
|
37
|
-
buffers: Map<number, MeshBuffers>;
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
export const RasterResource = resource<RasterState>("raster");
|
|
41
|
-
|
|
42
|
-
const RasterUploadSystem: System = {
|
|
43
|
-
group: "draw",
|
|
44
|
-
|
|
45
|
-
update(state: State) {
|
|
46
|
-
const render = Render.from(state);
|
|
47
|
-
const raster = RasterResource.from(state);
|
|
48
|
-
const compute = Compute.from(state);
|
|
49
|
-
if (!render || !raster || !compute) return;
|
|
50
|
-
|
|
51
|
-
const { device } = compute;
|
|
52
|
-
const meshEntities = state.query([Mesh, WorldTransform]);
|
|
53
|
-
|
|
54
|
-
collectBatches(
|
|
55
|
-
meshEntities,
|
|
56
|
-
(eid) => Surface.type[eid] ?? SurfaceType.Default,
|
|
57
|
-
raster.batchEntities
|
|
58
|
-
);
|
|
59
|
-
updateBatches(device, raster.batchEntities, raster, raster.indirect);
|
|
60
|
-
},
|
|
12
|
+
createShadowAtlas,
|
|
13
|
+
createShadowBuffer,
|
|
14
|
+
createShadowUploadNode,
|
|
15
|
+
createShadowForwardNode,
|
|
16
|
+
} from "./shadow";
|
|
17
|
+
import { createBatch, uploadBatch, type Batch } from "./batch";
|
|
18
|
+
import { createRasterForwardNode } from "./forward";
|
|
19
|
+
|
|
20
|
+
export { compileRasterShader, compileCompositeShader } from "./forward";
|
|
21
|
+
|
|
22
|
+
export const RasterShadowSettings = {
|
|
23
|
+
softness: [] as number[],
|
|
24
|
+
samples: [] as number[],
|
|
25
|
+
distance: [] as number[],
|
|
61
26
|
};
|
|
62
27
|
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
${vertexBody}
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
fn userFragment_${id}(surface: ptr<function, SurfaceData>, position: vec4<f32>) {
|
|
74
|
-
${fragmentBody}
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
fn applyLighting_${id}(surface: SurfaceData) -> vec3<f32> {
|
|
78
|
-
${
|
|
79
|
-
lit
|
|
80
|
-
? `${WGSL_LIGHTING_CALC}
|
|
81
|
-
return surface.baseColor * lighting + surface.emission;`
|
|
82
|
-
: "return surface.baseColor;"
|
|
83
|
-
}
|
|
84
|
-
}
|
|
85
|
-
`;
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
function compileDispatchFunctions(surfaceCount: number): string {
|
|
89
|
-
const vertexCases = Array.from(
|
|
90
|
-
{ length: surfaceCount },
|
|
91
|
-
(_, i) => ` case ${i}u: { return userVertexTransform_${i}(worldPos, normal, eid); }`
|
|
92
|
-
).join("\n");
|
|
93
|
-
|
|
94
|
-
const fragmentCases = Array.from(
|
|
95
|
-
{ length: surfaceCount },
|
|
96
|
-
(_, i) => ` case ${i}u: { userFragment_${i}(surface, position); }`
|
|
97
|
-
).join("\n");
|
|
98
|
-
|
|
99
|
-
const lightingCases = Array.from(
|
|
100
|
-
{ length: surfaceCount },
|
|
101
|
-
(_, i) => ` case ${i}u: { return applyLighting_${i}(surface); }`
|
|
102
|
-
).join("\n");
|
|
103
|
-
|
|
104
|
-
return `
|
|
105
|
-
fn dispatchVertexTransform(surfaceId: u32, worldPos: vec3<f32>, normal: vec3<f32>, eid: u32) -> vec3<f32> {
|
|
106
|
-
switch surfaceId {
|
|
107
|
-
${vertexCases}
|
|
108
|
-
default: { return userVertexTransform_0(worldPos, normal, eid); }
|
|
109
|
-
}
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
fn dispatchFragment(surfaceId: u32, surface: ptr<function, SurfaceData>, position: vec4<f32>) {
|
|
113
|
-
switch surfaceId {
|
|
114
|
-
${fragmentCases}
|
|
115
|
-
default: { userFragment_0(surface, position); }
|
|
116
|
-
}
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
fn dispatchLighting(surfaceId: u32, surface: SurfaceData) -> vec3<f32> {
|
|
120
|
-
switch surfaceId {
|
|
121
|
-
${lightingCases}
|
|
122
|
-
default: { return applyLighting_0(surface); }
|
|
123
|
-
}
|
|
124
|
-
}
|
|
125
|
-
`;
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
function compileRasterShader(surfaces: SurfaceData[]): string {
|
|
129
|
-
const surfaceVariants = surfaces.map((s, i) => compileSurfaceVariant(i, s)).join("\n");
|
|
130
|
-
const dispatchFunctions = compileDispatchFunctions(surfaces.length);
|
|
131
|
-
|
|
132
|
-
return /* wgsl */ `
|
|
133
|
-
${WGSL_STRUCTS}
|
|
134
|
-
${NOISE_WGSL}
|
|
135
|
-
|
|
136
|
-
${surfaceVariants}
|
|
137
|
-
${dispatchFunctions}
|
|
138
|
-
|
|
139
|
-
@vertex
|
|
140
|
-
fn vs(input: VertexInput) -> VertexOutput {
|
|
141
|
-
let eid = entityIds[input.instance];
|
|
142
|
-
let world = matrices[eid];
|
|
143
|
-
let scaledPos = input.position * sizes[eid].xyz;
|
|
144
|
-
let baseWorldPos = (world * vec4<f32>(scaledPos, 1.0)).xyz;
|
|
145
|
-
let worldNormal = normalize((world * vec4<f32>(input.normal, 0.0)).xyz);
|
|
146
|
-
let d = data[eid];
|
|
147
|
-
let surfaceId = d.flags & 0xFFu;
|
|
148
|
-
let finalWorldPos = dispatchVertexTransform(surfaceId, baseWorldPos, worldNormal, eid);
|
|
149
|
-
|
|
150
|
-
var output: VertexOutput;
|
|
151
|
-
output.position = scene.viewProj * vec4<f32>(finalWorldPos, 1.0);
|
|
152
|
-
output.color = d.baseColor;
|
|
153
|
-
output.worldNormal = worldNormal;
|
|
154
|
-
output.entityId = eid;
|
|
155
|
-
output.worldPos = finalWorldPos;
|
|
156
|
-
return output;
|
|
157
|
-
}
|
|
158
|
-
|
|
159
|
-
@fragment
|
|
160
|
-
fn fs(input: VertexOutput) -> FragmentOutput {
|
|
161
|
-
let eid = input.entityId;
|
|
162
|
-
let d = data[eid];
|
|
163
|
-
let surfaceId = d.flags & 0xFFu;
|
|
164
|
-
|
|
165
|
-
var surface: SurfaceData;
|
|
166
|
-
surface.baseColor = input.color.rgb;
|
|
167
|
-
surface.roughness = d.pbr.x;
|
|
168
|
-
surface.metallic = d.pbr.y;
|
|
169
|
-
surface.emission = d.emission.rgb * d.emission.a;
|
|
170
|
-
surface.normal = normalize(input.worldNormal);
|
|
171
|
-
surface.worldPos = input.worldPos;
|
|
172
|
-
|
|
173
|
-
dispatchFragment(surfaceId, &surface, input.position);
|
|
174
|
-
|
|
175
|
-
let litColor = dispatchLighting(surfaceId, surface);
|
|
176
|
-
|
|
177
|
-
var output: FragmentOutput;
|
|
178
|
-
output.color = vec4<f32>(litColor, input.color.a);
|
|
179
|
-
output.entityId = input.entityId;
|
|
180
|
-
return output;
|
|
181
|
-
}
|
|
182
|
-
`;
|
|
183
|
-
}
|
|
184
|
-
|
|
185
|
-
async function createRasterPipeline(
|
|
186
|
-
device: GPUDevice,
|
|
187
|
-
surfaces: SurfaceData[],
|
|
188
|
-
colorFormat: GPUTextureFormat
|
|
189
|
-
): Promise<GPURenderPipeline> {
|
|
190
|
-
const code = compileRasterShader(surfaces);
|
|
191
|
-
const module = device.createShaderModule({ code });
|
|
192
|
-
|
|
193
|
-
return device.createRenderPipelineAsync({
|
|
194
|
-
layout: "auto",
|
|
195
|
-
vertex: {
|
|
196
|
-
module,
|
|
197
|
-
entryPoint: "vs",
|
|
198
|
-
buffers: [
|
|
199
|
-
{
|
|
200
|
-
arrayStride: 24,
|
|
201
|
-
attributes: [
|
|
202
|
-
{ shaderLocation: 0, offset: 0, format: "float32x3" },
|
|
203
|
-
{ shaderLocation: 1, offset: 12, format: "float32x3" },
|
|
204
|
-
],
|
|
205
|
-
},
|
|
206
|
-
],
|
|
207
|
-
},
|
|
208
|
-
fragment: {
|
|
209
|
-
module,
|
|
210
|
-
entryPoint: "fs",
|
|
211
|
-
targets: [{ format: colorFormat }, { format: "r32uint" }],
|
|
212
|
-
},
|
|
213
|
-
depthStencil: {
|
|
214
|
-
format: "depth24plus",
|
|
215
|
-
depthWriteEnabled: true,
|
|
216
|
-
depthCompare: "less",
|
|
217
|
-
},
|
|
218
|
-
primitive: {
|
|
219
|
-
topology: "triangle-list",
|
|
220
|
-
cullMode: "back",
|
|
221
|
-
},
|
|
222
|
-
});
|
|
223
|
-
}
|
|
224
|
-
|
|
225
|
-
function compileSkyShader(): string {
|
|
226
|
-
return /* wgsl */ `
|
|
227
|
-
${SCENE_STRUCT_WGSL}
|
|
228
|
-
${SKY_STRUCT_WGSL}
|
|
229
|
-
|
|
230
|
-
@group(0) @binding(0) var<uniform> scene: Scene;
|
|
231
|
-
@group(0) @binding(1) var<uniform> sky: Sky;
|
|
232
|
-
|
|
233
|
-
${SKY_DIR_WGSL}
|
|
234
|
-
${NOISE_WGSL}
|
|
235
|
-
${SKY_WGSL}
|
|
236
|
-
|
|
237
|
-
struct VertexOutput {
|
|
238
|
-
@builtin(position) position: vec4<f32>,
|
|
239
|
-
@location(0) uv: vec2<f32>,
|
|
240
|
-
}
|
|
241
|
-
|
|
242
|
-
@vertex
|
|
243
|
-
fn vs(@builtin(vertex_index) vertexIndex: u32) -> VertexOutput {
|
|
244
|
-
var positions = array<vec2<f32>, 3>(
|
|
245
|
-
vec2(-1.0, -1.0),
|
|
246
|
-
vec2(3.0, -1.0),
|
|
247
|
-
vec2(-1.0, 3.0)
|
|
248
|
-
);
|
|
249
|
-
var output: VertexOutput;
|
|
250
|
-
output.position = vec4(positions[vertexIndex], 0.0, 1.0);
|
|
251
|
-
output.uv = (positions[vertexIndex] + 1.0) * 0.5;
|
|
252
|
-
output.uv.y = 1.0 - output.uv.y;
|
|
253
|
-
return output;
|
|
254
|
-
}
|
|
255
|
-
|
|
256
|
-
@fragment
|
|
257
|
-
fn fs(input: VertexOutput) -> @location(0) vec4<f32> {
|
|
258
|
-
let dir = computeSkyDir(input.uv.x, input.uv.y);
|
|
259
|
-
let color = sampleSky(dir);
|
|
260
|
-
return vec4(color, 1.0);
|
|
261
|
-
}
|
|
262
|
-
`;
|
|
263
|
-
}
|
|
264
|
-
|
|
265
|
-
async function createSkyPipeline(
|
|
266
|
-
device: GPUDevice,
|
|
267
|
-
colorFormat: GPUTextureFormat
|
|
268
|
-
): Promise<GPURenderPipeline> {
|
|
269
|
-
const code = compileSkyShader();
|
|
270
|
-
const module = device.createShaderModule({ code });
|
|
271
|
-
|
|
272
|
-
return device.createRenderPipelineAsync({
|
|
273
|
-
layout: "auto",
|
|
274
|
-
vertex: { module, entryPoint: "vs" },
|
|
275
|
-
fragment: {
|
|
276
|
-
module,
|
|
277
|
-
entryPoint: "fs",
|
|
278
|
-
targets: [{ format: colorFormat }],
|
|
279
|
-
},
|
|
280
|
-
depthStencil: {
|
|
281
|
-
format: "depth24plus",
|
|
282
|
-
depthWriteEnabled: false,
|
|
283
|
-
depthCompare: "always",
|
|
284
|
-
},
|
|
285
|
-
primitive: { topology: "triangle-list" },
|
|
286
|
-
});
|
|
287
|
-
}
|
|
288
|
-
|
|
289
|
-
interface RasterPassConfig {
|
|
290
|
-
scene: GPUBuffer;
|
|
291
|
-
sky: GPUBuffer;
|
|
292
|
-
data: GPUBuffer;
|
|
293
|
-
matrices: GPUBuffer;
|
|
294
|
-
sizes: GPUBuffer;
|
|
295
|
-
indirect: GPUBuffer;
|
|
296
|
-
rasterPipeline: GPURenderPipeline;
|
|
297
|
-
skyPipeline: GPURenderPipeline | null;
|
|
298
|
-
getClearColor: () => { r: number; g: number; b: number };
|
|
299
|
-
getSky: () => boolean;
|
|
300
|
-
batches: () => (Batch | null)[];
|
|
301
|
-
skyBindGroup?: GPUBindGroup | null;
|
|
302
|
-
batchBindGroups?: GPUBindGroup[];
|
|
303
|
-
}
|
|
304
|
-
|
|
305
|
-
function executeRasterPass(ctx: ExecutionContext, config: RasterPassConfig): void {
|
|
306
|
-
const { device, encoder } = ctx;
|
|
307
|
-
|
|
308
|
-
const colorView = ctx.getTextureView("color");
|
|
309
|
-
const eidView = ctx.getTextureView("eid");
|
|
310
|
-
const zView = ctx.getTextureView("z");
|
|
311
|
-
|
|
312
|
-
if (!colorView || !eidView || !zView) {
|
|
313
|
-
return;
|
|
314
|
-
}
|
|
315
|
-
|
|
316
|
-
const batches = config.batches();
|
|
317
|
-
const colorTexture = ctx.getTexture("color");
|
|
318
|
-
if (!colorTexture) return;
|
|
28
|
+
setTraits(RasterShadowSettings, {
|
|
29
|
+
defaults: () => ({
|
|
30
|
+
softness: 0,
|
|
31
|
+
samples: 1,
|
|
32
|
+
distance: 100,
|
|
33
|
+
}),
|
|
34
|
+
});
|
|
319
35
|
|
|
320
|
-
|
|
321
|
-
const hasSky = config.getSky() && config.skyPipeline;
|
|
322
|
-
|
|
323
|
-
if (hasSky) {
|
|
324
|
-
const skyPass = encoder.beginRenderPass({
|
|
325
|
-
colorAttachments: [
|
|
326
|
-
{
|
|
327
|
-
view: colorView as GPUTextureView,
|
|
328
|
-
clearValue: { r: clearColor.r, g: clearColor.g, b: clearColor.b, a: 1 },
|
|
329
|
-
loadOp: "clear" as const,
|
|
330
|
-
storeOp: "store" as const,
|
|
331
|
-
},
|
|
332
|
-
],
|
|
333
|
-
depthStencilAttachment: {
|
|
334
|
-
view: zView as GPUTextureView,
|
|
335
|
-
depthClearValue: 1.0,
|
|
336
|
-
depthLoadOp: "clear" as const,
|
|
337
|
-
depthStoreOp: "store" as const,
|
|
338
|
-
},
|
|
339
|
-
});
|
|
340
|
-
|
|
341
|
-
if (!config.skyBindGroup) {
|
|
342
|
-
config.skyBindGroup = device.createBindGroup({
|
|
343
|
-
layout: config.skyPipeline!.getBindGroupLayout(0),
|
|
344
|
-
entries: [
|
|
345
|
-
{ binding: 0, resource: { buffer: config.scene } },
|
|
346
|
-
{ binding: 1, resource: { buffer: config.sky } },
|
|
347
|
-
],
|
|
348
|
-
});
|
|
349
|
-
}
|
|
350
|
-
|
|
351
|
-
skyPass.setPipeline(config.skyPipeline!);
|
|
352
|
-
skyPass.setBindGroup(0, config.skyBindGroup);
|
|
353
|
-
skyPass.draw(3);
|
|
354
|
-
skyPass.end();
|
|
355
|
-
}
|
|
356
|
-
|
|
357
|
-
const pass = encoder.beginRenderPass({
|
|
358
|
-
colorAttachments: [
|
|
359
|
-
{
|
|
360
|
-
view: colorView as GPUTextureView,
|
|
361
|
-
clearValue: { r: clearColor.r, g: clearColor.g, b: clearColor.b, a: 1 },
|
|
362
|
-
loadOp: hasSky ? ("load" as const) : ("clear" as const),
|
|
363
|
-
storeOp: "store" as const,
|
|
364
|
-
},
|
|
365
|
-
{
|
|
366
|
-
view: eidView as GPUTextureView,
|
|
367
|
-
clearValue: { r: 0, g: 0, b: 0, a: 0 },
|
|
368
|
-
loadOp: "clear" as const,
|
|
369
|
-
storeOp: "store" as const,
|
|
370
|
-
},
|
|
371
|
-
],
|
|
372
|
-
depthStencilAttachment: {
|
|
373
|
-
view: zView as GPUTextureView,
|
|
374
|
-
depthClearValue: 1.0,
|
|
375
|
-
depthLoadOp: hasSky ? ("load" as const) : ("clear" as const),
|
|
376
|
-
depthStoreOp: "store" as const,
|
|
377
|
-
},
|
|
378
|
-
});
|
|
379
|
-
|
|
380
|
-
pass.setPipeline(config.rasterPipeline);
|
|
381
|
-
|
|
382
|
-
if (!config.batchBindGroups) {
|
|
383
|
-
config.batchBindGroups = [];
|
|
384
|
-
}
|
|
385
|
-
|
|
386
|
-
for (let i = 0; i < batches.length; i++) {
|
|
387
|
-
const batch = batches[i];
|
|
388
|
-
if (!batch || batch.count === 0) continue;
|
|
389
|
-
|
|
390
|
-
if (!config.batchBindGroups[i]) {
|
|
391
|
-
config.batchBindGroups[i] = device.createBindGroup({
|
|
392
|
-
layout: config.rasterPipeline.getBindGroupLayout(0),
|
|
393
|
-
entries: [
|
|
394
|
-
{ binding: 0, resource: { buffer: config.scene } },
|
|
395
|
-
{ binding: 1, resource: { buffer: batch.entityIds } },
|
|
396
|
-
{ binding: 2, resource: { buffer: config.matrices } },
|
|
397
|
-
{ binding: 3, resource: { buffer: config.sizes } },
|
|
398
|
-
{ binding: 4, resource: { buffer: config.data } },
|
|
399
|
-
],
|
|
400
|
-
});
|
|
401
|
-
}
|
|
402
|
-
|
|
403
|
-
pass.setBindGroup(0, config.batchBindGroups[i]);
|
|
404
|
-
pass.setVertexBuffer(0, batch.buffers.vertex);
|
|
405
|
-
pass.setIndexBuffer(batch.buffers.index, "uint16");
|
|
406
|
-
pass.drawIndexedIndirect(config.indirect, i * INDIRECT_SIZE);
|
|
407
|
-
}
|
|
408
|
-
|
|
409
|
-
pass.end();
|
|
410
|
-
}
|
|
411
|
-
|
|
412
|
-
interface RasterForwardConfig {
|
|
413
|
-
scene: GPUBuffer;
|
|
414
|
-
sky: GPUBuffer;
|
|
415
|
-
data: GPUBuffer;
|
|
416
|
-
indirect: GPUBuffer;
|
|
417
|
-
matrices: GPUBuffer;
|
|
418
|
-
sizes: GPUBuffer;
|
|
419
|
-
getSurfaces: () => SurfaceData[];
|
|
420
|
-
getClearColor: () => { r: number; g: number; b: number };
|
|
421
|
-
getSky: () => boolean;
|
|
422
|
-
batches: () => (Batch | null)[];
|
|
423
|
-
}
|
|
424
|
-
|
|
425
|
-
function createRasterForwardNode(config: RasterForwardConfig): ComputeNode {
|
|
426
|
-
let rasterPipeline: GPURenderPipeline | null = null;
|
|
427
|
-
let skyPipeline: GPURenderPipeline | null = null;
|
|
428
|
-
let skyBindGroup: GPUBindGroup | null = null;
|
|
429
|
-
let batchBindGroups: GPUBindGroup[] = [];
|
|
430
|
-
|
|
431
|
-
return {
|
|
432
|
-
id: "forward",
|
|
433
|
-
inputs: [{ id: "data", access: "read" }],
|
|
434
|
-
outputs: [
|
|
435
|
-
{ id: "color", access: "write" },
|
|
436
|
-
{ id: "eid", access: "write" },
|
|
437
|
-
],
|
|
438
|
-
|
|
439
|
-
async prepare(device: GPUDevice) {
|
|
440
|
-
const surfaces = config.getSurfaces();
|
|
441
|
-
rasterPipeline = await createRasterPipeline(device, surfaces, COLOR_FORMAT);
|
|
442
|
-
skyPipeline = await createSkyPipeline(device, COLOR_FORMAT);
|
|
443
|
-
},
|
|
36
|
+
import { resource } from "../../core";
|
|
444
37
|
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
matrices: config.matrices,
|
|
452
|
-
sizes: config.sizes,
|
|
453
|
-
indirect: config.indirect,
|
|
454
|
-
rasterPipeline,
|
|
455
|
-
skyPipeline,
|
|
456
|
-
getClearColor: config.getClearColor,
|
|
457
|
-
getSky: config.getSky,
|
|
458
|
-
batches: config.batches,
|
|
459
|
-
skyBindGroup,
|
|
460
|
-
batchBindGroups,
|
|
461
|
-
};
|
|
462
|
-
executeRasterPass(ctx, passConfig);
|
|
463
|
-
skyBindGroup = passConfig.skyBindGroup ?? null;
|
|
464
|
-
batchBindGroups = passConfig.batchBindGroups ?? [];
|
|
465
|
-
},
|
|
466
|
-
};
|
|
467
|
-
}
|
|
38
|
+
export const RasterResource = resource<{
|
|
39
|
+
batching: Batch;
|
|
40
|
+
shadowAtlas: GPUTexture;
|
|
41
|
+
shadowBuffer: GPUBuffer;
|
|
42
|
+
rtRendered: boolean;
|
|
43
|
+
}>("raster");
|
|
468
44
|
|
|
469
45
|
export const RasterPlugin: Plugin = {
|
|
470
|
-
systems: [
|
|
46
|
+
systems: [],
|
|
47
|
+
components: {
|
|
48
|
+
RasterShadowSettings,
|
|
49
|
+
},
|
|
471
50
|
dependencies: [RenderPlugin],
|
|
472
51
|
|
|
473
52
|
async initialize(state: State) {
|
|
@@ -475,42 +54,137 @@ export const RasterPlugin: Plugin = {
|
|
|
475
54
|
const render = Render.from(state);
|
|
476
55
|
if (!compute || !render) return;
|
|
477
56
|
|
|
57
|
+
registerShadowSettings({
|
|
58
|
+
softness: RasterShadowSettings.softness,
|
|
59
|
+
samples: RasterShadowSettings.samples,
|
|
60
|
+
isActive: (s, eid) => s.hasComponent(eid, RasterShadowSettings),
|
|
61
|
+
});
|
|
62
|
+
|
|
478
63
|
const { device } = compute;
|
|
479
64
|
|
|
480
|
-
const
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
65
|
+
const shadowAtlas = createShadowAtlas(device);
|
|
66
|
+
const shadowBuffer = createShadowBuffer(device);
|
|
67
|
+
const batching = createBatch(device);
|
|
68
|
+
|
|
69
|
+
const rasterState = {
|
|
70
|
+
batching,
|
|
71
|
+
shadowAtlas,
|
|
72
|
+
shadowBuffer,
|
|
73
|
+
rtRendered: false,
|
|
485
74
|
};
|
|
486
75
|
|
|
487
76
|
state.setResource(RasterResource, rasterState);
|
|
488
77
|
|
|
489
|
-
const
|
|
78
|
+
const getActiveCamera = () => {
|
|
490
79
|
for (const eid of state.query([Camera])) {
|
|
491
|
-
if (Camera.active[eid]) return
|
|
80
|
+
if (Camera.active[eid]) return eid;
|
|
492
81
|
}
|
|
82
|
+
return -1;
|
|
83
|
+
};
|
|
84
|
+
|
|
85
|
+
const getActiveClearColor = () => {
|
|
86
|
+
const eid = getActiveCamera();
|
|
87
|
+
if (eid >= 0) return getClearColor(eid);
|
|
493
88
|
return { r: 0, g: 0, b: 0 };
|
|
494
89
|
};
|
|
495
90
|
|
|
496
91
|
const getSky = () => {
|
|
497
|
-
|
|
498
|
-
|
|
92
|
+
const eid = getActiveCamera();
|
|
93
|
+
return eid >= 0 && hasSkyComponent(state, eid);
|
|
94
|
+
};
|
|
95
|
+
|
|
96
|
+
const getShadowsEnabled = () => {
|
|
97
|
+
const eid = getActiveCamera();
|
|
98
|
+
return eid >= 0 && state.hasComponent(eid, Shadows);
|
|
99
|
+
};
|
|
100
|
+
|
|
101
|
+
const getShadowDistance = () => {
|
|
102
|
+
const eid = getActiveCamera();
|
|
103
|
+
if (eid >= 0 && state.hasComponent(eid, RasterShadowSettings)) {
|
|
104
|
+
return RasterShadowSettings.distance[eid];
|
|
105
|
+
}
|
|
106
|
+
return 100;
|
|
107
|
+
};
|
|
108
|
+
|
|
109
|
+
const getCameraData = () => {
|
|
110
|
+
const eid = getActiveCamera();
|
|
111
|
+
if (eid >= 0) {
|
|
112
|
+
return {
|
|
113
|
+
world: WorldTransform.data.subarray(eid * 16, eid * 16 + 16),
|
|
114
|
+
fov: Camera.fov[eid],
|
|
115
|
+
near: Camera.near[eid],
|
|
116
|
+
far: Camera.far[eid],
|
|
117
|
+
width: render.width,
|
|
118
|
+
height: render.height,
|
|
119
|
+
};
|
|
499
120
|
}
|
|
500
|
-
return
|
|
121
|
+
return null;
|
|
501
122
|
};
|
|
502
123
|
|
|
124
|
+
const getLightDir = (): [number, number, number] => {
|
|
125
|
+
for (const eid of state.query([DirectionalLight])) {
|
|
126
|
+
const [dx, dy, dz] = normalizeDirection(
|
|
127
|
+
DirectionalLight.directionX[eid],
|
|
128
|
+
DirectionalLight.directionY[eid],
|
|
129
|
+
DirectionalLight.directionZ[eid]
|
|
130
|
+
);
|
|
131
|
+
return [dx, dy, dz];
|
|
132
|
+
}
|
|
133
|
+
return [-0.5, -1.0, -0.5];
|
|
134
|
+
};
|
|
135
|
+
|
|
136
|
+
const getOpacity = (eid: number) => MeshColors.data[eid * 4 + 3];
|
|
137
|
+
|
|
138
|
+
const batchUploadNode: ComputeNode = {
|
|
139
|
+
id: "batch-upload",
|
|
140
|
+
inputs: [{ id: "data", access: "read" }],
|
|
141
|
+
outputs: [{ id: "batched", access: "write" }],
|
|
142
|
+
execute() {
|
|
143
|
+
const meshEntities = state.query([Mesh, WorldTransform]);
|
|
144
|
+
uploadBatch(
|
|
145
|
+
device,
|
|
146
|
+
meshEntities,
|
|
147
|
+
(eid) => Surface.type[eid] ?? SurfaceType.Default,
|
|
148
|
+
getOpacity,
|
|
149
|
+
rasterState.batching
|
|
150
|
+
);
|
|
151
|
+
},
|
|
152
|
+
};
|
|
153
|
+
compute.graph.add(batchUploadNode);
|
|
154
|
+
|
|
155
|
+
const shadowUploadNode = createShadowUploadNode({
|
|
156
|
+
shadow: shadowBuffer,
|
|
157
|
+
getCameraData,
|
|
158
|
+
getLightDir,
|
|
159
|
+
shadowsEnabled: getShadowsEnabled,
|
|
160
|
+
getShadowDistance,
|
|
161
|
+
});
|
|
162
|
+
compute.graph.add(shadowUploadNode);
|
|
163
|
+
|
|
164
|
+
const shadowNodes = createShadowForwardNode({
|
|
165
|
+
shadow: shadowBuffer,
|
|
166
|
+
matrices: render.matrices,
|
|
167
|
+
sizes: render.sizes,
|
|
168
|
+
batching: rasterState.batching,
|
|
169
|
+
atlas: shadowAtlas,
|
|
170
|
+
shadowsEnabled: getShadowsEnabled,
|
|
171
|
+
});
|
|
172
|
+
for (const node of shadowNodes) compute.graph.add(node);
|
|
173
|
+
|
|
503
174
|
const forwardNode = createRasterForwardNode({
|
|
504
175
|
scene: render.scene,
|
|
505
176
|
sky: render.sky,
|
|
506
177
|
data: render.data,
|
|
507
|
-
indirect: rasterState.indirect,
|
|
508
178
|
matrices: render.matrices,
|
|
509
179
|
sizes: render.sizes,
|
|
180
|
+
batching: rasterState.batching,
|
|
181
|
+
shadowBuffer,
|
|
182
|
+
shadowAtlas,
|
|
510
183
|
getSurfaces: getDefaultAllSurfaces,
|
|
511
184
|
getClearColor: getActiveClearColor,
|
|
512
185
|
getSky,
|
|
513
|
-
|
|
186
|
+
getShadowsEnabled,
|
|
187
|
+
getRTRendered: () => rasterState.rtRendered,
|
|
514
188
|
});
|
|
515
189
|
compute.graph.add(forwardNode);
|
|
516
190
|
},
|