Related
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 -->
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 have to solve a task where is required to apply an orthogonal projection by using sliders or buttons to control the near and far plane of projection.
The object is a rotating cube. I've already defined many transformation functions , like rotation, scaling. It works, but when I tried to apply the model view matrix and the projection matrix it stops to work and the html does not show the cube.
The console said to me that there is a normalization problem of a NaN vector.
Here is my code :
HTML
<!DOCTYPE html>
<html>
<button id = "ButtonX">Rotate X</button>
<button id = "ButtonY">Rotate Y</button>
<button id = "ButtonZ">Rotate Z</button>
<button id = "ButtonT">Toggle Rotation</button>
<button id="Direction">Change Direction</button>
<div>Traslation on X -1 <input id="slideX" type="range"
min="-1" max="1" step="0.1" value="0" />
1 </div>
<div>Traslation on Y -1 <input id="slideY" type="range"
min="-1" max="1" step="0.1" value="0" />
1 </div>
<div>Traslation on Z -1 <input id="slideZ" type="range"
min="-1" max="1" step="0.1" value="0" />
1 </div>
<div>Scaling on X -1 <input id="ScalingX" type="range"
min="0" max="1" step="0.1" value="0" />
1 </div>
<div>Scaling on Y -1 <input id="ScalingY" type="range"
min="0" max="1" step="0.1" value="0" />
1 </div>
<div>Scaling on Z -1 <input id="ScalingZ" type="range"
min="0" max="1" step="0.1" value="0" />
1 </div>
<button id="Button1">Increase Z</button>
<button id="Button2">Decrease Z</button>
<script id="vertex-shader" type="x-shader/x-vertex">
attribute vec4 vPosition;
attribute vec4 vColor;
varying vec4 fColor;
//uniform vec3 theta;
// Point 2 -> Move the matrices
// Per spostare le matrici le abbiamo dovuto dichiarare nel file GLSL come uniform
// le matrici rx ry e rz sono rispettivamente le matrici di rotazione sugli assi
uniform mat4 rx;
uniform mat4 ry;
uniform mat4 rz;
// Points 3 -> Traslation Matrix
uniform mat4 traslation;
// Points 3 -> Scaling Matrix
uniform mat4 scaling;
//Point 4 -> MV and P matrices
uniform mat4 modelView;
uniform mat4 projection;
void main()
{
// Compute the sines and cosines of theta for each of
// the three axes in one computation.
//vec3 angles = radians( theta );
//vec3 c = cos( angles );
//vec3 s = sin( angles );
// Remember: the matrices are column-major
/*
mat4 rx = mat4( 1.0, 0.0, 0.0, 0.0,
0.0, c.x, s.x, 0.0,
0.0, -s.x, c.x, 0.0,
0.0, 0.0, 0.0, 1.0 );
mat4 ry = mat4( c.y, 0.0, -s.y, 0.0,
0.0, 1.0, 0.0, 0.0,
s.y, 0.0, c.y, 0.0,
0.0, 0.0, 0.0, 1.0 );
mat4 rz = mat4( c.z, s.z, 0.0, 0.0,
-s.z, c.z, 0.0, 0.0,
0.0, 0.0, 1.0, 0.0,
0.0, 0.0, 0.0, 1.0 );
*/
fColor = vColor;
gl_Position = scaling *rz * ry * rx * traslation * vPosition ; // ORDINE : scaling -> rotazione -> traslation
//gl_Position = scaling *rz * ry * rx * traslation *projection*modelView*vPosition ;
gl_Position.z = -gl_Position.z;
}
</script>
<script id="fragment-shader" type="x-shader/x-fragment">
precision mediump float;
varying vec4 fColor;
void
main()
{
gl_FragColor = fColor;
}
</script>
<script type="text/javascript" src="../Common/webgl-utils.js"></script>
<script type="text/javascript" src="../Common/initShaders.js"></script>
<script type="text/javascript" src="../Common/MV.js"></script>
<script type="text/javascript" src="Homework1.js"></script>
<body>
<canvas id="gl-canvas" width="1024" height="1024">
Oops ... your browser doesn't support the HTML5 canvas element
</canvas>
</body>
</html>
JS
"use strict";
var canvas;
var gl;
var numVertices = 36;
var numChecks = 8;
var program;
var c;
var flag = true;
var direction = true;
var rx;
var ry;
var rz;
var traslation_loc;
var tx = 0 ;
var ty = 0;
var tz = 0;
var scaling_loc;
var sx = 1.0;
var sy = 1.0;
var sz = 1.0;
var pointsArray = [];
var colorsArray = [];
//Point 4
var near = -1;
var far = 1;
var radius = 1.0;
var theta = 0.0;
var phi = 0.0;
var dr = 5.0 * Math.PI/180.0;
var left = -1.0;
var right = 1.0;
var ytop = 1.0;
var bottom = -1.0;
var mvMatrix, pMatrix;
var modelView, projection;
var eye;
const at = vec3(0.0, 0.0, 0.0);
const up = vec3(0.0, 1.0, 0.0);
//
var vertices = [
vec4( -0.5, -0.5, 0.5, 1.0 ),
vec4( -0.5, 0.5, 0.5, 1.0 ),
vec4( 0.5, 0.5, 0.5, 1.0 ),
vec4( 0.5, -0.5, 0.5, 1.0 ),
vec4( -0.5, -0.5, -0.5, 1.0 ),
vec4( -0.5, 0.5, -0.5, 1.0 ),
vec4( 0.5, 0.5, -0.5, 1.0 ),
vec4( 0.5, -0.5, -0.5, 1.0 )
];
var vertexColors = [
vec4( 0.0, 0.0, 0.0, 1.0 ), // black
vec4( 1.0, 0.0, 0.0, 1.0 ), // red
vec4( 1.0, 1.0, 0.0, 1.0 ), // yellow
vec4( 0.0, 1.0, 0.0, 1.0 ), // green
vec4( 0.0, 0.0, 1.0, 1.0 ), // blue
vec4( 1.0, 0.0, 1.0, 1.0 ), // magenta
vec4( 0.0, 1.0, 1.0, 1.0 ), // white
vec4( 0.0, 1.0, 1.0, 1.0 ) // cyan
];
var xAxis = 0;
var yAxis = 1;
var zAxis = 2;
var axis = xAxis;
var theta = [45.0, 45.0, 45.0];
//var thetaLoc;
function quad(a, b, c, d) {
pointsArray.push(vertices[a]);
colorsArray.push(vertexColors[a]);
pointsArray.push(vertices[b]);
colorsArray.push(vertexColors[a]);
pointsArray.push(vertices[c]);
colorsArray.push(vertexColors[a]);
pointsArray.push(vertices[a]);
colorsArray.push(vertexColors[a]);
pointsArray.push(vertices[c]);
colorsArray.push(vertexColors[a]);
pointsArray.push(vertices[d]);
colorsArray.push(vertexColors[a]);
}
function colorCube()
{
quad( 1, 0, 3, 2 );
quad( 2, 3, 7, 6 );
quad( 3, 0, 4, 7 );
quad( 6, 5, 1, 2 );
quad( 4, 5, 6, 7 );
quad( 5, 4, 0, 1 );
}
window.onload = function init() {
canvas = document.getElementById( "gl-canvas" );
gl = WebGLUtils.setupWebGL( canvas );
if ( !gl ) { alert( "WebGL isn't available" ); }
gl.viewport( 0, 0, canvas.width, canvas.height );
gl.clearColor( 1.0, 1.0, 1.0, 1.0 );
gl.enable(gl.DEPTH_TEST);
//
// Load shaders and initialize attribute buffers
//
program = initShaders( gl, "vertex-shader", "fragment-shader" );
gl.useProgram( program );
colorCube();
var cBuffer = gl.createBuffer();
gl.bindBuffer( gl.ARRAY_BUFFER, cBuffer );
gl.bufferData( gl.ARRAY_BUFFER, flatten(colorsArray), gl.STATIC_DRAW );
var vColor = gl.getAttribLocation( program, "vColor" );
gl.vertexAttribPointer( vColor, 4, gl.FLOAT, false, 0, 0 );
gl.enableVertexAttribArray( vColor );
var vBuffer = gl.createBuffer();
gl.bindBuffer( gl.ARRAY_BUFFER, vBuffer);
gl.bufferData( gl.ARRAY_BUFFER, flatten(pointsArray), gl.STATIC_DRAW );
var vPosition = gl.getAttribLocation( program, "vPosition" );
gl.vertexAttribPointer( vPosition, 4, gl.FLOAT, false, 0, 0 );
gl.enableVertexAttribArray( vPosition );
// Possiamo commentare quello che riguarda il theta per il punto 2
//thetaLoc = gl.getUniformLocation(program, "theta");
// Point 2 - Rotation
//X AXIS
rx = gl.getUniformLocation(program, "rx");
//Y AXIS
ry = gl.getUniformLocation(program, "ry");
//Z AXIS
rz = gl.getUniformLocation(program, "rz");
// Traslation Matrix
traslation_loc = gl.getUniformLocation(program , "traslation");
// Scaling Matrix
scaling_loc = gl.getUniformLocation(program , "scaling");
// Projection and Model matrix
modelView = gl.getUniformLocation( program, "modelView" );
projection = gl.getUniformLocation( program, "projection" );
//**************
document.getElementById("ButtonX").onclick = function(){axis = xAxis;};
document.getElementById("ButtonY").onclick = function(){axis = yAxis;};
document.getElementById("ButtonZ").onclick = function(){axis = zAxis;};
document.getElementById("ButtonT").onclick = function(){flag = !flag;};
document.getElementById("Direction").onclick = function() { direction = !direction;};
document.getElementById( "slideX" ).oninput = function(){ tx = parseFloat(event.target.value,10); };
document.getElementById( "slideY" ).oninput = function(){ ty = parseFloat(event.target.value,10); };
document.getElementById( "slideZ" ).oninput = function(){ tz = parseFloat(event.target.value,10); };
document.getElementById( "ScalingX" ).oninput = function(){ sx = parseFloat(event.target.value,10); };
document.getElementById( "ScalingY" ).oninput = function(){ sy = parseFloat(event.target.value,10); };
document.getElementById( "ScalingZ" ).oninput = function(){ sz = parseFloat(event.target.value,10); };
// Point 4
document.getElementById("Button1").onclick = function(){near *= 1.1; far *= 1.1;};
document.getElementById("Button2").onclick = function(){near *= 0.9; far *= 0.9;};
render();
}
var render = function() {
gl.clear( gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
// Point 4
//*************************************
eye = vec3(radius*Math.sin(phi), radius*Math.sin(theta),
radius*Math.cos(phi));
mvMatrix = lookAt(eye, at , up);
pMatrix = ortho(left, right, bottom, ytop, near, far);
gl.uniformMatrix4fv( modelView, false, flatten(mvMatrix) );
gl.uniformMatrix4fv( projection, false, flatten(pMatrix) );
//*************************************
// Point 3 -> Scaling
var scaling = [sx , 0.0 , 0.0 , 0.0,
0.0 , sy, 0.0 , 0.0,
0.0 , 0.0 , sz , 0.0,
0.0 , 0.0 , 0.0 , 1];
gl.uniformMatrix4fv(scaling_loc,false,scaling);
// ****************************************
//X AXIS - Point 2
var theta_x_degree = theta[0];
var theta_x_radians = theta_x_degree * Math.PI / 180;
var s_x = Math.sin(theta_x_radians);
var c_x = Math.cos(theta_x_radians);
var rx_loc = [ 1.0, 0.0, 0.0, 0.0,
0.0, c_x, s_x, 0.0,
0.0, -s_x, c_x, 0.0,
0.0, 0.0, 0.0, 1.0 ];
gl.uniformMatrix4fv(rx, false, rx_loc);
//Y AXIS - Point 2
var theta_y_degree = theta[1];
var theta_y_radians = theta_y_degree * Math.PI / 180;
var s_y = Math.sin(theta_y_radians);
var c_y = Math.cos(theta_y_radians);
var ry_loc = [ c_y, 0.0, -s_y, 0.0,
0.0, 1.0, 0.0, 0.0,
s_y, 0.0, c_y, 0.0,
0.0, 0.0, 0.0, 1.0 ];
gl.uniformMatrix4fv(ry, false, ry_loc);
//Z AXIS - Point 2
var theta_z_degree = theta[2];
var theta_z_radians = theta_z_degree * Math.PI / 180;
var s_z = Math.sin(theta_z_radians);
var c_z = Math.cos(theta_z_radians);
var rz_loc = [ c_z, s_z, 0.0, 0.0,
-s_z, c_z, 0.0, 0.0,
0.0, 0.0, 1.0, 0.0,
0.0, 0.0, 0.0, 1.0 ];
gl.uniformMatrix4fv(rz, false, rz_loc);
// ****************************************
// Point 3 -> Traslation
var traslation = [1.0 , 0.0 , 0.0 , 0.0,
0.0 , 1.0 , 0.0 , 0.0,
0.0 , 0.0 , 1.0 , 0.0,
tx , ty , tz , 1.0];
gl.uniformMatrix4fv(traslation_loc,false,traslation);
// ****************************************
// ****************************************
// Point 1 --> Change and Toggle Rotation
if((direction)&&(!flag)) theta[axis] += -2.0;
if((!direction)&&(!flag)) theta[axis] += +2.0;
if(!direction) {theta[axis] += -2.0; }
if(direction) {theta[axis] += 2.0 ; }
// ****************************************
//gl.uniform3fv(thetaLoc, theta);
gl.drawArrays( gl.TRIANGLES, 0, numVertices );
requestAnimFrame(render);
}
I found the problem , it was the initialization of the var "eye" . There was a "0" that can not be computed.
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.