Skip to content

Commit e6d7fc3

Browse files
committed
address feedback and add barycentric coordinate based lines
1 parent be43431 commit e6d7fc3

File tree

4 files changed

+228
-103
lines changed

4 files changed

+228
-103
lines changed

sample/wireframe/main.ts

Lines changed: 133 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
/* eslint-disable prettier/prettier */
22
import { mat4, mat3 } from 'wgpu-matrix';
3+
import { GUI } from 'dat.gui';
34
import { modelData } from './models';
5+
import { randElement, randColor } from './utils';
46
import solidColorLitWGSL from './solidColorLit.wgsl';
57
import wireframeWGSL from './wireframe.wgsl';
68

@@ -9,11 +11,11 @@ type TypedArrayView = Float32Array | Uint32Array;
911
function createBufferWithData(
1012
device: GPUDevice,
1113
data: TypedArrayView,
12-
usage: number
14+
usage: GPUBufferUsageFlags,
1315
) {
1416
const buffer = device.createBuffer({
1517
size: data.byteLength,
16-
usage: usage,
18+
usage,
1719
});
1820
device.queue.writeBuffer(buffer, 0, data);
1921
return buffer;
@@ -66,25 +68,6 @@ const depthFormat = 'depth24plus';
6668

6769
const models = Object.values(modelData).map(data => createVertexAndIndexBuffer(device, data));
6870

69-
function rand(min?: number, max?: number) {
70-
if (min === undefined) {
71-
max = 1;
72-
min = 0;
73-
} else if (max === undefined) {
74-
max = min;
75-
min = 0;
76-
}
77-
return Math.random() * (max - min) + min;
78-
}
79-
80-
function randInt(min: number, max?: number) {
81-
return Math.floor(rand(min, max));
82-
}
83-
84-
function randColor() {
85-
return [rand(), rand(), rand(), 1];
86-
}
87-
8871
const litModule = device.createShaderModule({
8972
code: solidColorLitWGSL,
9073
});
@@ -141,6 +124,7 @@ const wireframePipeline = device.createRenderPipeline({
141124
},
142125
fragment: {
143126
module: wireframeModule,
127+
entryPoint: 'fs',
144128
targets: [{ format: presentationFormat }],
145129
},
146130
primitive: {
@@ -153,13 +137,51 @@ const wireframePipeline = device.createRenderPipeline({
153137
},
154138
});
155139

140+
const barycentricCoordinatesBasedWireframePipeline = device.createRenderPipeline({
141+
label: 'barycentric coordinates based wireframe pipeline',
142+
layout: 'auto',
143+
vertex: {
144+
module: wireframeModule,
145+
entryPoint: 'vsIndexedU32BarycentricCoordinateBasedLines',
146+
},
147+
fragment: {
148+
module: wireframeModule,
149+
entryPoint: 'fsBarycentricCoordinateBasedLines',
150+
targets: [
151+
{
152+
format: presentationFormat,
153+
blend: {
154+
color: {
155+
srcFactor: 'one',
156+
dstFactor: 'one-minus-src-alpha',
157+
},
158+
alpha: {
159+
srcFactor: 'one',
160+
dstFactor: 'one-minus-src-alpha',
161+
},
162+
},
163+
},
164+
],
165+
},
166+
primitive: {
167+
topology: 'triangle-list',
168+
},
169+
depthStencil: {
170+
depthWriteEnabled: true,
171+
depthCompare: 'less-equal',
172+
format: depthFormat,
173+
},
174+
});
175+
156176
type ObjectInfo = {
157177
worldViewProjectionMatrixValue: Float32Array;
158178
worldMatrixValue: Float32Array;
159179
uniformValues: Float32Array;
160180
uniformBuffer: GPUBuffer;
181+
lineUniformValues: Float32Array;
182+
lineUniformBuffer: GPUBuffer;
161183
litBindGroup: GPUBindGroup;
162-
wireframeBindGroup: GPUBindGroup;
184+
wireframeBindGroups: GPUBindGroup[];
163185
model: Model;
164186
};
165187

@@ -187,29 +209,47 @@ for (let i = 0; i < numObjects; ++i) {
187209
const colorValue = uniformValues.subarray(kColorOffset, kColorOffset + 4);
188210
colorValue.set(randColor());
189211

190-
const model = models[randInt(models.length)];
212+
const model = randElement(models);
191213

192214
// Make a bind group for this uniform
193215
const litBindGroup = device.createBindGroup({
194216
layout: litPipeline.getBindGroupLayout(0),
195217
entries: [{ binding: 0, resource: { buffer: uniformBuffer } }],
196218
});
197219

198-
const strideValues = new Uint32Array(1 + 3);
199-
const strideBuffer = device.createBuffer({
200-
size: strideValues.byteLength,
220+
// Note: We're making one lineUniformBuffer per object.
221+
// This is only because stride might be different per object.
222+
// In this sample stride is the same across all objects so
223+
// we could have made just a single shared uniform buffer for
224+
// these settings.
225+
const lineUniformValues = new Float32Array(3 + 1);
226+
const lineUniformValuesAsU32 = new Uint32Array(lineUniformValues.buffer);
227+
const lineUniformBuffer = device.createBuffer({
228+
size: lineUniformValues.byteLength,
201229
usage: GPUBufferUsage.UNIFORM | GPUBufferUsage.COPY_DST,
202230
});
203-
strideValues[0] = 6;
204-
device.queue.writeBuffer(strideBuffer, 0, strideValues);
231+
lineUniformValuesAsU32[0] = 6; // the array stride for positions for this model.
205232

233+
// We're creating 2 bindGroups, one for each pipeline.
234+
// We could create just one since they are identical. To do
235+
// so we'd have to manually create a bindGroupLayout.
206236
const wireframeBindGroup = device.createBindGroup({
207237
layout: wireframePipeline.getBindGroupLayout(0),
208238
entries: [
209239
{ binding: 0, resource: { buffer: uniformBuffer } },
210240
{ binding: 1, resource: { buffer: model.vertexBuffer } },
211241
{ binding: 2, resource: { buffer: model.indexBuffer } },
212-
{ binding: 3, resource: { buffer: strideBuffer } },
242+
{ binding: 3, resource: { buffer: lineUniformBuffer } },
243+
],
244+
});
245+
246+
const barycentricCoordinatesBasedWireframeBindGroup = device.createBindGroup({
247+
layout: barycentricCoordinatesBasedWireframePipeline.getBindGroupLayout(0),
248+
entries: [
249+
{ binding: 0, resource: { buffer: uniformBuffer } },
250+
{ binding: 1, resource: { buffer: model.vertexBuffer } },
251+
{ binding: 2, resource: { buffer: model.indexBuffer } },
252+
{ binding: 3, resource: { buffer: lineUniformBuffer } },
213253
],
214254
});
215255

@@ -218,8 +258,13 @@ for (let i = 0; i < numObjects; ++i) {
218258
worldMatrixValue,
219259
uniformValues,
220260
uniformBuffer,
261+
lineUniformValues,
262+
lineUniformBuffer,
221263
litBindGroup,
222-
wireframeBindGroup,
264+
wireframeBindGroups: [
265+
wireframeBindGroup,
266+
barycentricCoordinatesBasedWireframeBindGroup,
267+
],
223268
model,
224269
});
225270
}
@@ -242,7 +287,45 @@ const renderPassDescriptor: GPURenderPassDescriptor = {
242287
},
243288
};
244289

245-
let depthTexture;
290+
const settings = {
291+
barycentricCoordinatesBased: false,
292+
thickness: 2,
293+
alphaThreshold: 0.5,
294+
lines: true,
295+
models: true,
296+
};
297+
298+
const gui = new GUI();
299+
gui.add(settings, 'barycentricCoordinatesBased').onChange(addRemoveGUI);
300+
gui.add(settings, 'lines');
301+
gui.add(settings, 'models');
302+
303+
const guis = [];
304+
function addRemoveGUI() {
305+
if (settings.barycentricCoordinatesBased) {
306+
guis.push(
307+
gui.add(settings, 'thickness', 0.0, 10).onChange(updateThickness),
308+
gui.add(settings, 'alphaThreshold', 0, 1).onChange(updateThickness),
309+
);
310+
} else {
311+
guis.forEach(g => g.remove());
312+
guis.length = 0;
313+
}
314+
}
315+
316+
function updateThickness() {
317+
objectInfos.forEach(({
318+
lineUniformBuffer,
319+
lineUniformValues,
320+
}) => {
321+
lineUniformValues[1] = settings.thickness;
322+
lineUniformValues[2] = settings.alphaThreshold;
323+
device.queue.writeBuffer(lineUniformBuffer, 0, lineUniformValues);
324+
});
325+
}
326+
updateThickness();
327+
328+
let depthTexture: GPUTexture | undefined;
246329

247330
function render(time: number) {
248331
time *= 0.001; // convert to seconds;
@@ -311,20 +394,27 @@ function render(time: number) {
311394
// Upload our uniform values.
312395
device.queue.writeBuffer(uniformBuffer, 0, uniformValues);
313396

314-
pass.setVertexBuffer(0, vertexBuffer);
315-
pass.setIndexBuffer(indexBuffer, indexFormat);
316-
pass.setBindGroup(0, litBindGroup);
317-
pass.drawIndexed(vertexCount);
397+
if (settings.models) {
398+
pass.setVertexBuffer(0, vertexBuffer);
399+
pass.setIndexBuffer(indexBuffer, indexFormat);
400+
pass.setBindGroup(0, litBindGroup);
401+
pass.drawIndexed(vertexCount);
402+
}
318403
});
319404

320-
objectInfos.forEach(({
321-
wireframeBindGroup,
322-
model: { vertexCount },
323-
}) => {
324-
pass.setPipeline(wireframePipeline);
325-
pass.setBindGroup(0, wireframeBindGroup)
326-
pass.draw(vertexCount * 2);
327-
});
405+
if (settings.lines) {
406+
// Note: If we're using the line-list based pipeline then we need to
407+
// multiply the vertex count by 2 since we need to emit 6 vertices
408+
// for each triangle (3 edges).
409+
const [bindGroupNdx, countMult, pipeline] = settings.barycentricCoordinatesBased
410+
? [1, 1, barycentricCoordinatesBasedWireframePipeline]
411+
: [0, 2, wireframePipeline];
412+
pass.setPipeline(pipeline);
413+
objectInfos.forEach(({ wireframeBindGroups, model: { vertexCount } }) => {
414+
pass.setBindGroup(0, wireframeBindGroups[bindGroupNdx]);
415+
pass.draw(vertexCount * countMult);
416+
});
417+
}
328418

329419
pass.end();
330420

sample/wireframe/meta.ts

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,12 @@
11
export default {
22
name: 'Wireframe',
33
description: `
4-
This example demonstrates drawing a wireframe from triangles by using the
5-
vertex buffers as storage buffers and then using \`@builtin(vertex_index)\`
6-
to index the vertex data to generate 3 lines per triangle. It uses
7-
array<f32> in the vertex shader so it can pull out \`vec3f\` positions
8-
at any valid stride.
4+
This example demonstrates drawing a wireframe from triangles in 2 ways.
5+
Both use the vertex and index buffers as storage buffers and the use \`@builtin(vertex_index)\`
6+
to index the vertex data. One method generates 6 vertices per triangle and uses line-list to draw lines.
7+
The other method draws triangles with a fragment shader that uses barycentric coordinates to draw edges.
8+
as detailed [here](https://web.archive.org/web/20130424093557/http://codeflow.org/entries/2012/aug/02/easy-wireframe-display-with-barycentric-coordinates/).
9+
910
`,
1011
filename: __DIRNAME__,
1112
sources: [

sample/wireframe/utils.ts

Lines changed: 22 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -184,22 +184,25 @@ export const create3DRenderPipeline = (
184184
return device.createRenderPipeline(pipelineDescriptor);
185185
};
186186

187-
export const createTextureFromImage = (
188-
device: GPUDevice,
189-
bitmap: ImageBitmap
190-
) => {
191-
const texture: GPUTexture = device.createTexture({
192-
size: [bitmap.width, bitmap.height, 1],
193-
format: 'rgba8unorm',
194-
usage:
195-
GPUTextureUsage.TEXTURE_BINDING |
196-
GPUTextureUsage.COPY_DST |
197-
GPUTextureUsage.RENDER_ATTACHMENT,
198-
});
199-
device.queue.copyExternalImageToTexture(
200-
{ source: bitmap },
201-
{ texture: texture },
202-
[bitmap.width, bitmap.height]
203-
);
204-
return texture;
205-
};
187+
export function rand(min?: number, max?: number) {
188+
if (min === undefined) {
189+
max = 1;
190+
min = 0;
191+
} else if (max === undefined) {
192+
max = min;
193+
min = 0;
194+
}
195+
return Math.random() * (max - min) + min;
196+
}
197+
198+
export function randInt(min: number, max?: number) {
199+
return Math.floor(rand(min, max));
200+
}
201+
202+
export function randColor() {
203+
return [rand(), rand(), rand(), 1];
204+
}
205+
206+
export function randElement<T>(arr: T[]): T {
207+
return arr[randInt(arr.length)];
208+
}

0 commit comments

Comments
 (0)