wordPress如何把菜单加入导航,seo排名软件免费,网络营销推广的劣势,如何做网站快捷键的元素目录
一、概念
1、模板测试
2、模板缓冲
二、模板测试如何使用
1、开启和关闭模板测试
2、开启/禁止模板缓冲区写入
3、模板测试策略函数
4、更新模板缓冲
5、模板测试应用——物体轮廓
三、模板缓冲如何使用
1、创建模板缓冲
2、使用模板缓冲
3、模板缓冲应用——…目录
一、概念
1、模板测试
2、模板缓冲
二、模板测试如何使用
1、开启和关闭模板测试
2、开启/禁止模板缓冲区写入
3、模板测试策略函数
4、更新模板缓冲
5、模板测试应用——物体轮廓
三、模板缓冲如何使用
1、创建模板缓冲
2、使用模板缓冲
3、模板缓冲应用——描边
四、源码下载 一、概念
1、模板测试
在OpenGL中模板测试Stencil Testing是一种用于控制像素绘制的方式。它允许开发者根据模板缓冲区Stencil Buffer中的值来决定是否绘制某个像素。模板测试通常用于实现一些高级的图形效果如阴影、反射、折射等。
当片段着色器处理完片段之后模板测试(Stencil Test) 就开始执行了和深度测试一样它能丢弃一些片段。仍然保留下来的片段进入深度测试阶段深度测试可能丢弃更多。模板测试基于另一个缓冲这个缓冲叫做模板缓冲(Stencil Buffer)我们被允许在渲染时更新它来获取有意思的效果。
模板测试与深度测试类似但在渲染管线中发生在深度测试之前。模板测试也会丢弃掉一些片段只是丢弃的片段数量比深度测试少。
2、模板缓冲
模板测试是基于一个缓冲区 -- 模板缓冲区(stenci buffer),同理该缓冲区也是由我们创建窗口库创建的我使用的库是GLFW库。
模板缓冲中的模板值(Stencil Value)通常是8位的因此每个片段/像素共有256种不同的模板值译注8位就是1字节大小因此和char的容量一样是256个不同值。这样我们就能将这些模板值设置为我们链接的然后在模板测试时根据这个模板值我们就可以决定丢弃或保留它了。
StencilBuffer是模板缓冲可以用来实现一些诸如描边遮罩之类的操作。
模板测试和深度测试作用类似模板测试主要是通过对比模板缓冲区来决定是否需要对片段进行丢弃。
在渲染时更新模板缓冲区可以获取很多有趣的效果。
一个模板缓冲区通常使用8位来表示因此一个像素可以有256中表示方法。
每个窗口库都需要为你设置模板缓冲。GLFW自动做了这件事所以你不必告诉GLFW去创建它但是其他库可能没默认创建模板库所以一定要查看你使用的库的文档。GLFW 是一个 OpenGL 的应用框架支持 Linux 和 Windows。GLFW 主要用来处理特定操作系统下的特定任务例如 OpenGL 窗口管理、分辨率切换、键盘、鼠标以及游戏手柄、定时器输入、线程创建等等。 上面图示中首先要使用模板测试必须先开启glEnabIe(GL STENCIL TEST),然后先使用0来清空模板缓冲区然后开启矩形片段用1填充此时我们将1写入模板缓冲区绘制出来的图形只会显示模板缓冲区为1的像素图形。
模板测试就是通过模板缓冲中记录的模板信息实现的具体来说就是渲染管线在模板缓冲区中为每个位置的片元保存了一个“模板值”当像素需要进行模板测试时将设定的模板参考值与该片元对应位置的模板值进行比较符合条件的片元通过测试不符合条件的则被丢弃不进行渲染 二、模板测试如何使用
无论我们在渲染哪里的片段模板缓冲操作都允许我们把模板缓冲设置为一个特定值。改变模板缓冲的内容实际上就是对模板缓冲进行写入。在同一次或接下来的渲染迭代我们可以读取这些值来决定丢弃还是保留这些片段。当使用模板缓冲的时候你可以随心所欲但是需要遵守下面的原则
开启模板缓冲写入。渲染物体更新模板缓冲。关闭模板缓冲写入。渲染其他物体这次基于模板缓冲内容丢弃特定片段。
使用模板缓冲我们可以基于场景中已经绘制的片段来决定是否丢弃特定的片段。
1、开启和关闭模板测试
你可以开启GL_STENCIL_TEST来开启模板测试。接着所有渲染函数调用都会以这样或那样的方式影响到模板缓冲。
glEnable(GL_STENCIL_TEST);注意在Android中只使用这句是无法启用模板测试的还需要使用下面代码配置GLSurfaceView因为默认的配置是没有配置模板测试的。
要注意的是像颜色和深度缓冲一样在每次循环你也得清空模板缓冲。
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);2、开启/禁止模板缓冲区写入
同时和深度测试的glDepthMask函数一样模板缓冲也有一个相似函数。glStencilMask允许我们给模板值设置一个位掩码(Bitmask掩码是一种用于对数据进行编码和解码的技术通过二进制代码对目标字段进行位与运算屏蔽当前的输入位从而实现数据的部分或全部隐藏以保护敏感信息。掩码在计算机科学和数据处理中有着广泛的应用包括数据压缩、权限控制、网络编程等掩码在图像处理和对象分割等领域中用于标识和处理特定的图像区域。)它与模板值进行按位与(AND)运算决定缓冲是否可写。默认设置的位掩码都是1这样就不会影响输出但是如果我们设置为0x00所有写入模板缓冲最后都是0。这和深度缓冲的glDepthMask(GL_FALSE)很类似
// 0xFF 0b11111111
//此时模板值与它进行按位与运算结果是模板值模板缓冲可写
// 每一位写入模板缓冲时都保持原样
glStencilMask(0xFF); // 0x00 0b00000000 0
//此时模板值与它进行按位与运算结果是0模板缓冲不可写
// 每一位在写入模板缓冲时都会变成0禁用写入
//注意他不是关闭模板而是不让模板被写入关闭模板的话连判断都没了。
glStencilMask(0x00); 大多数情况你的模板遮罩stencil mask写为0x00或0xFF就行但是最好知道有一个选项可以自定义位遮罩。
3、模板测试策略函数
和深度测试类似模板测试也有对应的api函数来设置模板测试的策略如测试失败或者成功的情况下如何处理模板缓冲区。glStencilFunc函数决定一个片段的去留——是否会显示在屏幕上。通过测试则显示否则不显示。
模板测试提供了两个函数glStencilFunc and glStencilOp.
void glStencilFunc(GLenum func, GLint ref, GLuint mask)
函数有三个参数
func设置模板测试操作。这个测试操作应用到已经储存的模板值和 giStenciFunc 的 ref 值上即判断模板测试func:通过的条件例如我func设置为GL EQUALref为1那么模板测试通过的条件就是模板值等于1,可用的选项是GL_NEVER、GL_LEQUAL、GL_GREATER、GL_GEQUAL、GL_EQUAL、GL_NOTEQUAL、GL_ALWAYS。它们的语义和深度缓冲的相似。func设置模板测试成功与否的策略。包括下面几种情况:
func策略描述GL_NEVER每次模板测试都失败GL_LESS比参考值更小就通过测试GL_LEQUAL小余或等于参考值就通过测试GL_GREATER比参考值更大就通过测试GL_GEQUAL大于或等于参考值就通过测试GL_EQUAL缓冲区的值和参数值ref相等GL_NOTEQUAL缓冲区的值和参数值ref不相等GL_ALWAYS每次模板测试都成功 ref指定模板测试的参考值这个值会用来和模板缓冲区中的值进行比较来判断是否丢弃片段按照上述func参数设置的类型进行比较。mask设置一个掩码在模板缓冲区和ref参考值进行比较值前会使用mask做一个按位与的操作初始化当前值一般是在为0xFF即可保留原值。
在上面简单模板的例子里方程应该设置为
glStencilFunc(GL_EQUAL, 1, 0xFF)它会告诉OpenGL无论何时一个片段模板值等于(GL_EQUAL)引用值1片段就能通过测试被绘制了否则就会被丢弃。
但是glStencilFunc只描述了OpenGL对模板缓冲做什么而不是描述我们如何更新缓冲。这就需要glStencilOp登场了。
4、更新模板缓冲
glStencilOp函数决定片段如何改变模板。判断达成某些条件的片段要如何更新模板。这个函数决定一个片段是否并如何改变一个模板上的值。
void glStencilOp(GLenum sfail, GLenum dpfail, GLenum dppass)
函数包含三个选项我们可以指定每个选项的动作
sfail 如果模板测试失败将采取的动作。dpfail 如果模板测试通过但是深度测试失败时采取的动作。dppass 如果深度测试和模板测试都通过将采取的动作。
每个选项都可以使用下列任何一个动作。
操作描述GL_KEEP保持现有的模板值GL_ZERO将模板值置为0GL_REPLACE将模板值设置为用glStencilFunc函数设置的ref值GL_INCR如果模板值不是最大值就将模板值1GL_INCR_WRAP与GL_INCR一样将模板值1如果模板值已经是最大值则设为0GL_DECR如果模板值不是最小值就将模板值-1GL_DECR_WRAP与GL_DECR一样将模板值-1如果模板值已经是最小值则设为最大值GL_INVERT按位反转当前模板缓冲区的值
如设置
glStencilOp(GL_KEEP,GL_KEEP,GL_REPLACE);
则表示模板测试失败时保持原有模板值模板测试成功但是深度测试失败时保持原有模板值两种都成功则更新模板值为参考值。
glStencilOp函数默认设置为 (GL_KEEP, GL_KEEP, GL_KEEP) 所以任何测试的任何结果模板缓冲都会保留它的值。默认行为不会更新模板缓冲所以如果你想写入模板缓冲的话你必须像任意选项指定至少一个不同的动作。
使用glStencilFunc和glStencilOp我们就可以指定在什么时候以及我们打算怎么样去更新模板缓冲了我们也可以指定何时让测试通过或不通过。什么时候片段会被抛弃。glStencilOp一般只设置一次而glStencilFunc在绘制每个模型前都会设置一次。
5、模板测试应用——物体轮廓
下面我们使用一个案例来分析模板测试。
看了前面的部分你未必能理解模板测试是如何工作的所以我们会展示一个用模板测试实现的一个特别的和有用的功能叫做物体轮廓(Object Outlining)。 物体轮廓就像它的名字所描述的那样它能够给每个或一个物体创建一个有颜色的边。在策略游戏中当你打算选择一个单位的时候它特别有用。给物体加上轮廓的步骤如下
首先开启模板测试清空模板缓冲区开启模板缓冲区写入在绘制源物体之前设置模板测试操作为GL_ALWAYS设置参考值为1掩码为0xFF,这样可以在每次绘制的时候都可以将模板缓冲区写入1。//使用gIStencil0p选项中的GL_REPLACE将模板值设置成glStencilFunc中的ref值绘制源物体写入模板缓冲禁止模板缓冲区写入并禁止深度测试。将源物体放大一点。使用一个纯颜色的片段着色器对源物体进行绘制。绘制源物体但是此时使用GL_NOTEQUAL 的方式进行模板缓冲区绘制策略重新开启深度缓冲区和模板可写入操作。
这个过程将每个物体的片段模板缓冲设置为1当我们绘制边框的时候我们基本上绘制的是放大版本的物体的通过测试的地方放大的版本绘制后物体就会有一个边。我们基本会使用模板缓冲丢弃所有的不是原来物体的片段的放大的版本内容。
场景中的物体边框的绘制方法最后看起来像这样
glEnable(GL_DEPTH_TEST);
glStencilOp(GL_KEEP, GL_KEEP, GL_REPLACE); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);glStencilMask(0x00); // 绘制地板时确保关闭模板缓冲的写入
normalShader.Use();
DrawFloor() glStencilFunc(GL_ALWAYS, 1, 0xFF);
glStencilMask(0xFF);
DrawTwoContainers();glStencilFunc(GL_NOTEQUAL, 1, 0xFF);
glStencilMask(0x00);
glDisable(GL_DEPTH_TEST);
shaderSingleColor.Use();
DrawTwoScaledUpContainers();
glStencilMask(0xFF);
glEnable(GL_DEPTH_TEST);理解这段代码后面的模板测试的思路并不难以理解。如果还不明白尝试再仔细阅读上面的部分尝试理解每个函数的作用现在你已经看到了它的使用方法的例子。
这个边框的算法的结果在深度测试教程的那个场景中看起来像这样 三、模板缓冲如何使用
使用模板缓存分为两大部分
1、创建模板缓冲
创建Stencil用其他模型遮罩等的片段去创建模板缓冲。遮罩也完全可以不被显示出来只更新模板打开模板缓冲的写入 glStencilMask(0xFF) 用某些模型或遮罩图片写入缓冲。
2、使用模板缓冲
利用创建好的Stencil去和需要被遮罩处理的片段比较去决定目标片段是否被保留。
3、模板缓冲应用——描边
描边的例子步骤如下
1、创建模板缓冲
打开模板缓冲的写入和深度测试以正常的绘制模型和写入模板。
写入原始模型的片段绘图同时写入模板缓冲创建Mask
2、使用缓冲的部分
关闭模板缓冲写入防止模板为污染
关闭Z-test深度测试将描边画在最上面。
将原始模型放大一点改用单色shader描边色
当模板值不是1的时候写入单色中间有原模型的部位不会被写入。没有原模型的部位被写入。
注意整体绘制顺序因为关闭ZBuffer深度缓冲之后后画的一定会覆盖先画的。
四、源码下载 推荐文章
android opengl测试demo opengl模板测试_mob64ca14150f43的技术博客_51CTO博客
https://learnopengl-cn.readthedocs.io/zh/latest/04%20Advanced%20OpenGL/02%20Stencil%20testing/#_1https://zhuanlan.zhihu.com/p/620697689
OpenGL学习笔记——StencilBuffer-腾讯游戏学堂
学习OpenGL ES for Android十九— 模板测试_gles20-CSDN博客