const assert = (condition, message) => {
if (!condition) throw new Error(message);
};
const canvas = document.getElementById('canvas');
const gl = canvas.getContext('webgl2');
assert(gl !== null, `WebGL2 was unexpectedly not supported.`);
const testImage = new Uint8Array(Array.from(
{ length: 512 * 512 },
(_, i) => (i % 16 > 7) !== (Math.floor(i / 512) % 16 > 7)
? [0, 0xff, 0, 0xff]
: [0, 0, 0xff, 0xff],
).flat());
const bitsRequired = 16;
const offset = -1 / (1 << bitsRequired);
const vData = new Float32Array([
-1, 0, 0, 0.5 + offset, 1, 0, 0.015625, 0.5 + offset,
1, 2, 0.015625, 0.515625 + offset, -1, 2, 0, 0.515625 + offset,
]);
const zoom = 2 ** (bitsRequired - 14);
const projection = new Float32Array([
zoom, 0, 0, 0,
0, zoom, 0, 0,
0, 0, -zoom, 0,
0, 0, 0, 1,
]);
const indices = new Uint16Array([0, 1, 2, 0, 2, 3]);
const texture = gl.createTexture();
gl.bindTexture(gl.TEXTURE_2D, texture);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, 512, 512, 0, gl.RGBA, gl.UNSIGNED_BYTE, testImage);
const vao = gl.createVertexArray();
gl.bindVertexArray(vao);
gl.enableVertexAttribArray(0);
gl.enableVertexAttribArray(1);
const vBuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, vBuffer);
gl.vertexAttribPointer(0, 2, gl.FLOAT, false, 16, 0);
gl.vertexAttribPointer(1, 2, gl.FLOAT, false, 16, 8);
gl.bufferData(gl.ARRAY_BUFFER, vData, gl.STATIC_DRAW);
const iBuffer = gl.createBuffer();
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, iBuffer);
gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, indices, gl.STATIC_DRAW);
const vertexShaderSrc = `#version 300 es
precision highp float;
uniform mat4 projection;
layout(location = 0) in vec2 aPosition;
layout(location = 1) in vec2 aTextureCoord;
out vec2 vTextureCoord;
void main(void) {
gl_Position = projection * vec4(aPosition, 0.0, 1.0);
vTextureCoord = aTextureCoord;
}`;
const fragmentShaderSrc = `#version 300 es
precision highp float;
uniform sampler2D sampler;
in vec2 vTextureCoord;
out vec4 fColor;
void main(void){
// vec2 texCoordRounded = vec2(
// (floor(vTextureCoord.x * 512.) + 0.5) / 512.,
// (floor(vTextureCoord.y * 512.) + 0.5) / 512.
// );
// vec4 color = texture(sampler, texCoordRounded);
vec4 color = texture(sampler, vTextureCoord);
fColor = color;
}`;
const program = gl.createProgram();
assert(program !== null, `Program was unexpectedly `null`.`);
const vertexShader = gl.createShader(gl.VERTEX_SHADER);
assert(vertexShader !== null, `Vertex-shader was unexpectedly `null`.`);
gl.shaderSource(vertexShader, vertexShaderSrc);
gl.compileShader(vertexShader);
assert(gl.getShaderParameter(vertexShader, gl.COMPILE_STATUS), `Vertex-shader failed to compile:n${gl.getShaderInfoLog(vertexShader)}`);
const fragmentShader = gl.createShader(gl.FRAGMENT_SHADER);
assert(fragmentShader !== null, `Vertex-shader was unexpectedly `null`.`);
gl.shaderSource(fragmentShader, fragmentShaderSrc);
gl.compileShader(fragmentShader);
assert(gl.getShaderParameter(fragmentShader, gl.COMPILE_STATUS), `Fragment-shader failed to compile:n${gl.getShaderInfoLog(fragmentShader)}`);
gl.attachShader(program, vertexShader);
gl.attachShader(program, fragmentShader);
gl.linkProgram(program);
assert(gl.getProgramParameter(program, gl.LINK_STATUS), `Program linking failed:n${gl.getProgramInfoLog(program)}`);
gl.useProgram(program);
const uniformLocationSampler = gl.getUniformLocation(program, 'sampler');
gl.uniform1i(uniformLocationSampler, 0);
const uniformLocationProjection = gl.getUniformLocation(program, 'projection');
gl.uniformMatrix4fv(uniformLocationProjection, false, projection);
gl.clearColor(0, 0, 0, 1.0);
gl.clear(gl.COLOR_BUFFER_BIT);
gl.drawElements(gl.TRIANGLES, 6, gl.UNSIGNED_SHORT, 0);
<canvas id='canvas' width='256' height='256'></canvas>