Skip to content

Commit bf8511e

Browse files
committed
depth for tunnel
1 parent 6763a54 commit bf8511e

8 files changed

Lines changed: 93 additions & 57 deletions

File tree

src/canvasWebGPU.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { ColorsRGB } from './colors';
2-
import { CANVAS_SIZE } from './constants';
2+
import { CANVAS_SIZE, SUZANNE_SCALE } from './constants';
33
import { Mat4x4 } from './mat4x4';
44
import { VERTICES_PER_SQUARE } from './tunnel/square';
55
import { TunnelState } from './tunnel/tunnel';
@@ -18,7 +18,7 @@ export async function setupCanvasWebGPU(
1818

1919
const gpuContext = await initGPUContext(element);
2020

21-
const suzanneRenderer = SolidRenderer.init(shape, gpuContext);
21+
const suzanneRenderer = SolidRenderer.init(shape, gpuContext, SUZANNE_SCALE);
2222

2323
// Tunnel setup
2424
const tunnelConfig = DEFAULT_TUNNEL_CONFIG;

src/constants.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,4 +12,12 @@ export const SCREEN_DIMENSIONS: Dimensions = {
1212
export const FPS = 60;
1313
export const MILLIS_PER_FRAME = 1000 / FPS;
1414

15+
export const SUZANNE_SCALE = 0.5;
16+
export const SUZANNE_SPEED = { x: 1.8, y: 1.4 };
17+
export const SUZANNE_BOUNDS = 0.78;
18+
export const SUZANNE_INITIAL_Z = 4.0;
19+
export const SUZANNE_ROTATION_AXIS = { x: 0, y: -1, z: 0 };
20+
export const RAMP_DELAY = 2.0;
21+
export const RAMP_DURATION = 5.0;
22+
1523
export const ID_WEBGPU_TARGET = 'canvas-webgpu';

src/main.ts

Lines changed: 18 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,15 @@
11
import { setupCanvasWebGPU } from './canvasWebGPU';
2-
import { FPS, ID_WEBGPU_TARGET, MILLIS_PER_FRAME } from './constants';
2+
import {
3+
FPS,
4+
ID_WEBGPU_TARGET,
5+
MILLIS_PER_FRAME,
6+
RAMP_DELAY,
7+
RAMP_DURATION,
8+
SUZANNE_BOUNDS,
9+
SUZANNE_INITIAL_Z,
10+
SUZANNE_ROTATION_AXIS,
11+
SUZANNE_SPEED,
12+
} from './constants';
313
import { Mat4x4 } from './mat4x4';
414
import SUZANNE from './suzanne.json';
515
import './style.css';
@@ -32,16 +42,11 @@ const drawCanvasWebGPU = await setupCanvasWebGPU(canvasWebGPU, SUZANNE);
3242
// DRAWING //
3343
/////////////
3444

35-
const BOUNDS = 3.0;
36-
const ROTATION_AXIS = { x: 0, y: -1, z: 0 };
37-
const RAMP_DELAY = 2.0;
38-
const RAMP_DURATION = 5.0;
39-
40-
let vx = 2.8;
41-
let vy = 2.0;
45+
let vx = SUZANNE_SPEED.x;
46+
let vy = SUZANNE_SPEED.y;
4247
let dx = 0;
4348
let dy = 0;
44-
let dz = 4.0;
49+
let dz = SUZANNE_INITIAL_Z;
4550
let zPhase = 0;
4651
let angle = Math.PI;
4752
let elapsed = 0;
@@ -55,18 +60,18 @@ function draw() {
5560
zPhase = (zPhase + dt * 0.3 * ramp) % (2 * Math.PI);
5661
dx += vx * dt * ramp;
5762
dy += vy * dt * ramp;
58-
dz = 4.0 + Math.sin(zPhase) * 0.5;
63+
dz = SUZANNE_INITIAL_Z + Math.sin(zPhase) * 2.0;
5964

60-
if (dx > BOUNDS || dx < -BOUNDS) {
65+
if (dx > SUZANNE_BOUNDS || dx < -SUZANNE_BOUNDS) {
6166
vx = -vx;
6267
}
63-
if (dy > BOUNDS || dy < -BOUNDS) {
68+
if (dy > SUZANNE_BOUNDS || dy < -SUZANNE_BOUNDS) {
6469
vy = -vy;
6570
}
6671

6772
const modelTransformMatrix = Mat4x4.fromTransform(
6873
{ dx, dy, dz },
69-
{ axis: ROTATION_AXIS, angle },
74+
{ axis: SUZANNE_ROTATION_AXIS, angle },
7075
);
7176

7277
drawCanvasWebGPU(modelTransformMatrix, dt, ramp);

src/mat4x4.ts

Lines changed: 27 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -31,10 +31,10 @@ export class Mat4x4 {
3131

3232
// biome-ignore format: matrix layout
3333
m.mat = [
34-
f / aspect, 0, 0, 0,
35-
0, f, 0, 0,
36-
0, 0, far * nf, -near * far * nf,
37-
0, 0, 1, 0,
34+
f / aspect, 0, 0, 0,
35+
0, f, 0, 0,
36+
0, 0, far * nf, -near * far * nf,
37+
0, 0, 1, 0,
3838
];
3939
return m;
4040
}
@@ -77,6 +77,18 @@ export class Mat4x4 {
7777
return m;
7878
}
7979

80+
static scale(s: number): Mat4x4 {
81+
const m = new Mat4x4();
82+
// biome-ignore format: matrix layout
83+
m.mat = [
84+
s, 0, 0, 0,
85+
0, s, 0, 0,
86+
0, 0, s, 0,
87+
0, 0, 0, 1,
88+
];
89+
return m;
90+
}
91+
8092
static fromTransform(
8193
{ dx, dy, dz }: Translation,
8294
{ axis, angle }: { axis: Point3D; angle: number },
@@ -107,9 +119,9 @@ export class Mat4x4 {
107119

108120
// row-major access: m[row * 4 + col]
109121
return {
110-
x: m[0]! * x + m[1]! * y + m[2]! * z + m[3]!,
111-
y: m[4]! * x + m[5]! * y + m[6]! * z + m[7]!,
112-
z: m[8]! * x + m[9]! * y + m[10]! * z + m[11]!,
122+
x: m[0] * x + m[1] * y + m[2] * z + m[3],
123+
y: m[4] * x + m[5] * y + m[6] * z + m[7],
124+
z: m[8] * x + m[9] * y + m[10] * z + m[11],
113125
};
114126
}
115127

@@ -125,10 +137,10 @@ export class Mat4x4 {
125137
for (let row = 0; row < 4; row++) {
126138
for (let col = 0; col < 4; col++) {
127139
result.mat[row * 4 + col] =
128-
a[row * 4 + 0]! * b[0 * 4 + col]! +
129-
a[row * 4 + 1]! * b[1 * 4 + col]! +
130-
a[row * 4 + 2]! * b[2 * 4 + col]! +
131-
a[row * 4 + 3]! * b[3 * 4 + col]!;
140+
a[row * 4 + 0] * b[0 * 4 + col] +
141+
a[row * 4 + 1] * b[1 * 4 + col] +
142+
a[row * 4 + 2] * b[2 * 4 + col] +
143+
a[row * 4 + 3] * b[3 * 4 + col];
132144
}
133145
}
134146

@@ -142,10 +154,10 @@ export class Mat4x4 {
142154
const m = this.mat;
143155
// biome-ignore format: matrix layout
144156
return Float32Array.from([
145-
m[0]!, m[4]!, m[8]!, m[12]!,
146-
m[1]!, m[5]!, m[9]!, m[13]!,
147-
m[2]!, m[6]!, m[10]!, m[14]!,
148-
m[3]!, m[7]!, m[11]!, m[15]!,
157+
m[0], m[4], m[8], m[12],
158+
m[1], m[5], m[9], m[13],
159+
m[2], m[6], m[10], m[14],
160+
m[3], m[7], m[11], m[15],
149161
]);
150162
}
151163
}

src/tunnel/tunnel.ts

Lines changed: 12 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -73,25 +73,28 @@ export class TunnelState {
7373
sq.rotZ * t,
7474
]);
7575

76-
// Transform quad vertices: rotate, translate to z, perspective project
77-
const projected: { x: number; y: number }[] = [];
76+
// transform quad vertices: rotate, translate to z
77+
const worldVerts: { x: number; y: number; z: number }[] = [];
7878
for (const localVert of QUAD_VERTICES) {
7979
const scaled = {
8080
x: localVert.x * squareSize,
8181
y: localVert.y * squareSize,
8282
z: localVert.z,
8383
};
8484
const rotated = rotMatrix.multiplyPoint(scaled);
85-
const worldZ = rotated.z + sq.z;
86-
projected.push({ x: rotated.x / worldZ, y: rotated.y / worldZ });
85+
worldVerts.push({
86+
x: rotated.x,
87+
y: rotated.y,
88+
z: rotated.z + sq.z,
89+
});
8790
}
8891

89-
// Write edge vertices into output buffer
92+
// write edge vertices into output buffer
9093
for (const edgeIdx of QUAD_EDGE_INDICES) {
91-
const p = projected[edgeIdx]!;
92-
out[offset++] = p.x;
93-
out[offset++] = p.y;
94-
out[offset++] = 1.0; // z=1.0 so shader's x/z is a no-op
94+
const v = worldVerts[edgeIdx];
95+
out[offset++] = v.x;
96+
out[offset++] = v.y;
97+
out[offset++] = v.z;
9598
}
9699
}
97100

src/webGPU/shaders/wireframe.ts

Lines changed: 2 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,26 +1,13 @@
11
export const vertexShader = `\
22
struct Uniforms {
3-
transformMatrix: mat4x4<f32>,
3+
mvpMatrix: mat4x4<f32>,
44
}
55
66
@group(0) @binding(0) var<uniform> uniforms: Uniforms;
77
8-
// Project the 3D point onto the XY-plane (perspective projection)
9-
fn project(pos: vec3<f32>) -> vec2<f32> {
10-
return vec2<f32>(pos.x / pos.z, pos.y / pos.z);
11-
}
12-
13-
// Convert projected coordinates to clip space
14-
// z is set to 0.5 (middle of depth range), w is 1.0 (already perspective-divided)
15-
fn screen(ndc: vec2<f32>) -> vec4<f32> {
16-
return vec4<f32>(ndc.x, ndc.y, 0.5, 1.0);
17-
}
18-
198
@vertex
209
fn vs_main(@location(0) position: vec3<f32>) -> @builtin(position) vec4<f32> {
21-
let transformed = uniforms.transformMatrix * vec4<f32>(position, 1.0);
22-
let projected = project(transformed.xyz);
23-
return screen(projected);
10+
return uniforms.mvpMatrix * vec4<f32>(position, 1.0);
2411
}`;
2512

2613
export const fragmentShader = `\

src/webGPU/solid.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ export class SolidRenderer {
4141
static init(
4242
shape: IndexedShape,
4343
gpuContext: GPUContext,
44+
scale: number = 1,
4445
): SolidRenderer {
4546
const { device } = gpuContext;
4647

@@ -78,6 +79,15 @@ export class SolidRenderer {
7879
const triangleVertices = expandFacesToTriangles(shape);
7980
const vertexCount = triangleVertices.length / 6;
8081

82+
// Scale position components (stride 6: px, py, pz, nx, ny, nz)
83+
if (scale !== 1) {
84+
for (let i = 0; i < triangleVertices.length; i += 6) {
85+
triangleVertices[i] *= scale;
86+
triangleVertices[i + 1] *= scale;
87+
triangleVertices[i + 2] *= scale;
88+
}
89+
}
90+
8191
const vertexBuffer = device.createBuffer({
8292
label: 'solid_vertex_buffer',
8393
size: triangleVertices.byteLength,

src/webGPU/wireframe.ts

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import type { ColorRGB } from '../colors';
2-
import { CLEAR_COLOR, COLOR } from '../constants';
3-
import type { Mat4x4 } from '../mat4x4';
2+
import { CANVAS_SIZE, CLEAR_COLOR, COLOR } from '../constants';
3+
import { Mat4x4 } from '../mat4x4';
44
import { DEPTH_FORMAT, type GPUContext } from './gpuContext';
55
import { createPipeline } from './gpuUtils';
66
import { hexToRgb } from './hexToRgb';
@@ -19,6 +19,7 @@ type WireframeGPUConfig = {
1919
fragmentUniformBuffer: GPUBuffer;
2020
bindGroup: GPUBindGroup;
2121
vertexCount: number;
22+
projectionMatrix: Mat4x4;
2223
};
2324

2425
export type Frame = {
@@ -127,6 +128,13 @@ export class WireframeRenderer {
127128
],
128129
});
129130

131+
const projectionMatrix = Mat4x4.perspective(
132+
Math.PI / 2,
133+
CANVAS_SIZE / CANVAS_SIZE,
134+
0.1,
135+
100,
136+
);
137+
130138
return new WireframeRenderer({
131139
device,
132140
pipeline,
@@ -135,6 +143,7 @@ export class WireframeRenderer {
135143
fragmentUniformBuffer,
136144
bindGroup,
137145
vertexCount: 0,
146+
projectionMatrix,
138147
});
139148
}
140149

@@ -155,11 +164,13 @@ export class WireframeRenderer {
155164
fragmentUniformBuffer,
156165
bindGroup,
157166
vertexCount,
167+
projectionMatrix,
158168
} = this.config;
159169

160170
if (vertexCount === 0) return;
161171

162-
device.queue.writeBuffer(vertexUniformBuffer, 0, matrix.toFloat32Array());
172+
const mvpMatrix = projectionMatrix.multiply(matrix);
173+
device.queue.writeBuffer(vertexUniformBuffer, 0, mvpMatrix.toFloat32Array());
163174

164175
const colorArray = color
165176
? Float32Array.from(color)

0 commit comments

Comments
 (0)