DX11 游戏开发笔记 (二) DX11 基础框架三角形 下
? ?
? ? ? ? ? ? ? ? ? ? ?頂點緩存之幾何三角
?
?
?
老鳥:怎么樣,昨天游戲玩的開心嗎,有沒有秀出你快樂刀妹的操作。
小白:哇,那當然的,最開心的一把我現(xiàn)在還記憶猶新,我們前期劣勢,有一波我們打野“盲僧”
? ? ? ? ? ?Q到對面。W摸眼,一個R踢飛對面四人,我們中單亞索接大,我接R,直接團滅對面,
? ? ? ? ? ?一波推家贏得勝利,我同學的幾何瞎,畫出了一個完美的三角,都可以上起小點了,
? ? ? ? ? ?可惜沒錄下來,不然我一定幫他扣7777777777777。
老鳥:三角?哈哈,看來三角在哪里都是藝術(shù)的存在,DX11的世界就是由三角形組成的,
? ? ? ? ?而且我們用DX寫的第一個圖形實例就是一個三角形。
小白:什么都能被你聯(lián)系到DX,看來我今天又有東西可學了。
老鳥:能學習知識你還不開心,好好聽著。在DX11這座軍營中,最基本的就是點(士兵),
? ? ? ? ? ?而兩個戰(zhàn)士相互配合就成了線,三個士兵(不共線)組合就成了三角形,三角形便能
? ? ? ? ? ?組成無數(shù)多邊形,故DX11最基本的陣型就是“鐵三角”。
?
本次博客借鑒了:https://blog.csdn.net/poem_qianmo/article/details/8276363(淺墨)
? ? ? ? ? ? ? ? ? ? ? ? ? ? ?https://blog.csdn.net/BonChoix/article/details/8300939(BonChoix)
?
?
?
頂點緩存使用四步曲之一:設(shè)計頂點緩存
?
?
?
老鳥:下面我來介紹DX11最基本的頂點(士兵),士兵也分很多類:有步兵、騎兵、弓兵、
傳信兵..., 頂點也分別有不同的形式,好的是頂點格式我們是可以設(shè)計的,也就是一個士兵
最后是什么樣子?是由我們親手打造的;創(chuàng)建自定義靈活頂點格式時,根據(jù)實際的需求,需要定
義一個包含特定頂點信息的結(jié)構(gòu)體,主動權(quán)在我們這我們可以隨心所欲地定義頂點包含的屬性,
比如我們可以定義一個只包含頂點三維坐標和顏色的結(jié)構(gòu)體(士兵模板)。
struct CUSTOMVERTEX {float x, y, z; //頂點的三維坐標值,x,y,zDWORD color; //頂點的顏色值 };在DX11中,我們給它換了一身裝備,讓它變得更炫酷了:
struct CUSTOMVERTEX {XMFLOAT3 pos;XMFLOAT4 color; };ps:{
在DX9中,我們需定義一個D3DFVF_CUSTOMVERTEX (D3DFVF_XYZRHW|D3DFVF_DIFFUSE),
去幫助CPU辨別我們寫的頂點順序,但在DX11中,我們把它用更為快樂的方式替代了,我們把頂點
和像素的大部分處理工作全扔給了GPU,讓CPU更加專心做自己的事。
?
(DX9)需要注意的是,我們在書寫靈活頂點格式的宏定義的時候需要遵守一個順序原則,
順序就是優(yōu)先級需要這樣來分:?頂點坐標位置>RHW值>頂點混合權(quán)重值>
頂點法線向量>漫反射顏色值>鏡面反射顏色值>紋理坐標信息。
具體信息參見:https://blog.csdn.net/poem_qianmo/article/details/8276363(淺墨)。
?
DX11雖然不需要定義順序宏,但我們還是以規(guī)范的順序方式定義頂點,當然你如果想搞個
怪,顛倒順序,只要注意你的shader文件中格式做相對應改變就行。但是這會帶來麻煩,尤其
是你或別人去維護代碼時。}
?
老鳥:DX11中為了方便管理常用顏色,也給他們換了身新衣服放在一起:
//定義幾個常見的顏色值,方便在程序中使用 //這里用到XMVECTORF32結(jié)構(gòu),在定義常量的XMVECTOR時使用, //用它可以在創(chuàng)建變量時直接初始化且可以轉(zhuǎn)化為XMVECTOR。const XMVECTORF32 White = {1.0f, 1.0f, 1.0f, 1.0f}; const XMVECTORF32 Black = {0.0f, 0.0f, 0.0f, 1.0f}; const XMVECTORF32 Green = {0.0f, 1.0f, 0.0f, 1.0f}; const XMVECTORF32 Blue = {0.0f, 0.0f, 1.0f, 1.0f}; const XMVECTORF32 Yellow = {1.0f, 1.0f, 0.0f, 1.0f}; const XMVECTORF32 Cyan = {0.0f, 1.0f, 1.0f, 1.0f}; const XMVECTORF32 Magenta = {1.0f, 0.0f, 1.0f, 1.0f}; const XMVECTORF32 Silver = {0.75f,0.75f,0.75f,1.0f};DX11龍書中采用的也是類似的形式:真是對顏色的偏愛呀:
namespace Colors {XMGLOBALCONST XMVECTORF32 White = { 1.0f, 1.0f, 1.0f, 1.0f };XMGLOBALCONST XMVECTORF32 Black = { 0.0f, 0.0f, 0.0f, 1.0f };XMGLOBALCONST XMVECTORF32 Red = { 1.0f, 0.0f, 0.0f, 1.0f };XMGLOBALCONST XMVECTORF32 Green = { 0.0f, 1.0f, 0.0f, 1.0f };XMGLOBALCONST XMVECTORF32 Blue = { 0.0f, 0.0f, 1.0f, 1.0f };XMGLOBALCONST XMVECTORF32 Yellow = { 1.0f, 1.0f, 0.0f, 1.0f };XMGLOBALCONST XMVECTORF32 Cyan = { 0.0f, 1.0f, 1.0f, 1.0f };XMGLOBALCONST XMVECTORF32 Magenta = { 1.0f, 0.0f, 1.0f, 1.0f };XMGLOBALCONST XMVECTORF32 Silver = { 0.75f, 0.75f, 0.75f, 1.0f };XMGLOBALCONST XMVECTORF32 LightSteelBlue = { 0.69f, 0.77f, 0.87f, 1.0f }; } // 創(chuàng)建頂點緩沖 Vertex vertices[] = {{ XMFLOAT3(-1.0f, -1.0f, -1.0f), (const float*)&Colors::White } }?
?
?
頂點緩存使用四步曲之二:創(chuàng)建頂點緩存
?
?
?
老鳥:上面我們創(chuàng)建了一個具有(頂點坐標+顏色)的士兵模板(頂點),這聽起來有點奇怪,
一個擁有顏色的士兵模板,什么鬼?模板做出來了,我們自然就要生產(chǎn)士兵了,幾個好呢?
今天我們講的是三角形,就試試水,創(chuàng)建三個士兵(頂點):
Vertex vertices[3] = {{XMFLOAT3(-1.0f,0.0f,1.0f),reinterpret_cast<const float*>(&Blue)},{XMFLOAT3(0.0f,2.0f,1.0f),reinterpret_cast<const float*>(&Green)},{XMFLOAT3(1.0f, 0.0f,1.0f),reinterpret_cast<const float*>(&Red)}};老鳥:嗯恩,氣宇軒揚,英姿勃發(fā),是我李云龍的兵!
小白:鬼臉(......);
老鳥:我李云龍(ID3D11Buffer)的兵,扔哪就扎在哪,打哪就響哪,你去打聽打聽,我的兵!
? ? ? ? ??有名聲( D3D11_BUFFER_DESC),我們現(xiàn)在要去前線,
? ? ? ? ? 給我打開戰(zhàn)場(D3D11_SUBRESOURCE_DATA ),
? ? ? ? ? 你給還是不給, 楊書記(D11Device)!
DX11龍書:為了讓 GPU 訪問頂點數(shù)組,我們必須把它放置在一個稱為緩沖(buffer)
的特殊資源容器中,該容器由 ID3D11Buffer 接口表示,
用于存儲頂點的緩沖區(qū)稱為頂點緩沖(vertex buffer)。Direct3D 緩沖不僅可以存儲數(shù)據(jù),
而且還說明了如何訪問數(shù)據(jù)以及數(shù)據(jù)被綁定到圖形管線的那個階段。要創(chuàng)建一個頂點緩沖,
我們必須執(zhí)行以下步驟:
1.填寫一個 D3D11_BUFFER_DESC 結(jié)構(gòu)體,描述我們所要創(chuàng)建的緩沖區(qū)。
2.填寫一個 D3D11_SUBRESOURCE_DATA 結(jié)構(gòu)體,為緩沖區(qū)指定初始化數(shù)據(jù)。
3.調(diào)用 ID3D11Device::CreateBuffer 方法來創(chuàng)建緩沖區(qū)。
?
(1)D3D11_BUFFER_DESC 結(jié)構(gòu)體的定義如下:
typedef struct D3D11_BUFFER_DESC {UINT ByteWidth;D3D11_USAGE Usage;UINT BindFlags;UINT CPUAccessFlags;UINT MiscFlags;UINT StructureByteStride; } D3D11_BUFFER_DESC;ByteWidth:緩存大小,字節(jié)為單位;
?
Usage:對于不變化的頂點、索引緩存,我們設(shè)為為D3D11_USAGE_VERTEX_IMMUTABLE,
? ? ? ? ? ? ?此外,針對CPU對緩存的讀、寫權(quán)限,這個成員有多個類型:
? ? ? ? ? ? ?D3D11_USAGE_DEFAULT:CPU不可讀寫;
? ? ? ? ? ? ?D3D11_USAGE_IMMUTABLE:表示在創(chuàng)建資源后,資源中的內(nèi)容不會改變;
? ? ? ? ? ? ?D3D11_USAGE_DYNAMIC:CPU可讀寫;
? ? ? ? ? ? ?D3D11_USAGE_STAGING:CPU可讀,即可拷貝;
?
BindFlags:針對頂點緩存為D3D11_BIND_VERTEX_BUFFER,
? ? ? ? ? ? ? ? ? ? 針對索引緩存為D3D11_BIND_INDEX_BUFFER;
?
CPUAccessFlags:指定 CPU 對資源的訪問權(quán)限,此處設(shè)為0;
MiscFlags:不需要為頂點緩沖區(qū)指定任何雜項(miscellaneous)標志值,此處設(shè)為0;
StructureByteStride:這個屬性只用于結(jié)構(gòu)化緩沖,處設(shè)為0。
?
(2)D3D11_SUBRESOURCE_DATA 結(jié)構(gòu)體的定義如下:
typedef struct D3D11_SUBRESOURCE_DATA {const void *pSysMem;UINT SysMemPitch;UINT SysMemSlicePitch; } D3D11_SUBRESOURCE_DATA;pSysMem:包含初始化數(shù)據(jù)的系統(tǒng)內(nèi)存數(shù)組的指針。當緩沖區(qū)可以存儲 n 個頂點時,
? ? ? ? ? ? ? ? ? ? 對應的初始化數(shù)組也應至少包含 n 個頂點,從而使整個緩沖區(qū)得到初始化。
SysMemPitch:頂點緩沖區(qū)不使用該參數(shù)。(2D or 3D 紋理)
SysMemSlicePitch:頂點緩沖區(qū)不使用該參數(shù)。(3D 紋理)
?
楊書記(ID3D11Device):給給給,我惹不起。
李云龍:哈哈,你老小子就是爽快,走,兄弟們。
//創(chuàng)建頂點數(shù)據(jù) Vertex vertices[3] = {{ XMFLOAT3(-1.0f, 0.0f, 1.0f), reinterpret_cast<const float*>(&Blue) },{ XMFLOAT3(1.0f, 0.0f, 1.0f), reinterpret_cast<const float*>(&Red) },{ XMFLOAT3(0.0f, 2.0f, 1.0f), reinterpret_cast<const float*>(&Green) }, };//創(chuàng)建描述 D3D11_BUFFER_DESC vbDesc = { 0 }; vbDesc.ByteWidth = 3* sizeof(Vertex); vbDesc.BindFlags = D3D11_BIND_VERTEX_BUFFER; vbDesc.Usage = D3D11_USAGE_DEFAULT;//創(chuàng)建空間 D3D11_SUBRESOURCE_DATA vbData = { 0 }; vbData.pSysMem = vertices;//根描述和空間創(chuàng)建頂點緩存 ID3D11Buffer *g_VB(NULL); hr = g_device->CreateBuffer(&vbDesc, &vbData, &g_VB);?
?
頂點緩存使用四步曲之三:訪問頂點緩存
?
?
前方戰(zhàn)場聯(lián)線:
?楊書記:前方戰(zhàn)士如何?
李云龍:哈哈,你放心,我們這里不是號稱鬼跳地嗎,這里地形復雜,有16條地槽(or32?),
? ? ? ? ? ? ? 我已命令一團躲在0號槽中,只等敵人來到,便打個出其不意,除非二團來替更,不然
? ? ? ? ? ? ? 一團會死守到底。(上面都是老鳥自導自演)
//訪問頂點緩存(綁定至流水線管道) UINT stride = sizeof(Vertex);UINT offset = 0;g_deviceContext->IASetVertexBuffers(0, 1, &g_VB, &stride, &offset);?
老鳥:下面是函數(shù)的解釋:
void ID3D11DeviceContext::IASetVertexBuffers( UINT StartSlot, UINT NumBuffers, ID3D10Buffer *const *ppVertexBuffers, const UINT *pStrides, const UINT *pOffsets);StartSlot:頂點緩沖區(qū)所要綁定的起始輸入槽。一共有 16 個輸入槽,索引依次為 0到 15。
NumBuffers:頂點緩沖區(qū)所要綁定的輸入槽的數(shù)量,如果起始輸入槽為索引 k,我
? ? ? ? 們綁定了 n 個緩沖,那么緩沖將綁定在索引為 ?k,?k+1 ……k+n-1 的輸入槽上。
ppVertexBuffers:指向頂點緩沖區(qū)數(shù)組的第一個元素的指針。
pStrides:指向步長數(shù)組的第一個元素的指針。
pOffsets:指向偏移數(shù)組的第一個元素的指針。
因為 IASetVertexBuffers 方法支持將一個頂點緩沖數(shù)組設(shè)置到不同的輸入槽中,其實是將
一個頂點緩沖按屬性分成不同的部分,以便提高效率。
這里我簡單介紹一下:
struct Vertex {XMFLOAT3 Pos;XMFLOAT4 Color; };struct vertexPos {XMFLOAT4 Color; };struct vertexColor {XMFLOAT4 Color; }; vertexPos vertexPos[] ={{ XMFLOAT3(-1.0f, -1.0f, -1.0f) },{ XMFLOAT3(-1.0f, +1.0f, -1.0f) },{ XMFLOAT3(+1.0f, +1.0f, -1.0f) },{ XMFLOAT3(+1.0f, -1.0f, -1.0f) },{ XMFLOAT3(-1.0f, -1.0f, +1.0f) },{ XMFLOAT3(-1.0f, +1.0f, +1.0f) },{ XMFLOAT3(+1.0f, +1.0f, +1.0f) },{ XMFLOAT3(+1.0f, -1.0f, +1.0f) }};vertexColor vertexColor[] ={{ (const float*)&Colors::White },{ (const float*)&Colors::Black },{ (const float*)&Colors::Red },{ (const float*)&Colors::Green },{ (const float*)&Colors::Blue },{ (const float*)&Colors::Yellow },{ (const float*)&Colors::Cyan },{ (const float*)&Colors::Magenta }};修改相對應的屬性:
D3D11_BUFFER_DESC vbd;ID3D11Buffer *g_VB[2];//頂點坐標vbd.Usage = D3D11_USAGE_IMMUTABLE;vbd.ByteWidth = sizeof(vertexPos) * 8;vbd.BindFlags = D3D11_BIND_VERTEX_BUFFER;vbd.CPUAccessFlags = 0;vbd.MiscFlags = 0;vbd.StructureByteStride = 0;D3D11_SUBRESOURCE_DATA vinitData;vinitData.pSysMem = vertexPos;HR(md3dDevice->CreateBuffer(&vbd, &vinitData, &g_VB[[0]));//頂點顏色 vbd.ByteWidth = sizeof(vertexColor) * 8;vinitData.pSysMem = vertexColor;HR(md3dDevice->CreateBuffer(&vbd, &vinitData, &g_VB[[1])); UINT stride[2] = {sizeof(vertexPos),sizeof(vertexColor)};UINT offset[2] = {0,0};md3dImmediateContext->IASetVertexBuffers(0, 2, mBoxVB, stride, offset);如果我們想讓mBoxVB[0]里的所有頂點完整地裝配到管線里,
而mBoxVB[1]里需要把除第一個頂點之外的其他頂點裝配到管線,
那么pOffsets = {0,1}即可。
?
呼呼,看看美圖休息一下:
?
?
?
?
真想讓小螢草奶我一下,就能元氣滿滿了。
?
?
頂點緩存使用四步曲之四:繪制頂點
?
?
很簡單,調(diào)用函數(shù)就行。(csdn注釋看不清楚,請復制至VS后觀看)
void ID3D11DeviceContext::Draw(UINT VertexCount, UINT StartVertexLocation);//指定圖元拓撲類型 g_deviceContext->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST);//實際使用 //頂點數(shù)量:3 //選擇從哪個頂點繪制:0 g_deviceContext->Draw(3, 0);老鳥:頂點是以一個叫做頂點緩沖區(qū)的 Direct3D 數(shù)據(jù)結(jié)構(gòu)的形式綁定到圖形管線的。頂點緩
沖區(qū)只是在連續(xù)的內(nèi)存中存儲了一個頂點列表。它并沒有說明以何種方式組織頂點,形成幾
何圖元。例如,是應該把頂點緩沖區(qū)中的每兩個頂點解釋為一條直線,還是應該把頂點緩沖
區(qū)中的每三個頂點解釋為一個三角形?我們通過指定圖元拓撲來告訴 Direct3D 以何種方式
組成幾何圖元:
這里我們主講幾個:
(1)點列表(point list)由 D3D11_PRIMITIVE_TOPOLOGY_POINTLIST 標志值表示。
?
(2)線帶(line strip)由 D3D11_PRIMITIVE_TOPOLOGY_LINESTRIP 標志值表示。
?
(3)線列表(line list)由 D3D11_PRIMITIVE_TOPOLOGY_LINELIST 標志值表示。
?
(4)三角形帶(triangle strip)由 D3D11_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP
? ? ? ? ?標志值表示。
?
(5)三角形列表(triangle list)由 D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST
? ? ? ? ?標志值表示。(最常用)
?
(6)通過指定 D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST_ADJ 拓撲標志
? ? ? ? ? 值可以使管線知道如何從頂點緩沖區(qū)中構(gòu)建三角形以及它的鄰接三角形。
?
(7)D3D11_PRIMITIVE_TOPOLOGY_N_CONTROL_POINT_PATCHLIST 拓撲標志表
示將頂點數(shù)據(jù)作為 N 控制點的面片列表,這些點用于(可選)圖形管線的曲面細分階段。
ps:帶 與 列表 的區(qū)別就在其單元圖形是 連結(jié) 還是可分的。
?
?
?
?
頂點緩存使用四步曲之零:頂點的腳踏兩只船(一)
?
上面我們DX11頂點按DX9的CPU方面的實現(xiàn)就大功告成,但是DX11的頂點與時俱進,
她選擇投入GPU的環(huán)抱,這個DX11的新寵兒,
這里我建議您看以下此篇博客,對GPU語言"HLSL"有一個提前的準備:
https://blog.csdn.net/chenjinxian_3D/article/details/51811944(1)
https://blog.csdn.net/chenjinxian_3D/article/details/51813948(2)
因為筆者的知識技術(shù)不到位,講解也許會使您誤解、迷惑,這都是一篇博文不該
有的元素,望君能查看更為專業(yè)的文檔。
HLSL ?聽起來似乎很高端,全名叫高階著色器語言(High Level Shader Language)
實際上有點c++底子的人10分鐘就能掌握個大概,很簡單、很人性化的語言,
與此相對的是 OpenGL ? 的GLSL ? ?,現(xiàn)在如日中天的還有CG(語法跟HLSL相似)
讀者若想了解更多,可自行查閱資料。
?
老鳥:咳咳,還想不想繼續(xù)了,再插嘴我可就罷工了,
? ? ? ? ?小二,上菜,好叻,來了,客官,小心燙嘴:
struct VertexIn {float3 pos : POSITION;float4 color : COLOR; };小白:咦,這不是士兵模板嗎?好像多了個尾巴。
?
老鳥:是的,這就是士兵模板,只不過在HLSL中我們給它貼個小尾巴做標示用;
小尾巴名叫“語意”,顧名思義,就是用來解釋主人的屬性的,便于c++文件傳入相
對應的數(shù)據(jù);VertexIn:頂點輸入,這是一個未經(jīng)人事的小純男。
?
老鳥:有輸入,自然少不了輸出:
struct VertexOut {float4?posH?: SV_POSITION;float4?color?: COLOR; };這就是小純男被玩弄后的狀態(tài),最基本的東西(坐標)已經(jīng)變了,而且還加了一個奇奇怪怪
的float,因為向量的列數(shù)必須和矩陣的行數(shù)相匹配。因為要做變換操作的向量是一個position,
把第4個float值(w分量)指定為1。要是向量表示的是一個direction,w分量應該被設(shè)置為0。
(向量和點表示及其相像,故我們用第四個w分量去區(qū)別他們)
?
小白:啊啊,HLSL這么這個的嗎,快點,我要看小純男被玩弄的那段。
?
老鳥:。。。。。。。。(一臉黑線),頂點在HLSL中變化函數(shù):
VertexOut VS(VertexIn In) {VertexOut Out;vout.posH = mul(float4(In.pos,1.f),g_worldViewProj);Out.color = In.color;return Out; }老鳥:函數(shù)很簡單,VS的名字是VertexShaer的縮寫,你可以按照你自己的喜好改名,
mul:HLSL的內(nèi)置函數(shù),就是乘法。
g_worldViewProj:一個混合矩陣,(不是本節(jié)博客范圍,下節(jié)將會詳細介紹)。
?
老鳥:頂點進行變化了,我們的像素也不能閑著:
float4 PS(VertexOut In):SV_TARGET {return In.color; }小白:等等,頂點我知道,像素是個神馬?
老鳥:啊,怪我,一時疏忽,忘記向你介紹HLSL的兩大成員:
The Vertex Shader ?先生,
The Pixel Shader ? ?女士。
?
The Vertex Shader ?先生 ?:主要是接待C++來賓(其實是c++文件的副本),
幫助它們完成一系列的升級和變化,但是顏色這種細致活,他就做不來了,
但他有個好伉儷:The Pixel Shader ? ?女士。
?
The Pixel Shader ?女士:從先生那里取得任務,開始了她那令人驚嘆的手藝,
其在視覺上的造詣,令人嘆為觀止!當然做完后便通過GPU去展示。
?
老鳥:下面我們看一下其整體的面貌:
struct VertexIn {float3 pos : POSITION;float4 color : COLOR; };struct VertexOut { float4?posH?: SV_POSITION; float4?color?: COLOR; };VertexOut VS(VertexIn In) {VertexOut Out;Out.posH = mul(float4(In.pos, 1.f), g_worldViewProj);Out.color = In.color;return Out; }float4 PS(VertexOut In) :SV_TARGET {return In.color; }老鳥:小白,你看一下,這總覽有什么不妥之處。
小白:嗯恩,g_worldViewProj這個小玩意似乎沒有定義。
老鳥:不錯,看來你已經(jīng)入門了,是的,HLSL現(xiàn)在并不認識g_worldViewProj,
? ? ? ? ? ?所以我們要為它安裝身份:
cbuffer CBufferPerObject {float4x4 g_worldViewProj : WORLDVIEWPROJECTION; }老鳥:WORLDVIEWPROJECTION這個語意是我們自己加的,這樣方便我們改變量名,
CBufferPerObject:每個實例對象都擁有此常量,(出現(xiàn)一個新對象更新一次)。
同理還有:CBufferPerFrame則是指該buffer的數(shù)據(jù)每一幀更新一次(燈光),
? ? ? ? ? ? ? ? ? 允許多個objects使用相同的shader常量進行渲染。
?
? 老鳥 :?DX9的時候,The Vertex Shader ?先生 和The Pixel Shader ?女士是可以分開的,
? ? ? ? ? ? ? ? 當然,現(xiàn)在也可以,為了能讓一個對象能進行不同的特效渲染,我們常常得去
? ? ? ? ? ? ? ? 內(nèi)存中去找不同的特效文件,這給我們帶來了困擾,我們就想能不能把他們放在
? ? ? ? ? ? ? ? ? 一個文件中,一起處理,這就是Effect的來源,DX9時就有了,在DX11中被單獨
? ? ? ? ? ? ? ? ? 提取出來,這不是我們不需要它,而是把他開源,讓用戶更加自由設(shè)計。
ps:如果項目很大,需要將The Vertex Shader和The Pixel Shader分開,也是可以嘗試的,
? ? ? 但文件管理會讓人頭痛。
老鳥:下面我們就看看其是怎么將The Vertex Shader和The Pixel Shader整合的:
technique11 main11 {Pass p0{SetVertexShader(CompileShader(vs_5_0,VS()));SetPixelShader(CompileShader(ps_5_0,PS()));} }老鳥:不可思議,是不是,就是這么簡單,(其實c++文件中會有相對應的改變,
但這種改變是有益的,至少我們不要單獨去記憶頂點和像素的區(qū)別,并小心翼翼的
使用他們,當然使用Effect讓錯誤變得更加不易查找,但這點負擔我們樂于承受。
讀者想知道怎樣分開使用,閱讀DX9龍書即可)。
?
?老鳥:到這里整個HLSL(其實我們更應該叫做FX文件)就寫完了:
cbuffer CBufferPerObject {float4x4 g_worldViewProj : WORLDVIEWPROJECTION; }struct VertexIn {float3 pos : POSITION;float4 color : COLOR; };struct VertexOut { float4?posH?: SV_POSITION; float4?color?: COLOR; };VertexOut VS(VertexIn In) {VertexOut Out;Out.posH = mul(float4(In.pos, 1.f), g_worldViewProj);Out.color = In.color;return Out; }float4 PS(VertexOut In) :SV_TARGET {return In.color; }technique11 main11 {Pass p0{SetVertexShader(CompileShader(vs_5_0, VS()));SetPixelShader(CompileShader(ps_5_0, PS()));} }?
?
?
頂點緩存使用四步曲之零:頂點的腳踏兩只船(二)
?
老鳥:上文我們講完了頂點與GPU的纏綿,而我們的CPU只能在一旁苦看嗎?當然不!
CPU決定插入此事,卻 “ 有心栽花花不成,無心護柳柳成蔭“,反倒成了月下之老,氣哉氣哉!
那么我們就來看看CPU是如何去”幫助我的敵人,痛擊我自己“的:
?
老鳥:我叫CPU,我深愛頂點緩存,可是她最近跟一個叫GPU的小子走的很近,啊呸,
以為從C變成G就能得到頂點緩存妹妹的喜愛嗎?
頂點緩存:啊,我們是不是前世在哪里見過,GPU哥哥。
CPU:啊,不行,我一定要阻止他們,嘿嘿,找到了,作為CPU,我最擅長的便是
找BUG,我要把GPU的缺點全顯示出來:
//編譯Effect的參數(shù)UINT flag(0); #if defined(DEBUG) || defined(_DEBUG)flag |= D3D10_SHADER_DEBUG;flag |= D3D10_SHADER_SKIP_OPTIMIZATION; #endif//兩個ID3D10Blob用來存放編譯好的shader及錯誤消息ID3D10Blob *shader(NULL);ID3D10Blob *errMsg(NULL);//編譯effectHRESULT hr = D3DX11CompileFromFile(L"FX/BasicDraw.fx", 0, 0, 0, "fx_5_0", flag, 0, 0, &shader, &errMsg, 0);//如果有編譯錯誤,顯示之if (errMsg){MessageBoxA(NULL, (char*)errMsg->GetBufferPointer(), "ShaderCompileError", MB_OK);errMsg->Release();return FALSE;}if (FAILED(hr)){MessageBox(NULL, L"CompileShader錯誤!", L"錯誤", MB_OK);return FALSE;}//從編譯好的Effect創(chuàng)建Effecthr = D3DX11CreateEffectFromMemory(shader->GetBufferPointer(), shader->GetBufferSize(), 0, g_device, &g_effect);if (FAILED(hr)){MessageBox(NULL, L"CreateEffectFromMemory錯誤!", L"錯誤", MB_OK);shader->Release();return FALSE;}詳細解釋:
HRESULT D3DX11CompileFromFile ( LPCTSTR pSrcFile , CONST D3D10_SHADE R_MACRO *pDefines, LPD3D10INCLUDE pInclude , LPCSTR pFunctionName , LPCSTR pProfile, UINT Flags 1, UINT Flags 2, ID3DX11ThreadPump *pPump , ID3D10Blob **ppShader, ID3D10Blob **ppErrorMsgs, HRESULT *pHResult);1 .pSrcFile:.fx 文件名,該文件包含了我們所要編譯的效果源代碼。
2 .pDefines:高級選項,我們不使用;請參閱 SDK 文檔。
3 .pInclude:高級選項,我們不使用;請參閱 SDK 文檔。
4 .pFunctionName:著色器入口函數(shù)的名字。只用于單獨編譯著色器程序的情況。當
使用 effect 框架時設(shè)置為 null,這是因為在 effect 文件中已經(jīng)定義了入口點。
5 .pProfile:用于指定著色器版本的字符串。對于 Direct3D 11 來說,我們使用的著色
器版本為 5.0(“fx_5_0”)。
6 .Flags1:用于指定著色器代碼編譯方式的標志值。SDK 文檔列出了很多標志值,但
本書只使用其中的 2 個:
? ?D3D10_SHADER_DEBUG:以調(diào)試模式編譯著色器。
? ?D3D10_SHADER_SKIP_OPTIMIZATION:告訴編譯器不做優(yōu)化處理(用于進行
調(diào)試)。
7 .Flags2:高級選項,我們不使用;請參閱 SDK 文檔。
8 .pPump:指向線程泵的指針,多線程編程時使用,是高級選項,我們不使用;請參
閱 SDK 文檔。本書中這個值都設(shè)為 null。
9 .ppShader:返回一個指向 ID3D10Blob 數(shù)據(jù)對象的指針,這個數(shù)據(jù)對象保存了經(jīng)過
編譯的代碼。
10 .ppErrorMsgs:返回一個指向 ID3D10Blob 數(shù)據(jù)對象的指針,這個數(shù)據(jù)對象存儲了
一個包含錯誤信息的字符串。
11 .pHResult:在使用異步編譯時,用于獲得返回的錯誤代碼。僅當使用 pPump 時才
使用該參數(shù);我們在本書中將該參數(shù)設(shè)為空值。
?
ID3D10Blob 只是一個通用內(nèi)存塊,它有兩個方法:
(a)LPVOID GetBufferPointer:返回指向數(shù)據(jù)的一個 void*,所以在使用時應該對它執(zhí)
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? 行相應的類型轉(zhuǎn)換 (對應下面1)。
(b)SIZE_T GetBufferSize:返回緩沖的大小,以字節(jié)為單位(對應下面2)。
?
HRESULT D3DX11CreateEffectFromMemory( void *pData, SIZE_T DataLength, UINT FXFlags , ID3D11Device *pDevice, ID3DX11Effect **ppEffect);1 .pData:指向編譯好的 effect 數(shù)據(jù)的指針。
2 .DataLength:effect 數(shù)據(jù)的長度,以字節(jié)為單位。
3?.FXFlags:Effect 標識必須與定義在 D3DX11CompileFromFile 方法中的
? ? ? ?Flags2(0) 匹配。
4 .pDevice:指向 Direct3D 11 設(shè)備的指針。
5 .ppEffect:指向創(chuàng)建好的 effect 的指針。
?
CPU:頂點緩存妹妹,你看,這些都是GPU的缺點!
GPU:原諒我,我是一個對待愛情認真的男人,在自己喜歡的女孩面前,我毫無保留,
? ? ? ? ?通過別人之手讓你來了解我,是我的錯誤。
頂點緩存:好真誠,好感動,好man。
CPU:哪里真誠,哪里值得感動了,這都是渣男的通用臺詞好嗎?不行,
? ? ? ? ? ?我不能讓小頂落入他的手中。
?
某天上午,CPU走在小路上,一張被扔在草叢的廢紙引起了他的注意。
CPU:咦,這是什么,”啊,啊,啊,小頂“,后面還有一小串字符:D3DX11_PASS_DESC,
? ? ? ? ? ?署名:GPU。看來這是GPU寫的情書,D3DX11_PASS_DESC這個就是破解情書的
? ? ? ? ? ?關(guān)鍵。
下午時分:
CPU:小頂,你看這是什么:
//開始創(chuàng)建輸入布局:InputLayout//因為這個程序頂點著色器輸入只有兩個變量:頂點坐標(float3)和顏色值(float4),//因此輸入的描述數(shù)組含有兩個成員,分別針對“頂點”和“顏色”D3D11_INPUT_ELEMENT_DESC inputDesc[2] ={//頂點 //語義名 語義名索引 數(shù)據(jù)格式 輸入槽 偏移 輸入類型 此處不用,設(shè)0{ "POSITION", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 0, D3D11_INPUT_PER_VERTEX_DATA, 0 },//顏色{ "COLOR", 0, DXGI_FORMAT_R32G32B32A32_FLOAT, 0, 12, D3D11_INPUT_PER_VERTEX_DATA, 0 }};//創(chuàng)建InputLayout//這里用到了編譯好的shader的輸入信息(InputSignature),可以從Effect相應的Technique中獲取D3DX11_PASS_DESC passDesc = { 0 };ID3D11InputLayout *g_inputLayout(NULL);g_effect->GetTechniqueByName("main10")->GetPassByIndex(0)->GetDesc(&passDesc);hr = g_device->CreateInputLayout(inputDesc, 2, passDesc.pIAInputSignature, passDesc.IAInputSignatureSize, &g_inputLayout);if (FAILED(hr)){MessageBox(NULL, L"CreateInputLayout錯誤!", L"錯誤", MB_OK);return FALSE;}我們上文有提到,我們不再用DX9那種頂點順序宏聲明方式,我們把它改成了
上文的?D3D11_INPUT_ELEMENT_DESC ? inputDesc[2] ,
老鳥: ?CreateInputLayout(inputDesc, 2, passDesc.pIAInputSignature,?
? ? ? ? ? ?passDesc.IAInputSignatureSize, &g_inputLayout);
? ? ? ? ? ?這個函數(shù),小白,你應該猜都能猜出來他的釋義吧。
小白:是的,
(1)D3D11_INPUT_ELEMENT_DESC 結(jié)構(gòu)體數(shù)組指針。
(2)D3D11_INPUT_ELEMENT_DESC 數(shù)組元素個數(shù)。
(3)指向Efftcts中頂點著色器字節(jié)碼的指針
(4 ) ?頂點著色器參數(shù)的字節(jié)碼長度,單位為字節(jié)。??
(5)返回創(chuàng)建后的 ID3D11InputLayout 指針。
?
?老鳥:欣慰狀。
?
好了,我們的CPU該繼續(xù)上場了。? ?? ? ? ? ?
?
?CPU:小頂,GPU居然拍你的生活私密照(inputDesc),居然還上傳分享到Effects,
你看這條評論:這個小頂好清純呀,居然只有頂點坐標和顏色兩個屬性,
真是人間不可多的一朵鮮花呀,我好想蹂躪她。?The Vertex Shader ?先生留。
我就說這樣一個剛剛認識3天19分鐘零23秒的男人怎么信的過,看本性暴露無疑了吧,
啊,呸,渣男!”果然世界上像我這樣長得又帥有純情的美男子真是太少了呀,可惜我已經(jīng)
愛上小頂,不然我一定讓世界女子知道我的風采。“
GPU:抱歉,是我的錯,我從認識你的第一刻開始,便認為你是我的終身,故此我所做的一切
都是把你已經(jīng)當做了我一生的陪伴,我想跟世界傳達我的感受,想讓世界見證你的美,是我用情
太深,怪我,怪我。
小頂:一臉深情與憐惜的望著GPU。
CPU:怎么辦,他說的好有道理,這樣下去,我就徹底沒戲了呀,不行,我一定要
阻止他。
?
兩天后:
一個人走到了CPU的面前,你是不是想揭露GPU的真面目呀,我可以幫你,但你要幫我做三件事。
CPU:思慮萬分,道:什么事?
陌生人:就是這三件:(世界變換,下節(jié)博客詳解,本例只需看著開心就行)
XMMATRIX world=XMMatrixIdentity(); //視角變換XMVECTOR eyePos = XMVectorSet(0.f, 2.f, -5.f, 1.f);XMVECTOR lookAt = XMVectorSet(0.f, 0.f, 0.f, 1.f);XMVECTOR up = XMVectorSet(0.f, 1.f, 0.f, 1.f);XMMATRIX view = XMMatrixLookAtLH(eyePos, lookAt, up);//投影變換XMMATRIX proj = XMMatrixPerspectiveFovLH(XM_PI*0.25f, g_winWidth*1.f / g_winHeight, 1.f, 1000.f);//把三個變換相乘,合成一個XMMATRIX worldViewProj = world*view*proj;這個是我們之間的通信憑證:g_fxWorldViewProj
ID3DX11EffectMatrixVariable *g_fxWorldViewProj(NULL);g_fxWorldViewProj = g_effect->GetVariableBySemantic("WORLDVIEWPROJECTION")->AsMatrix();g_fxWorldViewProj->SetMatrix(reinterpret_cast<float*>(&worldViewProj));你創(chuàng)建好worldViewProj 之后,去g_effect那里給?g_fxWorldViewProj
獲取權(quán)限(跟WORLDVIEWPROJECTION綁定),然后把worldViewProj放進去,
(把C++文件數(shù)據(jù)傳入)。這樣你的工作就完成了,而我也將實現(xiàn)我的諾言。
?
獲取權(quán)限:應在資源初始化時完成。
C++文件數(shù)據(jù)傳入以及三件事:在資源更新里進行,因為要求會發(fā)生改變。
?
簡單來說:通過g_fxWorldViewProj獲取(綁定)HLSL中的常量buffer,
然后在C++中創(chuàng)建對應實體,并通過g_fxWorldViewProj傳入數(shù)據(jù)。
?
CPU:啊,就這點小事,等我1s,做好了,給你。
?
CPU:我叫CPU,萬萬沒想到,我成功散拆了GPU跟小頂,但小頂還是沒跟我在一起,
因為有一個叫FX女人纏上了我。小頂,我還是愛你的,嗚 嗚 嗚。
(FX:陌生人,因cpu創(chuàng)建worldViewProj 一事而愛上cpu,是一個腹黑美少女)
FX:cpu 哥哥,你認真辦事的樣子好帥,我好喜歡。(未完)。
?
源碼鏈接:https://pan.baidu.com/s/1T5JfjCKRdoKgqJjd1rpTMA
?
張愛玲:在這個光怪陸離的人間,沒有誰可以將日子過得行云流水。但我始終相信,走過平湖煙雨,歲月山河,那些歷盡劫數(shù)、嘗遍百味的人,會更加生動而干凈。時間永遠是旁觀者,所有的過程和結(jié)果,都需要我們自己承擔。(淺墨csdn)
?
總結(jié)
以上是生活随笔為你收集整理的DX11 游戏开发笔记 (二) DX11 基础框架三角形 下的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: scram-sha1
- 下一篇: 基于Mac制作iPhone铃声教程,iT