热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

StencilBuffer简单应用

写在前面:本文章为个人学习笔记,方便以后自己复习,也希望能帮助到他人。由于本人水平有限难免出现错误,还请评论区指出,多多指教。部分图元和素材来源于网络,如有侵权请联系本人删除。

写在前面:

本文章为个人学习笔记,方便以后自己复习,也希望能帮助到他人。

由于本人水平有限难免出现错误,还请评论区指出,多多指教。

部分图元和素材来源于网络,如有侵权请联系本人删除。

参考资料与链接会在文章末尾贴出。

=======================================================================

还记得深度缓冲区吗?它帮助我们比较对象的深度,以确保它们有正确相互遮挡关系。而本文会简单介绍模板缓冲区,模板缓冲区主要用于仅渲染对象的一部分而丢弃其他对象。

1 Writing from the Stencil Buffer

模板缓冲区为每个像素存储一个 8 位整数值(0-255)在帧缓冲区中。在执行片段着色器之前对于给定的像素,GPU 可以将模板缓冲区中的当前值与给定的参考值进行比较,这称为模板测试(Stencil Test)。如果模板测试通过,GPU 将执行深度测试。如果模板测试失败,GPU 会跳过该像素的其余处理。这意味着我们可以使用模板缓冲区作为掩码来告诉 GPU 哪些像素要绘制,哪些像素要丢弃。

模板测试是可配置的,所有的模板操作都是通过我们的 hlsl 代码之外的一个小的模板代码块来完成的。像大多数shaderlab的东西一样,我们可以在我们的子着色器中编写它们以将它们用于整个subshader或仅在某个shader pass中使用它们。

模板测试的基本公式是:


整一块模板测试的语义:

看着非常多,但其实我们并不需要为所有的signature指定值,因为很多signature都有默认值,本文的例子中我们只需要指定几个参数就能做出我们想要的效果。

本文例子将会有两个shader,一个write shader,一个read shader。

stencil 操作最重要的参数是 Ref,它标记了我们操作的模板参考值。默认值为 0,这是缓冲区在写入任何内容之前的默认值。我们首先将它设置为 0,这目前不会改变任何东西,但是现在输入它会使代码结构更清晰一些,且后面更改起来更轻松。

我们需要的模板操作的另一个参数是 Comp,它定义了模板操作何时通过。默认值为 Always,这意味着无论我们使用什么参考值,对象都将始终被绘制。对于read shader,我们将使用 Equal,意思是我们标记的Ref值与该位置的模板缓冲区Ref值相等时才绘制对象。

场景中我们简单搭建了一个窗框,窗框前后各有一个plane,现在把write shader的材质赋给front plane,并在fragment shader 中return 0,意料之中没有什么改变:

这是因为现在所有默认的stencil ref都是0,假如我们将ref改为1:

现在我们将ref作为属性暴露在外方便后续调试,[IntRange] attribute是保证我们的值为整数值。

目前的效果仍然是0为可见,其他的值全都不可见。

2 Reading to the Stencil Buffer

首先我们会完善之前的write shader,我们暴露Ref值,并把Comp改为always,这样它总是会通过模板测试,在通过模板测试后会把当前模板缓冲区的值改为自己当前的值,还有就是把深度写入关闭,不然会遮挡后面的物体:


接下来我们需要编写read shader,我们将之前光照模型的shader作为基础,再加上Stencil部分即可:


这里Comp改为Equal,即当前ref值与模板缓冲区ref值相等才会通过模板测试。

 

这样说明可能会更好理解一点,我们的plane实际上就是提前为屏幕中某片区域设置好了stencil value(这里为1),我们这里叫作write(Stencil Ref)shader;而真正想要渲染的物体也设定一个当前的Stencil value,但只有当此物体与模板缓冲区的值一致时才会渲染,假如我们设为1,那么因为默认为0,该物体无法通过模板测试自然不会渲染。当时当我们透过之前的plane观察此物体时,由于这个plane覆盖区域的stecil value被设为1,该物体Stencil value与之相等,因此就会被渲染。

先上效果图:


可以看到,假如不是透过plane的话是无法看到此物体的,因为没有通过模板测试不会被渲染。


接下来我们需要编写read shader

为了真正使用从模板缓冲区读取的着色器,我们将编写第二个从模板缓冲区读取的着色器。第二个着色器本身不会写入屏幕,而是在第一个着色器之前渲染,因此我们确保模板缓冲区在读取时已经写入了正确的值。

对于这个着色器,我们从一个基本的无光照着色器开始,就像在基础中一样,因为它非常简单而且我们不需要太多。为了让它根本不渲染到屏幕上,而只是操纵模板缓冲区,我们将向它添加一些其他的小细节。

我们可以继续完善一下write shader,因为我们在fragment shader中return0,所以看到背景是黑色的,我们可以把ColorMask设为0,这样就完全不输出颜色;此外,按照上面的逻辑我们需要write shader在read shader之前渲染,因为我们把其渲染队列改为Geometry-1,而read shader则保持Geometry:


我们把画框的另一面也做同样的配置,只不过前面的ref值为1,后面的ref值为2:


实际上两个物体处于相近的位置,却因为我们利用了模板缓冲区,呈现了这种效果。




