@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
|
@@ -241,22 +241,267 @@ export interface PostProcessConfig {
|
|
|
241
241
|
getRenderSize?: () => { width: number; height: number };
|
|
242
242
|
}
|
|
243
243
|
|
|
244
|
+
interface PostProcessGPU {
|
|
245
|
+
pipeline: GPURenderPipeline;
|
|
246
|
+
blitPipeline: GPURenderPipeline;
|
|
247
|
+
uniformBuffer: GPUBuffer;
|
|
248
|
+
uniformData: ArrayBuffer;
|
|
249
|
+
uniformFloats: Float32Array;
|
|
250
|
+
uniformUints: Uint32Array;
|
|
251
|
+
linearSampler: GPUSampler;
|
|
252
|
+
nearestSampler: GPUSampler;
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
interface PostProcessCache {
|
|
256
|
+
mainBindGroup: GPUBindGroup | null;
|
|
257
|
+
blitBindGroup: GPUBindGroup | null;
|
|
258
|
+
inputView: GPUTextureView | null;
|
|
259
|
+
sampler: GPUSampler | null;
|
|
260
|
+
maskView: GPUTextureView | null;
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
async function preparePostProcess(device: GPUDevice): Promise<PostProcessGPU> {
|
|
264
|
+
const format: GPUTextureFormat = "bgra8unorm";
|
|
265
|
+
|
|
266
|
+
const [mainModule, blitModule] = await Promise.all([
|
|
267
|
+
device.createShaderModule({ code: shader }),
|
|
268
|
+
device.createShaderModule({ code: blitShader }),
|
|
269
|
+
]);
|
|
270
|
+
|
|
271
|
+
const [pipeline, blitPipeline] = await Promise.all([
|
|
272
|
+
device.createRenderPipelineAsync({
|
|
273
|
+
layout: "auto",
|
|
274
|
+
vertex: { module: mainModule, entryPoint: "vertexMain" },
|
|
275
|
+
fragment: {
|
|
276
|
+
module: mainModule,
|
|
277
|
+
entryPoint: "fragmentMain",
|
|
278
|
+
targets: [{ format }],
|
|
279
|
+
},
|
|
280
|
+
primitive: { topology: "triangle-list" },
|
|
281
|
+
}),
|
|
282
|
+
device.createRenderPipelineAsync({
|
|
283
|
+
layout: "auto",
|
|
284
|
+
vertex: { module: blitModule, entryPoint: "vertexMain" },
|
|
285
|
+
fragment: {
|
|
286
|
+
module: blitModule,
|
|
287
|
+
entryPoint: "fragmentMain",
|
|
288
|
+
targets: [{ format }],
|
|
289
|
+
},
|
|
290
|
+
primitive: { topology: "triangle-list" },
|
|
291
|
+
}),
|
|
292
|
+
]);
|
|
293
|
+
|
|
294
|
+
const uniformData = new ArrayBuffer(48);
|
|
295
|
+
|
|
296
|
+
return {
|
|
297
|
+
pipeline,
|
|
298
|
+
blitPipeline,
|
|
299
|
+
uniformBuffer: device.createBuffer({
|
|
300
|
+
size: 48,
|
|
301
|
+
usage: GPUBufferUsage.UNIFORM | GPUBufferUsage.COPY_DST,
|
|
302
|
+
}),
|
|
303
|
+
uniformData,
|
|
304
|
+
uniformFloats: new Float32Array(uniformData),
|
|
305
|
+
uniformUints: new Uint32Array(uniformData),
|
|
306
|
+
linearSampler: device.createSampler({ magFilter: "linear", minFilter: "linear" }),
|
|
307
|
+
nearestSampler: device.createSampler({ magFilter: "nearest", minFilter: "nearest" }),
|
|
308
|
+
};
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
function blitToCanvas(
|
|
312
|
+
encoder: GPUCommandEncoder,
|
|
313
|
+
pipeline: GPURenderPipeline,
|
|
314
|
+
bindGroup: GPUBindGroup,
|
|
315
|
+
canvasView: GPUTextureView
|
|
316
|
+
): void {
|
|
317
|
+
const pass = encoder.beginRenderPass({
|
|
318
|
+
colorAttachments: [
|
|
319
|
+
{
|
|
320
|
+
view: canvasView,
|
|
321
|
+
loadOp: "clear" as const,
|
|
322
|
+
storeOp: "store" as const,
|
|
323
|
+
clearValue: { r: 0, g: 0, b: 0, a: 1 },
|
|
324
|
+
},
|
|
325
|
+
],
|
|
326
|
+
});
|
|
327
|
+
pass.setPipeline(pipeline);
|
|
328
|
+
pass.setBindGroup(0, bindGroup);
|
|
329
|
+
pass.draw(3);
|
|
330
|
+
pass.end();
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
function executeContributors(
|
|
334
|
+
contributors: { execute: (ctx: DrawContext) => void }[],
|
|
335
|
+
baseCtx: Omit<DrawContext, "inputView" | "outputView">,
|
|
336
|
+
pingAView: GPUTextureView,
|
|
337
|
+
pingBView: GPUTextureView,
|
|
338
|
+
currentInput: GPUTextureView,
|
|
339
|
+
pingPong: boolean
|
|
340
|
+
): { currentInput: GPUTextureView; pingPong: boolean } {
|
|
341
|
+
for (const contributor of contributors) {
|
|
342
|
+
const currentOutput = pingPong ? pingAView : pingBView;
|
|
343
|
+
contributor.execute({
|
|
344
|
+
...baseCtx,
|
|
345
|
+
inputView: currentInput,
|
|
346
|
+
outputView: currentOutput,
|
|
347
|
+
});
|
|
348
|
+
currentInput = currentOutput;
|
|
349
|
+
pingPong = !pingPong;
|
|
350
|
+
}
|
|
351
|
+
return { currentInput, pingPong };
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
function ensureBlitBindGroup(
|
|
355
|
+
device: GPUDevice,
|
|
356
|
+
gpu: PostProcessGPU,
|
|
357
|
+
cache: PostProcessCache,
|
|
358
|
+
inputView: GPUTextureView,
|
|
359
|
+
sampler: GPUSampler
|
|
360
|
+
): GPUBindGroup {
|
|
361
|
+
if (inputView !== cache.inputView || sampler !== cache.sampler) {
|
|
362
|
+
cache.blitBindGroup = device.createBindGroup({
|
|
363
|
+
layout: gpu.blitPipeline.getBindGroupLayout(0),
|
|
364
|
+
entries: [
|
|
365
|
+
{ binding: 0, resource: inputView },
|
|
366
|
+
{ binding: 1, resource: sampler },
|
|
367
|
+
],
|
|
368
|
+
});
|
|
369
|
+
cache.inputView = inputView;
|
|
370
|
+
cache.sampler = sampler;
|
|
371
|
+
}
|
|
372
|
+
return cache.blitBindGroup!;
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
function executePostProcess(
|
|
376
|
+
gpu: PostProcessGPU,
|
|
377
|
+
cache: PostProcessCache,
|
|
378
|
+
ctx: ExecutionContext,
|
|
379
|
+
config: PostProcessConfig
|
|
380
|
+
): void {
|
|
381
|
+
const { device, encoder, canvasView, format, context } = ctx;
|
|
382
|
+
const width = context.canvas.width;
|
|
383
|
+
const height = context.canvas.height;
|
|
384
|
+
const colorView = ctx.getTextureView("color")!;
|
|
385
|
+
const maskView = ctx.getTextureView("mask")!;
|
|
386
|
+
const zView = ctx.getTextureView("z")!;
|
|
387
|
+
const eidView = ctx.getTextureView("eid")!;
|
|
388
|
+
const pingAView = ctx.getTextureView("pingA")!;
|
|
389
|
+
const pingBView = ctx.getTextureView("pingB")!;
|
|
390
|
+
|
|
391
|
+
const renderSize = config.getRenderSize?.();
|
|
392
|
+
const isUpscaling = renderSize && (renderSize.width !== width || renderSize.height !== height);
|
|
393
|
+
const sampler = isUpscaling ? gpu.nearestSampler : gpu.linearSampler;
|
|
394
|
+
|
|
395
|
+
let currentInput = colorView;
|
|
396
|
+
let pingPong = false;
|
|
397
|
+
|
|
398
|
+
const baseCtx = {
|
|
399
|
+
device,
|
|
400
|
+
encoder,
|
|
401
|
+
format,
|
|
402
|
+
width,
|
|
403
|
+
height,
|
|
404
|
+
sceneView: colorView,
|
|
405
|
+
zView,
|
|
406
|
+
entityIdView: eidView,
|
|
407
|
+
maskView,
|
|
408
|
+
canvasView,
|
|
409
|
+
};
|
|
410
|
+
|
|
411
|
+
const beforePostProcess = getDrawsByPass(config.state, Pass.BeforePost);
|
|
412
|
+
({ currentInput, pingPong } = executeContributors(
|
|
413
|
+
beforePostProcess,
|
|
414
|
+
baseCtx,
|
|
415
|
+
pingAView,
|
|
416
|
+
pingBView,
|
|
417
|
+
currentInput,
|
|
418
|
+
pingPong
|
|
419
|
+
));
|
|
420
|
+
|
|
421
|
+
const postProcessContributors = getDrawsByPass(config.state, Pass.Post);
|
|
422
|
+
const hasBuiltinEffects =
|
|
423
|
+
config.uniforms.tonemap ||
|
|
424
|
+
config.uniforms.fxaa ||
|
|
425
|
+
config.uniforms.vignetteStrength > 0 ||
|
|
426
|
+
config.uniforms.bloomIntensity > 0 ||
|
|
427
|
+
config.uniforms.quantize > 0;
|
|
428
|
+
|
|
429
|
+
if (hasBuiltinEffects || postProcessContributors.length > 0) {
|
|
430
|
+
({ currentInput, pingPong } = executeContributors(
|
|
431
|
+
postProcessContributors,
|
|
432
|
+
baseCtx,
|
|
433
|
+
pingAView,
|
|
434
|
+
pingBView,
|
|
435
|
+
currentInput,
|
|
436
|
+
pingPong
|
|
437
|
+
));
|
|
438
|
+
|
|
439
|
+
if (hasBuiltinEffects) {
|
|
440
|
+
let flags = 0;
|
|
441
|
+
if (config.uniforms.tonemap) flags |= FLAG_TONEMAP;
|
|
442
|
+
if (config.uniforms.fxaa) flags |= FLAG_FXAA;
|
|
443
|
+
if (config.uniforms.vignetteStrength > 0) flags |= FLAG_VIGNETTE;
|
|
444
|
+
if (config.uniforms.bloomIntensity > 0) flags |= FLAG_BLOOM;
|
|
445
|
+
if (config.uniforms.quantize > 0) flags |= FLAG_QUANTIZE;
|
|
446
|
+
|
|
447
|
+
gpu.uniformFloats[0] = config.uniforms.exposure;
|
|
448
|
+
gpu.uniformFloats[1] = config.uniforms.vignetteStrength;
|
|
449
|
+
gpu.uniformFloats[2] = config.uniforms.vignetteInner;
|
|
450
|
+
gpu.uniformFloats[3] = config.uniforms.vignetteOuter;
|
|
451
|
+
gpu.uniformFloats[4] = 1.0 / width;
|
|
452
|
+
gpu.uniformFloats[5] = 1.0 / height;
|
|
453
|
+
gpu.uniformUints[6] = flags;
|
|
454
|
+
gpu.uniformFloats[7] = config.uniforms.bloomIntensity;
|
|
455
|
+
gpu.uniformFloats[8] = config.uniforms.bloomThreshold;
|
|
456
|
+
gpu.uniformFloats[9] = config.uniforms.bloomRadius;
|
|
457
|
+
gpu.uniformFloats[10] = config.uniforms.quantize;
|
|
458
|
+
|
|
459
|
+
device.queue.writeBuffer(gpu.uniformBuffer, 0, gpu.uniformData);
|
|
460
|
+
|
|
461
|
+
if (
|
|
462
|
+
currentInput !== cache.inputView ||
|
|
463
|
+
sampler !== cache.sampler ||
|
|
464
|
+
maskView !== cache.maskView
|
|
465
|
+
) {
|
|
466
|
+
cache.mainBindGroup = device.createBindGroup({
|
|
467
|
+
layout: gpu.pipeline.getBindGroupLayout(0),
|
|
468
|
+
entries: [
|
|
469
|
+
{ binding: 0, resource: currentInput },
|
|
470
|
+
{ binding: 1, resource: sampler },
|
|
471
|
+
{ binding: 2, resource: { buffer: gpu.uniformBuffer } },
|
|
472
|
+
{ binding: 3, resource: maskView },
|
|
473
|
+
],
|
|
474
|
+
});
|
|
475
|
+
cache.inputView = currentInput;
|
|
476
|
+
cache.sampler = sampler;
|
|
477
|
+
cache.maskView = maskView;
|
|
478
|
+
}
|
|
479
|
+
|
|
480
|
+
blitToCanvas(encoder, gpu.pipeline, cache.mainBindGroup!, canvasView);
|
|
481
|
+
} else {
|
|
482
|
+
const bindGroup = ensureBlitBindGroup(device, gpu, cache, currentInput, sampler);
|
|
483
|
+
blitToCanvas(encoder, gpu.blitPipeline, bindGroup, canvasView);
|
|
484
|
+
}
|
|
485
|
+
} else {
|
|
486
|
+
const bindGroup = ensureBlitBindGroup(device, gpu, cache, currentInput, sampler);
|
|
487
|
+
blitToCanvas(encoder, gpu.blitPipeline, bindGroup, canvasView);
|
|
488
|
+
}
|
|
489
|
+
|
|
490
|
+
const afterPostProcess = getDrawsByPass(config.state, Pass.AfterPost);
|
|
491
|
+
for (const contributor of afterPostProcess) {
|
|
492
|
+
contributor.execute(baseCtx);
|
|
493
|
+
}
|
|
494
|
+
}
|
|
495
|
+
|
|
244
496
|
export function createPostProcessNode(config: PostProcessConfig): ComputeNode {
|
|
245
|
-
let
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
let uniformUints: Uint32Array;
|
|
254
|
-
|
|
255
|
-
let mainBindGroup: GPUBindGroup | null = null;
|
|
256
|
-
let blitBindGroup: GPUBindGroup | null = null;
|
|
257
|
-
let cachedInputView: GPUTextureView | null = null;
|
|
258
|
-
let cachedSampler: GPUSampler | null = null;
|
|
259
|
-
let cachedMaskView: GPUTextureView | null = null;
|
|
497
|
+
let gpu: PostProcessGPU | null = null;
|
|
498
|
+
const cache: PostProcessCache = {
|
|
499
|
+
mainBindGroup: null,
|
|
500
|
+
blitBindGroup: null,
|
|
501
|
+
inputView: null,
|
|
502
|
+
sampler: null,
|
|
503
|
+
maskView: null,
|
|
504
|
+
};
|
|
260
505
|
|
|
261
506
|
return {
|
|
262
507
|
id: "postprocess",
|
|
@@ -269,261 +514,12 @@ export function createPostProcessNode(config: PostProcessConfig): ComputeNode {
|
|
|
269
514
|
outputs: [{ id: "framebuffer", access: "write" }],
|
|
270
515
|
|
|
271
516
|
async prepare(device: GPUDevice) {
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
const [mainModule, blitModule] = await Promise.all([
|
|
275
|
-
device.createShaderModule({ code: shader }),
|
|
276
|
-
device.createShaderModule({ code: blitShader }),
|
|
277
|
-
]);
|
|
278
|
-
|
|
279
|
-
[pipeline, blitPipeline] = await Promise.all([
|
|
280
|
-
device.createRenderPipelineAsync({
|
|
281
|
-
layout: "auto",
|
|
282
|
-
vertex: { module: mainModule, entryPoint: "vertexMain" },
|
|
283
|
-
fragment: {
|
|
284
|
-
module: mainModule,
|
|
285
|
-
entryPoint: "fragmentMain",
|
|
286
|
-
targets: [{ format }],
|
|
287
|
-
},
|
|
288
|
-
primitive: { topology: "triangle-list" },
|
|
289
|
-
}),
|
|
290
|
-
device.createRenderPipelineAsync({
|
|
291
|
-
layout: "auto",
|
|
292
|
-
vertex: { module: blitModule, entryPoint: "vertexMain" },
|
|
293
|
-
fragment: {
|
|
294
|
-
module: blitModule,
|
|
295
|
-
entryPoint: "fragmentMain",
|
|
296
|
-
targets: [{ format }],
|
|
297
|
-
},
|
|
298
|
-
primitive: { topology: "triangle-list" },
|
|
299
|
-
}),
|
|
300
|
-
]);
|
|
301
|
-
|
|
302
|
-
uniformBuffer = device.createBuffer({
|
|
303
|
-
size: 48,
|
|
304
|
-
usage: GPUBufferUsage.UNIFORM | GPUBufferUsage.COPY_DST,
|
|
305
|
-
});
|
|
306
|
-
|
|
307
|
-
linearSampler = device.createSampler({
|
|
308
|
-
magFilter: "linear",
|
|
309
|
-
minFilter: "linear",
|
|
310
|
-
});
|
|
311
|
-
|
|
312
|
-
nearestSampler = device.createSampler({
|
|
313
|
-
magFilter: "nearest",
|
|
314
|
-
minFilter: "nearest",
|
|
315
|
-
});
|
|
316
|
-
|
|
317
|
-
uniformData = new ArrayBuffer(48);
|
|
318
|
-
uniformFloats = new Float32Array(uniformData);
|
|
319
|
-
uniformUints = new Uint32Array(uniformData);
|
|
517
|
+
gpu = await preparePostProcess(device);
|
|
320
518
|
},
|
|
321
519
|
|
|
322
520
|
execute(ctx: ExecutionContext) {
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
const height = context.canvas.height;
|
|
326
|
-
const colorView = ctx.getTextureView("color")!;
|
|
327
|
-
const maskView = ctx.getTextureView("mask")!;
|
|
328
|
-
const zView = ctx.getTextureView("z")!;
|
|
329
|
-
const eidView = ctx.getTextureView("eid")!;
|
|
330
|
-
const pingAView = ctx.getTextureView("pingA")!;
|
|
331
|
-
const pingBView = ctx.getTextureView("pingB")!;
|
|
332
|
-
|
|
333
|
-
const renderSize = config.getRenderSize?.();
|
|
334
|
-
const isUpscaling =
|
|
335
|
-
renderSize && (renderSize.width !== width || renderSize.height !== height);
|
|
336
|
-
const sampler = isUpscaling ? nearestSampler! : linearSampler!;
|
|
337
|
-
|
|
338
|
-
let currentInput = colorView;
|
|
339
|
-
let currentOutput = pingAView;
|
|
340
|
-
let pingPong = false;
|
|
341
|
-
|
|
342
|
-
const beforePostProcess = getDrawsByPass(config.state, Pass.BeforePost);
|
|
343
|
-
for (const contributor of beforePostProcess) {
|
|
344
|
-
const passCtx: DrawContext = {
|
|
345
|
-
device,
|
|
346
|
-
encoder,
|
|
347
|
-
format,
|
|
348
|
-
width,
|
|
349
|
-
height,
|
|
350
|
-
sceneView: colorView,
|
|
351
|
-
zView,
|
|
352
|
-
entityIdView: eidView,
|
|
353
|
-
maskView,
|
|
354
|
-
canvasView,
|
|
355
|
-
inputView: currentInput,
|
|
356
|
-
outputView: currentOutput,
|
|
357
|
-
};
|
|
358
|
-
contributor.execute(passCtx);
|
|
359
|
-
|
|
360
|
-
currentInput = currentOutput;
|
|
361
|
-
currentOutput = pingPong ? pingAView : pingBView;
|
|
362
|
-
pingPong = !pingPong;
|
|
363
|
-
}
|
|
364
|
-
|
|
365
|
-
const postProcessContributors = getDrawsByPass(config.state, Pass.Post);
|
|
366
|
-
const hasBuiltinEffects =
|
|
367
|
-
config.uniforms.tonemap ||
|
|
368
|
-
config.uniforms.fxaa ||
|
|
369
|
-
config.uniforms.vignetteStrength > 0 ||
|
|
370
|
-
config.uniforms.bloomIntensity > 0 ||
|
|
371
|
-
config.uniforms.quantize > 0;
|
|
372
|
-
|
|
373
|
-
if (hasBuiltinEffects || postProcessContributors.length > 0) {
|
|
374
|
-
for (const contributor of postProcessContributors) {
|
|
375
|
-
const passCtx: DrawContext = {
|
|
376
|
-
device,
|
|
377
|
-
encoder,
|
|
378
|
-
format,
|
|
379
|
-
width,
|
|
380
|
-
height,
|
|
381
|
-
sceneView: colorView,
|
|
382
|
-
zView,
|
|
383
|
-
entityIdView: eidView,
|
|
384
|
-
maskView,
|
|
385
|
-
canvasView,
|
|
386
|
-
inputView: currentInput,
|
|
387
|
-
outputView: currentOutput,
|
|
388
|
-
};
|
|
389
|
-
contributor.execute(passCtx);
|
|
390
|
-
|
|
391
|
-
currentInput = currentOutput;
|
|
392
|
-
currentOutput = pingPong ? pingAView : pingBView;
|
|
393
|
-
pingPong = !pingPong;
|
|
394
|
-
}
|
|
395
|
-
|
|
396
|
-
if (hasBuiltinEffects) {
|
|
397
|
-
let flags = 0;
|
|
398
|
-
if (config.uniforms.tonemap) flags |= FLAG_TONEMAP;
|
|
399
|
-
if (config.uniforms.fxaa) flags |= FLAG_FXAA;
|
|
400
|
-
if (config.uniforms.vignetteStrength > 0) flags |= FLAG_VIGNETTE;
|
|
401
|
-
if (config.uniforms.bloomIntensity > 0) flags |= FLAG_BLOOM;
|
|
402
|
-
if (config.uniforms.quantize > 0) flags |= FLAG_QUANTIZE;
|
|
403
|
-
|
|
404
|
-
uniformFloats[0] = config.uniforms.exposure;
|
|
405
|
-
uniformFloats[1] = config.uniforms.vignetteStrength;
|
|
406
|
-
uniformFloats[2] = config.uniforms.vignetteInner;
|
|
407
|
-
uniformFloats[3] = config.uniforms.vignetteOuter;
|
|
408
|
-
uniformFloats[4] = 1.0 / width;
|
|
409
|
-
uniformFloats[5] = 1.0 / height;
|
|
410
|
-
uniformUints[6] = flags;
|
|
411
|
-
uniformFloats[7] = config.uniforms.bloomIntensity;
|
|
412
|
-
uniformFloats[8] = config.uniforms.bloomThreshold;
|
|
413
|
-
uniformFloats[9] = config.uniforms.bloomRadius;
|
|
414
|
-
uniformFloats[10] = config.uniforms.quantize;
|
|
415
|
-
|
|
416
|
-
device.queue.writeBuffer(uniformBuffer!, 0, uniformData);
|
|
417
|
-
|
|
418
|
-
if (
|
|
419
|
-
currentInput !== cachedInputView ||
|
|
420
|
-
sampler !== cachedSampler ||
|
|
421
|
-
maskView !== cachedMaskView
|
|
422
|
-
) {
|
|
423
|
-
mainBindGroup = device.createBindGroup({
|
|
424
|
-
layout: pipeline!.getBindGroupLayout(0),
|
|
425
|
-
entries: [
|
|
426
|
-
{ binding: 0, resource: currentInput },
|
|
427
|
-
{ binding: 1, resource: sampler },
|
|
428
|
-
{ binding: 2, resource: { buffer: uniformBuffer! } },
|
|
429
|
-
{ binding: 3, resource: maskView },
|
|
430
|
-
],
|
|
431
|
-
});
|
|
432
|
-
cachedInputView = currentInput;
|
|
433
|
-
cachedSampler = sampler;
|
|
434
|
-
cachedMaskView = maskView;
|
|
435
|
-
}
|
|
436
|
-
|
|
437
|
-
const pass = encoder.beginRenderPass({
|
|
438
|
-
colorAttachments: [
|
|
439
|
-
{
|
|
440
|
-
view: canvasView,
|
|
441
|
-
loadOp: "clear" as const,
|
|
442
|
-
storeOp: "store" as const,
|
|
443
|
-
clearValue: { r: 0, g: 0, b: 0, a: 1 },
|
|
444
|
-
},
|
|
445
|
-
],
|
|
446
|
-
});
|
|
447
|
-
|
|
448
|
-
pass.setPipeline(pipeline!);
|
|
449
|
-
pass.setBindGroup(0, mainBindGroup!);
|
|
450
|
-
pass.draw(3);
|
|
451
|
-
pass.end();
|
|
452
|
-
} else {
|
|
453
|
-
if (currentInput !== cachedInputView || sampler !== cachedSampler) {
|
|
454
|
-
blitBindGroup = device.createBindGroup({
|
|
455
|
-
layout: blitPipeline!.getBindGroupLayout(0),
|
|
456
|
-
entries: [
|
|
457
|
-
{ binding: 0, resource: currentInput },
|
|
458
|
-
{ binding: 1, resource: sampler },
|
|
459
|
-
],
|
|
460
|
-
});
|
|
461
|
-
cachedInputView = currentInput;
|
|
462
|
-
cachedSampler = sampler;
|
|
463
|
-
}
|
|
464
|
-
|
|
465
|
-
const pass = encoder.beginRenderPass({
|
|
466
|
-
colorAttachments: [
|
|
467
|
-
{
|
|
468
|
-
view: canvasView,
|
|
469
|
-
loadOp: "clear" as const,
|
|
470
|
-
storeOp: "store" as const,
|
|
471
|
-
clearValue: { r: 0, g: 0, b: 0, a: 1 },
|
|
472
|
-
},
|
|
473
|
-
],
|
|
474
|
-
});
|
|
475
|
-
|
|
476
|
-
pass.setPipeline(blitPipeline!);
|
|
477
|
-
pass.setBindGroup(0, blitBindGroup!);
|
|
478
|
-
pass.draw(3);
|
|
479
|
-
pass.end();
|
|
480
|
-
}
|
|
481
|
-
} else {
|
|
482
|
-
if (currentInput !== cachedInputView || sampler !== cachedSampler) {
|
|
483
|
-
blitBindGroup = device.createBindGroup({
|
|
484
|
-
layout: blitPipeline!.getBindGroupLayout(0),
|
|
485
|
-
entries: [
|
|
486
|
-
{ binding: 0, resource: currentInput },
|
|
487
|
-
{ binding: 1, resource: sampler },
|
|
488
|
-
],
|
|
489
|
-
});
|
|
490
|
-
cachedInputView = currentInput;
|
|
491
|
-
cachedSampler = sampler;
|
|
492
|
-
}
|
|
493
|
-
|
|
494
|
-
const pass = encoder.beginRenderPass({
|
|
495
|
-
colorAttachments: [
|
|
496
|
-
{
|
|
497
|
-
view: canvasView,
|
|
498
|
-
loadOp: "clear" as const,
|
|
499
|
-
storeOp: "store" as const,
|
|
500
|
-
clearValue: { r: 0, g: 0, b: 0, a: 1 },
|
|
501
|
-
},
|
|
502
|
-
],
|
|
503
|
-
});
|
|
504
|
-
|
|
505
|
-
pass.setPipeline(blitPipeline!);
|
|
506
|
-
pass.setBindGroup(0, blitBindGroup!);
|
|
507
|
-
pass.draw(3);
|
|
508
|
-
pass.end();
|
|
509
|
-
}
|
|
510
|
-
|
|
511
|
-
const afterPostProcess = getDrawsByPass(config.state, Pass.AfterPost);
|
|
512
|
-
for (const contributor of afterPostProcess) {
|
|
513
|
-
const passCtx: DrawContext = {
|
|
514
|
-
device,
|
|
515
|
-
encoder,
|
|
516
|
-
format,
|
|
517
|
-
width,
|
|
518
|
-
height,
|
|
519
|
-
sceneView: colorView,
|
|
520
|
-
zView,
|
|
521
|
-
entityIdView: eidView,
|
|
522
|
-
maskView,
|
|
523
|
-
canvasView,
|
|
524
|
-
};
|
|
525
|
-
contributor.execute(passCtx);
|
|
526
|
-
}
|
|
521
|
+
if (!gpu) return;
|
|
522
|
+
executePostProcess(gpu, cache, ctx, config);
|
|
527
523
|
},
|
|
528
524
|
};
|
|
529
525
|
}
|
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
import { MAX_ENTITIES } from "../../../core";
|
|
2
2
|
import { setTraits } from "../../../core/component";
|
|
3
|
+
import { WGSL_STRUCTS } from "./structs";
|
|
4
|
+
import { compileVertexBody, WGSL_LIGHTING_CALC, SPECULAR_WGSL } from "./shaders";
|
|
3
5
|
|
|
4
6
|
export interface SurfaceData {
|
|
5
7
|
vertex?: string;
|
|
6
8
|
fragment?: string;
|
|
7
|
-
lit?: boolean;
|
|
8
9
|
}
|
|
9
10
|
|
|
10
11
|
interface ComposedSurface extends SurfaceData {
|
|
@@ -21,23 +22,28 @@ export function createSurfaceRegistry(): SurfaceRegistry {
|
|
|
21
22
|
return registry;
|
|
22
23
|
}
|
|
23
24
|
|
|
25
|
+
const surfaceNames = new Map<string, number>();
|
|
26
|
+
|
|
24
27
|
function initBuiltIns(registry: SurfaceRegistry): void {
|
|
25
|
-
registry.surfaces.push({
|
|
28
|
+
registry.surfaces.push({});
|
|
26
29
|
|
|
27
30
|
registry.surfaces.push({
|
|
28
|
-
lit: false,
|
|
29
31
|
fragment: `(*surface).baseColor = (*surface).normal * 0.5 + 0.5;`,
|
|
30
32
|
});
|
|
31
33
|
|
|
32
34
|
registry.surfaces.push({
|
|
33
|
-
lit: false,
|
|
34
35
|
fragment: `
|
|
35
36
|
let depth = position.z;
|
|
36
37
|
let remapped = pow(1.0 - depth, 0.1);
|
|
37
38
|
(*surface).baseColor = vec3(remapped);`,
|
|
38
39
|
});
|
|
39
40
|
|
|
40
|
-
registry.surfaces.push({
|
|
41
|
+
registry.surfaces.push({});
|
|
42
|
+
|
|
43
|
+
surfaceNames.set("default", 0);
|
|
44
|
+
surfaceNames.set("normals", 1);
|
|
45
|
+
surfaceNames.set("depth", 2);
|
|
46
|
+
surfaceNames.set("albedo", 3);
|
|
41
47
|
}
|
|
42
48
|
|
|
43
49
|
export const SurfaceType = {
|
|
@@ -73,14 +79,11 @@ export function composeSurfaces(registry: SurfaceRegistry, ...ids: number[]): nu
|
|
|
73
79
|
|
|
74
80
|
const vertexParts: string[] = [];
|
|
75
81
|
const fragmentParts: string[] = [];
|
|
76
|
-
let lit = true;
|
|
77
82
|
|
|
78
83
|
for (const id of validIds) {
|
|
79
84
|
const s = registry.surfaces[id];
|
|
80
85
|
if (!s) continue;
|
|
81
86
|
|
|
82
|
-
if (s.lit === false) lit = false;
|
|
83
|
-
|
|
84
87
|
if ("composed" in s) {
|
|
85
88
|
for (const composedId of s.composed) {
|
|
86
89
|
const inner = registry.surfaces[composedId];
|
|
@@ -96,7 +99,6 @@ export function composeSurfaces(registry: SurfaceRegistry, ...ids: number[]): nu
|
|
|
96
99
|
const composedData: ComposedSurface = {
|
|
97
100
|
vertex: vertexParts.length > 0 ? vertexParts.join("\n ") : undefined,
|
|
98
101
|
fragment: fragmentParts.length > 0 ? fragmentParts.join("\n ") : undefined,
|
|
99
|
-
lit,
|
|
100
102
|
composed: validIds,
|
|
101
103
|
};
|
|
102
104
|
|
|
@@ -107,8 +109,14 @@ export function composeSurfaces(registry: SurfaceRegistry, ...ids: number[]): nu
|
|
|
107
109
|
|
|
108
110
|
const defaultRegistry = createSurfaceRegistry();
|
|
109
111
|
|
|
110
|
-
export function surface(data: SurfaceData): number {
|
|
111
|
-
|
|
112
|
+
export function surface(data: SurfaceData, name?: string): number {
|
|
113
|
+
const id = registerSurface(defaultRegistry, data);
|
|
114
|
+
if (name) surfaceNames.set(name, id);
|
|
115
|
+
return id;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
export function getSurfaceByName(name: string): number | undefined {
|
|
119
|
+
return surfaceNames.get(name);
|
|
112
120
|
}
|
|
113
121
|
|
|
114
122
|
export function getDefaultSurface(id: number): SurfaceData | undefined {
|
|
@@ -120,6 +128,7 @@ export function getDefaultAllSurfaces(): SurfaceData[] {
|
|
|
120
128
|
}
|
|
121
129
|
|
|
122
130
|
export function clearDefaultSurfaces(): void {
|
|
131
|
+
surfaceNames.clear();
|
|
123
132
|
clearSurfaces(defaultRegistry);
|
|
124
133
|
}
|
|
125
134
|
|
|
@@ -141,6 +150,66 @@ setTraits(Surface, {
|
|
|
141
150
|
defaults: () => ({
|
|
142
151
|
type: SurfaceType.Default,
|
|
143
152
|
}),
|
|
153
|
+
parse: { type: getSurfaceByName },
|
|
144
154
|
});
|
|
145
155
|
|
|
146
|
-
export
|
|
156
|
+
export function compileSurface(data: SurfaceData): string {
|
|
157
|
+
const vertexTransform = compileVertexBody(data.vertex);
|
|
158
|
+
|
|
159
|
+
const fragmentBody = data.fragment ?? "";
|
|
160
|
+
|
|
161
|
+
return /* wgsl */ `
|
|
162
|
+
${WGSL_STRUCTS}
|
|
163
|
+
${SPECULAR_WGSL}
|
|
164
|
+
|
|
165
|
+
fn userVertexTransform(worldPos: vec3<f32>, normal: vec3<f32>, eid: u32) -> vec3<f32> {
|
|
166
|
+
${vertexTransform}
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
fn userFragment(surface: ptr<function, SurfaceData>, position: vec4<f32>) {
|
|
170
|
+
${fragmentBody}
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
@vertex
|
|
174
|
+
fn vs(input: VertexInput) -> VertexOutput {
|
|
175
|
+
let eid = entityIds[input.instance];
|
|
176
|
+
let world = matrices[eid];
|
|
177
|
+
let scaledPos = input.position * sizes[eid].xyz;
|
|
178
|
+
let baseWorldPos = (world * vec4<f32>(scaledPos, 1.0)).xyz;
|
|
179
|
+
let worldNormal = normalize((world * vec4<f32>(input.normal, 0.0)).xyz);
|
|
180
|
+
let finalWorldPos = userVertexTransform(baseWorldPos, worldNormal, eid);
|
|
181
|
+
|
|
182
|
+
var output: VertexOutput;
|
|
183
|
+
output.position = scene.viewProj * vec4<f32>(finalWorldPos, 1.0);
|
|
184
|
+
output.color = data[eid].baseColor;
|
|
185
|
+
output.worldNormal = worldNormal;
|
|
186
|
+
output.entityId = eid;
|
|
187
|
+
output.worldPos = finalWorldPos;
|
|
188
|
+
return output;
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
@fragment
|
|
192
|
+
fn fs(input: VertexOutput) -> FragmentOutput {
|
|
193
|
+
let eid = input.entityId;
|
|
194
|
+
let d = data[eid];
|
|
195
|
+
|
|
196
|
+
var surface: SurfaceData;
|
|
197
|
+
surface.baseColor = input.color.rgb;
|
|
198
|
+
surface.roughness = d.pbr.x;
|
|
199
|
+
surface.metallic = d.pbr.y;
|
|
200
|
+
surface.emission = d.emission.rgb * d.emission.a;
|
|
201
|
+
surface.normal = normalize(input.worldNormal);
|
|
202
|
+
surface.worldPos = input.worldPos;
|
|
203
|
+
|
|
204
|
+
userFragment(&surface, input.position);
|
|
205
|
+
|
|
206
|
+
let shadowFactor = 1.0;
|
|
207
|
+
${WGSL_LIGHTING_CALC}
|
|
208
|
+
|
|
209
|
+
var output: FragmentOutput;
|
|
210
|
+
output.color = vec4<f32>(litColor, input.color.a);
|
|
211
|
+
output.entityId = input.entityId;
|
|
212
|
+
return output;
|
|
213
|
+
}
|
|
214
|
+
`;
|
|
215
|
+
}
|