I can generate a cube in WebGL using a simple vertices array. I want to generate a larger cube comprised of many of the smaller cubes. What is wrong with my programmatic approach to this and/or with the peripheral WebGL syntax which I am not familiar with?
let canvas = document.getElementById(name);
let gl = canvas.getContext("webgl");
canvas.width = 300;
canvas.height = 300;
// grid spacing
const N = 3;
const h = 1.5/N;
// vertices used to create box
const box = [
// Front
h, h, h,
h, -h, h,
-h, h, h,
-h, h, h,
h, -h, h,
-h, -h, h,
// Left
-h, h, h,
-h, -h, h,
-h, h, -h,
-h, h, -h,
-h, -h, h,
-h, -h, -h,
// Back
-h, h, -h,
-h, -h, -h,
h, h, -h,
h, h, -h,
-h, -h, -h,
h, -h, -h,
// Right
h, h, -h,
h, -h, -h,
h, h, h,
h, h, h,
h, -h, h,
h, -h, -h,
// Top
h, h, h,
h, h, -h,
-h, h, h,
-h, h, h,
h, h, -h,
-h, h, -h,
// Bottom
h, -h, h,
h, -h, -h,
-h, -h, h,
-h, -h, h,
h, -h, -h,
-h, -h, -h,
];
const vertices = [];
for (let i = 0; i < 2; i++) {
let out = box;
vertices.push(...mat4.translate(out, box, [i*h, i*h, i*h]));
out = null;
}
// assigning color to each cube
let colorData = [];
// divide by N and then by faces
for (let cube = 0; cube < vertices.length/N; cube++) {
let faceColor = randomColor();
for (let face = 0; face < vertices.length/(N*6); face++) {
colorData.push(...faceColor);
}
}
// load buffers
const positionBuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer); // bind to current array buffer
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(vertices), gl.STATIC_DRAW); // load vertex data into buffer and choose draw mode
const colorBuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, colorBuffer); // bind to current array buffer
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(colorData), gl.STATIC_DRAW); // load vertex data into buffer and choose draw mode
// routine to output xyz coordinates from buffer into vertex shader
const vertexShader = gl.createShader(gl.VERTEX_SHADER);
gl.shaderSource(vertexShader, `
precision mediump float;
attribute vec3 position;
attribute vec3 color;
varying vec3 vColor;
uniform mat4 matrix;
void main() {
vColor = color;
gl_Position = matrix * vec4(position, 1);
}
`);
gl.compileShader(vertexShader)
//routine to assign color shader
const fragmentShader = gl.createShader(gl.FRAGMENT_SHADER);
gl.shaderSource(fragmentShader, `
precision mediump float;
varying vec3 vColor;
void main() {
gl_FragColor = vec4(vColor, 1);
}
`);
gl.compileShader(fragmentShader);
// "link" vertex and color shaders
const program = gl.createProgram();
gl.attachShader(program, vertexShader);
gl.attachShader(program, fragmentShader);
gl.linkProgram(program);
// assign position, color, and uniform locations
const positionLocation = gl.getAttribLocation(program, `position`); // attribute index
gl.enableVertexAttribArray(positionLocation);
gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer);
gl.vertexAttribPointer(positionLocation, 3, gl.FLOAT, false, 0, 0);
const colorLocation = gl.getAttribLocation(program, `color`); // attribute index
gl.enableVertexAttribArray(colorLocation);
gl.bindBuffer(gl.ARRAY_BUFFER, colorBuffer);
gl.vertexAttribPointer(colorLocation, 3, gl.FLOAT, false, 0, 0);
const uniformLocation = {
matrix : gl.getUniformLocation(program, `matrix`)
};
gl.useProgram(program);
gl.enable(gl.DEPTH_TEST);
let matrix = mat4.create();
mat4.translate(matrix, matrix, [0, 0, -3]);
mat4.rotateY(matrix, matrix, Math.PI/4);
let projectionMatrix = mat4.create();
mat4.perspective(projectionMatrix,
90 * Math.PI/180, // vertical fov
canvas.height/canvas.width, // aspect ratio
1e-4, // near cull distance
1e4 // far cull distance
);
let outMatrix = mat4.create();
// animate
animate();
function animate() {
requestAnimationFrame(animate);
mat4.multiply(outMatrix, projectionMatrix, matrix);
gl.uniformMatrix4fv(uniformLocation.matrix, false, outMatrix);
gl.drawArrays(gl.TRIANGLES, 0, vertices.length / 3); // triangle, first vertex, draw all three
// divide length of vertices array by 3 to get the number of vertices. vertices = coordinateComponents/componentsPerCoordinate(x,y,z)
}
I believe the issue is in the first 75-100 lines, from the initialization of the box to the end of the loop that appends translated vertices to the vertices array. I thought it may have to do with the location of the final vertex of one cube and the first of the following, but because each triangle is a closed shape, I no longer think that is it. Please share your insights, and thank you in advance for your help.
There are a few issues with your approach. Let's visited them one-by-one:
mat4.translate(out, box, [i*h, i*h, i*h]);
According to the documentation, mat4.translate
does not actually transform your box
vertices. Instead it generates a translation matrix.
let translation = [];
mat4.translate(translation, mat4.identity([]), [i * h, i * h, i * h]);
You must then multiply each box
vertex by this translation matrix to get the transformed vertex:
for (let j = 0; j < box.length / N; j += N) {
let translatedVertex = [];
const vertex = [box[j], box[j + 1], box[j + 2]];
vec3.transformMat4(translatedVertex, vertex, translation);
vertices.push(...translatedVertex);
}
Translating by i*h
for each dimension will only offset your boxes along all three dimensions, so you will need to change your loop to translate along just the x-axis, then the y-axis, then the z-axis.
const vertices = [];
for (let x = 0; x < 3; x++) {
for (let y = 0; y < 3; y++) {
for (let z = 0; z < 3; z++) {
let translation = [];
mat4.translate(translation, mat4.identity([]), [x * h, y * h, z * h]);
for (let j = 0; j < box.length / N; j += N) {
let translatedVertex = [];
const vertex = [box[j], box[j + 1], box[j + 2]];
vec3.transformMat4(translatedVertex, vertex, translation);
vertices.push(...translatedVertex);
}
}
}
}