个人的Directx9研究总结 (1)
?
自己最近在研究一個(gè)Directx9的小型游戲引擎,總結(jié)一下目前的成果以及遇到的各種問題的解決方法。
雖然是Directx9但是繪制全部使用Shader實(shí)現(xiàn),現(xiàn)狀除了物理模擬外其他功能基本都有。
碰撞檢測有AABB,OBB和球體對球體。由于開發(fā)環(huán)境是日語環(huán)境所以UI說明用的日文,模型都來源自MMD模型。
1. ImGui介紹
IMGUI 全稱是immediate mode gui,這種實(shí)現(xiàn)方法的GUI非常適合作為實(shí)時(shí)程序輔助組件用來調(diào)試。
Dear Imgui是目前比較好用的一個(gè)開源Imgui項(xiàng)目,
下載: https://github.com/ocornut/imgui
下載后把必要文件包含到自己的工程并把對應(yīng)函數(shù)加入到程序循環(huán)中即可使用。具體組入方法參考Demo。
IMGUI很合適實(shí)時(shí)項(xiàng)目使用,如果同樣是對Directx或者OpenGL進(jìn)行研究的人來說,我強(qiáng)烈推薦使用Dear Imgui,可以大幅度縮短各種Debug時(shí)間。
2. 3D空間數(shù)學(xué)
3D空間表示一件物體的運(yùn)動可以用縮放+旋轉(zhuǎn)+平移來描述。而矩陣則可以描述以上行為的總和
?
如圖,綠色部分表示物體的旋轉(zhuǎn)和縮放,紅色部分表示物體的平移。當(dāng)無縮放時(shí)(也就是S = 1)。
綠色部分可以直接表示旋轉(zhuǎn)。而矩陣有了縮放的情況下乘法計(jì)算后狀況就變得比較復(fù)雜。
所以對于游戲制作等情況,我推薦使用兩套矩陣來記錄3D物體的運(yùn)動,一個(gè)是s = 1,用來記錄物體平移和旋轉(zhuǎn)的矩陣,另外一個(gè)是縮放旋轉(zhuǎn)平移的矩陣,用來修正物體在空間里的原本大小和朝向,兩個(gè)矩陣相乘后便是最終矩陣。
?
3.關(guān)于3D場景管理的優(yōu)化的意義
如上圖,這是3D空間一個(gè)物體最終位置的計(jì)算過程。從局部坐標(biāo)系經(jīng)過世界變換,視野變換,投影變換后得到在裁剪空間內(nèi)的坐標(biāo),裁剪空間內(nèi)會把視野外(觀察矩陣和投影矩陣構(gòu)成的視錐體,在此之外的物體會被裁減掉不進(jìn)行繪制。所以看不到的物體其實(shí)到最后沒有占用GPU。
那么諸如包圍球(BVH),2叉樹, n叉樹等場景算法是干什么用的呢?。
(1).程序如果本身存在大量物體,在世界>視野>投影變換時(shí)的矩陣計(jì)算已經(jīng)會占用大量內(nèi)存,而場景管理可以減少這部分的計(jì)算。也就是說通過場景管理使得后面的變換階段只計(jì)算必要的矩陣。
(2).物理計(jì)算與碰撞計(jì)算等,如果不進(jìn)行管理時(shí)隨著物體的增多必然會消耗越來越多的內(nèi)存,場景管理可以篩選出只存在可能碰撞的物體,減少計(jì)算量。
?
4. Directx9.0 下的一些Mesh處理
D3DXCreateBox,D3DXCerateSphere等系列函數(shù)可以創(chuàng)建自帶的簡單幾何圖形,不過這些圖形并沒有紋理坐標(biāo),那么如果想給這些默認(rèn)圖形貼上紋理要怎么做呢?方法是:
(1).從D3DVERTEXELEMENT9數(shù)組定義一個(gè)包含紋理坐標(biāo)的數(shù)據(jù)流。
(2).把Mesh下的LockVertexBuffer函數(shù)鎖定頂點(diǎn)緩存,傳入剛才定義的D3DVERTEXELEMENT9
(3).重新計(jì)算紋理坐標(biāo)后再解鎖頂點(diǎn)緩存,再用CloneMesh克隆回去。
球型物體或者近似球型物體的紋理坐標(biāo)有固定算法,像最初的圖中球和茶壺便是程序中重新計(jì)算紋理坐標(biāo)后所得。這套方法可改變Mesh的所包含頂點(diǎn)信息。將來在做法線貼圖時(shí)需要切線。而給物體添加切線也是用這種方式:聲明頂點(diǎn)數(shù)據(jù)流>鎖定Mesh頂點(diǎn)緩存>傳入重構(gòu)的數(shù)據(jù)>解鎖>克隆Mesh的方式重構(gòu)Mesh包含的頂點(diǎn)信息
?
4.關(guān)于Lambert光照模型,Phong光照模型的光照方向:
(1).漫反射光照模型
Lambert光照模型是一種漫反射光照模型,計(jì)算其漫反射率方法為:
float Diff = max(dot(Object.Normal, Light.Dir), 0.0);其中Object.Normal是物體的法線,而Light.Dir是光射出的方向。
注意是物體表面射出的方向而不是原本方向,如果光原本方向是L,則Light.Dir為-L。
如果直接用光原本方向L那么點(diǎn)乘結(jié)果肯定是負(fù)值,漫反射部分就會漆黑一片。順便說一下Lambert光照模型會導(dǎo)致物體沒受到光的部分比較暗,因此有了改良了Harf Lambert,其漫反射率方法為:
原本的點(diǎn)乘結(jié)果由[-1,1]變成了[0,1],所以Harf Lambert在沒受光的部分也不會太黑。
而現(xiàn)在有一種更加便利的模型,叫Wrap Lighting,可以同時(shí)滿足以上情況并且能自己調(diào)整。其算法為:
看出來了嗎?Lambert就是wrap=0的狀況,Half Lambert就是warp=1。改變wrap可以調(diào)整漫反射率。
(2). 高光光照模型
Phong和BlinnPhong光照模型是計(jì)算高光的光照模型,Phong的高光計(jì)算方法為:
float3 viewDir = normalize(Camera.ViewPos – Object.Pos); float3 reflectDir = reflect(Light.Dir, Object.Normal); float spec = pow(max(dot(viewDir, reflectDir), 0.0), Light.SpecPower);?
Phong光照模型會在視線向量和反射向量大于90度情況下點(diǎn)乘為負(fù)數(shù),于是高光就會突然消失沒有。
Blinn-Phong是Phong光照模型的改良版,Blinn-Phong的使用半程向量,不會有高光突然消失的情況,而且不需要使用reflect函數(shù),性能消耗上也略低。
注意這里的Light.Dir是光射出方向。和上文的Lambert光照所用方向一致,和Phong的相反。下圖是各種光線方向總結(jié)
?
6.關(guān)于渲染到紋理
渲染到紋理可以說是非常重要的手段,后處理以及一系列需要屏幕空間的渲染方式都離不開他,Directx9中有兩種方式,一種是使用LPD3DXRENDERTOSURFACE接口,非常簡單易用,但是缺點(diǎn)是多重采樣會消失,也就是沒有多重采樣。而且如果需要各個(gè)表面有承接關(guān)系的話,這個(gè)接口很難用。
因此還是傳統(tǒng)方法自己建兩個(gè)Surface,一個(gè)負(fù)責(zé)RendeTarget一個(gè)負(fù)責(zé)DepthBuffer。在渲染前Get現(xiàn)在的RendeTarget和DepthBuffer,開始繪制時(shí)Set現(xiàn)在預(yù)定的表面,完畢后再Set回最初取得的的表面。這個(gè)方法結(jié)合LPDIRECT3DDEVICE9下的StretchRect函數(shù)可以把當(dāng)前渲染具有多重采樣的表面綁回紋理所在的表面,最終結(jié)果就帶上了多重采樣效果。
總結(jié)
以上是生活随笔為你收集整理的个人的Directx9研究总结 (1)的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Rhino入门教程---莫比乌斯环建模
- 下一篇: 小学接触web的我是如何拿下蚂蚁实习 O