推荐阅读
  • 本文整理了315道Python基础题目及答案,帮助读者检验学习成果。文章介绍了学习Python的途径、Python与其他编程语言的对比、解释型和编译型编程语言的简述、Python解释器的种类和特点、位和字节的关系、以及至少5个PEP8规范。对于想要检验自己学习成果的读者,这些题目将是一个不错的选择。请注意,答案在视频中,本文不提供答案。 ... [详细]
  • 本文详细介绍了Linux中进程控制块PCBtask_struct结构体的结构和作用,包括进程状态、进程号、待处理信号、进程地址空间、调度标志、锁深度、基本时间片、调度策略以及内存管理信息等方面的内容。阅读本文可以更加深入地了解Linux进程管理的原理和机制。 ... [详细]
  • Oracle分析函数first_value()和last_value()的用法及原理
    本文介绍了Oracle分析函数first_value()和last_value()的用法和原理,以及在查询销售记录日期和部门中的应用。通过示例和解释,详细说明了first_value()和last_value()的功能和不同之处。同时,对于last_value()的结果出现不一样的情况进行了解释,并提供了理解last_value()默认统计范围的方法。该文对于使用Oracle分析函数的开发人员和数据库管理员具有参考价值。 ... [详细]
  • EPPlus绘制刻度线的方法及示例代码
    本文介绍了使用EPPlus绘制刻度线的方法,并提供了示例代码。通过ExcelPackage类和List对象,可以实现在Excel中绘制刻度线的功能。具体的方法和示例代码在文章中进行了详细的介绍和演示。 ... [详细]
  • 本文详细介绍了SQL日志收缩的方法,包括截断日志和删除不需要的旧日志记录。通过备份日志和使用DBCC SHRINKFILE命令可以实现日志的收缩。同时,还介绍了截断日志的原理和注意事项,包括不能截断事务日志的活动部分和MinLSN的确定方法。通过本文的方法,可以有效减小逻辑日志的大小,提高数据库的性能。 ... [详细]
  • 本文介绍了lua语言中闭包的特性及其在模式匹配、日期处理、编译和模块化等方面的应用。lua中的闭包是严格遵循词法定界的第一类值,函数可以作为变量自由传递,也可以作为参数传递给其他函数。这些特性使得lua语言具有极大的灵活性,为程序开发带来了便利。 ... [详细]
  • 本文介绍了Python高级网络编程及TCP/IP协议簇的OSI七层模型。首先简单介绍了七层模型的各层及其封装解封装过程。然后讨论了程序开发中涉及到的网络通信内容,主要包括TCP协议、UDP协议和IPV4协议。最后还介绍了socket编程、聊天socket实现、远程执行命令、上传文件、socketserver及其源码分析等相关内容。 ... [详细]
  • 本文介绍了在Python3中如何使用选择文件对话框的格式打开和保存图片的方法。通过使用tkinter库中的filedialog模块的asksaveasfilename和askopenfilename函数,可以方便地选择要打开或保存的图片文件,并进行相关操作。具体的代码示例和操作步骤也被提供。 ... [详细]
  • 本文分享了一个关于在C#中使用异步代码的问题,作者在控制台中运行时代码正常工作,但在Windows窗体中却无法正常工作。作者尝试搜索局域网上的主机,但在窗体中计数器没有减少。文章提供了相关的代码和解决思路。 ... [详细]
  • 本文介绍了使用Java实现大数乘法的分治算法,包括输入数据的处理、普通大数乘法的结果和Karatsuba大数乘法的结果。通过改变long类型可以适应不同范围的大数乘法计算。 ... [详细]
  • Java序列化对象传给PHP的方法及原理解析
    本文介绍了Java序列化对象传给PHP的方法及原理,包括Java对象传递的方式、序列化的方式、PHP中的序列化用法介绍、Java是否能反序列化PHP的数据、Java序列化的原理以及解决Java序列化中的问题。同时还解释了序列化的概念和作用,以及代码执行序列化所需要的权限。最后指出,序列化会将对象实例的所有字段都进行序列化,使得数据能够被表示为实例的序列化数据,但只有能够解释该格式的代码才能够确定数据的内容。 ... [详细]
  • 给定一个二维平面上的一些点,通过计算曼哈顿距离,求连接所有点的最小总费用。只有任意两点之间有且仅有一条简单路径时,才认为所有点都已连接。给出了几个示例并给出了对应的输出。 ... [详细]
  • 本文介绍了Python爬虫技术基础篇面向对象高级编程(中)中的多重继承概念。通过继承,子类可以扩展父类的功能。文章以动物类层次的设计为例,讨论了按照不同分类方式设计类层次的复杂性和多重继承的优势。最后给出了哺乳动物和鸟类的设计示例,以及能跑、能飞、宠物类和非宠物类的增加对类数量的影响。 ... [详细]
  • 本文介绍了操作系统的定义和功能,包括操作系统的本质、用户界面以及系统调用的分类。同时还介绍了进程和线程的区别,包括进程和线程的定义和作用。 ... [详细]
  • 深入解析Linux下的I/O多路转接epoll技术
    本文深入解析了Linux下的I/O多路转接epoll技术,介绍了select和poll函数的问题,以及epoll函数的设计和优点。同时讲解了epoll函数的使用方法,包括epoll_create和epoll_ctl两个系统调用。 ... [详细]
author-avatar
爱与嫉妒咋
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有