I'm trying to use normals for simple point lights on this simple scene, but I need the normals to modify correctly to the rotation of the cube.
Some notes for the code:
The reason it's so large is because it uses multiple files, which provides more organization.
The reasoning for the cube being rotated within the vertex shader but the cosine and sine operations being performed outside of it is due to performance improvements.
Instead of changing a buffer for, say, 100 cubes and rotating each and every one of them, in order to rotate the cube, we just need to input one buffer. If you just want to rotate the model view projection, that's fine, but if you want to have two cubes (side by side) rotating in different directions or have a first-person shooter with different rotating objects, that's nearly impossible.
// "shaders.js"
const vsSource = [
`attribute vec4 aVertexPosition;`,
`attribute vec3 aVertexNormal;`,
`uniform mat4 uModelViewMatrix;`,
`uniform mat4 uProjectionMatrix;`,
`uniform mat4 uNormalMatrix;`,
`uniform vec3 uTransform;`,
`uniform vec2 uRotationX;`,
`uniform vec2 uRotationY;`,
`uniform vec2 uRotationZ;`,
`varying highp vec3 vLighting;`,
`void main(void) {`,
` vec4 p = aVertexPosition;`,
` float n0x = p.x * uRotationX.y + p.y * uRotationX.x;`,
` float n0y = p.y * uRotationX.y - p.x * uRotationX.x;`,
` float n0z = p.z;`,
` float n1x = n0x;`,
` float n1y = n0y * uRotationY.y + n0z * uRotationY.x;`,
` float n1z = n0z * uRotationY.y - n0y * uRotationY.x;`,
` float n2x = n1x * uRotationZ.y - n1z * uRotationZ.x;`,
` float n2y = n1y;`,
` float n2z = n1z * uRotationZ.y + n1x * uRotationZ.x;`,
` gl_Position = uProjectionMatrix * uModelViewMatrix * (vec4(vec3(n2x, n2y, n2z) + uTransform, p.w));`,
` highp vec3 ambientLight = vec3(0.3, 0.3, 0.3);`,
` highp vec3 directionalLightColor = vec3(1.0, 1.0, 1.0);`,
` highp vec3 directionalVector = normalize(vec3(0.85, 0.8, 0.75));`,
` highp vec4 transformedNormal = uNormalMatrix * vec4(aVertexNormal, 1.0);`,
` highp float directional = max(dot(transformedNormal.xyz, directionalVector), 0.0);`,
` vLighting = ambientLight + (directionalLightColor * directional);`,
`}`
].join(`\n`);
const fsSource = [
`varying highp vec3 vLighting;`,
`void main(void) {`,
` gl_FragColor = vec4(vec3(1.0, 0.0, 0.0) * vLighting, 1.0);`,
`}`
].join(`\n`);
const initShaders = (gl) => {
const vertexShader = gl.createShader(gl.VERTEX_SHADER),
fragmentShader = gl.createShader(gl.FRAGMENT_SHADER);
gl.shaderSource(vertexShader, vsSource);
gl.shaderSource(fragmentShader, fsSource);
gl.compileShader(vertexShader);
if (!gl.getShaderParameter(vertexShader, gl.COMPILE_STATUS)) {
alert(`An error occurred compiling the shaders: ${gl.getShaderInfoLog(vertexShader)}`);
gl.deleteShader(vertexShader);
return null;
};
gl.compileShader(fragmentShader);
if (!gl.getShaderParameter(fragmentShader, gl.COMPILE_STATUS)) {
alert(`An error occurred compiling the shaders: ${gl.getShaderInfoLog(fragmentShader)}`);
gl.deleteShader(fragmentShader);
return null;
};
return {
'vertex': vertexShader,
'fragment': fragmentShader
};
};
// "object.js"
class Obj {
#positions;
#index;
#normals;
get positions() {
return [...(this.#positions)];
};
get index() {
return [...(this.#index)];
};
get normals() {
return [...(this.#normals)];
};
constructor(positions, index, normals) {
if (typeof positions == `object` && typeof index == `object` && typeof normals == `object`) {
this.#positions = positions;
this.#index = index;
this.#normals = index;
} else {
console.error(`Need "object" type, got: "${typeof positions}", "${typeof index}"`)
};
return;
};
};
class Object {
static Cube = class Cube extends Obj {
constructor() {
super([-1.0, -1.0, 1.0,
1.0, -1.0, 1.0,
1.0, 1.0, 1.0, -1.0, 1.0, 1.0, -1.0, -1.0, -1.0, -1.0, 1.0, -1.0,
1.0, 1.0, -1.0,
1.0, -1.0, -1.0, -1.0, 1.0, -1.0, -1.0, 1.0, 1.0,
1.0, 1.0, 1.0,
1.0, 1.0, -1.0, -1.0, -1.0, -1.0,
1.0, -1.0, -1.0,
1.0, -1.0, 1.0, -1.0, -1.0, 1.0,
1.0, -1.0, -1.0,
1.0, 1.0, -1.0,
1.0, 1.0, 1.0,
1.0, -1.0, 1.0, -1.0, -1.0, -1.0, -1.0, -1.0, 1.0, -1.0, 1.0, 1.0, -1.0, 1.0, -1.0
], [
0, 1, 2, 0, 2, 3,
4, 5, 6, 4, 6, 7,
8, 9, 10, 8, 10, 11,
12, 13, 14, 12, 14, 15,
16, 17, 18, 16, 18, 19,
20, 21, 22, 20, 22, 23
], [
0.0, 0.0, 1.0,
0.0, 0.0, 1.0,
0.0, 0.0, 1.0,
0.0, 0.0, 1.0,
0.0, 0.0, -1.0,
0.0, 0.0, -1.0,
0.0, 0.0, -1.0,
0.0, 0.0, -1.0,
0.0, 1.0, 0.0,
0.0, 1.0, 0.0,
0.0, 1.0, 0.0,
0.0, 1.0, 0.0,
0.0, -1.0, 0.0,
0.0, -1.0, 0.0,
0.0, -1.0, 0.0,
0.0, -1.0, 0.0,
1.0, 0.0, 0.0,
1.0, 0.0, 0.0,
1.0, 0.0, 0.0,
1.0, 0.0, 0.0, -1.0, 0.0, 0.0, -1.0, 0.0, 0.0, -1.0, 0.0, 0.0, -1.0, 0.0, 0.0
]);
};
};
};
const getBuffers = (gl, clas) => {
if (typeof clas != `function`) {
console.error(`Need "function" type, got: "${typeof clas}"`);
return;
};
const classy = new clas();
var positions = classy.positions,
index = classy.index,
normals = classy.normals;
if (positions != undefined && positions != null && index != undefined && index != null && normals != undefined && normals != null) {
const positionBuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer);
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(positions), gl.STATIC_DRAW);
const normalBuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, normalBuffer);
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(normals), gl.STATIC_DRAW);
const indexBuffer = gl.createBuffer();
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, indexBuffer);
gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, new Uint16Array(index), gl.STATIC_DRAW);
return {
'position': positionBuffer,
'index': indexBuffer,
'normal': normalBuffer
}
} else {
console.error(`Need any input type, got: "${typeof positions}", "${typeof index}", "${typeof normals}"`);
};
return;
};
// "draw.js"
var doDraw,
draw,
drawRad;
(() => {
const DTR = Math.PI / 180.0,
RTD = 180.0 / Math.PI;
var draws = [];
doDraw = (gl, aVertexPosition, aVertexNormal, uProjectionMatrix, uModelViewMatrix, uNormalMatrix, uTransform, uRotationX, uRotationY, uRotationZ, projectionMatrix, modelViewMatrix) => {
draws.forEach((e) => {
const normalMatrix = mat4.create();
mat4.invert(normalMatrix, normalMatrix);
mat4.transpose(normalMatrix, normalMatrix);
gl.bindBuffer(gl.ARRAY_BUFFER, e[`buffs`][`position`]);
gl.vertexAttribPointer(aVertexPosition, 3, gl.FLOAT, false, 0, 0);
gl.enableVertexAttribArray(aVertexPosition);
gl.bindBuffer(gl.ARRAY_BUFFER, e[`buffs`][`normal`]);
gl.vertexAttribPointer(aVertexNormal, 3, gl.FLOAT, false, 0, 0);
gl.enableVertexAttribArray(aVertexNormal);
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, e[`buffs`][`index`]);
gl.uniformMatrix4fv(uProjectionMatrix, false, projectionMatrix);
gl.uniformMatrix4fv(uModelViewMatrix, false, modelViewMatrix);
gl.uniformMatrix4fv(uNormalMatrix, false, normalMatrix);
gl.uniform3fv(uTransform, e[`transform`]);
gl.uniform2fv(uRotationX, e[`rotateX`]);
gl.uniform2fv(uRotationY, e[`rotateY`]);
gl.uniform2fv(uRotationZ, e[`rotateZ`]);
gl.drawElements(gl.TRIANGLES, e[`count`], gl.UNSIGNED_SHORT, 0); // LINE_LOOP, TRIANGLES
return;
});
draws = [];
return;
};
draw = (buffs, vertexCount, x, y, z, rx, ry, rz) => {
const rxr = rx * DTR,
ryr = ry * DTR,
rzr = rz * DTR;
draws.push({
'buffs': buffs,
'transform': [
x,
y,
z
],
'rotateX': [
Math.sin(rxr),
Math.cos(rxr)
],
'rotateY': [
Math.sin(ryr),
Math.cos(ryr)
],
'rotateZ': [
Math.sin(rzr),
Math.cos(rzr)
],
'rx': rxr,
'ry': ryr,
'rz': rzr,
'count': vertexCount
});
return;
};
drawRad = (buffs, vertexCount, x, y, z, rx, ry, rz) => {
draws.push({
'buffs': buffs,
'transform': [
x,
y,
z
],
'rotateX': [
Math.sin(rx),
Math.cos(rx)
],
'rotateY': [
Math.sin(ry),
Math.cos(ry)
],
'rotateZ': [
Math.sin(rz),
Math.cos(rz)
],
'rx': rx,
'ry': ry,
'rz': rz,
'count': vertexCount
});
return;
};
return;
})();
// "script.js"
(() => {
const DTR = Math.PI / 180.0,
RTD = 180.0 / Math.PI;
var gl,
ctx,
canvasgl,
canvasctx,
width = 0,
height = 0,
cubeBuffs,
shaderProgram,
vertexShader,
fragmentShader,
aVertexPosition,
aVertexNormal,
uProjectionMatrix,
uModelViewMatrix,
uNormalMatrix,
uTransform,
uRotationX,
uRotationY,
uRotationZ,
aProjectionMatrix,
aModelViewMatrix,
projectionMatrix,
modelViewMatrix,
cx = 0.0,
cy = 0.0,
cz = 0.0,
crx = 0.0,
cry = 0.0,
crz = 0.0;
var cubeTime = 0.0;
var updates = [],
updateTime = 0,
fpsupdate = 0.0,
fpsupdatemin = 0.0,
fpsupdatemax = 0.0;
const loop = (deltaTime) => {
cubeTime += deltaTime * 90;
updateTime += deltaTime;
updates.push(deltaTime);
if (updates.length > 60) {
var n = updates.shift();
};
var l = updates.length,
ftime = 0.0;
var max = 0.0,
min = 1000.0;
updates.forEach((e) => {
ftime += e;
if (min > e) {
min = e;
};
if (max < e) {
max = e;
};
return;
});
fpsupdatemin = min;
fpsupdatemax = max;
fpsupdate = ftime / l;
ctx.fillStyle = `#fff`;
ctx.font = `12px serif`;
var txt0 = ` 1 / deltaTime: [ FPS: ${(1 / deltaTime).toFixed(1)} ]`,
txt1 = ` Average / deltaTime: [ FPS: ${(1 / fpsupdate).toFixed(1)} ]`,
txt2 = ` Maximum / deltaTime: [ FPS: ${(1 / fpsupdatemin).toFixed(1)} ]`,
txt3 = ` Minimum / deltaTime: [ FPS: ${(1 / fpsupdatemax).toFixed(1)} ]`;
var metrics0 = ctx.measureText(` FPS:`),
metrics1 = ctx.measureText(txt0),
metrics2 = ctx.measureText(txt1),
metrics3 = ctx.measureText(txt2),
metrics4 = ctx.measureText(txt3);
var hmet0 = metrics0.actualBoundingBoxAscent + metrics0.actualBoundingBoxDescent + 2,
hmet1 = metrics1.actualBoundingBoxAscent + metrics1.actualBoundingBoxDescent + 2,
hmet2 = metrics2.actualBoundingBoxAscent + metrics2.actualBoundingBoxDescent + 2,
hmet3 = metrics3.actualBoundingBoxAscent + metrics3.actualBoundingBoxDescent + 2,
hmet4 = metrics4.actualBoundingBoxAscent + metrics4.actualBoundingBoxDescent + 2;
ctx.fillText(` FPS:`, 0, hmet0);
ctx.fillText(txt0, 0, hmet0 + hmet1);
ctx.fillText(txt1, 0, hmet0 + hmet1 + hmet2);
ctx.fillText(txt2, 0, hmet0 + hmet1 + hmet2 + hmet3);
ctx.fillText(txt3, 0, hmet0 + hmet1 + hmet2 + hmet3 + hmet4);
draw(cubeBuffs, 36, 0.0, 0.0, 0.0, cubeTime * 0.3, cubeTime * 0.7, cubeTime);
return;
};
var l,
then = 0;
l = (now) => {
requestAnimationFrame(l);
now *= 0.001;
const dT = now - then;
then = now;
mat4.copy(projectionMatrix, aProjectionMatrix);
mat4.copy(modelViewMatrix, aModelViewMatrix);
mat4.translate(modelViewMatrix, modelViewMatrix, [
cx, -cy,
cz
]);
mat4.rotate(projectionMatrix, projectionMatrix, (-crx) * DTR, [
1,
0,
0
]);
mat4.rotate(projectionMatrix, projectionMatrix, cry * DTR, [
0,
1,
0
]);
mat4.rotate(projectionMatrix, projectionMatrix, crz * DTR, [
0,
0,
1
]);
gl.clearColor(0.0, 0.0, 0.0, 1.0);
gl.clearDepth(1.0);
gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
ctx.clearRect(0, 0, width, height);
loop(dT);
doDraw(gl, aVertexPosition, aVertexNormal, uProjectionMatrix, uModelViewMatrix, uNormalMatrix, uTransform, uRotationX, uRotationY, uRotationZ, projectionMatrix, modelViewMatrix);
return;
};
const init = () => {
cubeBuffs = getBuffers(gl, Object.Cube);
cx = -6.0;
cy = 6.0;
cz = -6.0;
crx = -45.0;
cry = -45.0;
return false;
};
const resize = () => {
height = document.body.clientHeight;
width = document.body.clientWidth;
canvasctx.style.height = `${height}px`;
canvasctx.style.width = `${width}px`;
canvasctx.height = height;
canvasctx.width = width;
canvasgl.style.height = `${height}px`;
canvasgl.style.width = `${width}px`;
canvasgl.height = height;
canvasgl.width = width;
mat4.perspective(aProjectionMatrix, (45.0 * Math.PI) / 180.0, width / height, 0.0, 1000.0);
gl.viewport(0, 0, width, height);
return;
};
const ini = () => {
gl.clearColor(0.0, 0.0, 0.0, 1.0);
gl.clear(gl.COLOR_BUFFER_BIT);
const shaders = initShaders(gl);
vertexShader = shaders[`vertex`];
fragmentShader = shaders[`fragment`];
shaderProgram = gl.createProgram();
gl.attachShader(shaderProgram, vertexShader);
gl.attachShader(shaderProgram, fragmentShader);
gl.linkProgram(shaderProgram);
if (!gl.getProgramParameter(shaderProgram, gl.LINK_STATUS)) {
alert(`Unable to initialize the shader program: ${gl.getProgramInfoLog(shaderProgram)}`);
return true;
};
gl.useProgram(shaderProgram);
aVertexPosition = gl.getAttribLocation(shaderProgram, `aVertexPosition`);
aVertexNormal = gl.getAttribLocation(shaderProgram, `aVertexNormal`);
uProjectionMatrix = gl.getUniformLocation(shaderProgram, `uProjectionMatrix`);
uModelViewMatrix = gl.getUniformLocation(shaderProgram, `uModelViewMatrix`);
uNormalMatrix = gl.getUniformLocation(shaderProgram, `uNormalMatrix`);
uTransform = gl.getUniformLocation(shaderProgram, `uTransform`);
uRotationX = gl.getUniformLocation(shaderProgram, `uRotationX`);
uRotationY = gl.getUniformLocation(shaderProgram, `uRotationY`);
uRotationZ = gl.getUniformLocation(shaderProgram, `uRotationZ`);
gl.disable(gl.BLEND);
gl.blendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA);
gl.enable(gl.CULL_FACE);
gl.cullFace(gl.BACK);
gl.enable(gl.DEPTH_TEST);
gl.depthFunc(gl.LEQUAL);
return init();
};
window.addEventListener(`load`, () => {
canvasctx = document.createElement(`canvas`);
canvasgl = document.createElement(`canvas`);
canvasctx.style.imageRendering = `pixelated`;
canvasctx.style.position = `fixed`;
canvasctx.style.zIndex = `999`;
canvasctx.style.left = `0px`;
canvasctx.style.top = `0px`;
canvasgl.style.position = `fixed`;
canvasgl.style.zIndex = `989`;
canvasgl.style.left = `0px`;
canvasgl.style.top = `0px`;
document.body.appendChild(canvasctx);
document.body.appendChild(canvasgl);
projectionMatrix = mat4.create();
modelViewMatrix = mat4.create();
aProjectionMatrix = mat4.create();
aModelViewMatrix = mat4.create();
ctx = canvasctx.getContext(`2d`);
gl = canvasgl.getContext(`webgl2`, {
'powerPreference': `high-performance`
});
if (ctx == null || typeof ctx == `undefined`) {
console.error(`Unable to get 2D context`);
alert(`Unable to get 2D context`);
return;
};
if (gl == null || typeof gl == `undefined`) {
console.error(`Unable to get WebGL2 context`);
alert(`Unable to get WebGL2 context`);
return;
};
resize();
var cb;
window.addEventListener(`resize`, () => {
clearTimeout(cb);
cb = setTimeout(resize, 150);
return;
});
if (ini() == false) {
then = performance.now() * 0.001;
requestAnimationFrame(l);
};
return;
});
return;
})();
* {
padding: 0px;
margin: 0px;
}
html,
body {
height: 100%;
width: 100%;
}
<script src="https://intre-webgl-rendering-engine.804kn.repl.co/gl-matrix-min.js"></script>
<!-- Modified version of gl-matrix as to not include imports -->
Related
I currently have a webgl renderer, and I'm trying to draw a box (Provided by https://github.com/mdn/dom-examples/blob/master/webgl-examples/tutorial/sample2/webgl-demo.js)
I have it working in my editors preview window:
But in the website, its just a blank screen:
If I press "save image as..." on my preview window, it will have the png outputted correctly
If I instead press "save image as..." on my website window, it will output an empty txt file.
Heres my code:
(() => {
window.addEventListener(`load`, () => {
try {
//////////////
//////////////
//////////////
const vsSource = [
`attribute vec4 Position;`,
`uniform mat4 CamMatrix;`,
`uniform mat4 WorldMatrix;`,
`void main() {`,
`gl_Position = CamMatrix * WorldMatrix * Position;`,
`}`
].join(`\n`);
const fsSource = [
`void main() {`,
`gl_FragColor = vec4(1.0, 1.0, 1.0, 1.0);`,
`}`
].join(`\n`);
//////////////
//////////////
//////////////
const canvas = document.getElementById(`main`);
const gl = canvas.getContext(`webgl2`, {
antialias: true,
powerPreference: "high-performance",
premultipliedAlpha: true,
preserveDrawingBuffer: false
});
var program;
var width = document.body.clientWidth;
var height = document.body.clientHeight;
canvas.width = width;
canvas.height = height;
var locations = {};
var buffers = {
'box': {}
};
var f = 1.0 / Math.tan((45.0 * Math.PI / 180.0) / 2.0);
var nf = 1 / (0.1 - 100.0);
var cammatrix = [
f / (width / height), 0.0, 0.0, 0.0,
0.0, f, 0.0, 0.0,
0.0, 0.0, (100.0 + 0.1) * nf, -1.0,
0.0, 0.0, 2 * 100.0 * 0.1 * nf, 0.0
];
var worldmatrix = [
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, 0.0, 1.0
];
worldmatrix[12] = worldmatrix[0] * -0.0 + worldmatrix[4] * 0.0 + worldmatrix[8] * -6.0 + worldmatrix[12];
worldmatrix[13] = worldmatrix[1] * -0.0 + worldmatrix[5] * 0.0 + worldmatrix[9] * -6.0 + worldmatrix[13];
worldmatrix[14] = worldmatrix[2] * -0.0 + worldmatrix[6] * 0.0 + worldmatrix[10] * -6.0 + worldmatrix[14];
worldmatrix[15] = worldmatrix[3] * -0.0 + worldmatrix[7] * 0.0 + worldmatrix[11] * -6.0 + worldmatrix[15];
//////////////
//////////////
//////////////
window.addEventListener(`resize`, (e) => {
width = document.body.clientWidth;
height = document.body.clientHeight;
canvas.width = width;
canvas.height = height;
var f = 1.0 / Math.tan((45.0 * Math.PI / 180.0) / 2.0);
var nf = 1 / (0.1 - 100.0);
cammatrix = [
f / (width / height), 0.0, 0.0, 0.0,
0.0, f, 0.0, 0.0,
0.0, 0.0, (100.0 + 0.1) * nf, -1.0,
0.0, 0.0, 2 * 100.0 * 0.1 * nf, 0.0
];
});
//////////////
//////////////
//////////////
function frame() {
//////////////
//////////////
gl.viewport(0, 0, width, height);
gl.clearColor(0.0, 0.0, 0.0, 1.0);
gl.clearDepth(1.0);
gl.enable(gl.DEPTH_TEST);
gl.depthFunc(gl.LEQUAL);
gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
//////////////
//////////////
{
gl.bindBuffer(gl.ARRAY_BUFFER, buffers[`box`][`gl`]);
gl.vertexAttribPointer(locations[`pos`], 2, gl.FLOAT, false, 0, 0);
gl.enableVertexAttribArray(locations[`pos`]);
}
gl.useProgram(program);
//////////////
//////////////
gl.uniformMatrix4fv(locations[`cam`], false, cammatrix);
gl.uniformMatrix4fv(locations[`world`], false, worldmatrix);
//////////////
//////////////
gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4);
//////////////
//////////////
}
const vertexShader = gl.createShader(gl.VERTEX_SHADER);
gl.shaderSource(vertexShader, vsSource);
gl.compileShader(vertexShader);
if (!gl.getShaderParameter(vertexShader, gl.COMPILE_STATUS)) {
alert('An error occurred compiling the shaders: ' + gl.getShaderInfoLog(vertexShader));
gl.deleteShader(vertexShader);
return null;
}
const fragmentShader = gl.createShader(gl.FRAGMENT_SHADER);
gl.shaderSource(fragmentShader, fsSource);
gl.compileShader(fragmentShader);
if (!gl.getShaderParameter(fragmentShader, gl.COMPILE_STATUS)) {
alert('An error occurred compiling the shaders: ' + gl.getShaderInfoLog(fragmentShader));
gl.deleteShader(fragmentShader);
return null;
}
program = gl.createProgram();
gl.attachShader(program, vertexShader);
gl.attachShader(program, fragmentShader);
gl.linkProgram(program);
if (!gl.getProgramParameter(program, gl.LINK_STATUS)) {
alert('Unable to initialize the shader program: ' + gl.getProgramInfoLog(program));
return null;
}
locations[`pos`] = gl.getAttribLocation(program, `Position`);
locations[`cam`] = gl.getUniformLocation(program, `CamMatrix`);
locations[`world`] = gl.getUniformLocation(program, `WorldMatrix`);
var vectors = [
1.0, 1.0, -1.0, 1.0,
1.0, -1.0, -1.0, -1.0,
];
var bbuf = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, bbuf);
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(vectors), gl.STATIC_DRAW);
buffers[`box`][`vectors`] = vectors;
buffers[`box`][`gl`] = bbuf;
setTimeout(frame);
} catch (err) {
alert(`There was an error.\n\nStack: "${err.stack.toString()}"\nMessage: "${err.message.toString()}"\nName: "${err.name.toString()}"`);
return;
};
});
})();
body {
margin: 0;
}
#main {
position: fixed;
left: 0px;
top: 0px;
width: 100vw;
height: 100vh;
z-index: 1;
}
<!DOCTYPE html>
<html lang="en-US">
<head>
<title>OS</title>
</head>
<body>
<canvas id="main"></canvas>
</body>
</html>
I’m a contributor to MDN, where there’s an issue that was raised about a code demo in the Animating textures in WebGL article.
In order to have a self-contained MRE in this question, I’ve copied the demo code from that article into a runnable snippet here (below).
The demo creates a video element, sets the autoplay attribute on it, and then layers that video over a 3D rotating cube as a texture.
My question is: How can we make that demo code work as expected on iOS?
Expected behavior
The expected behavior is for the browser to display a 3D rotating cube, with the video from https://mdn.github.io/dom-examples/webgl-examples/tutorial/sample8/Firefox.mp4 layered over the rotating cube as a texture — like this:
…and in all current browsers on all platforms except for iOS, that is the actual behavior.
Actual behavior on iOS
On iOS, in Safari (mobile) — and I think in any other browsers running on iOS (since they all use WebKit as their engine) — a 3D rotating cube is displayed as expected; however, the video is not layered over the rotating cube as expected but instead the browser just displays a plain blue rotating cube, with no texture at all layered over it — like this:
Troubleshooting steps taken, and error messages seen
I don’t have iOS browser debugging tools myself, but an issue comment from an MDN user says the browser logs this:
setupVideo webgl-demo.js:141 — Unhandled Promise Rejection: NotAllowedError: The request is not allowed by the user agent or the platform in the current context, possibly because the user denied permission.
Because the line numbers don’t match up directly, I’m not sure what’s a line 141 in the code as the iOS browser sees it — but https://github.com/mdn/dom-examples/blob/master/webgl-examples/tutorial/sample8/webgl-demo.js#L154 is my best guess — the video.play() call.
So the same issue comment which quotes the above message also references Browser denying javascript play() as a related SO question.
…and that SO question cites a similar “not allowed by the user agent or the platform in the current context, possibly because the user denied permission” error message.
Runnable code snippet that doesn’t work as expected on iOS
var cubeRotation = 0.0;
var copyVideo = false;
main();
function main() {
const canvas = document.querySelector("#glcanvas");
const gl = canvas.getContext("webgl");
if (!gl) {
alert(
"Unable to initialize WebGL. Your browser or machine may not support it."
);
return;
}
const vsSource = `
attribute vec4 aVertexPosition;
attribute vec3 aVertexNormal;
attribute vec2 aTextureCoord;
uniform mat4 uNormalMatrix;
uniform mat4 uModelViewMatrix;
uniform mat4 uProjectionMatrix;
varying highp vec2 vTextureCoord;
varying highp vec3 vLighting;
void main(void) {
gl_Position = uProjectionMatrix * uModelViewMatrix * aVertexPosition;
vTextureCoord = aTextureCoord;
// Apply lighting effect
highp vec3 ambientLight = vec3(0.3, 0.3, 0.3);
highp vec3 directionalLightColor = vec3(1, 1, 1);
highp vec3 directionalVector = normalize(vec3(0.85, 0.8, 0.75));
highp vec4 transformedNormal = uNormalMatrix * vec4(aVertexNormal, 1.0);
highp float directional = max(dot(transformedNormal.xyz, directionalVector), 0.0);
vLighting = ambientLight + (directionalLightColor * directional);
}
`;
const fsSource = `
varying highp vec2 vTextureCoord;
varying highp vec3 vLighting;
uniform sampler2D uSampler;
void main(void) {
highp vec4 texelColor = texture2D(uSampler, vTextureCoord);
gl_FragColor = vec4(texelColor.rgb * vLighting, texelColor.a);
}
`;
const shaderProgram = initShaderProgram(gl, vsSource, fsSource);
const programInfo = {
program: shaderProgram,
attribLocations: {
vertexPosition: gl.getAttribLocation(shaderProgram, "aVertexPosition"),
vertexNormal: gl.getAttribLocation(shaderProgram, "aVertexNormal"),
textureCoord: gl.getAttribLocation(shaderProgram, "aTextureCoord"),
},
uniformLocations: {
projectionMatrix: gl.getUniformLocation(
shaderProgram,
"uProjectionMatrix"
),
modelViewMatrix: gl.getUniformLocation(shaderProgram, "uModelViewMatrix"),
normalMatrix: gl.getUniformLocation(shaderProgram, "uNormalMatrix"),
uSampler: gl.getUniformLocation(shaderProgram, "uSampler"),
},
};
const buffers = initBuffers(gl);
const texture = initTexture(gl);
const video = setupVideo("https://mdn.github.io/dom-examples/webgl-examples/tutorial/sample8/Firefox.mp4");
var then = 0;
function render(now) {
now *= 0.001; // convert to seconds
const deltaTime = now - then;
then = now;
if (copyVideo) {
updateTexture(gl, texture, video);
}
drawScene(gl, programInfo, buffers, texture, deltaTime);
requestAnimationFrame(render);
}
requestAnimationFrame(render);
}
function setupVideo(url) {
const video = document.createElement("video");
var playing = false;
var timeupdate = false;
video.muted = true;
video.autoplay = true;
video.loop = true;
video.crossOrigin = "anonymous";
video.addEventListener(
"playing",
function () {
playing = true;
checkReady();
},
true
);
video.addEventListener(
"timeupdate",
function () {
timeupdate = true;
checkReady();
},
true
);
video.src = url;
video.play();
function checkReady() {
if (playing && timeupdate) {
copyVideo = true;
}
}
return video;
}
function initBuffers(gl) {
const positionBuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer);
const positions = [
-1.0, -1.0, 1.0, 1.0, -1.0, 1.0, 1.0, 1.0, 1.0, -1.0, 1.0, 1.0,
-1.0, -1.0, -1.0, -1.0, 1.0, -1.0, 1.0, 1.0, -1.0, 1.0, -1.0, -1.0,
-1.0, 1.0, -1.0, -1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, -1.0,
-1.0, -1.0, -1.0, 1.0, -1.0, -1.0, 1.0, -1.0, 1.0, -1.0, -1.0, 1.0,
1.0, -1.0, -1.0, 1.0, 1.0, -1.0, 1.0, 1.0, 1.0, 1.0, -1.0, 1.0,
-1.0, -1.0, -1.0, -1.0, -1.0, 1.0, -1.0, 1.0, 1.0, -1.0, 1.0, -1.0,
];
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(positions), gl.STATIC_DRAW);
const normalBuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, normalBuffer);
const vertexNormals = [
0.0, 0.0, 1.0, 0.0, 0.0, 1.0, 0.0, 0.0, 1.0, 0.0, 0.0, 1.0,
0.0, 0.0, -1.0, 0.0, 0.0, -1.0, 0.0, 0.0, -1.0, 0.0, 0.0, -1.0,
0.0, 1.0, 0.0, 0.0, 1.0, 0.0, 0.0, 1.0, 0.0, 0.0, 1.0, 0.0,
0.0, -1.0, 0.0, 0.0, -1.0, 0.0, 0.0, -1.0, 0.0, 0.0, -1.0, 0.0,
1.0, 0.0, 0.0, 1.0, 0.0, 0.0, 1.0, 0.0, 0.0, 1.0, 0.0, 0.0,
-1.0, 0.0, 0.0, -1.0, 0.0, 0.0, -1.0, 0.0, 0.0, -1.0, 0.0, 0.0,
];
gl.bufferData(
gl.ARRAY_BUFFER,
new Float32Array(vertexNormals),
gl.STATIC_DRAW
);
const textureCoordBuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, textureCoordBuffer);
const textureCoordinates = [
0.0, 0.0, 1.0, 0.0, 1.0, 1.0, 0.0, 1.0,
0.0, 0.0, 1.0, 0.0, 1.0, 1.0, 0.0, 1.0,
0.0, 0.0, 1.0, 0.0, 1.0, 1.0, 0.0, 1.0,
0.0, 0.0, 1.0, 0.0, 1.0, 1.0, 0.0, 1.0,
0.0, 0.0, 1.0, 0.0, 1.0, 1.0, 0.0, 1.0,
0.0, 0.0, 1.0, 0.0, 1.0, 1.0, 0.0, 1.0,
];
gl.bufferData(
gl.ARRAY_BUFFER,
new Float32Array(textureCoordinates),
gl.STATIC_DRAW
);
const indexBuffer = gl.createBuffer();
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, indexBuffer);
const indices = [
0,
1,
2,
0,
2,
3, // front
4,
5,
6,
4,
6,
7, // back
8,
9,
10,
8,
10,
11, // top
12,
13,
14,
12,
14,
15, // bottom
16,
17,
18,
16,
18,
19, // right
20,
21,
22,
20,
22,
23, // left
];
gl.bufferData(
gl.ELEMENT_ARRAY_BUFFER,
new Uint16Array(indices),
gl.STATIC_DRAW
);
return {
position: positionBuffer,
normal: normalBuffer,
textureCoord: textureCoordBuffer,
indices: indexBuffer,
};
}
function initTexture(gl, url) {
const texture = gl.createTexture();
gl.bindTexture(gl.TEXTURE_2D, texture);
const level = 0;
const internalFormat = gl.RGBA;
const width = 1;
const height = 1;
const border = 0;
const srcFormat = gl.RGBA;
const srcType = gl.UNSIGNED_BYTE;
const pixel = new Uint8Array([0, 0, 255, 255]); // opaque blue
gl.texImage2D(
gl.TEXTURE_2D,
level,
internalFormat,
width,
height,
border,
srcFormat,
srcType,
pixel
);
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.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
return texture;
}
function updateTexture(gl, texture, video) {
const level = 0;
const internalFormat = gl.RGBA;
const srcFormat = gl.RGBA;
const srcType = gl.UNSIGNED_BYTE;
gl.bindTexture(gl.TEXTURE_2D, texture);
gl.texImage2D(
gl.TEXTURE_2D,
level,
internalFormat,
srcFormat,
srcType,
video
);
}
function isPowerOf2(value) {
return (value & (value - 1)) == 0;
}
function drawScene(gl, programInfo, buffers, texture, deltaTime) {
gl.clearColor(0.0, 0.0, 0.0, 1.0); // Clear to black, fully opaque
gl.clearDepth(1.0); // Clear everything
gl.enable(gl.DEPTH_TEST); // Enable depth testing
gl.depthFunc(gl.LEQUAL); // Near things obscure far things
gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
const fieldOfView = (45 * Math.PI) / 180; // in radians
const aspect = gl.canvas.clientWidth / gl.canvas.clientHeight;
const zNear = 0.1;
const zFar = 100.0;
const projectionMatrix = mat4.create();
mat4.perspective(projectionMatrix, fieldOfView, aspect, zNear, zFar);
const modelViewMatrix = mat4.create();
mat4.translate(
modelViewMatrix, // destination matrix
modelViewMatrix, // matrix to translate
[-0.0, 0.0, -6.0]
); // amount to translate
mat4.rotate(
modelViewMatrix, // destination matrix
modelViewMatrix, // matrix to rotate
cubeRotation, // amount to rotate in radians
[0, 0, 1]
); // axis to rotate around (Z)
mat4.rotate(
modelViewMatrix, // destination matrix
modelViewMatrix, // matrix to rotate
cubeRotation * 0.7, // amount to rotate in radians
[0, 1, 0]
); // axis to rotate around (X)
const normalMatrix = mat4.create();
mat4.invert(normalMatrix, modelViewMatrix);
mat4.transpose(normalMatrix, normalMatrix);
{
const numComponents = 3;
const type = gl.FLOAT;
const normalize = false;
const stride = 0;
const offset = 0;
gl.bindBuffer(gl.ARRAY_BUFFER, buffers.position);
gl.vertexAttribPointer(
programInfo.attribLocations.vertexPosition,
numComponents,
type,
normalize,
stride,
offset
);
gl.enableVertexAttribArray(programInfo.attribLocations.vertexPosition);
}
{
const numComponents = 2;
const type = gl.FLOAT;
const normalize = false;
const stride = 0;
const offset = 0;
gl.bindBuffer(gl.ARRAY_BUFFER, buffers.textureCoord);
gl.vertexAttribPointer(
programInfo.attribLocations.textureCoord,
numComponents,
type,
normalize,
stride,
offset
);
gl.enableVertexAttribArray(programInfo.attribLocations.textureCoord);
}
{
const numComponents = 3;
const type = gl.FLOAT;
const normalize = false;
const stride = 0;
const offset = 0;
gl.bindBuffer(gl.ARRAY_BUFFER, buffers.normal);
gl.vertexAttribPointer(
programInfo.attribLocations.vertexNormal,
numComponents,
type,
normalize,
stride,
offset
);
gl.enableVertexAttribArray(programInfo.attribLocations.vertexNormal);
}
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, buffers.indices);
gl.useProgram(programInfo.program);
gl.uniformMatrix4fv(
programInfo.uniformLocations.projectionMatrix,
false,
projectionMatrix
);
gl.uniformMatrix4fv(
programInfo.uniformLocations.modelViewMatrix,
false,
modelViewMatrix
);
gl.uniformMatrix4fv(
programInfo.uniformLocations.normalMatrix,
false,
normalMatrix
);
gl.activeTexture(gl.TEXTURE0);
gl.bindTexture(gl.TEXTURE_2D, texture);
gl.uniform1i(programInfo.uniformLocations.uSampler, 0);
{
const vertexCount = 36;
const type = gl.UNSIGNED_SHORT;
const offset = 0;
gl.drawElements(gl.TRIANGLES, vertexCount, type, offset);
}
cubeRotation += deltaTime;
}
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);
if (!gl.getProgramParameter(shaderProgram, gl.LINK_STATUS)) {
alert(
"Unable to initialize the shader program: " +
gl.getProgramInfoLog(shaderProgram)
);
return null;
}
return shaderProgram;
}
function loadShader(gl, type, source) {
const shader = gl.createShader(type);
gl.shaderSource(shader, source);
gl.compileShader(shader);
if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) {
alert(
"An error occurred compiling the shaders: " + gl.getShaderInfoLog(shader)
);
gl.deleteShader(shader);
return null;
}
return shader;
}
canvas { border: 2px solid black; background-color: black; }
video { display: none; }
<!doctype html><meta charset="utf-8"><title>WebGL Demo</title>
<script src="https://cdnjs.cloudflare.com/ajax/libs/gl-matrix/2.8.1/gl-matrix-min.js"></script>
<canvas id="glcanvas" width="640" height="480"></canvas>
Solution: Drop the autoplay attribute, and add the playsInline attribute.
The clue on playsInline came from https://stackoverflow.com/a/65965158/441757.
The realization on autoplay came from trial-and-error — by dropping it to see what’d happen.
And what happens is that without that attribute set, the demo still continues to work in all other browsers as expected — but then also works as expected on iOS too.
So it seems: when using a video as a WebGL texture, it’s unnecessary to set autoplay to get the video to play — but it is necessary to set playsInline to make it work on iOS.
Runnable snippet that works in all browsers — including on iOS
var cubeRotation = 0.0;
var copyVideo = false;
main();
function main() {
const canvas = document.querySelector("#glcanvas");
const gl = canvas.getContext("webgl");
if (!gl) {
alert(
"Unable to initialize WebGL. Your browser or machine may not support it."
);
return;
}
const vsSource = `
attribute vec4 aVertexPosition;
attribute vec3 aVertexNormal;
attribute vec2 aTextureCoord;
uniform mat4 uNormalMatrix;
uniform mat4 uModelViewMatrix;
uniform mat4 uProjectionMatrix;
varying highp vec2 vTextureCoord;
varying highp vec3 vLighting;
void main(void) {
gl_Position = uProjectionMatrix * uModelViewMatrix * aVertexPosition;
vTextureCoord = aTextureCoord;
// Apply lighting effect
highp vec3 ambientLight = vec3(0.3, 0.3, 0.3);
highp vec3 directionalLightColor = vec3(1, 1, 1);
highp vec3 directionalVector = normalize(vec3(0.85, 0.8, 0.75));
highp vec4 transformedNormal = uNormalMatrix * vec4(aVertexNormal, 1.0);
highp float directional = max(dot(transformedNormal.xyz, directionalVector), 0.0);
vLighting = ambientLight + (directionalLightColor * directional);
}
`;
const fsSource = `
varying highp vec2 vTextureCoord;
varying highp vec3 vLighting;
uniform sampler2D uSampler;
void main(void) {
highp vec4 texelColor = texture2D(uSampler, vTextureCoord);
gl_FragColor = vec4(texelColor.rgb * vLighting, texelColor.a);
}
`;
const shaderProgram = initShaderProgram(gl, vsSource, fsSource);
const programInfo = {
program: shaderProgram,
attribLocations: {
vertexPosition: gl.getAttribLocation(shaderProgram, "aVertexPosition"),
vertexNormal: gl.getAttribLocation(shaderProgram, "aVertexNormal"),
textureCoord: gl.getAttribLocation(shaderProgram, "aTextureCoord"),
},
uniformLocations: {
projectionMatrix: gl.getUniformLocation(
shaderProgram,
"uProjectionMatrix"
),
modelViewMatrix: gl.getUniformLocation(shaderProgram, "uModelViewMatrix"),
normalMatrix: gl.getUniformLocation(shaderProgram, "uNormalMatrix"),
uSampler: gl.getUniformLocation(shaderProgram, "uSampler"),
},
};
const buffers = initBuffers(gl);
const texture = initTexture(gl);
const video = setupVideo("https://mdn.github.io/dom-examples/webgl-examples/tutorial/sample8/Firefox.mp4");
var then = 0;
function render(now) {
now *= 0.001; // convert to seconds
const deltaTime = now - then;
then = now;
if (copyVideo) {
updateTexture(gl, texture, video);
}
drawScene(gl, programInfo, buffers, texture, deltaTime);
requestAnimationFrame(render);
}
requestAnimationFrame(render);
}
function setupVideo(url) {
const video = document.createElement("video");
var playing = false;
var timeupdate = false;
video.muted = true;
video.playsInline = true;
video.loop = true;
video.crossOrigin = "anonymous";
video.addEventListener(
"playing",
function () {
playing = true;
checkReady();
},
true
);
video.addEventListener(
"timeupdate",
function () {
timeupdate = true;
checkReady();
},
true
);
video.src = url;
video.play();
function checkReady() {
if (playing && timeupdate) {
copyVideo = true;
}
}
return video;
}
function initBuffers(gl) {
const positionBuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer);
const positions = [
-1.0, -1.0, 1.0, 1.0, -1.0, 1.0, 1.0, 1.0, 1.0, -1.0, 1.0, 1.0,
-1.0, -1.0, -1.0, -1.0, 1.0, -1.0, 1.0, 1.0, -1.0, 1.0, -1.0, -1.0,
-1.0, 1.0, -1.0, -1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, -1.0,
-1.0, -1.0, -1.0, 1.0, -1.0, -1.0, 1.0, -1.0, 1.0, -1.0, -1.0, 1.0,
1.0, -1.0, -1.0, 1.0, 1.0, -1.0, 1.0, 1.0, 1.0, 1.0, -1.0, 1.0,
-1.0, -1.0, -1.0, -1.0, -1.0, 1.0, -1.0, 1.0, 1.0, -1.0, 1.0, -1.0,
];
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(positions), gl.STATIC_DRAW);
const normalBuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, normalBuffer);
const vertexNormals = [
0.0, 0.0, 1.0, 0.0, 0.0, 1.0, 0.0, 0.0, 1.0, 0.0, 0.0, 1.0,
0.0, 0.0, -1.0, 0.0, 0.0, -1.0, 0.0, 0.0, -1.0, 0.0, 0.0, -1.0,
0.0, 1.0, 0.0, 0.0, 1.0, 0.0, 0.0, 1.0, 0.0, 0.0, 1.0, 0.0,
0.0, -1.0, 0.0, 0.0, -1.0, 0.0, 0.0, -1.0, 0.0, 0.0, -1.0, 0.0,
1.0, 0.0, 0.0, 1.0, 0.0, 0.0, 1.0, 0.0, 0.0, 1.0, 0.0, 0.0,
-1.0, 0.0, 0.0, -1.0, 0.0, 0.0, -1.0, 0.0, 0.0, -1.0, 0.0, 0.0,
];
gl.bufferData(
gl.ARRAY_BUFFER,
new Float32Array(vertexNormals),
gl.STATIC_DRAW
);
const textureCoordBuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, textureCoordBuffer);
const textureCoordinates = [
0.0, 0.0, 1.0, 0.0, 1.0, 1.0, 0.0, 1.0,
0.0, 0.0, 1.0, 0.0, 1.0, 1.0, 0.0, 1.0,
0.0, 0.0, 1.0, 0.0, 1.0, 1.0, 0.0, 1.0,
0.0, 0.0, 1.0, 0.0, 1.0, 1.0, 0.0, 1.0,
0.0, 0.0, 1.0, 0.0, 1.0, 1.0, 0.0, 1.0,
0.0, 0.0, 1.0, 0.0, 1.0, 1.0, 0.0, 1.0,
];
gl.bufferData(
gl.ARRAY_BUFFER,
new Float32Array(textureCoordinates),
gl.STATIC_DRAW
);
const indexBuffer = gl.createBuffer();
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, indexBuffer);
const indices = [
0,
1,
2,
0,
2,
3, // front
4,
5,
6,
4,
6,
7, // back
8,
9,
10,
8,
10,
11, // top
12,
13,
14,
12,
14,
15, // bottom
16,
17,
18,
16,
18,
19, // right
20,
21,
22,
20,
22,
23, // left
];
gl.bufferData(
gl.ELEMENT_ARRAY_BUFFER,
new Uint16Array(indices),
gl.STATIC_DRAW
);
return {
position: positionBuffer,
normal: normalBuffer,
textureCoord: textureCoordBuffer,
indices: indexBuffer,
};
}
function initTexture(gl, url) {
const texture = gl.createTexture();
gl.bindTexture(gl.TEXTURE_2D, texture);
const level = 0;
const internalFormat = gl.RGBA;
const width = 1;
const height = 1;
const border = 0;
const srcFormat = gl.RGBA;
const srcType = gl.UNSIGNED_BYTE;
const pixel = new Uint8Array([0, 0, 255, 255]); // opaque blue
gl.texImage2D(
gl.TEXTURE_2D,
level,
internalFormat,
width,
height,
border,
srcFormat,
srcType,
pixel
);
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.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
return texture;
}
function updateTexture(gl, texture, video) {
const level = 0;
const internalFormat = gl.RGBA;
const srcFormat = gl.RGBA;
const srcType = gl.UNSIGNED_BYTE;
gl.bindTexture(gl.TEXTURE_2D, texture);
gl.texImage2D(
gl.TEXTURE_2D,
level,
internalFormat,
srcFormat,
srcType,
video
);
}
function isPowerOf2(value) {
return (value & (value - 1)) == 0;
}
function drawScene(gl, programInfo, buffers, texture, deltaTime) {
gl.clearColor(0.0, 0.0, 0.0, 1.0); // Clear to black, fully opaque
gl.clearDepth(1.0); // Clear everything
gl.enable(gl.DEPTH_TEST); // Enable depth testing
gl.depthFunc(gl.LEQUAL); // Near things obscure far things
gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
const fieldOfView = (45 * Math.PI) / 180; // in radians
const aspect = gl.canvas.clientWidth / gl.canvas.clientHeight;
const zNear = 0.1;
const zFar = 100.0;
const projectionMatrix = mat4.create();
mat4.perspective(projectionMatrix, fieldOfView, aspect, zNear, zFar);
const modelViewMatrix = mat4.create();
mat4.translate(
modelViewMatrix, // destination matrix
modelViewMatrix, // matrix to translate
[-0.0, 0.0, -6.0]
); // amount to translate
mat4.rotate(
modelViewMatrix, // destination matrix
modelViewMatrix, // matrix to rotate
cubeRotation, // amount to rotate in radians
[0, 0, 1]
); // axis to rotate around (Z)
mat4.rotate(
modelViewMatrix, // destination matrix
modelViewMatrix, // matrix to rotate
cubeRotation * 0.7, // amount to rotate in radians
[0, 1, 0]
); // axis to rotate around (X)
const normalMatrix = mat4.create();
mat4.invert(normalMatrix, modelViewMatrix);
mat4.transpose(normalMatrix, normalMatrix);
{
const numComponents = 3;
const type = gl.FLOAT;
const normalize = false;
const stride = 0;
const offset = 0;
gl.bindBuffer(gl.ARRAY_BUFFER, buffers.position);
gl.vertexAttribPointer(
programInfo.attribLocations.vertexPosition,
numComponents,
type,
normalize,
stride,
offset
);
gl.enableVertexAttribArray(programInfo.attribLocations.vertexPosition);
}
{
const numComponents = 2;
const type = gl.FLOAT;
const normalize = false;
const stride = 0;
const offset = 0;
gl.bindBuffer(gl.ARRAY_BUFFER, buffers.textureCoord);
gl.vertexAttribPointer(
programInfo.attribLocations.textureCoord,
numComponents,
type,
normalize,
stride,
offset
);
gl.enableVertexAttribArray(programInfo.attribLocations.textureCoord);
}
{
const numComponents = 3;
const type = gl.FLOAT;
const normalize = false;
const stride = 0;
const offset = 0;
gl.bindBuffer(gl.ARRAY_BUFFER, buffers.normal);
gl.vertexAttribPointer(
programInfo.attribLocations.vertexNormal,
numComponents,
type,
normalize,
stride,
offset
);
gl.enableVertexAttribArray(programInfo.attribLocations.vertexNormal);
}
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, buffers.indices);
gl.useProgram(programInfo.program);
gl.uniformMatrix4fv(
programInfo.uniformLocations.projectionMatrix,
false,
projectionMatrix
);
gl.uniformMatrix4fv(
programInfo.uniformLocations.modelViewMatrix,
false,
modelViewMatrix
);
gl.uniformMatrix4fv(
programInfo.uniformLocations.normalMatrix,
false,
normalMatrix
);
gl.activeTexture(gl.TEXTURE0);
gl.bindTexture(gl.TEXTURE_2D, texture);
gl.uniform1i(programInfo.uniformLocations.uSampler, 0);
{
const vertexCount = 36;
const type = gl.UNSIGNED_SHORT;
const offset = 0;
gl.drawElements(gl.TRIANGLES, vertexCount, type, offset);
}
cubeRotation += deltaTime;
}
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);
if (!gl.getProgramParameter(shaderProgram, gl.LINK_STATUS)) {
alert(
"Unable to initialize the shader program: " +
gl.getProgramInfoLog(shaderProgram)
);
return null;
}
return shaderProgram;
}
function loadShader(gl, type, source) {
const shader = gl.createShader(type);
gl.shaderSource(shader, source);
gl.compileShader(shader);
if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) {
alert(
"An error occurred compiling the shaders: " + gl.getShaderInfoLog(shader)
);
gl.deleteShader(shader);
return null;
}
return shader;
}
canvas { border: 2px solid black; background-color: black; }
video { display: none; }
<!doctype html><meta charset="utf-8"><title>WebGL Demo</title>
<script src="https://cdnjs.cloudflare.com/ajax/libs/gl-matrix/2.8.1/gl-matrix-min.js"></script>
<canvas id="glcanvas" width="640" height="480"></canvas>
I am trying to render an indexed cube (with duplicate vertices to achieve flat shading). I set up a position buffer, an indices buffer, set the shader inputs and draw it using gl.drawElements(gl.TRIANGLES, ...):
However, on the screen, I only see the vertices, but the triangles are not being rendered.
I have put prints with gl.getError() after each gl call, but all return 0 (no error). Here is the live demo (the cube can be rotated by clicking and dragging on the canvas):
function printError(gl, msg)
{
console.log(msg + " " + gl.getError());
}
let clicking = false;
let lastXPos = -1;
let lastYPos = -1;
let rotationSpeed = 0.5 // (deg/pixel) 0.1 degree rotation on a given axis per pixel
let pitch = 0.0
let maxPitch = 90.0
let yaw = 0.0
let projMatrix = Object();
let modelViewMatrix = Object();
let buffers = Object();
let programInfo = Object();
function deg2Rad(degrees)
{
return degrees * (Math.PI / 180.0);
}
function main_gl()
{
const canvas = document.querySelector('#glcanvas');
const gl = canvas.getContext('webgl2');
// If we don't have a GL context, give up now
if (!gl)
{
alert('Unable to initialize WebGL. Your browser or machine may not support it.');
return;
}
// Vertex shader program
const vsSource = `
attribute vec4 aVertexPosition;
uniform mat4 uModelViewMatrix;
uniform mat4 uProjectionMatrix;
void main(void) {
gl_Position = uProjectionMatrix * uModelViewMatrix * aVertexPosition;
}
`;
// Fragment shader program
const fsSource = `
void main(void) {
gl_FragColor = vec4(1, 1, 1, 1);
}
`;
// Initialize a shader program; this is where all the lighting
// for the vertices and so forth is established.
const shaderProgram = initShaderProgram(gl, vsSource, fsSource);
// Collect all the info needed to use the shader program.
// Look up which attributes our shader program is using
// for aVertexPosition, aVevrtexColor and also
// look up uniform locations.
programInfo =
{
program: shaderProgram,
attribLocations:
{
vertexPosition: gl.getAttribLocation(shaderProgram, 'aVertexPosition'),
vertexColor: gl.getAttribLocation(shaderProgram, 'aVertexColor'),
},
uniformLocations:
{
projectionMatrix: gl.getUniformLocation(shaderProgram, 'uProjectionMatrix'),
modelViewMatrix: gl.getUniformLocation(shaderProgram, 'uModelViewMatrix'),
},
};
printError(gl, "Attributes and uniform gathered");
setUpInputCallbacks(canvas, gl);
// Here's where we call the routine that builds all the
// objects we'll be drawing.
buffers = initBuffers(gl);
setUpScene(gl);
// Draw the scene
drawScene(gl);
}
// ================================================================================================
function initBuffers(gl)
{
const positions = [
-1.0, 1.0, 1.0,
-1.0, -1.0, 1.0,
1.0, -1.0, 1.0,
1.0, -1.0, 1.0,
1.0, 1.0, 1.0,
-1.0, 1.0, 1.0,
-1.0, 1.0, -1.0,
-1.0, 1.0, 1.0,
1.0, 1.0, 1.0,
-1.0, 1.0, -1.0,
1.0, 1.0, 1.0,
1.0, 1.0, -1.0,
-1.0, -1.0, -1.0,
1.0, -1.0, 1.0,
-1.0, -1.0, 1.0,
-1.0, -1.0, -1.0,
1.0, -1.0, -1.0,
1.0, -1.0, 1.0,
-1.0, 1.0, -1.0,
-1.0, -1.0, -1.0,
-1.0, -1.0, 1.0,
-1.0, 1.0, -1.0,
-1.0, -1.0, 1.0,
-1.0, 1.0, 1.0,
1.0, 1.0, 1.0,
1.0, -1.0, 1.0,
1.0, -1.0, -1.0,
1.0, 1.0, 1.0,
1.0, -1.0, -1.0,
1.0, 1.0, -1.0,
1.0, 1.0, -1.0,
1.0, -1.0, -1.0,
-1.0, -1.0, -1.0,
1.0, 1.0, -1.0,
-1.0, -1.0, -1.0,
-1.0, 1.0, -1.0,
];
const positionBuffer = gl.createBuffer();
printError(gl, "Position buffer created");
gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer);
printError(gl, "Position bufffer binded");
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(positions), gl.STATIC_DRAW);
printError(gl, "Position buffer filled");
const indices = [
0, 1, 2,
3, 4, 5,
6, 7, 8,
9, 10, 11,
12, 13, 14,
15, 16, 17,
18, 19, 20,
21, 22, 23,
24, 25, 26,
27, 28, 29,
30, 31, 32,
33, 34, 35,
];
var indexBuffer = gl.createBuffer ();
printError(gl, "Index buffer created");
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, indexBuffer);
printError(gl, "Index buffer binded");
gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, new Uint16Array(indices), gl.STATIC_DRAW);
printError(gl, "Index buffer filled");
return {
indices: indexBuffer,
position: positionBuffer,
};
}
// ================================================================================================
function setUpScene(gl)
{
const fieldOfView = 45 * Math.PI / 180; // in radians
const aspect = gl.canvas.clientWidth / gl.canvas.clientHeight;
const zNear = 0.1;
const zFar = 100.0;
projMatrix = mat4.create();
// note: glmatrix.js always has the first argument
// as the destination to receive the result.
mat4.perspective(projMatrix,
fieldOfView,
aspect,
zNear,
zFar);
modelViewMatrix = mat4.create();
const vNumComponents = 3;
const vType = gl.FLOAT;
const vNormalize = false;
const vStride = 0;
const vOffset = 0;
gl.bindBuffer(gl.ARRAY_BUFFER, buffers.position);
printError(gl, "Bind position buffer");
gl.vertexAttribPointer(
programInfo.attribLocations.vertexPosition,
vNumComponents,
vType,
vNormalize,
vStride,
vOffset);
gl.enableVertexAttribArray(programInfo.attribLocations.vertexPosition);
printError(gl, "Setted shader position input");
}
function drawScene(gl)
{
printError(gl, "Draw scene begin");
gl.clearColor(0.0, 0.0, 0.0, 1.0);
gl.clearDepth(1.0); // Clear everything
gl.enable(gl.DEPTH_TEST);
gl.depthFunc(gl.LEQUAL);
gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
printError(gl, "OpenGL configured");
mat4.identity(modelViewMatrix);
mat4.translate(modelViewMatrix, modelViewMatrix, [-0.0, 0.0, -6.0]);
mat4.rotateX(modelViewMatrix, modelViewMatrix, deg2Rad(pitch));
mat4.rotateY(modelViewMatrix, modelViewMatrix, deg2Rad(yaw));
// Tell WebGL to use our program when drawing
gl.useProgram(programInfo.program);
printError(gl, "Bind program");
// Set the shader uniforms
gl.uniformMatrix4fv(
programInfo.uniformLocations.projectionMatrix,
false,
projMatrix);
gl.uniformMatrix4fv(
programInfo.uniformLocations.modelViewMatrix,
false,
modelViewMatrix);
printError(gl, "Setted uniforms");
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, buffers.indices);
printError(gl, "Bind index buffer");
gl.drawElements(gl.GL_LINES, 36, gl.UNSIGNED_SHORT, 0);
printError(gl, "Drawing");
}
// ================================================================================================
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();
printError(gl, "Program created");
gl.attachShader(shaderProgram, vertexShader);
printError(gl, "Vertex shader attached");
gl.attachShader(shaderProgram, fragmentShader);
printError(gl, "Fragment shader attached");
gl.linkProgram(shaderProgram);
printError(gl, "Program linked");
if (!gl.getProgramParameter(shaderProgram, gl.LINK_STATUS))
{
alert('Unable to initialize the shader program: ' + gl.getProgramInfoLog(shaderProgram));
return null;
}
return shaderProgram;
}
// ================================================================================================
function loadShader(gl, type, source)
{
const shader = gl.createShader(type);
printError(gl, "Shader created");
gl.shaderSource(shader, source);
printError(gl, "Shader source setted");
gl.compileShader(shader);
printError(gl, "Shader compiled");
if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS))
{
alert('An error occurred compiling the shaders: ' + gl.getShaderInfoLog(shader));
gl.deleteShader(shader);
return null;
}
return shader;
}
function setUpInputCallbacks(canvas, gl)
{
canvas.onmousedown = function(event)
{
clicking = event.button === 0;
}
canvas.onmouseup = function(event)
{
clicking = !event.button === 0;
lastXPos = -1;
lastYPos = -1;
}
canvas.onmousemove = function(event)
{
if(clicking)
{
if(lastXPos === -1 || lastYPos === -1)
{
lastXPos = event.clientX;
lastYPos = event.clientY;
}
else
{
xDiff = lastXPos - event.clientX;
yDiff = lastYPos - event.clientY;
lastXPos = event.clientX;
lastYPos = event.clientY;
rotatePitch = yDiff * rotationSpeed;
rotateYaw = xDiff * rotationSpeed;
pitch += rotatePitch;
pitchSign = pitch / Math.abs(pitch);
if(isNaN(pitchSign))
pitchSign = 1.0;
pitch = Math.min(Math.abs(pitch), maxPitch);
pitch *= pitchSign;
yaw += rotateYaw;
drawScene(gl);
}
}
}
canvas.onmouseout = function(event)
{
lastXPos = -1;
lastYPos = -1;
}
}
canvas {
border: 2px solid black;
background-color: black;
}
<html>
<head>
<script src="https://cdnjs.cloudflare.com/ajax/libs/gl-matrix/2.8.1/gl-matrix-min.js"
integrity="sha512-zhHQR0/H5SEBL3Wn6yYSaTTZej12z0hVZKOv3TwCUXT1z5qeqGcXJLLrbERYRScEDDpYIJhPC1fk31gqR783iQ=="
crossorigin="anonymous" defer>
</script>
</head>
<body onload="main_gl()">
<canvas id="glcanvas" width="640" height="480"></canvas>
</body>
Any idea what could be wrong?
GL_LINES and GL_TRIANGLES are not valid WebGL enumerator constants. However, LINES and TRIANGLES are valide:
gl.drawElements(gl.GL_LINES, 36, gl.UNSIGNED_SHORT, 0);
gl.drawElements(gl.LINES, 36, gl.UNSIGNED_SHORT, 0);
gl.drawElements(gl.GL_TRIANGLES, 36, gl.UNSIGNED_SHORT, 0);
gl.drawElements(gl.TRIANGLES, 36, gl.UNSIGNED_SHORT, 0);
function printError(gl, msg)
{
console.log(msg + " " + gl.getError());
}
let clicking = false;
let lastXPos = -1;
let lastYPos = -1;
let rotationSpeed = 0.5 // (deg/pixel) 0.1 degree rotation on a given axis per pixel
let pitch = 0.0
let maxPitch = 90.0
let yaw = 0.0
let projMatrix = Object();
let modelViewMatrix = Object();
let buffers = Object();
let programInfo = Object();
function deg2Rad(degrees)
{
return degrees * (Math.PI / 180.0);
}
function main_gl()
{
const canvas = document.querySelector('#glcanvas');
const gl = canvas.getContext('webgl2');
// If we don't have a GL context, give up now
if (!gl)
{
alert('Unable to initialize WebGL. Your browser or machine may not support it.');
return;
}
// Vertex shader program
const vsSource = `
attribute vec4 aVertexPosition;
uniform mat4 uModelViewMatrix;
uniform mat4 uProjectionMatrix;
void main(void) {
gl_Position = uProjectionMatrix * uModelViewMatrix * aVertexPosition;
}
`;
// Fragment shader program
const fsSource = `
void main(void) {
gl_FragColor = vec4(1, 1, 1, 1);
}
`;
// Initialize a shader program; this is where all the lighting
// for the vertices and so forth is established.
const shaderProgram = initShaderProgram(gl, vsSource, fsSource);
// Collect all the info needed to use the shader program.
// Look up which attributes our shader program is using
// for aVertexPosition, aVevrtexColor and also
// look up uniform locations.
programInfo =
{
program: shaderProgram,
attribLocations:
{
vertexPosition: gl.getAttribLocation(shaderProgram, 'aVertexPosition'),
vertexColor: gl.getAttribLocation(shaderProgram, 'aVertexColor'),
},
uniformLocations:
{
projectionMatrix: gl.getUniformLocation(shaderProgram, 'uProjectionMatrix'),
modelViewMatrix: gl.getUniformLocation(shaderProgram, 'uModelViewMatrix'),
},
};
printError(gl, "Attributes and uniform gathered");
setUpInputCallbacks(canvas, gl);
// Here's where we call the routine that builds all the
// objects we'll be drawing.
buffers = initBuffers(gl);
setUpScene(gl);
// Draw the scene
drawScene(gl);
}
// ================================================================================================
function initBuffers(gl)
{
const positions = [
-1.0, 1.0, 1.0,
-1.0, -1.0, 1.0,
1.0, -1.0, 1.0,
1.0, -1.0, 1.0,
1.0, 1.0, 1.0,
-1.0, 1.0, 1.0,
-1.0, 1.0, -1.0,
-1.0, 1.0, 1.0,
1.0, 1.0, 1.0,
-1.0, 1.0, -1.0,
1.0, 1.0, 1.0,
1.0, 1.0, -1.0,
-1.0, -1.0, -1.0,
1.0, -1.0, 1.0,
-1.0, -1.0, 1.0,
-1.0, -1.0, -1.0,
1.0, -1.0, -1.0,
1.0, -1.0, 1.0,
-1.0, 1.0, -1.0,
-1.0, -1.0, -1.0,
-1.0, -1.0, 1.0,
-1.0, 1.0, -1.0,
-1.0, -1.0, 1.0,
-1.0, 1.0, 1.0,
1.0, 1.0, 1.0,
1.0, -1.0, 1.0,
1.0, -1.0, -1.0,
1.0, 1.0, 1.0,
1.0, -1.0, -1.0,
1.0, 1.0, -1.0,
1.0, 1.0, -1.0,
1.0, -1.0, -1.0,
-1.0, -1.0, -1.0,
1.0, 1.0, -1.0,
-1.0, -1.0, -1.0,
-1.0, 1.0, -1.0,
];
const positionBuffer = gl.createBuffer();
printError(gl, "Position buffer created");
gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer);
printError(gl, "Position bufffer binded");
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(positions), gl.STATIC_DRAW);
printError(gl, "Position buffer filled");
const indices = [
0, 1, 2,
3, 4, 5,
6, 7, 8,
9, 10, 11,
12, 13, 14,
15, 16, 17,
18, 19, 20,
21, 22, 23,
24, 25, 26,
27, 28, 29,
30, 31, 32,
33, 34, 35,
];
var indexBuffer = gl.createBuffer ();
printError(gl, "Index buffer created");
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, indexBuffer);
printError(gl, "Index buffer binded");
gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, new Uint16Array(indices), gl.STATIC_DRAW);
printError(gl, "Index buffer filled");
return {
indices: indexBuffer,
position: positionBuffer,
};
}
// ================================================================================================
function setUpScene(gl)
{
const fieldOfView = 45 * Math.PI / 180; // in radians
const aspect = gl.canvas.clientWidth / gl.canvas.clientHeight;
const zNear = 0.1;
const zFar = 100.0;
projMatrix = mat4.create();
// note: glmatrix.js always has the first argument
// as the destination to receive the result.
mat4.perspective(projMatrix,
fieldOfView,
aspect,
zNear,
zFar);
modelViewMatrix = mat4.create();
const vNumComponents = 3;
const vType = gl.FLOAT;
const vNormalize = false;
const vStride = 0;
const vOffset = 0;
gl.bindBuffer(gl.ARRAY_BUFFER, buffers.position);
printError(gl, "Bind position buffer");
gl.vertexAttribPointer(
programInfo.attribLocations.vertexPosition,
vNumComponents,
vType,
vNormalize,
vStride,
vOffset);
gl.enableVertexAttribArray(programInfo.attribLocations.vertexPosition);
printError(gl, "Setted shader position input");
}
function drawScene(gl)
{
printError(gl, "Draw scene begin");
gl.clearColor(0.0, 0.0, 0.0, 1.0);
gl.clearDepth(1.0); // Clear everything
gl.enable(gl.DEPTH_TEST);
gl.depthFunc(gl.LEQUAL);
gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
printError(gl, "OpenGL configured");
mat4.identity(modelViewMatrix);
mat4.translate(modelViewMatrix, modelViewMatrix, [-0.0, 0.0, -6.0]);
mat4.rotateX(modelViewMatrix, modelViewMatrix, deg2Rad(pitch));
mat4.rotateY(modelViewMatrix, modelViewMatrix, deg2Rad(yaw));
// Tell WebGL to use our program when drawing
gl.useProgram(programInfo.program);
printError(gl, "Bind program");
// Set the shader uniforms
gl.uniformMatrix4fv(
programInfo.uniformLocations.projectionMatrix,
false,
projMatrix);
gl.uniformMatrix4fv(
programInfo.uniformLocations.modelViewMatrix,
false,
modelViewMatrix);
printError(gl, "Setted uniforms");
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, buffers.indices);
printError(gl, "Bind index buffer");
gl.drawElements(gl.TRIANGLES, 36, gl.UNSIGNED_SHORT, 0);
printError(gl, "Drawing");
}
// ================================================================================================
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();
printError(gl, "Program created");
gl.attachShader(shaderProgram, vertexShader);
printError(gl, "Vertex shader attached");
gl.attachShader(shaderProgram, fragmentShader);
printError(gl, "Fragment shader attached");
gl.linkProgram(shaderProgram);
printError(gl, "Program linked");
if (!gl.getProgramParameter(shaderProgram, gl.LINK_STATUS))
{
alert('Unable to initialize the shader program: ' + gl.getProgramInfoLog(shaderProgram));
return null;
}
return shaderProgram;
}
// ================================================================================================
function loadShader(gl, type, source)
{
const shader = gl.createShader(type);
printError(gl, "Shader created");
gl.shaderSource(shader, source);
printError(gl, "Shader source setted");
gl.compileShader(shader);
printError(gl, "Shader compiled");
if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS))
{
alert('An error occurred compiling the shaders: ' + gl.getShaderInfoLog(shader));
gl.deleteShader(shader);
return null;
}
return shader;
}
function setUpInputCallbacks(canvas, gl)
{
canvas.onmousedown = function(event)
{
clicking = event.button === 0;
}
canvas.onmouseup = function(event)
{
clicking = !event.button === 0;
lastXPos = -1;
lastYPos = -1;
}
canvas.onmousemove = function(event)
{
if(clicking)
{
if(lastXPos === -1 || lastYPos === -1)
{
lastXPos = event.clientX;
lastYPos = event.clientY;
}
else
{
xDiff = lastXPos - event.clientX;
yDiff = lastYPos - event.clientY;
lastXPos = event.clientX;
lastYPos = event.clientY;
rotatePitch = yDiff * rotationSpeed;
rotateYaw = xDiff * rotationSpeed;
pitch += rotatePitch;
pitchSign = pitch / Math.abs(pitch);
if(isNaN(pitchSign))
pitchSign = 1.0;
pitch = Math.min(Math.abs(pitch), maxPitch);
pitch *= pitchSign;
yaw += rotateYaw;
drawScene(gl);
}
}
}
canvas.onmouseout = function(event)
{
lastXPos = -1;
lastYPos = -1;
}
}
canvas {
border: 2px solid black;
background-color: black;
}
<html>
<head>
<script src="https://cdnjs.cloudflare.com/ajax/libs/gl-matrix/2.8.1/gl-matrix-min.js"
integrity="sha512-zhHQR0/H5SEBL3Wn6yYSaTTZej12z0hVZKOv3TwCUXT1z5qeqGcXJLLrbERYRScEDDpYIJhPC1fk31gqR783iQ=="
crossorigin="anonymous" defer>
</script>
</head>
<body onload="main_gl()">
<canvas id="glcanvas" width="640" height="480"></canvas>
</body>
I am working on a textured pyramid, but I got some warnings. I did it by step by step from the book "Beginning WebGL for HTML5" and it does not work.
Here is my code:
<!doctype html>
<html>
<head>
<title>Project 2</title>
<script type="text/javascript" src="gl-matrix-min.js"></script>
<style>
body{ background-color: grey; }
canvas{ background-color: white; }
</style>
<script id="shader-vs" type="x-shader/x-vertex">
attribute vec4 aVertexPosition;
attribute vec4 aVertexColor;
attribute vec2 aVertexTextureCoord;
varying highp vec2 vTextureCoord;
varying vec4 vColor;
/*
Couting On GPU
// Model matrix
uniform mat4 uMVMatrix;
// Projection matrix
uniform mat4 uPMatrix;
// View matrix
uniform mat4 uVMatrix;
*/
uniform mat4 uPVMatrix;
void main(void) {
vColor = aVertexColor;
gl_Position = uPVMatrix * aVertexPosition;
vTextureCoord = aVertexTextureCoord;
// gl_Position = uPMatrix * uVMatrix * uMVMatrix * aVertexPosition;
}
</script>
<script id="shader-fs" type="x-shader/x-fragment">
precision mediump float;
varying vec4 vColor;
varying highp vec2 vTextureCoord;
uniform sampler2D uSampler;
void main(void) {
gl_FragColor = texture2D(uSampler, vTextureCoord);
}
</script>
<script>
var gl = null,
canvas = null,
glProgram = null,
fragmentShader = null,
vertexShader = null;
var coordinateArray = [],
triangleVerticeColors = [],
verticesArray = [],
verticesIndexArray = [],
triangleTexCoords = [];
var vertexPositionAttribute = null,
trianglesVerticeBuffer = null,
vertexColorAttribute = null,
trianglesColorBuffer = null,
triangleVerticesIndexBuffer = null,
vertexTexCoordAttribute = null,
trianglesTexCoordBuffer = null;
var P = mat4.create(),
V = mat4.create(),
M = mat4.create(),
VM = mat4.create(),
PVM = mat4.create();
var uPVMMatrix;
var texture;
var textureImage = null;
function initWebGL() {
canvas = document.getElementById("my-canvas");
try {
gl = canvas.getContext("webgl") ||
canvas.getContext("experimental-webgl");
}catch(e){ }
if(gl) {
setupWebGL();
initShaders();
setupTexture();
setupBuffers();
//getMatrixUniforms();
//setMatrixUniforms();
//animationLoop();
drawScene();
}else{
alert( "Error: Your browser does not appear to" + "support WebGL.");
}
}
function animationLoop() {
var R = mat4.create();
var angle = 0;
var i =0;
var loop = function() {
angle = performance.now() / 1000 / 6 * 2 * Math.PI;
i++;
mat4.rotate(PVM, R, angle, [0, 1, 0]);
gl.uniformMatrix4fv(uPVMMatrix, false, PVM);
gl.clearColor(0.0, 0.0, 0.0, 0.0);
gl.clear(gl.DEPTH_BUFFER_BIT | gl.COLOR_BUFFER_BIT);
drawScene();
requestAnimationFrame(loop);
};
requestAnimationFrame(loop);
}
function setupWebGL() {
gl.enable(gl.DEPTH_TEST);
gl.enable(gl.CULL_FACE);
gl.frontFace(gl.CW);
gl.cullFace(gl.BACK);
gl.clearColor(0.0, 0.0, 0.0, 0.0);
gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
console.log(P);
console.log(V);
console.log(M);
// mat4.identity(M);
mat4.lookAt(V, [5, 0, -5], [0, 0, 0], [0, 1, 0]);
mat4.perspective(P, glMatrix.toRadian(45), canvas.width / canvas.height, 0.1, 1000.0);
mat4.multiply(VM,V,M);
mat4.multiply(PVM,P,VM);
}
function initShaders() {
var fs_source = document.getElementById('shader-fs').innerHTML,
vs_source = document.getElementById('shader-vs').innerHTML;
vertexShader = makeShader(vs_source, gl.VERTEX_SHADER);
fragmentShader = makeShader(fs_source, gl.FRAGMENT_SHADER);
glProgram = gl.createProgram();
gl.attachShader(glProgram, vertexShader);
gl.attachShader(glProgram, fragmentShader);
gl.linkProgram(glProgram);
if (!gl.getProgramParameter(glProgram, gl.LINK_STATUS)) {
alert("Unable to initialize the shader program.");
}
gl.useProgram(glProgram);
uPVMMatrix = gl.getUniformLocation(glProgram, "uPVMatrix");
gl.uniformMatrix4fv(uPVMMatrix, false, PVM);
}
function loadTexture() {
textureImage = $("#troll").get(0);
setupTexture();
}
function setupTexture() {
texture = gl.createTexture();
gl.bindTexture(gl.TEXTURE_2D, texture);
gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, true);
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNISGNED_BYTE, textureImage);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST);
glProgram.sampleUniform = gl.getUniformLocation(glProgram, "uSampler");
gl.uniform1i(glProgram.sampleUniform, 0);
if(!gl.isTexture(texture)) {
console.log("Error : Texture is invalid");
}
}
function makeShader(src, type) {
var shader = gl.createShader(type);
gl.shaderSource(shader, src);
gl.compileShader(shader);
if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) {
alert("Error compiling shader: " + gl.getShaderInfoLog(shader));
}
return shader;
}
function setupBuffers() {
// n-sides polygon
var n = 6;
var radius = 1;
var angle = (Math.PI * 2) / n;
var xCoordinate = 0;
var yCoordinate = 0;
for(var i = 0 ; i < n ; i++) {
var a = angle * i;
var xNewCoordinate = xCoordinate + radius * Math.cos(a);
var yNewCoordinate = yCoordinate + radius * Math.sin(a);
var zNewCoordinate = 0;
coordinateArray.push(xNewCoordinate);
coordinateArray.push(yNewCoordinate);
coordinateArray.push(zNewCoordinate);
}
verticesArray = [
//Bottom Face
0.0, 0.0, 0.0,
0.0, 0.0, -1.0,
1.0, 0.0, -1.0,
0.0, 0.0, 0.0,
1.0, 0.0, -1.0,
1.0, 0.0, 0.0,
//Front Face
0.0, 0.0, 0.0,
1.0, 0.0, 0.0,
0.5, 1.0, -0.5,
//Right Face
1.0, 0.0, 0.0,
1.0, 0.0, -1.0,
0.5, 1.0, -0.5,
//Back Face
1.0, 0.0, -1.0,
0.0, 0.0, -1.0,
0.5, 1.0, -0.5,
//Left Face
0.0, 0.0, -1.0,
0.0, 0.0, 0.0,
0.5, 1.0, -0.5,
];
trianglesVerticeBuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, trianglesVerticeBuffer);
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(verticesArray), gl.STATIC_DRAW);
verticesIndexArray = [
0, 1, 2,
3, 4, 5,
6, 7, 8,
9, 10, 11,
12, 13, 14,
15, 16, 17,
];
triangleVerticesIndexBuffer = gl.createBuffer();
triangleVerticesIndexBuffer.number_vertext_points = verticesIndexArray.length;
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, triangleVerticesIndexBuffer);
gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, new Uint16Array(verticesIndexArray), gl.STATIC_DRAW);
triangleTexCoords = [
0.5000, 0.1910,
0.1910, 0.5000,
0.5000, 0.8090,
0.5000, 0.1910,
0.5000, 0.8090,
0.8090, 0.5000,
0.5000, 0.1910,
0.8090, 0.5000,
1.0000, 0.0000,
0.8090, 0.5000,
0.5000, 0.8090,
1.0000, 1.0000,
0.5000, 0.8090,
0.1910, 0.5000,
0.0000, 1.0000,
0.1910, 0.5000,
0.5000, 0.1910,
0.0000, 0.0000,
];
trianglesTexCoordBuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, trianglesTexCoordBuffer);
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(triangleTexCoords), gl.STATIC_DRAW);
triangleVerticeColors = [
// Bottom quad
0.470, 0.796, 0.886,
0.470, 0.796, 0.886,
0.470, 0.796, 0.886,
0.470, 0.796, 0.886,
0.470, 0.796, 0.886,
0.470, 0.796, 0.886,
// Back triangle
0.772, 0.470, 0.886,
0.772, 0.470, 0.886,
0.772, 0.470, 0.886,
// Left triangle
0.886, 0.552, 0.470,
0.886, 0.552, 0.470,
0.886, 0.552, 0.470,
// Front triangle
0.886, 0.882, 0.470,
0.886, 0.882, 0.470,
0.886, 0.882, 0.470,
// Right triangle
0.470, 0.886, 0.505,
0.470, 0.886, 0.505,
0.470, 0.886, 0.505,
];
trianglesColorBuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, trianglesColorBuffer);
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(triangleVerticeColors), gl.STATIC_DRAW);
}
// GPU
function getMatrixUniforms() {
glProgram.mvMatrixUniform = gl.getUniformLocation(glProgram, "uMVMatrix");
glProgram.pMatrixUniform = gl.getUniformLocation(glProgram, "uPMatrix");
glProgram.vMatrixUniform = gl.getUniformLocation(glProgram, "uVMatrix");
}
// GPU
function setMatrixUniforms() {
gl.uniformMatrix4fv(glProgram.mvMatrixUniform, false, M);
gl.uniformMatrix4fv(glProgram.pMatrixUniform, false, P);
gl.uniformMatrix4fv(glProgram.vMatrixUniform, false, V);
}
function drawScene() {
vertexPositionAttribute = gl.getAttribLocation(glProgram, "aVertexPosition");
gl.enableVertexAttribArray(vertexPositionAttribute);
gl.bindBuffer(gl.ARRAY_BUFFER, trianglesVerticeBuffer);
gl.vertexAttribPointer(vertexPositionAttribute, 3, gl.FLOAT, false, 0, 0);
vertexTexCoordAttribute = gl.getAttribLocation(glProgram, "aVertexTexCoord");
gl.enableVertexAttribArray(vertexTexCoordAttribute);
gl.bindBuffer(gl.ARRAY_BUFFER, trianglesTexCoordBuffer);
gl.vertexAttribPointer(vertexTexCoordAttribute, 2, gl.FLOAT, false, 0, 0);
/*vertexColorAttribute = gl.getAttribLocation(glProgram, "aVertexColor");
gl.enableVertexAttribArray(vertexColorAttribute);
gl.bindBuffer(gl.ARRAY_BUFFER, trianglesColorBuffer);
gl.vertexAttribPointer(vertexColorAttribute, 3, gl.FLOAT, false, 0, 0);
*/
gl.drawElements(gl.TRIANGLE_STRIP, triangleVerticesIndexBuffer.number_vertext_points, gl.UNSIGNED_SHORT, 0);
}
</script>
</head>
<body onload="initWebGL()">
<canvas id="my-canvas" width="800" height="600">
Your browser does not support the HTML5 canvas element.
</canvas>
<img src="./trollface.png" id="troll" />
</body>
</html>
The texture coordinates I used looks like this:
Here is the texture:
The warnings are about out of range arrays and about the image not loading.
You have multiple errors in your code.
Your function loadTexture() is never called and so the texture is actually never loaded.
Replace setupTexture() with loadTexture() inside initWebGL().
You are also seem to be using jQuery to retrieve the image from DOM, but you didn't load the library.
Replace $("#troll").get(0) with document.getElementById("troll") inside loadTexture().
The dimensions of the texture needs to be a power of 2 (128x32, 256x256, 512x1024, ...)
You should resize your image to 256x256.
You made a typo with one of the parameters for texImage2D().
Replace gl.UNISGNED_BYTE with gl.UNSIGNED_BYTE inside your gl.texImage2D() call.
Your names for he texture coordinates attribute don't match between your gl.getAttribLocation() call and your vertex shader code.
Replace aVertexTexCoord with aVertexTextureCoord inside your gl.getAttribLocation() call.
If you are rendering individual triangles replace gl.TRIANGLE_STRIP with gl.TRIANGLES inside your gl.drawElements() call.
If you fix all those mistakes your code will run without errors.
I have been rewriting this code a few times now, but the same problem with the lighting appears... I am comparing this code with the code I wrote a few months ago that does the same thing (lighting a cube) and it doesn't seem like I am missing anything.
Front and back side of the cube are okay, but the left and right sides are acting strangely, and also the top and the bottom... looks like there is a problem with the normals, but they are okay... checked them and rewrote them a few times just to be sure.
Example: http://gamedevelopment.t15.org/WebGL/WebGL%20Examples/Example%207%20-%20Ambient%20And%20Directional%20Light/
Shaders:
<script id="vShader" type="x-shader/x-vertex">
attribute vec3 aVertexPosition;
attribute vec2 aTextureCoord;
attribute vec3 aVertexNormal;
uniform mat4 uPMatrix;
uniform mat4 uMVMatrix;
uniform mat3 uNMatrix;
uniform vec3 uAmbientLightColor;
uniform vec3 uDirectionalLightColor;
uniform vec3 uLightDirection;
uniform bool uUseLighting;
varying vec2 vTextureCoord;
varying vec3 vLightWeighting;
void main(void){
gl_Position = uPMatrix * uMVMatrix * vec4(aVertexPosition, 1.0);
vTextureCoord = aTextureCoord;
if(!uUseLighting){
vLightWeighting = vec3(1.0, 1.0, 1.0);
}else{
vec3 transformedNormal = aVertexNormal * uNMatrix;
float directionalLightWeighting = max(dot(uLightDirection, transformedNormal), 0.0);
vLightWeighting = uAmbientLightColor + uDirectionalLightColor * directionalLightWeighting;
}
}
</script>
<script id="fShader" type="x-shader/x-fragment">
precision mediump float;
varying vec2 vTextureCoord;
varying vec3 vLightWeighting;
uniform sampler2D uSampler;
void main(void){
vec4 textureColor = texture2D(uSampler, vec2(vTextureCoord.s, vTextureCoord.t));
gl_FragColor = vec4(textureColor.rgb * vLightWeighting, textureColor.a);
}
</script>
Code:
var gl;
function initGL(canvas){
try{
gl = canvas.getContext("webgl");
gl.viewportWidth = canvas.width;
gl.viewportHeight = canvas.height;
}catch(e){
console.log("WebGL context was not initialized.");
return null;
}
}
function getShader(id, gl){
var shaderScript = document.getElementById(id);
if(!shaderScript){
console.log(id + " - invalid shader id.");
}
var shaderString = "";
var shaderChild = shaderScript.firstChild;
while(shaderChild){
if(shaderChild.nodeType == "3")
shaderString += shaderChild.textContent;
shaderChild = shaderChild.nextSibling;
}
var shader;
if(shaderScript.type == "x-shader/x-vertex")
shader = gl.createShader(gl.VERTEX_SHADER);
else if(shaderScript.type == "x-shader/x-fragment")
shader = gl.createShader(gl.FRAGMENT_SHADER);
else{
console.log(id + " - invalid shader id.");
return null;
}
gl.shaderSource(shader, shaderString);
gl.compileShader(shader);
if(!gl.getShaderParameter(shader, gl.COMPILE_STATUS)){
console.log(id + " error: " + gl.getShaderInfoLog(shader));
return null;
}
return shader;
}
var shaderProgram;
function initShaders(){
var vShader = getShader("vShader", gl);
var fShader = getShader("fShader", gl);
shaderProgram = gl.createProgram();
gl.attachShader(shaderProgram, vShader);
gl.attachShader(shaderProgram, fShader);
gl.linkProgram(shaderProgram);
if(!gl.getProgramParameter(shaderProgram, gl.LINK_STATUS)){
console.log("Shader program was not linked.");
return null;
}
gl.useProgram(shaderProgram);
shaderProgram.vertexPositionAttribute = gl.getAttribLocation(shaderProgram, "aVertexPosition");
gl.enableVertexAttribArray(shaderProgram.vertexPositionAttribute);
shaderProgram.textureCoordAttribute = gl.getAttribLocation(shaderProgram, "aTextureCoord");
gl.enableVertexAttribArray(shaderProgram.vertexColorAttribute);
shaderProgram.vertexNormalAttribute = gl.getAttribLocation(shaderProgram, "aVertexNormal");
gl.enableVertexAttribArray(shaderProgram.vertexNormalAttribute);
shaderProgram.pMatrixUniform = gl.getUniformLocation(shaderProgram, "uPMatrix");
shaderProgram.mvMatrixUniform = gl.getUniformLocation(shaderProgram, "uMVMatrix");
shaderProgram.nMatrixUniform = gl.getUniformLocation(shaderProgram, "uNMatrix");
shaderProgram.samplerUniform = gl.getUniformLocation(shaderProgram, "uSampler");
shaderProgram.lightDirectionUniform = gl.getUniformLocation(shaderProgram, "uLightDirection");
shaderProgram.directionalLightColorUniform = gl.getUniformLocation(shaderProgram, "uDirectionalLightColor");
shaderProgram.ambientLightColorUniform = gl.getUniformLocation(shaderProgram, "uAmbientLightColor");
shaderProgram.useLightingUniform = gl.getUniformLocation(shaderProgram, "uUseLighting");
}
var cubeVertexPositionBuffer, cubeVertexIndexBuffer, cubeVertexTextureCoordBuffer, cubeVertexNormalBuffer;
function initBuffers(){
// Cube.
cubeVertexPositionBuffer = gl.createBuffer();
vertices = [
// Front face.
-1.0, 1.0, 1.0,
1.0, 1.0, 1.0,
1.0, -1.0, 1.0,
-1.0, -1.0, 1.0,
// Back face.
-1.0, 1.0, -1.0,
1.0, 1.0, -1.0,
1.0, -1.0, -1.0,
-1.0, -1.0, -1.0,
// Left face.
-1.0, 1.0, -1.0,
-1.0, 1.0, 1.0,
-1.0, -1.0, 1.0,
-1.0, -1.0, -1.0,
// Right face.
1.0, 1.0, -1.0,
1.0, 1.0, 1.0,
1.0, -1.0, 1.0,
1.0, -1.0, -1.0,
// Top face.
1.0, 1.0, -1.0,
-1.0, 1.0, -1.0,
-1.0, 1.0, 1.0,
1.0, 1.0, 1.0,
// Bottom face.
1.0, -1.0, -1.0,
-1.0, -1.0, -1.0,
-1.0, -1.0, 1.0,
1.0, -1.0, 1.0
]
gl.bindBuffer(gl.ARRAY_BUFFER, cubeVertexPositionBuffer);
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(vertices), gl.STATIC_DRAW);
cubeVertexPositionBuffer.itemSize = 3;
cubeVertexPositionBuffer.rotAngle = 0;
cubeVertexIndexBuffer = gl.createBuffer();
var indices = [
0, 1, 2, 0, 2, 3,
4, 5, 6, 4, 6, 7,
8, 9, 10, 8, 10, 11,
12, 13, 14, 12, 14, 15,
16, 17, 18, 16, 18, 19,
20, 21, 22, 20, 22, 23
];
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, cubeVertexIndexBuffer);
gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, new Uint16Array(indices), gl.STATIC_DRAW);
cubeVertexIndexBuffer.numItems = 36;
cubeVertexTextureCoordBuffer = gl.createBuffer();
textureCoords = [
0.0, 0.0,
1.0, 0.0,
1.0, 1.0,
0.0, 1.0,
0.0, 0.0,
1.0, 0.0,
1.0, 1.0,
0.0, 1.0,
0.0, 0.0,
1.0, 0.0,
1.0, 1.0,
0.0, 1.0,
0.0, 0.0,
1.0, 0.0,
1.0, 1.0,
0.0, 1.0,
0.0, 0.0,
1.0, 0.0,
1.0, 1.0,
0.0, 1.0,
0.0, 0.0,
1.0, 0.0,
1.0, 1.0,
0.0, 1.0
];
gl.bindBuffer(gl.ARRAY_BUFFER, cubeVertexTextureCoordBuffer);
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(textureCoords), gl.STATIC_DRAW);
cubeVertexTextureCoordBuffer.itemSize = 2;
cubeVertexNormalBuffer = gl.createBuffer();
var normals = [
0.0, 0.0, 1.0,
0.0, 0.0, 1.0,
0.0, 0.0, 1.0,
0.0, 0.0, 1.0,
0.0, 0.0, -1.0,
0.0, 0.0, -1.0,
0.0, 0.0, -1.0,
0.0, 0.0, -1.0,
-1.0, 0.0, 0.0,
-1.0, 0.0, 0.0,
-1.0, 0.0, 0.0,
-1.0, 0.0, 0.0,
1.0, 0.0, 0.0,
1.0, 0.0, 0.0,
1.0, 0.0, 0.0,
1.0, 0.0, 0.0,
0.0, 1.0, 0.0,
0.0, 1.0, 0.0,
0.0, 1.0, 0.0,
0.0, 1.0, 0.0,
0.0, -1.0, 0.0,
0.0, -1.0, 0.0,
0.0, -1.0, 0.0,
0.0, -1.0, 0.0,
]
gl.bindBuffer(gl.ARRAY_BUFFER, cubeVertexNormalBuffer);
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(normals), gl.STATIC_DRAW);
cubeVertexNormalBuffer.itemSize = 3;
}
var pMatrix = mat4.create();
var mvMatrixStack = [];
var mvMatrix = mat4.create();
var nMatrix = mat3.create();
function setMatrixUniforms(){
gl.uniformMatrix4fv(shaderProgram.pMatrixUniform, false, pMatrix);
gl.uniformMatrix4fv(shaderProgram.mvMatrixUniform, false, mvMatrix);
mat3.normalFromMat4(nMatrix, mvMatrix);
gl.uniformMatrix3fv(shaderProgram.nMatrixUniform, false, nMatrix);
}
function mvPushMatrix(){
var copy = mat4.create();
mat4.copy(copy, mvMatrix);
mvMatrixStack.push(copy);
}
function mvPopMatrix(){
mvMatrix = mvMatrixStack.pop();
}
function drawScene(){
gl.viewport(0, 0, gl.viewportWidth, gl.viewportHeight);
gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
mat4.perspective(pMatrix, 45, gl.viewportWidth / gl.viewportHeight, 0.1, 100.0);
mat4.identity(mvMatrix);
// Draw cube.
mvPushMatrix();
mat4.translate(mvMatrix, mvMatrix, [0.0, 0.0, -7.0]);
mat4.rotate(mvMatrix, mvMatrix, degToRad(cubeVertexPositionBuffer.rotAngle), [0.0, 1.0, 0.0]);
gl.bindBuffer(gl.ARRAY_BUFFER, cubeVertexPositionBuffer);
gl.vertexAttribPointer(shaderProgram.vertexPositionAttribute, cubeVertexPositionBuffer.itemSize, gl.FLOAT, false, 0, 0);
gl.bindBuffer(gl.ARRAY_BUFFER, cubeVertexTextureCoordBuffer);
gl.vertexAttribPointer(shaderProgram.textureCoordAttribute, cubeVertexTextureCoordBuffer.itemSize, gl.FLOAT, false, 0, 0);
gl.bindBuffer(gl.ARRAY_BUFFER, cubeVertexNormalBuffer);
gl.vertexAttribPointer(shaderProgram.vertexNormalAttribute, cubeVertexNormalBuffer.itemSize, gl.FLOAT, false, 0, 0);
gl.activeTexture(gl.TEXTURE0);
gl.bindTexture(gl.TEXTURE_2D, cubeTexture);
gl.uniform1i(shaderProgram.samplerUniform, 0);
// Lighting.
gl.uniform1i(shaderProgram.useLightingUniform, true);
gl.uniform3f(shaderProgram.ambientLightColorUniform, 0.2, 0.2, 0.2);
gl.uniform3f(shaderProgram.directionalLightColorUniform, 0.8, 0.8, 0.8);
var lightingDirection = [0.0, 0.0, -1.0];
var adjustedLD = vec3.create();
vec3.normalize(adjustedLD, lightingDirection);
vec3.scale(adjustedLD, adjustedLD, -1);
gl.uniform3fv(shaderProgram.lightDirectionUniform, adjustedLD);
setMatrixUniforms();
gl.drawElements(gl.TRIANGLES, cubeVertexIndexBuffer.numItems, gl.UNSIGNED_SHORT, 0);
mvPopMatrix();
}
var lastTime = 0;
function animate(){
var timeNow = new Date().getTime();
if(lastTime != 0){
var elapsed = timeNow - lastTime;
if(cubeVertexPositionBuffer.rotAngle > 360) cubeVertexPositionBuffer.rotAngle = 0;
cubeVertexPositionBuffer.rotAngle += 45 * elapsed / 1000;
}
lastTime = timeNow;
}
function tick(){
animate();
drawScene();
requestAnimFrame(tick);
}
function degToRad(degrees){
return degrees * Math.PI / 180;
}
var cubeTexture;
function initTextures(){
cubeTexture = gl.createTexture();
cubeTexture.image = new Image();
cubeTexture.image.onload = function(){
handleLoadedTexture(cubeTexture);
}
cubeTexture.image.src = "textures/cube.png";
}
function handleLoadedTexture(texture){
gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, true);
gl.bindTexture(gl.TEXTURE_2D, texture);
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, texture.image);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);
gl.bindTexture(gl.TEXTURE_2D, null);
}
function webGLStart(){
initGL(document.getElementById("glCanvas"));
initShaders();
initBuffers();
initTextures();
gl.clearColor(0.0, 0.0, 0.0, 1.0);
gl.enable(gl.DEPTH_TEST);
tick();
}
Lighting code is: references in initShaders(), normal and vertex buffers in initBuffers() and setting uniform variables in drawScene(), also creating normalMatrix in setMatrixUniforms().
I would be thankful if someone took their time to take a look at it and tell me what is wrong.
Okay.. got angry and rewrote code once again, then little by little replaced code with some code from a working example... guys tell me! The problem is in the vertex shader and please tell me the difference between
vec3 transformedNormal = uNMatrix * aVertexNormal;
and
vec3 transformedNormal = aVertexNormal * uNMatrix;
... the second one creates the bug, but it makes no sense... what does it matter the order in which i multiply.