I am new to Webgl and have been following numerous guides to create a 3d cube that has interpolated colors, the colors are interpolated and the animation is working. However, I need the shape to be a 3D cube and instead it's a 3D pyramid and I for the life of me cannot figure out why.
This is my code
//Defing Vector shader
var VSHADER_SOURCE =
'precision mediump float;\n '+
'attribute vec3 vertPosition;\n '+
'attribute vec3 vertColor;\n '+
'uniform mat4 mWorld;\n '+
'uniform mat4 mView;\n '+
'uniform mat4 mProj;\n '+
'varying vec3 vColor;\n '+
'void main()\n '+
'{\n '+
' vColor = vertColor;\n '+
' gl_Position = mProj * mView * mWorld * vec4(vertPosition, 1.0);\n '+
'}'
//Defining Fragment Shader
var FSHADER_SOURCE =
'precision mediump float;\n '+
'varying vec3 vColor;\n '+
'void main()\n '+
'{\n '+
' gl_FragColor = vec4(vColor, 1.0);\n '+
'}';
//Defining vertices for rectangular cube
var boxVertices =
[ // X, Y, Z
// 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,
// 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,
// 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,
// 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,
];
//Defining indices for rectangular cube
var boxIndices =
[
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
];
//Defining colors for the color buffer
const colors = [
1.0,1.0,1.0, //white color
1.0,0.0,0.0, //red color
0.0,1.0,0.0, //green color
0.0,0.0,1.0, //blue color
0.0,0.0,0.0, // black color
1.0,1.0,1.0, //white color
1.0,0.0,0.0, //red color
0.0,1.0,0.0, //green color
0.0,0.0,1.0, //blue color
0.0,0.0,0.0, // black color
1.0,1.0,1.0, //white color
1.0,0.0,0.0, //red color
0.0,1.0,0.0, //green color
0.0,0.0,1.0, //blue color
0.0,0.0,0.0, // black color
1.0,1.0,1.0, //white color
1.0,0.0,0.0, //red color
0.0,1.0,0.0, //green color
0.0,0.0,1.0, //blue color
0.0,0.0,0.0, // black color
1.0,0.0,0.0, //red color
0.0,1.0,0.0, //green color
0.0,0.0,1.0, //blue color
0.0,0.0,0.0, // black color
];
var angle = 0;
function main() {
var canvas = document.getElementById('canvas');
var gl = canvas.getContext('webgl');
if (!gl) {
console.log('Your browser does not support WebGL');
}
// Initializing shaders
if (!initShaders(gl, VSHADER_SOURCE, FSHADER_SOURCE)) {
console.log('Failed to intialize shaders.');
return;
}
gl.clearColor(0.5, 0.5, 0.5, 0.9);
gl.clearDepth(1.0);
gl.enable(gl.DEPTH_TEST);
gl.depthFunc(gl.LEQUAL);
gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
var n = initBuffers(gl,boxVertices,boxIndices,colors);
var matWorldUniformLocation = gl.getUniformLocation(gl.program, 'mWorld');
checkLocations(matWorldUniformLocation);
var matViewUniformLocation = gl.getUniformLocation(gl.program, 'mView');
checkLocations(matViewUniformLocation);
var matProjUniformLocation = gl.getUniformLocation(gl.program, 'mProj');
checkLocations(matProjUniformLocation);
var worldMatrix = mat4.create();
var viewMatrix = mat4.create();
var projMatrix = mat4.create();
mat4.lookAt(viewMatrix, [3, 3, 7], //x,y,z
[0, 0, 0], [0, 1, 0]);
mat4.perspective(projMatrix, glMatrix.toRadian(20), 1/1, 1, 100);
gl.uniformMatrix4fv(matWorldUniformLocation, gl.FALSE, worldMatrix);
gl.uniformMatrix4fv(matViewUniformLocation, gl.FALSE, viewMatrix);
gl.uniformMatrix4fv(matProjUniformLocation, gl.FALSE, projMatrix);
var xRotationMatrix = mat4.create();
var yRotationMatrix = mat4.create();
var identityMatrix = mat4.create();
function loop() {
angle = performance.now() / 1000 / 6 * 2 * Math.PI;
mat4.rotate(yRotationMatrix, identityMatrix, angle, [0, 1, 0]);
mat4.rotate(xRotationMatrix, identityMatrix, angle / 4, [1, 0, 0]);
mat4.mul(worldMatrix, yRotationMatrix, xRotationMatrix);
gl.uniformMatrix4fv(matWorldUniformLocation, gl.FALSE, worldMatrix);
gl.clearColor(0.5, 0.5, 0.5, 0.8);
gl.clear(gl.DEPTH_BUFFER_BIT | gl.COLOR_BUFFER_BIT);
gl.drawElements(gl.TRIANGLES, 36, gl.UNSIGNED_SHORT, 0);
requestAnimationFrame(loop);
};
requestAnimationFrame(loop);
};
function checkLocations(location){
if(!location){
console.log("ERROR: cannot find storage location of " + location);
}
}
function initBuffers(gl,boxVertices,boxIndices,colors){
const boxVertexBufferObject = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, boxVertexBufferObject);
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(boxVertices), gl.STATIC_DRAW);
const boxIndexBufferObject = gl.createBuffer();
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, boxIndexBufferObject);
gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, new Uint16Array(boxIndices), gl.STATIC_DRAW);
const colorBuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, colorBuffer);
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(colors),gl.STATIC_DRAW);
var positionAttribLocation = gl.getAttribLocation(gl.program, 'vertPosition');
var colorAttribLocation = gl.getAttribLocation(gl.program, 'vertColor');
gl.vertexAttribPointer(positionAttribLocation, // Attribute location
3, // Number of elements per attribute
gl.FLOAT, // Type of elements
gl.FALSE,
0, // Size of an individual vertex
0 // Offset from the beginning of a single vertex to this attribute
);
gl.vertexAttribPointer(
colorAttribLocation, // Attribute location
3, // Number of elements per attribute
gl.FLOAT, // Type of elements
gl.FALSE,
0,
0
);
gl.enableVertexAttribArray(positionAttribLocation);
gl.enableVertexAttribArray(colorAttribLocation);
// Tell OpenGL state machine which program should be active.
gl.useProgram(gl.program);
var n = boxIndices.length;
return n;
}
When you call vertexAttribPointer
it reads data from the currently bound gl.ARRAY_BUFFER
.
In your code you should bind your boxVertexBufferObject
buffer and then call the corresponding gl.vertexAttribPointer(positionAttribLocation...
.
Then you should bind your colorBuffer
and then call the corresponding gl.vertexAttribPointer(colorAttribLocation...
.
The error in your code was that you bound the boxVertexBufferObject
buffer and then bound the colorBuffer
before ever calling gl.vertexAttribPointer
, effectively overwriting the data you needed to load. The result was that the colorBuffer
was loaded into both the vertPosition
and vertColor
attributes and that's why your faces were loading incorrectly. The faces were using the colorBuffer
values for vertices instead of the boxIndices
values.
<!DOCTYPE html>
<html>
<body>
<canvas id="canvas"></canvas>
</body>
<script type="module">
import { glMatrix, mat4 } from 'https://cdn.jsdelivr.net/npm/gl-matrix@3.4.3/+esm'
window.mat4 = mat4;
console.log(mat4)
//Defing Vector shader
var VSHADER_SOURCE =
'precision mediump float;\n ' +
'attribute vec3 vertPosition;\n ' +
'attribute vec3 vertColor;\n ' +
'uniform mat4 mWorld;\n ' +
'uniform mat4 mView;\n ' +
'uniform mat4 mProj;\n ' +
'varying vec3 vColor;\n ' +
'void main()\n ' +
'{\n ' +
' vColor = vertColor;\n ' +
' gl_Position = mProj * mView * mWorld * vec4(vertPosition, 1.0);\n ' +
'}'
//Defining Fragment Shader
var FSHADER_SOURCE =
'precision mediump float;\n ' +
'varying vec3 vColor;\n ' +
'void main()\n ' +
'{\n ' +
' gl_FragColor = vec4(vColor, 1.0);\n ' +
'}';
//Defining vertices for rectangular cube
var boxVertices =
[ // X, Y, Z
// 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,
// 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,
// 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,
// 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,
];
//Defining indices for rectangular cube
var boxIndices =
[
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
];
//Defining indices for rectangular cube
// var boxIndices =
// [
// 0,
// 1,
// 2,
// // 7,
// // 8,
// // 9,
// 4,
// 5,
// 6
// ];
//Defining colors for the color buffer
const colors = [
1.0, 1.0, 1.0, //white color
1.0, 0.0, 0.0, //red color
0.0, 1.0, 0.0, //green color
0.0, 0.0, 1.0, //blue color
0.0, 0.0, 0.0, // black color
1.0, 1.0, 1.0, //white color
1.0, 0.0, 0.0, //red color
0.0, 1.0, 0.0, //green color
0.0, 0.0, 1.0, //blue color
0.0, 0.0, 0.0, // black color
1.0, 1.0, 1.0, //white color
1.0, 0.0, 0.0, //red color
0.0, 1.0, 0.0, //green color
0.0, 0.0, 1.0, //blue color
0.0, 0.0, 0.0, // black color
1.0, 1.0, 1.0, //white color
1.0, 0.0, 0.0, //red color
0.0, 1.0, 0.0, //green color
0.0, 0.0, 1.0, //blue color
0.0, 0.0, 0.0, // black color
1.0, 0.0, 0.0, //red color
0.0, 1.0, 0.0, //green color
0.0, 0.0, 1.0, //blue color
0.0, 0.0, 0.0, // black color
];
var angle = 0;
function initShaders(gl, vertexSource, fragmentSource) {
const vertexShader = gl.createShader(gl.VERTEX_SHADER);
gl.shaderSource(vertexShader, vertexSource);
gl.compileShader(vertexShader);
const fragmentShader = gl.createShader(gl.FRAGMENT_SHADER);
gl.shaderSource(fragmentShader, fragmentSource);
gl.compileShader(fragmentShader);
const program = gl.createProgram();
gl.attachShader(program, vertexShader);
gl.attachShader(program, fragmentShader);
gl.linkProgram(program);
gl.validateProgram(program);
if (!gl.getProgramParameter(program, gl.LINK_STATUS)) {
console.error('Link failed: ' + gl.getProgramInfoLog(program));
console.error('vs info-log: ' + gl.getShaderInfoLog(vertexShader));
console.error('fs info-log: ' + gl.getShaderInfoLog(fragmentShader));
}
gl.useProgram(program);
gl.program = program;
return program;
}
function main() {
var canvas = document.getElementById('canvas');
var gl = canvas.getContext('webgl');
if (!gl) {
console.log('Your browser does not support WebGL');
}
// Initializing shaders
if (!initShaders(gl, VSHADER_SOURCE, FSHADER_SOURCE)) {
console.log('Failed to intialize shaders.');
return;
}
gl.clearColor(0.5, 0.5, 0.5, 0.9);
gl.clearDepth(1.0);
gl.enable(gl.DEPTH_TEST);
gl.depthFunc(gl.LEQUAL);
gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
var n = initBuffers(gl, boxVertices, boxIndices, colors);
var matWorldUniformLocation = gl.getUniformLocation(gl.program, 'mWorld');
checkLocations(matWorldUniformLocation);
var matViewUniformLocation = gl.getUniformLocation(gl.program, 'mView');
checkLocations(matViewUniformLocation);
var matProjUniformLocation = gl.getUniformLocation(gl.program, 'mProj');
checkLocations(matProjUniformLocation);
var worldMatrix = mat4.create();
var viewMatrix = mat4.create();
var projMatrix = mat4.create();
mat4.lookAt(viewMatrix, [3, 3, 7], //x,y,z
[0, 0, 0], [0, 1, 0]);
mat4.perspective(projMatrix, glMatrix.toRadian(20), 1 / 1, 1, 100);
gl.uniformMatrix4fv(matWorldUniformLocation, gl.FALSE, worldMatrix);
gl.uniformMatrix4fv(matViewUniformLocation, gl.FALSE, viewMatrix);
gl.uniformMatrix4fv(matProjUniformLocation, gl.FALSE, projMatrix);
var xRotationMatrix = mat4.create();
var yRotationMatrix = mat4.create();
var identityMatrix = mat4.create();
function loop() {
angle = performance.now() / 1000 / 6 * 2 * Math.PI;
mat4.rotate(yRotationMatrix, identityMatrix, angle, [0, 1, 0]);
mat4.rotate(xRotationMatrix, identityMatrix, angle / 4, [1, 0, 0]);
mat4.mul(worldMatrix, yRotationMatrix, xRotationMatrix);
gl.uniformMatrix4fv(matWorldUniformLocation, gl.FALSE, worldMatrix);
gl.clearColor(0.5, 0.5, 0.5, 0.8);
gl.clear(gl.DEPTH_BUFFER_BIT | gl.COLOR_BUFFER_BIT);
gl.drawElements(gl.TRIANGLES, boxIndices.length, gl.UNSIGNED_SHORT, 0);
requestAnimationFrame(loop);
};
requestAnimationFrame(loop);
};
function checkLocations(location) {
if (!location) {
console.log("ERROR: cannot find storage location of " + location);
}
}
function initBuffers(gl, boxVertices, boxIndices, colors) {
gl.useProgram(gl.program);
var positionAttribLocation = gl.getAttribLocation(gl.program, 'vertPosition');
var colorAttribLocation = gl.getAttribLocation(gl.program, 'vertColor');
gl.enableVertexAttribArray(positionAttribLocation);
gl.enableVertexAttribArray(colorAttribLocation);
const boxVertexBufferObject = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, boxVertexBufferObject);
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(boxVertices), gl.STATIC_DRAW);
gl.vertexAttribPointer(positionAttribLocation, // Attribute location
3, // Number of elements per attribute
gl.FLOAT, // Type of elements
false,
0, // Size of an individual vertex
0 // Offset from the beginning of a single vertex to this attribute
);
const colorBuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, colorBuffer);
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(colors), gl.STATIC_DRAW);
gl.vertexAttribPointer(
colorAttribLocation, // Attribute location
3, // Number of elements per attribute
gl.FLOAT, // Type of elements
false,
0,
0
);
const boxIndexBufferObject = gl.createBuffer();
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, boxIndexBufferObject);
gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, new Uint16Array(boxIndices), gl.STATIC_DRAW);
// Tell OpenGL state machine which program should be active.
var n = boxIndices.length;
return n;
}
main();
</script>
</html>