@multiplekex/shallot 0.1.12 → 0.2.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 +3 -4
- package/src/core/builder.ts +71 -32
- package/src/core/component.ts +25 -11
- package/src/core/index.ts +14 -13
- package/src/core/math.ts +135 -0
- package/src/core/runtime.ts +0 -1
- package/src/core/state.ts +9 -68
- package/src/core/xml.ts +381 -265
- package/src/editor/format.ts +5 -0
- package/src/editor/index.ts +101 -0
- package/src/extras/arrows/index.ts +28 -69
- package/src/extras/gradient/index.ts +36 -52
- package/src/extras/lines/index.ts +51 -122
- package/src/extras/orbit/index.ts +40 -15
- package/src/extras/text/font.ts +546 -0
- package/src/extras/text/index.ts +158 -204
- package/src/extras/text/sdf.ts +429 -0
- package/src/standard/activity/index.ts +172 -0
- package/src/standard/compute/graph.ts +23 -23
- package/src/standard/compute/index.ts +76 -61
- package/src/standard/defaults.ts +8 -5
- package/src/standard/index.ts +1 -0
- package/src/standard/input/index.ts +30 -19
- package/src/standard/loading/index.ts +18 -13
- package/src/standard/render/bvh/blas.ts +752 -0
- package/src/standard/render/bvh/radix.ts +476 -0
- package/src/standard/render/bvh/structs.ts +167 -0
- package/src/standard/render/bvh/tlas.ts +886 -0
- package/src/standard/render/bvh/traverse.ts +467 -0
- package/src/standard/render/camera.ts +302 -27
- package/src/standard/render/data.ts +93 -0
- package/src/standard/render/depth.ts +117 -0
- package/src/standard/render/forward/index.ts +259 -0
- package/src/standard/render/forward/raster.ts +228 -0
- package/src/standard/render/index.ts +443 -70
- package/src/standard/render/indirect.ts +40 -0
- package/src/standard/render/instance.ts +214 -0
- package/src/standard/render/intersection.ts +72 -0
- package/src/standard/render/light.ts +16 -16
- package/src/standard/render/mesh/index.ts +67 -75
- package/src/standard/render/mesh/unified.ts +96 -0
- package/src/standard/render/{transparent.ts → overlay.ts} +14 -15
- package/src/standard/render/pass.ts +10 -4
- package/src/standard/render/postprocess.ts +142 -64
- package/src/standard/render/ray.ts +61 -0
- package/src/standard/render/scene.ts +38 -164
- package/src/standard/render/shaders.ts +484 -0
- package/src/standard/render/surface/compile.ts +3 -10
- package/src/standard/render/surface/index.ts +60 -30
- package/src/standard/render/surface/noise.ts +45 -0
- package/src/standard/render/surface/structs.ts +60 -19
- package/src/standard/render/surface/wgsl.ts +573 -0
- package/src/standard/render/triangle.ts +84 -0
- package/src/standard/transforms/index.ts +4 -6
- package/src/standard/tween/index.ts +10 -1
- package/src/standard/tween/sequence.ts +24 -16
- package/src/standard/tween/tween.ts +67 -16
- package/src/core/types.ts +0 -37
- package/src/standard/compute/inspect.ts +0 -201
- package/src/standard/compute/pass.ts +0 -23
- package/src/standard/compute/timing.ts +0 -139
- package/src/standard/render/forward.ts +0 -273
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
import { getMesh } from ".";
|
|
2
|
+
|
|
3
|
+
export interface ShapeMeta {
|
|
4
|
+
vertexOffset: number;
|
|
5
|
+
indexOffset: number;
|
|
6
|
+
triCount: number;
|
|
7
|
+
_pad: number;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export interface ShapeAtlas {
|
|
11
|
+
vertices: GPUBuffer;
|
|
12
|
+
indices: GPUBuffer;
|
|
13
|
+
meta: GPUBuffer;
|
|
14
|
+
shapeCount: number;
|
|
15
|
+
maxTriangles: number;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
const MAX_SHAPES = 16;
|
|
19
|
+
|
|
20
|
+
export function createShapeAtlas(device: GPUDevice): ShapeAtlas {
|
|
21
|
+
const allVertices: number[] = [];
|
|
22
|
+
const allIndices: number[] = [];
|
|
23
|
+
const shapeMetas: ShapeMeta[] = [];
|
|
24
|
+
|
|
25
|
+
let vertexOffset = 0;
|
|
26
|
+
let indexOffset = 0;
|
|
27
|
+
let maxTriangles = 0;
|
|
28
|
+
|
|
29
|
+
for (let shapeId = 0; shapeId < MAX_SHAPES; shapeId++) {
|
|
30
|
+
const mesh = getMesh(shapeId);
|
|
31
|
+
if (!mesh) {
|
|
32
|
+
shapeMetas.push({ vertexOffset: 0, indexOffset: 0, triCount: 0, _pad: 0 });
|
|
33
|
+
continue;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
const triCount = mesh.indexCount / 3;
|
|
37
|
+
shapeMetas.push({
|
|
38
|
+
vertexOffset,
|
|
39
|
+
indexOffset,
|
|
40
|
+
triCount,
|
|
41
|
+
_pad: 0,
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
for (let i = 0; i < mesh.vertices.length; i++) {
|
|
45
|
+
allVertices.push(mesh.vertices[i]);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
for (let i = 0; i < mesh.indices.length; i++) {
|
|
49
|
+
allIndices.push(mesh.indices[i]);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
vertexOffset += mesh.vertices.length;
|
|
53
|
+
indexOffset += mesh.indices.length;
|
|
54
|
+
maxTriangles += triCount;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
const verticesData = new Float32Array(allVertices);
|
|
58
|
+
const indicesData = new Uint32Array(allIndices);
|
|
59
|
+
const metaData = new Uint32Array(MAX_SHAPES * 4);
|
|
60
|
+
|
|
61
|
+
for (let i = 0; i < shapeMetas.length; i++) {
|
|
62
|
+
metaData[i * 4] = shapeMetas[i].vertexOffset;
|
|
63
|
+
metaData[i * 4 + 1] = shapeMetas[i].indexOffset;
|
|
64
|
+
metaData[i * 4 + 2] = shapeMetas[i].triCount;
|
|
65
|
+
metaData[i * 4 + 3] = 0;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
const vertices = device.createBuffer({
|
|
69
|
+
label: "unified-vertices",
|
|
70
|
+
size: Math.max(verticesData.byteLength, 16),
|
|
71
|
+
usage: GPUBufferUsage.STORAGE | GPUBufferUsage.COPY_DST,
|
|
72
|
+
});
|
|
73
|
+
device.queue.writeBuffer(vertices, 0, verticesData);
|
|
74
|
+
|
|
75
|
+
const indices = device.createBuffer({
|
|
76
|
+
label: "unified-indices",
|
|
77
|
+
size: Math.max(indicesData.byteLength, 16),
|
|
78
|
+
usage: GPUBufferUsage.STORAGE | GPUBufferUsage.COPY_DST,
|
|
79
|
+
});
|
|
80
|
+
device.queue.writeBuffer(indices, 0, indicesData);
|
|
81
|
+
|
|
82
|
+
const meta = device.createBuffer({
|
|
83
|
+
label: "unified-meta",
|
|
84
|
+
size: metaData.byteLength,
|
|
85
|
+
usage: GPUBufferUsage.STORAGE | GPUBufferUsage.COPY_DST,
|
|
86
|
+
});
|
|
87
|
+
device.queue.writeBuffer(meta, 0, metaData);
|
|
88
|
+
|
|
89
|
+
return {
|
|
90
|
+
vertices,
|
|
91
|
+
indices,
|
|
92
|
+
meta,
|
|
93
|
+
shapeCount: shapeMetas.filter((m) => m.triCount > 0).length,
|
|
94
|
+
maxTriangles,
|
|
95
|
+
};
|
|
96
|
+
}
|
|
@@ -1,30 +1,29 @@
|
|
|
1
1
|
import type { State } from "../../core";
|
|
2
2
|
import type { ComputeNode, ExecutionContext } from "../compute";
|
|
3
|
-
import { MASK_FORMAT } from "./scene";
|
|
3
|
+
import { COLOR_FORMAT, MASK_FORMAT } from "./scene";
|
|
4
4
|
import { Pass, getDrawsByPass, type DrawContext, type SharedPassContext } from "./pass";
|
|
5
5
|
|
|
6
6
|
export { MASK_FORMAT };
|
|
7
7
|
|
|
8
|
-
export interface
|
|
8
|
+
export interface OverlayNodeConfig {
|
|
9
9
|
state: State;
|
|
10
10
|
}
|
|
11
11
|
|
|
12
|
-
export function
|
|
12
|
+
export function createOverlayNode(config: OverlayNodeConfig): ComputeNode {
|
|
13
13
|
return {
|
|
14
|
-
id: "
|
|
15
|
-
pass: Pass.Transparent,
|
|
14
|
+
id: "overlay",
|
|
16
15
|
inputs: [{ id: "depth", access: "read" }],
|
|
17
16
|
outputs: [
|
|
18
|
-
{ id: "
|
|
17
|
+
{ id: "color", access: "write" },
|
|
19
18
|
{ id: "mask", access: "write" },
|
|
20
19
|
],
|
|
21
20
|
|
|
22
21
|
execute(ctx: ExecutionContext) {
|
|
23
22
|
const { device, encoder, format, context } = ctx;
|
|
24
|
-
const targetView = ctx.getTextureView("
|
|
23
|
+
const targetView = ctx.getTextureView("color") ?? ctx.canvasView;
|
|
25
24
|
const depthView = ctx.getTextureView("depth")!;
|
|
26
25
|
const maskView = ctx.getTextureView("mask")!;
|
|
27
|
-
const
|
|
26
|
+
const eidView = ctx.getTextureView("eid")!;
|
|
28
27
|
|
|
29
28
|
const drawCtx: DrawContext = {
|
|
30
29
|
device,
|
|
@@ -34,17 +33,17 @@ export function createTransparentNode(config: TransparentNodeConfig): ComputeNod
|
|
|
34
33
|
height: context.canvas.height,
|
|
35
34
|
sceneView: targetView,
|
|
36
35
|
depthView,
|
|
37
|
-
entityIdView,
|
|
36
|
+
entityIdView: eidView,
|
|
38
37
|
maskView,
|
|
39
38
|
canvasView: ctx.canvasView,
|
|
40
39
|
};
|
|
41
40
|
|
|
42
|
-
const
|
|
43
|
-
for (const draw of
|
|
41
|
+
const beforeOverlays = getDrawsByPass(config.state, Pass.BeforeOverlay);
|
|
42
|
+
for (const draw of beforeOverlays) {
|
|
44
43
|
draw.execute(drawCtx);
|
|
45
44
|
}
|
|
46
45
|
|
|
47
|
-
const draws = getDrawsByPass(config.state, Pass.
|
|
46
|
+
const draws = getDrawsByPass(config.state, Pass.Overlay);
|
|
48
47
|
if (draws.length > 0) {
|
|
49
48
|
const pass = encoder.beginRenderPass({
|
|
50
49
|
colorAttachments: [
|
|
@@ -69,7 +68,7 @@ export function createTransparentNode(config: TransparentNodeConfig): ComputeNod
|
|
|
69
68
|
|
|
70
69
|
const sharedCtx: SharedPassContext = {
|
|
71
70
|
device,
|
|
72
|
-
format,
|
|
71
|
+
format: COLOR_FORMAT,
|
|
73
72
|
maskFormat: MASK_FORMAT,
|
|
74
73
|
};
|
|
75
74
|
|
|
@@ -82,8 +81,8 @@ export function createTransparentNode(config: TransparentNodeConfig): ComputeNod
|
|
|
82
81
|
pass.end();
|
|
83
82
|
}
|
|
84
83
|
|
|
85
|
-
const
|
|
86
|
-
for (const draw of
|
|
84
|
+
const afterOverlays = getDrawsByPass(config.state, Pass.AfterOverlay);
|
|
85
|
+
for (const draw of afterOverlays) {
|
|
87
86
|
draw.execute(drawCtx);
|
|
88
87
|
}
|
|
89
88
|
},
|
|
@@ -1,7 +1,13 @@
|
|
|
1
1
|
import { resource, type State } from "../../core";
|
|
2
|
-
import { Pass } from "../compute/pass";
|
|
3
2
|
|
|
4
|
-
export
|
|
3
|
+
export enum Pass {
|
|
4
|
+
BeforeOverlay,
|
|
5
|
+
Overlay,
|
|
6
|
+
AfterOverlay,
|
|
7
|
+
BeforePost,
|
|
8
|
+
Post,
|
|
9
|
+
AfterPost,
|
|
10
|
+
}
|
|
5
11
|
|
|
6
12
|
export interface DrawContext {
|
|
7
13
|
readonly device: GPUDevice;
|
|
@@ -34,11 +40,11 @@ export interface Draw {
|
|
|
34
40
|
draw?(pass: GPURenderPassEncoder, ctx: SharedPassContext): void;
|
|
35
41
|
}
|
|
36
42
|
|
|
37
|
-
export interface
|
|
43
|
+
export interface Draws {
|
|
38
44
|
draws: Map<string, Draw>;
|
|
39
45
|
}
|
|
40
46
|
|
|
41
|
-
export const Draws = resource<
|
|
47
|
+
export const Draws = resource<Draws>("draws");
|
|
42
48
|
|
|
43
49
|
export function registerDraw(state: State, draw: Draw): void {
|
|
44
50
|
const draws = Draws.from(state);
|
|
@@ -16,7 +16,11 @@ struct Uniforms {
|
|
|
16
16
|
texelSizeX: f32,
|
|
17
17
|
texelSizeY: f32,
|
|
18
18
|
flags: u32,
|
|
19
|
-
|
|
19
|
+
bloomIntensity: f32,
|
|
20
|
+
bloomThreshold: f32,
|
|
21
|
+
bloomRadius: f32,
|
|
22
|
+
quantizeBands: f32,
|
|
23
|
+
_pad: f32,
|
|
20
24
|
}
|
|
21
25
|
|
|
22
26
|
@group(0) @binding(0) var inputTexture: texture_2d<f32>;
|
|
@@ -27,6 +31,8 @@ struct Uniforms {
|
|
|
27
31
|
const FLAG_TONEMAP: u32 = 1u;
|
|
28
32
|
const FLAG_FXAA: u32 = 2u;
|
|
29
33
|
const FLAG_VIGNETTE: u32 = 4u;
|
|
34
|
+
const FLAG_BLOOM: u32 = 8u;
|
|
35
|
+
const FLAG_QUANTIZE: u32 = 16u;
|
|
30
36
|
|
|
31
37
|
@vertex
|
|
32
38
|
fn vertexMain(@builtin(vertex_index) vertexIndex: u32) -> VertexOutput {
|
|
@@ -112,6 +118,43 @@ fn applyVignette(color: vec3f, uv: vec2f) -> vec3f {
|
|
|
112
118
|
return color * vignette;
|
|
113
119
|
}
|
|
114
120
|
|
|
121
|
+
fn sampleBloom(uv: vec2f) -> vec3f {
|
|
122
|
+
let texelSize = vec2f(uniforms.texelSizeX, uniforms.texelSizeY);
|
|
123
|
+
// Normalize to reference height (1080p) for resolution-independent bloom
|
|
124
|
+
let height = 1.0 / uniforms.texelSizeY;
|
|
125
|
+
let resolutionScale = height / 1080.0;
|
|
126
|
+
let spread = uniforms.bloomRadius * 4.0 * resolutionScale;
|
|
127
|
+
let threshold = uniforms.bloomThreshold;
|
|
128
|
+
let knee = 0.15;
|
|
129
|
+
|
|
130
|
+
var bloom = vec3f(0.0);
|
|
131
|
+
var totalWeight = 0.0;
|
|
132
|
+
|
|
133
|
+
// 9x9 kernel with gaussian weights
|
|
134
|
+
for (var y = -4; y <= 4; y++) {
|
|
135
|
+
for (var x = -4; x <= 4; x++) {
|
|
136
|
+
let offset = vec2f(f32(x), f32(y)) * texelSize * spread;
|
|
137
|
+
let dist2 = f32(x * x + y * y);
|
|
138
|
+
let weight = exp(-dist2 * 0.125);
|
|
139
|
+
|
|
140
|
+
let sampleColor = textureSample(inputTexture, inputSampler, uv + offset).rgb;
|
|
141
|
+
let brightness = max(max(sampleColor.r, sampleColor.g), sampleColor.b);
|
|
142
|
+
let soft = clamp((brightness - threshold + knee) / (2.0 * knee), 0.0, 1.0);
|
|
143
|
+
let contribution = soft * soft;
|
|
144
|
+
|
|
145
|
+
bloom += sampleColor * contribution * weight;
|
|
146
|
+
totalWeight += weight;
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
return bloom / totalWeight;
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
fn applyQuantize(color: vec3f) -> vec3f {
|
|
154
|
+
let bands = uniforms.quantizeBands;
|
|
155
|
+
return floor(color * bands + 0.5) / bands;
|
|
156
|
+
}
|
|
157
|
+
|
|
115
158
|
@fragment
|
|
116
159
|
fn fragmentMain(input: VertexOutput) -> @location(0) vec4f {
|
|
117
160
|
var color = textureSample(inputTexture, inputSampler, input.uv).rgb;
|
|
@@ -122,6 +165,16 @@ fn fragmentMain(input: VertexOutput) -> @location(0) vec4f {
|
|
|
122
165
|
color = select(fxaaColor, color, maskValue >= 0.5);
|
|
123
166
|
}
|
|
124
167
|
|
|
168
|
+
// Bloom applied before tonemapping
|
|
169
|
+
if (uniforms.flags & FLAG_BLOOM) != 0u {
|
|
170
|
+
let bloom = sampleBloom(input.uv);
|
|
171
|
+
color += bloom * uniforms.bloomIntensity;
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
if (uniforms.flags & FLAG_QUANTIZE) != 0u {
|
|
175
|
+
color = applyQuantize(color);
|
|
176
|
+
}
|
|
177
|
+
|
|
125
178
|
if (uniforms.flags & FLAG_TONEMAP) != 0u {
|
|
126
179
|
color = aces(color * uniforms.exposure);
|
|
127
180
|
}
|
|
@@ -169,6 +222,8 @@ fn fragmentMain(input: VertexOutput) -> @location(0) vec4f {
|
|
|
169
222
|
const FLAG_TONEMAP = 1;
|
|
170
223
|
const FLAG_FXAA = 2;
|
|
171
224
|
const FLAG_VIGNETTE = 4;
|
|
225
|
+
const FLAG_BLOOM = 8;
|
|
226
|
+
const FLAG_QUANTIZE = 16;
|
|
172
227
|
|
|
173
228
|
export interface PostProcessUniforms {
|
|
174
229
|
tonemap: boolean;
|
|
@@ -177,63 +232,99 @@ export interface PostProcessUniforms {
|
|
|
177
232
|
vignetteStrength: number;
|
|
178
233
|
vignetteInner: number;
|
|
179
234
|
vignetteOuter: number;
|
|
235
|
+
bloomIntensity: number;
|
|
236
|
+
bloomThreshold: number;
|
|
237
|
+
bloomRadius: number;
|
|
238
|
+
quantize: number;
|
|
180
239
|
}
|
|
181
240
|
|
|
182
241
|
export interface PostProcessConfig {
|
|
183
242
|
state: State;
|
|
184
243
|
uniforms: PostProcessUniforms;
|
|
244
|
+
getRenderSize?: () => { width: number; height: number };
|
|
185
245
|
}
|
|
186
246
|
|
|
187
247
|
export function createPostProcessNode(config: PostProcessConfig): ComputeNode {
|
|
188
248
|
let pipeline: GPURenderPipeline | null = null;
|
|
189
249
|
let blitPipeline: GPURenderPipeline | null = null;
|
|
190
250
|
let uniformBuffer: GPUBuffer | null = null;
|
|
191
|
-
let
|
|
251
|
+
let linearSampler: GPUSampler | null = null;
|
|
252
|
+
let nearestSampler: GPUSampler | null = null;
|
|
192
253
|
|
|
193
254
|
return {
|
|
194
255
|
id: "postprocess",
|
|
195
|
-
pass: Pass.Post,
|
|
196
256
|
inputs: [
|
|
197
|
-
{ id: "
|
|
257
|
+
{ id: "color", access: "read" },
|
|
198
258
|
{ id: "mask", access: "read" },
|
|
199
259
|
{ id: "pingA", access: "read" },
|
|
200
260
|
{ id: "pingB", access: "read" },
|
|
201
261
|
],
|
|
202
262
|
outputs: [{ id: "framebuffer", access: "write" }],
|
|
203
263
|
|
|
264
|
+
async prepare(device: GPUDevice) {
|
|
265
|
+
const format: GPUTextureFormat = "bgra8unorm";
|
|
266
|
+
|
|
267
|
+
const [mainModule, blitModule] = await Promise.all([
|
|
268
|
+
device.createShaderModule({ code: shader }),
|
|
269
|
+
device.createShaderModule({ code: blitShader }),
|
|
270
|
+
]);
|
|
271
|
+
|
|
272
|
+
[pipeline, blitPipeline] = await Promise.all([
|
|
273
|
+
device.createRenderPipelineAsync({
|
|
274
|
+
layout: "auto",
|
|
275
|
+
vertex: { module: mainModule, entryPoint: "vertexMain" },
|
|
276
|
+
fragment: {
|
|
277
|
+
module: mainModule,
|
|
278
|
+
entryPoint: "fragmentMain",
|
|
279
|
+
targets: [{ format }],
|
|
280
|
+
},
|
|
281
|
+
primitive: { topology: "triangle-list" },
|
|
282
|
+
}),
|
|
283
|
+
device.createRenderPipelineAsync({
|
|
284
|
+
layout: "auto",
|
|
285
|
+
vertex: { module: blitModule, entryPoint: "vertexMain" },
|
|
286
|
+
fragment: {
|
|
287
|
+
module: blitModule,
|
|
288
|
+
entryPoint: "fragmentMain",
|
|
289
|
+
targets: [{ format }],
|
|
290
|
+
},
|
|
291
|
+
primitive: { topology: "triangle-list" },
|
|
292
|
+
}),
|
|
293
|
+
]);
|
|
294
|
+
|
|
295
|
+
uniformBuffer = device.createBuffer({
|
|
296
|
+
size: 48,
|
|
297
|
+
usage: GPUBufferUsage.UNIFORM | GPUBufferUsage.COPY_DST,
|
|
298
|
+
});
|
|
299
|
+
|
|
300
|
+
linearSampler = device.createSampler({
|
|
301
|
+
magFilter: "linear",
|
|
302
|
+
minFilter: "linear",
|
|
303
|
+
});
|
|
304
|
+
|
|
305
|
+
nearestSampler = device.createSampler({
|
|
306
|
+
magFilter: "nearest",
|
|
307
|
+
minFilter: "nearest",
|
|
308
|
+
});
|
|
309
|
+
},
|
|
310
|
+
|
|
204
311
|
execute(ctx: ExecutionContext) {
|
|
205
312
|
const { device, encoder, canvasView, format, context } = ctx;
|
|
206
313
|
const width = context.canvas.width;
|
|
207
314
|
const height = context.canvas.height;
|
|
208
|
-
const
|
|
315
|
+
const colorView = ctx.getTextureView("color")!;
|
|
209
316
|
const maskView = ctx.getTextureView("mask")!;
|
|
210
317
|
const depthView = ctx.getTextureView("depth")!;
|
|
211
|
-
const
|
|
318
|
+
const eidView = ctx.getTextureView("eid")!;
|
|
212
319
|
const pingAView = ctx.getTextureView("pingA")!;
|
|
213
320
|
const pingBView = ctx.getTextureView("pingB")!;
|
|
214
321
|
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
});
|
|
220
|
-
}
|
|
221
|
-
|
|
222
|
-
if (!blitPipeline) {
|
|
223
|
-
const module = device.createShaderModule({ code: blitShader });
|
|
224
|
-
blitPipeline = device.createRenderPipeline({
|
|
225
|
-
layout: "auto",
|
|
226
|
-
vertex: { module, entryPoint: "vertexMain" },
|
|
227
|
-
fragment: {
|
|
228
|
-
module,
|
|
229
|
-
entryPoint: "fragmentMain",
|
|
230
|
-
targets: [{ format }],
|
|
231
|
-
},
|
|
232
|
-
primitive: { topology: "triangle-list" },
|
|
233
|
-
});
|
|
234
|
-
}
|
|
322
|
+
const renderSize = config.getRenderSize?.();
|
|
323
|
+
const isUpscaling =
|
|
324
|
+
renderSize && (renderSize.width !== width || renderSize.height !== height);
|
|
325
|
+
const sampler = isUpscaling ? nearestSampler! : linearSampler!;
|
|
235
326
|
|
|
236
|
-
let currentInput =
|
|
327
|
+
let currentInput = colorView;
|
|
237
328
|
let currentOutput = pingAView;
|
|
238
329
|
let pingPong = false;
|
|
239
330
|
|
|
@@ -245,9 +336,9 @@ export function createPostProcessNode(config: PostProcessConfig): ComputeNode {
|
|
|
245
336
|
format,
|
|
246
337
|
width,
|
|
247
338
|
height,
|
|
248
|
-
sceneView,
|
|
339
|
+
sceneView: colorView,
|
|
249
340
|
depthView,
|
|
250
|
-
entityIdView,
|
|
341
|
+
entityIdView: eidView,
|
|
251
342
|
maskView,
|
|
252
343
|
canvasView,
|
|
253
344
|
inputView: currentInput,
|
|
@@ -264,7 +355,9 @@ export function createPostProcessNode(config: PostProcessConfig): ComputeNode {
|
|
|
264
355
|
const hasBuiltinEffects =
|
|
265
356
|
config.uniforms.tonemap ||
|
|
266
357
|
config.uniforms.fxaa ||
|
|
267
|
-
config.uniforms.vignetteStrength > 0
|
|
358
|
+
config.uniforms.vignetteStrength > 0 ||
|
|
359
|
+
config.uniforms.bloomIntensity > 0 ||
|
|
360
|
+
config.uniforms.quantize > 0;
|
|
268
361
|
|
|
269
362
|
if (hasBuiltinEffects || postProcessContributors.length > 0) {
|
|
270
363
|
for (const contributor of postProcessContributors) {
|
|
@@ -274,9 +367,9 @@ export function createPostProcessNode(config: PostProcessConfig): ComputeNode {
|
|
|
274
367
|
format,
|
|
275
368
|
width,
|
|
276
369
|
height,
|
|
277
|
-
sceneView,
|
|
370
|
+
sceneView: colorView,
|
|
278
371
|
depthView,
|
|
279
|
-
entityIdView,
|
|
372
|
+
entityIdView: eidView,
|
|
280
373
|
maskView,
|
|
281
374
|
canvasView,
|
|
282
375
|
inputView: currentInput,
|
|
@@ -290,33 +383,14 @@ export function createPostProcessNode(config: PostProcessConfig): ComputeNode {
|
|
|
290
383
|
}
|
|
291
384
|
|
|
292
385
|
if (hasBuiltinEffects) {
|
|
293
|
-
if (!pipeline) {
|
|
294
|
-
const module = device.createShaderModule({ code: shader });
|
|
295
|
-
pipeline = device.createRenderPipeline({
|
|
296
|
-
layout: "auto",
|
|
297
|
-
vertex: { module, entryPoint: "vertexMain" },
|
|
298
|
-
fragment: {
|
|
299
|
-
module,
|
|
300
|
-
entryPoint: "fragmentMain",
|
|
301
|
-
targets: [{ format }],
|
|
302
|
-
},
|
|
303
|
-
primitive: { topology: "triangle-list" },
|
|
304
|
-
});
|
|
305
|
-
}
|
|
306
|
-
|
|
307
|
-
if (!uniformBuffer) {
|
|
308
|
-
uniformBuffer = device.createBuffer({
|
|
309
|
-
size: 32,
|
|
310
|
-
usage: GPUBufferUsage.UNIFORM | GPUBufferUsage.COPY_DST,
|
|
311
|
-
});
|
|
312
|
-
}
|
|
313
|
-
|
|
314
386
|
let flags = 0;
|
|
315
387
|
if (config.uniforms.tonemap) flags |= FLAG_TONEMAP;
|
|
316
388
|
if (config.uniforms.fxaa) flags |= FLAG_FXAA;
|
|
317
389
|
if (config.uniforms.vignetteStrength > 0) flags |= FLAG_VIGNETTE;
|
|
390
|
+
if (config.uniforms.bloomIntensity > 0) flags |= FLAG_BLOOM;
|
|
391
|
+
if (config.uniforms.quantize > 0) flags |= FLAG_QUANTIZE;
|
|
318
392
|
|
|
319
|
-
const data = new ArrayBuffer(
|
|
393
|
+
const data = new ArrayBuffer(48);
|
|
320
394
|
const floats = new Float32Array(data);
|
|
321
395
|
const uints = new Uint32Array(data);
|
|
322
396
|
floats[0] = config.uniforms.exposure;
|
|
@@ -326,15 +400,19 @@ export function createPostProcessNode(config: PostProcessConfig): ComputeNode {
|
|
|
326
400
|
floats[4] = 1.0 / width;
|
|
327
401
|
floats[5] = 1.0 / height;
|
|
328
402
|
uints[6] = flags;
|
|
403
|
+
floats[7] = config.uniforms.bloomIntensity;
|
|
404
|
+
floats[8] = config.uniforms.bloomThreshold;
|
|
405
|
+
floats[9] = config.uniforms.bloomRadius;
|
|
406
|
+
floats[10] = config.uniforms.quantize;
|
|
329
407
|
|
|
330
|
-
device.queue.writeBuffer(uniformBuffer
|
|
408
|
+
device.queue.writeBuffer(uniformBuffer!, 0, data);
|
|
331
409
|
|
|
332
410
|
const bindGroup = device.createBindGroup({
|
|
333
|
-
layout: pipeline
|
|
411
|
+
layout: pipeline!.getBindGroupLayout(0),
|
|
334
412
|
entries: [
|
|
335
413
|
{ binding: 0, resource: currentInput },
|
|
336
414
|
{ binding: 1, resource: sampler },
|
|
337
|
-
{ binding: 2, resource: { buffer: uniformBuffer } },
|
|
415
|
+
{ binding: 2, resource: { buffer: uniformBuffer! } },
|
|
338
416
|
{ binding: 3, resource: maskView },
|
|
339
417
|
],
|
|
340
418
|
});
|
|
@@ -350,13 +428,13 @@ export function createPostProcessNode(config: PostProcessConfig): ComputeNode {
|
|
|
350
428
|
],
|
|
351
429
|
});
|
|
352
430
|
|
|
353
|
-
pass.setPipeline(pipeline);
|
|
431
|
+
pass.setPipeline(pipeline!);
|
|
354
432
|
pass.setBindGroup(0, bindGroup);
|
|
355
433
|
pass.draw(3);
|
|
356
434
|
pass.end();
|
|
357
435
|
} else {
|
|
358
436
|
const bindGroup = device.createBindGroup({
|
|
359
|
-
layout: blitPipeline
|
|
437
|
+
layout: blitPipeline!.getBindGroupLayout(0),
|
|
360
438
|
entries: [
|
|
361
439
|
{ binding: 0, resource: currentInput },
|
|
362
440
|
{ binding: 1, resource: sampler },
|
|
@@ -374,14 +452,14 @@ export function createPostProcessNode(config: PostProcessConfig): ComputeNode {
|
|
|
374
452
|
],
|
|
375
453
|
});
|
|
376
454
|
|
|
377
|
-
pass.setPipeline(blitPipeline);
|
|
455
|
+
pass.setPipeline(blitPipeline!);
|
|
378
456
|
pass.setBindGroup(0, bindGroup);
|
|
379
457
|
pass.draw(3);
|
|
380
458
|
pass.end();
|
|
381
459
|
}
|
|
382
460
|
} else {
|
|
383
461
|
const bindGroup = device.createBindGroup({
|
|
384
|
-
layout: blitPipeline
|
|
462
|
+
layout: blitPipeline!.getBindGroupLayout(0),
|
|
385
463
|
entries: [
|
|
386
464
|
{ binding: 0, resource: currentInput },
|
|
387
465
|
{ binding: 1, resource: sampler },
|
|
@@ -399,7 +477,7 @@ export function createPostProcessNode(config: PostProcessConfig): ComputeNode {
|
|
|
399
477
|
],
|
|
400
478
|
});
|
|
401
479
|
|
|
402
|
-
pass.setPipeline(blitPipeline);
|
|
480
|
+
pass.setPipeline(blitPipeline!);
|
|
403
481
|
pass.setBindGroup(0, bindGroup);
|
|
404
482
|
pass.draw(3);
|
|
405
483
|
pass.end();
|
|
@@ -413,9 +491,9 @@ export function createPostProcessNode(config: PostProcessConfig): ComputeNode {
|
|
|
413
491
|
format,
|
|
414
492
|
width,
|
|
415
493
|
height,
|
|
416
|
-
sceneView,
|
|
494
|
+
sceneView: colorView,
|
|
417
495
|
depthView,
|
|
418
|
-
entityIdView,
|
|
496
|
+
entityIdView: eidView,
|
|
419
497
|
maskView,
|
|
420
498
|
canvasView,
|
|
421
499
|
};
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import type { Vec3, Ray } from "./bvh/structs";
|
|
2
|
+
|
|
3
|
+
export type { Ray };
|
|
4
|
+
|
|
5
|
+
const DEG_TO_RAD = Math.PI / 180;
|
|
6
|
+
|
|
7
|
+
export function generateRay(
|
|
8
|
+
screenX: number,
|
|
9
|
+
screenY: number,
|
|
10
|
+
width: number,
|
|
11
|
+
height: number,
|
|
12
|
+
fov: number,
|
|
13
|
+
near: number,
|
|
14
|
+
cameraWorld: Float32Array
|
|
15
|
+
): Ray {
|
|
16
|
+
const ndcX = screenX * 2 - 1;
|
|
17
|
+
const ndcY = 1 - screenY * 2;
|
|
18
|
+
|
|
19
|
+
const aspect = width / height;
|
|
20
|
+
const tanHalfFov = Math.tan((fov * DEG_TO_RAD) / 2);
|
|
21
|
+
|
|
22
|
+
const camDirX = ndcX * aspect * tanHalfFov;
|
|
23
|
+
const camDirY = ndcY * tanHalfFov;
|
|
24
|
+
const camDirZ = -1;
|
|
25
|
+
|
|
26
|
+
const r00 = cameraWorld[0];
|
|
27
|
+
const r10 = cameraWorld[1];
|
|
28
|
+
const r20 = cameraWorld[2];
|
|
29
|
+
const r01 = cameraWorld[4];
|
|
30
|
+
const r11 = cameraWorld[5];
|
|
31
|
+
const r21 = cameraWorld[6];
|
|
32
|
+
const r02 = cameraWorld[8];
|
|
33
|
+
const r12 = cameraWorld[9];
|
|
34
|
+
const r22 = cameraWorld[10];
|
|
35
|
+
|
|
36
|
+
let dirX = r00 * camDirX + r01 * camDirY + r02 * camDirZ;
|
|
37
|
+
let dirY = r10 * camDirX + r11 * camDirY + r12 * camDirZ;
|
|
38
|
+
let dirZ = r20 * camDirX + r21 * camDirY + r22 * camDirZ;
|
|
39
|
+
|
|
40
|
+
const len = Math.sqrt(dirX * dirX + dirY * dirY + dirZ * dirZ);
|
|
41
|
+
dirX /= len;
|
|
42
|
+
dirY /= len;
|
|
43
|
+
dirZ /= len;
|
|
44
|
+
|
|
45
|
+
const originX = cameraWorld[12];
|
|
46
|
+
const originY = cameraWorld[13];
|
|
47
|
+
const originZ = cameraWorld[14];
|
|
48
|
+
|
|
49
|
+
return {
|
|
50
|
+
origin: {
|
|
51
|
+
x: originX + dirX * near,
|
|
52
|
+
y: originY + dirY * near,
|
|
53
|
+
z: originZ + dirZ * near,
|
|
54
|
+
},
|
|
55
|
+
direction: {
|
|
56
|
+
x: dirX,
|
|
57
|
+
y: dirY,
|
|
58
|
+
z: dirZ,
|
|
59
|
+
},
|
|
60
|
+
};
|
|
61
|
+
}
|