I'm currently working on a website about WebGL. My partner and I want to use a small but interesting WebGL background.
What we're trying to do is to get a box rotating on a canvas. Now, the principle of it is already working. We already have a rotating box, however many of the triangles are using the wrong vertices to form their triangle causing the picture below:
The rotating cube with a bad triangle setup.
So, we figured: Lets use indices to set it up properly. This however causes the box to just vanish from the canvas. It doesn't appear to be anywhere anymore.
Our question is: Could someone see why this is happening? We got the feeling we're very close but just this tiny detail is really causing some trouble for us.
The code we're using is provided below. Any other feedback is always welcome too.
Current Program
// ROTATING EXAMPLE
// The canvas that the GL environment will be using.
var canvas = null;
// The Graphics Library, aka: WebGL.
var openGL = null;
// The shader used by the graphics library.
var shaderProgram = null;
// Our matrices.
var modelViewMatrix;
var modelNormalMatrix;
var perspectiveMatrix;
var VertexBuffer;
var ColorBuffer;
var NormalBuffer;
var IndicesBuffer;
var vertexPositionAttribute;
var vertexColorAttribute;
var vertexNormalAttribute;
var squareRotation = 0.0;
var lastSquareUpdateTime = null;
// Encapsulation of the initialisation of WebGL.
function initWebGL(openGLCanvas) {
var gl = null;
// Attempt to build the context.
try {
gl = openGLCanvas.getContext("webgl", { premultipliedAlpha: false })
|| openGLCanvas.getContext("experimental-webgl", { premultipliedAlpha: false });
}
// Report back to use when some exception is thrown.
catch (exception) {
console.log("An error happened when trying to initialise the openGL environment. See the exception below for details.");
console.log(exception);
gl = null;
}
return gl;
}
function initialise(){
// Keep this variable local. We do not need it globally.
var openGLCanvas = document.getElementById("openGLCanvas");
openGL = initWebGL(openGLCanvas);
if(openGL)
{
// The color to use to clear the screen.
openGL.clearColor(0.9, 0.9, 0.9, 1.0);
openGL.clearDepth(1.0);
// Enables the depth testing.
openGL.enable(openGL.DEPTH_TEST);
// Closer things will overlap with things further away.
openGL.depthFunc(openGL.LEQUAL);
// Clear both the color as the depth buffer.
openGL.clear(openGL.COLOR_BUFFER_BIT |openGL.DEPTH_BUFFER_BIT);
openGL.viewport(0, 0, openGLCanvas.width, openGLCanvas.height);
InitialiseShaders();
InitialiseBuffers();
setInterval(drawScene, 15);
//drawScene();
}
}
function InitialiseShaders()
{
var fragmentShader = retrieveShader(openGL, "04fragment", "f", "shader");
var vertexShader = retrieveShader(openGL, "04vertex", "v", "shader");
//console.log(fragmentShader);
//console.log(vertexShader);
shaderProgram = openGL.createProgram();
openGL.attachShader(shaderProgram, vertexShader);
openGL.attachShader(shaderProgram, fragmentShader);
openGL.linkProgram(shaderProgram);
if(!openGL.getProgramParameter(shaderProgram, openGL.LINK_STATUS))
{
console.log("Something went wrong during the initialisation of the shader program. See below for extra information.");
console.log(openGL.getProgramParameter(shaderProgram, openGL.LINK_STATUS));
}
openGL.useProgram(shaderProgram);
vertexPositionAttribute = openGL.getAttribLocation(shaderProgram, "position");
openGL.enableVertexAttribArray(vertexPositionAttribute);
vertexColorAttribute = openGL.getAttribLocation(shaderProgram, "color");
openGL.enableVertexAttribArray(vertexColorAttribute);
vertexNormalAttribute = openGL.getAttribLocation(shaderProgram, "normal");
openGL.enableVertexAttribArray(vertexNormalAttribute);
}
function retrieveShader(openGL, filename, type, filetype)
{ // Ensure the file type is always given.
filetype = filetype || "txt";
// Read out the source file.
var source = "";
$.ajax({
url: "Shaders/" + filename + "." + filetype,
async: false,
success: function(data) { source = data; }
});
console.info("Found source for the following filename: " + filename + ", of type: " + type + ".");
console.info(source);
// Check what type the shader should be.
// If the type is unknown we can see the error report within the console.
var shader;
if(type == "f")
{ shader = openGL.createShader(openGL.FRAGMENT_SHADER);}
else if (type == "v")
{ shader = openGL.createShader(openGL.VERTEX_SHADER);}
else
{
console.log("Unknown shader type. See below for extra information.");
console.log(identification);
console.log(shaderScript.type);
return null;
}
// Attempt to compile the shader.
openGL.shaderSource(shader, source);
openGL.compileShader(shader);
// Check whether or not there are any compilation errors.
// If there are any, we'll be able to see the error report within the console.
if(!openGL.getShaderParameter(shader, openGL.COMPILE_STATUS))
{
console.log("An error occured during the compilation of the shader. See below for extra information.");
console.log(openGL.getShaderInfoLog(shader));
return null;
}
// All is green.
// Lets start this baby up.
return shader;
}
function InitialiseBuffers()
{
bufferPosition();
bufferColor();
bufferNormal();
bufferIndices();
}
function bufferIndices(){
var 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
];
IndicesBuffer = openGL.createBuffer();
openGL.bindBuffer(openGL.ELEMENT_ARRAY_BUFFER, IndicesBuffer);
openGL.bufferData(openGL.ELEMENT_ARRAY_BUFFER, new Uint16Array(indices), openGL.STATIC_DRAW);
}
function bufferPosition()
{
// Vertex data for a 3D box.
var 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,
// 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
];
// Create a buffer, prep it for filling and then fill it.
VertexBuffer = openGL.createBuffer();
openGL.bindBuffer(openGL.ARRAY_BUFFER, VertexBuffer);
openGL.bufferData(openGL.ARRAY_BUFFER, new Float32Array(vertices), openGL.STATIC_DRAW);
}
function bufferColor()
{
// Color data for a 3D Box.
var colorPerFace = [
[0.8, 0.9, 0.75, 1.0], // Front face
[0.8, 0.9, 0.75, 1.0], // Back face
[0.8, 0.9, 0.75, 1.0], // Top face
[0.8, 0.9, 0.75, 1.0], // Bottom face
[0.8, 0.9, 0.75, 1.0], // Right face
[0.8, 0.9, 0.75, 1.0], // Left face
];
var colors = [];
// For each face.
for (j=0; j<6; j++) {
var c = colorPerFace[j];
// generate a color for every vertex on that face.
for (var i=0; i<4; i++) {
colors = colors.concat(c);
}
}
// Create a buffer, prep it and then fill it.
ColorBuffer = openGL.createBuffer();
openGL.bindBuffer(openGL.ARRAY_BUFFER, ColorBuffer);
openGL.bufferData(openGL.ARRAY_BUFFER, new Float32Array(colors), openGL.STATIC_DRAW);
}
function bufferNormal()
{
var vertexNormals = [
// Front
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
0.0, 0.0, -1.0,
0.0, 0.0, -1.0,
0.0, 0.0, -1.0,
0.0, 0.0, -1.0,
// Top
0.0, 1.0, 0.0,
0.0, 1.0, 0.0,
0.0, 1.0, 0.0,
0.0, 1.0, 0.0,
// Bottom
0.0, -1.0, 0.0,
0.0, -1.0, 0.0,
0.0, -1.0, 0.0,
0.0, -1.0, 0.0,
// Right
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
-1.0, 0.0, 0.0,
-1.0, 0.0, 0.0,
-1.0, 0.0, 0.0,
-1.0, 0.0, 0.0
];
NormalBuffer = openGL.createBuffer();
openGL.bindBuffer(openGL.ARRAY_BUFFER, NormalBuffer);
openGL.bufferData(openGL.ARRAY_BUFFER, new Float32Array(vertexNormals), openGL.STATIC_DRAW);
}
function drawScene()
{
openGL.clear(openGL.GL_COLOR_BUFFER_BIT | openGL.DEPTH_BUFFER_BIT);
updateScene();
// Clear both the color as the depth buffer.
openGLBindings();
openGL.drawElements(openGL.TRIANGLE, 24, openGL.UNSIGNED_SHORT, 0);
openGL.drawArrays(openGL.TRIANGLE_STRIP, 0, 24);
console.log("Scene made!");
}
function updateScene()
{
var currentTime = Date.now();
if (lastSquareUpdateTime) {
var delta = currentTime - lastSquareUpdateTime;
squareRotation += (30 * delta) / 2000.0;
}
lastSquareUpdateTime = currentTime;
// Calculate the matrices
modelViewMatrix = Matrix.I(4);
var inRadians = squareRotation * Math.PI / 180;
modelViewMatrix = modelViewMatrix.x(Matrix.Translation($V([0.0, 0.0, -12.0])).ensure4x4());
modelViewMatrix = modelViewMatrix.x(Matrix.Rotation(inRadians, $V([1, 1, 0])).ensure4x4());
modelNormalMatrix = modelViewMatrix.inverse();
modelNormalMatrix = modelNormalMatrix.transpose();
perspectiveMatrix = makePerspective(45, 520 / 520, 0.1, 100.0);
// Send the matrices to the GPU.
var mvUniform = openGL.getUniformLocation(shaderProgram, "matrixModelView");
openGL.uniformMatrix4fv(mvUniform,
false,
new Float32Array(modelViewMatrix.flatten()));
var pUniform = openGL.getUniformLocation(shaderProgram, "matrixPerspective");
openGL.uniformMatrix4fv(pUniform,
false,
new Float32Array(perspectiveMatrix.flatten()));
var nUniform = openGL.getUniformLocation(shaderProgram, "matrixNormal");
openGL.uniformMatrix4fv(nUniform,
false,
new Float32Array(modelNormalMatrix.flatten()));
}
function openGLBindings()
{
// Send the data to the GPU.
openGL.bindBuffer(openGL.ARRAY_BUFFER, VertexBuffer);
openGL.vertexAttribPointer(vertexPositionAttribute, // Where to bind it to
3, // The number of data per set.
openGL.FLOAT, // The type of data
false, // Normalisation (or not).
0, // Stride
0); // Offset
openGL.bindBuffer(openGL.ARRAY_BUFFER, ColorBuffer);
openGL.vertexAttribPointer(vertexColorAttribute, // Where to bind it to
4, // The number of data per set.
openGL.FLOAT, // The type of data.
false, // Normalisation (or not).
0, // Stride
0); // Offset
openGL.bindBuffer(openGL.ARRAY_BUFFER, NormalBuffer);
openGL.vertexAttribPointer(vertexNormalAttribute, // Where to bind it to
3, // The number of data per set.
openGL.FLOAT, // The type of data.
false, // Normalisation (or not).
0, // Stride
0); // Offset
openGL.bindBuffer(openGL.ELEMENT_ARRAY_BUFFER, IndicesBuffer);
}
Fragment Shader
varying lowp vec4 fColor;
void main(void) {
gl_FragColor = fColor;
}
Vertex Shader
attribute vec3 position;
attribute vec3 normal;
attribute vec4 color;
uniform mat4 matrixModelView;
uniform mat4 matrixPerspective;
uniform mat4 matrixNormal;
varying lowp vec4 fColor;
void main(void) {
gl_Position = matrixPerspective * matrixModelView * vec4(position, 1.0);
highp vec3 directionalVector = vec3(1.0, 0.85, 0.6);
highp vec4 transformedNormal = matrixNormal * vec4(normal, 1.0);
highp float directional = max(dot(transformedNormal.xyz, directionalVector), 0.0);
fColor = color * directional;
}