LRESULT CALLBACK WinProc( HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
switch ( msg )
{
case WM_DESTROY:
//...这里将调用一个是收尾函数来释放所有界面指针
PostQuitMessage(0);
DestroyWindow(hWnd);
break;
default:
break;
}
return ( DefWindowProc( hWnd, msg, wParam, lParam ) );
}
int WINAPI WinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nShowCmd )
{
//create a main window:
WNDCLASSEX mainwnd;
mainwnd.cbSize=sizeof(WNDCLASSEX);
mainwnd.cbClsExtra=0;
mainwnd.cbWndExtra=0;
mainwnd.hbrBackground=NULL;//这将是一个没有背景
mainwnd.hCursor=NULL;//鼠标是沙漏
mainwnd.hIcon=NULL;//没有图标
mainwnd.hIconSm=NULL;
mainwnd.hInstance=hInstance;
mainwnd.lpfnWndProc=WinProc;//没有标题栏
mainwnd.lpszClassName="main window";
mainwnd.lpszMenuName=NULL;//没有菜单
mainwnd.style=CS_OWNDC;//的窗口
RegisterClassEx(&mainwnd);
HWND hWnd = CreateWindowEx( NULL,"main window","only a demo",
WS_POPUP|WS_VISIBLE, 0, 0, 800, 600, NULL, NULL, hInstance, NULL);
//在此初始化D3D设备,物体
//Init_D3D();
//主事件循环:
MSG msg;
ZeroMemory(&msg,sizeof(msg));
while (msg.message!=WM_QUIT)
{
//proc message:
if ( PeekMessage( &msg, hWnd, 0, 0, PM_REMOVE ) )
{
TranslateMessage( &msg );
DispatchMessage( &msg );
}
else
{
//每帧一次的渲染就在这里
//Render();
}
}
//UnRegiste the window:
UnregisterClass( "main window", mainwnd.hInstance );
return ( msg.wParam);
}//end winmain
Direct3D是DirectX中极其重要的组建,提供了一套完整的用来设置场景、操作3D物体、渲染画面的方法。来复习一下D3D程序的一般流程:首先得有个要渲染的物体,你得给出它的顶点、贴图、材质信息。然后把它的坐标(3D的)转换为屏幕坐标,再添加一些灯光信息,然后调用一个渲染方法,它所做的就是根据各个顶点的位置(已是屏幕坐标),用灯光和材质信息计算亮度,把贴图“填”进去,这样你看起来就是个立体的东西。渲染方法函数把图画在后备缓冲,因此最后别忘了把它换到前缓冲。你要有个Dirext3D的接口作为创建一切的基础。像这样: LPDIRECT3D9 g_lpd3d9;//全局的
g_lpd3d9 = Direct3DCreate9( D3D_SDK_VERSION );//参数代表了你的D3D SDK版本。必须要这样写
然后通过它来创建一个Direct3D设备接口,其他所有创建物体、灯光,以及渲染等方法都要通过这个设备接口指针来调用:
LPDIRECT3DDEVICE9 g_lpd3ddevice;//全局的
HRESULT InitD3D(HWND hWnd)
{
D3DPRESENT_PARAMETERS m_d3dpp;
//我们要把当前适配器(显卡)的信息读到这个结构体里,它将被用于创建设备
D3DDISPLAYMODE m_d3ddm;
g_lpd3d9->GetAdapterDisplayMode(D3DADAPTER_DEFAULT,&m_d3ddm);
//D3DADAPTER_DEFAULT表示默认显卡
ZeroMemory(&m_d3dpp,sizeof(m_d3dpp));
m_d3dpp.Windowed=false;//要建立一个全屏程序,则置此项为FALSE
m_d3dpp.BackBufferWidth=m_d3ddm.Width;
m_d3dpp.BackBufferHeight=m_d3ddm.Height;
m_d3dpp.BackBufferFormat=m_d3ddm.Format;//此项记录色深信息
m_d3dpp.SwapEffect=D3DSWAPEFFECT_DISCARD;
m_d3dpp.EnableAutoDepthStencil = TRUE;//让D3D管理深度缓冲
m_d3dpp.AutoDepthStencilFormat=D3DFMT_D16;//设置深度缓冲为16位
m_d3dpp.hDeviceWindow=hWnd;
//create the device:
g_lpd3d9->CreateDevice(D3DADAPTER_DEFAULT,//适配器标识符,大多数情况使用默认的
D3DDEVTYPE_HAL,//硬件抽象层
hWnd,//这是一个当前窗口的句柄,使用刚才CreateWindowEx()的返回值就行了
D3DCREATE_HARDWARE_VERTEXPROCESSING,//顶点处理类型,这里使用硬件处理
&m_d3dpp,//描述设备的结构体
&g_lpd3ddevice);//最后给一个要被创建的设备的指针
//启用Z缓冲
g_lpd3ddevice->SetRenderState(D3DRS_ZENABLE,TRUE);
return S_OK;
}
Z缓冲是深度缓冲的一种。深度缓冲是一块与屏幕大小相同的区域,记录着屏幕上每个点的“最近像素”有多“近”。比如说程序要画一个三角形中的某个点,但它发现深度缓冲中记录的距离比这个点要近,也就是说其他物体的某点挡在它前面,自然也就不会去画它;否则就画它,然后把该点的深度信息写进缓冲。现在我们把Z-Buffer打开,就是把这些事交给系统处理。调节缓冲数据位数是在较精确的数据与较大的空间之间作个权衡。
该用到刚刚做完的那个东西了。在D3D中,可以说所有物体的外形位置由顶点构成;材质决定着物体如何反射光线;贴图的作用自不言而喻。任何3D程序首先要做的就是创建一套这些信息。刚才说道,现在这些信息都包含在现成的mesh物体中,我们要做的就是把它从文件中提取出来。D3D中的IDirect3DMesh界面提供了这些方法。 LPD3DXMESH g_lpmesh;//全局的
mesh物体的定点信息都存放在g_lpmesh里,而材质和贴图还要单独提取出。D3D里,材质和贴图分别用D3DMATERIAL9结构体和ID3DTexture9界面描述。一般来说,mesh中都含有多种贴图和材质,我们要将其声明为数组。 D3DMATERIAL9 *g_meshmtrl;
LPDIRECT3DTEXTURE9 *g_meshtex;
数组的大小事先并不知道,取决于mesh物体的SebSet的数量。在一个mesh物体中,拥有相同材质贴图属性的一组三角形叫它的一个SubSet。因为转换当前材质和贴图是十分耗时的,因此在D3D中是按照一个SubSet组接着另一个去渲染的,这样就避免了材质贴图的频繁转变。我们用一个DWOD型的全局变量g_meshmtrlnum记录这个mesh中的SubSet数量。
HRESULT InitGeometry()
{
LPD3DXBUFFER lpmeshmtrlbuffer;//材质贴图的临时缓冲
//从文件装载一个mesh物体:
D3DXLoadMeshFromX("c:\\", //文件路径
D3DXMESH_SYSTEMMEM,//g_lpmesh被创建的位置,这里它被创建在系统内存而非显示内存
g_lpd3ddevice,NULL,&lpmeshmtrlbuffer,NULL,
&g_meshmtrlnum,//一个用于存放SubSet数量的变量指针
&g_lpmesh);//最后是要被创建的mesh指针
//我们要一次性读取出全部材质和贴图信息,并都存放在这里:
D3DXMATERIAL *pd3dxmtrl=(D3DXMATERIAL*)lpmeshmtrlbuffer->GetBufferPointer();
//然后再细分:
g_meshmtrl=new D3DMATERIAL9[g_meshmtrlnum];
g_meshtex=new LPDIRECT3DTEXTURE9[g_meshmtrlnum];
//对每个SubSet,我们分别提取它的材质和贴图
for(DWORD i=0;i<g_meshmtrlnum;i++)
{
g_meshmtrl=pd3dxmtrl.MatD3D;
//实际上,pd3dxmtrl.pTextureFilename只是个文件名
TCHAR texname[512];//存放完整的贴图文件路径
strcpy(texname,"c:\\");
strcat(texname, pd3dxmtrl.pTextureFilename);
D3DXCreateTextureFromFile(g_lpd3ddevice,texname,&g_meshtex);
}
lpmeshmtrlbuffer->Release();
lpmeshmtrlbuffer = NULL;
return S_OK;
}
打开*.x文件(如果是文本模式的)你会看到贴图在文件中只被记录该图片的文件名。而CreateTextureFromFile()所需要的参数是一个完整路径,所以如果这些图不在程序同一目录下,就要在代码中做些处理。否则我敢保证你什么也看不见。这里,我只是简单的使用了绝对路径。
好,所有信息都被从文件中摘抄了出来,但现在还不是渲染的时候。没有进行坐标转换,目前所有顶点使用的,都是其局部的3D坐标,并非渲染函数能够直接使用的屏幕坐标。通常需要经过以下三种坐标转换: |