渲染管道 它是用来创建为3D世界进行几何描述的2D图形并设定一个虚拟摄相机确定这个世界中哪一部分将被透视投影到屏幕上
D3D中最基础的东西——顶点渲染,这个东西就和2D游戏中的画点是一个道理,重要性就不多说了,一个字,“太重要了!”。 渲染的基本步骤是这样的: (1) 定义顶点格式和FVF (2) 创建顶点缓冲区并填充之 (3) 用D3D进行渲染 为了创建一个自定义的顶点结构,我们首先要创建一个包含能存放我们选择的顶点数据的结构。例如,下面我们定放了两种顶点数据类型,一种包含了位置和颜色信息,第二种则包含了位置,法线向量,纹理坐标信息 struct ColorVertex { float _x, _y, _z; // position DWORD _color; }; struct NormalTexVertex { float _x, _y, _z; // position float _nx, _ny, _nz; // normal vector float _u, _v; // texture coordinates }; |
一旦我们有了完整的顶点格式,我们就要使用灵活顶点格式(FVF)的组合标志来描述它。例如第一个顶点结构,我们要使用如下的顶点格式: #define FVF_COLOR (D3DFVF_XYZ | D3DFVF_DIFFUSE) |
上面的顶点结构表明它包含位置和颜色属性。 而第二种结构则要使用: #define FVF_NORMAL_TEX (D3DFVF_XYZ | D3DFVF_NORMAL | D3DFVF_TEX1) |
上面的顶点结构表明它包含了位置,法线向量,纹理坐标的属性。 有一点要注意,你的标志的顺序必须要和你的顶点结构的顺序一一对应。如果想知道所有的D3DFVF标志
上次写了个初始化设备的程序,现在就利用初始化的设备来进行画图了。
废话 不多说了 还是直接看程序了,这个程序是我刚才完成的。搞了一天才搞定的,先把程序贴出来再说吧。 下次来看在场景中设置摄像机,灯光、纹理、模型等等。记住路漫漫其修远兮,这才是长征的第一步:)。
下面是程序运行后效果
下面那个程序包含的头文件在上篇文章里有
////////////////////////////////////////////////////////////////////////////////////////////////// // // // (1) 定义点格式和FVF // 定点渲染的基本三曲 // (2) 创建点缓冲区并填之 // // (3) 用D3D进行渲染 // // // /////////////////////////////////////////////////////////////////////////////////////////////////
#include "d3dUtility.h"
// // Globals //
IDirect3DDevice9* Device = 0;
const int Width = 640; const int Height = 480;
//想要渲染点,首你得有个缓冲区,D3D中这样定义 //点缓冲区 IDirect3DVertexBuffer9* Triangle = 0; // vertex buffer to store // our triangle data.
// // Classes and Structures // //然后还得为顶点缓冲区里的数据定义格式,这样: //自定义顶点结构 struct Vertex { Vertex(){} Vertex(float x, float y, float z,DWORD color) { _x = x; _y = y; _z = z; _color=color; } float _x, _y, _z;//顶点位置 DWORD _color; //顶点颜色 static const DWORD FVF;//顶点格式 };
//FVF在D3D中的解释是Flexible vertex format,意思就是可变的顶点格式,具体内容参看DX9 const DWORD Vertex::FVF = D3DFVF_XYZ|D3DFVF_DIFFUSE;
// // Framework Functions //
//Setup的主要功能是创建顶点缓冲区并且填充该缓冲区 bool Setup() { // // Create the vertex buffer. //创建顶点缓冲区(顶点缓存) Device->CreateVertexBuffer( 3 * sizeof(Vertex), //Length —— 分配给缓存的字节大小。假如想得到一个能存储8个顶点的顶点缓存, //那么我们就要在顶点结构中设置这个参数为 8 * sizeof ( Vertex ) D3DUSAGE_WRITEONLY, // 第二个参数暂时不管,填0 Vertex::FVF, // FVF —— 存储在缓存中的顶点格式 D3DPOOL_DEFAULT, // Pool —— 缓存放置在哪一个内存池中 &Triangle, // ——返回创建好的顶点缓存的指针。 0); //没有使用;设置为0。 //////////////////////////////////////////////////////////////////////////////////////////// // Fill the buffers with the triangle data. // 访问缓冲内存 //为了访问一个顶点/索引缓存,我们需要得到一个指针。 //我们通过一个指针获得缓存数据必须使用Lock方法。当我们访问完缓存后必须对它解锁。 //一旦有一个指向内存的指针,我们就能对它进行读写。 // LOCK方式 // HRESULT IDirect3DVertexBuffer9::Lock(UINT OffsetToLock,UINT SizeToLock,BYTE** ppbData, // DWORD Flags);ppbData —— 一个指向锁定内存开始位置的指针 ///////////////////////////////////////////////////////////////////////////////////////////////////// // OffsetToLock —— 偏移量,以字节为单位,从缓存开始位置到锁定开始位置的距离。 //SizeToLock —— 锁定的字节数。 //ppbData —— 一个指向锁定内存开始位置的指针。 //Flags —— 标记描述怎样锁定内存。它可能是0或者是下面参数中的1个或多个的组合: //D3DLOCK_DISCARD——这个参数仅仅会在动态缓存时被使用。它指示硬件丢弃缓存并返回一个指向新分配的缓存的指针。 // 这是很有用的因为当我们存取一个新分配的缓存时它允许硬件继续从丢弃的缓存渲染。这防止了硬件延迟。 //D3DLOCK_NOOVERWRITE——这个参数仅仅会在动态缓存时被使用。它声明你将向缓存中添加数据。即,你不能向已经渲染的内存中写数据。 // 这是有好处的因为他允许你在添加新数据到缓存的同时让硬件继续渲染。 //D3DLOCK_READONLY——这个参数声明你锁定的缓存只能从中读取数据而不能写数据。这允许一些内在的优化。 //用参数D3DLOCK_DISCARD和D3DLOCK_NOOVERWRITE的地址实际上就是缓存的一部分被使用(正在渲染)时它被锁定。 // 假如情况允许这些标记被使用,当在锁定时他们防止渲染停止。 ///////////////////////////////////////////////////////////////////////////////////////////////// Vertex* vertices; Triangle->Lock(0, 0,(void**)&vertices, 0); vertices[0] = Vertex(-1.0f, 0.0f, 2.0f,0xffff0000); vertices[1] = Vertex( 0.0f, 1.0f, 2.0f,0xff0000ff); vertices[2] = Vertex( 1.0f, 0.0f, 2.0f,0xffffffff); Triangle->Unlock(); // // Set the projection matrix. // D3DXMATRIX proj; D3DXMatrixPerspectiveFovLH( &proj, // result D3DX_PI * 0.5f, // 90 - degrees (float)Width / (float)Height, // aspect ratio 1.0f, // near plane 1000.0f); // far plane Device->SetTransform(D3DTS_PROJECTION, &proj); // // Set wireframe mode render state. // // 关闭culling,让我们能看到3角型的正反面 Device->SetRenderState( D3DRS_CULLMODE, D3DCULL_NONE ); // 关闭灯光,因为我们的顶点有自己的颜色 Device->SetRenderState( D3DRS_LIGHTING, FALSE ); //Device->SetRenderState(D3DRS_FILLMODE, D3DFILL_WIREFRAME); return TRUE; } void Cleanup() { d3d::Release<IDirect3DVertexBuffer9*>(Triangle); }
bool Display(float timeDelta) { if( Device ) { // 清除背景为0xffff0000色 Device->Clear(0, 0, D3DCLEAR_TARGET | D3DCLEAR_ZBUFFER, 0xffff0000, 1.0f, 0); // 开始绘制场景 Device->BeginScene(); // --渲染图形-- // 指定渲染源 Device->SetStreamSource(0, Triangle, 0, sizeof(Vertex)); // 指定自定义的FVF Device->SetFVF(Vertex::FVF); // Draw one triangle. 渲染 Device->DrawPrimitive(D3DPT_TRIANGLELIST, 0, 1); // 结束场景绘制 Device->EndScene(); // 显示到屏幕上 Device->Present(0, 0, 0, 0); } return true; }
// // WndProc //消息处理 LRESULT CALLBACK d3d::WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) { switch( msg ) { case WM_DESTROY: ::PostQuitMessage(0); break; case WM_KEYDOWN: if( wParam == VK_ESCAPE ) ::DestroyWindow(hwnd); break; } return ::DefWindowProc(hwnd, msg, wParam, lParam); }
// // WinMain // int WINAPI WinMain(HINSTANCE hinstance, HINSTANCE prevInstance, PSTR cmdLine, int showCmd) { if(!d3d::InitD3D(hinstance, Width, Height, true, D3DDEVTYPE_HAL, &Device)) { ::MessageBox(0, "InitD3D() - FAILED", 0, 0); return 0; } if(!Setup()) { ::MessageBox(0, "Setup() - FAILED", 0, 0); return 0; } d3d::EnterMsgLoop( Display ); Cleanup(); Device->Release(); return 0; }
|