Skip to content

Instantly share code, notes, and snippets.

@breezewish
Created April 30, 2018 15:20
Show Gist options
  • Save breezewish/a3c9e86da3e4b83ebf2d881b0bf37611 to your computer and use it in GitHub Desktop.
Save breezewish/a3c9e86da3e4b83ebf2d881b0bf37611 to your computer and use it in GitHub Desktop.
GPU Accelerated Image Rendering (RGBA in Uint8Array) using WebGL
(function () {
const VERTEX_SHADER = `
attribute vec4 a_position;
attribute vec2 a_texcoord;
varying vec2 v_texcoord;
void main() {
gl_Position = a_position;
v_texcoord = a_texcoord;
}
`;
const FRAGMENT_SHADER = `
precision highp float;
varying vec2 v_texcoord;
uniform sampler2D u_texture;
void main() {
gl_FragColor = texture2D(u_texture, v_texcoord);
}
`;
class WebGLRenderer {
/**
* @param {HTMLCanvasElement} canvas
* @param {*} options
*/
constructor(canvas, options = {}) {
if (canvas == null) {
throw new Error('Canvas should be not null');
}
options = {
alpha: false,
antialias: false,
depth: false,
preserveDrawingBuffer: false,
...options,
};
const gl = this.gl = canvas.getContext('webgl', options);
if (!gl) {
throw new Error('WebGL init failed');
}
this.canvas = canvas;
this._initShaders();
}
_initShaders() {
const gl = this.gl;
const vertexShader = gl.createShader(gl.VERTEX_SHADER);
gl.shaderSource(vertexShader, VERTEX_SHADER);
gl.compileShader(vertexShader);
const fragmentShader = gl.createShader(gl.FRAGMENT_SHADER);
gl.shaderSource(fragmentShader, FRAGMENT_SHADER);
gl.compileShader(fragmentShader);
const program = this.program = gl.createProgram();
gl.attachShader(program, vertexShader);
gl.attachShader(program, fragmentShader);
gl.linkProgram(program);
gl.useProgram(program);
if (!gl.getProgramParameter(program, gl.LINK_STATUS)) {
throw new Error('Shader link failed');
}
const positionLocation = gl.getAttribLocation(program, 'a_position');
const positionBuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer);
// TODO: 2D vector is enough?
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([1.0, 1.0, 0.0, -1.0, 1.0, 0.0, 1.0, -1.0, 0.0, -1.0, -1.0, 0.0]), gl.STATIC_DRAW);
gl.enableVertexAttribArray(positionLocation);
gl.vertexAttribPointer(positionLocation, 3, gl.FLOAT, false, 0, 0);
const texcoordLocation = gl.getAttribLocation(program, 'a_texcoord');
const texcoodBuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, texcoodBuffer);
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([1.0, 0.0, 0.0, 0.0, 1.0, 1.0, 0.0, 1.0]), gl.STATIC_DRAW);
gl.enableVertexAttribArray(texcoordLocation);
gl.vertexAttribPointer(texcoordLocation, 2, gl.FLOAT, false, 0, 0);
const textureLocation = gl.getUniformLocation(program, 'u_texture');
const tex = this.tex = gl.createTexture();
gl.bindTexture(gl.TEXTURE_2D, tex);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
gl.activeTexture(gl.TEXTURE0);
gl.uniform1i(textureLocation, 0);
}
render(width, height, data) {
const gl = this.gl;
const sizeChanged = this._updateSize(width, height);
if (sizeChanged) {
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, width, height, 0, gl.RGBA, gl.UNSIGNED_BYTE, data);
} else {
// this should be faster.
gl.texSubImage2D(gl.TEXTURE_2D, 0, 0, 0, width, height, gl.RGBA, gl.UNSIGNED_BYTE, data);
}
gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4);
}
_updateSize(width, height) {
if (width == this.width && height == this.height) {
return false;
}
this.width = width;
this.height = height;
this.canvas.width = width;
this.canvas.height = height;
this.gl.viewport(0, 0, width, height);
return true;
}
}
window.WebGLRenderer = WebGLRenderer;
})()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment