<!doctype html>
<html>
<head>
<meta charset="utf-8">
<style>
body {
background-color: black;
}
.center {
display: block;
margin-top: 30px;
margin-left: auto;
margin-right: auto;
border: solid 1px white;
border-radius: 10px;
}
script {
display: none;
}
</style>
</head>
<body>
<canvas id="canvas" class="center"></canvas>
<input id="scale" type="range" min="1" max="100" value="100" class="center"></input>
<script type="application/javascript">
void function() {
"use strict";
// DOM
var canvasWidth = 180 << 1;
var canvasHeight = 160 << 1;
var canvas = document.getElementById("canvas");
var scale = document.getElementById("scale");
function onScaleChange() {
var scale = this.value * 0.01;
internalWidth = (canvasWidth * scale) | 0;
internalHeight = (canvasHeight * scale) | 0;
gl.uniform1f(uAspectRatio,1.0 / (internalWidth / internalHeight));
gl.deleteFramebuffer(framebuffer);
gl.deleteTexture(framebufferTexture);
[framebuffer,framebufferTexture] = createFramebuffer(internalWidth,internalHeight);
}
// GL
var internalWidth = canvasWidth;
var internalHeight = canvasHeight;
var currentCubeAngle = -0.5;
var gl = canvas.getContext("webgl",{ preserveDrawingBuffer: true, antialias: false }) || console.warn("WebGL Not Supported.");
var cubeProgram = null; // Shaders to draw 3D cube
var scaleProgram = null; // Shaders to scale the frame
var uAspectRatio = null; // Aspect ratio for projection matrix
var uCubeRotation = null; // uniform location for cube program
var cubeBuffer = null; // cube model (attributes)
var scaleBuffer = null; // quad position & UV's
var framebuffer = null; // render target
var framebufferTexture = null; // textured that is rendered to. (The cube is drawn on this)
function createProgram(vertexCode,fragmentCode) {
var vertexShader = gl.createShader(gl.VERTEX_SHADER);
var fragmentShader = gl.createShader(gl.FRAGMENT_SHADER);
gl.shaderSource(vertexShader,vertexCode);
gl.shaderSource(fragmentShader,fragmentCode);
gl.compileShader(vertexShader);
gl.compileShader(fragmentShader);
try {
if (!gl.getShaderParameter(vertexShader,gl.COMPILE_STATUS)) { throw "VS: " + gl.getShaderInfoLog(vertexShader); }
if (!gl.getShaderParameter(fragmentShader,gl.COMPILE_STATUS)) { throw "FS: " + gl.getShaderInfoLog(fragmentShader); }
} catch(error) {
gl.deleteShader(vertexShader);
gl.deleteShader(fragmentShader);
console.error(error);
}
var program = gl.createProgram();
gl.attachShader(program,vertexShader);
gl.attachShader(program,fragmentShader);
gl.deleteShader(vertexShader);
gl.deleteShader(fragmentShader);
gl.linkProgram(program);
return program;
}
function createBuffer(data) {
var buffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER,buffer);
gl.bufferData(gl.ARRAY_BUFFER,Float32Array.from(data),gl.STATIC_DRAW);
return buffer;
}
function createFramebuffer(width,height) {
var 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.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.texImage2D(gl.TEXTURE_2D,0,gl.RGBA,width,height,0,gl.RGBA,gl.UNSIGNED_BYTE,null);
var _framebuffer = gl.createFramebuffer();
gl.bindFramebuffer(gl.FRAMEBUFFER,_framebuffer);
gl.framebufferTexture2D(gl.FRAMEBUFFER,gl.COLOR_ATTACHMENT0,gl.TEXTURE_2D,texture,0);
gl.bindTexture(gl.TEXTURE_2D,null);
gl.bindFramebuffer(gl.FRAMEBUFFER,null);
return [_framebuffer,texture];
}
function loop() {
//
currentCubeAngle += 0.01;
if (currentCubeAngle > 2.0 * Math.PI) {
currentCubeAngle = 0.0;
}
//
gl.bindFramebuffer(gl.FRAMEBUFFER,framebuffer);
gl.bindTexture(gl.TEXTURE_2D,null);
gl.viewport(0,0,internalWidth,internalHeight);
gl.useProgram(cubeProgram);
gl.uniform1f(uCubeRotation,currentCubeAngle);
gl.bindBuffer(gl.ARRAY_BUFFER,cubeBuffer);
gl.vertexAttribPointer(0,3,gl.FLOAT,gl.FALSE,36,0);
gl.vertexAttribPointer(1,3,gl.FLOAT,gl.FALSE,36,12);
gl.vertexAttribPointer(2,3,gl.FLOAT,gl.FALSE,36,24);
gl.enableVertexAttribArray(2);
gl.clear(gl.COLOR_BUFFER_BIT);
gl.drawArrays(gl.TRIANGLES,0,24);
gl.bindFramebuffer(gl.FRAMEBUFFER,null);
gl.bindTexture(gl.TEXTURE_2D,framebufferTexture);
gl.viewport(0,0,canvasWidth,canvasHeight);
gl.useProgram(scaleProgram);
gl.bindBuffer(gl.ARRAY_BUFFER,scaleBuffer);
gl.vertexAttribPointer(0,2,gl.FLOAT,gl.FALSE,16,0);
gl.vertexAttribPointer(1,2,gl.FLOAT,gl.FALSE,16,8);
gl.disableVertexAttribArray(2);
gl.clear(gl.COLOR_BUFFER_BIT);
gl.drawArrays(gl.TRIANGLES,0,6);
//
requestAnimationFrame(loop);
}
// Entry Point
onload = function() {
// DOM
canvas.width = canvasWidth;
canvas.height = canvasHeight;
scale.onmouseup = onScaleChange;
// GL
gl.clearColor(0.5,0.5,0.5,1.0);
gl.enable(gl.CULL_FACE);
gl.enableVertexAttribArray(0);
gl.enableVertexAttribArray(1);
cubeProgram = createProgram(`
precision mediump float;
const float LIGHT_ANGLE = 0.5;
const vec3 LIGHT_DIR = vec3(sin(LIGHT_ANGLE),0.0,cos(LIGHT_ANGLE));
const mat4 OFFSET = mat4(
1.0,0.0,0.0,0.0,
0.0,1.0,0.0,0.0,
0.0,0.0,1.0,0.0,
0.0,0.0,-5.0,1.0
);
const float FOV = 0.698132;
const float Z_NEAR = 1.0;
const float Z_FAR = 20.0;
const float COT_FOV = 1.0 / tan(FOV * 0.5);
const float Z_FACTOR_1 = -(Z_FAR / (Z_FAR - Z_NEAR));
const float Z_FACTOR_2 = -((Z_NEAR * Z_FAR) / (Z_FAR - Z_NEAR));
attribute vec3 aPosition;
attribute vec3 aNormal;
attribute vec3 aColour;
varying vec3 vColour;
uniform float uAspectRatio;
uniform float uRotation;
void main() {
float s = sin(uRotation);
float c = cos(uRotation);
mat4 PROJ = mat4(
COT_FOV * uAspectRatio,0.0,0.0,0.0,
0.0,COT_FOV,0.0,0.0,
0.0,0.0,Z_FACTOR_1,Z_FACTOR_2,
0.0,0.0,-1.0,0.0
);
mat4 rot = mat4(
c ,0.0,-s ,0.0,
0.0,1.0,0.0,0.0,
s ,0.0,c ,0.0,
0.0,0.0,0.0,1.0
);
vec3 normal = (vec4(aNormal,0.0) * rot).xyz;
vColour = aColour * max(0.4,dot(normal,LIGHT_DIR));
gl_Position = PROJ * OFFSET * rot * vec4(aPosition,1.0);
}
`,`
precision mediump float;
varying vec3 vColour;
void main() {
gl_FragColor = vec4(vColour,1.0);
}
`);
uAspectRatio = gl.getUniformLocation(cubeProgram,"uAspectRatio");
uCubeRotation = gl.getUniformLocation(cubeProgram,"uRotation");
gl.useProgram(cubeProgram);
gl.uniform1f(uAspectRatio,1.0 / (internalWidth / internalHeight));
scaleProgram = createProgram(`
precision mediump float;
attribute vec2 aPosition;
attribute vec2 aUV;
varying vec2 vUV;
void main() {
vUV = aUV;
gl_Position = vec4(aPosition,0.0,1.0);
}
`,`
precision mediump float;
varying vec2 vUV;
uniform sampler2D uTexture;
void main() {
gl_FragColor = texture2D(uTexture,vUV);
}
`);
cubeBuffer = createBuffer([
// Position Normal Colour
// Front
1.0, 1.0, 1.0, 0.0, 0.0, 1.0, 0.0,0.0,0.6,
-1.0, 1.0, 1.0, 0.0, 0.0, 1.0, 0.0,0.0,0.6,
-1.0,-1.0, 1.0, 0.0, 0.0, 1.0, 0.0,0.0,0.6,
-1.0,-1.0, 1.0, 0.0, 0.0, 1.0, 0.0,0.0,0.6,
1.0,-1.0, 1.0, 0.0, 0.0, 1.0, 0.0,0.0,0.6,
1.0, 1.0, 1.0, 0.0, 0.0, 1.0, 0.0,0.0,0.6,
// Back
-1.0,-1.0,-1.0, 0.0, 0.0,-1.0, 0.0,0.0,0.6,
-1.0, 1.0,-1.0, 0.0, 0.0,-1.0, 0.0,0.0,0.6,
1.0, 1.0,-1.0, 0.0, 0.0,-1.0, 0.0,0.0,0.6,
1.0, 1.0,-1.0, 0.0, 0.0,-1.0, 0.0,0.0,0.6,
1.0,-1.0,-1.0, 0.0, 0.0,-1.0, 0.0,0.0,0.6,
-1.0,-1.0,-1.0, 0.0, 0.0,-1.0, 0.0,0.0,0.6,
// Left
-1.0, 1.0, 1.0, 1.0, 0.0, 0.0, 0.0,0.0,0.6,
-1.0,-1.0,-1.0, 1.0, 0.0, 0.0, 0.0,0.0,0.6,
-1.0,-1.0, 1.0, 1.0, 0.0, 0.0, 0.0,0.0,0.6,
-1.0, 1.0, 1.0, 1.0, 0.0, 0.0, 0.0,0.0,0.6,
-1.0, 1.0,-1.0, 1.0, 0.0, 0.0, 0.0,0.0,0.6,
-1.0,-1.0,-1.0, 1.0, 0.0, 0.0, 0.0,0.0,0.6,
// Right
1.0,-1.0, 1.0, -1.0, 0.0, 0.0, 0.0,0.0,0.6,
1.0,-1.0,-1.0, -1.0, 0.0, 0.0, 0.0,0.0,0.6,
1.0, 1.0, 1.0, -1.0, 0.0, 0.0, 0.0,0.0,0.6,
1.0,-1.0,-1.0, -1.0, 0.0, 0.0, 0.0,0.0,0.6,
1.0, 1.0,-1.0, -1.0, 0.0, 0.0, 0.0,0.0,0.6,
1.0, 1.0, 1.0, -1.0, 0.0, 0.0, 0.0,0.0,0.6
]);
scaleBuffer = createBuffer([
// Position UV
1.0, 1.0, 1.0,1.0,
-1.0, 1.0, 0.0,1.0,
-1.0,-1.0, 0.0,0.0,
1.0, 1.0, 1.0,1.0,
-1.0,-1.0, 0.0,0.0,
1.0,-1.0, 1.0,0.0
]);
[framebuffer,framebufferTexture] = createFramebuffer(internalWidth,internalHeight);
loop();
}
// Exit point
onunload = function() {
gl.deleteProgram(cubeProgram);
gl.deleteProgram(scaleProgram);
gl.deleteBuffer(cubeBuffer);
gl.deleteBuffer(scaleBuffer);
gl.deleteFramebuffer(framebuffer);
gl.deleteTexture(framebufferTexture);
}
}();
</script>
</body>
</html>