Irrlicht中添加过程化网格失败问题求助
Irrlicht多网格缓冲区地形生成访问错误问题
问题情况
在Irrlicht中生成过程化地形网格时,使用addOctreeSceneNode或addMeshSceneNode均失败,报错触发在irrArray.h的调试断点。GenerateMesh函数内所有检查都通过,Mesh的缓冲区数量、顶点/索引内容、材质纹理信息看起来都正常,但就是无法正常渲染。
如果移除分块缓冲区逻辑,只用一个忽略BlockType的缓冲区就能正常运行;但切换为IMesh会导致地形代码几乎全部重构,不想走这条路。
原问题代码
SMesh* Terrain::GenerateMesh(Chunk& chunk){ const vector<Chunk::Vertex>& vertices = chunk.vertices; const vector<u16>& indices = chunk.indices; SMeshBuffer* grassBuffer = new SMeshBuffer(); SMeshBuffer* dirtBuffer = new SMeshBuffer(); SMeshBuffer* rockBuffer = new SMeshBuffer(); SMeshBuffer* bedrockBuffer = new SMeshBuffer(); for (u16 i = 0; i < indices.size(); ++i) { u16 ix = indices[i]; // Get index from the indices vector // Check if the index is within the bounds of the vertices vector if (ix < vertices.size()) { // Access the vertex using the index Chunk::Vertex vertex = vertices[ix]; // Handle the block types based on a more explicit comparison switch (vertex.type) { case Chunk::BLOCKTYPE::Grass: grassBuffer->Vertices.push_back(S3DVertex(vertex.position, vertex.normal, vertex.color, vertex.texCoords)); grassBuffer->Indices.push_back(ix); break; case Chunk::BLOCKTYPE::Dirt: dirtBuffer->Vertices.push_back(S3DVertex(vertex.position, vertex.normal, vertex.color, vertex.texCoords)); dirtBuffer->Indices.push_back(ix); break; case Chunk::BLOCKTYPE::Rock: rockBuffer->Vertices.push_back(S3DVertex(vertex.position, vertex.normal, vertex.color, vertex.texCoords)); rockBuffer->Indices.push_back(ix); break; case Chunk::BLOCKTYPE::BedRock: bedrockBuffer->Vertices.push_back(S3DVertex(vertex.position, vertex.normal, vertex.color, vertex.texCoords)); bedrockBuffer->Indices.push_back(ix); break; default: break; } } else { // Print additional debug information if the index is invalid printf("Invalid index: %d (Index exceeds vertices size of %zu)\n", ix, vertices.size()); } } printf("GrassBuffer vertices: %i, indices: %i\n", grassBuffer->Vertices.size(), grassBuffer->Indices.size()); printf("DirtBuffer vertices: %i, indices: %i\n", dirtBuffer->Vertices.size(), dirtBuffer->Indices.size()); printf("RockBuffer vertices: %i, indices: %i\n", rockBuffer->Vertices.size(), rockBuffer->Indices.size()); printf("BedrockBuffer vertices: %i, indices: %i\n", bedrockBuffer->Vertices.size(), bedrockBuffer->Indices.size()); SMesh* mesh = new SMesh(); if(grassBuffer->Vertices.size() > 0) { grassBuffer->Material.MaterialType = video::EMT_SOLID; grassBuffer->getMaterial().setTexture(0, grassTexture); mesh->addMeshBuffer(grassBuffer); } if(dirtBuffer->Vertices.size() > 0) { dirtBuffer->Material.MaterialType = video::EMT_SOLID; dirtBuffer->getMaterial().setTexture(0, dirtTexture); mesh->addMeshBuffer(dirtBuffer); } if(rockBuffer->Vertices.size() > 0) { rockBuffer->Material.MaterialType = video::EMT_SOLID; rockBuffer->getMaterial().setTexture(0, rockTexture); mesh->addMeshBuffer(rockBuffer); } if(bedrockBuffer->Vertices.size() > 0) { bedrockBuffer->Material.MaterialType = video::EMT_SOLID; bedrockBuffer->getMaterial().setTexture(0, bedrockTexture); mesh->addMeshBuffer(bedrockBuffer); } mesh->recalculateBoundingBox(); grassBuffer->drop(); dirtBuffer->drop(); rockBuffer->drop(); bedrockBuffer->drop(); return mesh; }
问题根源
核心错误是索引复用错误:每个子缓冲区(grassBuffer、dirtBuffer等)的Vertices是独立的局部列表,但你直接把原chunk的全局顶点索引塞进了缓冲区的Indices数组。比如原chunk的第10个顶点是Grass类型,被加到grassBuffer的Vertices里成为第0个顶点,但你却把10写入grassBuffer的Indices,渲染时会去访问grassBuffer->Vertices[10],直接越界触发irrArray的断言断点。
修复代码
SMesh* Terrain::GenerateMesh(Chunk& chunk){ const vector<Chunk::Vertex>& vertices = chunk.vertices; const vector<u16>& indices = chunk.indices; SMeshBuffer* grassBuffer = new SMeshBuffer(); SMeshBuffer* dirtBuffer = new SMeshBuffer(); SMeshBuffer* rockBuffer = new SMeshBuffer(); SMeshBuffer* bedrockBuffer = new SMeshBuffer(); // 维护每个缓冲区的顶点映射:原全局索引 -> 局部索引 unordered_map<u16, u16> grassVertexMap; unordered_map<u16, u16> dirtVertexMap; unordered_map<u16, u16> rockVertexMap; unordered_map<u16, u16> bedrockVertexMap; for (u16 i = 0; i < indices.size(); ++i) { u16 globalIx = indices[i]; if (globalIx >= vertices.size()) { printf("Invalid index: %d (Index exceeds vertices size of %zu)\n", globalIx, vertices.size()); continue; } const Chunk::Vertex& vertex = vertices[globalIx]; u16 localIx; switch (vertex.type) { case Chunk::BLOCKTYPE::Grass: if (grassVertexMap.find(globalIx) == grassVertexMap.end()) { localIx = grassBuffer->Vertices.size(); grassBuffer->Vertices.push_back(S3DVertex(vertex.position, vertex.normal, vertex.color, vertex.texCoords)); grassVertexMap[globalIx] = localIx; } else { localIx = grassVertexMap[globalIx]; } grassBuffer->Indices.push_back(localIx); break; case Chunk::BLOCKTYPE::Dirt: if (dirtVertexMap.find(globalIx) == dirtVertexMap.end()) { localIx = dirtBuffer->Vertices.size(); dirtBuffer->Vertices.push_back(S3DVertex(vertex.position, vertex.normal, vertex.color, vertex.texCoords)); dirtVertexMap[globalIx] = localIx; } else { localIx = dirtVertexMap[globalIx]; } dirtBuffer->Indices.push_back(localIx); break; case Chunk::BLOCKTYPE::Rock: if (rockVertexMap.find(globalIx) == rockVertexMap.end()) { localIx = rockBuffer->Vertices.size(); rockBuffer->Vertices.push_back(S3DVertex(vertex.position, vertex.normal, vertex.color, vertex.texCoords)); rockVertexMap[globalIx] = localIx; } else { localIx = rockVertexMap[globalIx]; } rockBuffer->Indices.push_back(localIx); break; case Chunk::BLOCKTYPE::BedRock: if (bedrockVertexMap.find(globalIx) == bedrockVertexMap.end()) { localIx = bedrockBuffer->Vertices.size(); bedrockBuffer->Vertices.push_back(S3DVertex(vertex.position, vertex.normal, vertex.color, vertex.texCoords)); bedrockVertexMap[globalIx] = localIx; } else { localIx = bedrockVertexMap[globalIx]; } bedrockBuffer->Indices.push_back(localIx); break; default: break; } } printf("GrassBuffer vertices: %i, indices: %i\n", grassBuffer->Vertices.size(), grassBuffer->Indices.size()); printf("DirtBuffer vertices: %i, indices: %i\n", dirtBuffer->Vertices.size(), dirtBuffer->Indices.size()); printf("RockBuffer vertices: %i, indices: %i\n", rockBuffer->Vertices.size(), rockBuffer->Indices.size()); printf("BedrockBuffer vertices: %i, indices: %i\n", bedrockBuffer->Vertices.size(), bedrockBuffer->Indices.size()); SMesh* mesh = new SMesh(); if(grassBuffer->Vertices.size() > 0) { grassBuffer->Material.MaterialType = video::EMT_SOLID; grassBuffer->getMaterial().setTexture(0, grassTexture); grassBuffer->recalculateBoundingBox(); // 给每个缓冲区单独计算包围盒 mesh->addMeshBuffer(grassBuffer); } if(dirtBuffer->Vertices.size() > 0) { dirtBuffer->Material.MaterialType = video::EMT_SOLID; dirtBuffer->getMaterial().setTexture(0, dirtTexture); dirtBuffer->recalculateBoundingBox(); mesh->addMeshBuffer(dirtBuffer); } if(rockBuffer->Vertices.size() > 0) { rockBuffer->Material.MaterialType = video::EMT_SOLID; rockBuffer->getMaterial().setTexture(0, rockTexture); rockBuffer->recalculateBoundingBox(); mesh->addMeshBuffer(rockBuffer); } if(bedrockBuffer->Vertices.size() > 0) { bedrockBuffer->Material.MaterialType = video::EMT_SOLID; bedrockBuffer->getMaterial().setTexture(0, bedrockTexture); bedrockBuffer->recalculateBoundingBox(); mesh->addMeshBuffer(bedrockBuffer); } mesh->recalculateBoundingBox(); grassBuffer->drop(); dirtBuffer->drop(); rockBuffer->drop(); bedrockBuffer->drop(); return mesh; }
额外优化点
- 给每个子缓冲区单独调用
recalculateBoundingBox(),确保每个缓冲区的包围盒正确,避免Octree节点计算错误 - 使用
unordered_map来映射全局索引到局部索引,避免重复添加相同顶点,减少冗余数据
内容的提问来源于stack exchange,提问作者bard mask
相关产品推荐
相关产品推荐

