DirectX渲染数千方块性能低下,咨询优化方案与渲染实践合理性
优化你的DirectX体素引擎渲染性能
嘿,很高兴看到你在动手做Minecraft克隆的项目!先直接回应你的两个核心问题:
关于单个顶点缓冲区平移渲染的实践
这确实是导致你帧率暴跌的主要原因,属于需要立刻改进的不良实践。每调用一次DrawIndexedPrimitive,CPU和GPU之间都会产生一次调用开销——你现在循环4096次,等于把这个开销放大了4096倍,CPU会被频繁的API调用占满,GPU也没法高效地批处理任务,自然帧率会降到个位数。
核心优化方案
下面是针对你的场景最有效的优化手段,按优先级排序:
1. 实例化渲染(Instanced Rendering)
这是解决批量重复物体渲染的最优解,DirectX 9支持实例化绘制,你只需要存储一份方块的基础顶点/索引数据,再单独存储所有方块的位置(或其他实例化属性),通过一次API调用就能渲染所有方块:
- 第一步:定义实例数据结构,比如每个方块的位置:
struct INSTANCE_DATA { D3DXVECTOR3 position; }; - 第二步:创建实例顶点缓冲区,填充所有方块的位置信息:
LPDIRECT3DVERTEXBUFFER9 instance_buffer = nullptr; d3ddev->CreateVertexBuffer( 4096 * sizeof(INSTANCE_DATA), 0, 0, // 实例数据不需要FVF,后续通过流设置 D3DPOOL_MANAGED, &instance_buffer, NULL ); VOID* pInstanceData; instance_buffer->Lock(0, 0, (void**)&pInstanceData, 0); // 这里填充每个方块的position,比如循环生成网格位置 for (int i = 0; i < 4096; ++i) { ((INSTANCE_DATA*)pInstanceData)[i].position = D3DXVECTOR3( (i % 64) * 6.0f, // 匹配你的顶点±3的方块尺寸 ((i / 64) % 64) * 6.0f, (i / 64 / 64) * 6.0f ); } instance_buffer->Unlock(); - 第三步:设置双顶点流并执行实例化绘制:
这样一来,API调用从4096次降到1次,CPU开销会大幅降低,GPU也能高效处理批量任务。void render_frame(void) { // ... 你的视图/投影变换设置 ... // 设置基础方块的顶点流 d3ddev->SetStreamSource(0, v_buffer, 0, sizeof(CUSTOMVERTEX)); // 设置实例数据的顶点流,标记为实例化数据源 d3ddev->SetStreamSource(1, instance_buffer, 0, sizeof(INSTANCE_DATA), D3DSTREAMSOURCE_INSTANCEDATA); d3ddev->SetIndices(i_buffer); d3ddev->SetFVF(CUSTOMFVF); // 一次渲染所有4096个实例 d3ddev->DrawIndexedPrimitive(D3DPT_TRIANGLELIST, 0, 0, 24, 0, 12, 4096); }
2. 遮挡剔除(Occlusion Culling)
体素引擎最大的性能潜力在这里:只渲染暴露在空气中的方块面。比如一个方块的左侧相邻有另一个方块,那么左侧的面完全被遮挡,根本不需要渲染。
实现思路:
- 遍历所有方块,对每个方块的6个面进行检查:如果某个方向(上/下/左/右/前/后)存在其他方块,就跳过这个面的顶点/索引。
- 把所有需要渲染的面合并到一个大的顶点缓冲区和索引缓冲区,然后一次绘制。
这个优化能让你实际渲染的三角形数量减少70%以上(比如完全被包围的方块一个面都不用渲染),效果非常显著。
3. 视锥体剔除(Frustum Culling)
只渲染相机视野范围内的方块,完全在视锥体外面的方块直接跳过渲染(不管是实例化还是合并顶点,都可以提前过滤掉这些方块)。
实现方法:
- 计算相机的视锥体边界(可以用DirectX的
D3DXMatrixPerspectiveFovLH结合视图矩阵生成视锥体)。 - 给每个方块创建一个AABB包围盒,检查包围盒是否和视锥体相交,只有相交的方块才加入渲染队列。
4. 启用背面剔除
你已经给顶点设置了法线,只需要开启DirectX的背面剔除功能,就能自动丢弃每个方块背面的三角形:
// 在初始化时设置
d3ddev->SetRenderState(D3DRS_CULLMODE, D3DCULL_CCW); // 剔除逆时针的背面
这个设置默认可能没开启,开启后每个方块的三角形数量会减半(从12个降到6个)。
5. 顶点格式优化
你的CUSTOMVERTEX包含FLOAT X,Y,Z和D3DVECTOR NORMAL,可以尝试压缩顶点数据来减少内存带宽占用:
- 把XYZ改成半精度浮点数(
D3DFVF_XYZ16),如果你的方块尺寸固定且场景范围不大,精度完全足够。 - 法线可以用压缩格式,比如用
D3DCOLOR存储(把法线分量映射到0-255的范围),减少每个顶点的字节数。
总结
优先实现实例化渲染解决API调用开销问题,然后加上遮挡剔除减少三角形数量,这两个优化做完,帧率应该能立刻提升到60以上。后续再逐步加入视锥体剔除和顶点格式优化,进一步榨干性能。
内容的提问来源于stack exchange,提问作者Valerio Nenci




