VS2022+DirectX9坐标系与基本图元之顶点缓冲区(VertexBuffer)_0301

目前已经实现了DX9的基础渲染框架了,开始进行实际的3D渲染,这就要理解世界坐标系和本地坐标系以及图元的相关概念。

什么是顶点

简单并且通俗的理解,它就是线与线之间的交点,DX9设备通过这些交点自动计算出相关的面,然后,渲染出来。

概念上来讲:一个多边形中相邻两边的交汇点成为顶点(vertex),所以这个三角形有三个顶点来 表述。
而一个正方形,就需要两个三角形进行组合


顺序为绕续:

V0,V1,V2
V0,V2,V3

所以,一个三角形就是3个顶点,一个正方形需要3N个顶点那就是6个顶点,每个顶点需要X,Y,Z三个数据来描述在坐标系里的实际位置。

显卡与内存之间的关系,它们是怎么交互的

CPU实际上是调度者,而显卡实际上是一个服务端,而CPU充当客户端(应用程序),当需要把数据传到显卡上的时候,就会通过Direct3D API调用相关的命令把内存里的数据传到显卡上。然后,客户端依次传递命令,让显卡做事情,从而把显示的内容渲染到屏幕上。

创建顶点缓存

就是把三角形的三个顶点通过3DX的方式,把数据从内存加载到显卡缓存里(显卡缓冲区)便于下个渲染指令直接进行调用它,而不用再次从内存加载到显卡里,当然也可以通过DrawPrimitiveUP命令直接使用内存里的数据,显然效率不高,但是也可以作为测试使用。

顶点格式

为了让显卡了解你的顶点是什么格式,所以,有一个自定义的顶点格式规范,要按照这个规范来,显卡才能知道你给的顶点数据是什么结构。

struct CUSTOMVERTEX
{
    FLOAT x, y, z, rhw;
    DWORD color;
};
#define D3DFVF_CUSTOMVERTEX (D3DFVF_XYZRHW|D3DFVF_DIFFUSE)  //顶点格式

这里定义了一个CUSTOMVERTEX 自定义顶点类型。

D3DFVF_XYZRHW 这个宏表示x, y, z:顶点的3D坐标,rhw表示用于 2D 渲染的齐次坐标(无需透视变换,直接映射到屏幕)。
D3DFVF_DIFFUSE 指颜色

通过这种特定的方式就定义了顶点的格式信息,成为了显卡理解的顶点结构描述信息。

顶点缓存

顶点缓存就是把顶点数据通过顶点格式的方式从内存传到显卡的内存里的方式。

LPDIRECT3DVERTEXBUFFER9 g_pVB = NULL;    //顶点缓冲区对象
//顶点数据
CUSTOMVERTEX vertices[] =
{
    { 100.0f, 400.0f, 0.5f, 1.0f, 0xffff0000, },
    { 300.0f,  50.0f, 0.5f, 1.0f, 0xff00ff00, },
    { 500.0f, 400.0f, 0.5f, 1.0f, 0xff0000ff, },
};

//创建顶点缓冲区
if (FAILED(g_pd3dDevice->CreateVertexBuffer(3 * sizeof(CUSTOMVERTEX),0, D3DFVF_CUSTOMVERTEX,D3DPOOL_DEFAULT, &g_pVB, NULL)))
{
    return E_FAIL;
}

//填充顶点缓冲区
VOID* pVertices;
if (FAILED(g_pVB->Lock(0, sizeof(vertices), (void**)&pVertices, 0)))
    return E_FAIL;
    
CUSTOMVERTEX* pVerts = (CUSTOMVERTEX*)pVertices;
pVerts[0] = vertices[0];
pVerts[1] = vertices[1];
pVerts[2] = vertices[2];

//memcpy(pVertices, vertices, sizeof(vertices)); //memcpy内存拷贝效率更高,仅作示例

g_pVB->Unlock();

vertices 就是三角形D3DFVF_CUSTOMVERTEX格式的数据很容易看明白相关结构,通过Direct3D设备的CreateVertexBuffer创建顶点缓存API进行创建。

CreateVertexBuffer 函数详解

HRESULT CreateVertexBuffer(
UINT Length, // 缓冲区总大小(字节)
DWORD Usage, // 缓冲区使用标志
DWORD FVF, // 顶点格式(FVF 码)
D3DPOOL Pool, // 内存池(存储位置)
LPDIRECT3DVERTEXBUFFER9 *ppVertexBuffer, // 输出:创建的顶点缓冲区指针
HANDLE *pSharedHandle // 共享句柄(通常为 NULL)
);

  1. Length 缓存的对象大小,目前是使用了三个顶点就是三个CUSTOMVERTEX结构的大小
  2. Usage 标志为0
  3. FVF 顶点格式为前面定义的D3DFVF_CUSTOMVERTEX格式
  4. Pool 显卡内存池,选择默认 D3DPOOL_DEFAULT
  5. ppVertexBuffer 顶点缓存对象引用
  6. pSharedHandle 共享句柄,为NULL

顶点缓存区填充细节

通过 CreateVertexBuffer API创建了一个g_pVB 顶点缓冲区对象,那么,就可以对这个对象进行赋值操作,把它当成是一个数组就可以了。
对这个对象操作必须通过Lock和Unlock方式进行。

