DirectX11 With Windows SDK--12 深度/模板状态、平面镜反射绘制
前言
深度/模板測試使用的是與后備緩沖區(qū)同等分辨率大小的緩沖區(qū),每個元素的一部分連續(xù)位用于深度測試,其余的則用作模板測試。兩個測試的目的都是為了能夠根據(jù)深度/模板狀態(tài)需求的設(shè)置來選擇需要繪制的像素。
DirectX11 With Windows SDK完整目錄
Github項目源碼
歡迎加入QQ群: 727623616 可以一起探討DX11,以及有什么問題也可以在這里匯報。
深度/模板測試
深度測試、模板測試的執(zhí)行是在混合操作之前執(zhí)行的,具體的執(zhí)行順序為:模板測試→深度測試→混合操作。
深度測試
深度測試需要用到深度/模板緩沖區(qū),對每個像素使用24位或32位來映射物體從世界到NDC坐標(biāo)系下z的值(即深度,范圍[0.0, 1.0])。0.0時達(dá)到攝像機的最近可視距離,而1.0時達(dá)到攝像機的最遠(yuǎn)可視距離。若某一像素位置接收到多個像素片元,只有z值最小的像素才會通過最終的深度測試。具體細(xì)化的話,就是現(xiàn)在有一個像素片元,已知它的深度值,然后需要跟深度緩沖區(qū)中的深度值進行比較,若小于深度緩沖區(qū)的深度值,則該像素片元將會覆蓋后備緩沖區(qū)原來的像素片元,并更新深度緩沖區(qū)中對應(yīng)位置的深度值。
模板測試
除了深度測試以為,我們還可以設(shè)定模板測試來阻擋某些特定的區(qū)域的像素通過后備緩沖區(qū)。而且模板測試在操作上自由度會比深度測試大。對于需要進行模板測試的像素,比較式如下:
(StencilRef & StencilReadMask) ? (Value & StencilReadMask)
該表達(dá)式首先括號部分是兩個操作數(shù)進行與運算,然后通過?(用戶指定的運算符)對兩個結(jié)果進行比較。若該表達(dá)式的值為真,則最終通過模板測試,并保留該像素進行后續(xù)的混合操作。
其中StencilReadMask則是應(yīng)用程序所提供的掩碼值。
深度/模板格式
深度/模板緩沖區(qū)是一個2D數(shù)組(紋理),它必須經(jīng)由確定的數(shù)據(jù)格式創(chuàng)建:
ID3D11DeviceContext::ClearDepthStencilView方法–深度/模板緩沖區(qū)內(nèi)容清空
方法原型如下:
void ID3D11DeviceContext::ClearDepthStencilView(ID3D11DepthStencilView *pDepthStencilView, // [In]深度模板視圖UINT ClearFlags, // [In]使用D3D11_CLEAR_FLAG枚舉類型決定需要清空的部分FLOAT Depth, // [In]使用Depth值填充所有元素的深度部分UINT8 Stencil); // [In]使用Stencil值填充所有元素的模板部分其中D3D11_CLEAR_FLAG有如下枚舉值:
| D3D11_CLEAR_DEPTH | 清空深度部分 |
| D3D11_CLEAR_STENCIL | 清空模板部分 |
可以使用位運算或來同時清理。
通常深度值會默認(rèn)設(shè)為1.0以確保任何在攝像機視野范圍內(nèi)的物體都能被顯示出來
模板值則默認(rèn)會設(shè)置為0
ID3D11Device::CreateDepthStencilState方法–創(chuàng)建深度/模板狀態(tài)
要創(chuàng)建深度/模板狀態(tài)ID3D11DepthStencilState之前,首先需要填充D3D11_DEPTH_STENCIL_DESC結(jié)構(gòu)體:
typedef struct D3D11_DEPTH_STENCIL_DESC {BOOL DepthEnable; // 是否開啟深度測試D3D11_DEPTH_WRITE_MASK DepthWriteMask; // 深度值寫入掩碼D3D11_COMPARISON_FUNC DepthFunc; // 深度比較函數(shù)BOOL StencilEnable; // 是否開啟模板測試UINT8 StencilReadMask; // 模板值讀取掩碼UINT8 StencilWriteMask; // 模板值寫入掩碼D3D11_DEPTH_STENCILOP_DESC FrontFace; // 對正面朝向的三角形進行深度/模板操作描述D3D11_DEPTH_STENCILOP_DESC BackFace; // 對背面朝向的三角形進行深度/模板操作的描述 } D3D11_DEPTH_STENCIL_DESC;深度狀態(tài)設(shè)定
| D3D11_DEPTH_WRITE_MASK_ZERO | 不寫入深度/模板緩沖區(qū) |
| D3D11_DEPTH_WRITE_MASK_ALL | 允許寫入深度/模板緩沖區(qū) |
但即便設(shè)置了D3D11_DEPTH_WRITE_MASK_ZERO,如果DepthEnable開著的話仍會取原來的深度值進行深度比較,只是不會更新深度緩沖區(qū)。
枚舉類型D3D11_COMPARISON_FUNC的枚舉值如下:
| D3D11_COMPARISON_NEVER = 1 | 該比較函數(shù)一定返回false |
| D3D11_COMPARISON_LESS = 2 | 使用<來替換? |
| D3D11_COMPARISON_EQUAL = 3 | 使用==來替換? |
| D3D11_COMPARISON_LESS_EQUAL = 4 | 使用<=來替換? |
| D3D11_COMPARISON_GREATER = 5 | 使用>來替換? |
| D3D11_COMPARISON_NOT_EQUAL = 6 | 使用!=來替換? |
| D3D11_COMPARISON_GREATER_EQUAL = 7 | 使用>=來替換? |
| D3D11_COMPARISON_ALWAYS = 8 | 該比較函數(shù)一定返回true |
默認(rèn)情況下,深度狀態(tài)的值如下:
DepthEnable = TRUE; DepthWriteMask = D3D11_DEPTH_WRITE_MASK_ALL DepthFunc = D3D11_COMPARISION_LESS模板狀態(tài)設(shè)定
#define D3D11_DEFAULT_STENCIL_READ_MASK (0xff)
#define D3D11_DEFAULT_STENCIL_WRITE_MASK (0xff)
深度/模板操作描述結(jié)構(gòu)體如下:
typedefstruct D3D11_DEPTH_STENCILOP_DESC {D3D11_STENCIL_OP StencilFailOp; D3D11_STENCIL_OP StencilDepthFailOp; D3D11_STENCIL_OP StencilPassOp; D3D11_COMPARISON_FUNC StencilFunc; } D3D11_DEPTH_STENCILOP_DESC;枚舉類型D3D11_STENCIL_OP的枚舉值如下:
| D3D11_STENCIL_OP_KEEP | 保持目標(biāo)模板值不變 |
| D3D11_STENCIL_OP_ZERO | 保持目標(biāo)模板值為0 |
| D3D11_STENCIL_OP_REPLACE | 使用StencilRef的值替換模板模板值 |
| D3D11_STENCIL_OP_INCR_SAT | 對目標(biāo)模板值加1,超過255的話將值保持在255 |
| D3D11_STENCIL_OP_DECR_SAT | 對目標(biāo)模板值減1,低于0的話將保持在0 |
| D3D11_STENCIL_OP_INVERT | 對目標(biāo)模板值的每個位進行翻轉(zhuǎn) |
| D3D11_STENCIL_OP_INCR | 對目標(biāo)模板值加1,超過255的話值將上溢變成0 |
| D3D11_STENCIL_OP_DECR | 對目標(biāo)模板值減1,低于0的話將下溢變成255 |
默認(rèn)情況下,模板狀態(tài)的值如下:
StencilEnable = FALSE; StencilReadMask = D3D11_DEFAULT_STENCIL_READ_MASK; StencilWriteMask = D3D11_DEFAULT_STENCIL_WRITE_MASK;FrontFace.StencilFunc = D3D11_COMPARISON_ALWAYS; FrontFace.StencilDepthFailOp = D3D11_STENCIL_OP_KEEP; FrontFace.StencilPassOp = D3D11_STENCIL_OP_KEEP; FrontFace.StencilFailOp = D3D11_STENCIL_OP_KEEP;BackFace.StencilFunc = D3D11_COMPARISON_ALWAYS; BackFace.StencilDepthFailOp = D3D11_STENCIL_OP_KEEP; BackFace.StencilPassOp = D3D11_STENCIL_OP_KEEP; BackFace.StencilFailOp = D3D11_STENCIL_OP_KEEP;填充完上面一堆結(jié)構(gòu)體信息后,就終于可以創(chuàng)建深度模板狀態(tài)了:
HRESULT ID3D11Device::CreateDepthStencilState(const D3D11_DEPTH_STENCIL_DESC *pDepthStencilDesc, // [In]深度/模板狀態(tài)描述ID3D11DepthStencilState **ppDepthStencilState // [Out]輸出深度/模板狀態(tài) );ID3D11DeviceContext::OMSetDepthStencilState方法–輸出合并階段設(shè)置深度/模板狀態(tài)
創(chuàng)建好深度/模板狀態(tài)后,我們就可以將它綁定到渲染管線上:
void ID3D11DeviceContext::OMSetDepthStencilState(ID3D11DepthStencilState *pDepthStencilState, // [In]深度/模板狀態(tài),使用nullptr的話則是默認(rèn)深度/模板狀態(tài)UINT StencilRef); // [In]提供的模板值如果要恢復(fù)到默認(rèn)狀況,可以這樣調(diào)用:
md3dImmediateContext->OMSetDepthStencilState(nullptr, 0);利用模板測試?yán)L制平面鏡
要實現(xiàn)鏡面反射的效果,我們需要解決兩個問題:
若一個有平面鏡的場景中包含透明和非透明物體,則實際的繪制順序為:
在3D場景中,要繪制鏡面反射的物體,我們只需要將原本的物體(所有頂點位置)進行鏡面反射矩陣的變換即可得到。但是反射的物體僅可以在物體一側(cè)透過鏡面看到,在鏡面的另一邊是無法看到反射的物體的。通過模板測試,我們可以在攝像機僅與鏡面同側(cè)的時候標(biāo)定鏡面區(qū)域,并繪制鏡面反射的物體。
我們可以使用XMMatrixReflection函數(shù)來創(chuàng)建反射矩陣,提供的參數(shù)為平面向量\((\mathbf{n} ,d)\)
這里簡單了解一下,平面可以表示為點法式:
\[\mathbf{n} \cdot \mathbf{p} + d = 0\]
n為平面法向量,p為平面一點,進行叉乘運算。
d是一個有向距離值
上面的式子展開后就是我們高數(shù)見到的平面方程:
\[Ax + By + Cz + D = 0\]
這相當(dāng)于我
例如(0.0f, 0.0f, -1.0f, 10.0f)可以表示z = 10的平面
HLSL代碼的變化
在Basic.hlsli中,添加了一個常量緩沖區(qū)用來控制反射開關(guān),它的更新頻率僅次于每次繪制更新的緩沖區(qū)。并且由于鏡面是固定的,這里將鏡面反射矩陣放在不會變化的常量緩沖區(qū)上:
cbuffer CBChangesEveryDrawing : register(b0) {matrix g_World;matrix g_WorldInvTranspose;Material g_Material; }cbuffer CBDrawingStates : register(b1) {int g_IsReflection;float3 g_Pad1; }cbuffer CBChangesEveryFrame : register(b2) {matrix g_View;float3 g_EyePosW; }cbuffer CBChangesOnResize : register(b3) {matrix g_Proj; }cbuffer CBChangesRarely : register(b4) {matrix g_Reflection;DirectionalLight g_DirLight[10];PointLight g_PointLight[10];SpotLight g_SpotLight[10];int g_NumDirLight;int g_NumPointLight;int g_NumSpotLight;float g_Pad2; }所以現(xiàn)在目前已經(jīng)使用了5個常量緩沖區(qū),可以說在管理上會非常復(fù)雜,其中頂點著色器需要用到所有的常量緩沖區(qū),而像素著色器需要用到除了CBChangesOnResize外的所有常量緩沖區(qū)。
然后3D頂點著色器添加了是否需要乘上反射矩陣的判定:
// Basic_VS_3D.hlsl #include "Basic.hlsli"// 頂點著色器(3D) VertexPosHWNormalTex VS_3D(VertexPosNormalTex vIn) {VertexPosHWNormalTex vOut;matrix viewProj = mul(g_View, g_Proj);float4 posW = mul(float4(vIn.PosL, 1.0f), g_World);float3 normalW = mul(vIn.NormalL, (float3x3) g_WorldInvTranspose);// 若當(dāng)前在繪制反射物體,先進行反射操作[flatten]if (g_IsReflection){posW = mul(posW, g_Reflection);normalW = mul(normalW, (float3x3) g_Reflection);}vOut.PosH = mul(posW, viewProj);vOut.PosW = posW.xyz;vOut.NormalW = normalW;vOut.Tex = vIn.Tex;return vOut; }對于像素著色器來說,由于點光燈和聚光燈都可以看做是物體,所以也應(yīng)該進行鏡面反射矩陣變換(主要反射光的方向和位置):
// Basic_PS_3D.hlsl #include "Basic.hlsli"// 像素著色器(3D) float4 PS_3D(VertexPosHWNormalTex pIn) : SV_Target {// 提前進行裁剪,對不符合要求的像素可以避免后續(xù)運算float4 texColor = g_Tex.Sample(g_SamLinear, pIn.Tex);clip(texColor.a - 0.1f);// 標(biāo)準(zhǔn)化法向量pIn.NormalW = normalize(pIn.NormalW);// 頂點指向眼睛的向量float3 toEyeW = normalize(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);int i;[unroll]for (i = 0; i < 5; ++i){DirectionalLight dirLight = g_DirLight[i];[flatten]if (g_IsReflection){dirLight.Direction = mul(dirLight.Direction, (float3x3) (g_Reflection));}ComputeDirectionalLight(g_Material, g_DirLight[i], pIn.NormalW, toEyeW, A, D, S);ambient += A;diffuse += D;spec += S;}// 若當(dāng)前在繪制反射物體,需要對光照進行反射矩陣變換PointLight pointLight;[unroll]for (i = 0; i < 5; ++i){pointLight = g_PointLight[i];[flatten]if (g_IsReflection){pointLight.Position = (float3) mul(float4(pointLight.Position, 1.0f), g_Reflection);}ComputePointLight(g_Material, pointLight, pIn.PosW, pIn.NormalW, toEyeW, A, D, S);ambient += A;diffuse += D;spec += S;}SpotLight spotLight;// 若當(dāng)前在繪制反射物體,需要對光照進行反射矩陣變換[unroll]for (i = 0; i < 5; ++i){spotLight = g_SpotLight[i];[flatten]if (g_IsReflection){spotLight.Position = (float3) mul(float4(spotLight.Position, 1.0f), g_Reflection);spotLight.Direction = mul(spotLight.Direction, (float3x3) g_Reflection);}ComputeSpotLight(g_Material, spotLight, pIn.PosW, pIn.NormalW, toEyeW, A, D, S);ambient += A;diffuse += D;spec += S;}float4 litColor = texColor * (ambient + diffuse) + spec;litColor.a = texColor.a * g_Material.Diffuse.a;return litColor; }RenderStates類的變化
RenderStates類變化如下:
class RenderStates { public:template <class T>using ComPtr = Microsoft::WRL::ComPtr<T>;static void InitAll(ID3D11Device * device);// 使用ComPtr無需手工釋放public:static ComPtr<ID3D11RasterizerState> RSWireframe; // 光柵化器狀態(tài):線框模式static ComPtr<ID3D11RasterizerState> RSNoCull; // 光柵化器狀態(tài):無背面裁剪模式static ComPtr<ID3D11RasterizerState> RSCullClockWise; // 光柵化器狀態(tài):順時針裁剪模式static ComPtr<ID3D11SamplerState> SSLinear; // 采樣器狀態(tài):線性過濾static ComPtr<ID3D11SamplerState> SSAnistropic; // 采樣器狀態(tài):各項異性過濾static ComPtr<ID3D11BlendState> BSNoColorWrite; // 混合狀態(tài):不寫入顏色static ComPtr<ID3D11BlendState> BSTransparent; // 混合狀態(tài):透明混合static ComPtr<ID3D11BlendState> BSAlphaToCoverage; // 混合狀態(tài):Alpha-To-Coveragestatic ComPtr<ID3D11DepthStencilState> DSSMarkMirror; // 深度/模板狀態(tài):標(biāo)記鏡面區(qū)域static ComPtr<ID3D11DepthStencilState> DSSDrawReflection; // 深度/模板狀態(tài):繪制反射區(qū)域static ComPtr<ID3D11DepthStencilState> DSSNoDoubleBlend; // 深度/模板狀態(tài):無二次混合區(qū)域static ComPtr<ID3D11DepthStencilState> DSSNoDepthTest; // 深度/模板狀態(tài):關(guān)閉深度測試static ComPtr<ID3D11DepthStencilState> DSSNoDepthWrite; // 深度/模板狀態(tài):僅深度測試,不寫入深度值 };新增的渲染狀態(tài)的定義如下:
void RenderStates::InitAll(ID3D11Device * device) {// 先前初始化過的話就沒必要重來了if (IsInit())return;// ***********初始化光柵化器狀態(tài)***********D3D11_RASTERIZER_DESC rasterizerDesc;ZeroMemory(&rasterizerDesc, sizeof(rasterizerDesc));// ...// 順時針剔除模式rasterizerDesc.FillMode = D3D11_FILL_SOLID;rasterizerDesc.CullMode = D3D11_CULL_BACK;rasterizerDesc.FrontCounterClockwise = true;rasterizerDesc.DepthClipEnable = true;HR(device->CreateRasterizerState(&rasterizerDesc, &RSCullClockWise));// ***********初始化采樣器狀態(tài)***********// ...// ***********初始化混合狀態(tài)***********// ...// ***********初始化深度/模板狀態(tài)***********D3D11_DEPTH_STENCIL_DESC dsDesc;// 鏡面標(biāo)記深度/模板狀態(tài)// 這里不寫入深度信息// 無論是正面還是背面,原來指定的區(qū)域的模板值都會被寫入StencilRefdsDesc.DepthEnable = true;dsDesc.DepthWriteMask = D3D11_DEPTH_WRITE_MASK_ZERO;dsDesc.DepthFunc = D3D11_COMPARISON_LESS;dsDesc.StencilEnable = true;dsDesc.StencilReadMask = D3D11_DEFAULT_STENCIL_READ_MASK;dsDesc.StencilWriteMask = D3D11_DEFAULT_STENCIL_WRITE_MASK;dsDesc.FrontFace.StencilFailOp = D3D11_STENCIL_OP_KEEP;dsDesc.FrontFace.StencilDepthFailOp = D3D11_STENCIL_OP_KEEP;dsDesc.FrontFace.StencilPassOp = D3D11_STENCIL_OP_REPLACE;dsDesc.FrontFace.StencilFunc = D3D11_COMPARISON_ALWAYS;// 對于背面的幾何體我們是不進行渲染的,所以這里的設(shè)置無關(guān)緊要dsDesc.BackFace.StencilFailOp = D3D11_STENCIL_OP_KEEP;dsDesc.BackFace.StencilDepthFailOp = D3D11_STENCIL_OP_KEEP;dsDesc.BackFace.StencilPassOp = D3D11_STENCIL_OP_REPLACE;dsDesc.BackFace.StencilFunc = D3D11_COMPARISON_ALWAYS;HR(device->CreateDepthStencilState(&dsDesc, DSSMarkMirror.GetAddressOf()));// 反射繪制深度/模板狀態(tài)// 由于要繪制反射鏡面,需要更新深度// 僅當(dāng)鏡面標(biāo)記模板值和當(dāng)前設(shè)置模板值相等時才會進行繪制dsDesc.DepthEnable = true;dsDesc.DepthWriteMask = D3D11_DEPTH_WRITE_MASK_ALL;dsDesc.DepthFunc = D3D11_COMPARISON_LESS;dsDesc.StencilEnable = true;dsDesc.StencilReadMask = D3D11_DEFAULT_STENCIL_READ_MASK;dsDesc.StencilWriteMask = D3D11_DEFAULT_STENCIL_WRITE_MASK;dsDesc.FrontFace.StencilFailOp = D3D11_STENCIL_OP_KEEP;dsDesc.FrontFace.StencilDepthFailOp = D3D11_STENCIL_OP_KEEP;dsDesc.FrontFace.StencilPassOp = D3D11_STENCIL_OP_KEEP;dsDesc.FrontFace.StencilFunc = D3D11_COMPARISON_EQUAL;// 對于背面的幾何體我們是不進行渲染的,所以這里的設(shè)置無關(guān)緊要dsDesc.BackFace.StencilFailOp = D3D11_STENCIL_OP_KEEP;dsDesc.BackFace.StencilDepthFailOp = D3D11_STENCIL_OP_KEEP;dsDesc.BackFace.StencilPassOp = D3D11_STENCIL_OP_KEEP;dsDesc.BackFace.StencilFunc = D3D11_COMPARISON_EQUAL;HR(device->CreateDepthStencilState(&dsDesc, DSSDrawReflection.GetAddressOf()));// 無二次混合深度/模板狀態(tài)// 允許默認(rèn)深度測試// 通過自遞增使得原來StencilRef的值只能使用一次,實現(xiàn)僅一次混合dsDesc.DepthEnable = true;dsDesc.DepthWriteMask = D3D11_DEPTH_WRITE_MASK_ALL;dsDesc.DepthFunc = D3D11_COMPARISON_LESS;dsDesc.StencilEnable = true;dsDesc.StencilReadMask = D3D11_DEFAULT_STENCIL_READ_MASK;dsDesc.StencilWriteMask = D3D11_DEFAULT_STENCIL_WRITE_MASK;dsDesc.FrontFace.StencilFailOp = D3D11_STENCIL_OP_KEEP;dsDesc.FrontFace.StencilDepthFailOp = D3D11_STENCIL_OP_KEEP;dsDesc.FrontFace.StencilPassOp = D3D11_STENCIL_OP_INCR;dsDesc.FrontFace.StencilFunc = D3D11_COMPARISON_EQUAL;// 對于背面的幾何體我們是不進行渲染的,所以這里的設(shè)置無關(guān)緊要dsDesc.BackFace.StencilFailOp = D3D11_STENCIL_OP_KEEP;dsDesc.BackFace.StencilDepthFailOp = D3D11_STENCIL_OP_KEEP;dsDesc.BackFace.StencilPassOp = D3D11_STENCIL_OP_INCR;dsDesc.BackFace.StencilFunc = D3D11_COMPARISON_EQUAL;HR(device->CreateDepthStencilState(&dsDesc, DSSNoDoubleBlend.GetAddressOf()));// 關(guān)閉深度測試的深度/模板狀態(tài)// 若繪制非透明物體,務(wù)必嚴(yán)格按照繪制順序// 繪制透明物體則不需要擔(dān)心繪制順序// 而默認(rèn)情況下模板測試就是關(guān)閉的dsDesc.DepthEnable = false;dsDesc.StencilEnable = false;HR(device->CreateDepthStencilState(&dsDesc, DSSNoDepthTest.GetAddressOf()));// 進行深度測試,但不寫入深度值的狀態(tài)// 若繪制非透明物體時,應(yīng)使用默認(rèn)狀態(tài)// 繪制透明物體時,使用該狀態(tài)可以有效確保混合狀態(tài)的進行// 并且確保較前的非透明物體可以阻擋較后的一切物體dsDesc.DepthEnable = true;dsDesc.DepthWriteMask = D3D11_DEPTH_WRITE_MASK_ZERO;dsDesc.DepthFunc = D3D11_COMPARISON_LESS;dsDesc.StencilEnable = false;HR(device->CreateDepthStencilState(&dsDesc, DSSNoDepthWrite.GetAddressOf()));}場景繪制
現(xiàn)在場景內(nèi)有四面墻,一個平面鏡,一面地板,一個籬笆盒和水面。
開始繪制前,我們需要清空深度/模板緩沖區(qū)和渲染目標(biāo)視圖:
md3dImmediateContext->ClearRenderTargetView(mRenderTargetView.Get(), reinterpret_cast<const float*>(&Colors::Black)); md3dImmediateContext->ClearDepthStencilView(mDepthStencilView.Get(), D3D11_CLEAR_DEPTH | D3D11_CLEAR_STENCIL, 1.0f, 0);第1步: 鏡面區(qū)域?qū)懭肽0寰彌_區(qū)
這一步通過對鏡面所在區(qū)域?qū)懭肽0逯?來標(biāo)定鏡面繪制區(qū)域。
// ********************* // 1. 給鏡面反射區(qū)域?qū)懭胫?到模板緩沖區(qū) // // 裁剪掉背面三角形 // 標(biāo)記鏡面區(qū)域的模板值為1 // 不寫入像素顏色 m_pd3dImmediateContext->RSSetState(nullptr); m_pd3dImmediateContext->OMSetDepthStencilState(RenderStates::DSSMarkMirror.Get(), 1); m_pd3dImmediateContext->OMSetBlendState(RenderStates::BSNoColorWrite.Get(), nullptr, 0xFFFFFFFF);m_Mirror.Draw(m_pd3dImmediateContext.Get());通過VS圖形調(diào)試器可以看到模板值為1的區(qū)域
第2步:繪制不透明的鏡面反射物體
理論上會有三面墻和地板可能會透過鏡面看到,這里都需要繪制,但要注意在對頂點位置做反射變換時,原來平面向外的法向量變成了平面向內(nèi)部,因此還需要額外對法向量做反射變換(龍書缺少了對法向量的反射變換)。并且原來按順時針排布的三角形頂點也變成了逆時針排布。所以需要對順時針排布的頂點做裁剪處理。
在做模板測試的時候,我們僅對模板值為1的像素點通過測試,這樣保證限定繪制區(qū)域在鏡面上。
// *********************** // 2. 繪制不透明的反射物體 //// 開啟反射繪制 m_CBStates.isReflection = true; D3D11_MAPPED_SUBRESOURCE mappedData; HR(m_pd3dImmediateContext->Map(m_pConstantBuffers[1].Get(), 0, D3D11_MAP_WRITE_DISCARD, 0, &mappedData)); memcpy_s(mappedData.pData, sizeof(CBDrawingStates), &m_CBStates, sizeof(CBDrawingStates)); m_pd3dImmediateContext->Unmap(m_pConstantBuffers[1].Get(), 0); // 繪制不透明物體,需要順時針裁剪 // 僅對模板值為1的鏡面區(qū)域繪制 m_pd3dImmediateContext->RSSetState(RenderStates::RSCullClockWise.Get()); m_pd3dImmediateContext->OMSetDepthStencilState(RenderStates::DSSDrawReflection.Get(), 1); m_pd3dImmediateContext->OMSetBlendState(nullptr, nullptr, 0xFFFFFFFF);m_Walls[2].Draw(m_pd3dImmediateContext.Get()); m_Walls[3].Draw(m_pd3dImmediateContext.Get()); m_Walls[4].Draw(m_pd3dImmediateContext.Get()); m_Floor.Draw(m_pd3dImmediateContext.Get());到這時候繪制效果如下:
第3步:繪制透明的鏡面反射物體
這一步需要繪制的透明反射物體有籬笆盒以及水面,繪制了這些透明物體后就可以連同鏡面一起混合繪制了。其中籬笆盒要優(yōu)于水面先行繪制:
// *********************** // 3. 繪制透明的反射物體 //// 關(guān)閉順逆時針裁剪 // 僅對模板值為1的鏡面區(qū)域繪制 // 透明混合 m_pd3dImmediateContext->RSSetState(RenderStates::RSNoCull.Get()); m_pd3dImmediateContext->OMSetDepthStencilState(RenderStates::DSSDrawReflection.Get(), 1); m_pd3dImmediateContext->OMSetBlendState(RenderStates::BSTransparent.Get(), nullptr, 0xFFFFFFFF);m_WireFence.Draw(m_pd3dImmediateContext.Get()); m_Water.Draw(m_pd3dImmediateContext.Get()); m_Mirror.Draw(m_pd3dImmediateContext.Get());// 關(guān)閉反射繪制 m_CBStates.isReflection = false; HR(m_pd3dImmediateContext->Map(m_pConstantBuffers[1].Get(), 0, D3D11_MAP_WRITE_DISCARD, 0, &mappedData)); memcpy_s(mappedData.pData, sizeof(CBDrawingStates), &m_CBStates, sizeof(CBDrawingStates)); m_pd3dImmediateContext->Unmap(m_pConstantBuffers[1].Get(), 0);繪制完后效果如下:
第4步:繪制不透明的正常物體
這一步僅有墻體和地板需要繪制:
// ************************ // 4. 繪制不透明的正常物體 //m_pd3dImmediateContext->RSSetState(nullptr); m_pd3dImmediateContext->OMSetDepthStencilState(nullptr, 0); m_pd3dImmediateContext->OMSetBlendState(nullptr, nullptr, 0xFFFFFFFF);for (auto& wall : m_Walls)wall.Draw(m_pd3dImmediateContext.Get()); m_Floor.Draw(m_pd3dImmediateContext.Get());第5步:繪制透明的正常物體
// *********************** // 5. 繪制透明的正常物體 //// 關(guān)閉順逆時針裁剪 // 透明混合 m_pd3dImmediateContext->RSSetState(RenderStates::RSNoCull.Get()); m_pd3dImmediateContext->OMSetDepthStencilState(nullptr, 0); m_pd3dImmediateContext->OMSetBlendState(RenderStates::BSTransparent.Get(), nullptr, 0xFFFFFFFF);m_WireFence.Draw(m_pd3dImmediateContext.Get()); m_Water.Draw(m_pd3dImmediateContext.Get());完成所有繪制后,顯示效果如下:
先繪制鏡面場景還是繪制主場景?
一開始我是根據(jù)龍書的順序先繪制主場景,再繪制鏡面場景的。但是在繪制帶有透明物體的場景時,會得到下面的結(jié)果:
可以看到鏡面下面的部分有黑邊,是因為在繪制主場景的時候,黑色背景和水面產(chǎn)生了混合,并且改寫了深度值,導(dǎo)致在繪制鏡面后面的物體(主要是地板部分)時水面以下的部分沒有通過深度測試,地板也就沒有被繪制。
DirectX11 With Windows SDK完整目錄
Github項目源碼
歡迎加入QQ群: 727623616 可以一起探討DX11,以及有什么問題也可以在這里匯報。
轉(zhuǎn)載于:https://www.cnblogs.com/X-Jun/p/9380099.html
總結(jié)
以上是生活随笔為你收集整理的DirectX11 With Windows SDK--12 深度/模板状态、平面镜反射绘制的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: socket介绍
- 下一篇: linux_安装jdk和mysql简单步