WebGL - Cube Rotation
Hello there, future WebGL wizards! Today, we're going to embark on an exciting journey into the world of 3D graphics. By the end of this tutorial, you'll be able to create a rotating cube using WebGL. Isn't that cool? Let's dive in!
Understanding the Basics
Before we start spinning cubes like a DJ, let's get our heads around some fundamental concepts.
What is WebGL?
WebGL (Web Graphics Library) is a JavaScript API that allows us to render 3D graphics in a web browser. It's like giving your browser a pair of 3D glasses!
Why a Cube?
You might wonder, "Why are we starting with a cube?" Well, my dear students, a cube is like the "Hello World" of 3D graphics. It's simple enough to understand but complex enough to teach us important concepts. Plus, who doesn't love a good cube?
Setting Up Our WebGL Environment
The HTML Canvas
First things first, we need a stage for our cube to perform on. In WebGL, this stage is called a canvas. Let's set it up:
<canvas id="glcanvas" width="640" height="480">
Your browser doesn't support HTML5 canvas
</canvas>
This creates a 640x480 pixel canvas. If you can't see it, don't worry - it's like an invisible dance floor right now.
Initializing WebGL
Now, let's get WebGL ready to party:
var canvas = document.getElementById('glcanvas');
var gl = canvas.getContext('webgl');
if (!gl) {
console.log('WebGL not supported, falling back on experimental-webgl');
gl = canvas.getContext('experimental-webgl');
}
if (!gl) {
alert('Your browser does not support WebGL');
}
This code gets our WebGL context. If your browser doesn't support WebGL, it's like trying to play a DVD on a VHS player - it just won't work!
Creating Our 3D Cube
Defining Vertices
A cube has 8 corners (vertices). We need to tell WebGL where these corners are:
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
];
Each set of three numbers represents a corner of our cube in 3D space. It's like giving WebGL a map of our cube!
Creating the Buffer
Now we need to send this data to the GPU:
var vertexBuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, vertexBuffer);
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(vertices), gl.STATIC_DRAW);
This is like packing our cube into a suitcase (buffer) and sending it to the GPU.
Shaders - The Magicians of WebGL
Vertex Shader
The vertex shader positions our vertices:
var vertexShaderSource = `
attribute vec3 aVertexPosition;
uniform mat4 uModelViewMatrix;
uniform mat4 uProjectionMatrix;
void main(void) {
gl_Position = uProjectionMatrix * uModelViewMatrix * vec4(aVertexPosition, 1.0);
}
`;
This shader takes each vertex and applies our transformation matrices. It's like a magician moving the corners of our cube around!
Fragment Shader
The fragment shader colors our cube:
var fragmentShaderSource = `
void main(void) {
gl_FragColor = vec4(1.0, 1.0, 1.0, 1.0);
}
`;
This simple shader just colors everything white. It's like painting our cube!
Compiling and Linking Shaders
Now we need to compile our shaders and link them into a program:
function getShader(gl, source, type) {
var 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));
return null;
}
return shader;
}
var vertexShader = getShader(gl, vertexShaderSource, gl.VERTEX_SHADER);
var fragmentShader = getShader(gl, fragmentShaderSource, gl.FRAGMENT_SHADER);
var 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));
}
gl.useProgram(shaderProgram);
This is like teaching our magicians (shaders) their tricks and then putting them on stage!
Making Our Cube Rotate
Setting Up the Rotation
To make our cube rotate, we need to update its rotation angle over time:
var cubeRotation = 0.0;
function render(now) {
now *= 0.001; // convert to seconds
const deltaTime = now - then;
then = now;
cubeRotation += deltaTime;
drawScene();
requestAnimationFrame(render);
}
This function gets called repeatedly, updating our cube's rotation each time.
Drawing the Scene
Now let's put it all together and draw our rotating cube:
function drawScene() {
gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
const projectionMatrix = mat4.create();
mat4.perspective(projectionMatrix, 45 * Math.PI / 180, gl.canvas.clientWidth / gl.canvas.clientHeight, 0.1, 100.0);
const modelViewMatrix = mat4.create();
mat4.translate(modelViewMatrix, modelViewMatrix, [-0.0, 0.0, -6.0]);
mat4.rotate(modelViewMatrix, modelViewMatrix, cubeRotation, [0, 1, 1]);
gl.uniformMatrix4fv(gl.getUniformLocation(shaderProgram, 'uProjectionMatrix'), false, projectionMatrix);
gl.uniformMatrix4fv(gl.getUniformLocation(shaderProgram, 'uModelViewMatrix'), false, modelViewMatrix);
gl.drawArrays(gl.TRIANGLE_STRIP, 0, 36);
}
This function clears the canvas, sets up our perspective, applies our rotation, and finally draws our cube.
Conclusion
And there you have it, folks! We've created a rotating 3D cube using WebGL. Remember, mastering WebGL is like learning to juggle - it takes practice, but once you get it, you can do amazing things!
Here's a table summarizing the main methods we used:
Method | Description |
---|---|
gl.createBuffer() |
Creates a new buffer object |
gl.bindBuffer() |
Binds a buffer object to a target |
gl.bufferData() |
Creates and initializes a buffer object's data store |
gl.createShader() |
Creates a shader object |
gl.shaderSource() |
Sets the source code of a shader object |
gl.compileShader() |
Compiles a shader object |
gl.createProgram() |
Creates a program object |
gl.attachShader() |
Attaches a shader object to a program object |
gl.linkProgram() |
Links a program object |
gl.useProgram() |
Sets the specified program as part of the current rendering state |
gl.clear() |
Clears buffers to preset values |
gl.uniformMatrix4fv() |
Specifies the value of a uniform variable for the current program object |
gl.drawArrays() |
Renders primitives from array data |
Keep practicing, keep coding, and soon you'll be creating amazing 3D graphics in your browser. Happy coding!
Credits: Image by storyset