WebGL生成多小立方体组合大立方体的代码问题排查
WebGL生成立方体网格的问题排查与修复
我能通过顶点数组在WebGL中生成单个立方体,但现在想生成由多个小立方体组成的大立方体,实际呈现效果与预期不符(期望是3×3×3的立方体网格,实际画面混乱)。我怀疑问题出在box顶点初始化到顶点平移的循环代码段,附上代码并寻求问题分析与解决帮助:
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) }
我曾排查过立方体顶点衔接问题,已排除该可能,恳请分享见解。
问题分析
顶点平移逻辑完全错误
mat4.translate是用来变换4x4矩阵的API,不是直接处理顶点数组的。原代码中把out指向原始box数组,调用mat4.translate会直接修改原数组内容,导致后续循环使用被污染的顶点数据;同时最终push的是16个浮点数的矩阵数据,不是平移后的顶点,完全不符合预期。循环范围与维度错误
原循环仅遍历2次且只在单一维度平移,无法生成3×3×3的立方体网格,需要三层循环遍历x、y、z三个轴的位置才能生成完整网格。颜色数据生成逻辑混乱
vertices.length/N和vertices.length/(N*6)的计算无依据,无法正确对应每个立方体、每个面的颜色分配,应基于立方体数量、每个立方体的顶点数/面数来生成颜色数据。透视矩阵宽高比错误
mat4.perspective的第二个参数是宽高比(width/height),原代码用了canvas.height/canvas.width,会导致画面拉伸变形。
修复后的关键代码
顶点生成部分
const vertices = []; const boxVertexCount = box.length / 3; // 每个立方体36个顶点 // 生成3×3×3的立方体网格,偏移量让网格居中 for (let x = 0; x < N; x++) { for (let y = 0; y < N; y++) { for (let z = 0; z < N; z++) { // 计算当前立方体的中心偏移:从-(N-1)*h到(N-1)*h,间隔2h const offsetX = (x - (N - 1) / 2) * 2 * h; const offsetY = (y - (N - 1) / 2) * 2 * h; const offsetZ = (z - (N - 1) / 2) * 2 * h; // 遍历原始顶点,添加偏移后存入vertices for (let i = 0; i < box.length; i += 3) { vertices.push( box[i] + offsetX, box[i + 1] + offsetY, box[i + 2] + offsetZ ); } } } }
颜色数据生成部分
let colorData = []; const totalCubes = N * N * N; const verticesPerFace = 6; // 每个面6个顶点(2个三角形) for (let cube = 0; cube < totalCubes; cube++) { // 给当前立方体的6个面分配随机颜色 for (let face = 0; face < 6; face++) { const faceColor = randomColor(); // 假设该函数返回[r, g, b]格式的数组 // 每个面的6个顶点都使用同一颜色 for (let v = 0; v < verticesPerFace; v++) { colorData.push(...faceColor); } } }
透视矩阵修复
mat4.perspective(projectionMatrix, 90 * Math.PI/180, // vertical fov canvas.width/canvas.height, // 修正为宽/高 1e-4, 1e4 );
内容的提问来源于stack exchange,提问作者giveearholdtongue
相关产品推荐
相关产品推荐

