DirectX11 With Windows SDK--13 抛弃FX11并初步实现BasicManager类
前言
DirectX11 With Windows SDK完整目录:
到现在为止,所有的教程项目都没有使用Effects11框架类来绘制场景。因为在D3DCompile API (#47)版本中,如果你尝试编译fx_5_0的效果文件,会收到这样的警告:X4717: Effects deprecated for D3DCompiler_47
在未来的版本中,D3DCompiler可能会停止对FX11的支持,所以我们需要自行去管理各种特效,并改用HLSL编译器去编译每一个着色器。可以参考本系列前面的教程:
教程 |
---|
这篇教程还会提到用深度/模板状态去实现简单的阴影效果,但不会深入数学公式原理。
项目源码点此:
回顾RenderStates类
目前的RenderStates
类存放有比较常用的各种状态,原来在Effects11框架下是可以在fx文件初始化各种渲染状态,并设置到Technique11中。但现在我们只能在C++代码层中一次性创建好各种所需的渲染状态:
class RenderStates { public: template <class T> using ComPtr = Microsoft::WRL::ComPtr<T>; static void InitAll(const ComPtr<ID3D11Device>& device); // 使用ComPtr无需手工释放 public: static ComPtr<ID3D11RasterizerState> RSWireframe; // 光栅化器状态:线框模式 static ComPtr<ID3D11RasterizerState> RSNoCull; // 光栅化器状态:无背面裁剪模式 static ComPtr<ID3D11RasterizerState> RSCullClockWise; // 光栅化器状态:顺时针裁剪模式 static ComPtr<ID3D11SamplerState> SSLinearWrap; // 采样器状态:线性过滤 static ComPtr<ID3D11SamplerState> SSAnistropicWrap; // 采样器状态:各项异性过滤 static ComPtr<ID3D11BlendState> BSNoColorWrite; // 混合状态:不写入颜色 static ComPtr<ID3D11BlendState> BSTransparent; // 混合状态:透明混合 static ComPtr<ID3D11BlendState> BSAlphaToCoverage; // 混合状态:Alpha-To-Coverage static ComPtr<ID3D11DepthStencilState> DSSWriteStencil; // 深度/模板状态:写入模板值 static ComPtr<ID3D11DepthStencilState> DSSDrawWithStencil; // 深度/模板状态:对指定模板值的区域进行绘制 static ComPtr<ID3D11DepthStencilState> DSSNoDoubleBlend; // 深度/模板状态:无二次混合区域 static ComPtr<ID3D11DepthStencilState> DSSNoDepthTest; // 深度/模板状态:关闭深度测试 static ComPtr<ID3D11DepthStencilState> DSSNoDepthWrite; // 深度/模板状态:仅深度测试,不写入深度值 };
具体的设置可以参照源码或者上一章内容。
BasicManager类
现在,为了减轻GameApp
类的负担,并且为了能够实现部分类似Effects11的功能,需要根据HLSL的代码去实现一个对应的简易BasicManager
类。
在BasicManager.h
中还包含了对应HLSL常量缓冲区的结构体:
#ifndef BASICMANAGER_H #define BASICMANAGER_H #include <wrl/client.h> #include <d3d11_1.h> #include <d3dcompiler.h> #include <directxmath.h> #include <vector> #include "LightHelper.h" #include "RenderStates.h" #include "Vertex.h" // 由于常量缓冲区的创建需要是16字节的倍数,该函数可以返回合适的字节大小 inline UINT Align16Bytes(UINT size) { return (size + 15) & (UINT)(-16); } struct CBChangesEveryDrawing { DirectX::XMMATRIX world; DirectX::XMMATRIX worldInvTranspose; DirectX::XMMATRIX texTransform; Material material; }; struct CBChangesEveryFrame { DirectX::XMMATRIX view; DirectX::XMFLOAT4 eyePos; }; struct CBDrawingState { int isReflection; int isShadow; DirectX::XMINT2 pad; }; struct CBChangesOnResize { DirectX::XMMATRIX proj; }; struct CBNeverChange { DirectX::XMMATRIX reflection; DirectX::XMMATRIX shadow; DirectX::XMMATRIX refShadow; DirectionalLight dirLight[10]; PointLight pointLight[10]; SpotLight spotLight[10]; int numDirLight; int numPointLight; int numSpotLight; float pad; // 打包保证16字节对齐 }; // 暂时省略BasicManager类 // ... #endif
现在HLSL中的Basic.fx
如下:
#include "LightHelper.hlsli" Texture2D tex : register(t0); SamplerState sam : register(s0); cbuffer CBChangesEveryDrawing : register(b0) { row_major matrix gWorld; row_major matrix gWorldInvTranspose; row_major matrix gTexTransform; Material gMaterial; } cbuffer CBDrawingState : register(b1) { int gIsReflection; int gIsShadow; } cbuffer CBChangesEveryFrame : register(b2) { row_major matrix gView; float3 gEyePosW; } cbuffer CBChangesOnResize : register(b3) { row_major matrix gProj; } cbuffer CBNeverChange : register(b4) { row_major matrix gReflection; row_major matrix gShadow; row_major matrix gRefShadow; DirectionalLight gDirLight[10]; PointLight gPointLight[10]; SpotLight gSpotLight[10]; int gNumDirLight; int gNumPointLight; int gNumSpotLight; float gPad; } struct Vertex3DIn { float3 Pos : POSITION; float3 Normal : NORMAL; float2 Tex : TEXCOORD; }; struct Vertex3DOut { float4 PosH : SV_POSITION; float3 PosW : POSITION; // 在世界中的位置 float3 NormalW : NORMAL; // 法向量在世界中的方向 float2 Tex : TEXCOORD; }; struct Vertex2DIn { float3 Pos : POSITION; float2 Tex : TEXCOORD; }; struct Vertex2DOut { float4 PosH : SV_POSITION; float2 Tex : TEXCOORD; };
各着色器的HLSL代码都分别放入独立的文件内,后面会详细讲述。
一个简易的BasicManager
具有如下功能:
- 修改常量缓冲区的变量(整块更新)
- 类似Effects11那样,可以自己设置单通道下的各种状态、着色器
但是该管理类并不用于绘制,具体的绘制函数交给了简易GameObject
类来操作。所以每次绘制物体时,根据情况可能需要调用BasicManager
的设置方法来指定当前要以怎样的形式来渲染,然后才是调用GameObject::Draw
方法绘制。
因为该类没有反射功能,所以用户需要决定什么时候才去更新常量缓冲区资源。
class BasicManager { public: // 使用模板别名(C++11)简化类型名 template <class T> using ComPtr = Microsoft::WRL::ComPtr<T>; // 初始化Basix.fx所需资源并初始化光栅化状态 bool InitAll(ComPtr<ID3D11Device> device); // 是否已经初始化 bool IsInit() const; template <class T> void UpdateConstantBuffer(const T& cbuffer); // 默认状态来绘制 void SetRenderDefault(); // Alpha混合绘制 void SetRenderAlphaBlend(); // 无二次混合 void SetRenderNoDoubleBlend(UINT stencilRef); // 仅写入模板值 void SetWriteStencilOnly(UINT stencilRef); // 对指定模板值的区域进行绘制,采用默认状态 void SetRenderDefaultWithStencil(UINT stencilRef); // 对指定模板值的区域进行绘制,采用Alpha混合 void SetRenderAlphaBlendWithStencil(UINT stencilRef); // 2D默认状态绘制 void Set2DRenderDefault(); // 2D混合绘制 void Set2DRenderAlphaBlend(); private: // 从.fx/.hlsl文件中编译着色器 HRESULT CompileShaderFromFile(const WCHAR* szFileName, LPCSTR szEntryPoint, LPCSTR szShaderModel, ID3DBlob** ppBlobOut); private: ComPtr<ID3D11VertexShader> mVertexShader3D; // 用于3D的顶点着色器 ComPtr<ID3D11PixelShader> mPixelShader3D; // 用于3D的像素着色器 ComPtr<ID3D11VertexShader> mVertexShader2D; // 用于2D的顶点着色器 ComPtr<ID3D11PixelShader> mPixelShader2D; // 用于2D的像素着色器 ComPtr<ID3D11InputLayout> mVertexLayout2D; // 用于2D的顶点输入布局 ComPtr<ID3D11InputLayout> mVertexLayout3D; // 用于3D的顶点输入布局 ComPtr<ID3D11DeviceContext> md3dImmediateContext; // 设备上下文 std::vector<ComPtr<ID3D11Buffer>> mConstantBuffers; // 常量缓冲区 };
默认状态绘制
该绘制模式和后面的所有绘制模式都使用的是线性Wrap采样器。
BasicManager::SetRenderDefault
方法使用了默认的3D像素着色器和顶点着色器,并且其余各状态都保留使用默认状态:
void BasicManager::SetRenderDefault() { md3dImmediateContext->IASetInputLayout(mVertexLayout3D.Get()); md3dImmediateContext->VSSetShader(mVertexShader3D.Get(), nullptr, 0); md3dImmediateContext->RSSetState(nullptr); md3dImmediateContext->PSSetShader(mPixelShader3D.Get(), nullptr, 0); md3dImmediateContext->PSSetSamplers(0, 1, RenderStates::SSLinearWrap.GetAddressOf()); md3dImmediateContext->OMSetDepthStencilState(nullptr, 0); md3dImmediateContext->OMSetBlendState(nullptr, nullptr, 0xFFFFFFFF); }
Alpha透明混合绘制
该绘制模式关闭了光栅化裁剪,并采用透明混合方式。
void BasicManager::SetRenderAlphaBlend() { md3dImmediateContext->IASetInputLayout(mVertexLayout3D.Get()); md3dImmediateContext->VSSetShader(mVertexShader3D.Get(), nullptr, 0); md3dImmediateContext->RSSetState(RenderStates::RSNoCull.Get()); md3dImmediateContext->PSSetShader(mPixelShader3D.Get(), nullptr, 0); md3dImmediateContext->PSSetSamplers(0, 1, RenderStates::SSLinearWrap.GetAddressOf()); md3dImmediateContext->OMSetDepthStencilState(nullptr, 0); md3dImmediateContext->OMSetBlendState(RenderStates::BSTransparent.Get(), nullptr, 0xFFFFFFFF); }
无重复混合(单次混合)
该绘制模式用于绘制阴影,防止过度混合。需要指定绘制区域的模板值。
void BasicManager::SetRenderNoDoubleBlend(UINT stencilRef) { md3dImmediateContext->IASetInputLayout(mVertexLayout3D.Get()); md3dImmediateContext->VSSetShader(mVertexShader3D.Get(), nullptr, 0); md3dImmediateContext->RSSetState(RenderStates::RSNoCull.Get()); md3dImmediateContext->PSSetShader(mPixelShader3D.Get(), nullptr, 0); md3dImmediateContext->PSSetSamplers(0, 1, RenderStates::SSLinearWrap.GetAddressOf()); md3dImmediateContext->OMSetDepthStencilState(RenderStates::DSSNoDoubleBlend.Get(), stencilRef); md3dImmediateContext->OMSetBlendState(RenderStates::BSTransparent.Get(), nullptr, 0xFFFFFFFF); }
仅写入模板值
该模式用于向模板缓冲区写入用户指定的模板值,并且不写入到深度缓冲区和后备缓冲区。
void BasicManager::SetWriteStencilOnly(UINT stencilRef) { md3dImmediateContext->IASetInputLayout(mVertexLayout3D.Get()); md3dImmediateContext->VSSetShader(mVertexShader3D.Get(), nullptr, 0); md3dImmediateContext->RSSetState(nullptr); md3dImmediateContext->PSSetShader(mPixelShader3D.Get(), nullptr, 0); md3dImmediateContext->PSSetSamplers(0, 1, RenderStates::SSLinearWrap.GetAddressOf()); md3dImmediateContext->OMSetDepthStencilState(RenderStates::DSSWriteStencil.Get(), stencilRef); md3dImmediateContext->OMSetBlendState(RenderStates::BSNoColorWrite.Get(), nullptr, 0xFFFFFFFF); }
对指定模板值区域进行常规绘制
该模式下,仅对模板缓冲区的模板值和用户指定的相等的区域进行常规绘制。
void BasicManager::SetRenderDefaultWithStencil(UINT stencilRef) { md3dImmediateContext->IASetInputLayout(mVertexLayout3D.Get()); md3dImmediateContext->VSSetShader(mVertexShader3D.Get(), nullptr, 0); md3dImmediateContext->RSSetState(RenderStates::RSCullClockWise.Get()); md3dImmediateContext->PSSetShader(mPixelShader3D.Get(), nullptr, 0); md3dImmediateContext->PSSetSamplers(0, 1, RenderStates::SSLinearWrap.GetAddressOf()); md3dImmediateContext->OMSetDepthStencilState(RenderStates::DSSDrawWithStencil.Get(), stencilRef); md3dImmediateContext->OMSetBlendState(nullptr, nullptr, 0xFFFFFFFF); }
对指定模板值区域进行Alpha透明混合绘制
该模式下,仅对模板缓冲区的模板值和用户指定的相等的区域进行Alpha透明混合绘制。
void BasicManager::SetRenderAlphaBlendWithStencil(UINT stencilRef) { md3dImmediateContext->IASetInputLayout(mVertexLayout3D.Get()); md3dImmediateContext->VSSetShader(mVertexShader3D.Get(), nullptr, 0); md3dImmediateContext->RSSetState(RenderStates::RSNoCull.Get()); md3dImmediateContext->PSSetShader(mPixelShader3D.Get(), nullptr, 0); md3dImmediateContext->PSSetSamplers(0, 1, RenderStates::SSLinearWrap.GetAddressOf()); md3dImmediateContext->OMSetDepthStencilState(RenderStates::DSSDrawWithStencil.Get(), stencilRef); md3dImmediateContext->OMSetBlendState(RenderStates::BSTransparent.Get(), nullptr, 0xFFFFFFFF); }
2D默认绘制
该模式使用的是2D顶点着色器和像素着色器,并修改为2D输入布局。
void BasicManager::Set2DRenderDefault() { md3dImmediateContext->IASetInputLayout(mVertexLayout2D.Get()); md3dImmediateContext->VSSetShader(mVertexShader2D.Get(), nullptr, 0); md3dImmediateContext->RSSetState(nullptr); md3dImmediateContext->PSSetShader(mPixelShader2D.Get(), nullptr, 0); md3dImmediateContext->PSSetSamplers(0, 1, RenderStates::SSLinearWrap.GetAddressOf()); md3dImmediateContext->OMSetDepthStencilState(nullptr, 0); md3dImmediateContext->OMSetBlendState(nullptr, nullptr, 0xFFFFFFFF); }
2D透明混合绘制
相比上面,多了透明混合状态。
void BasicManager::Set2DRenderAlphaBlend() { md3dImmediateContext->IASetInputLayout(mVertexLayout2D.Get()); md3dImmediateContext->VSSetShader(mVertexShader2D.Get(), nullptr, 0); md3dImmediateContext->RSSetState(RenderStates::RSNoCull.Get()); md3dImmediateContext->PSSetShader(mPixelShader2D.Get(), nullptr, 0); md3dImmediateContext->PSSetSamplers(0, 1, RenderStates::SSLinearWrap.GetAddressOf()); md3dImmediateContext->OMSetDepthStencilState(nullptr, 0); md3dImmediateContext->OMSetBlendState(RenderStates::BSTransparent.Get(), nullptr, 0xFFFFFFFF); }
更新常量缓冲区
这里使用的是成员模板函数,根据传入的结构体类型来更新常量缓冲区:
template<class T> void BasicManager::UpdateConstantBuffer(const T& cbuffer) { } template<> void BasicManager::UpdateConstantBuffer<CBChangesEveryDrawing>(const CBChangesEveryDrawing& cbuffer) { md3dImmediateContext->UpdateSubresource(mConstantBuffers[0].Get(), 0, nullptr, &cbuffer, 0, 0); } template<> void BasicManager::UpdateConstantBuffer<CBDrawingState>(const CBDrawingState& cbuffer) { md3dImmediateContext->UpdateSubresource(mConstantBuffers[1].Get(), 0, nullptr, &cbuffer, 0, 0); } template<> void BasicManager::UpdateConstantBuffer<CBChangesEveryFrame>(const CBChangesEveryFrame& cbuffer) { md3dImmediateContext->UpdateSubresource(mConstantBuffers[2].Get(), 0, nullptr, &cbuffer, 0, 0); } template<> void BasicManager::UpdateConstantBuffer<CBChangesOnResize>(const CBChangesOnResize& cbuffer) { md3dImmediateContext->UpdateSubresource(mConstantBuffers[3].Get(), 0, nullptr, &cbuffer, 0, 0); } template<> void BasicManager::UpdateConstantBuffer<CBNeverChange>(const CBNeverChange& cbuffer) { md3dImmediateContext->UpdateSubresource(mConstantBuffers[4].Get(), 0, nullptr, &cbuffer, 0, 0); }
防止GameApp::OnResize在BasicManager未初始化时修改投影矩阵
BasicManager::IsInit
方法判断摄像机是否经过了初始化。由于BasicManager
的初始化在GameApp::Init
之前,但却会调用GameApp::OnResize
先更新其中的一个常量缓冲区,所以需要加上一重防护在未初始化的时候不应该操作,并在GameApp::InitResource
方法初始化摄像机的投影矩阵。
当然,目前BasicManager
能做的事情还是比较有限的,并且还需要随着HLSL代码的变动而随之调整。更多的功能会在后续教程中实现。
绘制平面阴影
使用XMMatrixShadow
可以生成阴影矩阵,根据光照类型和位置对几何体投影到平面上的。
XMMATRIX XMMatrixShadow( FXMVECTOR ShadowPlane, // 平面向量(nx, ny, nz, d) FXMVECTOR LightPosition); // w = 0时表示平行光方向, w = 1时表示光源位置
通常指定的平面会稍微比实际平面高那么一点点,以避免深度缓冲区资源争夺导致阴影显示有问题。
使用模板缓冲区防止过度混合
一个物体投影到平面上时,投影区域的某些位置可能位于多个三角形之内,这会导致这些位置会有多个像素通过测试并进行混合操作,渲染的次数越多,显示的颜色会越黑。
我们可以使用模板缓冲区来解决这个问题。
- 在之前的例子中,我们用模板值为0的区域表示非镜面反射区,模板值为1的区域表示为镜面反射区;
- 使用
RenderStates::DSSNoDoubleBlend
的深度模板状态,当给定的模板值和深度/模板缓冲区的模板值一致时,通过模板测试并对模板值加1,绘制该像素的混合,然后下一次由于给定的模板值比深度/模板缓冲区的模板值小1,不会再通过模板测试,也就阻挡了后续像素的绘制; - 应当先绘制镜面的阴影区域,再绘制正常的阴影区域。
着色器代码的变化
Basic_PS_2D.hlsl文件变化如下:
#include "Basic.fx" // 像素着色器(2D) float4 PS_2D(Vertex2DOut pIn) : SV_Target { float4 color = tex.Sample(sam, pIn.Tex); clip(color.a - 0.1f); return color; }
Basic_PS_3D.hlsl文件变化如下:
#include "Basic.fx" // 像素着色器(3D) float4 PS_3D(Vertex3DOut pIn) : SV_Target { // 提前进行裁剪,对不符合要求的像素可以避免后续运算 float4 texColor = tex.Sample(sam, pIn.Tex); clip(texColor.a - 0.1f); // 标准化法向量 pIn.NormalW = normalize(pIn.NormalW); // 顶点指向眼睛的向量 float3 toEyeW = normalize(gEyePosW - 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 < gNumDirLight; ++i) { ComputeDirectionalLight(gMaterial, gDirLight[i], pIn.NormalW, toEyeW, A, D, S); ambient += A; diffuse += D; spec += S; } [unroll] for (i = 0; i < gNumPointLight; ++i) { PointLight pointLight = gPointLight[i]; // 若当前在绘制反射物体,需要对光照进行反射矩阵变换 [flatten] if (gIsReflection) { pointLight.Position = (float3) mul(float4(pointLight.Position, 1.0f), gReflection); } ComputePointLight(gMaterial, pointLight, pIn.PosW, pIn.NormalW, toEyeW, A, D, S); ambient += A; diffuse += D; spec += S; } [unroll] for (i = 0; i < gNumSpotLight; ++i) { SpotLight spotLight = gSpotLight[i]; // 若当前在绘制反射物体,需要对光照进行反射矩阵变换 [flatten] if (gIsReflection) { spotLight.Position = (float3) mul(float4(spotLight.Position, 1.0f), gReflection); } ComputeSpotLight(gMaterial, 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 * gMaterial.Diffuse.a; return litColor; }
Basic_VS_2D.hlsl变化如下:
#include "Basic.fx" // 顶点着色器(2D) Vertex2DOut VS_2D(Vertex2DIn pIn) { Vertex2DOut pOut; pOut.PosH = float4(pIn.Pos, 1.0f); pOut.Tex = mul(float4(pIn.Tex, 0.0f, 1.0f), gTexTransform).xy; return pOut; }
Basic_VS_3D.hlsl变化如下:
#include "Basic.fx" // 顶点着色器(3D) Vertex3DOut VS_3D(Vertex3DIn pIn) { Vertex3DOut pOut; float4 posH = mul(float4(pIn.Pos, 1.0f), gWorld); // 若当前在绘制反射物体,先进行反射操作 [flatten] if (gIsReflection) { posH = mul(posH, gReflection); } // 若当前在绘制阴影,先进行投影操作 [flatten] if (gIsShadow) { posH = (gIsReflection ? mul(posH, gRefShadow) : mul(posH, gShadow)); } pOut.PosH = mul(mul(posH, gView), gProj); pOut.PosW = mul(float4(pIn.Pos, 1.0f), gWorld).xyz; pOut.NormalW = mul(pIn.Normal, (float3x3) gWorldInvTranspose); pOut.Tex = mul(float4(pIn.Tex, 0.0f, 1.0f), gTexTransform).xy; return pOut; }
现在每个着色器的代码都放到单独的文件来参与编译了。
场景绘制
现在场景只有墙体、地板、木箱和镜面。
第1步: 镜面区域写入模板缓冲区
// ********************* // 1. 给镜面反射区域写入值1到模板缓冲区 // mBasicManager.SetWriteStencilOnly(1); mMirror.Draw(md3dImmediateContext);
第2步: 绘制不透明的反射物体和阴影
// *********************** // 2. 绘制不透明的反射物体 // // 开启反射绘制 mDrawingState.isReflection = 1; // 反射开启 mBasicManager.UpdateConstantBuffer(mDrawingState); mBasicManager.SetRenderDefaultWithStencil(1); mWalls[2].Draw(md3dImmediateContext); mWalls[3].Draw(md3dImmediateContext); mWalls[4].Draw(md3dImmediateContext); mFloor.Draw(md3dImmediateContext); mWoodCrate.Draw(md3dImmediateContext); // 绘制阴影 mWoodCrate.SetMaterial(mShadowMat); mDrawingState.isShadow = 1; // 反射开启,阴影开启 mBasicManager.UpdateConstantBuffer(mDrawingState); mBasicManager.SetRenderNoDoubleBlend(1); mWoodCrate.Draw(md3dImmediateContext); // 恢复到原来的状态 mDrawingState.isShadow = 0; mBasicManager.UpdateConstantBuffer(mDrawingState); mWoodCrate.SetMaterial(mWoodCrateMat);
第3步: 绘制透明镜面
// *********************** // 3. 绘制透明镜面 // mBasicManager.SetRenderAlphaBlendWithStencil(1); mMirror.Draw(md3dImmediateContext); // 关闭反射绘制 mDrawingState.isReflection = 0; mBasicManager.UpdateConstantBuffer(mDrawingState);
第4步: 绘制不透明的正常物体和阴影
// ************************ // 4. 绘制不透明的正常物体 // mBasicManager.SetRenderDefault(); for (auto& wall : mWalls) wall.Draw(md3dImmediateContext); mFloor.Draw(md3dImmediateContext); mWoodCrate.Draw(md3dImmediateContext); // 绘制阴影 mWoodCrate.SetMaterial(mShadowMat); mDrawingState.isShadow = 1; // 反射关闭,阴影开启 mBasicManager.UpdateConstantBuffer(mDrawingState); mBasicManager.SetRenderNoDoubleBlend(0); mWoodCrate.Draw(md3dImmediateContext); mDrawingState.isShadow = 0; // 反射关闭 mBasicManager.UpdateConstantBuffer(mDrawingState); mWoodCrate.SetMaterial(mWoodCrateMat);
绘制效果如下:
注意该样例只生成点光灯到地板的阴影。你可以用各种摄像机模式来进行测试。