var gl = document.querySelector("canvas").getContext("webgl");
var ext = gl.getExtension("OES_texture_float");
if (!ext) {
alert("need OES_texture_float extension cause I'm lazy");
}
if (gl.getParameter(gl.MAX_VERTEX_TEXTURE_IMAGE_UNITS) < 2) {
alert("need to be able to access textures from vertex shaders");
}
var m4 = twgl.m4;
var v3 = twgl.v3;
var program = twgl.createProgramFromScripts(gl, ["vshader", "fshader"]);
var positionIndexLoc = gl.getAttribLocation(program, "a_positionIndex");
var normalIndexLoc = gl.getAttribLocation(program, "a_normalIndex");
var colorLoc = gl.getUniformLocation(program, "u_color");
var mvpMatrixLoc = gl.getUniformLocation(program, "u_mvpMatrix");
var mvMatrixLoc = gl.getUniformLocation(program, "u_mvMatrix");
var lightDirLoc = gl.getUniformLocation(
program, "u_lightDirection");
var u_positionsLoc = gl.getUniformLocation(
program, "u_positions");
var u_normalsLoc = gl.getUniformLocation(
program, "u_normals");
gl.uniform1i(u_positionsLoc, 0);
gl.uniform1i(u_normalsLoc, 1);
var positions = [
-1, -1, -1,
+1, -1, -1,
-1, +1, -1,
+1, +1, -1,
-1, -1, +1,
+1, -1, +1,
-1, +1, +1,
+1, +1, +1,
];
var positionIndices = [
3, 7, 5, 3, 5, 1,
6, 2, 0, 6, 0, 4,
6, 7, 3, 6, 3, 2,
0, 1, 5, 0, 5, 4,
7, 6, 4, 7, 4, 5,
2, 3, 1, 2, 1, 0,
];
var normals = [
+1, 0, 0,
-1, 0, 0,
0, +1, 0,
0, -1, 0,
0, 0, +1,
0, 0, -1,
]
var normalIndices = [
0, 0, 0, 0, 0, 0,
1, 1, 1, 1, 1, 1,
2, 2, 2, 2, 2, 2,
3, 3, 3, 3, 3, 3,
4, 4, 4, 4, 4, 4,
5, 5, 5, 5, 5, 5,
];
function degToRad(deg) {
return deg * Math.PI / 180;
}
function uploadIndices(loc, data, indices) {
var scaledIndices = new Float32Array(indices.length);
var size = data.length / 3;
var texel = 1 / size;
var halfTexel = texel / 2;
for (var ii = 0; ii < indices.length; ++ii) {
scaledIndices[ii] = indices[ii] / size + halfTexel;
}
var buffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, buffer);
gl.bufferData(gl.ARRAY_BUFFER,
scaledIndices,
gl.STATIC_DRAW);
gl.enableVertexAttribArray(loc);
gl.vertexAttribPointer(loc, 1, gl.FLOAT, false, 0, 0);
}
function uploadTexture(unit, data) {
gl.activeTexture(gl.TEXTURE0 + unit);
var tex = gl.createTexture();
gl.bindTexture(gl.TEXTURE_2D, tex);
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGB,
data.length / 3, 1, 0,
gl.RGB, gl.FLOAT, new Float32Array(data));
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);
}
uploadIndices(positionIndexLoc, positions, positionIndices);
uploadIndices(normalIndexLoc, normals, normalIndices);
uploadTexture(0, positions);
uploadTexture(1, normals);
var xRot = 30;
var yRot = 20;
var zRot = 0;
var lightDir = v3.normalize([-0.2, -0.1, 0.5]);
function draw() {
xRot += 0;
yRot += 1;
twgl.resizeCanvasToDisplaySize(gl.canvas);
gl.viewport(0, 0, gl.canvas.width, gl.canvas.height);
gl.enable(gl.DEPTH_TEST);
gl.enable(gl.CULL_FACE);
gl.clearColor(0, 0, 0, 1);
gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
gl.useProgram(program);
var projection = m4.perspective(
degToRad(45),
gl.canvas.clientWidth / gl.canvas.clientHeight,
0.1, 100.0);
var world = m4.identity();
world = m4.translate(world, [0.0, 0.0, -5.0]);
world = m4.rotateX(world, degToRad(xRot));
world = m4.rotateY(world, degToRad(yRot));
world = m4.rotateZ(world, degToRad(zRot));
var mvp = m4.multiply(projection, world);
gl.uniformMatrix4fv(mvMatrixLoc, false, world);
gl.uniformMatrix4fv(mvpMatrixLoc, false, mvp);
gl.uniform4f(colorLoc, 0.5, 0.8, 1, 1);
gl.uniform3fv(lightDirLoc, lightDir);
gl.drawArrays(gl.TRIANGLES, 0, 36);
requestAnimationFrame(draw);
}
draw();
body {
margin: 0;
}
canvas {
width: 100vw;
height: 100vh;
display: block;
}
<script src="//twgljs.org/dist/2.x/twgl-full.min.js"></script>
<script id="vshader" type="whatever">
attribute float a_positionIndex;
attribute float a_normalIndex;
attribute vec4 a_pos;
uniform sampler2D u_positions;
uniform sampler2D u_normals;
uniform mat4 u_mvpMatrix;
uniform mat4 u_mvMatrix;
varying vec3 v_normal;
void main() {
vec3 position = texture2D(
u_positions, vec2(a_positionIndex, 0.5)).rgb;
vec3 normal = texture2D(
u_normals, vec2(a_normalIndex, 0.5)).rgb;
gl_Position = u_mvpMatrix * vec4(position, 1);
v_normal = (u_mvMatrix * vec4(normal, 0)).xyz;
}
</script>
<script id="fshader" type="whatever">
precision mediump float;
uniform vec4 u_color;
uniform vec3 u_lightDirection;
varying vec3 v_normal;
void main() {
float light = dot(
normalize(v_normal), u_lightDirection) * 0.5 + 0.5;
gl_FragColor = vec4(u_color.rgb * light, u_color.a);
}
</script>
<canvas></canvas>