首页 > 代码库 > DirectX学习笔记(四):利用D3DX网格数据结构绘制可旋转茶壶

DirectX学习笔记(四):利用D3DX网格数据结构绘制可旋转茶壶

前言:

在上篇文章(DirectX学习笔记三)中,我详细的说明了如何利用线框模式绘制可旋转的正方体。链接:点击打开链接。但是应该看到的是,如果我们通过创建三角形单元来创建3D物体是十分繁琐的事情。幸运的是,在D3DX库中提供了一些用于生成简单3D物体的网格数据方法。

如:利用网格数据创建一个茶壶:

1. 我们需要使用ID3DXMesh网格数据结构接口来创建我们的茶壶网格数据,这时我们需要使用此函数:


HRESULT D3DXCreateTeapot(LPDIRECT3DDEVICE9 pDevice, LPD3DXMESH* ppMesh, LPD3DXBUFFER* ppAdjacency)

下面是一个使用此函数的例子:

ID3DXMesh *mesh = 0;
D3DXCreateTeapot(_device, &mesh, 0);

那么我们为了绘制一个可旋转的茶壶都需要做哪些工作呢:


1.生成网格数据:

D3DXCreateTeapot(Device, &Teapot, 0);

2.取景变换:


在当摄像机的位置和朝向任意时,进行一些操作会造成效率低下很是麻烦。为了简化运算,我们将摄像机变换到世界坐标系的原点,并旋转摄像机使其光轴与世界坐标系的z轴朝向一致。要特别注意的是,世界中的所有物体都会随之摄像机变换,以保证摄像机的视场恒定。
我们先使用D3DXMatrixLookAtLH(...)函数来获取一个取景变换矩阵,然后利用::SetTransform(..)方法设定:

//取景变换
	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);

	Device->SetTransform(D3DTS_VIEW, &V);

3.投影:


Cube 作为一个3D物体,如何在3D平面中显示?这需要用到投影(投影:从n维变换到n-1 维的过程).
我们利用::D3DXMatrixPerspectiveFovLH(...)来创建一个投影矩阵,然后利用::SetTransform(...)方法来实现该投影变换矩阵的应用:

//投影变换
	D3DXMATRIX proj;
	D3DXMatrixPerspectiveFovLH(&proj,D3DX_PI * 0.5f, (float)Width / (float)Height,1.0f,1000.0f);
	Device->SetTransform(D3DTS_PROJECTION, &proj);

4.绘制状态:

Direct3D中封装了很多绘制状态,这些绘制状态将影响几何体的绘制方式。我们使用::SetRenderState(...)函数来设置绘制状态。
因为我们要利用线框模式进行绘制,于是我们这样设置:

Device->SetRenderState(D3DRS_FILLMODE, D3DFILL_WIREFRAME);


5.绘制:

利用DrawSubset(...)函数:

Teapot->DrawSubset(0);

6.旋转:


我们利用:D3DXMatrixRotationX(...)和D3DXMatrixRotationY(...)函数来进行旋转功能实现。
D3DXMatrixRotationY 函数   (D3DXMatrixRotationX类似)
创建绕着Y轴旋转矩阵。
定义:
D3DXMATRIX *WINAPI D3DXMatrixRotationY( D3DXMATRIX *pOut,FLOAT Angle);
参数:
pOut  指向D3DXMATRIX 结构的操作结果矩阵。 
Angle 绕着Y轴旋转的角度(单位是弧度)。角度计算方法是当朝着旋转轴的原点看去时,顺时针方向为正值。 
返回值:
指向D3DXMATRIX 结构的绕着Y轴旋转矩阵。

	D3DXMATRIX  Ry;
	static float y = 0.0f;
	D3DXMatrixRotationY(&Ry, y);
	y += 0.0001f;
	if (y >= 6.28f)
		y = 0.0f;
	Device->SetTransform(D3DTS_WORLD, &Ry);



完整代码(可运行):

#include<d3d9.h>
#include<d3dx9math.h>
#include<windows.h>

IDirect3DDevice9* Device = 0; // 一个C++对象,代表了我们用来显示3D图形的物理硬件设备

const int Width = 640; //窗口的宽度
const int Height = 480; //高度
ID3DXMesh* Teapot = 0;


//------------------------------以下为窗口过程---------------------------------
LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)//窗口过程
{
	switch (msg)
	{
	case WM_DESTROY://销毁
		PostQuitMessage(0); //终止请求
		break;
	}
	//调用缺省的窗口过程来为应用程序没有处理的任何窗口消息提供缺省的处理。  
	//该函数确保每一个消息得到处理  
	return ::DefWindowProc(hwnd, msg, wParam, lParam);
}


//----------------------------以下为初始化窗口信息---------------------------------
bool InitWindow(HINSTANCE hInstance,HWND &hwnd, int width, int height)
{
	//定义窗口样式
	WNDCLASS wc;
	wc.style = CS_HREDRAW | CS_VREDRAW;
	wc.lpfnWndProc = WndProc;
	wc.cbClsExtra = 0;
	wc.cbWndExtra = 0;
	wc.hInstance = hInstance;
	wc.hIcon = LoadIcon(0, IDI_APPLICATION);
	wc.hCursor = LoadCursor(0, IDC_ARROW);
	wc.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);
	wc.lpszMenuName = 0;
	wc.lpszClassName = "LszDX";

	//窗口注册
	RegisterClass(&wc);

	//创建窗口 
	hwnd = ::CreateWindow("LszDX", "LszDX", WS_OVERLAPPEDWINDOW, 0, 0, width, height, 0, 0, hInstance, 0);

	//绘制更新窗口 
	ShowWindow(hwnd, SW_SHOW);
	UpdateWindow(hwnd);
	return true;
}


