在DirectX11下用Stencil Buffer绘制可视化Depth Complexity
这是一道在《Introduction to 3D Game Programming with DirectX 11》上的练习题。
要求把某个像素点上的Depth Complexity(深度复杂度?)可视化输出。Depth Complexity即某个点上有多少次depth test。
根据题目中的提示,我们可以用stencil buffer完成。
简要步骤如下:
- 绘制场景,设置DepthStencilState为stencil test永远通过,通过后stencil buffer中的值+1。这样每个点绘制后在stencil buffer中的点就会+1。 
- 根据stencil buffer中的值绘制深度信息。 
 要做到这一点,我们只需要置StencilFunc为D3D11_COMPARISON_EQUAL,此时只有在stencil buffer中的值和stencilRef中的值相同的像素点会通过测试。接着对所有的可能的深度值(比如0~5),把整个屏幕用设定的颜色重新绘制一遍即可。这里的绘制不需要具体的顶点信息,只需要覆盖整个屏幕即可(我们会绘制一个覆盖屏幕的四边形,在接下来的shader中可以看到我们是如何做到这一点的)
综上所述,我们需要创建两个depth stencil state,第一个用来绘制场景,同时给stencil buffer中对应的点+1。注意,这个depth stencil state应该设置为在不通过或者通过depth test时都为stencil buffer +1。
建立一个名为DepthCounterDSS的depth stencil state
	D3D11_DEPTH_STENCIL_DESC dsdesc = { 0 };
	dsdesc.DepthEnable = true;										//深度测试无所谓
	dsdesc.DepthFunc = D3D11_COMPARISON_LESS;
	dsdesc.DepthWriteMask = D3D11_DEPTH_WRITE_MASK_ALL;
	dsdesc.StencilEnable = true;
	dsdesc.StencilReadMask = 0xff;
	dsdesc.StencilWriteMask = 0xff;
	dsdesc.FrontFace.StencilFunc = D3D11_COMPARISON_ALWAYS;			//Stencil test永远通过
	dsdesc.FrontFace.StencilDepthFailOp = D3D11_STENCIL_OP_INCR;	//depth fail失败了也+1
	dsdesc.FrontFace.StencilPassOp = D3D11_STENCIL_OP_INCR;
	dsdesc.FrontFace.StencilFailOp = D3D11_STENCIL_OP_KEEP;			//不会失败,这项无意义。
	//背面被剔除了,下面这些设置无意义。
	dsdesc.BackFace.StencilDepthFailOp = D3D11_STENCIL_OP_INCR;
	dsdesc.BackFace.StencilFailOp = D3D11_STENCIL_OP_KEEP;
	dsdesc.BackFace.StencilFunc = D3D11_COMPARISON_ALWAYS;
	dsdesc.BackFace.StencilPassOp = D3D11_STENCIL_OP_INCR;
	HR(device->CreateDepthStencilState(&dsdesc, &DepthCounterDSS));
第二个depth stencil state用于绘制深度信息。需要做的工作就是让stencil buffer中的值和stencilRef相等时通过stencil test,否则不通过即可。
注意,此时我们要绘制的图形只有一个覆盖整个屏幕的四边形(两个三角形组成)。
建立一个名为DepthVisualDSS的depth stencil state
	D3D11_DEPTH_STENCIL_DESC dvdesc = { 0 };
	dvdesc.DepthEnable = false;										//深度测试无所谓
	dvdesc.DepthFunc = D3D11_COMPARISON_LESS;
	dvdesc.DepthWriteMask = D3D11_DEPTH_WRITE_MASK_ALL;
	dvdesc.StencilEnable = true;
	dvdesc.StencilReadMask = 0xff;
	dvdesc.StencilWriteMask = 0xff;
	dvdesc.FrontFace.StencilFunc = D3D11_COMPARISON_EQUAL;			//相等时才通过
	dvdesc.FrontFace.StencilDepthFailOp = D3D11_STENCIL_OP_KEEP;	//不管成功失败都不修改stencil buffer
	dvdesc.FrontFace.StencilPassOp = D3D11_STENCIL_OP_KEEP;
	dvdesc.FrontFace.StencilFailOp = D3D11_STENCIL_OP_KEEP;
	//背面无所谓。
	dvdesc.BackFace.StencilDepthFailOp = D3D11_STENCIL_OP_KEEP;
	dvdesc.BackFace.StencilFailOp = D3D11_STENCIL_OP_KEEP;
	dvdesc.BackFace.StencilFunc = D3D11_COMPARISON_EQUAL;
	dvdesc.BackFace.StencilPassOp = D3D11_STENCIL_OP_KEEP;
	HR(device->CreateDepthStencilState(&dvdesc, &DepthVisualDSS));
接下来我们修改DrawScene()函数,使其在绘制场景前设置stencil depth state为我们的DepthCounterDSS,然后照常绘制即可。(注意在绘制中途stencil depth state有无改变)
void BlendApp::DrawScene()
{
	md3dImmediateContext->ClearRenderTargetView(mRenderTargetView, reinterpret_cast<const float*>(&Colors::Silver));
	md3dImmediateContext->ClearDepthStencilView(mDepthStencilView, D3D11_CLEAR_DEPTH|D3D11_CLEAR_STENCIL, 1.0f, 0);
	md3dImmediateContext->OMSetDepthStencilState(RenderStates::DepthCounterDSS, 0); //StencilRef没有用
	//普通绘制
    //……
    //绘制结束,此时stencil buffer中存的就是每个像素的深度复杂度
    DrawComplexity();
}
再实现DrawComplexity函数
void BlendApp::DrawComplexity()
{
	ID3DX11EffectTechnique* quadTech = Effects::BasicFX->DepthVisual;
	D3DX11_TECHNIQUE_DESC techDesc;
	quadTech->GetDesc(&techDesc);
	for (UINT p = 0; p<techDesc.Passes; ++p)
	{
		for (int i = 0; i<10; ++i)			//假设最多有10层深度
		{
			md3dImmediateContext->OMSetDepthStencilState(RenderStates::DepthVisualDSS, i);	//设置stencilref=i
			Effects::BasicFX->SetDVColor(Colors::White/10*i);		//设置绘制颜色,i越大白色越浓
			quadTech->GetPassByIndex(p)->Apply(0, md3dImmediateContext);
			md3dImmediateContext->Draw(3, 0);		//绘制一个四边形,具体的顶点信息由vertex shader自己给出
		}
	}
}
以上代码中的DepthVisual是一个简单的对全屏幕进行一次指定颜色绘制的technique,shader代码如下。
cbuffer quadColor
{
    float4 gDVColor;
}
VertexOut VSfullscreenQuad(uint id : SV_VERTEXID)		//SV_VERTEXID由gpu给出,指定当前顶点序号
{
    VertexOut output;
	// 计算齐次坐标
    output.PosH.x = (float) (id / 2) * 4.0 - 1.0f;
    output.PosH.y = (float) (id % 2) * 4.0 - 1.0f;
    output.PosH.z = 0.0f;
    output.PosH.w = 1.0f;
    return output;
}
float4 PScolorOnly(VertexOut pin) : SV_Target
{
    return gDVColor;
}
technique11 DepthVisual
{
    pass P0
    {
        SetVertexShader(CompileShader(vs_5_0, VSfullscreenQuad()));
        SetGeometryShader(NULL);
        SetPixelShader(CompileShader(ps_5_0, PScolorOnly()));
    }
}
完成效果如下

在DirectX11下用Stencil Buffer绘制可视化Depth Complexity的更多相关文章
- windows API下的模板缓冲(stencil buffer)
		在windows API搭建的OpenGL窗口中使用模板缓冲,需要在像素格式描述表中设置stencil buffer位宽为8,这样窗口会自动生成stencil buffer,然后可以在opengl环境 ... 
- Directx11教程(50) 输出depth/stencil buffer的内容
		原文:Directx11教程(50) 输出depth/stencil buffer的内容 有时候,我们需要查看depth/stencil buffer的内容,比如上一章中,我们要查看sten ... 
- Directx11教程(48) depth/stencil buffer的作用
		原文:Directx11教程(48) depth/stencil buffer的作用 在D3D11中,有depth/stencil buffer,它们和framebuffer相对应,如下图所 ... 
