Skip to content

Commit c43cd0e

Browse files
committed
days of webgl madness later
1 parent 4662144 commit c43cd0e

5 files changed

Lines changed: 306 additions & 76 deletions

File tree

public/shader.fs

Lines changed: 2 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,28 +1,9 @@
11
#version 300 es
22
precision highp float;
33

4-
in vec3 v_barycentric;
54
in vec3 v_color;
6-
7-
out vec4 fragColor;
8-
9-
// Controls how thick the wireframe edges are
10-
uniform float u_lineWidth; // e.g. 0.05
11-
12-
// Helper: find min distance to an edge
13-
float edgeFactor() {
14-
vec3 d = fwidth(v_barycentric);
15-
vec3 a3 = smoothstep(vec3(0.0), d * u_lineWidth, v_barycentric);
16-
return min(min(a3.x, a3.y), a3.z);
17-
}
5+
out vec4 outColor;
186

197
void main() {
20-
float edge = edgeFactor();
21-
22-
// edge ~ 0 near edges, ~1 inside face
23-
if (edge < 0.5) {
24-
fragColor = vec4(v_color, 1.0); // draw edge
25-
} else {
26-
discard; // transparent background
27-
}
8+
outColor = vec4(v_color, 1.0);
289
}

public/shader.vs

Lines changed: 11 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,30 +1,19 @@
11
#version 300 es
2-
precision highp float;
2+
in vec3 position;
3+
in vec3 instanceCenter;
4+
in vec3 instanceColor;
5+
in float instanceSize;
36

4-
// Cube vertex position (object space)
5-
in vec3 a_position;
6-
7-
// Barycentric coordinates for wireframe effect
8-
in vec3 a_barycentric;
9-
10-
// Instance attributes
11-
uniform mat4 u_projection;
127
uniform mat4 u_view;
13-
uniform mat4 u_model; // base model transform for cube
14-
uniform vec3 u_cubeOffset; // per-instance offset (grid position)
15-
uniform float u_cubeScale; // per-instance scale
16-
uniform vec3 u_cubeColor; // per-instance color
8+
uniform mat4 u_proj;
179

18-
// Pass to fragment shader
19-
out vec3 v_barycentric;
2010
out vec3 v_color;
2111

