WebGL递归八面体生成球体:索引生成异常问题求助
问题描述
我正在尝试基于传入的recursionDepth参数,通过递归八面体细分生成类球体模型。目前顶点生成工作正常,但递归过程中三角形与线的索引生成不完整,例如当recursionDepth为2时,模型呈现残缺状态。以下是我的实现代码,恳请帮忙分析解决:
var octahedron = (function () { function midpoint(v1, v2) { return [ (v1[0] + v2[0]) / 2, (v1[1] + v2[1]) / 2, (v1[2] + v2[2]) / 2 ]; } function normalize(v) { var length = Math.sqrt(v[0] * v[0] + v[1] * v[1] + v[2] * v[2]); return [v[0] / length, v[1] / length, v[2] / length]; } function createVertexData(recursionDepth) { // Positions. const originVertices = new Float32Array([ 0.0, 1.0, 0.0, // Vertex 0: Top 1.0, 0.0, 0.0, // Vertex 1: Front-right -1.0, 0.0, 0.0, // Vertex 2: Front-left 0.0, 0.0, 1.0, // Vertex 3: Front-bottom 0.0, 0.0, -1.0, // Vertex 4: Back-bottom 0.0, -1.0, 0.0 // Vertex 5: Bottom ]); // Index data for triangles. const originIndicesTris = new Uint16Array([ 0, 1, 3, // Top-front-right triangle 0, 3, 2, // Top-front-left triangle 0, 2, 4, // Top-back-left triangle 0, 4, 1, // Top-back-right triangle 5, 3, 1, // Bottom-front-right triangle 5, 2, 3, // Bottom-front-left triangle 5, 4, 2, // Bottom-back-left triangle 5, 1, 4 // Bottom-back-right triangle ]); // Normals. const originNormals = new Float32Array([ // Top vertex 0.0, 1.0, 0.0, // Bottom vertex 0.0, -1.0, 0.0, // Front vertex 0.0, 0.0, 1.0, // Back vertex 0.0, 0.0, -1.0, // Left vertex -1.0, 0.0, 0.0, // Right vertex 1.0, 0.0, 0.0, ]); // Index data for lines. const originIndicesLines = new Uint16Array([ 0, 1, 1, 2, 2, 0, // Top face 0, 3, 3, 1, // Front face 0, 4, 4, 2, // Back face 5, 1, 1, 3, 3, 5, // Bottom face 5, 2, 2, 4, // Left face 5, 4, 4, 1 // Right face ]); // Subdivide each triangle and move the new vertices to the surface of a sphere. this.vertices = originVertices; this.normals = originNormals; this.indicesLines = originIndicesLines; this.indicesTris = originIndicesTris; function subdivideGeometry(depth, indicesTris, vertices) { if (depth <= 0) { // Base case: Return current geometry return { vertices: vertices, normals: new Float32Array(vertices), // 球体模型法线等于顶点位置 indicesTris: indicesTris, indicesLines: generateLinesFromTris(indicesTris) }; } const midpointCache = new Map(); const newVertices = Array.from(vertices); const newIndicesTris = []; // 生成唯一的中点键(确保v1索引小于v2,避免重复缓存) function getMidpointKey(i1, i2) { return i1 < i2 ? `${i1}-${i2}` : `${i2}-${i1}`; } // 从缓存获取或生成中点 function getOrCreateMidpoint(i1, i2) { const key = getMidpointKey(i1, i2); if (midpointCache.has(key)) { return midpointCache.get(key); } const v1 = vertices.subarray(i1 * 3, i1 * 3 + 3); const v2 = vertices.subarray(i2 * 3, i2 * 3 + 3); const m = normalize(midpoint(v1, v2)); const index = newVertices.length / 3; newVertices.push(...m); midpointCache.set(key, index); return index; } // 处理每个三角形 for (let i = 0; i < indicesTris.length; i += 3) { const v0 = indicesTris[i]; const v1 = indicesTris[i + 1]; const v2 = indicesTris[i + 2]; // 生成三个中点 const m01 = getOrCreateMidpoint(v0, v1); const m12 = getOrCreateMidpoint(v1, v2); const m20 = getOrCreateMidpoint(v2, v0); // 添加四个新三角形的索引 newIndicesTris.push( v0, m01, m20, m01, v1, m12, m20, m12, v2, m01, m12, m20 ); } // 递归进行下一层细分 return subdivideGeometry(depth - 1, new Uint16Array(newIndicesTris), new Float32Array(newVertices)); } // 根据三角形索引生成线框索引(避免重复边) function generateLinesFromTris(tris) { const edgeCache = new Set(); const lines = []; function addEdge(a, b) { const key = a < b ? `${a}-${b}` : `${b}-${a}`; if (!edgeCache.has(key)) { edgeCache.add(key); lines.push(a, b); } } for (let i = 0; i < tris.length; i += 3) { const a = tris[i]; const b = tris[i + 1]; const c = tris[i + 2]; addEdge(a, b); addEdge(b, c); addEdge(c, a); } return new Uint16Array(lines); } if (recursionDepth > 0) { // Call the recursive function const updatedGeometryData = subdivideGeometry(recursionDepth, this.indicesTris, this.vertices); this.vertices = updatedGeometryData.vertices; this.normals = updatedGeometryData.normals; this.indicesTris = updatedGeometryData.indicesTris; this.indicesLines = updatedGeometryData.indicesLines; } } return { createVertexData: createVertexData }; }());
关键问题分析与修复
顶点重复冗余
原代码在处理每个三角形时,重复添加原始顶点和中点,导致顶点数量呈指数级冗余,同时索引计算完全混乱。修复后通过midpointCache缓存已生成的中点,确保每个边的中点只生成一次,大幅减少顶点数量并保证索引正确性。索引生成逻辑错误
原代码的新三角形索引写法完全错误,修复后将每个原始三角形拆分为4个新三角形,索引正确对应原始顶点和新生成的中点:
- 第一个三角形:原顶点v0、中点m01、中点m20
- 第二个三角形:中点m01、原顶点v1、中点m12
- 第三个三角形:中点m20、中点m12、原顶点v2
- 第四个三角形:中点m01、中点m12、中点m20
递归逻辑错误
原递归时错误地用深层递归结果覆盖当前层的细分结果,修复后递归是在当前层细分的基础上继续处理,保证每一层细分都正确累积。线框索引生成优化
新增generateLinesFromTris函数,通过缓存边避免重复生成线框索引,确保线框完整且无冗余边。全局变量修复
给所有原始数据变量添加const声明,避免污染全局作用域。
内容的提问来源于stack exchange,提问作者neverhalfnot
相关产品推荐
相关产品推荐

