DirectX11学习笔记八 平面镜 模版测试

  一开始看到平面镜效果我以为要在着色器里写光反射blablabla(我是菜鸟),后来发现,教程中实现平面镜的方法是把场景渲染两次,一次正常渲染,一次镜像渲染,然后设置一个模版将平面镜表面以外的镜像部分剔除掉,只显示镜子中的部分,就像unity里的LayerMask一样。
要做到上面的工作,需要弄明白两个问题

  • 如何剔除平面镜以外的镜像部分
  • 如何渲染镜像的部分

  对于问题1,这里就需要用到深度/模版测试接口ID3D11DepthStencilState

模板缓冲区(stencil buffer)是一种用来实现特殊效果的离屏(off-screen)缓冲区。模板缓冲的大小与后台缓冲及深度缓冲的大小相同,也就是说,模板缓冲的第ij个像素对应于后台缓冲和深度缓冲第ij个像素。我们在4.1.5节的“注意”中提到,当指定一个模板缓冲时,它总是与深度缓冲共享相同的内存空间。尤如名字所指出的,模板缓冲区的用法就像是模板一样,它可以挡住某些像素片段,不让它们存入后台缓冲。(译者注:比如喷油漆时使用的图案模板,先把模板贴在汽车上或者其他什么地方,然后开始喷油漆。在模板镂空的地方会有油漆喷到汽车上,而没有镂空的地方会挡住油漆。在喷完之后,揭下模板,图案就喷涂在汽车上了。例如,当实现一个镜像效果时,我们需要反射镜子对面的物体;不过,我们希望镜像只显示在镜子里面。我们可以使用模板缓冲区来控制镜像范围,阻止镜像绘制到镜子之外的区域)

  我们可以通过ID3D11DepthStencilState接口控制模板缓冲(和深度缓冲)。与混合一样,该接口也提供了一套灵活而强大的功能集合。要学习如何高效地使用模板缓冲区,最有效的方法是仔细研究现有的示例应用程序。当你弄懂了几个使用模板缓冲区的应用程序之后,就会对它有一个更清晰的认识,知道该如何用它来解决实际工作问题。
在这里插入图片描述
  深度测试、模板测试的执行是在混合操作之前执行的,共享同一个内存空间,具体的执行顺序为:
模板测试→深度测试→混合操作
  他们三个都发生在Output-Merger输出合并阶段。在启用模板功能之后,每个光栅化像素都要与下面的两个操作数进行模板测试:

if( StencilRef & StencilReadMask  ⊴  Value &StencilReadMask) 
    accept pixel 
