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