生成MC地图时的优化
一. 合批
问题:传统渲染方式每个方块一次渲染调用
解决方案:使用Three.js的InstancedMesh实现实例化渲染
效果:1000个方块从1000次调用减少到1次调用
1 合批的工作原理:
几何体共享:所有相同类型的方块(如草方块、石头方块)共享同一个 BoxGeometry(1, 1, 1)
材质共享:相同类型的方块使用相同的材质
实例化矩阵:通过 instanceMatrix 存储每个方块的位置信息
单次渲染调用:GPU只需要一次渲染调用就能绘制所有相同类型的方块
private createMultiTextureInstancedMesh(
positions: THREE.Vector3[],
materials: THREE.Material[],
blockType: BlockType
): THREE.InstancedMesh {
// 使用 BoxGeometry 和材质数组,Three.js 会自动处理多材质
const geometry = new THREE.BoxGeometry(1, 1, 1);
const mesh = new THREE.InstancedMesh(geometry, materials, positions.length);
mesh.name = `${blockType}_Multi_${positions.length}`;
mesh.castShadow = true;
mesh.receiveShadow = true;
// 设置实例位置
const matrix = new THREE.Matrix4();
positions.forEach((pos, index) => {
matrix.setPosition(pos);
mesh.setMatrixAt(index, matrix);
});
mesh.instanceMatrix.needsUpdate = true;
mesh.computeBoundingSphere();
return mesh;
}2 数据对比
2.1 渲染调用次数对比
传统渲染方式 vs 实例化渲染:
GPU性能数据
// 假设一个32x32的区块,平均每个区块有800个方块
const blocksPerChunk = 800;
const chunkCount = 9; // 3x3 绘制距离
const totalBlocks = blocksPerChunk * chunkCount; // 7,200个方块
// 性能对比
const traditionalDrawCalls = 7200; // 传统方式
const instancedDrawCalls = 9; // 实例化方式(按区块类型分组)
const performanceImprovement = traditionalDrawCalls / instancedDrawCalls; // 800x内存使用对比
二. LOD(Level of Detail)实现
1 LOD的优势:
内存管理:只加载玩家附近的区块,远处区块自动卸载
渲染优化:只渲染可见区块,减少GPU负担
无限世界:支持无限大的地图,而不占用无限内存
性能分级:近处区块高精度,远处区块低精度
2 基本参数
private blockSize: number = 1; // 方块大小
private chunkSize: number = 32; // 区块大小
private drawDistance: number = 3; // 绘制距离3 距离分级
// (playerChunkX,playerChunkZ) 玩家所在位置坐标
private getVisibleChunks(playerChunkX: number, playerChunkZ: number): Array<{x: number, z: number}> {
const visibleChunks: Array<{x: number, z: number}> = [];
for (let x = playerChunkX - this.drawDistance; x <= playerChunkX + this.drawDistance; x++) {
for (let z = playerChunkZ - this.drawDistance; z <= playerChunkZ + this.drawDistance; z++) {
visibleChunks.push({ x, z });
}
}
return visibleChunks;
}不同绘制距离的性能对比:
4 动态加载/卸载
private updateChunkVisibility(): void {
if (!this.isInfiniteWorld) return;
const playerChunkX = Math.floor(this.playerPosition.x / this.chunkSize);
const playerChunkZ = Math.floor(this.playerPosition.z / this.chunkSize);
// 计算应该可见的区块
const visibleChunks = this.getVisibleChunks(playerChunkX, playerChunkZ);
// 找出需要加载的新区块
const chunksToAdd = this.getChunksToAdd(visibleChunks);
// 找出需要卸载的区块
const chunksToRemove = this.getChunksToRemove(visibleChunks);
// 添加到加载队列
chunksToAdd.forEach(chunk => {
if (!this.chunkLoadingQueue.some(c => c.x === chunk.x && c.z === chunk.z)) {
this.chunkLoadingQueue.push(chunk);
}
});
// 添加到卸载队列
chunksToRemove.forEach(chunkKey => {
if (!this.chunkUnloadingQueue.includes(chunkKey)) {
this.chunkUnloadingQueue.push(chunkKey);
}
});
// 异步处理区块加载和卸载
this.processChunkQueue();
}区块加载时间:12.3ms(草方块区块)
区块卸载时间:8.3ms
玩家移动影响:步行时3.5ms/秒,跑步时8.6ms/秒