else
    reject pixel`

  1.左操作数(StencilRef & StencilReadMask)由应用程序指定的一个模板参考值(StencilRef)和一个模板掩码(StencilReadMask)进行按位与运算得到。
  2.右操作数(Value &StencilReadMask)由当前像素在模板缓冲区中的对应值(Value)和一个模板掩码(StencilReadMask)进行按位与运算得到。
如果像素无法通过模板测试,则直接丢弃,不参与深度测试;反之,则继续进行深度测试。
运算符⊴可以是D3D11_COMPARISON_FUNC枚举类型定义的任何一个函数:

D3D11_COMPARISON_FUNC 描述
D3D11_COMPARISON_NEVER 始终返回false
D3D11_COMPARISON_LESS <
D3D11_COMPARISON_EQUAL ==
D3D11_COMPARISON_LESS_EQUAL <=
D3D11_COMPARISON_GREATER >
D3D11_COMPARISON_NOT_EQUAL !=
D3D11_COMPARISON_GREATER_EQUAL >=
D3D11_COMPARISON_ALWAYS 始终返回true

创建模版/深度缓冲区接口

创建该接口需要先定义描述
D3D11_DEPTH_STENCIL_DESC

typedef struct D3D11_DEPTH_STENCIL_DESC
    {
   
    BOOL DepthEnable;   //启用深度测试
    D3D11_DEPTH_WRITE_MASK DepthWriteMask;  //深度缓冲区初始化掩码
    D3D11_COMPARISON_FUNC DepthFunc;  //深度比较运算符
    BOOL StencilEnable;  //启用模版测试
    UINT8 StencilReadMask;  //模版值读取掩码
    UINT8 StencilWriteMask;  //模版值写入掩码
    D3D11_DEPTH_STENCILOP_DESC FrontFace;  //对正面朝向摄像机的三角形进行深度/模版操作描述
    D3D11_DEPTH_STENCILOP_DESC BackFace;  //对背面朝向三角形进行深度/模版操作的描述
    } 	D3D11_DEPTH_STENCIL_DESC;
  1. 如果不开启深度测试,则绘制顺序影响像素的先后,就像unity的UGUI那样,先渲染的被后渲染的盖住。
  2. D3D11_DEPTH_WRITE_MASK 深度值写入掩码有两种描述
D3D11_DEPTH_WRITE_MASK 描述
D3D11_DEPTH_WRITE_MASK_ZERO 关闭对深度缓冲区的写入
D3D11_DEPTH_WRITE_MASK_ALL 开启对深度缓冲区的写入
  1. DepthFunc定义两种像素的深度值比较的办法,跟模版值比较的办法一样,一般情况都用Less,即现实中的透视关系。
  2. 是否开启模版测试
  3. 模版值读取掩码,对应模版函数中的StencilReadMask,默认掩码不屏蔽任何二进制位
  4. 模版值写入掩码,当更新模板缓冲区时,我们可以通过掩码来屏蔽某些二进制位,不让它们存入模板缓冲区。例如,当你希望屏蔽前4位数据时,可将掩码设为0x0f。默认掩码不屏蔽任何二进制位:
  5. D3D11_DEPTH_STENCILOP_DESC就是描述模版测试操作的
D3D11_DEPTH_STENCILOP_DESC 描述
StencilFailOp 模版测试失败时的操作
StencilDepthFailOp 模版测试通过而深度测试不通过的操作
StencilPassOp 模版/深度测试通过时的操作
StencilFunc 模版测试所用的比较函数

  StencilFunc类型为D3D11_COMPARISON_FUNC ,跟深度测试的比较符号操作是一样的。
  上面三个Op的数据类型为D3D11_STENCIL_OP

D3D11_STENCIL_OP 描述
D3D11_STENCIL_OP_KEEP 保留存在的模版数据
D3D11_STENCIL_OP_ZERO 将模版数据设为0
D3D11_STENCIL_OP_REPLACE 将模版数据替换为StencilRef,StencilRef 由ID3D11DeviceContext::OMSetDepthStencilState(ID3D11DepthStencilState,StencilRef)定义
D3D11_STENCIL_OP_INCR_SAT 得到Clamp(模版值+1,255)
D3D11_STENCIL_OP_DECR_SAT 得到Clamp(0,模版值-1)
D3D11_STENCIL_OP_INVERT 将模版值按位反转
D3D11_STENCIL_OP_INCR 对目标模板值加1,超过255的话值将上溢变成0
D3D11_STENCIL_OP_DECR 对目标模板值减1,低于0的话将下溢变成255

  填充完模版/深度缓冲区描述后,就可以创建了

HRESULT CreateDepthStencilState(
  const D3D11_DEPTH_STENCIL_DESC *pDepthStencilDesc,  //深度/模版缓冲区描述
  ID3D11DepthStencilState **ppDepthStencilState  //要接收创建出来的缓冲区的句柄(深度/模版测试接口)
);

清除和创建深度/模版缓冲区

  在一帧开始前先清除模版缓冲区

void ClearDepthStencilView(
  ID3D11DepthStencilView *pDepthStencilView,  //之前创建的深度模版视图
  UINT                   ClearFlags,  //要清除的类型,一共就两种
  FLOAT                  Depth,  //深度缓冲区的替换值
  UINT8                  Stencil  //
);
m_pd3dImmediateContext->ClearDepthStencilView(m_pDepthStencilView.Get(), D3D11_CLEAR_DEPTH | D3D11_CLEAR_STENCIL, 1.0f, 0);

  在draw前绑定模版缓冲区,即可

void OMSetDepthStencilState(
  ID3D11DepthStencilState *pDepthStencilState,
  UINT                    StencilRef  //模版函数中的StencilRef
);
m_pd3dImmediateContext->OMSetDepthStencilState(RenderStates::DSSDrawWithStencil.Get(), 1);

实现平面镜效果

  • 第一步,将平面镜像素在内存中相应的位置的模版值设为1,丢弃掉平面镜的像素
	// 裁剪掉背面三角形
	// 标记镜面区域的模板值为1
	// 不写入像素颜色
	m_pd3dImmediateContext->RSSetState(nullptr);
	m_pd3dImmediateContext->OMSetDepthStencilState(RenderStates::DSSWriteStencil.Get(), 1);
	m_pd3dImmediateContext->OMSetBlendState(RenderStates::BSNoColorWrite.Get(), nullptr, 0xFFFFFFFF);
	m_Mirror.Draw(m_pd3dImmediateContext.Get());

   其中渲染状态的定义

   // 镜面标记深度/模板状态
   // 无论是正面还是背面,原来指定的区域的模板值都会被写入StencilRef
   dsDesc.DepthEnable = true;  //也可以关闭
   dsDesc.DepthWriteMask = D3D11_DEPTH_WRITE_MASK_ZERO;  //这里不写入深度信息,否则会遮挡后面的像素,或者关闭深度测试也可以
   dsDesc.DepthFunc = D3D11_COMPARISON_LESS
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值