22-
void main() {
23-
// Apply scale and offset
24-
vec3 pos = a_position * u_cubeScale + u_cubeOffset;
12+
// TODO: test more math out in here using gl_InstanceID, see if that gets
13+
// more performance.
2514

26-
gl_Position = u_projection * u_view * u_model * vec4(pos, 1.0);
27-
28-
v_barycentric = a_barycentric;
29-
v_color = u_cubeColor;
15+
void main() {
16+
vec3 scaledPos = position * instanceSize + instanceCenter;
17+
gl_Position = u_proj * u_view * vec4(scaledPos, 1.0);
18+
v_color = instanceColor;
3019
}

src/bg-anim/bg-main.ts

Lines changed: 141 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,34 @@
1-
import * as twgl from "twgl.js"
1+
import * as twgl from "twgl.js";
2+
3+
const vertShaderSrc = `#version 300 es
4+
in vec3 position;
5+
in vec3 instanceCenter;
6+
in vec3 instanceColor;
7+
in float instanceSize;
8+
9+
uniform mat4 u_view;
10+
uniform mat4 u_proj;
11+
12+
out vec3 v_color;
13+
14+
// TODO: test more math out in here using gl_InstanceID, see if that gets
15+
// more performance.
16+
17+
void main() {
18+
vec3 scaledPos = position * instanceSize + instanceCenter;
19+
gl_Position = u_proj * u_view * vec4(scaledPos, 1.0);
20+
v_color = instanceColor;
21+
}`;
22+
23+
const fragShaderSrc = `#version 300 es
24+
precision highp float;
25+
26+
in vec3 v_color;
27+
out vec4 outColor;
28+
29+
void main() {
30+
outColor = vec4(v_color, 1.0);
31+
}`;
232

333
const CENTER = twgl.v3.create(0, 0, 0);
434
const EPSILON = 1e-6; // from raymath
@@ -56,7 +86,7 @@ const Direction = {
5686

5787
type DirVals = typeof Direction[keyof typeof Direction];
5888

59-
type Freelist = { head: number, tail: number }
89+
type Freelist = [head: number, tail: number];
6090

6191
// ------------------------------------------
6292

@@ -86,16 +116,16 @@ const initFreelist = (frie: Freelist, bullets: PointBufferArray) => {
86116
for (let i = 0; i < BULLET_COUNT - 1; i++)
87117
bullets.nextFreeOrSpawned[i] = i + 1;
88118
bullets.nextFreeOrSpawned[BULLET_COUNT - 1] = LIST_END;
89-
frie.head = 0;
90-
frie.tail = BULLET_COUNT - 1;
119+
frie[0] = 0;
120+
frie[1] = BULLET_COUNT - 1;
91121
};
92122

93123
const spawnPoint = (freelist: Freelist, points: PointBufferArray) => {
94-
if (freelist.head == LIST_END) return LIST_END;
95-
const idx = freelist.head;
96-
freelist.head = points.nextFreeOrSpawned[idx];
97-
if (freelist.head == LIST_END)
98-
freelist.tail = LIST_END;
124+
if (freelist[0] == LIST_END) return LIST_END;
125+
const idx = freelist[0];
126+
freelist[0] = points.nextFreeOrSpawned[idx];
127+
if (freelist[0] == LIST_END)
128+
freelist[1] = LIST_END;
99129
points.nextFreeOrSpawned[idx] = IS_SPAWNED;
100130

101131
const [bx, by, bz] = getVec3Idx(idx);
@@ -105,7 +135,7 @@ const spawnPoint = (freelist: Freelist, points: PointBufferArray) => {
105135
points.colors[by] = (0x51 / 255) * lerpA + (0x0C / 255) * lerpB;
106136
points.colors[bz] = (0x08 / 255) * lerpA + (0xCF / 255) * lerpB;
107137

108-
points.directions[idx] = (1 << (Math.floor(Math.random() * Direction.LEN))) as DirVals;
138+
points.directions[idx] = 1 << (Math.floor(Math.random() * Direction.LEN)) as DirVals;
109139
points.signs[idx] = points.directions[idx] & (Direction.PX | Direction.PY | Direction.PZ) ? 1 : -1;
110140
points.activeIdx[idx] = points.directions[idx] & (Direction.PX | Direction.NX) ? bx
111141
: points.directions[idx] & (Direction.PY | Direction.NY) ? by : bz;
@@ -130,18 +160,19 @@ const freeBullet = (freelist: Freelist, points: PointBufferArray, idx: number) =
130160
points.positions[vy] = Number.MAX_VALUE;
131161
points.positions[vz] = Number.MAX_VALUE;
132162
points.nextFreeOrSpawned[idx] = LIST_END;
133-
if (freelist.tail != LIST_END)
134-
points.nextFreeOrSpawned[freelist.tail] = idx;
163+
if (freelist[1] != LIST_END)
164+
points.nextFreeOrSpawned[freelist[1]] = idx;
135165
else
136-
freelist.head = idx;
137-
freelist.tail = idx;
166+
freelist[0] = idx;
167+
freelist[1] = idx;
138168
};
139169

140170
const worldToIndex = (coord: number, basePos: number, maxIdx: number) => {
141171
// NOTE: idk why ceilf had to be used here but it fixed an off by -1 offset
142172
// issue.
143173
const ret = Math.ceil((coord - basePos) / (CUBE_SIZE + CUBE_PADDING));
144174
return ret > maxIdx ? maxIdx : ret < 0 ? 0 : ret;
175+
// todo: revisit this to see what's up, why it broke, so it just naturally gives the correct val at correct range without clamping
145176
}
146177

147178
const getBulletBoundingBox = (points: PointBufferArray, idx: number) => {
@@ -176,7 +207,7 @@ const isOutOfBounds = (bullets: PointBufferArray, idx: number) => {
176207
}
177208
};
178209

179-
// ------------ Raylib ports ---------------
210+
// ------------ rendering fns ---------------
180211

181212
type Vector3 = { x: number, y: number, z: number };
182213
type Camera = {
@@ -193,12 +224,66 @@ const UpdateCamera = (camera: Camera) => {
193224

194225
// -----------------------------------------
195226

196-
const main = () => {
227+
export const main = async () => {
197228
const gl = (document.getElementById("bg-canvas") as HTMLCanvasElement).getContext("webgl2");
198229
if (!gl) return;
230+
twgl
231+
// todo: upload cubePositions to VAO.
232+
233+
// todo: constants
234+
let spawnTimer = PRNG.nextRange(0, 1);
235+
236+
const maxInstances = BULLET_COUNT * 100; // safe upper bound
237+
238+
// ------ gl setup ------
239+
const progInfo = twgl.createProgramInfo(gl, [vertShaderSrc, fragShaderSrc]);
240+
const instanceCenters = new Float32Array(maxInstances * 3);
241+
const instanceColors = new Float32Array(maxInstances * 3);
242+
const instanceSizes = new Float32Array(maxInstances);
243+
const cubeBufferInfo = twgl.createBufferInfoFromArrays(gl, {
244+
position: {
245+
numComponents: 3,
246+
data: [
247+
// 8 corners
248+
-0.5, -0.5, -0.5, // 0
249+
0.5, -0.5, -0.5, // 1
250+
0.5, 0.5, -0.5, // 2
251+
-0.5, 0.5, -0.5, // 3
252+
-0.5, -0.5, 0.5, // 4
253+
0.5, -0.5, 0.5, // 5
254+
0.5, 0.5, 0.5, // 6
255+
-0.5, 0.5, 0.5, // 7
256+
]
257+
},
258+
indices: [
259+
// bottom face
260+
0, 1, 1, 2, 2, 3, 3, 0,
261+
// top face
262+
4, 5, 5, 6, 6, 7, 7, 4,
263+
// vertical edges
264+
0, 4, 1, 5, 2, 6, 3, 7,
265+
],
266+
instanceCenter: {
267+
numComponents: 3,
268+
data: instanceCenters,
269+
divisor: 1,
270+
},
271+
instanceColor: {
272+
numComponents: 3,
273+
data: instanceColors,
274+
divisor: 1,
275+
},
276+
instanceSize: {
277+
numComponents: 1,
278+
data: instanceSizes,
279+
divisor: 1,
280+
}
281+
});
282+
283+
const vao = twgl.createVAOAndSetAttributes(gl, progInfo.attribSetters, cubeBufferInfo.attribs!, cubeBufferInfo.indices);
199284

200285
const points = new PointBufferArray(BULLET_COUNT);
201-
const freelist: Freelist = { head: LIST_END, tail: LIST_END };
286+
const freelist: Freelist = [LIST_END, LIST_END];
202287
initFreelist(freelist, points);
203288

204289
const cubePositions = new Float32Array(CUBES_COUNT * 3);
@@ -218,29 +303,21 @@ const main = () => {
218303
}
219304
refPos[2] += CUBE_SIZE + CUBE_PADDING;
220305
}
221-
// todo: upload cubePositions to VAO.
222-
223-
// todo: constants
224-
let spawnTimer = PRNG.nextRange(0, 1);
225-
226-
// ------ gl setup ------
227-
const progInfo = twgl.createProgramInfo(gl, ["shader.vs", "shader.fs"]);
228306

229-
// todo/architecture: pass around "base index" (pointing to x component) instead of doing getVec3Indices everywhere
230307
const render = (dt: number) => {
231308
if ((spawnTimer -= dt) <= 0) {
232309
spawnPoint(freelist, points);
233310
spawnTimer = PRNG.nextRange(0, 1); // todo: constants
234311
}
235312

313+
const cubeData: { centerIdx: number, colorIdx: number, size: number }[] = [];
236314
// todo: UpdateCamera (orbital)
237315
for (let i = 0; i < BULLET_COUNT; i++) {
238316
if (points.nextFreeOrSpawned[i] != IS_SPAWNED)
239317
continue;
240318
// debug: keep track of bullet count
241319
// bullet_count++;
242320

243-
const dir = points.directions[i] as DirVals;
244321
points.positions[points.activeIdx[i]] += dt * points.signs[i] * points.speeds[i];
245322
if (isOutOfBounds(points, i)) {
246323
freeBullet(freelist, points, i);
@@ -250,8 +327,6 @@ const main = () => {
250327
// DrawSphereEx(points.positions[i], CUBE_SIZE / 8.0f, 4, 4,
251328
// ColorFromNormalized(points.colors[i]));
252329

253-
const cubeData: { centerIdx: number, colorIdx: number, size: number }[] = [];
254-
255330
const bbox = getBulletBoundingBox(points, i);
256331
const [vx, vy, vz] = getVec3Idx(i);
257332
for (let z = bbox.min_z; z < bbox.max_z; z++) {
@@ -272,19 +347,52 @@ const main = () => {
272347
}
273348
}
274349
}
350+
351+
// --- Fill instance arrays ---
352+
for (let i = 0; i < cubeData.length; i++) {
353+
if (i >= maxInstances) break;
354+
const c = cubeData[i];
355+
const offset = i * 3;
356+
cubeBufferInfo.attribs!["instanceCenter"];
357+
instanceCenters.set([
358+
cubePositions[c.centerIdx],
359+
cubePositions[c.centerIdx + 1],
360+
cubePositions[c.centerIdx + 2]
361+
], offset);
362+
instanceColors.set([
363+
points.colors[c.colorIdx],
364+
points.colors[c.colorIdx + 1],
365+
points.colors[c.colorIdx + 2]
366+
], offset);
367+
instanceSizes[i] = c.size;
368+
}
369+
// --- Upload updated instance data ---
370+
twgl.setAttribInfoBufferFromArray(gl, cubeBufferInfo.attribs!.instanceCenter, instanceCenters);
371+
twgl.setAttribInfoBufferFromArray(gl, cubeBufferInfo.attribs!.instanceColor, instanceColors);
372+
twgl.setAttribInfoBufferFromArray(gl, cubeBufferInfo.attribs!.instanceSize, instanceSizes);
373+
374+
// --- Draw ---
275375
twgl.resizeCanvasToDisplaySize(gl.canvas as HTMLCanvasElement);
276-
gl.viewport(0, 0, gl.canvas.width, gl.canvas.height);
376+
gl.viewport(0, 0, gl.drawingBufferWidth, gl.drawingBufferHeight);
377+
gl.useProgram(progInfo.program);
378+
gl.clearColor(0.1, 0.1, 0.1, 1.0);
379+
gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
380+
gl.enable(gl.DEPTH_TEST);
381+
382+
gl.bindVertexArray(vao);
383+
twgl.setUniforms(progInfo, {
384+
u_view: twgl.m4.lookAt([10, 10, 10], [0, 0, 0], [0, 1, 0]),
385+
u_proj: twgl.m4.perspective(Math.PI / 4, gl.canvas.width / gl.canvas.height, 0.1, 1000),
386+
});
387+
twgl.drawBufferInfo(gl, cubeBufferInfo, gl.TRIANGLES, cubeBufferInfo.numElements, 0, cubeData.length);
388+
gl.bindVertexArray(null);
277389

278390
requestAnimationFrame(render);
279391
};
280392

281393
requestAnimationFrame(render);
282-
283394
};
284395

285-
main();
286-
287-
288396
class PointBufferArray {
289397
private data: ArrayBuffer;
290398
// data layout: positions(vec3float), colors(vec3float), scales(vec3float), speeds(int32), nextFreeOrSpawned(int32), directions(uint8)

0 commit comments

Comments
 (0)