首先检验总的脏标记是否为true,若有任意数据被修改,则检验每个常量缓冲区的脏标记,并根据该标记决定是否要更新常量缓冲区。
void BasicEffect::Apply(ID3D11DeviceContext * deviceContext) { auto& pCBuffers = pImpl->m_pCBuffers; // 将缓冲区绑定到渲染管线上 pCBuffers[0]->BindVS(deviceContext); pCBuffers[1]->BindVS(deviceContext); pCBuffers[2]->BindVS(deviceContext); pCBuffers[3]->BindVS(deviceContext); pCBuffers[4]->BindVS(deviceContext); pCBuffers[0]->BindPS(deviceContext); pCBuffers[1]->BindPS(deviceContext); pCBuffers[2]->BindPS(deviceContext); pCBuffers[4]->BindPS(deviceContext); // 设置纹理 deviceContext->PSSetShaderResources(0, 1, pImpl->m_pTexture.GetAddressOf()); if (pImpl->m_IsDirty) { pImpl->m_IsDirty = false; for (auto& pCBuffer : pCBuffers) { pCBuffer->UpdateBuffer(deviceContext); } } }当然,目前BasicEffect能做的事情还是比较有限的,并且还需要随着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.hlsli" // 像素着色器(2D) float4 PS_2D(VertexPosHTex pIn) : SV_Target { float4 color = g_Tex.Sample(g_Sam, pIn.Tex); clip(color.a - 0.1f); return color; }Basic_PS_3D.hlsl文件变化如下:
#include "Basic.hlsli" // 像素着色器(3D) // 像素着色器(3D) float4 PS_3D(VertexPosHWNormalTex pIn) : SV_Target { // 提前进行裁剪,对不符合要求的像素可以避免后续运算 float4 texColor = g_Tex.Sample(g_Sam, pIn.Tex); clip(texColor.a - 0.1f); // 标准化法向量 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; } // 若当前在绘制反射物体,需要对光照进行反射矩阵变换 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; // 若当前在绘制反射物体,需要对光照进行反射矩阵变换 [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; }