DirectX11 With Windows SDK--17 利用几何着色器实现公告板效果
前言
上一章我們知道了如何使用幾何著色器將頂點(diǎn)通過流輸出階段輸出到綁定的頂點(diǎn)緩沖區(qū)。接下來我們繼續(xù)利用它來實(shí)現(xiàn)一些新的效果,在這一章,你將了解:
在此之前需要額外了解的章節(jié)如下:
| 深入理解與使用2D紋理資源(重點(diǎn)閱讀紋理數(shù)組) |
| 15 幾何著色器初探 |
DirectX11 With Windows SDK完整目錄
Github項(xiàng)目源碼
歡迎加入QQ群: 727623616 可以一起探討DX11,以及有什么問題也可以在這里匯報(bào)。
實(shí)現(xiàn)霧效
雖然這部分與幾何著色器并沒有什么關(guān)系,但是霧的效果在該Demo中會(huì)用到,并且前面也沒有講過這部分內(nèi)容,故先在這里提出來。
有時(shí)候我們需要在游戲中模擬一些特定的天氣條件,比如說大霧。它可以讓物體平滑出現(xiàn)而不是突然蹦出來那樣(物體的一部分留在視錐體內(nèi)使得只能看到該部分,然后在逐漸靠近該物體的時(shí)候,該物體就像經(jīng)過了一個(gè)無形的掃描門被逐漸構(gòu)造出來那樣)。通過讓霧在某一范圍內(nèi)具有一定的層次(讓不可見區(qū)域比視錐體裁剪區(qū)域還近),我們可以避免上面所說的情況。但即便是晴朗的天氣,你可能仍希望包含一個(gè)較廣范圍的霧效,即距離達(dá)到很遠(yuǎn)的地方才逐漸看不清物體。
我們可以使用這種方式來實(shí)現(xiàn)霧效:指定霧的顏色,以攝像機(jī)為原點(diǎn)的霧開始的最小距離,霧效范圍值(超過起始距離+霧效范圍值的范圍外的顏色皆被指定的霧色取代)。在需要繪制的三角形內(nèi),某一像素片元的顏色如下:
\(\begin{align} foggedColor &= litColor + s(fogColor - litColor)\\ &= (1-s) \cdot litColor + s \cdot fogColor\\ \end{align}\)
該函數(shù)對(duì)應(yīng)HLSL中的lerp函數(shù),s取0的時(shí)候最終顏色為litColor,然后逐漸增大并逼近1的時(shí)候,最終顏色就逐漸趨近于fogColor。然后參數(shù)s的值取決于下面的函數(shù):
\(s = saturate(\frac{dist(\mathbf{p},\mathbf{E}) - fogStart}{fogRange})\)
\(saturate(x) = \begin{cases} x, 0 \le x \le 1\\ 0, x < 0\\ 1, x > 1\\ \end{cases}\)
其中dist(p,E)指的是兩點(diǎn)之間的距離值。配合下面的圖去理解:
還有注意一點(diǎn),在每次清空重新繪制的時(shí)候,要用霧的顏色進(jìn)行清空。
HLSL代碼
與霧效相關(guān)的值存儲(chǔ)在下面的常量緩沖區(qū)中,并且繪制3D物體的頂點(diǎn)沒有發(fā)生變化:
// Basic.fx // ...cbuffer CBDrawingStates : register(b2) {float4 g_FogColor;int g_FogEnabled;float g_FogStart;float g_FogRange;float g_Pad2; }// ... struct VertexPosNormalTex {float3 PosL : POSITION;float3 NormalL : NORMAL;float2 Tex : TEXCOORD; };struct VertexPosHWNormalTex {float4 PosH : SV_POSITION;float3 PosW : POSITION; // 在世界中的位置float3 NormalW : NORMAL; // 法向量在世界中的方向float2 Tex : TEXCOORD; };Basic_VS.hlsl也與之前一樣,沒有什么變動(dòng):
// Basic_VS.hlsl #include "Basic.hlsli"// 頂點(diǎn)著色器 VertexPosHWNormalTex VS(VertexPosNormalTex vIn) {VertexPosHWNormalTex vOut;matrix viewProj = mul(g_View, g_Proj);vector posW = mul(float4(vIn.PosL, 1.0f), g_World);vOut.PosW = posW.xyz;vOut.PosH = mul(posW, viewProj);vOut.NormalW = mul(vIn.NormalL, (float3x3) g_WorldInvTranspose);vOut.Tex = vIn.Tex;return vOut; }而Basic_PS.hlsl現(xiàn)在使用了4盞方向光以保證4種不同方向的光能夠均勻照射,并添加了霧效部分的處理:
// Basic_PS.hlsl #include "Basic.hlsli"// 像素著色器 float4 PS(VertexPosHWNormalTex pIn) : SV_Target {// 提前進(jìn)行裁剪,對(duì)不符合要求的像素可以避免后續(xù)運(yùn)算float4 texColor = g_Tex.Sample(g_Sam, pIn.Tex);clip(texColor.a - 0.05f);// 標(biāo)準(zhǔn)化法向量pIn.NormalW = normalize(pIn.NormalW);// 求出頂點(diǎn)指向眼睛的向量,以及頂點(diǎn)與眼睛的距離float3 toEyeW = normalize(g_EyePosW - pIn.PosW);float distToEye = distance(g_EyePosW, pIn.PosW);// 初始化為0 float4 ambient = float4(0.0f, 0.0f, 0.0f, 0.0f);float4 diffuse = float4(0.0f, 0.0f, 0.0f, 0.0f);float4 spec = float4(0.0f, 0.0f, 0.0f, 0.0f);float4 A = float4(0.0f, 0.0f, 0.0f, 0.0f);float4 D = float4(0.0f, 0.0f, 0.0f, 0.0f);float4 S = float4(0.0f, 0.0f, 0.0f, 0.0f);[unroll]for (int i = 0; i < 4; ++i){ComputeDirectionalLight(g_Material, g_DirLight[i], pIn.NormalW, toEyeW, A, D, S);ambient += A;diffuse += D;spec += S;}float4 litColor = texColor * (ambient + diffuse) + spec;// 霧效部分[flatten]if (g_FogEnabled){// 限定在0.0f到1.0f范圍float fogLerp = saturate((distToEye - g_FogStart) / g_FogRange);// 根據(jù)霧色和光照顏色進(jìn)行線性插值litColor = lerp(litColor, g_FogColor, fogLerp);}litColor.a = texColor.a * g_Material.Diffuse.a;return litColor; }對(duì)于白天來說,我們可以使用RGBA=(0.75f, 0.75f, 0.75f, 1.0f)來作為霧的顏色。
而對(duì)于黑夜來說,這個(gè)霧效更像是戰(zhàn)爭(zhēng)迷霧的效果,我們使用RGBA=(0.0f, 0.0f, 0.0f, 1.0f)來作為霧的顏色,這樣遠(yuǎn)處的物體我們就讓它看不見,而在可視范圍內(nèi),距離越遠(yuǎn)的物體能見度越低。
具體的演示效果在最后可以看到。
樹的公告板效果
當(dāng)一棵樹離攝像機(jī)太遠(yuǎn)的話,我們可以使用公告板技術(shù),用一張樹的貼圖來進(jìn)行繪制,取代原來繪制3D樹模型的方式。首先我們給出樹的紋理貼圖組成:
關(guān)注Alpha通道部分,白色區(qū)域指代Alpha值為1.0(完全不透明),而黑色區(qū)域指代Alpha值0.0(完全透明)。所以在渲染樹紋理的時(shí)候,我們只需要對(duì)Alpha值為0.0的像素區(qū)域進(jìn)行裁剪即可。
實(shí)現(xiàn)公告板的關(guān)鍵點(diǎn)在于:公告板要永遠(yuǎn)正向攝像機(jī)(即視線要與公告板表面垂直),使得用戶的視線在x0z面上的投影一直與貼圖表面垂直。這樣做省去了大量頂點(diǎn)的輸入和處理,顯得更加高效,并且這個(gè)小技巧還能夠欺騙玩家讓人誤以為還是原來的3D模型(眼尖的玩家還是有可能認(rèn)得出來),只要你別一開始就告訴人家這棵樹的繪制用了公告板原理就行了(→_→)。
現(xiàn)在不考慮坐標(biāo)系的Y軸部分(即從上方俯視),從下面的圖可以看到,公告板投影的中心部分的法向量是直接指向攝像機(jī)的。
因此我們可以得到公告板的u軸, v軸和w軸單位向量以及根據(jù)公告板構(gòu)建的局部坐標(biāo)系:
\(\mathbf{w}=\frac{(E_x-C_x,0,E_z-C_z)}{E_x-C_x,0,E_z-C_z}\)
\(\mathbf{v}=(0,1,0)\)
\(\mathbf{u}=\mathbf{v}\times\mathbf{w}\)
然后已知中心頂點(diǎn)位置、樹寬度和高度,就可以求得2D樹矩形的四個(gè)頂點(diǎn)了:
// 計(jì)算出公告板矩形的四個(gè)頂點(diǎn) // up // v1___|___v3 // | | | // right__|___| | // |__/____| // v0 / v2 // look v[0] = float4(center + halfWidth * right - halfHeight * up, 1.0f); v[1] = float4(center + halfWidth * right + halfHeight * up, 1.0f); v[2] = float4(center - halfWidth * right - halfHeight * up, 1.0f); v[3] = float4(center - halfWidth * right + halfHeight * up, 1.0f);注意上面的加減運(yùn)算是針對(duì)float3進(jìn)行的,然后用1.0f填充成4D向量。并且由于每個(gè)公告板所處的局部坐標(biāo)系不一樣,我們需要對(duì)它們分別計(jì)算出對(duì)應(yīng)的坐標(biāo)軸向量。
若現(xiàn)在我們需要繪制公告板,則在輸入的時(shí)候僅提供對(duì)應(yīng)的中心頂點(diǎn),然后圖元類型選擇D3D11_PRIMITIVE_TOPOLOGY_POINTLIST,在幾何著色階段我們直接將頂點(diǎn)直傳到幾何著色階段,這些頂點(diǎn)傳遞給幾何著色器后就會(huì)解釋成一個(gè)個(gè)矩形(兩個(gè)三角形),產(chǎn)生公告板。
HLSL代碼
下面是Basic.hlsli的完整代碼:
// Basic.hlsli#include "LightHelper.hlsli"Texture2D g_Tex : register(t0); Texture2DArray g_TexArray : register(t1); SamplerState g_Sam : register(s0);cbuffer CBChangesEveryDrawing : register(b0) {matrix g_World;matrix g_WorldInvTranspose;Material g_Material; }cbuffer CBChangesEveryFrame : register(b1) {matrix g_View;float3 g_EyePosW;float g_Pad; }cbuffer CBDrawingStates : register(b2) {float4 g_FogColor;int g_FogEnabled;float g_FogStart;float g_FogRange;float g_Pad2; }cbuffer CBChangesOnResize : register(b3) {matrix g_Proj; }cbuffer CBChangesRarely : register(b4) {DirectionalLight g_DirLight[5];PointLight g_PointLight[5];SpotLight g_SpotLight[5]; }struct VertexPosNormalTex {float3 PosL : POSITION;float3 NormalL : NORMAL;float2 Tex : TEXCOORD; };struct VertexPosHWNormalTex {float4 PosH : SV_POSITION;float3 PosW : POSITION; // 在世界中的位置float3 NormalW : NORMAL; // 法向量在世界中的方向float2 Tex : TEXCOORD; };struct PointSprite {float3 PosW : POSITION;float2 SizeW : SIZE; };struct BillboardVertex {float4 PosH : SV_POSITION;float3 PosW : POSITION;float3 NormalW : NORMAL;float2 Tex : TEXCOORD;uint PrimID : SV_PrimitiveID; };而幾何著色器的代碼如下:
// Billboard_GS.hlsl#include "Basic.hlsli"// 節(jié)省內(nèi)存資源,先用float4向量聲明。 static const float4 g_Vec[2] = { float4(0.0f, 1.0f, 0.0f, 0.0f), float4(1.0f, 1.0f, 1.0f, 0.0f) }; static const float2 g_TexCoord[4] = (float2[4])g_Vec;[maxvertexcount(4)] void GS(point PointSprite input[1], uint primID : SV_PrimitiveID, inout TriangleStream<BillboardVertex> output) {// 計(jì)算公告板所處的局部坐標(biāo)系,其中公告板相當(dāng)于// 被投影在了局部坐標(biāo)系的xy平面,z=0float3 up = float3(0.0f, 1.0f, 0.0f);float3 look = g_EyePosW - input[0].PosW;look.y = 0.0f; // look向量只取投影到xz平面的向量look = normalize(look);float3 right = cross(up, look);// 計(jì)算出公告板矩形的四個(gè)頂點(diǎn)// up// v1 ___|___ v3// | | |// right__|___| |// | / |// |_/_____|// v0 / v2// look float4 v[4];float3 center = input[0].PosW;float halfWidth = 0.5f * input[0].SizeW.x;float halfHeight = 0.5f * input[0].SizeW.y;v[0] = float4(center + halfWidth * right - halfHeight * up, 1.0f);v[1] = float4(center + halfWidth * right + halfHeight * up, 1.0f);v[2] = float4(center - halfWidth * right - halfHeight * up, 1.0f);v[3] = float4(center - halfWidth * right + halfHeight * up, 1.0f);// 對(duì)頂點(diǎn)位置進(jìn)行矩陣變換,并以TriangleStrip形式輸出BillboardVertex gOut;matrix viewProj = mul(g_View, g_Proj);[unroll]for (int i = 0; i < 4; ++i){gOut.PosW = v[i].xyz;gOut.PosH = mul(v[i], viewProj);gOut.NormalW = look;gOut.Tex = g_TexCoord[i];gOut.PrimID = primID;output.Append(gOut);}}首先一開始不用float2數(shù)組是因?yàn)槊總€(gè)float2元素會(huì)單獨(dú)打包,浪費(fèi)了一半的空間,因此這里采取一種特殊的語法形式使得內(nèi)存可以得到充分利用。
然后要注意maxvertexcount的值要設(shè)為4,盡管Append的次數(shù)為4,但實(shí)際上輸出的三角形頂點(diǎn)數(shù)為6。
圖元ID
現(xiàn)在講述系統(tǒng)值SV_PrimitiveID,我們可以將它作為函數(shù)的額外形參進(jìn)行提供。它告訴我們?cè)谳斎胙b配階段下自動(dòng)分配的圖元ID值。當(dāng)我們調(diào)用了一個(gè)draw方法,需要繪制n個(gè)圖元,那么第一個(gè)圖元對(duì)應(yīng)的ID值為0,第二個(gè)為1,直到最后一個(gè)為n-1.當(dāng)前的所有圖元ID僅在當(dāng)前的單次調(diào)用繪制是唯一的。其中該系統(tǒng)值的寫入操作允許在幾何著色器和像素著色器進(jìn)行,而讀取操作則允許在幾何/像素/外殼/域著色器中進(jìn)行。
在上面的例子中,我們將一個(gè)頂點(diǎn)產(chǎn)生的矩形四個(gè)頂點(diǎn)都標(biāo)記為同一個(gè)圖元ID,是因?yàn)榈胶罄m(xù)的像素著色器中,我們用該圖元ID映射到紋理數(shù)組的索引值,來對(duì)應(yīng)到要繪制的樹的紋理。
注意: 如果幾何著色器沒有提供圖元ID,在像素著色器中也可以將它加進(jìn)參數(shù)列表中以使用:
float4 PS(Vertex3DOut pin, uint primID : SV_PrimitiveID) : SV_Target { // Pixel shader body… }但如果像素著色器提供了圖元ID,渲染管線又綁定了幾何著色器,則幾何著色器必須提供該參數(shù)。在幾何著色器中你可以使用或修改圖元ID值。
頂點(diǎn)ID
緊接著是系統(tǒng)值SV_VertexID,在輸入裝配階段的時(shí)候渲染管線就會(huì)為這些輸入的頂點(diǎn)分配頂點(diǎn)ID值。若使用的是Draw方法,則這些頂點(diǎn)將會(huì)按順序從0到n-1被標(biāo)記(n為頂點(diǎn)數(shù)目);若使用的是DrawIndexed方法,則頂點(diǎn)ID對(duì)應(yīng)到的是該頂點(diǎn)所處的索引值。該參數(shù)僅能在頂點(diǎn)著色器的參數(shù)列表中提供:
VertexOut VS(VertexIn vin, uint vertID : SV_VertexID) { // vertex shader body… }最后給出像素著色器的代碼:
// Billboard_PS.hlsl#include "Basic.hlsli"float4 PS(BillboardVertex pIn) : SV_Target {// 每4棵樹一個(gè)循環(huán),盡量保證出現(xiàn)不同的樹float4 texColor = g_TexArray.Sample(g_Sam, float3(pIn.Tex, pIn.PrimID % 4));// 提前進(jìn)行裁剪,對(duì)不符合要求的像素可以避免后續(xù)運(yùn)算clip(texColor.a - 0.05f);// 標(biāo)準(zhǔn)化法向量pIn.NormalW = normalize(pIn.NormalW);// 求出頂點(diǎn)指向眼睛的向量,以及頂點(diǎn)與眼睛的距離float3 toEyeW = normalize(g_EyePosW - pIn.PosW);float distToEye = distance(g_EyePosW, pIn.PosW);// 初始化為0 float4 ambient = float4(0.0f, 0.0f, 0.0f, 0.0f);float4 diffuse = float4(0.0f, 0.0f, 0.0f, 0.0f);float4 spec = float4(0.0f, 0.0f, 0.0f, 0.0f);float4 A = float4(0.0f, 0.0f, 0.0f, 0.0f);float4 D = float4(0.0f, 0.0f, 0.0f, 0.0f);float4 S = float4(0.0f, 0.0f, 0.0f, 0.0f);[unroll]for (int i = 0; i < 4; ++i){ComputeDirectionalLight(g_Material, g_DirLight[i], pIn.NormalW, toEyeW, A, D, S);ambient += A;diffuse += D;spec += S;}float4 litColor = texColor * (ambient + diffuse) + spec;// 霧效部分[flatten]if (g_FogEnabled){// 限定在0.0f到1.0f范圍float fogLerp = saturate((distToEye - g_FogStart) / g_FogRange);// 根據(jù)霧色和光照顏色進(jìn)行線性插值litColor = lerp(litColor, g_FogColor, fogLerp);}litColor.a = texColor.a * g_Material.Diffuse.a;return litColor; }這里加上了剛才的霧效,并使用了紋理數(shù)組。如果你對(duì)紋理數(shù)組這部分內(nèi)容不熟悉的話,請(qǐng)回到開頭閱讀"深入理解與使用2D紋理資源"中的紋理數(shù)組部分
Alpha-To-Coverage
在Demo運(yùn)行的時(shí)候,仔細(xì)觀察可以發(fā)現(xiàn)樹公告板的某些邊緣部分有一些比較突出的黑邊。
這是因?yàn)楫?dāng)前默認(rèn)使用的是Alpha Test,即HLSL中使用clip函數(shù)將Alpha值為0的像素點(diǎn)給剔除掉,這些像素也不是樹的一部分。該函數(shù)決定某一像素是留下還是拋棄,這會(huì)導(dǎo)致不平滑的過渡現(xiàn)象,在攝像機(jī)逐漸靠近該紋理時(shí),圖片本身也在不斷放大,硬邊部分也會(huì)被放大,就像下面那張圖:
當(dāng)然,你也可以使用透明混合的方式,但是透明混合對(duì)繪制的順序是有要求的,要求透明物體按從后到前的順序進(jìn)行繪制,即需要在繪制透明物體前先對(duì)物體按到攝像機(jī)的距離排個(gè)序。當(dāng)然如果需要繪制大量的草叢的話,這種方法所需要的開銷會(huì)變得非常大,操作起來也十分麻煩。
當(dāng)然,我們可以考慮下使用MSAA(多重采樣抗鋸齒),并配合Alpha Test進(jìn)行。MSAA可以用于將多邊形的鋸齒邊緣平滑處理,然后讓Direct3D開啟alpha-to-coverage技術(shù),標(biāo)記邊緣部分。
在創(chuàng)建后備緩沖區(qū)、深度/模板緩沖區(qū)前需要打開4倍多重采樣的支持,目前的項(xiàng)目在d3dApp中已經(jīng)默認(rèn)開啟好了。
然后在之前的例子里,我們已經(jīng)在RenderStates類中預(yù)先創(chuàng)建好了混合狀態(tài):
D3D11_BLEND_DESC blendDesc; ZeroMemory(&blendDesc, sizeof(blendDesc)); auto& rtDesc = blendDesc.RenderTarget[0]; // Alpha-To-Coverage模式 blendDesc.AlphaToCoverageEnable = true; blendDesc.IndependentBlendEnable = false; rtDesc.BlendEnable = false; rtDesc.RenderTargetWriteMask = D3D11_COLOR_WRITE_ENABLE_ALL; HR(device->CreateBlendState(&blendDesc, BSAlphaToCoverage.ReleaseAndGetAddressOf()));然后只需要在需要的時(shí)候綁定該狀態(tài)即可。
BasicEffect的變化
BasicEffect::SetRenderBillboard方法--公告板繪制
該方法要考慮輸入的是一系列頂點(diǎn)圖元:
void BasicEffect::SetRenderBillboard(ID3D11DeviceContext * deviceContext, bool enableAlphaToCoverage) {deviceContext->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_POINTLIST);deviceContext->IASetInputLayout(pImpl->m_pVertexPosSizeLayout.Get());deviceContext->VSSetShader(pImpl->m_pBillboardVS.Get(), nullptr, 0);deviceContext->GSSetShader(pImpl->m_pBillboardGS.Get(), nullptr, 0);deviceContext->RSSetState(RenderStates::RSNoCull.Get());deviceContext->PSSetShader(pImpl->m_pBillboardPS.Get(), nullptr, 0);deviceContext->PSSetSamplers(0, 1, RenderStates::SSLinearWrap.GetAddressOf());deviceContext->OMSetDepthStencilState(nullptr, 0);deviceContext->OMSetBlendState((enableAlphaToCoverage ? RenderStates::BSAlphaToCoverage.Get() : nullptr),nullptr, 0xFFFFFFFF);}參數(shù)enableAlphaToCoverage決定是否要綁定渲染狀態(tài)對(duì)象RenderStates::BSAlphaToCoverage。
GameApp類的變化
GameApp::InitPointSpritesBuffer方法--初始化存放點(diǎn)精靈的緩沖區(qū)
該方法會(huì)生成20個(gè)頂點(diǎn),均勻并略帶隨機(jī)性地環(huán)繞在原點(diǎn)周圍。這些頂點(diǎn)一經(jīng)創(chuàng)建就不可以被修改了,它們將會(huì)被用于公告板的創(chuàng)建:
void GameApp::InitPointSpritesBuffer() {srand((unsigned)time(nullptr));VertexPosSize vertexes[16];float theta = 0.0f;for (int i = 0; i < 16; ++i){// 取20-50的半徑放置隨機(jī)的樹float radius = (float)(rand() % 31 + 20);float randomRad = rand() % 256 / 256.0f * XM_2PI / 16;vertexes[i].pos = XMFLOAT3(radius * cosf(theta + randomRad), 8.0f, radius * sinf(theta + randomRad));vertexes[i].size = XMFLOAT2(30.0f, 30.0f);theta += XM_2PI / 16;}// 設(shè)置頂點(diǎn)緩沖區(qū)描述D3D11_BUFFER_DESC vbd;ZeroMemory(&vbd, sizeof(vbd));vbd.Usage = D3D11_USAGE_IMMUTABLE; // 數(shù)據(jù)不可修改vbd.ByteWidth = sizeof (vertexes);vbd.BindFlags = D3D11_BIND_VERTEX_BUFFER;vbd.CPUAccessFlags = 0;// 新建頂點(diǎn)緩沖區(qū)D3D11_SUBRESOURCE_DATA InitData;ZeroMemory(&InitData, sizeof(InitData));InitData.pSysMem = vertexes;HR(m_pd3dDevice->CreateBuffer(&vbd, &InitData, mPointSpritesBuffer.GetAddressOf())); }GameApp::InitResource方法--初始化資源
該方法集成了所有資源的初始化,注意樹的紋理數(shù)組要提供到輸入槽1,對(duì)應(yīng)紋理寄存器t1的Texture2DArray:
bool GameApp::InitResource() {// ******************// 初始化各種物體//// 初始化樹紋理資源ComPtr<ID3D11Texture2D> test;HR(CreateDDSTexture2DArrayFromFile(m_pd3dDevice.Get(),m_pd3dImmediateContext.Get(),std::vector<std::wstring>{L"Texture\\tree0.dds",L"Texture\\tree1.dds",L"Texture\\tree2.dds",L"Texture\\tree3.dds"},test.GetAddressOf(),mTreeTexArray.GetAddressOf()));m_BasicEffect.SetTextureArray(mTreeTexArray.Get());// 初始化點(diǎn)精靈緩沖區(qū)InitPointSpritesBuffer();// 初始化樹的材質(zhì)m_TreeMat.ambient = XMFLOAT4(0.5f, 0.5f, 0.5f, 1.0f);m_TreeMat.diffuse = XMFLOAT4(1.0f, 1.0f, 1.0f, 1.0f);m_TreeMat.specular = XMFLOAT4(0.2f, 0.2f, 0.2f, 16.0f);ComPtr<ID3D11ShaderResourceView> texture;// 初始化地板m_Ground.SetBuffer(m_pd3dDevice.Get(), Geometry::CreatePlane(XMFLOAT3(0.0f, -5.0f, 0.0f), XMFLOAT2(100.0f, 100.0f), XMFLOAT2(10.0f, 10.0f)));HR(CreateDDSTextureFromFile(m_pd3dDevice.Get(), L"Texture\\Grass.dds", nullptr, texture.GetAddressOf()));m_Ground.SetTexture(texture.Get());Material material{};material.ambient = XMFLOAT4(0.5f, 0.5f, 0.5f, 1.0f);material.diffuse = XMFLOAT4(1.0f, 1.0f, 1.0f, 1.0f);material.specular = XMFLOAT4(0.2f, 0.2f, 0.2f, 16.0f);m_Ground.SetMaterial(material);// ******************// 初始化不會(huì)變化的值//// 方向光DirectionalLight dirLight[4];dirLight[0].ambient = XMFLOAT4(0.1f, 0.1f, 0.1f, 1.0f);dirLight[0].diffuse = XMFLOAT4(0.25f, 0.25f, 0.25f, 1.0f);dirLight[0].specular = XMFLOAT4(0.1f, 0.1f, 0.1f, 1.0f);dirLight[0].direction = XMFLOAT3(-0.577f, -0.577f, 0.577f);dirLight[1] = dirLight[0];dirLight[1].direction = XMFLOAT3(0.577f, -0.577f, 0.577f);dirLight[2] = dirLight[0];dirLight[2].direction = XMFLOAT3(0.577f, -0.577f, -0.577f);dirLight[3] = dirLight[0];dirLight[3].direction = XMFLOAT3(-0.577f, -0.577f, -0.577f);for (int i = 0; i < 4; ++i)m_BasicEffect.SetDirLight(i, dirLight[i]);// ******************// 初始化攝像機(jī)//auto camera = std::shared_ptr<FirstPersonCamera>(new FirstPersonCamera);m_pCamera = camera;camera->SetViewPort(0.0f, 0.0f, (float)m_ClientWidth, (float)m_ClientHeight);camera->SetPosition(XMFLOAT3());camera->SetFrustum(XM_PI / 3, AspectRatio(), 1.0f, 1000.0f);camera->LookTo(XMVectorSet(0.0f, 0.0f, 0.0f, 1.0f),XMVectorSet(0.0f, 0.0f, 1.0f, 1.0f),XMVectorSet(0.0f, 1.0f, 0.0f, 0.0f));camera->UpdateViewMatrix();m_BasicEffect.SetWorldMatrix(XMMatrixIdentity());m_BasicEffect.SetViewMatrix(camera->GetViewXM());m_BasicEffect.SetProjMatrix(camera->GetProjXM());m_BasicEffect.SetEyePos(camera->GetPositionXM());// ******************// 初始化霧效和天氣等//m_BasicEffect.SetFogState(m_FogEnabled);m_BasicEffect.SetFogColor(XMVectorSet(0.75f, 0.75f, 0.75f, 1.0f));m_BasicEffect.SetFogStart(15.0f);m_BasicEffect.SetFogRange(75.0f);return true; }其余方法限于篇幅就不放在這里了,讀者可以查看源碼觀察剩余部分的代碼實(shí)現(xiàn)。現(xiàn)在來看實(shí)現(xiàn)效果吧。
實(shí)現(xiàn)效果
可以觀察到,在與公告版近距離接觸時(shí)可以很明顯地看到公告板在跟著攝像機(jī)旋轉(zhuǎn)。如果距離很遠(yuǎn)的話轉(zhuǎn)動(dòng)的幅度就會(huì)很小,用戶才會(huì)比較難以分辨出遠(yuǎn)處物體是否為公告板或3D模型了。
下面演示了白天和黑夜的霧效
最后則是Alpha-To-Coverage的開啟/關(guān)閉效果對(duì)比
DirectX11 With Windows SDK完整目錄
Github項(xiàng)目源碼
歡迎加入QQ群: 727623616 可以一起探討DX11,以及有什么問題也可以在這里匯報(bào)。
posted on 2019-05-05 09:57 NET未來之路 閱讀(...) 評(píng)論(...) 編輯 收藏轉(zhuǎn)載于:https://www.cnblogs.com/lonelyxmas/p/10811385.html
總結(jié)
以上是生活随笔為你收集整理的DirectX11 With Windows SDK--17 利用几何着色器实现公告板效果的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 有关logistic(sigmoid)函
- 下一篇: Node.js 目录操作