为了方便,DX为我们提供了几种自带的几何体,我们就不需要定义成吨的顶点来绘制一个图形了。
常用的有几种,立方体,茶壶,柱体,球面体。
HRESULT D3DXCreateTeapot( __in LPDIRECT3DDEVICE9 pDevice, __out LPD3DXMESH *ppMesh, __out LPD3DXBUFFER *ppAdjacency );
上面是其中最简单的创建一个茶壶的代码,其余立方体与这个的创建大同小异,仅仅是在第一个参数之后加上几个尺寸的参数。
例如,我们创建一个立方体:
//生成立方体 D3DXCreateBox( g_pDevice, //设备对象 1.0f, 1.0f, 1.0f, //X,Y,Z方向的长度 &g_pbox, //创建的对象的指针 NULL //存储绘制网格的三角形索引参数,不使用,置为NULL );
在绘制时也很简单,只需要一句代码即可绘制:
g_pbox->DrawSubset(0);
创建了一个立方体和一个茶壶,分别绕X轴旋转和Y轴旋转:
// D3DDemo.cpp : 定义应用程序的入口点。 // #include "stdafx.h" #include "D3DDemo.h" #define MAX_LOADSTRING 100 // 全局变量: HINSTANCE hInst; // 当前实例 TCHAR szTitle[MAX_LOADSTRING]; // 标题栏文本 TCHAR szWindowClass[MAX_LOADSTRING]; // 主窗口类名 // 此代码模块中包含的函数的前向声明: HWND g_hWnd; ATOM MyRegisterClass(HINSTANCE hInstance); BOOL InitInstance(HINSTANCE, int); LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM); //---------改造3D窗口需要的内容------------ LPDIRECT3D9 g_pD3D = NULL; //D3D接口指针 LPDIRECT3DDEVICE9 g_pDevice = NULL;//D3D设备指针 //立方体的接口 LPD3DXMESH g_pteapot = NULL; LPD3DXMESH g_pbox = NULL; void onCreatD3D() { g_pD3D = Direct3DCreate9(D3D_SDK_VERSION); if (!g_pD3D) return; //检测硬件设备能力的方法 /*D3DCAPS9 caps; ZeroMemory(&caps, sizeof(caps)); g_pD3D->GetDeviceCaps(D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, &caps);*/ //获得相关信息,屏幕大小,像素点属性 D3DDISPLAYMODE d3ddm; ZeroMemory(&d3ddm, sizeof(d3ddm)); g_pD3D->GetAdapterDisplayMode(D3DADAPTER_DEFAULT, &d3ddm); //设置全屏模式 D3DPRESENT_PARAMETERS d3dpp; ZeroMemory(&d3dpp, sizeof(d3dpp)); /*d3dpp.Windowed = false; d3dpp.BackBufferWidth = d3ddm.Width; d3dpp.BackBufferHeight = d3ddm.Height;*/ d3dpp.Windowed = true; d3dpp.BackBufferFormat = d3ddm.Format; d3dpp.BackBufferCount = 1; d3dpp.SwapEffect = D3DSWAPEFFECT_DISCARD;//交换后原缓冲区数据丢弃 //是否开启自动深度模板缓冲 d3dpp.EnableAutoDepthStencil = true; //当前自动深度模板缓冲的格式 d3dpp.AutoDepthStencilFormat = D3DFMT_D16;//每个像素点有16位的存储空间,存储离摄像机的距离 g_pD3D->CreateDevice(D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, g_hWnd, D3DCREATE_SOFTWARE_VERTEXPROCESSING, &d3dpp, &g_pDevice); if (!g_pDevice) return; //设置渲染状态,设置启用深度值 g_pDevice->SetRenderState(D3DRS_ZENABLE, true); //设置渲染状态,关闭灯光 g_pDevice->SetRenderState(D3DRS_LIGHTING, false); //设置渲染状态,裁剪模式 g_pDevice->SetRenderState(D3DRS_CULLMODE, D3DCULL_NONE); //g_pDevice->SetRenderState(D3DRS_CULLMODE, D3DCULL_NONE) ; } void Transform() { //ViewTransform:取景变换 D3DXVECTOR3 vEyePt(0.0f, 0.0f, -10.0f); //摄像机世界坐标 D3DXVECTOR3 vLookatPt(0.0f, 0.0f, 0.0f); //观察点世界坐标 D3DXVECTOR3 vUpVec(0.0f, 1.0f, 0.0f); //摄像机的上向量,通常为(0.0f, 1.0f, 0.0f) D3DXMATRIXA16 matView; //View变换的矩阵 //根据上面的结果计算出矩阵,存入矩阵中 D3DXMatrixLookAtLH(&matView, &vEyePt, &vLookatPt, &vUpVec); //进行取景变换 g_pDevice->SetTransform(D3DTS_VIEW, &matView); //ProjectionTransform:投影变换 D3DXMATRIXA16 matProj; //投影变换矩阵 //生成投影变换矩阵,存入上面的矩阵中 D3DXMatrixPerspectiveFovLH( &matProj, //输出结果矩阵 D3DX_PI / 4, //视域角度,一般为PI/4 1.0f, //显示屏的长宽比 1.0f, //视截体中近截面距离摄像机的位置 100.0f //视截体中远截面距离摄像机的位置 ); //进行投影变换 g_pDevice->SetTransform(D3DTS_PROJECTION, &matProj); } void onInit() { //初始化D3D onCreatD3D(); //初始化对象 //生成茶壶 D3DXCreateTeapot( g_pDevice, //设备对象 &g_pteapot, //创建的对象的指针 NULL //通常为NULL ); //生成立方体 D3DXCreateBox( g_pDevice, //设备对象 1.0f, 1.0f, 1.0f, //X,Y,Z方向的长度 &g_pbox, //创建的对象的指针 NULL //存储绘制网格的三角形索引参数,不使用,置为NULL ); } void onDestroy() { if (!g_pDevice) g_pDevice->Release(); g_pDevice = NULL; } void onLogic(float fElapsedTime) { } void onRender(float fElasedTime) { //前两个参数是0和NULL时,清空整个游戏窗口的内容(清的是后台) //第三个是清除的对象:前面表示清除颜色缓冲区,后面表示清除深度缓冲区,D3DCLEAR_STENCIL清空模板缓冲区 g_pDevice->Clear(0, NULL, D3DCLEAR_TARGET|D3DCLEAR_ZBUFFER, D3DCOLOR_XRGB(0,100,100), 1.0f, 0); g_pDevice->BeginScene(); Transform(); D3DXMATRIXA16 matTY, matTX; D3DXMatrixRotationY(&matTY, timeGetTime() / 720.0f); D3DXMatrixRotationX(&matTX, timeGetTime() / 720.0f); //绘制茶壶 D3DXMATRIXA16 matTeapot; D3DXMatrixTranslation(&matTeapot, 0.0f, 0.0f, 0.0f); matTeapot = matTeapot * matTY; g_pDevice->SetTransform(D3DTS_WORLD, &matTeapot); g_pteapot->DrawSubset(0); //绘制立方体 D3DXMATRIXA16 matBox; D3DXMatrixTranslation(&matBox, 1.0f, 1.0f, 0.0f); matBox = matBox * matTX; g_pDevice->SetTransform(D3DTS_WORLD, &matBox); g_pbox->DrawSubset(0); //设置渲染状态为线框填充 g_pDevice->SetRenderState(D3DRS_FILLMODE, D3DFILL_WIREFRAME); g_pDevice->EndScene(); g_pDevice->Present(NULL, NULL, NULL, NULL); } int APIENTRY _tWinMain(_In_ HINSTANCE hInstance, _In_opt_ HINSTANCE hPrevInstance, _In_ LPTSTR lpCmdLine, _In_ int nCmdShow) { UNREFERENCED_PARAMETER(hPrevInstance); UNREFERENCED_PARAMETER(lpCmdLine); // TODO: 在此放置代码。 MSG msg; HACCEL hAccelTable; // 初始化全局字符串 LoadString(hInstance, IDS_APP_TITLE, szTitle, MAX_LOADSTRING); LoadString(hInstance, IDC_D3DDEMO, szWindowClass, MAX_LOADSTRING); MyRegisterClass(hInstance); // 执行应用程序初始化: if (!InitInstance (hInstance, nCmdShow)) { return FALSE; } hAccelTable = LoadAccelerators(hInstance, MAKEINTRESOURCE(IDC_D3DDEMO)); ZeroMemory(&msg, sizeof(msg)); while (msg.message != WM_QUIT) { if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) { TranslateMessage(&msg); DispatchMessage(&msg); } else { static DWORD dwTime = timeGetTime(); DWORD dwCurrentTime = timeGetTime(); DWORD dwElapsedTime = dwCurrentTime - dwTime; float fElapsedTime = dwElapsedTime * 0.001f; //------------渲染和逻辑部分代码---------- onLogic(fElapsedTime); onRender(fElapsedTime); //----------------------------------------- if (dwElapsedTime < 1000 / 60) { Sleep(1000/ 60 - dwElapsedTime); } dwTime = dwCurrentTime; } } onDestroy(); return (int) msg.wParam; } // // 函数: MyRegisterClass() // // 目的: 注册窗口类。 // ATOM MyRegisterClass(HINSTANCE hInstance) { WNDCLASSEX wcex; wcex.cbSize = sizeof(WNDCLASSEX); wcex.style = CS_HREDRAW | CS_VREDRAW; wcex.lpfnWndProc = WndProc; wcex.cbClsExtra = 0; wcex.cbWndExtra = 0; wcex.hInstance = hInstance; wcex.hIcon = LoadIcon(hInstance, MAKEINTRESOURCE(IDI_D3DDEMO)); wcex.hCursor = LoadCursor(NULL, IDC_ARROW); wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW+1); wcex.lpszMenuName = MAKEINTRESOURCE(IDC_D3DDEMO); wcex.lpszClassName = szWindowClass; wcex.hIconSm = LoadIcon(wcex.hInstance, MAKEINTRESOURCE(IDI_SMALL)); return RegisterClassEx(&wcex); } // // 函数: InitInstance(HINSTANCE, int) // // 目的: 保存实例句柄并创建主窗口 // // 注释: // // 在此函数中,我们在全局变量中保存实例句柄并 // 创建和显示主程序窗口。 // BOOL InitInstance(HINSTANCE hInstance, int nCmdShow) { hInst = hInstance; // 将实例句柄存储在全局变量中 g_hWnd = CreateWindow(szWindowClass, szTitle, WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, NULL, NULL, hInstance, NULL); if (!g_hWnd) { return FALSE; } SetMenu(g_hWnd, NULL); ShowWindow(g_hWnd, nCmdShow); UpdateWindow(g_hWnd); onInit(); return TRUE; } // // 函数: WndProc(HWND, UINT, WPARAM, LPARAM) // // 目的: 处理主窗口的消息。 // // WM_COMMAND - 处理应用程序菜单 // WM_PAINT - 绘制主窗口 // WM_DESTROY - 发送退出消息并返回 // // LRESULT CALLBACK WndProc(HWND g_hWnd, UINT message, WPARAM wParam, LPARAM lParam) { switch (message) { case WM_KEYDOWN: if (wParam == VK_ESCAPE) PostQuitMessage(0); break; case WM_CLOSE: DestroyWindow(g_hWnd); break; case WM_DESTROY: PostQuitMessage(0); break; default: return DefWindowProc(g_hWnd, message, wParam, lParam); } return 0; }
对于世界变换,我们每次绘制一个物体之前都要设置它的世界矩阵,否则会使用上一次绘制时使用的世界矩阵,即出现了“渲染状态泄露”的情况。其实不仅仅是世界矩阵,所有的渲染状态都需要在绘制之前进行设置,如果绘制完,其他对象不需要这种渲染状态,我们就要把渲染状态恢复。
typedef struct D3DMATERIAL9 { D3DCOLORVALUE Diffuse; // 指定表面反射的漫反射光 D3DCOLORVALUE Ambient; // 指定表面反射的环境光 D3DCOLORVALUE Specular; // 指定表面反射的镜面光 D3DCOLORVALUE Emissive; // 表面本身自发光 float Power; // 镜面高光 } D3DMATERIAL9, *LPD3DMATERIAL9;我们要做的就是填好这个结构体,然后调用设置材质的函数即可。
D3DMATERIAL9 mtrl; ZeroMemory(&mtrl, sizeof(mtrl)); mtrl.Ambient = D3DXCOLOR(1.0f, 0.5f, 0.1f, 1.0f); //物体表面对环境光的反射率,为一个颜色值 mtrl.Diffuse = D3DXCOLOR(0.5f, 0.5f, 0.5f, 1.0f); //物体表面对漫反射光的反射率,为一个颜色值 mtrl.Specular = D3DXCOLOR(1.0f, 1.0f, 1.0f, 1.0f); //物体表面对镜面反射光的反射率,为一个颜色值 mtrl.Emissive = D3DXCOLOR(0.0f, 0.0f, 0.0f, 1.0f); //物体自发光的颜色值 //mtrl.Power 镜面反射指数,该值越大,高光强度和周围亮度相差就越大 g_pDevice->SetMaterial(&mtrl);这样就设置好了物体的材质。配合下面的光照,就可以绘制出绚丽的3D世界啦!
//设置渲染状态,关闭灯光 g_pDevice->SetRenderState(D3DRS_LIGHTING, false);
g_pDevice->SetRenderState(D3DRS_AMBIENT, D3DCOLOR_XRGB(20, 20, 100));
//开启镜面反射 g_pDevice->SetRenderState(D3DRS_SPECULARENABLE, true);
typedef struct _D3DLIGHT9 { D3DLIGHTTYPE Type; // 定义了所要创建的光源类型 D3DCOLORVALUE Diffuse; // 该光源所发出的漫射光的颜色 D3DCOLORVALUE Specular; // 该光源所发出的镜面光的颜色 D3DCOLORVALUE Ambient; // 该光源所发出的环境光的颜色 D3DVECTOR Position; // 用于描述光源在世界坐标系中位置的向量。 D3DVECTOR Direction; // 一个描述光在世界坐标系中传播方向的向量。 float Range; // 光线“消亡”前,所能达到的最大光程。 float Falloff; // 仅用于聚光灯。定义了光强从内锥形到外锥形的衰减方式 float Attenuation0; // 该衰减变量定义了光强随距离衰减的方式。仅用于点光源和聚光灯 float Attenuation1; // 同Attenuation0 float Attenuation2; // 同Attenuation0 float Theta; // 仅用于聚光灯。指定了内部锥形的圆锥角,单位为弧度 float Phi; // 仅用于聚光灯。指定了外部锥形的圆锥角,单位为弧度 }D3DLIGHT9;
<span style="white-space:pre"> </span>D3DLIGHT9 light; ZeroMemory(&light, sizeof(light)); light.Type = D3DLIGHT_POINT; //光源类型 light.Ambient = D3DXCOLOR(0.6f, 0.6f, 0.6f, 1.0f); //光源的环境光颜色值 light.Diffuse = D3DXCOLOR(1.0f, 1.0f, 1.0f, 1.0f); //光源的漫反射光颜色值<span style="white-space:pre"> </span> light.Specular = D3DXCOLOR(0.3f, 0.3f, 0.3f, 1.0f); //光源的镜面反射光颜色值 light.Position = D3DXVECTOR3(0.0f,20.0f, 0.0f); //光源的位置 light.Attenuation0 = 1.0f; //衰减系数0 light.Attenuation1 = 0.0f; //衰减系数1 light.Attenuation2 = 0.0f; //衰减系数2 light.Range = 300.0f; //光源的光照范围 <span style="white-space:pre"> </span>//设置光照 g_pDevice->SetLight(0, &light); //启用光照 g_pDevice->LightEnable(0, true);
<span style="white-space:pre"> </span>D3DLIGHT9 light; ZeroMemory(&light, sizeof(light)); //平行光源 light.Type = D3DLIGHT_DIRECTIONAL; //光源类型 light.Ambient = D3DXCOLOR(0.5f, 0.5f, 0.5f, 1.0f); //光源的环境光颜色值<span style="white-space:pre"> </span> light.Diffuse = D3DXCOLOR(1.0f, 1.0f, 1.0f, 1.0f); //光源的漫反射光颜色值 light.Specular = D3DXCOLOR(0.3f, 0.3f, 0.3f, 1.0f); //光源的镜面反射光颜色值 light.Position = D3DXVECTOR3(0.0f,20.0f, 0.0f); //光源的位置 <span style="white-space:pre"> </span>//设置光照 g_pDevice->SetLight(0, &light); //启用光照 g_pDevice->LightEnable(0, true);
<span style="white-space:pre"> </span>D3DLIGHT9 light; ZeroMemory(&light, sizeof(light)); <span style="white-space:pre"> </span>//聚光灯光源 light.Type = D3DLIGHT_SPOT; //光源类型 light.Ambient = D3DXCOLOR(0.3f, 0.3f, 0.3f, 1.0f); //光源的环境光颜色值 light.Diffuse = D3DXCOLOR(1.0f, 1.0f, 1.0f, 1.0f); //光源的漫反射光颜色值 light.Specular = D3DXCOLOR(0.3f, 0.3f, 0.3f, 1.0f); //光源的镜面反射光颜色值 light.Position = D3DXVECTOR3(100.0f,100.0f, 100.0f); //光源的位置 light.Direction = D3DXVECTOR3(-1.0f, -1.0f, -1.0f); //光源方向 light.Attenuation0 = 1.0f; //衰减系数0 light.Attenuation1 = 0.0f; //衰减系数1 light.Attenuation2 = 0.0f; //衰减系数2 light.Range = 300.0f; //光源的光照范围 light.Falloff = 0.1f; //聚光灯特有的参数 light.Phi = D3DX_PI / 3.0f; //聚光灯特有的参数 light.Theta = D3DX_PI / 6.0f; //聚光灯特有的参数 <span style="white-space:pre"> </span>//设置光照 g_pDevice->SetLight(0, &light); //启用光照 g_pDevice->LightEnable(0, true);
还是很漂亮的,不同的光源,有不同的效果。
最后再总结一下三种光源的区别(结构体包含的属性):
点光源: 颜色,位置,半径,衰减率
平行光源:颜色,方向
聚光灯: 颜色,位置,方向,内椎,外锥,内外锥衰减,衰减率