(自用)learnOpenGL学习总结-高级OpenGL-模板测试


模板测试

模板测试简单来说就是一个mask,根据你的mask来保留或者丢弃片段。

那么可以用来显示什么功能呢?剪切,镂空、透明度等操作。

和深度缓冲的关系是: 先片段着色器,然后进入深度测试,最后加入模板测试。模板测试是根据又一个缓冲来进行的,它叫做模板缓冲(Stencil Buffer),我们可以在渲染的时候更新它来获得一些很有意思的效果。

具体流程为:

  • 启用模板缓冲
  • 渲染物体, loop中更新模板内容
  •  禁用模板缓冲的写入
  • 渲染其他物体,根据模板缓冲的内容丢弃特定的片段

所以,通过使用模板缓冲,我们可以根据场景中已绘制的其它物体的片段,来决定是否丢弃特定的片段。

这段话很有意思,其他物体的片段可以用来下一次的模板。

和深度一样,都需要先开启,然后再每次迭代前清除

glEnable(GL_STENCIL_TEST);
...
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);



模板函数

和深度函数一样,我们也可以自定义控制模板测试。

但是这个函数只描述了模板缓冲内容如何变化,还需要知道如何更新缓冲

实例-物体轮廓

会通过模板测试来给物体周围加上一圈色带。就像是游戏中选中一个单位之后那个单位会高亮标出一样。步骤如下:

思路很简单,这个过程将每个物体的片段的模板缓冲设置为1,当我们想要绘制边框的时候,我们主要绘制放大版本的物体中模板测试通过的部分,也就是物体的边框的位置。我们主要使用模板缓冲丢弃了放大版本中属于原物体片段的部分。

首先重新定义一个片段着色器,用来画边框,直接输出一个颜色就好。

#version 330 core									
																
out vec4 FragColor;								   
void main(){				
	FragColor = vec4(0.04, 0.28, 0.26, 1.0);
}

接下来我先按照教程的思路来做,然后我自己再多测试测试其他情况。

我现在只给箱子加上边框,我不想给机器人加边框。所有我应该先画机器人再画箱子。第一遍画正常的箱子,并写入模板缓冲,然后画放大的箱子,并丢弃覆盖了之前箱子的那些片段。

首先开启模板测试

glEnable(GL_STENCIL_TEST);
glStencilOp(GL_KEEP, GL_KEEP, GL_REPLACE);

最后一个gl_replace,表示当深度和模板测试都通过的时候,那么我们希望将储存的模板值设置为参考值,参考值能够通过glStencilFunc来设置,我们之后会设置为1。

先来看下对于箱子如何操作。

				glStencilFunc(GL_ALWAYS, 1, 0xFF); // 更新模板缓冲函数,所有的片段都要写入模板
				glStencilMask(0xFF); // 启用模板缓冲写入
				//正常绘制十个正方体,而后记录模板值
				cube.DrawArray(myMaterial->shader, myMaterial->diffuse, myMaterial->specular, myMaterial->emission);

				//现在模板缓冲在箱子被绘制的地方都更新为1了,我们将要绘制放大的箱子,也就是绘制边框
				glStencilFunc(GL_NOTEQUAL, 1, 0xFF);
				glStencilMask(0x00); // 禁止模板缓冲的写入
				glDisable(GL_DEPTH_TEST);
				border->use();

				//Set Model matrix
				modelMat = glm::translate(glm::mat4(1.0f), cubePositions[i - 1]);
				float angle = 20.0f * (i - 1);
				modelMat = glm::rotate(modelMat, glm::radians(angle), glm::vec3(1.0f, 0.3f, 0.5f));
				modelMat = glm::scale(modelMat, glm::vec3(1.2, 1.2, 1.2));

				glUniformMatrix4fv(glGetUniformLocation(border->ID, "modelMat"), 1, GL_FALSE, glm::value_ptr(modelMat));
				glUniformMatrix4fv(glGetUniformLocation(border->ID, "viewMat"), 1, GL_FALSE, glm::value_ptr(viewMat));
				glUniformMatrix4fv(glGetUniformLocation(border->ID, "projMat"), 1, GL_FALSE, glm::value_ptr(projMat));

				//因为之前设置了GL_NOTEQUAL,它会保证我们只绘制箱子上模板值不为1的部分
				cube.DrawArray(border, 1, 1, 1);
				glStencilMask(0xFF);
				glEnable(GL_DEPTH_TEST);

先通过glstenciFunc和glstenciMask来更新模板缓冲和启用模板缓存写入。

然后再draw的过程中,所有箱子绘制的地方都会被设置为1。其他地方都是0.

然后就是要画变大的箱子,但是这里就不能再写入了,而是比较

