Direct3D使用顶点缓存和索引缓存进行绘制
1. 使用顶点缓存绘制静止图形
效果图
1.0 模型表示
- 本例中只有一个三角形mesh,需要三个顶点来描述。FVF顶点格式如下定义
struct Vertex
{
float _x, _y, _z, _rhw;
DWORD _color;
Vertex(float x, float y, float z, float rhw, DWORD color):
_x(x), _y(y), _z(z), _rhw(rhw), _color(color){}
static const DWORD FVF;
};
const DWORD Vertex::FVF = D3DFVF_XYZRHW | D3DFVF_DIFFUSE;
-
D3DFVF_XYZRHW和D3DFVF_XYZ的区别
- D3DFVF_XYZRHW设置的坐标是屏幕坐标,客户区左上角为(0,0),不需要进行一系列变换,D3DFVF_XYZ设置的坐标必须经过一系列变换才能在屏幕上显示
- D3DFVF_XYZRHW默认有光照,D3DFVF_XYZ默认没有光照。因此使用D3DFVF_XYZ时需要设置光照
device->SetRenderState(D3DRS_LIGHTING, false);
-
由于不需要进行各类坐标变换,因此只要直接设置顶点缓存即可
IDirect3DVertexBuffer *pVB = NULL;
Device->CreateVertexBuffer(3 * sizeof(Vertex),
D3DUSAGE_WRITEONLY,
Vertex::FVF,
D3DPOOL_MANAGED,
&pVB,
NULL);
//然后填充顶点缓存
Vertex *vertices;
pVB->Lock(0, 0, (void**)&vertices, 0);
//第三个参数传入了void**,意味着可能更改指针自身的值,而数组名指向的地址是不可以被更改的,因而上一行必须定义为指针,定义为数组会出错,下面开始填充
vertices[0] = Vertex(400.0f, 150.0f, 0.0f, 1.0f, 0xffff0000);
vertices[1] = Vertex(600.0f, 450.0f, 0.0f, 1.0f, 0xff00ff00);
vertices[2] = Vertex(200.0f, 450.0f, 0.0f, 1.0f, 0xff0000ff);
pVB->Unlock();
-
参数中D3DUSAGE的取值
- 0:默认属性,无附加属性
- D3DUSAGE_DYNAMIC:将缓存设置为动态缓存
- D3DUSAGE_POINTS:标记规定缓存用于储存点图元
- D3DUSAGE_SOFTWAREPROCESSING:指定软件顶点运算方式
- D3DUSAGE_WRITEONLY:规定程序对缓存的操作模式为”只写“
-
至此完成了模型的定义,接下来就可以进行绘制了
-
缓存区域
- 创建缓存时,不使用D3DUSAGE_DYNAMIC标记的话,缓存默认为静态缓存,一般放置在显存中,这类缓存中的数据的处理速度极快,但对其进行IO操作则非常慢,因此这类储存区常用于储存不需经常访问或修改的数据,如地形数据。
- 若是用来D3DUSAGE_DYNAMIC,则创建的是一块动态缓存,这类缓存常被放置在AGP/PCIe储存区中,AGP/PCIe储存区也是内存的一部分,但是可以通过AGP/PCIe通道直接访问,因此访问速度高于内存,如果程序需要频繁更新缓存内容,则应该使用这类缓存
显卡缓存层次结构
1.1 绘制
我们编制的D3D框架中,绘制图象的工作在Render函数中完成,Render的基本结构如下,我们的绘制代码就将填充在BeginScene和EndScene之间
bool Render(float DeltaTime)
{
Device->Clear(...);
if(Device->BeginScene())
{
//TODO: Drawing codes
Device->EndScene();
}
Device->Present(...);
return true;
}
-
绘制工作分为四步
- 将绘制数据来源设置为顶点缓存
Device->SetStreamSource(...);
- 设置FVF格式
Device->SetFVF(...);
- (可选)设置索引缓存的地址
Device->SetIndices(...);
- 绘制图元
Device->DrawPrimitive(...);
-
在本例中代码即为
bool Render(float DelteTime)
{
//Set BackBuffer to white
pD3DDEV->Clear(0, NULL, D3DCLEAR_TARGET, D3DCOLOR_XRGB(0, 0, 0), 1.0f, 0);
//Set Wireframe
//pD3DDEV->SetRenderState(D3DRS_FILLMODE, D3DFILL_WIREFRAME);
//Render
if( SUCCEEDED(pD3DDEV->BeginScene()) )
{
//All actual render operations should be enclosed by BeginScene and EndScene
//Step 1. Set Source of data stream
pD3DDEV->SetStreamSource(0, pVB, 0, sizeof(COLORED_VERTEX));
//Step 2. Set Vertex Format, aka. FVF
pD3DDEV->SetFVF(D3DFVF_COLOREDVERTEX);
//Step 3(Optional). Set Index Buffer, only needed when using index buffer
//Step 4. Draw
pD3DDEV->DrawPrimitive(D3DPT_TRIANGLELIST, 0, 1);
//Finish Drawing
pD3DDEV->EndScene();
}
//Present BackBuffer
pD3DDEV->Present(NULL, NULL, NULL, NULL);
return true;
}
2. 使用索引缓存绘制动态图像
效果图
要说明的是,这儿直接使用顶点缓存也是可以的,但是正如图中所示,一个立方体有8个顶点,但描述这个立方体需要36个顶点,每个顶点被使用了四次,如果这儿使用索引缓存,则顶点缓存只需要储存8个顶点即可
- 顶点缓存的建立和上个例子一样,建立索引缓存的方式和建立顶点缓存类似,相比顶点缓存,多出了一个描述索引格式的参数。以三角形为例
Device->CreateIndexBuffer(3 * sizeof(DWORD),
D3DUSAGE_WRITEONLY,
D3DFMT_INDEX16,
D3DPOOL_MANAGED,
&pIB,
0);
WORD *indices = 0;
pIB->Lock(0, 0, (void**)&indices, 0);
indices[0] = 0;
indices[1] = 1;
indices[2] = 2;
pIB->Unlock();
-
由于我们要建立动态图像,所以D3DFVF_XYZRHW格式不再适合,需要使用D3DFVF_XYZ格式并设置各类变换矩阵
- 取景变换矩阵:EyeAt为摄像机的坐标,LookAt为摄像机的取景点坐标,up为坐标系”上“方向矢量,D3DXMatrixLookAtLH最后的LH说明这是左手坐标系
D3DXMATRIX cam; D3DXVECTOR3 EyeAt(0.0f, 0.0f, -3.0f); D3DXVECTOR3 LookAt(0.0f, 0.0f, 0.0f); D3DXVECTOR3 up(0.0f, 1.0f, 0.0f); D3DXMatrixLookAtLH(&cam, &EyeAt, &LookAt, &up); pD3DDEV->SetTransform(D3DTS_VIEW, &cam);
- 投影变换矩阵
D3DXMATRIX proj; D3DXMatrixPerspectiveFovLH(&proj, D3DX_PI * 0.5f, //FoV, Field of View 800.0f/ 600.0f, //Aspect Ratio 1.0f, //Z-near 100.0f); //Z-far pD3DDEV->SetTransform(D3DTS_PROJECTION, &proj);
- 世界变换,由于我们要让三维场景中的物体旋转,因此世界变换矩阵每帧都需要更新,因此将其放在Render函数内进行更新
D3DXMATRIX ry; static float y = 0.0f; D3DXMatrixRotationY(&ry, y); y += DeltaTime; if( y >= 6.28f ) y = 0.0f; pD3DDEV->SetTransform(D3DTS_WORLD, &ry);
-
绘图模式:由于三角形旋转过程中会将背面转向摄像机,因此要取消背面消隐,否则背面旋转到面向摄像机的时候不会显示,此外,前面也提过,使用D3DFVF_XYZ时要关闭光照
Device->SetRenderState(D3DRS_CULLMODE, D3DCULL_NONE); Device->SetRenderState(D3DRS_LIGHTING, false);
-
由于图形旋转过程中,有了Z方向的变化,因此在绘图前清除缓冲区时要一并清除Z缓存
pD3DDEV->Clear(0, 0, D3DCLEAR_TARGET | D3DCLEAR_ZBUFFER, 0xffffffff, 1.0f, 0);
Written with StackEdit.
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
· SQL Server 2025 AI相关能力初探
· Linux系列:如何用 C#调用 C方法造成内存泄露
· AI与.NET技术实操系列(二):开始使用ML.NET
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 【自荐】一款简洁、开源的在线白板工具 Drawnix
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY
· Docker 太简单,K8s 太复杂?w7panel 让容器管理更轻松!