const canvas = document.getElementById('glCanvas');
const gl = canvas.getContext('webgl');
const sceneVS = `
attribute vec4 aPosition;
attribute vec4 aColor;
uniform float uTime;
varying lowp vec4 vColor;
void main() {
float angle = uTime;
float c = cos(angle);
float s = sin(angle);
mat2 rot = mat2(c, s, -s, c);
vec2 rotated = rot * aPosition.xy;
gl_Position = vec4(rotated, 0.0, 1.0);
vColor = aColor;
}
`;
const sceneFS = `
varying lowp vec4 vColor;
void main() {
gl_FragColor = vColor;
}
`;
const postVS = `
attribute vec2 aPosition;
varying highp vec2 vTexCoord;
void main() {
gl_Position = vec4(aPosition, 0.0, 1.0);
vTexCoord = aPosition * 0.5 + 0.5;
}
`;
const postFS = `
varying highp vec2 vTexCoord;
uniform sampler2D uTexture;
void main() {
highp vec2 offset = (vTexCoord - 0.5) * 0.01;
highp float r = texture2D(uTexture, vTexCoord + offset).r;
highp float g = texture2D(uTexture, vTexCoord).g;
highp float b = texture2D(uTexture, vTexCoord - offset).b;
gl_FragColor = vec4(r, g, b, 1.0);
}
`;
function initShaderProgram(gl, vsSource, fsSource) {
const vertexShader = loadShader(gl, gl.VERTEX_SHADER, vsSource);
const fragmentShader = loadShader(gl, gl.FRAGMENT_SHADER, fsSource);
const shaderProgram = gl.createProgram();
gl.attachShader(shaderProgram, vertexShader);
gl.attachShader(shaderProgram, fragmentShader);
gl.linkProgram(shaderProgram);
return shaderProgram;
}
function loadShader(gl, type, source) {
const shader = gl.createShader(type);
gl.shaderSource(shader, source);
gl.compileShader(shader);
return shader;
}
// Create framebuffer and texture
const fb = gl.createFramebuffer();
const texture = gl.createTexture();
gl.bindTexture(gl.TEXTURE_2D, texture);
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, 512, 512, 0, gl.RGBA, gl.UNSIGNED_BYTE, null);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);
gl.bindFramebuffer(gl.FRAMEBUFFER, fb);
gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, texture, 0);
gl.bindFramebuffer(gl.FRAMEBUFFER, null);
// Scene geometry
const trianglePos = [0.0, 0.5, -0.5, -0.5, 0.5, -0.5];
const triangleCol = [1,0,0,1, 0,1,0,1, 0,0,1,1];
const quadPos = [-1,-1, 1,-1, -1,1, 1,1];
const trianglePosBuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, trianglePosBuffer);
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(trianglePos), gl.STATIC_DRAW);
const triangleColBuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, triangleColBuffer);
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(triangleCol), gl.STATIC_DRAW);
const quadPosBuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, quadPosBuffer);
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(quadPos), gl.STATIC_DRAW);
const sceneProgram = initShaderProgram(gl, sceneVS, sceneFS);
const postProgram = initShaderProgram(gl, postVS, postFS);
const scenePos = gl.getAttribLocation(sceneProgram, 'aPosition');
const sceneCol = gl.getAttribLocation(sceneProgram, 'aColor');
const sceneTime = gl.getUniformLocation(sceneProgram, 'uTime');
const postPos = gl.getAttribLocation(postProgram, 'aPosition');
let time = 0;
function render() {
time += 0.01;
// Render to texture
gl.bindFramebuffer(gl.FRAMEBUFFER, fb);
gl.viewport(0, 0, 512, 512);
gl.clearColor(0.1, 0.1, 0.15, 1.0);
gl.clear(gl.COLOR_BUFFER_BIT);
gl.useProgram(sceneProgram);
gl.uniform1f(sceneTime, time);
gl.bindBuffer(gl.ARRAY_BUFFER, trianglePosBuffer);
gl.vertexAttribPointer(scenePos, 2, gl.FLOAT, false, 0, 0);
gl.enableVertexAttribArray(scenePos);
gl.bindBuffer(gl.ARRAY_BUFFER, triangleColBuffer);
gl.vertexAttribPointer(sceneCol, 4, gl.FLOAT, false, 0, 0);
gl.enableVertexAttribArray(sceneCol);
gl.drawArrays(gl.TRIANGLES, 0, 3);
// Render to screen with post-processing
gl.bindFramebuffer(gl.FRAMEBUFFER, null);
gl.viewport(0, 0, canvas.width, canvas.height);
gl.clearColor(0.0, 0.0, 0.0, 1.0);
gl.clear(gl.COLOR_BUFFER_BIT);
gl.useProgram(postProgram);
gl.bindTexture(gl.TEXTURE_2D, texture);
gl.bindBuffer(gl.ARRAY_BUFFER, quadPosBuffer);
gl.vertexAttribPointer(postPos, 2, gl.FLOAT, false, 0, 0);
gl.enableVertexAttribArray(postPos);
gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4);
requestAnimationFrame(render);
}
render();