Direct3D使用顶点缓存和索引缓存进行绘制

1. 使用顶点缓存绘制静止图形

效果图
enter image description here

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通道直接访问,因此访问速度高于内存,如果程序需要频繁更新缓存内容,则应该使用这类缓存

显卡缓存层次结构

enter image description here

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. 使用索引缓存绘制动态图像

效果图

enter image description here

enter image description here

要说明的是,这儿直接使用顶点缓存也是可以的,但是正如图中所示,一个立方体有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.

posted @ 2014-09-28 02:24  current  阅读(2052)  评论(0编辑  收藏  举报