实例工程文件如下:点击打开链接
在DirectX 中,坐标系主要分为三种,分别是:自身坐标系,世界坐标系,和摄像机坐标系
自身坐标系:一般情况下,游戏角色的自身坐标系的原点一般在两脚之间,自身坐标系的作用是用于改变角色形状的,比如你的胳膊相对于自身原点的坐标系改变,胳膊的位置长短什么的就肯定变了
世界坐标系:世界坐标系是物体相对于整个场景的坐标,他是用来确定的物体的位置的,一般物体的平移旋转都是对世界坐标的改变
摄像机坐标系:顾名思义,摄像机坐标系就是相对于摄像机的坐标,以摄像机所在的位置为原点
DirectX中,坐标系采用的是左手坐标系,就是向右为X轴的正方形,向上为Y轴的正方形,向屏幕里是Z轴的正方向,OpenGL里采用右手坐标系,这个不在我们讨论范围,所以在此不再赘述,总之,不同框架有其自己的坐标系规定,我们注意即可。
接下来是顶点,首先要弄明白的就是,顶点有哪些属性:1、基于世界坐标系的世界坐标 2、颜色、法线、贴图、纹理坐标等
下面介绍灵活顶点格式的概念:
灵活顶点格式 ( Flexible Vertex Format )FVF
其实这就是一个我们自己声明定义的一个结构,在其中,我们进行图元顶点的存储,我们在程序中用它来记录一切顶点的属性,但DX本身对它有一定的限制,那就是限制我们在设计其属性时各类型参数的顺序。在这个FVF中,我们可以设置顶点的:
1:位置x,y,z坐标 2:RHW(已转化的顶点,齐次W倒数) 3:混合加权值 4:顶点的法线 5:扩散的颜色(漫射属性) 6:反射颜色(镜面反射属性) 7:纹理坐标集(UV坐标)等 我们可以根据不同的需要来选择部分的属性进行设置,这将比强硬全部设置来的方便,计算机在图形绘制时将根据我们的设置来处理必要的组成部分,节省了大量的内存和渲染时间。但是依旧是那句话,可以部分属性不进行设置,但顺序不可颠倒。(这一段是百度的)
我们着重介绍一下灵活顶点格式的坐标部分:
#define D3DFVF_CUSTOMVERTEX(D3DFVF_XYZRHW | D3DFVF_DIFFUSE)
struct stVertex
{
D3DXVECTOR4 vPos;//为什么是4维向量,因为是X Y Z RHW各占一维
DWORD dwColor;
};
D3D中的顶点缓冲区,为什么有了数组还是定义顶点缓冲区?
D3D的API在绘制的时候,顶点数据是存到数组中了,但是在画的时候,还是从缓冲区中拿。因此数组只是临时存一下,存完了还是要把数据放到缓冲区中,这样显卡在画的时候才能从缓冲中拿数据、绘制
LPDIRECT3DVERTEXBUFFER9 p_dVB = NULL;
stVertex cVertices[] =
{
{ D3DXVECTOR4(100,100,0,1), D3DCOLOR_XRGB(255 , 0, 0) },//因为使用的是XYZRHW,D3DXVECTOR4(100,100,0,1)最后一个数字一定是1
{ D3DXVECTOR4(400,100,0,1), D3DCOLOR_XRGB(0, 255, 0) },
{ D3DXVECTOR4(100,400,0,1), D3DCOLOR_XRGB(0, 0, 255) },
};
5.为顶点缓冲区分配空间,并将数组中的顶点数据拷贝到顶点缓冲区中
//通过设备指针来创建顶点缓冲区,CreateVertexBuffer类似new,在堆中申请的内存,因此不可以写在循环中,会造成内存泄露
g_pDevice->CreateVertexBuffer(
sizeof(cVertices),//顶点缓冲区的大小
D3DUSAGE_WRITEONLY,//顶点缓冲区的作用
D3DFVF_CUSTOMVERTEX,//通过我们已经定义好的灵活顶点模式,告知系统当前每一个顶点的格式
D3DPOOL_MANAGED,//顶点缓冲区的存储空间位于哪里,D3DPOOL_MANAGED代表当前存储空间受系统管理,自动分配
&p_dVB,//返回的顶点缓冲区指针
NULL//Handle*一律给NULL,系统保留参数,一律给NULL即可
);
//前面只是分配空间,下面要拷贝数据,格式是固定的,先声明一个无类型的指针
void* pVertices = NULL;
//锁定顶点缓冲区,以便向其中拷贝数据【有锁定就有unlock】
p_dVB->Lock(
0,//锁定的偏移量,0表示不偏移
sizeof(cVertices),//锁定的大小
&pVertices,//锁定之后的存储空间由该指针指向
0//锁定的标识,为0即可
);
//将cVertices中的数据拷贝到pVertices中,大小就是cVertices数组这么大
memcpy(pVertices,cVertices,sizeof(cVertices));
p_dVB->Unlock();
6.设置数据源、设置灵活顶点格式、绘制图元,这部分写在游戏循环里
//设置数据流的来源
g_pDevice->SetStreamSource(
0,//数据流管道号(d3d中可以同时支持16种管道,编号从0-15,使用的时候依次使用,从0开始)
p_dVB,//数据来源,将数据从这个往管道0里面放
0,//数据流的偏移量,前面p_dVB->Lock就没有偏移,这都是相对应的,因此这里也不偏移
sizeof(stVertex)//每个数据的字节数大小,但是系统不知道按照何种格式解析
);
//告知系统数据格式,以便解析数据
g_pDevice->SetFVF(D3DFVF_CUSTOMVERTEX);
//绘制图元----一种画图方式,以后讲解,PT—Primitive是图元,Type是类型,三角形Triangle
g_pDevice->DrawPrimitive(
D3DPT_TRIANGLELIST,//三角形列
0,//起始点编号(三角形中先画哪个点,等同于在数组中顶点的编号,即从数组的0号元素开始画)
1//图元的数量
);
完整代码示例如下
//Utility.h
#ifndef D3D_USEFUL_TOOLS_
#define D3D_USEFUL_TOOLS_
#include <Windows.h>
#include <MMSystem.h>
#pragma comment(lib,"Winmm.lib")
#include <d3d9.h> //d3d的基础函数头文件
#pragma comment(lib,"d3d9.lib") //d3d的基础函数库文件
#include <d3dx9.h> //d3d的扩展函数头文件
#pragma comment(lib,"d3dx9.lib") //d3d的扩展函数库文件
#endif
//main.cpp
#include "Utility.h"
HWND g_hWnd = 0;
//------改造3D窗口需要用到的内容-------
LPDIRECT3D9 g_pD3D = NULL; //D3D的接口指针
LPDIRECT3DDEVICE9 g_pDevice = NULL; //D3D的设备指针
/*
在3D窗口中绘制一个三角形的步骤为:
(1)、定义灵活顶点格式
(2)、根据顶点格式构建一个描述顶点的结构体
(3)、声明一个顶点缓冲区指针
(4)、定义一个结构体数组用来给每一个顶点赋值
(5)、为顶点缓冲区分配空间并将数组中的顶点数据拷贝到顶点缓冲区中
(6)、设置数据源、设置灵活顶点格式、绘制图元
*/
//----步骤(1)、定义灵活顶点格式----
//每一个顶点的格式(经过变换的屏幕坐标|顶点的颜色)
#define D3DFVF_CUSTOMVERTEX (D3DFVF_XYZRHW|D3DFVF_DIFFUSE)
//----步骤(2)、根据顶点格式构建一个描述顶点的结构体----
//描述一个顶点的结构体
struct stVertex
{
D3DXVECTOR4 vPos;
DWORD dwColor;
};
//----步骤(3)、声明一个顶点缓冲区指针----
//D3D中的顶点缓冲区
LPDIRECT3DVERTEXBUFFER9 g_pVB = NULL;
//初始化顶点缓冲区
void initVB()
{
//----步骤(4)、定义一个结构体数组用来给每一个顶点赋值----
//数组用来存储当前程序中的顶点数据
stVertex cVertices[] =
{
{D3DXVECTOR4(100,100,0,1),D3DCOLOR_XRGB(255,0,0)},
{D3DXVECTOR4(400,100,0,1),D3DCOLOR_XRGB(0,255,0)},
{D3DXVECTOR4(100,400,0,1),D3DCOLOR_XRGB(0,0,255)}
};
//----步骤(5)、为顶点缓冲区分配空间并将数组中的顶点数据拷贝到顶点缓冲区中----
//通过设备指针来创建顶点缓冲区,用来存储顶点数据
g_pDevice->CreateVertexBuffer(
sizeof(cVertices),//顶点缓冲区的大小
D3DUSAGE_WRITEONLY, //顶点缓冲区的作用
D3DFVF_CUSTOMVERTEX, //通过我们已经定义好的灵活顶点格式告知系统当前每一个顶点的格式
D3DPOOL_MANAGED,//顶点缓冲区的存储空间位于哪里,D3DPOOL_MANAGED代表当前存储空间受系统管理,自动分配
&g_pVB,//返回的顶点缓冲区指针
NULL //系统保留参数,为NULL即可
);
void * pVertices = NULL;
//锁定顶点缓冲区,以便向其中拷贝数据
g_pVB->Lock(
0,//锁定的偏移量
sizeof(cVertices),//锁定的大小
&pVertices, //锁定之后的存储空间由该指针指向
0 //锁定的标识,为0即可
);
memcpy(pVertices,cVertices,sizeof(cVertices));
g_pVB->Unlock();
}
//初始化D3D运行环境
void onCreateD3D()
{
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 = true; //窗口模式
////-------全屏模式的写法:---------
//d3dpp.Windowed = false;
//d3dpp.BackBufferWidth = d3ddm.Width;
//d3dpp.BackBufferHeight = d3ddm.Height;
d3dpp.BackBufferFormat = d3ddm.Format;
d3dpp.BackBufferCount = 1;
d3dpp.SwapEffect = D3DSWAPEFFECT_DISCARD;
//是否开启自动深度模板缓冲
d3dpp.EnableAutoDepthStencil = true;
//当前自动深度模板缓冲的格式
d3dpp.AutoDepthStencilFormat = D3DFMT_D16;
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);
}
//初始化游戏内容
void onInit()
{
onCreateD3D();
/*D3DXMATRIX matView , matProj;
D3DXMatrixIdentity(&matView);
D3DXMatrixIdentity(&matProj);
D3DXVECTOR3 vEyePos(0,3,-5);
D3DXVECTOR3 vLookAt(0,0,0);
D3DXVECTOR3 vUp(0,1,0);
D3DXMatrixLookAtLH(&matView,&vEyePos,&vLookAt,&vUp);
g_pDevice->SetTransform(D3DTS_VIEW,&matView);
D3DXMatrixPerspectiveFovLH(&matProj,D3DX_PI/3,4.0f/3.0f,1.0f,5000.0f);
g_pDevice->SetTransform(D3DTS_PROJECTION,&matProj);*/
initVB();
}
void onLogic(float fElapsedTime)
{
}
void onRender(float fElapsedTime)
{
g_pDevice->Clear(0,NULL,
D3DCLEAR_TARGET|D3DCLEAR_ZBUFFER,
D3DCOLOR_XRGB(234,123,123),1.0f,0);
g_pDevice->BeginScene();
//----步骤(6)、设置数据源、设置灵活顶点格式、绘制图元----
//设置数据流的来源
g_pDevice->SetStreamSource(
0,//数据流管道号(0-15)
g_pVB, //数据的来源
0,//数据流的偏移量
sizeof(stVertex)//每个数据的字节数大小
);
//告知系统数据格式,以便解析数据
g_pDevice->SetFVF(D3DFVF_CUSTOMVERTEX);
//绘制图元 Primitive:图元,Type:类型,Triangle:三角形
g_pDevice->DrawPrimitive(
D3DPT_TRIANGLELIST, //三角形列
0, //起始点编号(等同于在数组中顶点的编号)
1 //图元的数量
);
g_pDevice->EndScene();
g_pDevice->Present(NULL,NULL,NULL,NULL);
}
//销毁游戏
void onDestroy()
{
}
LRESULT CALLBACK WndProc(HWND _hWnd,UINT _uMsg,
WPARAM _wparam,LPARAM _lparam);
INT WINAPI WinMain( __in HINSTANCE hInstance,
__in_opt HINSTANCE hPrevInstance,
__in_opt LPSTR lpCmdLine,
__in int nShowCmd )
{
WNDCLASS wc;
ZeroMemory(&wc,sizeof(wc));
wc.cbClsExtra = NULL;
wc.cbWndExtra = NULL;
wc.hbrBackground = (HBRUSH)GetStockObject(GRAY_BRUSH);
wc.hCursor = LoadCursor(NULL,IDC_ARROW);
wc.hIcon = LoadIcon(NULL,IDI_APPLICATION);
wc.hInstance = hInstance;
wc.lpfnWndProc = WndProc;
wc.lpszClassName = TEXT("alibaba");
wc.lpszMenuName = NULL;
wc.style = CS_HREDRAW | CS_VREDRAW | CS_DBLCLKS;
RegisterClass(&wc);
g_hWnd = CreateWindow(wc.lpszClassName,
TEXT("First_3DApp"),WS_OVERLAPPEDWINDOW,100,20,
800,600,NULL,NULL,hInstance,NULL);
if(!g_hWnd)
return 0;
ShowWindow(g_hWnd,SW_SHOWNORMAL);
UpdateWindow(g_hWnd);
onInit();
MSG msg;
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 0;
}
LRESULT CALLBACK WndProc(HWND _hWnd,UINT _uMsg,
WPARAM _wparam,LPARAM _lparam)
{
switch(_uMsg)
{
case WM_KEYDOWN:
if(_wparam == VK_ESCAPE)
DestroyWindow(_hWnd);
break;
case WM_CLOSE:
DestroyWindow(_hWnd);
break;
case WM_DESTROY:
PostQuitMessage(0);
break;
}
return DefWindowProc(_hWnd,_uMsg,_wparam,_lparam);
}