Direct3D 初涉: Direct3D 中的绘制
1. 顶点缓存和索引缓存(vertex/index buffer)
一个顶点缓存是一个包含顶点数据的连续内存空间;一个索引缓存是包含索引数据的连续内存空间。顶点缓存和索引缓存可以被放置到显存(video memory)中。进行绘制时,使用显存中的数据将获得比使用系统内存中的数据(Array)快得多的速度。
1.1 创建
顶点缓存用接口 IDirect3DVertexBuffer9 表示,用CreateVertexBuffer 创建;索引缓存用 IDirect3DIndexBuffer9 表示,用 CreateIndexBuffer 创建。
*静态缓存与动态缓存
创建缓存时,如果未使用标记 D3DUSAGE_DYNAMIC ,则称所创建的缓存为静态缓存(static buffer)。静态缓存一般被放置在显存中,以保证存储于其中的数据得到最高效的处理。然而,静态缓存是以牺牲对静态缓存读写操作的速度为代价的,这是因为访问显存的速度本身就很慢。基于上述原因,我们用静态缓存来存储静态数据(那些不需要经常修改或访问的数据)。静态缓存必须在应用程序初始化时用几何体的数据进行填充。
创建缓存时,如果使用了标记 D3DUSAGE_DYNAMIC , 则称所创建的缓存为动态缓存(dynamic buffer)。动态缓存一般放置在 AGP(Accelerated Graphics Port) 存储区中,其内容可被迅速更新。动态缓存中数据的处理速度不像静态缓存那样快,这是因为在绘制之前数据必须传输到显存中。但动态缓存的突出优点是其更新速度相当快(快速的 CPU 写操作)。所以,如果您需要频繁更新缓存中的内容,该缓存应设置为动态的。
对显存和 AGP 存储区进行读操作非常慢。所以,如果您需要在程序中运行时读取几何数据,最好在系统内存中保留一份副本,然后需要时对其进行读操作。
1.2访问顶点缓存
使用 Lock 方法来获取指向缓存内容的指针。访问完毕之后,必须对缓存进行解锁(unlock)操作。
1.3 获取顶点缓存和索引缓存的信息
使用 GetDesc 方法来获取顶点缓存(D3DVERTEXBUFFER_DESC)和索引缓存(D3DINDEXBUFFER_DESC)的信息。
2. 绘制状态
绘制状态(render state),几何体的绘制方式。使用 SetRenderState 来指定绘制状态。
3. 绘制的准备工作
3.1 指定数据流输入源。将顶点缓存和数据流进行链接,实质上是将几何体的信息传输到绘制流水线中。使用 SetStreamSource 方法来设置输入源。
3.2 设置顶点格式。指定(SetFVF)后续绘制调用中使用的顶点格式。
3.3 设置索引缓存。如果我们想使用索引缓存,必须对后续绘制操作中所要使用的索引缓存进行设置(SetIndices)。任意时刻只允许使用一个索引缓存,索引如果您要用一个不同的索引缓存绘制物体时,必须进行切换。
4. 使用顶点缓存和索引缓存进行绘制
使用 DrawPrimitive 或 DrawIndexedPrimitive 方法将待绘制几何体的信息通过绘制流水线传输。这两个方法从顶点数据流中获取顶点信息,并从当前设定的索引缓存中提取索引信息。所有的绘制方法都必须在 BeginScene 和 EndScene 构成的方法对之间进行调用。
5. D3DX 几何体
在代码中通过创建三角形单元来构建 3D 物体是非常繁琐的。 D3DX 库提供了一些用于生成简单 3D 几何体的网格数据的方法。
这六个方法类似,都使用了 D3DX 网格数据结构 ID3DXMesh 和 ID3DXBuffer 接口。使用这些方法生成网格数据后,用 DrawSubSet 函数对其进行绘制。网格使用完毕后必须进行释放(Release)。
6. 例程(DrawCube)
例程框架请看:Direct3D 初涉:Direct3D 框架的搭建
在 Direct3DFrame 类中添加两个变量,保存立方体的顶点和索引数据:
IDirect3DVertexBuffer9* m_vb; // 顶点缓存
IDirect3DIndexBuffer9* m_ib; // 索引缓存
在构造函数中初始化为空:
this->m_vb = NULL;
this->m_ib = NULL;
定义顶点结构和该结构的灵活顶点格式。添加一个新文件 Vertex.h ,文件内容如下:
#ifndef __VERTEX_H__
#define __VERTEX_H__
struct Vertex
{
Vertex(){}
Vertex(float x, float y, float z)
{
m_x = x;
m_y = y;
m_z = z;
}
float m_x;
float m_y;
float m_z;
static const DWORD FVF;
};
const DWORD Vertex::FVF = D3DFVF_XYZ;
#endif /* __VERTEX_H_ */
在 Setup 函数中创建顶点缓存和索引缓存,然后对缓存进行锁定,将构成立方体的顶点数据以及构成立方体的三角形单元的索引数据分别写入顶点缓存和索引缓存。然后将摄像机沿 Z 轴负方向平移几个单位,以使绘制在世界坐标系原点的立方体处于摄像机的视场内。然后再实施投影变换。最终,将填充模式的绘制状态设为线框模式。
bool Direct3DFrame::Setup()
{
m_device->CreateVertexBuffer(
8 * sizeof(Vertex),
D3DUSAGE_WRITEONLY,
Vertex::FVF,
D3DPOOL_MANAGED,
&m_vb,
0);
m_device->CreateIndexBuffer(
36 * sizeof(WORD),
D3DUSAGE_WRITEONLY,
D3DFMT_INDEX16,
D3DPOOL_MANAGED,
&m_ib,
0);
// define unique vertices:
Vertex* vertices;
m_vb->Lock(0, 0, (void**)&vertices, 0);
// vertices of a unit cube
vertices[0] = Vertex(-1.0f, -1.0f, -1.0f);
vertices[1] = Vertex(-1.0f, 1.0f, -1.0f);
vertices[2] = Vertex( 1.0f, 1.0f, -1.0f);
vertices[3] = Vertex( 1.0f, -1.0f, -1.0f);
vertices[4] = Vertex(-1.0f, -1.0f, 1.0f);
vertices[5] = Vertex(-1.0f, 1.0f, 1.0f);
vertices[6] = Vertex( 1.0f, 1.0f, 1.0f);
vertices[7] = Vertex( 1.0f, -1.0f, 1.0f);
m_vb->Unlock();
// define the triangles of the cube:
WORD* indices = 0;
m_ib->Lock(0, 0, (void**)&indices, 0);
// front side
indices[0] = 0; indices[1] = 1; indices[2] = 2;
indices[3] = 0; indices[4] = 2; indices[5] = 3;
// back side
indices[6] = 4; indices[7] = 6; indices[8] = 5;
indices[9] = 4; indices[10] = 7; indices[11] = 6;
// left side
indices[12] = 4; indices[13] = 5; indices[14] = 1;
indices[15] = 4; indices[16] = 1; indices[17] = 0;
// right side
indices[18] = 3; indices[19] = 2; indices[20] = 6;
indices[21] = 3; indices[22] = 6; indices[23] = 7;
// top
indices[24] = 1; indices[25] = 5; indices[26] = 6;
indices[27] = 1; indices[28] = 6; indices[29] = 2;
// bottom
indices[30] = 4; indices[31] = 0; indices[32] = 3;
indices[33] = 4; indices[34] = 3; indices[35] = 7;
m_ib->Unlock();
D3DXVECTOR3 position(0.0f, 0.0f, -5.0f);
D3DXVECTOR3 target(0.0f, 0.0f, 0.0f);
D3DXVECTOR3 up(0.0f, 1.0f, 0.0f);
D3DXMATRIX v;
D3DXMatrixLookAtLH(&v, &position, &target, &up);
m_device->SetTransform(D3DTS_VIEW, &v);
//
// Set the projection matrix.
//
D3DXMATRIX proj;
D3DXMatrixPerspectiveFovLH(
&proj,
D3DX_PI * 0.5f, // 90 - degree
(float)m_width / (float)m_height,
1.0f,
1000.0f);
m_device->SetTransform(D3DTS_PROJECTION, &proj);
//
// Switch to wireframe mode.
//
m_device->SetRenderState(D3DRS_FILLMODE, D3DFILL_WIREFRAME);
return true;
}
Display 方法有两项任务:更新场景和绘制场景。想让立方体旋转起来,必须在程序生成的每帧图像中给旋转角一定的增量,从而指定立方体的旋转方式。通过更新每帧图像中立方体的角度,立方体在每帧图像中被微微地旋转,从而产生转动的视觉效果。
bool Direct3DFrame::Display(float timeDelta)
{
if (m_device) {
//
// spin the cube:
//
D3DXMATRIX rx, ry;
// rotate 45 degrees on x-axis
D3DXMatrixRotationX(&rx, 3.14f / 4.0f);
// incremement y-rotation angle each frame
D3DXMatrixRotationY(&ry, m_yRotAngle);
m_yRotAngle += timeDelta;
// reset angle to zero when angle reaches 2*PI
if( m_yRotAngle >= 6.28f ) {
m_yRotAngle = 0.0f;
}
// combine x- and y-axis rotation transformations.
D3DXMATRIX p = rx * ry;
m_device->SetTransform(D3DTS_WORLD, &p);
//
// draw the scene:
//
m_device->Clear(0, 0, D3DCLEAR_TARGET | D3DCLEAR_ZBUFFER, 0xffffffff, 1.0f, 0);
m_device->BeginScene();
m_device->SetStreamSource(0, m_vb, 0, sizeof(Vertex));
m_device->SetIndices(m_ib);
m_device->SetFVF(Vertex::FVF);
// Draw cube.
m_device->DrawIndexedPrimitive(D3DPT_TRIANGLELIST, 0, 0, 8, 0, 12);
m_device->EndScene();
m_device->Present(0, 0, 0, 0);
}
return true;
}
m_yRotAngle 表示旋转角度,在 Direct3DFrame 中添加定义,在构造函数中初始化为 0.0f 。
在 Cleanup 中添加内存清理:
m_vb->Release();
m_ib->Release();
m_device->Release();
运行效果:
/*******************************************************/
** 本文由 独酌逸醉 原创,转载请注明博客链接,谢谢!
** 小弟初学 Direct3D,文章中如果存在错误,烦请指证,不胜感激!
** 参考书籍:《DirectX 9.0 3D 游戏开发编程基础》
** 时间:2011.11.20
/*******************************************************/
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· Linux系列:如何用 C#调用 C方法造成内存泄露
· AI与.NET技术实操系列(二):开始使用ML.NET
· 记一次.NET内存居高不下排查解决与启示
· 探究高空视频全景AR技术的实现原理
· 理解Rust引用及其生命周期标识(上)
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 单线程的Redis速度为什么快?
· 展开说说关于C#中ORM框架的用法!
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?