This is a series of blog posts related to WebGL. New post will be available every day
Join mailing list to get new posts right to your inbox
Built with
Hey π
Welcome to WebGL month.
Yesterday we rendered a single minecraft dirt cube, let's render a terrain today!
We'll need to store each block position in separate transform matrix
π src/3d-textured.js
gl.viewport(0, 0, canvas.width, canvas.height); + const matrices = []; + function frame() { mat4.rotateY(cube.modelMatrix, cube.modelMatrix, Math.PI / 180);
Now let's create 10k blocks iteration over x and z axis from -50 to 50
π src/3d-textured.js
const matrices = []; + for (let i = -50; i < 50; i++) { + for (let j = -50; j < 50; j++) { + const matrix = mat4.create(); + } + } + function frame() { mat4.rotateY(cube.modelMatrix, cube.modelMatrix, Math.PI / 180);
Each block is a size of 2 (vertex coordinates are in [-1..1] range) so positions should be divisible by two
π src/3d-textured.js
for (let i = -50; i < 50; i++) { for (let j = -50; j < 50; j++) { const matrix = mat4.create(); + + const position = [i * 2, (Math.floor(Math.random() * 2) - 1) * 2, j * 2]; } }
Now we need to create a transform matrix. Let's use ma4.fromTranslation
π src/3d-textured.js
const matrix = mat4.create(); const position = [i * 2, (Math.floor(Math.random() * 2) - 1) * 2, j * 2]; + mat4.fromTranslation(matrix, position); } }
Let's also rotate each block around Y axis to make terrain look more random
π src/3d-textured.js
gl.viewport(0, 0, canvas.width, canvas.height); const matrices = []; + const rotationMatrix = mat4.create(); for (let i = -50; i < 50; i++) { for (let j = -50; j < 50; j++) { const position = [i * 2, (Math.floor(Math.random() * 2) - 1) * 2, j * 2]; mat4.fromTranslation(matrix, position); + + mat4.fromRotation(rotationMatrix, Math.PI * Math.round(Math.random() * 4), [0, 1, 0]); + mat4.multiply(matrix, matrix, rotationMatrix); } }
and finally push matrix of each block to matrices collection
π src/3d-textured.js
mat4.fromRotation(rotationMatrix, Math.PI * Math.round(Math.random() * 4), [0, 1, 0]); mat4.multiply(matrix, matrix, rotationMatrix); + + matrices.push(matrix); } }
Since our blocks are static, we don't need a rotation transform in each frame
π src/3d-textured.js
} function frame() { - mat4.rotateY(cube.modelMatrix, cube.modelMatrix, Math.PI / 180); - gl.uniformMatrix4fv(programInfo.uniformLocations.modelMatrix, false, cube.modelMatrix); gl.uniformMatrix4fv(programInfo.uniformLocations.normalMatrix, false, cube.normalMatrix);
Now we'll need to iterate over matrices collection and issue a draw call for each cube with its transform matrix passed to uniform
π src/3d-textured.js
} function frame() { - gl.uniformMatrix4fv(programInfo.uniformLocations.modelMatrix, false, cube.modelMatrix); - gl.uniformMatrix4fv(programInfo.uniformLocations.normalMatrix, false, cube.normalMatrix); + matrices.forEach((matrix) => { + gl.uniformMatrix4fv(programInfo.uniformLocations.modelMatrix, false, matrix); + gl.uniformMatrix4fv(programInfo.uniformLocations.normalMatrix, false, cube.normalMatrix); - gl.drawArrays(gl.TRIANGLES, 0, vertexBuffer.data.length / 3); + gl.drawArrays(gl.TRIANGLES, 0, vertexBuffer.data.length / 3); + }); requestAnimationFrame(frame); }
Now let's create an animation of rotating camera. Camera has a position and a point where it is pointed. So to implement this, we need to rotate focus point around camera position. Let's first get rid of static view matrix
π src/3d-textured.js
const viewMatrix = mat4.create(); const projectionMatrix = mat4.create(); - mat4.lookAt(viewMatrix, [0, 4, -7], [0, 0, 0], [0, 1, 0]); - mat4.perspective(projectionMatrix, (Math.PI / 360) * 90, canvas.width / canvas.height, 0.01, 100); gl.uniformMatrix4fv(programInfo.uniformLocations.viewMatrix, false, viewMatrix);
Define camera position, camera focus point vector and focus point transform matrix
π src/3d-textured.js
- import { mat4 } from 'gl-matrix'; + import { mat4, vec3 } from 'gl-matrix'; import vShaderSource from './shaders/3d-textured.v.glsl'; import fShaderSource from './shaders/3d-textured.f.glsl'; } } + const cameraPosition = [0, 10, 0]; + const cameraFocusPoint = vec3.fromValues(30, 0, 0); + const cameraFocusPointMatrix = mat4.create(); + + mat4.fromTranslation(cameraFocusPointMatrix, cameraFocusPoint); + function frame() { matrices.forEach((matrix) => { gl.uniformMatrix4fv(programInfo.uniformLocations.modelMatrix, false, matrix);
Our camera is located in 0.0.0, so we need to translate camera focus point to 0.0.0, rotate it, and translate back to original position
π src/3d-textured.js
mat4.fromTranslation(cameraFocusPointMatrix, cameraFocusPoint); function frame() { + mat4.translate(cameraFocusPointMatrix, cameraFocusPointMatrix, [-30, 0, 0]); + mat4.rotateY(cameraFocusPointMatrix, cameraFocusPointMatrix, Math.PI / 360); + mat4.translate(cameraFocusPointMatrix, cameraFocusPointMatrix, [30, 0, 0]); + matrices.forEach((matrix) => { gl.uniformMatrix4fv(programInfo.uniformLocations.modelMatrix, false, matrix); gl.uniformMatrix4fv(programInfo.uniformLocations.normalMatrix, false, cube.normalMatrix);
Final step β update view matrix uniform
π src/3d-textured.js
mat4.rotateY(cameraFocusPointMatrix, cameraFocusPointMatrix, Math.PI / 360); mat4.translate(cameraFocusPointMatrix, cameraFocusPointMatrix, [30, 0, 0]); + mat4.getTranslation(cameraFocusPoint, cameraFocusPointMatrix); + + mat4.lookAt(viewMatrix, cameraPosition, cameraFocusPoint, [0, 1, 0]); + gl.uniformMatrix4fv(programInfo.uniformLocations.viewMatrix, false, viewMatrix); + matrices.forEach((matrix) => { gl.uniformMatrix4fv(programInfo.uniformLocations.modelMatrix, false, matrix); - gl.uniformMatrix4fv(programInfo.uniformLocations.normalMatrix, false, cube.normalMatrix); gl.drawArrays(gl.TRIANGLES, 0, vertexBuffer.data.length / 3); });
That's it!
This approach is not very performant though, as we're issuing 2 gl calls for each object, so it is a 20k of gl calls each frame. GL calls are expensive, so we'll need to reduce this number. We'll learn a great technique tomorrow!
Join mailing list to get new posts right to your inbox
Built with
Top comments (0)