//----------------------------以下为初始化Direct3D----------------------------------------
//注意函数的hwnd为传引用
bool InitD3D(HINSTANCE hInstance,HWND &hwnd,int width, int height,bool windowed,D3DDEVTYPE deviceType,IDirect3DDevice9** device)
{
	//获取IDirect3D9的指针
	IDirect3D9* d3d9 = 0;
	d3d9 = Direct3DCreate9(D3D_SDK_VERSION);

	//检验硬件顶点运算
	D3DCAPS9 caps;
	d3d9->GetDeviceCaps(D3DADAPTER_DEFAULT, deviceType, &caps);

	int vp = 0;
	if (caps.DevCaps & D3DDEVCAPS_HWTRANSFORMANDLIGHT)
		vp = D3DCREATE_HARDWARE_VERTEXPROCESSING;
	else
		vp = D3DCREATE_SOFTWARE_VERTEXPROCESSING;

	//填充D3DPRESENT_PARAMETERS 结构
	D3DPRESENT_PARAMETERS d3dpp;
	d3dpp.BackBufferWidth = width;
	d3dpp.BackBufferHeight = height;
	d3dpp.BackBufferFormat = D3DFMT_A8R8G8B8;
	d3dpp.BackBufferCount = 1;
	d3dpp.MultiSampleType = D3DMULTISAMPLE_NONE;
	d3dpp.MultiSampleQuality = 0;
	d3dpp.SwapEffect = D3DSWAPEFFECT_DISCARD;
	d3dpp.hDeviceWindow = hwnd;
	d3dpp.Windowed = windowed;
	d3dpp.EnableAutoDepthStencil = true;
	d3dpp.AutoDepthStencilFormat = D3DFMT_D24S8;
	d3dpp.Flags = 0;
	d3dpp.FullScreen_RefreshRateInHz = D3DPRESENT_RATE_DEFAULT;
	d3dpp.PresentationInterval = D3DPRESENT_INTERVAL_IMMEDIATE;

	//创建IDirect3DDevice9接口
	d3d9->CreateDevice(	D3DADAPTER_DEFAULT,deviceType,hwnd,vp,&d3dpp,device);        
	d3d9->Release();
	return true;
}

//-----------------------此函用来处理为了显示Teapot所要进行的工作-------------------------
//生成网格数据
//取景变换
//投影变换
//设置绘制状态
bool InitTea()
{
	//生成网格数据
	D3DXCreateTeapot(Device, &Teapot, 0);
	//取景变换
	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);

	Device->SetTransform(D3DTS_VIEW, &V);

	//投影变换
	D3DXMATRIX proj;
	D3DXMatrixPerspectiveFovLH(&proj,D3DX_PI * 0.5f, (float)Width / (float)Height,1.0f,1000.0f);
	Device->SetTransform(D3DTS_PROJECTION, &proj);

	//设置绘制状态
	Device->SetRenderState(D3DRS_FILLMODE, D3DFILL_WIREFRAME);
	return true;
}

//-----------------------以下为对Cube进行绘制-------------------------------------
bool Display()
{
	MSG msg;
	ZeroMemory(&msg, sizeof(MSG));//用0来填充消息可类比为:memset()函数  
	while (msg.message != WM_QUIT) //退出 
	{
		//PeekMessage函数是以查看的方式从系统中获取消息  
		//并将该消息(如果存在)放于指定的结构  
		if (PeekMessage(&msg, 0, 0, 0, PM_REMOVE))
		{
			//PM_REMOVE:PeekMessage处理后,消息从队列里除掉。

			    //TranslateMessage函数将虚拟键消息转换为字符消息。
				//字符消息被寄送到调用线程的消息队列里,  
				//当下一次线程调用函数GetMessage或PeekMessage时被读出。  
				//TranslateMessage只能用于转换调用GetMessage或PeekMessage接收的消息。  

			TranslateMessage(&msg);

			//DispatchMessage函数
			//该函数分发一个消息给窗口程序。  
			//通常消息从GetMessage函数获得。  
			//消息被分发到回调函数(过程函数),作用是消息传递给操作系统,  
			//然后操作系统去调用我们的回调函数,也就是说我们在窗体的过程函数中处理消息  
			DispatchMessage(&msg);
		}
		else
		{
			if (Device)
			{
				//旋转
				D3DXMATRIX  Ry;
				static float y = 0.0f;
				D3DXMatrixRotationY(&Ry, y);
				y += 0.0001f;
				if (y >= 6.28f)
					y = 0.0f;

				Device->SetTransform(D3DTS_WORLD, &Ry);

				//绘制
				Device->Clear(0, 0, D3DCLEAR_TARGET | D3DCLEAR_ZBUFFER, 0xffffffff, 1.0f, 0);
				Device->BeginScene();
				Teapot->DrawSubset(0);

				Device->EndScene();
				Device->Present(0, 0, 0, 0);
			}
		}
	}
	return true;
}


//-------------------------Main函数-----------------------------------------------
int WINAPI WinMain(HINSTANCE hinstance,HINSTANCE prevInstance,PSTR cmdLine,int showCmd)
{
	HWND hwnd =0;

	//1.窗口创建
	InitWindow(hinstance,hwnd, Width, Height);

	//2.初始化Direct3D
	InitD3D(hinstance, hwnd, Width, Height, true, D3DDEVTYPE_HAL, &Device);

	//3.初始化cube信息
	InitTea();

	//4.进行显示绘制
	Display();

	//5.释放指针
	Device->Release();
	return 0;
}

可以看到的是:如果我们利用D3DX自带的网格数据绘制某些3D物体省去了顶点缓存和索引缓存的操作,以及数据的读写。

运行效果:
技术分享

DirectX学习笔记(四):利用D3DX网格数据结构绘制可旋转茶壶