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>
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 new to webgl and I've been trying to create two cubes that rotate around their own axis, but right now, they rotate only according to one axis. I would like to know what I am doing wrong, (I think it's because I need to create a new model matrix for the rotation but I am not sure how to do that). Thank you!
// Application info.
var app = app || {};
function initGL()
{
var gl = app.gl;
gl.enable(gl.DEPTH_TEST);
gl.depthFunc(gl.LEQUAL);
gl.viewport(0,0,app.can.width, app.can.height);
gl.clearColor(0.,0.,0., 1.0);
gl.clear(app.gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
var vs = createShaderFromElement(app.gl, "vs");
var fs = createShaderFromElement(app.gl, "fs");
app.progObject = buildProgram(app.gl, vs, fs);
gl.useProgram(app.progObject);
}
function initScene()
{
var gl = app.gl;
// Creer le buffer de geometrie (vertex)
//
var positions = [
// 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,
-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,
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,
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,
-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,
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,
-1.0, 1.0, -1.0,
-1.0, -1.0, -1.0
];
app.nPoints = positions.length / 3;
var colors = [
1,1,1, 1,1,1, 1,1,1, 1,1,1, 1,1,1, 1,1,1,
1,0,0, 1,0,0, 1,0,0, 1,0,0, 1,0,0, 1,0,0,
0,1,0, 0,1,0, 0,1,0, 0,1,0, 0,1,0, 0,1,0,
0,0,1, 0,0,1, 0,0,1, 0,0,1, 0,0,1, 0,0,1,
1,1,0, 1,1,0, 1,1,0, 1,1,0, 1,1,0, 1,1,0,
1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1
];
var loc, buffer;
// Create and copy position buffer.
loc = gl.getAttribLocation(app.progObject, "pos");
buffer = gl.createBuffer();
gl.enableVertexAttribArray(loc);
gl.bindBuffer(gl.ARRAY_BUFFER, buffer);
gl.vertexAttribPointer(loc, 3, gl.FLOAT, false /*no normalization*/, 0 /*stride*/, 0 /*offset*/);
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(positions), gl.STATIC_DRAW);
// Create and copy color buffer.
loc = gl.getAttribLocation(app.progObject, "color");
buffer = gl.createBuffer();
gl.enableVertexAttribArray(loc);
gl.bindBuffer(gl.ARRAY_BUFFER, buffer);
gl.vertexAttribPointer(loc, 3, gl.FLOAT, false /*no normalization*/, 0 /*stride*/, 0 /*offset*/);
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(colors), gl.STATIC_DRAW);
// Look for uniforms.
app.pmLocation = gl.getUniformLocation(app.progObject, "projMatrix");
app.mmLocation = gl.getUniformLocation(app.progObject, "modelMatrix");
app.vmLocation = gl.getUniformLocation(app.progObject, "viewMatrix");
var mat4 = glMatrix.mat4;
app.projMatrix = mat4.create();
app.modelMatrix = mat4.create();
app.viewMatrix = mat4.create();
mat4.perspective(app.projMatrix, Math.PI / 4.0 /*45 degrees*/, 1, 0.1, 100);
mat4.lookAt(app.viewMatrix, [0, 0, -10], [0, 0, 0], [0, 1, 0]);
}
function animate(time)
{
var gl = app.gl;
var mat4 = glMatrix.mat4;
// converts to seconds.
var seconds = time * 1E-3;
var dtime = time - app.oldTime;
var angle = dtime * 0.001;
mat4.rotateY(app.modelMatrix, app.modelMatrix, angle);
var mm1 = mat4.create();
mat4.translate(mm1, app.modelMatrix, [2, 0, 0]);
app.oldTime = time;
gl.clear(app.gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
gl.uniformMatrix4fv(app.pmLocation, false, app.projMatrix );
gl.uniformMatrix4fv(app.mmLocation, false, mm1 );
gl.uniformMatrix4fv(app.vmLocation, false, app.viewMatrix );
gl.drawArrays(gl.TRIANGLES, 0, app.nPoints);
// Pour dessiner autre cube, calculer autre model matrix
// et redessiner ... gl.drawArrays(gl.TRIANGLES, 0, app.nPoints);
mat4.rotateY( app.modelMatrix, app.modelMatrix, angle)
var mm2 = mat4.create();
mat4.translate(mm2, app.modelMatrix, [-2, 0, 0]);
gl.uniformMatrix4fv(app.pmLocation, false, app.projMatrix);
gl.uniformMatrix4fv(app.mmLocation, false, mm2);
gl.uniformMatrix4fv(app.vmLocation, false, app.viewMatrix);
gl.drawArrays(gl.TRIANGLES, 0, app.nPoints);
window.requestAnimationFrame(animate);
}
function fovChanged(id, value)
{
console.log("FOV Angle: ", value);
var label = document.getElementById( 'output-fov' );
label.innerHTML = value;
}
function farChanged(id, value)
{
console.log("Far Plane: ", value);
var label = document.getElementById( 'output-far' );
label.innerHTML = value;
}
function init()
{
[app.can, app.gl] = getContextGL('can');
if (app.can == null || app.gl == null)
{
alert("Can't init canvas or context");
return;
}
app.can.width = app.can.height * (app.can.clientWidth / app.can.clientHeight);
var rect = app.can.getBoundingClientRect();
app.scaleX = app.can.width / rect.width;
app.scaleY = app.can.height / rect.height;
initGL();
initScene();
app.oldTime = 0;
animate(0);
}
div
{
}
#main-div
{
display:inline-block;
}
#viewport, #manager
{
float: left;
margin: auto;
}
.color
{
width:100px;
height:50px;
}
.blue{
background:#0f0;
}
#viewport
{
width: 600px;
height:700px;
}
#can
{
width: 600px;
height: 500px;
border:1px solid orange;
}
#manager
{
width: 200px;
height:300px;
padding: 0 0 0 5px;
}
#obj-list
{
width: 200px;
}
<!DOCTYPE html>
<html>
<head>
<title>Cube Transform</title>
<link rel="stylesheet" type="text/css" href="style.css">
<script src="transforms.js"></script>
<script src="utils.js"></script>
<script src="gl-matrix-min.js"></script>
<script id="vs" type="x-shader/x-vertex">
precision mediump float;
uniform mat4 projMatrix;
uniform mat4 viewMatrix;
uniform mat4 modelMatrix;
attribute vec3 pos;
attribute vec3 color;
varying vec3 fColor;
void main()
{
fColor = color;
vec4 pt = vec4(pos, 1.0);
gl_Position = projMatrix * viewMatrix * modelMatrix * pt;
}
</script>
<script id="fs" type="x-shader/x-fragment">
precision mediump float;
varying vec3 fColor;
void main()
{
gl_FragColor = vec4(fColor,1);
}
</script>
</head>
<body onload="init();">
<div id="main-div">
<div id="viewport">
<canvas id="can" >Your browser doesn't seem to support canvas!</canvas>
<div class="slider">
<span class="slider-label">FOV cam</span>
<input id="fov" class="slider-input" type="range" min="20" max="90" step="1" value="0" onClick="fovChanged(this.id, this.value)" />
<span id="output-fov" class="slider-value">20</span>
</div>
<div class="slider">
<span class="slider-label">Far Plane</span>
<input id="far" class="slider-input" type="range" min="5" max="30" step="0.1" value="0" onClick="farChanged(this.id, this.value)" />
<span id="output-far" class="slider-value">5</span>
</div>
</div>
</div>
</body>
</html>
I'm not exactly sure what your code is trying to do.
In any case, in general to rotate in place to translate then rotate
mat = idenity
mat = mat * translation
mat = mat * rotation
or depending on the library
mat = identity
translate(mat, ...)
rotate(mat, ...)
or
mat = translation(...);
rotate(mat, ...);
etc.
// Application info.
var app = app || {};
function initGL() {
var gl = app.gl;
gl.enable(gl.DEPTH_TEST);
gl.depthFunc(gl.LEQUAL);
gl.viewport(0, 0, app.can.width, app.can.height);
gl.clearColor(0., 0., 0., 1.0);
gl.clear(app.gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
var vs = createShaderFromElement(app.gl, "vs");
var fs = createShaderFromElement(app.gl, "fs");
app.progObject = buildProgram(app.gl, vs, fs);
gl.useProgram(app.progObject);
}
function initScene() {
var gl = app.gl;
// Creer le buffer de geometrie (vertex)
//
var positions = [
// 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, -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,
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,
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, -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,
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, -1.0, 1.0, -1.0, -1.0, -1.0, -1.0
];
app.nPoints = positions.length / 3;
var colors = [
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0,
0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0,
0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1,
1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0,
1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1
];
var loc, buffer;
// Create and copy position buffer.
loc = gl.getAttribLocation(app.progObject, "pos");
buffer = gl.createBuffer();
gl.enableVertexAttribArray(loc);
gl.bindBuffer(gl.ARRAY_BUFFER, buffer);
gl.vertexAttribPointer(loc, 3, gl.FLOAT, false /*no normalization*/ , 0 /*stride*/ , 0 /*offset*/ );
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(positions), gl.STATIC_DRAW);
// Create and copy color buffer.
loc = gl.getAttribLocation(app.progObject, "color");
buffer = gl.createBuffer();
gl.enableVertexAttribArray(loc);
gl.bindBuffer(gl.ARRAY_BUFFER, buffer);
gl.vertexAttribPointer(loc, 3, gl.FLOAT, false /*no normalization*/ , 0 /*stride*/ , 0 /*offset*/ );
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(colors), gl.STATIC_DRAW);
// Look for uniforms.
app.pmLocation = gl.getUniformLocation(app.progObject, "projMatrix");
app.mmLocation = gl.getUniformLocation(app.progObject, "modelMatrix");
app.vmLocation = gl.getUniformLocation(app.progObject, "viewMatrix");
var mat4 = glMatrix.mat4;
app.projMatrix = mat4.create();
app.modelMatrix = mat4.create();
app.viewMatrix = mat4.create();
mat4.perspective(app.projMatrix, Math.PI / 4.0 /*45 degrees*/ , 1, 0.1, 100);
mat4.lookAt(app.viewMatrix, [0, 0, -10], [0, 0, 0], [0, 1, 0]);
}
function animate(time) {
var gl = app.gl;
var mat4 = glMatrix.mat4;
// converts to seconds.
var seconds = time * 1E-3;
var dtime = time - app.oldTime;
var mm1 = mat4.create();
mat4.translate(mm1, app.modelMatrix, [2, 0, 0]);
var angle = time * 0.001;
mat4.rotateY(mm1, mm1, angle);
app.oldTime = time;
gl.clear(app.gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
gl.uniformMatrix4fv(app.pmLocation, false, app.projMatrix);
gl.uniformMatrix4fv(app.mmLocation, false, mm1);
gl.uniformMatrix4fv(app.vmLocation, false, app.viewMatrix);
gl.drawArrays(gl.TRIANGLES, 0, app.nPoints);
// Pour dessiner autre cube, calculer autre model matrix
// et redessiner ... gl.drawArrays(gl.TRIANGLES, 0, app.nPoints);
var mm2 = mat4.create();
mat4.translate(mm2, app.modelMatrix, [-2, 0, 0]);
mat4.rotateY(mm2, mm2, angle)
gl.uniformMatrix4fv(app.pmLocation, false, app.projMatrix);
gl.uniformMatrix4fv(app.mmLocation, false, mm2);
gl.uniformMatrix4fv(app.vmLocation, false, app.viewMatrix);
gl.drawArrays(gl.TRIANGLES, 0, app.nPoints);
window.requestAnimationFrame(animate);
}
function fovChanged(id, value) {
console.log("FOV Angle: ", value);
var label = document.getElementById('output-fov');
label.innerHTML = value;
}
function farChanged(id, value) {
console.log("Far Plane: ", value);
var label = document.getElementById('output-far');
label.innerHTML = value;
}
function init() {
[app.can, app.gl] = getContextGL('can');
if (app.can == null || app.gl == null) {
alert("Can't init canvas or context");
return;
}
app.can.width = app.can.height * (app.can.clientWidth / app.can.clientHeight);
var rect = app.can.getBoundingClientRect();
app.scaleX = app.can.width / rect.width;
app.scaleY = app.can.height / rect.height;
initGL();
initScene();
app.oldTime = 0;
animate(0);
}
init();
// -----
function getContextGL(id) {
const can = document.getElementById(id);
const gl = can.getContext('webgl');
return [can, gl];
}
function createShaderFromElement(gl, id) {
const e = document.getElementById(id);
const s = gl.createShader(e.type.indexOf('vertex') >= 0 ? gl.VERTEX_SHADER : gl.FRAGMENT_SHADER);
gl.shaderSource(s, e.text);
gl.compileShader(s);
return s;
}
function buildProgram(gl, vs, fs) {
const p = gl.createProgram();
gl.attachShader(p, vs);
gl.attachShader(p, fs);
gl.linkProgram(p);
return p;
}
div {}
#main-div {
display: inline-block;
}
#viewport,
#manager {
float: left;
margin: auto;
}
.color {
width: 100px;
height: 50px;
}
.blue {
background: #0f0;
}
#viewport {
width: 600px;
height: 700px;
}
#can {
width: 600px;
height: 500px;
border: 1px solid orange;
}
#manager {
width: 200px;
height: 300px;
padding: 0 0 0 5px;
}
#obj-list {
width: 200px;
}
<script src="https://cdn.jsdelivr.net/npm/gl-matrix#3.3.0/gl-matrix-min.js"></script>
<script id="vs" type="x-shader/x-vertex">
precision mediump float;
uniform mat4 projMatrix;
uniform mat4 viewMatrix;
uniform mat4 modelMatrix;
attribute vec3 pos;
attribute vec3 color;
varying vec3 fColor;
void main()
{
fColor = color;
vec4 pt = vec4(pos, 1.0);
gl_Position = projMatrix * viewMatrix * modelMatrix * pt;
}
</script>
<script id="fs" type="x-shader/x-fragment">
precision mediump float;
varying vec3 fColor;
void main()
{
gl_FragColor = vec4(fColor,1);
}
</script>
</head>
<div id="main-div">
<div id="viewport">
<canvas id="can" >Your browser doesn't seem to support canvas!</canvas>
<div class="slider">
<span class="slider-label">FOV cam</span>
<input id="fov" class="slider-input" type="range" min="20" max="90" step="1" value="0" onClick="fovChanged(this.id, this.value)" />
<span id="output-fov" class="slider-value">20</span>
</div>
<div class="slider">
<span class="slider-label">Far Plane</span>
<input id="far" class="slider-input" type="range" min="5" max="30" step="0.1" value="0" onClick="farChanged(this.id, this.value)" />
<span id="output-far" class="slider-value">5</span>
</div>
</div>
</div>
I'd suggest these articles on matrices in WebGL
Also, please take a look at how to make your snippet runnable as a runnable snippet is far more useful.
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.
Hi I dont actually get the culling part here? Can someone explain why my object is not blending? I need to apply blending on the object together with lighting
Below is the code of my script
Vertex shader:
<script type="x-shader/x-vertex" id="vshader">
attribute vec3 aPosition;
attribute vec4 aColor;
uniform mat4 uModel;
uniform mat4 uView;
uniform mat4 uProjection;
varying vec4 vColor;
attribute vec3 aNormal;
uniform mat4 uNormal;
uniform vec3 uLightDiffuse;
uniform vec3 uLightDirection;
void main() {
gl_Position = uProjection * uView * uModel * vec4(aPosition,1.0);
float lambertCoefficient = max(dot(-normalize(uLightDirection),normalize(vec3(uNormal * vec4(aNormal,1.0)))),0.0);
vec3 diffuseColor = uLightDiffuse * aColor.rgb * lambertCoefficient;
vColor = vec4(diffuseColor,1.0);
}
</script>
fragment shader
<script type="x-shader/x-fragment" id="fshader">
precision mediump float;
varying vec4 vColor;
void main() {
gl_FragColor = vColor;
}
</script>
<script type="text/javascript" src="initUtils.js"></script>
<script type="text/javascript" src="gl-matrix-min.js"></script>
main code:
<script>
function main() {
var canvas = document.getElementById("c");
var gl = initializeWebGL(canvas);
var vertexShader = initializeShader(gl,"vshader");
var fragmentShader = initializeShader(gl, "fshader");
var program = initializeProgram(gl,vertexShader,fragmentShader);
gl.useProgram(program);
var cube_vertices = [ // Coordinates
1.0, 1.0, 1.0, -1.0, 1.0, 1.0, -1.0,-1.0, 1.0, 1.0,-1.0, 1.0, //front
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
1.0, 1.0, 1.0, 1.0, 1.0,-1.0, -1.0, 1.0,-1.0, -1.0, 1.0, 1.0, //up
-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
-1.0,-1.0,-1.0, 1.0,-1.0,-1.0, 1.0,-1.0, 1.0, -1.0,-1.0, 1.0, //down
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
];
//buffer creation
var cubeVerticesBuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, cubeVerticesBuffer);
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(cube_vertices), gl.STATIC_DRAW);
//attribute variable mapping to buffer
var aPosition = gl.getAttribLocation(program,"aPosition");
gl.vertexAttribPointer(aPosition,3,gl.FLOAT,false,0,0);
gl.enableVertexAttribArray(aPosition);
//unbind buffer to ARRAY_BUFFER POINTER
gl.bindBuffer(gl.ARRAY_BUFFER, null);
var cube_color = [ // Coordinates
1.0,0.0,0.0,0.5, 1.0,0.0,0.0,0.5, 1.0,0.0,0.0,0.5, 1.0,0.0,0.0,0.5, //front red
0.0,1.0,0.0,0.5, 0.0,1.0,0.0,0.5, 0.0,1.0,0.0,0.5, 0.0,1.0,0.0,0.5, //right green
0.0,0.0,1.0,0.5, 0.0,0.0,1.0,0.5, 0.0,0.0,1.0,0.5, 0.0,0.0,1.0,0.5, //up blue
1.0,1.0,0.0,0.5, 1.0,1.0,0.0,0.5, 1.0,1.0,0.0,0.5, 1.0,1.0,0.0,0.5, //left yellow
0.0,1.0,1.0,0.5, 0.0,1.0,1.0,0.5, 0.0,1.0,1.0,0.5, 0.0,1.0,1.0,0.5, //down cyan
1.0,0.0,1.0,0.5, 1.0,0.0,1.0,0.5, 1.0,0.0,1.0,0.5, 1.0,0.0,1.0,0.5 //back magenta
];
//buffer creation
var cubeColorBuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, cubeColorBuffer);
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(cube_color), gl.STATIC_DRAW);
//attribute variable mapping to buffer
var aColor = gl.getAttribLocation(program,"aColor");
gl.vertexAttribPointer(aColor,4,gl.FLOAT,false,0,0);
gl.enableVertexAttribArray(aColor);
//unbind buffer to ARRAY_BUFFER POINTER
gl.bindBuffer(gl.ARRAY_BUFFER, null);
var normals = [ // Normal of each vertex
0.0, 0.0, 1.0, 0.0, 0.0, 1.0, 0.0, 0.0, 1.0, 0.0, 0.0, 1.0, // front
1.0, 0.0, 0.0, 1.0, 0.0, 0.0, 1.0, 0.0, 0.0, 1.0, 0.0, 0.0, // right
0.0, 1.0, 0.0, 0.0, 1.0, 0.0, 0.0, 1.0, 0.0, 0.0, 1.0, 0.0, // up
-1.0, 0.0, 0.0, -1.0, 0.0, 0.0, -1.0, 0.0, 0.0, -1.0, 0.0, 0.0, // left
0.0,-1.0, 0.0, 0.0,-1.0, 0.0, 0.0,-1.0, 0.0, 0.0,-1.0, 0.0, // down
0.0, 0.0,-1.0, 0.0, 0.0,-1.0, 0.0, 0.0,-1.0, 0.0, 0.0,-1.0 // back
];
//buffer creation
var normalBuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, normalBuffer);
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(normals), gl.STATIC_DRAW);
//attribute variable mapping to buffer
var aNormal = gl.getAttribLocation(program,"aNormal");
gl.vertexAttribPointer(aNormal,3,gl.FLOAT,false,0,0);
gl.enableVertexAttribArray(aNormal);
//unbind buffer to ARRAY_BUFFER POINTER
gl.bindBuffer(gl.ARRAY_BUFFER, null);
// Indices of the vertices
var indices = [
0, 1, 2, 0, 2, 3, // front
4, 5, 6, 4, 6, 7, // right
8, 9,10, 8,10,11, // up
12,13,14, 12,14,15, // left
16,17,18, 16,18,19, // down
20,21,22, 20,22,23 // back
];
//buffer creation
var indexBuffer = gl.createBuffer();
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, indexBuffer);
gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, new Uint8Array(indices), gl.STATIC_DRAW);
//unbind buffer to gl.ELEMENT_ARRAY_BUFFER POINTER
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, null);
var modelMatrix = mat4.create();
var uModel = gl.getUniformLocation(program,"uModel");
gl.uniformMatrix4fv(uModel,false,modelMatrix);
//add normal matrix
var normalMatrix = mat4.create();
var uNormal = gl.getUniformLocation(program,"uNormal");
mat4.invert(normalMatrix,modelMatrix);
mat4.transpose(normalMatrix,normalMatrix);
gl.uniformMatrix4fv(uNormal,false,normalMatrix);
var viewMatrix = mat4.create();
var uView = gl.getUniformLocation(program,"uView");
mat4.lookAt(viewMatrix,[3,3,7],[0,0,0],[0,1,0]);
gl.uniformMatrix4fv(uView,false,viewMatrix);
var projectionMatrix = mat4.create();
var uProjection = gl.getUniformLocation(program,"uProjection");
mat4.perspective(projectionMatrix,glMatrix.toRadian(30),canvas.width/canvas.height,1,100);
gl.uniformMatrix4fv(uProjection,false,projectionMatrix);
var uLightDiffuse = gl.getUniformLocation(program,"uLightDiffuse");
gl.uniform3f(uLightDiffuse,1.0,1.0,1.0);
var uLightDirection= gl.getUniformLocation(program,"uLightDirection");
gl.uniform3f(uLightDirection,-1.0,-2.5,-5.0);
//draw scene
gl.clearColor(0, 0, 0, 1);
gl.enable(gl.DEPTH_TEST);
gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
gl.disable(gl.DEPTH_TEST);
gl.enable(gl.BLEND);
gl.blendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA);
gl.enable(gl.CULL_FACE);
gl.cullFace(gl.FRONT);
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, indexBuffer);
gl.drawElements(gl.TRIANGLES, indices.length, gl.UNSIGNED_BYTE, 0);
gl.enable(gl.CULL_FACE);
gl.cullFace(gl.BACK);
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, indexBuffer);
gl.drawElements(gl.TRIANGLES, indices.length, gl.UNSIGNED_BYTE, 0);
}
The below shader statement indicates that you are writing pixels with Alpha=1.0, and it is opaque.
vColor = vec4(diffuseColor,1.0);
To make it transparent, you would need to make it a non-1.0 value, maybe calculated or passed in from texture itself ?
Culling has no relation to blending. It indicates the capability of GL HW to discard certain primitives depending on whether they are front-facing or back-facing.