<img src="data:image/png;base64,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" id="icon" hidden />
canvas {
border: 2px solid red;
image-rendering: pixelated;
margin: 12px;
}
View Compiled
// create Canvas element, or you could grab it from DOM
const canvas = document.createElement('canvas');
document.body.appendChild(canvas);
// optional: set width/height, default is 300/150
canvas.width = 640;
canvas.height = 480;
// retrieve WebGLRenderingContext
const gl = canvas.getContext('webgl') || canvas.getContext('experimental-webgl');
const vertexShaderSource = `
uniform vec2 screenSize; // width/height of screen
attribute vec2 spritePosition; // position of sprite
void main() {
vec4 screenTransform =
vec4(2.0 / screenSize.x, -2.0 / screenSize.y, -1.0, 1.0);
gl_Position =
vec4(spritePosition * screenTransform.xy + screenTransform.zw, 0.0, 1.0);
gl_PointSize = 64.0;
}
`;
const fragmentShaderSource = `
uniform sampler2D spriteTexture; // texture we are drawing
void main() {
gl_FragColor = texture2D(spriteTexture, gl_PointCoord);
}
`;
function loadShader(gl, type, source) {
const shader = gl.createShader(type);
gl.shaderSource(shader, source);
gl.compileShader(shader);
const status = gl.getShaderParameter(shader, gl.COMPILE_STATUS);
if (!status) {
throw new TypeError(`couldn't compile shader:\n${gl.getShaderInfoLog(shader)}`);
}
return shader;
}
const vertexShader = loadShader(gl, gl.VERTEX_SHADER, vertexShaderSource);
const fragmentShader = loadShader(gl, gl.FRAGMENT_SHADER, fragmentShaderSource);
const shaderProgram = gl.createProgram();
gl.attachShader(shaderProgram, vertexShader);
gl.attachShader(shaderProgram, fragmentShader);
gl.linkProgram(shaderProgram);
gl.useProgram(shaderProgram);
const status = gl.getProgramParameter(shaderProgram, gl.LINK_STATUS);
if (!status) {
throw new TypeError(`couldn't link shader program (status=${status}): ${gl.getProgramInfoLog(shaderProgram)}`);
}
gl.useProgram(shaderProgram);
gl.uniform2f(gl.getUniformLocation(shaderProgram, 'screenSize'), canvas.width, canvas.height);
const array = new Float32Array(1000); // allow for 500 sprites
array[0] = 128; // x-value
array[1] = 128; // y-value
const glBuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, glBuffer);
gl.bufferData(gl.ARRAY_BUFFER, array, gl.DYNAMIC_DRAW); // upload data
const loc = gl.getAttribLocation(shaderProgram, 'spritePosition');
gl.enableVertexAttribArray(loc);
gl.vertexAttribPointer(loc,
2, // because it was a vec2
gl.FLOAT, // vec2 contains floats
false, // ignored
0, // each value is next to each other
0); // starts at start of array
const icon = document.getElementById('icon');
const glTexture = gl.createTexture();
gl.bindTexture(gl.TEXTURE_2D, glTexture);
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, icon);
gl.generateMipmap(gl.TEXTURE_2D);
// sneakily enable blending alpha
gl.enable(gl.BLEND);
gl.blendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA);
function draw(points=1) {
gl.clear(gl.COLOR_BUFFER_BIT);
gl.drawArrays(gl.POINTS, 0, points);
console.info('drawn');
}
draw();
let index = 2;
canvas.addEventListener('click', (ev) => {
array[index] = ev.offsetX;
array[index+1] = ev.offsetY;
gl.bufferData(gl.ARRAY_BUFFER, array, gl.DYNAMIC_DRAW); // upload data
index += 2;
draw(index/2);
});
View Compiled
This Pen doesn't use any external CSS resources.
This Pen doesn't use any external JavaScript resources.