DirectX11学习笔记九 平面阴影 简易代码框架

  本来说这节打算自己搞一个D3D框架的,但是跟了外网的教程走了半个月后发现Chili的框架对于我这种初学者而言太过于事无巨细,做了很多framework,如果只想学习d3d api以及用其要达到什么效果的话,有些“重量级”,所以我打算两手抓了,相比沉浸在c++新特性和指针、模版里,我相信龙书的教程更有指向性。拿龙书与Chili的教程相互比较学习,还是不错的,有了Chili的基础,再看龙书的代码感觉就快多了。

框架

  读了下教程里整理的简易框架
在这里插入图片描述
  在常量缓冲区和其集合里各设置了一个BOOL值,当Set数值修改后就修改BOOL,update时判断BOOL再进行Map,用来减少操作。

void GameObject::Draw(ID3D11DeviceContext * deviceContext, BasicEffect& effect)
{
	// 设置顶点/索引缓冲区
	UINT strides = m_VertexStride;
	UINT offsets = 0;
	deviceContext->IASetVertexBuffers(0, 1, m_pVertexBuffer.GetAddressOf(), &strides, &offsets);
	deviceContext->IASetIndexBuffer(m_pIndexBuffer.Get(), DXGI_FORMAT_R16_UINT, 0);

	// 更新数据并应用
	effect.SetWorldMatrix(XMLoadFloat4x4(&m_WorldMatrix));
	effect.SetTexture(m_pTexture.Get());
	effect.SetMaterial(m_Material);
	effect.Apply(deviceContext);

	deviceContext->DrawIndexed(m_IndexCount, 0, 0);
}
//...
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);
		}
	}
}

  跟Chili的框架目的一样,翻来覆去无非就是尽量减少常量缓冲区的Map操作,同时尽可能的让其他可以通用的渲染状态独立出来,而每次渲染的游戏物体都可能变化的顶点索引与GameObject绑定,只是优化的程度和方式不同,框架就会发生相应的变化。
在这里插入图片描述
  Chili是将每个可以Bind的API封装成Bindable类,一群Bindable的集合体就是Drawable,同时将可能变化的部分拆成静态和非静态,这样同样的GameObject对象就可以共享一些资源,反正框架方法不一,思想类似,但是看看还挺有意思。

平面阴影

  假设绘制的是box的阴影
  任务有两个:
一. 将盒子的顶点由光照投影到某一个平面
在这里插入图片描述
在这里插入图片描述

inline XMMATRIX XM_CALLCONV XMMatrixShadow
(
    FXMVECTOR ShadowPlane,    //投影到的平面
    FXMVECTOR LightPosition   //光源位置,平行光写负方向且w为0,点光源写坐标且w为1+
)

  定义矩阵

	const XMMATRIX reflectMatrix = XMMatrixReflect(XMVectorSet(0.0f, 0.0f, -1.0f, 10.0f));
	const XMVECTOR lightPos = XMVectorSet(0.0f, 10.0f, -10.0f, 1.0f);
	const XMVECTOR floorPlane = XMVectorSet(0.0f, 1.0f, 0.0f, 0.99f);//地板是y+0.99=0的平面,不到1是为了防止阴影平面与地板平面重叠
	const XMVECTOR mirrorPlane = XMVectorSet(0.0f, 0.0f, -1.0f, 10.0f);
	const XMVECTOR reflectLightPos = XMVector4Transform(lightPos, XMMatrixReflect(mirrorPlane));
	m_BasicEffect.SetReflectionMatrix(reflectMatrix);
	m_BasicEffect.SetShadowMatrix(XMMatrixShadow(floorPlane, lightPos));
	m_BasicEffect.SetRefShadowMatrix(XMMatrixShadow(floorPlane, reflectLightPos));

  顶点着色器

#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);
    // 若当前在绘制反射物体,先进行反射操作
    [flatten]
    if (g_IsReflection)
    {
        posW = mul(posW, g_Reflection);
        normalW = mul(normalW, (float3x3) g_Reflection);
    }
    // 若当前在绘制阴影,先进行投影操作
    [flatten]
    if (g_IsShadow)
    {
    	//将顶点投影到投影平面上
        posW = (g_IsReflection ? mul(posW, g_RefShadow) : mul(posW, g_Shadow));  
    }

    vOut.PosH = mul(posW, viewProj);
    vOut.PosW = posW.xyz;
    vOut.NormalW = normalW;
    vOut.Tex = vIn.Tex;
    return vOut;
}

  这样可以将箱子顶点在投影平面摊开,纹理仍是箱子的(二向箔降维打击!),这时候只要再把箱子材质的反射系数rgb改为全0,a不为0,就可以将纹理变成透明的黑色,办法有很多。
  物体绘制代码

//m_ShadowMat 初始化
	m_ShadowMat.ambient = XMFLOAT4(0.0f, 0.0f, 0.0f, 0.5f);
	m_ShadowMat.diffuse = XMFLOAT4(0.0f, 0.0f, 0.0f, 0.5f);
	m_ShadowMat.specular = XMFLOAT4(0.0f, 0.0f, 0.0f, 0.0f);
	
	//.......
	
//draw
	m_WoodCrate.SetMaterial(m_ShadowMat);
	m_BasicEffect.SetShadowState(true);	// 反射关闭,阴影开启
	m_BasicEffect.SetRenderNoDoubleBlend(m_pd3dImmediateContext.Get(), 0);

	m_WoodCrate.Draw(m_pd3dImmediateContext.Get(), m_BasicEffect);

	m_BasicEffect.SetShadowState(false);		// 阴影关闭
	m_WoodCrate.SetMaterial(m_WoodCrateMat);

二. 禁止颜色堆叠
  这样会存在一个颜色堆叠的问题,因为背面消除后顶点围成的三角形在平面上还是可能互相交叉堆叠,阴影也就叠加了起来,我们要防止这种问题。
  办法是模版缓冲区
  在一帧开始前清除模版缓冲区,并设初始值0。
  渲染阴影时开启模版缓冲区,传入参考模版值也为0,只有模版缓冲区内值与参考值相等时才通过,通过后区内值做修改成其他的,若不通过则keep,这样就可以保证一个像素只会叠加一次。

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;
	// 对于背面的几何体我们是不进行渲染的,所以这里的设置无关紧要
	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;

  本想看看投影的推导公式,但是书都在学校因为疫情也回不去。。。不说了,继续跟着Chili写框架了

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值