DirectX 总结和DirectX 9.0 学习笔记
轉自:http://www.cnblogs.com/graphics/archive/2009/11/25/1583682.html
DirectX 總結
DDS
DirectXDraw Surface file format, .dds。這是微軟從DirectX7開始引進的一種文件格式,它用來存儲壓縮的或未壓縮的紋理,該格式支持mimaps cube maps和volume maps, D3DX和許多其他的DX工具都支持這種格式,比如DirectX Texture Editor(dxtex.exe)和Texture Conversion Tool(Texconv.exe),從D3D110開始,DDS文件也支持紋理數組
DXGI
DirectX Graphics Infrastructure
轉換為.x格式
MeshConvert.exe,這個tool位于Microsoft DirectX SDK Utilitiesinx86目錄下面,可以用來轉換.x文件,支持.x, .obj, .sdkmesh格式之間的相互轉換,.sdkmesh是微軟新的格式在DX10/DX11之后使用,用來取代.x格式,MeshConvert是一個命令行工具,使用方法如下
Usage: meshconvert <options> <input filename>
<input filename> Input mesh filename. The input file can be of .x, .obj, or .sdkmesh format /o <file> Optional output mesh filename /y Overwrite existing destination file /n Generate normals /t Generate tangents /tb Generate tangents and binormals /tcount <count> Texcoord count for the output mesh /a Output animation information /op Vertex cache optimize the mesh before exportin (non-functional) /sdkmesh [default] convert to .sdkmesh binary file /x convert to .x binary file /xt convert to .x text file /v Verbose
.SDKMesh is a simple custom mesh format used by new SDK samples
使用DirectX Control Panel
使用DirectX Control Panel可以方便的設置DirectX相關的東西,比如用Debug庫還是Release庫,是否檢測內存泄漏,是否允許Shader調試等等,這個工具的位置在...Microsoft DirectX SDKUtilitiesinx86,界面如下圖。
使用PIX分析D3D應用程序
PIX是調試和分析D3D應用程序的,該工具位于...Microsoft DirectX SDKUtilitiesinx86下,關于它的用法,DX幫助文檔中有詳細的說明,位于tools-DirectX Performance Tools-PIX下,工具截圖
注意資源釋放的順序
有時候在釋放資源的時候會出現運行時錯誤,這可能是由于資源之間的依賴造成的,比如下面的代碼,運行時會出現一個違規訪問的錯誤,為什么呢,因為m_SoundBuffer是依賴m_pDirectSound的,而m_pDirectSound先釋放了,所以m_SoundBuffer變得不可訪問了
if(m_pDirectSound != NULL) { m_pDirectSound->Release() ; m_pDirectSound = NULL ; }
if(m_SoundBuffer != NULL) { m_SoundBuffer->Release() ; m_SoundBuffer = NULL ; }
將二者釋放的順序交換一下就好了,如下。
if(m_SoundBuffer != NULL) { m_SoundBuffer->Release() ; m_SoundBuffer = NULL ; }
if(m_pDirectSound != NULL) { m_pDirectSound->Release() ; m_pDirectSound = NULL ; }
注意變換的順序
有很多圖形學書籍都講到過,先旋轉再平移和先平移再旋轉效果是不一樣的,同理,先縮放再平移和先平移再縮放也是不一樣的。
不要在Render函數中設置變換矩陣
為了渲染,一般的對象都有個Render函數,在渲染之前要做一些變換,比如移動旋轉之類的,但是切記不可在Render函數中做這些變換,因為Render函數是被實時調用的,如果在這里做變換,則一個變換會被應用很多次。
D3D中頂點的順序問題
Remember in Direct3D the vertices must be defined in clockwise order. see the picture below, the order is v0, v1, v2, v3
正確使用時間函數timeGetTime()
這個函數返回的是當前時間-單位毫秒,可是類型是DWORD,而我們一般做計算都需要float類型,轉換一下,一般應該先計算差值,再做類型轉換
DWORD lastTime = timeGetTime();
...
DWORD currTime = timeGetTime(); float timeDelta = (currTime - lastTime) * 0.001f;
...//use timeDelta lastTime = currTime;
而不要這樣做:
static float lastTime = (float)timeGetTime();
... float currTime = (float)timeGetTime(); float timeDelta = (currTime - lastTime)*0.001f;
這樣是要損失精度的,因為DWORD是32為整數,而float只有24位精度,此法不可取。
讓代碼只執行一次
void RunOnce() { static bool flag = false ;
if(!flag) { cout << "you can not see this line twice!" << endl ; flag = true ; } }
頭文件包含警戒
通常我們用下面的代碼來防止頭文件被重復包含,這是標準的寫法
#ifndef __HEADER_H__ #define __HEADER_H__
// code of the file #endif //HEADER_H__
還有一種方法是,這種方法是MS特有的,不適合其他平臺,所以推薦使用第一種方法
#pragma once
// code of the file
解決從VC6轉換到VS2005/VS2008時編譯和鏈接錯誤
有很多幾年前編寫的游戲書籍,其中所附代碼大多是基于VC6的,而我們現在用的大多是VS2005/VS2008/VS2010,所以需要一次轉換,而在轉換中經常會遇到編譯或者鏈接錯誤,解決辦法如下:
編譯錯誤:error C2440: '=' : cannot convert from 'char [14]' to 'LPCWSTR'
這是由于作者默認使用的是函數的A版本,而我們的工程使用了W版本。
解決辦法一:手動將所有函數改為A版本,例如MessageBox -> MessageBoxA, 不推薦這種防范。或者將所有字符串改為寬字符串,即在字符串前加大寫字母L,這個方法比較笨,但是確實好的方法。推薦使用。
解決辦法二:在工程文件上點右鍵-Properties-Configuration Properties-C/C++-Preprocessor-Preprocessor Definitions-點擊右邊的省略號按鈕-在彈出的對話框中將Inherit from parent or project defaults選項去掉即可。這個方法一勞永逸,可以一次性去除所有此類編譯錯誤。但是卻不是好的編程習慣,我們應該盡量使用函數的W版本。
鏈接錯誤:unresolved external symbol xxx
這種錯誤一般都是由于缺少lib文件導致的,只要引用正確的lib文件就可以了
解決辦法:在工程文件上點右鍵-Properties-Configuration Properties-Linker-Input-Additional Dependencies-點擊右邊的小省略號按鈕-將所需的lib文件加入到彈出的對話框中即可。
調試選項的設置
通過注冊表也可以設置D3D的調試選項,這和使用DirectX控制面板是一樣的,而且兩者是相通的,改變一個,另一個也會跟著改變,如下,注冊表位置:
HKLMSoftwareMicrosoftDirect3D
如何求取視線
有很多時候需要用到視線,比如Billboard技術,要求被觀察物體始終面對觀察者,這時候就需要該物體與視線垂直,求取視線很簡單,用觀察點減去眼睛的位置即可
1 D3DXVECTOR3 viewRay = lookAt - eyePt ;
Zoom in/Zoom out效果是如何實現的?
很多圖形軟件都支持該效果,在瀏覽一個model的時候,鼠標滾輪向前滾時(mouse wheel rotate forward, away from the user),model會逐漸變大,謂之zoom in,鼠標滾輪向后滾時(mouse wheel rotate backward, toward the user),model會逐漸變小,謂之zoom out
那么這個效果是如何實現的呢?在我初學DirectX的時候,一直以為用縮放變換來實現。直到研究了DXUT的Camera類以后,才發現,其實有一個更簡單的辦法。通過改變Camera與Model之間的距離來實現,這與現實生活中的感覺是一樣的,當我們離一個物體近的時候,感覺它很大,而當我們漸漸遠離它的時候,它會變得越來越小,此其所以然也!編程的樂趣就在于靈活!
transform小結
Transform(變換)的本質就是從一個坐標系到另一個坐標系的過程 world transform model space -> world space
view transform world space -> view/camera space
projection transform view/camera space -> projection space
transform engine(變換引擎) 以頂點為輸入,對其進行world, view and projection transform, 然后進行剪
裁,將結果傳送給rasterizer進行光柵化
//world transform通過下面的代碼完成 // 將model移動至原點 D3DXMATRIXA16 matWorld ; D3DXMatrixTranslation( &matWorld, 0.0f, 0.0f, 0.0f) ; g_pd3dDevice->SetTransform( D3DTS_WORLD, &matWorld );
//view transform通過下面的代碼完成 D3DXVECTOR3 vEyePt( 0.0f, 0.0f,-10.0f ); //眼睛位置 D3DXVECTOR3 vLookatPt( 0.0f, 0.0f, 0.0f ); //觀察中心 D3DXVECTOR3 vUpVec( 0.0f, 1.0f, 0.0f ); //向上向量 D3DXMATRIXA16 matView; D3DXMatrixLookAtLH( &matView, &vEyePt, &vLookatPt, &vUpVec ); g_pd3dDevice->SetTransform( D3DTS_VIEW, &matView );
projection transform通過下面的代碼完成 D3DXMATRIXA16 matProj; //視角:pi/4,即45度 //縱橫比:1:1 //近剪裁平面:1.0f //遠剪裁平面:1000.0f D3DXMatrixPerspectiveFovLH( &matProj, D3DX_PI/4, 1.0f, 1.0f, 1000.0f ); g_pd3dDevice->SetTransform( D3DTS_PROJECTION, &matProj );
如何區分Point和Vector
在D3D中Point和vector都是用三維坐標表示的,比如,給定 v = [x, y, z], 如何區分這是一個點還是一個向量呢 答案是:在三維坐標下無法區分,于是在最后再加一維,形成齊次坐標 v = [x, y, z, w], 實際應用中w通常取值1 規定如下: w = 1時,v表示點 w = 0時,v表示向量 為什么呢? 先看一下平移變換的矩陣
因為向量是沒有位置屬性的,向量只有長度和方向兩個屬性,即平移一個向量是沒有意義的 所以將向量的第四維w設置為0,阻止平移。 但是點是有位置屬性的,平移一個點也是有意義的,所以將點的w置為1,使之可以平移
如何從一個坐標系變換到另一個坐標系
通常這種需求都是逆向的,因為正向的D3D已經做了 比如對于一個view space中的model,如何把它變換到world space呢?(在D3D頂點處理流水線中world space是view space的前一個過程) 首先我們應該知道world space -> view space 是怎樣一個過程,了解了這個過程后,應用它的逆過程即可 顯然,world space -> view space是通過視圖變換實現的,這個變換矩陣就是view matrix,好了,現在我們需要做的是: 1. 找出這個view矩陣 2. 求出它的逆矩陣 3. 將該逆矩陣應用到要變換的模型 下面以一個點為例,假設我們要將點p(x, y, z) 從view space變換到world space
步驟: 首先求出view matrix D3DXMATRIX view; Device->GetTransform(D3DTS_VIEW, &view);
再求出它的逆矩陣 D3DXMATRIX viewInverse; D3DXMatrixInverse(&viewInverse, 0, &view);
然后將這個矩陣乘到這個點上即可 D3DXVec3TransformCoord(p, p, viewInverse);
注:D3DXVec3TransformCoord是用來變換點的(即w=1的vector) 而 D3DXVec3TransformNormal是用來變換向量的(即w=0的vector)
矩陣連乘的順序
如果需要對同一個圖形連續進行多個變換,那么可以把多個變換矩陣連乘形成一個矩陣。
但矩陣乘法不滿足交換律,所以連乘的順序很重要。
大多數文檔都是以左右順序來區分的,這是不準確的,而且不便于理解和記憶。
這里講一個規則,比較方便
規則:在連乘式中,離被變換頂點近的矩陣對應的變換先進行。
比如,對頂點P(x, y, z, 1)進行如下變換
1. 平移到(1, 1, 1)點,變換矩陣為MT
2. 繞z軸旋轉30度,變換矩陣為 MR
3. 放大2倍,變換矩陣為 MS
那么正確的連成順序是
P * MT * MR * MS (在DirectX文檔里多采用這種寫法)
MT離P最近,那么它是最先進行的變換,MR次之,MS再次。
很多書籍或文檔里面說是按照從左到右的順序連乘,但是別忘了頂點在公式中的位置
如果頂點在右邊,那么就變成了下面的形式
MS * MR * MT * PT (在以OpenGL為基礎的圖形學書籍里多采用這種寫法)
所以準確的說應該是,在連乘式中,先進行的變換離被變換的頂點近。
注意如果P在連乘式右邊,那么應該取其轉置形式,否則無法與矩陣相乘。
為什么變換矩陣是4 x 4的
因為三維的矩陣無法完成某些變換,比如平移變換對應的矩陣如下
這用三維矩陣是無法實現的,類似的還有透視投影變換,所以在D3D中使用4×4矩陣來實現變換,又一個問題來了,由于3D中的頂點都是三維的,三維的vector和四維的matrix是無法相乘的,于是在三維的頂點后面再加上一維,形成齊次坐標,如下
[x, y, z, w]
最后一維通常用w表示,而w通常取值1
這樣就可以用一個1×4的vector與一個4×4matrix相乘了。
[x, y, z, w] *
d3dx9.h
這個頭文件包含了幾乎D3D用到的所有頭文件,如果編譯、鏈接或者運行有問題,可以嘗試包含這個文件試試。
如何查看VertexShader和PixelShader的版本號
如果安裝了DirectX SDK,可以使用其中的工具DirectX Caps Viewer 來查看,這個工具在DirectX的開始菜單中,也在SDK安裝目錄的UtilitiesBinx86(x64)下面
打開這個工具后,依次展開結點DirectX Graphics Adapters-video card name-D3D Device Types-HAL-Caps
注意:一定要選擇HAL分支,這才是你顯卡真正支持的特性,而Reference目錄下存儲的是所有D3D特性,這是用軟件模擬的,速度奇慢,只有在安裝了DirectX SDK的機器上才可用,這就是HAL Device和Reference Device的區別,大家可以查看SDK幫助文件中Device Type一節來獲取詳細的信息
如果沒有安裝DirectX SDK, 可以使用Everest來查看
如何發布游戲(XNA)
看這里吧,比較詳細
點積和叉積
設u和v是兩個三維向量,α是它們之間的夾角
點積
u·v = ux*vx + uy*vy + uz*vz =|u|*|v|*cos(α)
乍一看,幾何意義不十分明顯,注意:當u和v都是單位向量時,點積就是兩個向量夾角的余弦值
D3D中經常用這種方法來求夾角或者旋轉角度,或者頂點法向量與入射光的夾角。
叉積
u×v = [(uyvz - uzvy), (uzvx - uxvz), (uxvy - uyvx)] =|u|*|v|*sin(α)
叉積的結果同時垂直于兩個向量
叉積的結果值是以u和v為鄰邊的平行四邊形的面積,叉積可以用來求多邊形的面積
global variables are implicitly constant, enable compatibility mode to allow modification
編譯Shader程序時遇到以下錯誤
全局變量默認是常量,貌似在Shader程序中不能修改?新版的HLSL編譯器不支持
解決辦法:
將D3DXCompileShaderFromFile的第六個參數改為D3DXSHADER_ENABLE_BACKWARDS_COMPATIBILITY即可
花屏
如果程序執行后,窗口出現花屏現象,那么多半是由于depth buffer沒有clear導致的,比如下面這幅圖就是一個花屏的例子。
解決辦法,在調用Clear函數時,將depth buffer也clear一下即可。原來可能是這樣調用的
g_pd3dDevice->Clear( 0, NULL, D3DCLEAR_TARGET, 0xff00ff00, 1.0, 0 ); //僅僅clear render target
修改后如下
g_pd3dDevice->Clear( 0, NULL, D3DCLEAR_TARGET | D3DCLEAR_ZBUFFER , 0xff00ff00, 1.0, 0 );
需要注意的是,這個Clear函數可能會失敗,比如你在CreateDevice的時候并沒有創建depth buffer,而在Clear函數中卻要Clear depth buffer,所以請檢查你的CreateDevice函數,確保設備確實有一個關聯的depth buffer。
轉自:http://blog.csdn.net/fuli1215/article/details/5961632
DirectX 9.0 學習筆記
DirectX 9.0
1:平面(法向量(normal vector)和平面中一點表示。
2,D3DX庫用如下結構體來表示一個平面類,typedef struct D3DXPLANE{}
3點和平面的空間關系
Direct3D 基礎
一:
1, 初始化Direct3D
2, Direct3d 可以被視作應用程序與圖形設備(3d硬件)的交互中介
3, 一個中間環節 HAL(hardware abstraction layer)硬件抽象層,HAL是一個指示設備完成某些操作的設備的相關代碼集。規范 specification
4, 軟件定點運算???
二:
REF設備:Direct3D提供了參考光柵設備(reference rasterizer device),它能以軟件的方式完全支持direct3d API。
注意:REF設備僅用于開發階段,它與direct SDK捆綁(ship)在一起,無法發布給最終的用戶,此外REF設備的速度十分的緩慢,在測試以外的其他場合都很不實用。
D3DDEVTYPE :HAL 設備用值D3DDEVTYPE_HAL來指定,
1.2 COM(組建對象模型)
COM(Component Object Model)是一項能使Direct 獨立于編程語言并具備向下兼容的技術,可將其視為一個C++類來使用
創建COM接口時不可使用C++關鍵字new ,用完后應該用 Release方法而不是delete,
注意:COM接口都有一個前綴I,
1.3 預備知識
1.3.1 表面
表面主要是Direct3d 用于存儲2D圖像數據的一個像素矩陣。
線性數組(linear array)
跨度(pitch,stride的同義語)用字節表示,
代碼中用 接口 IDirect3DSurface9來描述表面。
獲取(retrieve)
1.3.2 多重采樣
用像素矩陣表示圖像時往往出現塊狀(blocky-looking)效應,多重采樣(multisampling)時一項用于平滑塊狀圖像的技術。
全屏反走樣(full-screen antialiasing)
多重采樣會降低應用程序的運行速度。如果要使用多重采樣技術 ,請務必使用IDirect3D9::CheckDeviceMultiSmpleType 方法來檢查你的設備是否支持多重采樣。
1.3.3 像素格式
1.3.4 內存池(memory Pool)
內存池可以用D3DPOOL 枚舉類型來表示,、
顯存(video memory)
1.3.5 交換鏈和頁面置換
表面集合(collection),交換鏈(Swap Chain),該集合用接口Idirect3DswapChain9 來表示,
置換技術(page Flipping),這兩項技術主要用于生成更加平滑的動畫。
前臺緩存槽(front buffer slot),幀頻(frame rate),后臺緩存(back buffer)
1.3.6 深度緩存
深度緩存(depth buffer)是一個只含有特定像素的深度信息而不含圖像數據的表面。
深度項(entry)。
Z-緩存 (z-buffering)。//前面的物體遮擋后面的物體。
深度緩存的格式決定了深度測試的精度。
1.3.7 定點運算
定點運算(vertex processing) 分為兩種,即:軟件定點運算(software vertex process)或硬件定點運算(hardware vertex proceeding)。無論怎樣 ,軟件定點運算總是被支持,所以總是可以實現的。但我們應該始終優先考慮硬件定點運算的方式,因為速度更快,而且可以不占用cpu資源,也就意味著cpu可以被解放出來進行其他的運算。
注意:圖像卡支持硬件定點運算的另外一種等價的說法是該圖形卡支持變換和光照的硬件計算。
1.3.8 設備性能
bool supporthardware ;
if(caps.DevCaps &D3DDEVCAPS_HWTRANSFORMNDLIGHT)
{
Supporthardware=true;
}
Else
{
Supporthardware=FALSE;
}
注:DevCaps 表示“設備性能 (Device Capabilities)”
1.4 Direct3D 的初始化
初始化的過程可以分為如下的步驟:
1:獲取接口IDirect3d 的指針。獲取物理硬件信息并創建接口IDirect3Ddevice9該接口為一個C++對象。
2:檢查設備性能(D3DCAPS9),判斷主顯卡(primary display adapter)是否支持硬件定點運算。
3:初始化D3DPRESENT_PARAMETERS結構中的一個實例。
4:利用已經初始化的D3DPRESENT_PARAMETERS結構創建IDirect3Ddevice對象。
1.4.1 獲取接口IDirect3D9的指針
IDirect3D9 * _d3d9;
_d3d9=Direct3DCreate9(D3D_SDK_VERSION);
_d3d9對象的用途:設備枚舉(device enumeration)以及創建IDirect3DDevice9類型的對象。
1.4.2 校驗硬件頂點運算
1.4.3填充D3DPRESENT_PARAMETER結構
1.4.4創建IDirect3DDevice接口
HRESULT IDirect3D9::createDevice();
1.5 例程,Direct3D的初始化
InitD3D 函數對應用程序主窗口進行初始化,如果成功,則返回一個已經創建好的IDirect3DDevice接口指針。(這個函數我們可以指定窗口的尺寸,運行模式)。
EnterMsgLoop 該函數封裝了應用程序的消息循環。
1.5.2 例程框架
。。。。bool Setup()例程所需進行的全部設置和初始化都在改函數中進行。如分配資源,檢查設備性能,設置應用程序的狀態。
。。。。。void Cleanup()該函數用于釋放在setUP函數中分配的任何資源。
。。。。。bool Display(float timedelta).實現全部的繪制代碼以及相鄰幀之間應該執行的操作。
1.5.3 例程 D3D初始化
第二章 繪制流水線
繪制流水線(rendering pipeline)
場景(scene)是物體和模型的集合。
任何物體都可以用三角形網格(triangle mesh)逼近表示。三角形網格是構建物體模型的基本單位。多邊形(polygons),圖元(primitives),網格幾何元(mesh geometry),
定點格式:
定點除了包含空間信息外,還包含其他的附加屬性,如顏色屬性,法線(normal)屬性,
靈活定點格式(flexible vertex format fvf),
2.1.2
三角形單元
三角形個定點的指定順序非常重要,我們稱為繞序(winding order),
2.1.3 索引
索引(indices),其原理,我們創建一個定點列表和一個索引列表,定點列表包含了全部獨立定點,索引列表包含了指向定點列表的索引,這些三角形規定了為構建三角形單元,各定點應按何種方式來組織,
定點列表可以這樣來建:
Vertex vertexlist【4】={v0,v1,v2,v3}
接下來,借助索引列表來定義頂點列表中頂點的組織方式以構成兩個三角形單元。
WORD indexList【6】={0,1,2,0,2,3}
2.2 虛擬攝像機
可見的空間體積(volume of space)。視域體(frustum)
丟棄看不見的視域體的這類數據運算過程我們稱為裁剪(clipping)。
投影窗口(projection window)是一個2D區域,
2.3 繪制流水線
一個簡單的繪制流水線
局部坐標系-》世界坐標系-》觀察坐標系-》背面消隱-》光照-》裁剪-》投影-》視口坐標系-》光柵化。
局部坐標系到世界坐標系的變換:
Device-》SetTransform(D3DTS_WORLD,&worldMatrix);
2.3.1 局部坐標系
局部坐標系(local space)又叫建模坐標系(modeling space),是用于定義構成物體三角形單元列表的坐標系,
2.3.2世界坐標系
世界坐標系又叫全局坐標系,一個位于局部坐標系的物體通過一個叫世界變換(world transform)的運算(過程)變換的世界坐標系(world space),該變換通常包括平移(translation),旋轉(rotation)以及比例(scaling)運算,
世界變換用一個矩陣來表示,并由Direct3D通過IDirect3DDevice9::SetTransform方法來加以應用,該方法的第一個參數表示要變換的類型,如 要進行世界變換則要設為:D3DTS_WOLRD,第二個參數表示采用的世界變換矩陣。
2.3.3 觀察坐標系
在世界空間中,集合體和攝像機都是相對世界坐標系定義的,
取景變換(view space transformation),我們稱變換后的集合體位于觀察坐標系(view space)中,
取景變換矩陣(即觀察矩陣)可由如下D3DX函數計算得到:
D3DXMATRIX *D3DXMaTrixLookAtlLH();
例如攝像機位于(5,3,-10),其觀察點位世界坐標系的原點(0,0,0),我們就可以這樣創建取景變換矩陣:
D3DXVECTOR3 position(5.0f,3.0f,-10.0f);
D3DXVECTOR3 targetPoint(0.0f,0.0f,0.0f);
D3DXVECTOR3 worldUp(0.0f,1.0f,0.0f);
D3DXMATRIX V;
D3DXMatrixLookAtLH(&v,&targetPoint,&worldUp);
Device->SetTransform(D3DTS_VIEW,&v);
2.3.4背面消隱
每個多邊形都有兩個側面(sides),我們把其中的一個側面標記為正面(front side),另一個側面標記為背面(back side),通常,多邊形的背面是看不見的,因為場景中的多數物體都是封閉的(enclosed volume),背面消隱(backface culling)。
Direct3D認為頂點排列順序為順時針(觀察坐標系中)的三角形單元是正面朝向的,頂點排列順序為逆時針(觀察坐標系)的三角形單元是背面朝向的。
如果因為某種原因,默認的消隱方式不能滿足用戶的要求,我們可以通過修改繪制狀態(render state)D3DRS_CULLMODE來達到目的。
Device->SetRenderState(D3DRS_CULLMODE,Value);
其中,value的值可以取:
D3DCULL_NONE 完全禁止背面消隱
D3DCULL_CW 只對順時針繞序的三角形單元進行消隱
D3DCULL_CCW 默認值,只對逆時針繞序的三角形單元進行消隱
2.3.5 光照
光源是在世界坐標系中定義的,但必須經過取景變換到觀察坐標系中才可以使用。
固定的流水線(fixed function pipeline)。
2.3.6裁剪
一個三角形單元與視域體的相對位置關系有以下三種:
。。完全在內部
。。完全在外部 將被踢出
。。部分在內(外),
2.3.7 投影
觀察坐標系中,我們的任務是獲取3D場景的2D表示,從N維變換到N-1維的過程稱為投影(projection),
我們只關心透視投影(perspective projection),它可以產生“透視縮短(foreshortening)”的視覺效果,即近大遠小,
投影變換定義了視域體,并負責視域體中的幾何體投影到投影窗口,投影矩陣比較復雜,我們將使用D3DX函數,其功能是依據視域體的描述信息創建一個投影矩陣。
D3DXMATRIX *D3DMatriPerspectiveFovLH(D3DXMATRI &pout,
FLOAT fovy,FLOAT Aspect,FLOAT zn,FLOAT zf);
橫縱比(aspect ratio)參數,投影窗口中的幾何體最終將變換到屏幕顯示區,從方形的投影窗口到矩陣的顯示屏的變換會導致拉伸畸形(stretching distortion),所謂縱橫比就是顯示屏縱橫兩維尺寸的比率,常用于矯正由方形到矩形的映射而引發的畸形。
縱橫比(aspect ratio)=屏幕寬度(screen width)/屏幕高度(screen height)。
Zn 近裁剪面到坐標原點的距離
Zf 遠裁剪面到原點的距離
2.3.8 視口變換
視口變換(viewport transform)的任務就是將頂點坐標從投影窗口轉換到屏幕的一個矩形區域中,該矩形區域成為視口(viewpoint)。
在Direct3D中,視口用結構D3DVIEWPORT9來表示。
Typedef strut D3DVIEWPORT9{
DWORD X,
DWORD Y,
DWORD width,
DWORD height,//前四個變量定義了矩形相對于父窗口的位置和大小
DWORD MINZ,//指定了深度緩存中的最小深度值。
DWORD MAXZ//深度緩存中最大深度值
}D3DVIEWPOER9;
注意,MINZ MAXZ的范圍設定在【0,1】,除非你想最求某中特效,否則其值都應該限制在這個范圍。
實例:
D3DVIEWPORT9 vp={0,0,640,480,0,1};
Device->SetViewport(&vp);
2.3.9 光柵化
光柵化(rasterization)的任務就是為了繪制每個三角形單元,如何計算構成三角形單元的每個像素的顏色值。光柵化的計算量非常大,我們一般要借助專門的圖形卡的加速功能,光柵化的最終結構就是顯示在屏幕上的一幅2D圖像。
小結
在觀察坐標系中,攝像機位于坐標原點,光軸方向與z軸正向一致,物體變換到觀察坐標系后,就可以通過投影映射到投影窗口中,視口變換將投影窗口中的幾何體在映射到視口中,最終在繪制流水線的光柵階段計算出最終顯示的2D圖像的每個像素的顏色值。
第三章 Direct3D中的繪制
3.1 頂點緩存與索引緩存
頂點緩存和索引緩存(vertex/index buffer)是兩類相似的接口。
為什么不用數組呢?因為頂點緩存和索引緩存可以被放置在顯存(video memory)中,進行繪制時,使用顯存中的數據將獲得比使用系統內存中的數據開得多的繪制速度。
在代碼中 ,頂點緩存用接口IDirect3DVertexBuffer9 表示,索引緩存用接口IDirect3DIndexBuffer9表示。
3.1.1
創建頂點緩存和索引緩存
HRESULT IDirect3DDevice9::CreateVertexBuffer(
UINT Length,//為緩存分配的字節數 如果有8個定點,則為 8*sizeof(Vertex)
DWORD Usage,////緩存的一些附加屬性
DWORD FVF,//存儲在定點緩存中頂點的靈活頂點格式
D3DPOOL POOL,//容納緩存的內存池
IDirect3DVretexBuffer9 **ppvertexBuffer,//用于接收所創建的頂點緩存的指針
HANDLE *Psharehandle);//不使用,該值設為0
HRESULT IDirect3DDevice9 ::CreateIndexBuffer(
UINT Length,
DWORD Usage,
D3DFORMAT Format,//指定索引的大小,設為D3DFMT_INDEX16表示16位索引,
IDirect3DIndexBuffer9 **ppIndexBuffer,//用于接收所創建的索引緩存指針
HANDLE psharedhandle);
注意:
創建緩存時,如果未使用D3DUSAGE_DYNAMIC,則所創建的緩存為靜態緩存(static Buffer),我們用靜態緩存來存儲靜態數據(那些不需要經常修改或訪問的數據),例如,地圖和城市建筑的數據,如果使用的動態緩存,動態緩存一般放置在AGP存儲區中,其內容可以迅速的更新,動態緩存的一個最大的優點是更新的速度非常的快,它的一個很常見的例子就是粒子系統,
3.1.2
訪問緩存內容
可以借助方法Lock來獲取指向緩存內容的指針,但訪問完之后,必須對緩存進行解鎖(unlock),
HRESULT IDirect3DVertexBuffer9::Lock(
UINT offsetToLock,從哪里開始鎖定
UINT SizeToLock,//要鎖定的長度(字節數)
BYTE **ppbdata,//指向被鎖定的存儲區位置的指針
DWORD flags//鎖定的方式,
};
3.1.3 獲取頂點緩存和索引緩存的信息
D3DVERTEXBUFFER_DESC vbDescription;
vertexBuffer->GetDesc(&vbDescription);
3.2 繪制狀態
Direct3D封裝了多種繪制狀態(rendering state),這些繪制狀態將影響幾何體的繪制方式。
HRESULT IDirect3DDevice9::SetRenderState(
D3DRENDERSTATETYPE State,
DWORD Value);
3.3 繪制的準備工作
在繪制之前,我們還有三個步驟需要完成:
(1):指定數據流輸入源。將頂點緩存和數據流進行連接。方法
HRESULT IDirect3DDevice9::SetStreamSource(
UINT,//標識與頂點緩存建立連接的數據流,我們總設為0
IDirect3DVertexBuffer9,//指向與給定數據流建立連接的頂點緩存的指針
UINT,//自數據流的起點的一個偏移量。單位為字節,
UIN);//將要連接到數據流的頂點緩存中每個元素的大小,單位為字節
例如:
_device->SetStreamSource(0,vb,0,sizeof(Vertex));
(2),這只頂點格式
_device->SetFVF(D3DFVF_XYZ|D3DFVF_DIFFUSE|D3DFVF_TEX1);
(3),設置索引緩存
_device->SetIndices(_ib);
3.4 使用頂點緩存和索引緩存進行繪制
即使用DrawPrimitive 或DrawIndexedPrimitive方法將待繪制幾何體的信息通過繪制流水線傳輸,這兩個方法從頂點數據流中獲取頂點信息,并從當前設定的索引緩存中提取索引信息。
3.5 D3DX幾何體
D3DX庫提供了如下6個網格創建函數:
。D3DXCreateBox
D3DXCreateSphere
D3DXCreateCylinder
D3DXCreateTeapot
D3DXCreatePolygon
D3DXCreateTorus
上述六個方法都非常的相似,都使用D3DX網格數據結構ID3DXMesh和ID3DXBuffer接口,
3.6 例程,三角形,立方體,茶壺,D3DXCreate*
第四章 顏色
這一章我們學習如何為幾何體添加顏色
4.1 顏色的表示
1.在Direct3D中,顏色用RGB三元組來表示,三種顏色的加性混合(additive mixing)決定了最終的顏色。
2.RGB數據可用兩種不同的格式來保存,第一種是D3DCOLOR,它實際上與DWORD類型完全相同,共有32位,Direct3D提供了D3DCOLOR_ARGB宏幫助我們完成這項工作,如:
D3DCOLOR brightRed=D3DCOLOR_ARGB(255,255,0,0);
D3DCOLOR_XRGB可以代替D3DCOLOR_ARGB;
在Direct3D中,存儲顏色數據的另外一種結構是D3DCOLORVALUE,我們用單精度浮點數來度量每個顏色的亮度值,范圍為0-1,
我們也可以用D3DXCOLOR替代D3DCOLORVALUE,且兩種都有4個浮點類型的成員,這樣我們就可以將其表達成一個4D向量,故其運算與向量完全一樣,在D3DXCOLOR中顏色的乘法表示成各分量的值分別相乘。
4.2 頂點顏色
圖元的顏色有構成該圖元的頂點顏色決定。
Struct colorVertex{
Float x,y,z;
D3DCOLOR color;
Static const DWORD FVF;
}
Const DWORD ColorVertex::FVF=D3DFVF_XYZ|D3DFVF_DIFFUSE;
4.3著色(shading)
目前我們使用兩種著色模式(shading mode),平面著色(flat shading)和Gouraud著色(gourand shading)。
如果使用平面著色模式,則每個圖元的每個像素被一致的賦予該圖元的第一個頂點所指定的顏色。平面著色容易使物體呈現塊狀,這是因為各顏色沒有平滑的過渡,平滑著色(smooth shading),其中各圖元的顏色由個頂點的顏色線性插值得到,
著色模式由Direct3D狀態機(state maching)控制,
Device->SetRenderState(D3DRS_SHARDMODE,D3DSHADE_FLAT或者D3DSHADE_GOURAUD);
4.4 具有顏色的三角形
第五章 光照
光照(lighting)有助于描述實體形狀和立體感。使用光照我們無需自行指定頂點的顏色值,Direct3D會將頂點送入光照計算引擎(light engine),依據光源類型,材質(material)以及物體表面相對于光源的朝向,計算出每個頂點的顏色值,基于某中光照模型計算出的個頂點的顏色,繪制結果更加的逼真。
5.1 光照的組成
在Direct3D的光照模型中,光源發出的光由以下三個分量或三種類型的光組成。
。環境光(Ambient light),反射
。漫射光(Diffuse light),這種類型光沿著特定的方向傳播。當它到達某一表面時,將沿著各個方向均勻反射。漫射光方程(diffuse lighting equation).
.鏡面光(specular light),這種類型的光沿著特定方向傳播,光到達某一表面時,將嚴格的按照另一個方向反射。因為鏡面光的計算量非常大,所以Direct3D為其提供了開關選項,一般都是關了的,如果要開啟,必須:
Device->SetRenderState(D3DRS_SPECULARENALE,true);
每種類型的光都可以用結構D3DCOLORVALUE 或D3DXCOLOR來表示。這些類型描述了光線的顏色,
5.2 材質
材質允許我們定義物體表面對個顏色光的反射比例。材質結構用D3DMATERIAL9來表示。
Typedef struct D3DMATERIAL9{
D3DCOLORVALUE ;//材質對漫射光的反射率
D3DCOLORVALUE;//材質對環境光的反射率
D3DCOLORVALUE;鏡面光的反射率
D3DCOLORVALUE;增強物體的亮度
Float ;鏡面高光點的銳度(sharpness),該值越大,高光點的銳角越大
}D3DMATERIAL9,*LPD3DMATERIAL9;
具體使用例子見書
5.3 頂點法線 是一個描述多邊形朝向的向量。
頂點法線(Vertex normal)正是基于上述思路而產生的,
光照計算是對每個頂點進行的,所以Direct3D需要知道表面再每個頂點處的局部朝向(法線方向),注意頂點法線和表面法線不一定相同,例如近似的求和園。
Struct Vertex{
Float x,y,z;
Float _nx,_ny,_nz;
Static const DWORD FVF;
};
Const DWORD Vertex::FVF=D3DFVF_XYZ|D3DFVF_NORMAL;
注意我們去掉了表示顏色的成員變量,這是因為我們將用光照計算頂點的顏色。
一種更好的計算頂點法向量的方法是計算法向量均值(normal averaging),
注意:在變換的過程中,頂點的法向量可能已經不再規范化,所以,要規范化
Device->SetRenderState(D3DRS_NORMALIZENORMALS,TRUE);
5.4 光源
Direct3D支持三種光源
。點光源(point light),在世界坐標系中有固定的位置
。方向光(directional lights),該光源沒有位置信息,
。聚光燈(spot lights),這種類型的光源與手電筒類似。有位置信息,光線呈錐形(conical shape)。有兩個角度,
在程序代碼中,光源用結構D3DLIGHT9來表示。
5.5 例程 光照
為一個場景添加光照的步驟如下:
(1):啟用光照
(2):為每個物體創建一種材質,并在繪制相應物體前啟用(設置)該材質,
(3):創建一種或多種光源,設置并啟用
(4):啟用所用其余的光照狀態,如鏡面高光(specular highlightings)。
第六章 紋理映射(texture mapping)
借此此技術 ,我們可以將圖像數據映射到三角形單元中,增加立體感,真實感。
在Direct3D中,紋理用接口IDirect3DTexture9表示,紋理類似于一個表面的像素矩陣,
6.1 紋理坐標
紋理元:
為了處理不同尺度的紋理,Direct3D將紋理坐標做了規范化處理,使之限定在區間【0,1】中。
為了實現該映射,必須修改頂點坐標。
增加:float _u,_v;
6.2 創建并啟用紋理
HRESULT D3DXCreateTextureFromFile();
創建紋理
IDirect3DTextrue9 *_stone;
D3DXCreateTextureFromFile(_device,”stone.bmp”,&_stone);
設置當前紋理
Device->SetTexture(0,_stone);
禁用某一層紋理
Device->SetTexture(0,0);
renderObjectWithoutTexture();
6.3紋理過濾器
紋理過濾技術主要處理紋理三角形與屏幕三角形的大小不一樣的問題。
Direct3D提供了3種類型的紋理過濾器(fileter)。紋理過濾方式可以用方法;
Device3DDevice->SetSamplerState來設置。
最近點采樣(nearest point samping)。
線性紋理過濾(linear filtering)
各向異性紋理過濾(anisotropic filtering),但使用此紋理時,必須對D3DSAMP_MAXAISOTROPIC 水平值進行設定,改值越大,圖像效果就越好。請調用IDirect3DDevice9::GetDeviceCaps函數檢驗返回D3DCAPS9結構參數。
Device->SetSamplerstate(0,D3DSAMP_MAXANISOTROPIC,4);
6.4 多級漸進紋理
多級漸進紋理鏈(Chain of mipmap),方法是,有某一紋理創建一系列分辨率逐漸減小的紋理圖像,并且對每種分辨率下的紋理所采用的過濾方式進行定制,以保證保留那些較重要的細節。
6.4.1 多級漸進紋理過濾器
使用Device->SetSamplerState(0,D3DSAMP_MIPFILTER,filter);
Filter可以取不同的值,如:D3DTEXT_NONE,D3DTEXT_POINT,D3DTEXT_LINEAR.
6.4.2 使用多級漸進紋理
6.5 尋址模式
前面說過紋理坐標必須限制在【0,1】中,但這樣是有問題的,Direct3D中定義了4中用來處理紋理坐標值超出【0,1】區間的紋理映射模式,它們分別是:
重復(wrap)尋址模式(address mode),邊界顏色(border color )尋址模式,箝位(clamp)尋址模式,以及鏡像(mirror)尋址模式。
6.6 例程 紋理四邊形
現在來總結一下為場景添加紋理映射的步驟:
(1):構造組成物體的頂點,并為其指定紋理坐標
(2):用函數D3DXCreateTextureFromFile為IDirect3DTexture9接口加載一種紋理
(3):設置縮小過濾器,放大過濾器和多級漸進紋理過濾器
(4):繪制物體前,用函數IDirect3DDevice9::SetTexture來設定與該物體關聯的紋理。
第七章 融合技術
融合(blending)技術,可以得到透明效果
7.1 融合方程
融合:將當前計算得到的像素(源像素)顏色值與先前計算得到的像素(目標)顏色值進行合成的做法稱為融合。
注意:融合的效果并不局限于普通的玻璃類型的透明效果,
在融合運算時,必須遵循以下的原則:
首先繪制的那些物體不需要進行融合的物體,然后將需要進行融合的物體按照相對于攝像機的深度值進行排序,如果物體已處于觀察坐標系中,該運算的效率會相當高,因為此時只需對z分量進行排序,最后,按照自后往前的順序逐個繪制將要融合運算的物體。
源融合因子,目標融合因子,Direct3D中,默認狀態下是禁止融合運算的,可以開啟:
Device->SetRenderState(D3DRS_ALPHABLENDENABLE,true);
注意:融合的計算量并不小,當你繪制完需要進行融合的幾何體之后,應該禁止Alpha融合,在對三角形單元組進行融合的時候,最好進行批處理,之后應立即繪制出來,這樣可以避免在每幀圖像中都啟用和禁止融合運算。
7.2 融合因子
可以通過繪制狀態D3DRS_SRCBLEND,D3DRS_DESTBLEND的值來對源融合因子和目標融合因子分別進行設定。
例如:
Device->SetRenderSate(D3DRS_SRCBLEND,Source);
Device->SetRenderState(D3DRS_DESTBLEND,Destination);
7.3 透明度
頂點顏色中的Alpha分量和材質。
Alpha分量主要用來指定像素的透明度(transparency)。
源融合因子和目標融合因子分別設置為D3DBLEND_SRCALPHA,D3DBLEND_INVSRCALPA.
7.3.1Alpha 通道
我們往往是從紋理的Alpha通道(channel)中獲取Alpha信息。Alpha通道是保留給存儲了Alpha分量的紋理元的一個額外的位集合(bits set)。
7.3.2 指定Alpha來源
如果當前的紋理擁有一個Alpha通道,Alpha值就取自該Alpha通道,如果沒有Alpha通道,Alpha值就取自頂點的顏色。
7.4 用DirectX Textrue Tool創建Alpha通道
DDS文件之專門的一種為DirectX應用程序的紋理設計的圖像格式。可使用D3DXCreateTextrueFromFile函數將DDS文件加載到紋理中,
7.5 例程 透明效果
融合處理的步驟總結如下:
(1):設置融合因子D3DRS_SRCBLEND ,D3DRS_DESTBLEND.
(2):如果使用Alpha分量,還需指定其來源
(3):啟用Alpha融合繪制狀態
第八章 模板
模板緩存(stencil Buffer)。模板緩存是一個用于獲得某種特效(special effect)的離屏(off-screen)緩存,模板緩存的分辨率(resolution)與后臺緩存(back Buffer)和深度緩存(depth Buffer)的分辨率完全相同,所以模板緩存與后臺緩存和深度緩存中的像素是一一對應的,它允許我們動態的,有針對性的決定是否將某個像素寫入后臺緩存中。
8.1 模板緩存的使用
1.詢問硬件是否支持,再啟用 Device->SetRenderState(D3DRS_STENCILENABLE,TRUE/FALSE);
Device->Clear(0,0,D3DCLEAR_TARGET|D3DCLEAR_ZBUFFER|D3DCLEAR_STENCIL,0Xff000000,1.0f,0);
D3DCLEAR_STENCIL表明我們要對模板緩存,目標緩存,深度緩存進行清空操作,第六個值為指定要將模板緩存清為何值。
8.1.1 模板緩存格式的查詢
8.1.2 模板測試
我們可以使用模板緩存對后臺緩存中的某些特定的區域進行繪制,判定是否將某個像素寫入后臺緩存的決策過程稱為模板測試(stencil test),其表達式為:
(ref &mask)ComparisonOperation(value & mask)。
注:模板參考值(ref),模板掩碼(mask)按位運算得到左操作數
返回值為bool型。
8.1.3 模板測試的控制
1.模板參考值
模板參考值(stencil reference value)ref的默認值為0,但可這樣將其改為1
Device->SetRenderState(D3DRS_STENCILREF,0X1);
注意:使用的為16進制數
2.模板掩碼(mask)
模板掩碼用于屏蔽或隱藏ref和value變量中的某些值,例如:
Device->SetRenderState(D3DRS_STENCILMASK,0X0000ffff);
模板值。
我們不能顯式的單獨設置模板的值,但是可以對模板緩存進行清空操作。
下面我們介紹與模板相關的繪制狀態:
比較運算:
我們可以通過繪制狀態D3DRS_STENCILFUNC來設置比較運算(comparison operation),該比較運算函數可取自如下枚舉類型:
Typedef enum D3DCAMPFUNC{
}
8.1.4 模板緩存的更新:
我們可以基于以下3種可能的情形定義模板緩存中的值如何進行更新:
一:第i行,第j列的像素模板測試失敗,我們可以借助繪制狀態D3DRS_STENCILFAIL將模板緩存中處于同樣位置的項更新為如下:
Device->SetRenderState(D3DRS_STENCILFAIL,StencilOperation);
二:第i行,第j列的像素深度測試失敗,這樣設置:
Device->SetRenderState(D3DRS_STENCILZFAIL,StencilOperation);
三:第i行,第j列的像素深度測試和模板測試均成功,我們這樣設置:
Device->SetRenderState(D3DRS_STENCILPASS,StencilOperation);
StencilOperation的值可取以下的常量:
8.1.5 模板寫掩碼
寫掩碼(write mask),該值可以屏蔽我們將寫入模板緩存的任何值的某些位:
可用繪制狀態D3DRS_STENCILWRITEMASK來設定寫掩碼的值,默認為0xffffffff。
下例我們將對高16位進行屏蔽:
Device->SetRenderState(D3DRS_STENCILWRITEMASK,0X0000ffff);
8.2 例程 鏡面效果
8.2.1 成像中的數學問題:
D3DX庫給我們提供了創建相對于任意平面的鏡像變換矩陣:
D3DXMATRIX *D3DXMatrixReflect(
D3DXMATRIX *pout;
CONST D3DXPLANE *pPlane;
);
總結
以上是生活随笔為你收集整理的DirectX 总结和DirectX 9.0 学习笔记的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 最详细的 SAP ABAP Web Se
- 下一篇: C++11标准库chrono库使用