DirectX9:基础篇 第二章 建立3D场景的几何描述
一.简介
d3dx9mesh.h有关网格模型
二.灵活的顶点格式
Direct3D中的顶点不只有空间位置属性,还可以有颜色 法线向量属性等,所以可以自定义一个顶点结构
1.使用
struct ColorVertex{
float _x,_y,_z; //位置
DWORD _color; //颜色
};
#define FVF_COLOR(D3DFVF_XYZ | D3DFVF_DIFFUSE) //包含位置属性和漫反射颜色属
struct NormaolTexVertex{
float _x,_y,_z; //位置
float _nx,_ny,_nz; //法线
float _u,_v; //纹理坐标
};
#define FVF_NORMAL_TEX(D3DFVF_XYZ | D3DFVF_NORMAL | D3DFVF_TEX1)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 | //灵活的顶点格式 struct Vertex{ Vertex(){} Vertex( float x, float y, float z){ _x=x; _y=y; _z=z; } float _x,_y,_z; static const DWORD FVF; }; const DWORD Vertex::FVF=D3DFVF_XYZ; bool Setup(){ //根据灵活的顶点格式来创建顶点缓存 Device->CreateVertexBuffer( 3* sizeof (Vertex), D3DUSAGE_WRITEONLY, Vertex::FVF, D3DPOOL_MANAGED, &Triangle, 0); } bool Display( float timeDelta){ //根据灵活的顶点格式来绑定模型数据 Device->SetFVF(Vertex::FVF); } |
2.FVF宏定义
#define D3DFVF_RESERVED0 0x001
#define D3DFVF_POSITION_MASK 0x400E
#define D3DFVF_XYZ 0x002 //包含三维坐标
#define D3DFVF_XYZRHW 0x004
#define D3DFVF_XYZB1 0x006
#define D3DFVF_XYZB2 0x008
#define D3DFVF_XYZB3 0x00a
#define D3DFVF_XYZB4 0x00c
#define D3DFVF_XYZB5 0x00e
#define D3DFVF_XYZW 0x4002
#define D3DFVF_NORMAL 0x010
#define D3DFVF_PSIZE 0x020
#define D3DFVF_DIFFUSE 0x040
#define D3DFVF_SPECULAR 0x080
#define D3DFVF_TEXCOUNT_MASK 0xf00
#define D3DFVF_TEXCOUNT_SHIFT 8
#define D3DFVF_TEX0 0x000
#define D3DFVF_TEX1 0x100
#define D3DFVF_TEX2 0x200
#define D3DFVF_TEX3 0x300
#define D3DFVF_TEX4 0x400
#define D3DFVF_TEX5 0x500
#define D3DFVF_TEX6 0x600
#define D3DFVF_TEX7 0x700
#define D3DFVF_TEX8 0x800
#define D3DFVF_LASTBETA_UBYTE4 0x1000
#define D3DFVF_LASTBETA_D3DCOLOR 0x8000
#define D3DFVF_RESERVED2 0x6000
三.顶点缓存
一个顶点缓存是一个包含顶点数据的连续内存空间,存放在显存之中,比内存快(用于存储网格顶点)
1.CreateVertexBuffer
注意:静态缓存处理快,但是更新重绘慢.动态缓存处理慢,但是更新重绘快
HRESULT IDirect3DDevice9::CreatVertexBuffer( //创建顶点缓存
UINT Length,DWORD Usage,
DWORD FVF,
D3DPOOL Pool,
IDirect3DVertexBuffer9** ppVertexBuffer,
HANDLE* pSharedHandle
);
Length:为缓存分配的字节数,8个顶点需要8*sizeof(Vertex)
Usage:附加属性
(1)D3DUSAGE_DYNAMIC 将缓存设为动态缓存
(2)D3DUSAGE_POINTS 规定缓存将用于存储点图元
(3)D3DUSAGE_SOFTWAREPROCESSING 指定软件顶点运算方法
(4)D3DUSAGE_WRITEONLY 只写
FVF:存储在顶点缓冲的灵活顶点格式,参考宏定义
Pool:容纳缓存的内存池
ppVertexBuffer:用于接收所创建的顶点缓存的指针
pSharedHandle:不使用,设为0
1 2 3 4 5 6 7 8 9 10 | //例子 IDirect3DVertexBuffer9* vb; device->CreateVertexBuffer( 8* sizeof (Vertex), 0, D3DFVF_XYZ, D3DPOOL_MANAGED, &vb, 0); |
2.Lock/Unlock
HRESULT IDirect3DVertexBuffer9::Lock(
UINT offsetToLock,
UINT SizeToLock,
BYTE** ppbData,
DWORD Flags
);
OffsetToLock:自缓存的起始点到开始锁定的位置的偏移量,单位为字节
SizeToLock:所要锁定的字节数.
如果OffsetToLock和SizeToLock都为0表示锁定整个缓存
1 2 3 4 5 6 7 8 9 | //顶点缓存 IDirect3DVertexBuffer9* Triangle=0; //访问顶点缓存内存 Vertex* vertices; Triangle->Lock(0,0,( void **)&vertices,0); ... Triangle->Unlock(); |
3.示例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 | //顶点缓存 IDirect3DVertexBuffer9* Triangle=0; void Cleaup(){ d3d::Release<IDirect3DVertexBuffer9*>(VB); } bool Setup{ //用D3D设备类来创建顶点缓存内存 Device->CreateVertexBuffer( 3* sizeof (Vertex), D3DUSAGE_WRITEONLY, Vertex::FVF, D3DPOOL_MANAGED, &Triangle, 0); //访问顶点缓存内存 Vertex* vertices; Triangle->Lock(0,0,( void **)&vertices,0); vertices[0]=Vertex(-1.0f,0.0f,2.0f); vertices[1]=Vertex(0.0f,1.0f,2.0f); vertices[2]=Vertex(1.0f,0.0f,2.0f); Triangle->Unlock(); } bool Display( float timeDelta){ Device->SetStreamSource(0,VB,0, sizeof (Vertex)); } |
四.索引缓存
一个索引缓存是一个包含索引数据的连续内存空间,存放于显存之中,比内存快
构建一个3D物体的所有三角形单元中会共享许多公共顶点,为了解决重合顶点问题,用顶点列表(vertex list)和索引列表(index list)来记录坐标(决定顶点应以何种组合方式构成网格的三角形单元)
假设不用索引来表示一个正方形,Vectex v[6]={v0,v1,v2,v1,v2,v3},需要六个顶点坐标
加入索引之后,Vertect v[4]={v0,v1,v2,v3} WORD indexList[6]={0,1,2,1,2,3}; 只需要记录四个顶点坐标
因为索引坐标只是一个整数,用来获取顶点坐标的第几个,所以比三维坐标的Vertex更省内存
1.创建索引缓存
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | HRESULT IDirect3DDevice9::CreateIndexBuffer( UINT Length, DWORD Usage, D3DFORMAT Format, D3DPOOL Pool, IDirect3DIndexBuffer9** ppIndexBuffer, HANDLE * pSharedHandle); Length:为缓存分配的字节数,8个顶点需要8* sizeof (Vertex) Usage:附加属性 (1)D3DUSAGE_DYNAMIC 将缓存设为动态缓存 (2)D3DUSAGE_POINTS 规定缓存将用于存储点图元 (3)D3DUSAGE_SOFTWAREPROCESSING 指定软件顶点运算方法 (4)D3DUSAGE_WRITEONLY 只写 Format:指定索引的大小 (1)D3DFMT_INDEX16 表示16位索引 (2)D3DFMT_INDEX32 表示32位索引 Pool:容纳缓存的内存池 ppIndexBuffer:用于接收所创建的索引缓存的指针 pSharedHandle:不使用,设为0 |
1 2 3 4 5 6 7 8 | IDirect3DIndexBuffer9* ib; device->CreateIndexBuffer( 36* sizeof ( WORD ), D3DUSAGE_DYNAMIC | D3DUSAGE_WRITEONLY, D3DFMT_INDEX16, D3DPOOL_MANAGED, &ib, 0); |
2.访问索引缓存
1 2 3 4 5 6 | HRESULT IDirect3DIndexBuffer9::Lock( UINT OffsetToLock, UINT SizeToLock, BYTE ** ppbData, DWORD Flags ); |
3.示例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 | //索引缓存 IDirect3DIndexBuffer9* IB=0; bool Setup(){ //用D3D设备类来创建索引缓存 Device->CreateIndexBuffer( 36* sizeof ( WORD ), D3DUSAGE_WRITEONLY, D3DFMT_INDEX16, D3DPOOL_MANAGED, &IB, 0); //访问索引缓存 WORD * Iindices=0; IB->LOCK(0,0,( void **)&indices,0); 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; IB->Unlock(); } void Cleanup(){ d3d::Release<IDirect3DIndexBuffer9*>(IB); } bool Display( float timeDelta){ //绑定模型的索引缓存 Device->SetIndices(IB); } |
五.网格
1.网格简介
一个网格由一个或多个子集组成,一个子集(subsets)是网格中一组可用相同属性(材质/纹理/绘制状态)进行绘制的三角形单元
假设一个房子是网格,那么地板/墙/天花板/窗口各有自己的属性,都能用来绘制房子网格
为了区分不同的子集,给每个子集指定一个唯一的非负整数值,该值为DWORD类型所能容纳任何非负整数
网格中的每个三角形单元都被赋予了一个属性ID,该ID指定了该三角形单元所属的子集,这些三角形单元的属性ID被存储在网格的属性缓存(Attribute Buffer)中,该属性缓存实际上就是一个DWORD类型的数组.
由于每个面片(三角形)在属性缓存中都有对应项,所以属性缓存中元素的个数与网格中面皮的个数相等.
2.网格类(ID3DXMesh)的方法
HRESULT ID3DMesh::GetVertexBuffer(LPDIRECT3DVERTEXBUFFER9* ppVB)
HRESULT ID3DMesh::GetIndexBuffer(LPDIRECT3DVERTEXBUFFER9* ppIB)
DWORD GetFVF()
DWORD GetNumVertices()
DWORD GetNumBytesPerVertex()
DWORD GetNumFaces()LockAttributeBuffer()
LockIndexBuffer()
LockVertexBuffer()
UnlockAttributeBuffer()
UnlockIndexBuffer()
UnlockVertexBuffer()
CloneMesh()
CloneMeshFVF()
1 2 3 4 5 6 7 8 9 10 | DrawSubset( DWORD AttribID) //用于绘制由参数AttribId指定的子集中的三角形单元 //例子 Mesh->DrawSubset(0); //绘制子集0中的所有三角形单元 for ( int i=0;i<numSubsets;i++){ Device->SetMaterial(mtrls[i]); Device->SetTexture(0,textures[i]); Mesh->DrawSubset(i); } |
3.D3D支持网格(ID3DXMESH)的方法
HRESULT WINAPI D3DXCreateMeshFVF(
DWORD NumFaces, //网格所具有的面皮个数
DWORD NumVertices, //网格所具有的顶点个数
DWORD Options, //创建网格时所使用的创建标记
DWORD FVF, // 顶点的灵活格式
LPDIRECT3DDEVICE9 pDevice, // 与网格相关的设备指针
LPD3DXMESH* ppMesh // 所创建的网格对象的指针
);
D3DXCreateMesh() //创建空网格
//绘制内置的网格模型
D3DXCreateTeapot() //茶壶
D3DXCreateBox() //正方体
D3DXCreateCylinder()
D3DXCreateTorus()
D3DXCreateSphere()
D3DXCreaetPolygon()
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 | //多个模型一起绘制 ID3DXMesh* Objects[5] = {0, 0, 0, 0, 0}; // World matrices for each object. These matrices // specify the locations of the objects in the world. D3DXMATRIX ObjWorldMatrices[5]; // // Framework Functions // bool Setup() { // //茶壶模型 // D3DXCreateTeapot( Device, &Objects[0], 0); // //立方体 // D3DXCreateBox( Device, 2.0f, // width 2.0f, // height 2.0f, // depth &Objects[1], 0); // //柱体 // D3DXCreateCylinder( Device, 1.0f, // radius at negative z end 1.0f, // radius at positive z end 3.0f, // length of cylinder 10, // slices 10, // stacks &Objects[2], 0); // //圆环体 // D3DXCreateTorus( Device, 1.0f, // inner radius 3.0f, // outer radius 10, // sides 10, // rings &Objects[3], 0); // //球面体 // D3DXCreateSphere( Device, 1.0f, // radius 10, // slices 10, // stacks &Objects[4], 0); // //多边形 //D3DXCreatePolygon() D3DXMatrixTranslation(&ObjWorldMatrices[0], 0.0f, 0.0f, 0.0f); D3DXMatrixTranslation(&ObjWorldMatrices[1], -5.0f, 0.0f, 5.0f); D3DXMatrixTranslation(&ObjWorldMatrices[2], 5.0f, 0.0f, 5.0f); D3DXMatrixTranslation(&ObjWorldMatrices[3], -5.0f, 0.0f, -5.0f); D3DXMatrixTranslation(&ObjWorldMatrices[4], 5.0f, 0.0f, -5.0f); // // Set the projection matrix. // D3DXMATRIX proj; D3DXMatrixPerspectiveFovLH( &proj, D3DX_PI * 0.5f, // 90 - degree ( float )Width / ( float )Height, 1.0f, 1000.0f); Device->SetTransform(D3DTS_PROJECTION, &proj); // // Switch to wireframe mode. // Device->SetRenderState(D3DRS_FILLMODE, D3DFILL_WIREFRAME); return true ; } void Cleanup() { for ( int i = 0; i < 5; i++) d3d::Release<ID3DXMesh*>(Objects[i]); } bool Display( float timeDelta) { if ( Device ) { // Animate the camera: // The camera will circle around the center of the scene. We use the // sin and cos functions to generate points on the circle, then scale them // by 10 to further the radius. In addition the camera will move up and down // as it circles about the scene. static float angle = (3.0f * D3DX_PI) / 2.0f; static float cameraHeight = 0.0f; static float cameraHeightDirection = 5.0f; D3DXVECTOR3 position( cosf(angle) * 10.0f, cameraHeight, sinf(angle) * 10.0f ); // the camera is targetted at the origin of the world D3DXVECTOR3 target(0.0f, 0.0f, 0.0f); // the worlds up vector D3DXVECTOR3 up(0.0f, 1.0f, 0.0f); D3DXMATRIX V; D3DXMatrixLookAtLH(&V, &position, &target, &up); Device->SetTransform(D3DTS_VIEW, &V); // compute the position for the next frame angle += timeDelta; if ( angle >= 6.28f ) angle = 0.0f; // compute the height of the camera for the next frame cameraHeight += cameraHeightDirection * timeDelta; if ( cameraHeight >= 10.0f ) cameraHeightDirection = -5.0f; if ( cameraHeight <= -10.0f ) cameraHeightDirection = 5.0f; // // Draw the Scene: // Device->Clear(0, 0, D3DCLEAR_TARGET | D3DCLEAR_ZBUFFER, 0xffffffff, 1.0f, 0); Device->BeginScene(); for ( int i = 0; i < 5; i++) { // Set the world matrix that positions the object. Device->SetTransform(D3DTS_WORLD, &ObjWorldMatrices[i]); // Draw the object using the previously set world matrix. Objects[i]->DrawSubset(0); } Device->EndScene(); Device->Present(0, 0, 0, 0); } return true ; } |
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 记一次.NET内存居高不下排查解决与启示
· 探究高空视频全景AR技术的实现原理
· 理解Rust引用及其生命周期标识(上)
· 浏览器原生「磁吸」效果!Anchor Positioning 锚点定位神器解析
· 没有源码,如何修改代码逻辑?
· 分享4款.NET开源、免费、实用的商城系统
· 全程不用写代码,我用AI程序员写了一个飞机大战
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了
· 上周热点回顾(2.24-3.2)