可以看到在第二次的时候模板函数为gl_notequal 保证只绘制箱子模板值不为1的地方。可以想象一个场景,在之前的模板缓冲中,有箱子的地方都为1,其他都是0.然后现在画一个大一点的箱子上去。有1的地方就不画了,只在不为1的地方画,也就是边框之外的地方。

而且 可以看到这里再画第二次的时候禁止了深度测试

现在来分析一下为什么会有这样的画面。

在机器人后面的那个箱子为什么露出来的部分是原来的,被机器人盖住的地方以及边框部分是纯色?为什么边框是连在一起的?

有这样的一个时间轴,在画的过程中依次进行了机器人、箱子、放大箱子的绘制。

其中,深度测试覆盖机器人和箱子,模板测试覆盖箱子和放大的箱子。

ok,现在按照时间轴走下去吧。

  • 首先画了一个机器人,因为有深度测试,所以更新了深度缓冲。又因为glStencilMask(0x00); 保证我们在绘制机器人的时候不会更新模板缓冲。
  • 现在轮到画箱子了,并启用了模板缓冲写入。所以在画箱子的过程中所有画着箱子的地方模板为1,其他为0.(但是因为深度测试,被遮挡的地方没有画箱子,也就是被遮挡的地方模板缓冲为0
  • 最后就是画放大的箱子。而且这时候是关闭了深度测试,也就是说 现在画的纯色是在最上面一层的。那么画在什么地方呢,画在放大的箱子覆盖的地方中模板缓冲为0的地方。

还记得之前的glStencilOp(GL_KEEP, GL_KEEP, GL_REPLACE);,这个表示的意思是只有在深度和模板都开启的情况下去吧储存的模板值设置为参考值。

这也就解释了为什么箱子被盖住的地方会被画上纯色了。又因为没有了深度,纯色色块是连在一起的,没有前后之分。

一些测试

现在我们如何不把深度关上,会出现什么情况呢?

这里面由几个地方比较奇怪,可以看到蓝圈这里,前面的边框挡住了后面的木箱本体,而红圈内的边框被后面木箱挡住。

这里是个疑问点,我暂时还想不出来如何解释。唯一能解释的是因为深度的原因,红圈部分的边框深度在后面。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mfbz.cn/a/355041.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Linux第37步_解决“Boot interface 6 not supported”之问题

在使用USB OTG将“自己移植的固件”烧写到eMMC中时,串口会输出“Boot interface 6 not supported”,发现很多人踩坑,我也一样。 见下图: 解决办法: 1、打开终端 输入“ls回车”,列出当前目录下所有的文件…

Centos7 升级Docker 至最新版本

卸载旧版本的Docker yum remove docker docker-client docker-client-latest docker-common docker-latest docker-latest-logrotate docker-logrotate docker-engine 安装需要的软件包 yum install -y yum-utils device-mapper-persistent-data lvm2 添加Docker的yum源 #yu…

防范[myers@airmail.cc].mkp攻击:解密[myers@airmail.cc].mkp勒索病毒的方法

引言: 随着科技的迅猛发展,网络安全问题日益突出,而勒索病毒也成为当前互联网威胁中的一大焦点。其中,[datastorecyberfear.com].mkp [hendersoncock.li].mkp [hudsonLcock.li].mkp[myersairmail.cc].mkp勒索病毒以其强大的加密能…

QT学习日记 | 初始QT

目录 一、创建QT文件 二、目录结构讲解 1、.pro文件 2、源文件与头文件 3、编译运行 4、界面文件 三、梦开始的地方(Hello World!) 1、代码方式 2、拖拽方式 四、Qt中的“容器” 五、Qt的对象树机制 1、对象树的引入 2、对象树…

Java 的文件类的学习总结

目录 一、File 的创建 二、File 类的常用方法 一、File 的创建 二、File 类的常用方法

开始学习第二十五天(番外)

今天分享一下写的小游戏啦 头文件game.h #include<stdio.h> #include<time.h> #include<stdlib.h> #define H 3 #define L 3 void InitBoard(char Board[H][L], int h, int l); void DisplayBoard(char Board[H][L], int h, int l); void playermove(cha…

【开源】基于JAVA+Vue+SpringBoot的智慧家政系统

目录 一、摘要1.1 项目介绍1.2 项目录屏 二、功能模块三、系统展示四、核心代码4.1 查询家政服务4.2 新增单条服务订单4.3 新增留言反馈4.4 小程序登录4.5 小程序数据展示 五、免责说明 一、摘要 1.1 项目介绍 基于微信小程序JAVAVueSpringBootMySQL的智慧家政系统&#xff0…

OpenCV-29 自适应阈值二值化

一、引入 在前面的部分我们使用的是全局阈值&#xff0c;整幅图像采用同一个数作为阈值。当时这种方法并不适应于所有情况。尤其是当同一幅图像上的不同部分具有不同的亮度时。这种情况下我们需要采用自适应阈值。此时的阈值时根据图像上的每一个小区域计算与其对应的阈值。因此…

Less-1(sqlmap自动注入攻击)--sqli

环境准备 打开火狐浏览器&#xff0c;进入sqli第一关的页面 工具准备 sqlmap 参数解释 -u URL 指定目标URL进行注入测试。--dataDATA指定POST请求的数据进行注入测试--cookieCOOKIE指定用于身份验证的cookie进行注入测试-p PARAMETER指定要测试的参数--levelLEVEL设置测试的深…

微信小程序开发 逐级选择地区

1.需求 微信小程序开发,逐级选择地区&#xff08;市、区县、街道、社区、网格&#xff09;&#xff0c;选择每一级然后展示下一级数据。 微信小程序逐级选择 2. 完整代码 2.1. 选择界面 2.1.1. selectArea.wxml <text bindtap"selectGrid">{{gridName}}</…

Java技术栈 —— Hadoop入门(二)

Java技术栈 —— Hadoop入门&#xff08;二&#xff09; 一、用MapReduce对统计单词个数1.1 项目流程1.2 可能遇到的问题1.3 代码勘误1.4 总结 一、用MapReduce对统计单词个数 1.1 项目流程 (1) 上传jar包。 (2) 上传words.txt文件。 (3) 用hadoop执行jar包的代码&#xff0c;…

go语言基础之泛型

1.泛型 泛型是一种独立于所使用的特定类型的编写代码的方法。使用泛型可以编写出适用于一组类型中的任何一种的函数和类型。 1.1 为什么需要泛型 func reverse(s []int) []int {l : len(s)r : make([]int, l)for i, e : range s {r[l-i-1] e}return r }fmt.Println(reverse…

ACL、VLAN、NAT笔记

一、ACL ---访问控制列表 1.ACL的作用 1&#xff0c;访问控制&#xff1a;在路由器流量流入或流出的接口上&#xff0c;匹配流量&#xff0c;然后 执行设定好的动作。 ---- permit 允许 , deny 拒绝 2&#xff0c;抓取感兴趣流&#xff1a;ACL可以和其他服务结合使用。ACL只…

Mac安装nvm,安装多个不同版本node,指定node版本

一.安装nvm brew install nvm二。配置文件 touch ~/.zshrc echo export NVM_DIR~/.nvm >> ~/.zshrc echo source $(brew --prefix nvm)/nvm.sh >> ~/.zshrc三.查看安装版本 nvm -vnvm常用命令如下&#xff1a;nvm ls &#xff1a;列出所有已安装的 node 版本nvm…

CMake 完整入门教程(五)

CMake 使用实例 13.1 例子一 一个经典的 C 程序&#xff0c;如何用 cmake 来进行构建程序呢&#xff1f; //main.c #include <stdio.h> int main() { printf("Hello World!/n"); return 0; } 编写一个 CMakeList.txt 文件 ( 可看做 cmake 的…

导航页配置服务Dashy本地部署并实现公网远程访问

文章目录 简介1. 安装Dashy2. 安装cpolar3.配置公网访问地址4. 固定域名访问 简介 Dashy 是一个开源的自托管的导航页配置服务&#xff0c;具有易于使用的可视化编辑器、状态检查、小工具和主题等功能。你可以将自己常用的一些网站聚合起来放在一起&#xff0c;形成自己的导航…

Unity 中介者模式 (实例详解)

文章目录 简介实例1&#xff1a;玩家与UI交互实例2&#xff1a;战斗模块中的攻击事件协调实例3&#xff1a;游戏场景中的事件广播实例4&#xff1a;模块间通信 - 地图导航与角色移动实例5&#xff1a;UI模块间同步 - 菜单切换与选项状态 简介 在Unity游戏开发中&#xff0c;中…

SpringBoot内置工具类

Collections java.util包下的Collections类&#xff0c;该类主要用于操作集合或者返回集合 一、排序 List<Integer> list new ArrayList<>();list.add(2);list.add(1);list.add(3);Collections.sort(list);//升序System.out.println(list);Collections.reverse(…

【机器学习】欠拟合与过拟合

过拟合&#xff1a;模型在训练数据上表现良好对不可见数据的泛化能力差。 欠拟合&#xff1a;模型在训练数据和不可见数据上泛化能力都很差。 欠拟合常见解决办法&#xff1a; &#xff08;1&#xff09;增加新特征&#xff0c;可以考虑加入特征组合、高次特征&#xff0c;以…

MySql45讲-08.事务到底是隔离的还是不隔离的?(结合MVCC视频)

命令的启动时机 begin/start transaction 命令并不是一个事务的起点&#xff0c;在执行到它们之后的第一个操作InnoDB表的语句&#xff0c;事务才真正启动。如果你想要马上启动一个事务&#xff0c;可以使用start transaction with consistent snapshot 这个命令。 事务的版本…
最新文章