- Stencil Buffer
		刚在草稿箱里发现了这篇充满特色的好日志.发表之. ------------------吃货的分割线---------------------------------------- Stencil Bu ... 
- UnityShader实例09:Stencil Buffer&Stencil Test
		http://blog.csdn.net/u011047171/article/details/46928463 Stencil Buffer&Stencil Test 在开始前先吐槽下uni ... 
- pytorch下可采用visidom作为可视化工具
		2018/9/18更新 感觉tensorboardX插件更好用,已转用https://github.com/lanpa/tensorboardX 更新:新版visdom0.1.7安装方式为:cond ... 
- Directx11教程(49) stencil的应用-镜面反射
		原文:Directx11教程(49) stencil的应用-镜面反射 本教程中,我们利用stencil来实现一个镜面反射效果. 1.首先我们要在D3DClass中增加几个成员变量及函数. I ... 
- UnityShader学习笔记- Stencil Buffer
		模板测试(Stencil Test)是现代渲染流水线的一环,其中涉及到的就是模板缓冲(Stencil Buffer),模板缓冲可以用来制作物体的遮罩.轮廓描边.阴影.遮挡显示等等效果 目录 Stenc ... 
- depth/stencil buffer的作用 ----------理解模板缓存  opengl
		在D3D11中,有depth/stencil buffer,它们和framebuffer相对应,如下图所示,framebuffer中一个像素,有相对应的depth buffer和stencil buf ... 
随机推荐
- asp.net 模拟CURL调用微信公共平台API 上传下载多媒体文件接口
			FormItem类 public class FormItem { public string Name { get; set; } public ParamType ParamType { get; ... 
- 虚拟现实-VR-UE4-创建一个自定义的角色 Character
			我学习的资料使用的是老版本的ue4 新版本有好多都是不一样的,好多东西需要自己来摸索, 比如,在老板版本中,默认创建一个GameMode 是回自动创建构造函数发的,而新版本,是没有的,需要自己手动填写 ... 
- 第十九章 Python os模块,pathlib 判断文件是目录还是文件
			OS模块 os.path.abspath() :返回path规范化的绝对路径 import sys import os BASE_DIR = os.path.dirname(os.path.dirna ... 
- python接口测试(二)——配置文件的使用
			在接口测试中,有些东西是固定不变的,比如url,若想更改的话就必须每个请求都更改,因此,可以放到配置文件中使用. 1.创建一个.ini的配置文件,如图: 2.读取配件文件中的内容,后续进行引用 #co ... 
- 深挖 NGUI 基础 之UIRoot (一)
			当你开始使用NGUI的时候,简单的从项目视图 中一个”Control”预设体 拖拽到场景视图中,你将会发现 Hierarchy层次面板中会出现以下层次结构: 其中 UI Root作为根节点,是每个NG ... 
- Python 中的容器 collections
			写在之前 我们都知道 Python 中内置了许多标准的数据结构,比如列表,元组,字典等.与此同时标准库还提供了一些额外的数据结构,我们可以基于它们创建所需的新数据结构. Python 附带了一个「容器 ... 
- Leetcode 673.最长递增子序列的个数
			最长递增子序列的个数 给定一个未排序的整数数组,找到最长递增子序列的个数. 示例 1: 输入: [1,3,5,4,7] 输出: 2 解释: 有两个最长递增子序列,分别是 [1, 3, 4, 7] 和[ ... 
- lintcode-86-二叉查找树迭代器
			86-二叉查找树迭代器 设计实现一个带有下列属性的二叉查找树的迭代器: 元素按照递增的顺序被访问(比如中序遍历) next()和hasNext()的询问操作要求均摊时间复杂度是O(1) 样例 对于下列 ... 
- SQL 视图 局部变量 全局变量 条件语句 事务 触发器
			一.视图 1.视图是一张虚拟表,他所存储的不是实际数据,而是查询语句,但我们可以对视图进行像数据表一样的操作. 2.为什么使用视图呢?我的理解是:1.在远程传输数据时,可以避免过长的查询字符,减少流量 ... 
- RunKit & NPM
			RunKit + NPM Try any Node.js package right in your browser https://npm.runkit.com/segmentit 