先调用对象的LOCK方法锁定对象,然后,获取 pVertices对象指针,就可以对其进行赋值操作了。实际上跟定义的vertices顶点数据一样,一个一个赋值即可。

赋值完毕然后进行Unlock解锁即可,那么,内存的数据就真的传递到显卡内存里了。

Lock参数

HRESULT Lock(
UINT OffsetToLock, // 锁定的起始偏移量(字节)
UINT SizeToLock, // 锁定的字节大小
void**ppbData, // 输出:指向锁定区域的指针(CPU可访问)
DWORD Flags // 锁定标志(控制锁定方式)
);

  1. OffsetToLock 可以锁定部分缓存,或者说从哪个位置开始锁定,默认为0,全部锁定
  2. SizeToLock 锁定的大小,或者说锁定的长度,目前是全部数据的长度
  3. ppbData 一个CPU 可以访问的对象指针
  4. Flags 锁定标志,为0默认

渲染图形

创建了顶点缓存那就可以进行渲染了

//清空后台缓冲区
g_pd3dDevice->Clear(0, NULL, D3DCLEAR_TARGET, D3DCOLOR_XRGB(45, 50, 170), 1.0f, 0);

//开始在后台缓冲区绘制图形
if (SUCCEEDED(g_pd3dDevice->BeginScene()))
{
    //在后台缓冲区绘制图形
    g_pd3dDevice->SetStreamSource(0, g_pVB, 0, sizeof(CUSTOMVERTEX));
    g_pd3dDevice->SetFVF(D3DFVF_CUSTOMVERTEX);
    g_pd3dDevice->DrawPrimitive(D3DPT_TRIANGLELIST, 0, 1);

    //结束在后台缓冲区绘制图形
    g_pd3dDevice->EndScene();
}

//将在后台缓冲区绘制的图形提交到前台缓冲区显示
g_pd3dDevice->Present(NULL, NULL, NULL, NULL);

可以看到这个颜色是渐变色,那是因为当顶点格式包含D3DFVF_DIFFUSE颜色的时候,Direct3D会自动对顶点颜色进行插值计算(Gouraud 着色),这是 3D 渲染中默认的颜色处理方式。

主要是SetStreamSource和SetFVF以及DrawPrimitive三个API的使用.

其中SetStreamSource和SetFVF是设置顶点数据和顶点格式的,而DrawPrimitive是具体的绘制。

SetStreamSource 指定数据流输入源

HRESULT SetStreamSource(
UINT StreamNumber, // 数据流通道编号
LPDIRECT3DVERTEXBUFFER9 pStreamData, // 要绑定的顶点缓冲区
UINT OffsetInBytes, // 缓冲区中数据的起始偏移量(字节)
UINT Stride // 每个顶点的字节大小(步长)
);
将顶点缓冲区(Vertex Buffer)绑定到渲染管线的数据流通道,告诉 GPU从哪个缓冲区读取顶点数据进行渲染。它是连接顶点数据和绘制函数DrawPrimitive的关键步骤。

  1. StreamNumber 默认为0,共有16个通道可以分为顶点数据通道和颜色通道。
  2. pStreamData 顶点缓冲数据,为之前创建的顶点缓冲数据。
  3. OffsetInBytes 从哪个位置开始读,默认为0
  4. Stride 顶点步长,数组每个结构的大小

SetFVF 设置自定义定点格式结构类型

告诉GPU该如何解析格式是什么结构类型

DrawPrimitive 绘制图元

这个API最终绘制的API
HRESULT DrawPrimitive(
D3DPRIMITIVETYPE PrimitiveType, // 图元类型(如三角形、线段等)
UINT StartVertex, // 起始顶点索引(从顶点缓冲区的第几个顶点开始)
UINT PrimitiveCount // 要绘制的图元数量
);

目前实际上支持很多个类型,但是,大部分3D绘制都是看模型有多少个三角形来进行绘制的所以都会选择三角形相关的绘制类型

  1. PrimitiveType D3DPT_TRIANGLELIST三角形列表,D3DPT_TRIANGLESTRIP三角形带,其他点线面
  2. StartVertex 从第一个顶点开始画,默认0
  3. PrimitiveCount 绘制三角形(图元)的个数,三角形列表(TRIANGLELIST):N个图元需要 3n 个顶点,三角形带/扇(TRIANGLESTRIP):N个图元需要 n+2 个顶点,目前绘制只有一个三角形。

总结

这个基础概念好多,需要深入理解。

git仓库

https://github.com/kesshei/direct9Demo/tree/main/03_%E7%AC%AC%E4%B8%89%E7%AB%A0%20%E5%9D%90%E6%A0%87%E7%B3%BB%E4%B8%8E%E5%9F%BA%E6%9C%AC%E5%9B%BE%E5%85%83/01_VertexBuffer
https://gitee.com/kesshei/direct9Demo/tree/main/03_%E7%AC%AC%E4%B8%89%E7%AB%A0%20%E5%9D%90%E6%A0%87%E7%B3%BB%E4%B8%8E%E5%9F%BA%E6%9C%AC%E5%9B%BE%E5%85%83/01_VertexBuffer
posted @ 2025-10-19 22:21  蓝创精英团队  阅读(33)  评论(0)    收藏  举报  来源