Skip to content

Commit 0022be2

Browse files
committed
chore: use webgl2
1 parent a411eb9 commit 0022be2

File tree

2 files changed

+183
-11
lines changed

2 files changed

+183
-11
lines changed

playground/src/lib/Bitmap.svelte

Lines changed: 177 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,189 @@
11
<script lang="ts">
22
import type { HTMLCanvasAttributes } from 'svelte/elements';
3-
import { rgb2rgba } from './rgb2rgba';
4-
type Props = Omit<HTMLCanvasAttributes, 'width' | 'height'> & {
3+
type Props = HTMLCanvasAttributes & {
54
data: Uint8Array;
65
width: number;
76
height: number;
87
};
98
let { data, width, height, ...props }: Props = $props();
109
let canvas: HTMLCanvasElement | undefined = $state();
10+
1111
$effect(() => {
12-
const ctx = canvas?.getContext('2d');
13-
if (!ctx) return;
14-
const imageData = ctx.createImageData(width, height);
15-
imageData.data.set(rgb2rgba(data));
16-
ctx.putImageData(imageData, 0, 0);
12+
if (!canvas) return;
13+
const gl = canvas.getContext('webgl2');
14+
if (!gl) return;
15+
16+
const vertexShaderSource = `\
17+
#version 300 es
18+
19+
// an attribute is an input (in) to a vertex shader.
20+
// It will receive data from a buffer
21+
in vec2 a_position;
22+
23+
in vec2 a_texCoord;
24+
25+
// Used to pass in the resolution of the canvas
26+
uniform vec2 u_resolution;
27+
28+
out vec2 v_texCoord;
29+
30+
// all shaders have a main function
31+
void main() {
32+
// convert the position from pixels to 0.0 to 1.0
33+
vec2 zeroToOne = a_position / u_resolution;
34+
35+
// convert from 0->1 to 0->2
36+
vec2 zeroToTwo = zeroToOne * 2.0;
37+
38+
// convert from 0->2 to -1->+1 (clipspace)
39+
vec2 clipSpace = zeroToTwo - 1.0;
40+
41+
// invert y axis
42+
clipSpace.y *= -1.0;
43+
44+
gl_Position = vec4(clipSpace, 0, 1);
45+
46+
// pass the texCoord to the fragment shader
47+
// The GPU will interpolate this value between points
48+
v_texCoord = a_texCoord;
49+
}
50+
`;
51+
52+
const fragmentShaderSource = `\
53+
#version 300 es
54+
precision highp float;
55+
56+
// our texture
57+
uniform sampler2D u_image;
58+
59+
// the texCoords passed in from the vertex shader.
60+
in vec2 v_texCoord;
61+
62+
// we need to declare an output for the fragment shader
63+
out vec4 outColor;
64+
65+
void main() {
66+
// Look up a color from the texture.
67+
outColor = texture(u_image, v_texCoord);
68+
}`;
69+
const vertexShader = gl.createShader(gl.VERTEX_SHADER);
70+
if (!vertexShader) return;
71+
gl.shaderSource(vertexShader, vertexShaderSource);
72+
gl.compileShader(vertexShader);
73+
const fragmentShader = gl.createShader(gl.FRAGMENT_SHADER);
74+
if (!fragmentShader) return;
75+
gl.shaderSource(fragmentShader, fragmentShaderSource);
76+
gl.compileShader(fragmentShader);
77+
78+
const program = gl.createProgram();
79+
gl.attachShader(program, vertexShader);
80+
gl.attachShader(program, fragmentShader);
81+
gl.linkProgram(program);
82+
83+
// look up where the vertex data needs to go.
84+
const positionAttributeLocation = gl.getAttribLocation(program, 'a_position');
85+
const texCoordAttributeLocation = gl.getAttribLocation(program, 'a_texCoord');
86+
87+
// look up uniform locations
88+
const resolutionUniformLocation = gl.getUniformLocation(program, 'u_resolution');
89+
const imageLocation = gl.getUniformLocation(program, 'u_image');
90+
91+
// Create a buffer and put a single pixel space rectangle in
92+
// it (2 triangles)
93+
// Create a buffer and put three 2d clip space points in it
94+
const positionBuffer = gl.createBuffer();
95+
96+
// Bind it to ARRAY_BUFFER (think of it as ARRAY_BUFFER = positionBuffer)
97+
gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer);
98+
99+
// Create a vertex array object (attribute state)
100+
const vao = gl.createVertexArray();
101+
102+
// and make it the one we're currently working with
103+
gl.bindVertexArray(vao);
104+
105+
// Turn on the attribute
106+
gl.enableVertexAttribArray(positionAttributeLocation);
107+
108+
// Tell the attribute how to get data out of positionBuffer (ARRAY_BUFFER)
109+
gl.vertexAttribPointer(positionAttributeLocation, 2, gl.FLOAT, false, 0, 0);
110+
111+
// Tell WebGL how to convert from clip space to pixels
112+
gl.viewport(0, 0, gl.canvas.width, gl.canvas.height);
113+
114+
// Clear the canvas
115+
gl.clearColor(0, 0, 0, 0);
116+
gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
117+
118+
// provide texture coordinates for the rectangle.
119+
const texCoordBuffer = gl.createBuffer();
120+
gl.bindBuffer(gl.ARRAY_BUFFER, texCoordBuffer);
121+
gl.bufferData(
122+
gl.ARRAY_BUFFER,
123+
new Float32Array([0.0, 0.0, 1.0, 0.0, 0.0, 1.0, 0.0, 1.0, 1.0, 0.0, 1.0, 1.0]),
124+
gl.STATIC_DRAW
125+
);
126+
gl.enableVertexAttribArray(texCoordAttributeLocation);
127+
gl.vertexAttribPointer(texCoordAttributeLocation, 2, gl.FLOAT, false, 0, 0);
128+
129+
// Create a texture.
130+
const texture = gl.createTexture();
131+
132+
// make unit 0 the active texture uint
133+
// (ie, the unit all other texture commands will affect
134+
gl.activeTexture(gl.TEXTURE0 + 0);
135+
136+
// Bind it to texture unit 0' 2D bind point
137+
gl.bindTexture(gl.TEXTURE_2D, texture);
138+
139+
// Set the parameters so we don't need mips and so we're not filtering
140+
// and we don't repeat
141+
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
142+
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
143+
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST);
144+
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);
145+
146+
// Upload the image into the texture.
147+
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGB, width, height, 0, gl.RGB, gl.UNSIGNED_BYTE, data);
148+
149+
// Tell it to use our program (pair of shaders)
150+
gl.useProgram(program);
151+
152+
// Bind the attribute/buffer set we want.
153+
gl.bindVertexArray(vao);
154+
155+
// Pass in the canvas resolution so we can convert from
156+
// pixels to clipspace in the shader
157+
gl.uniform2f(resolutionUniformLocation, gl.canvas.width, gl.canvas.height);
158+
159+
// Tell the shader to get the texture from texture unit 0
160+
gl.uniform1i(imageLocation, 0);
161+
162+
// Bind the position buffer so gl.bufferData that will be called
163+
// in setRectangle puts data in the position buffer
164+
gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer);
165+
166+
function setRectangle(
167+
gl: WebGL2RenderingContext,
168+
x: number,
169+
y: number,
170+
width: number,
171+
height: number
172+
) {
173+
const x1 = x;
174+
const x2 = x + width;
175+
const y1 = y;
176+
const y2 = y + height;
177+
gl.bufferData(
178+
gl.ARRAY_BUFFER,
179+
new Float32Array([x1, y1, x2, y1, x1, y2, x1, y2, x2, y1, x2, y2]),
180+
gl.STATIC_DRAW
181+
);
182+
}
183+
// Set a rectangle the same size as the image.
184+
setRectangle(gl, 0, 0, width, height);
185+
// draw
186+
gl.drawArrays(gl.TRIANGLES, 0, 6);
17187
});
18188
</script>
19189

playground/src/lib/loader/index.ts

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,10 @@ export function createLoader(): ComlinkLoader & Disposable {
99
const worker = new LoaderWorker();
1010
const comlink = Comlink.wrap<Loader>(worker);
1111
const dispose = () => worker.terminate();
12-
13-
Object.defineProperty(comlink, "dispose", { value: dispose });
14-
Object.defineProperty(comlink, Symbol.dispose, { value: dispose });
15-
return comlink as ComlinkLoader & Disposable;
12+
return new Proxy(comlink, {
13+
get(target, p, receiver) {
14+
if (p === "dispose" || p === Symbol.dispose) return dispose;
15+
return Reflect.get(target, p, receiver);
16+
},
17+
}) as ComlinkLoader & Disposable;
1618
}

0 commit comments

Comments
 (0)