Android OpenGL ES详解——模板Stencil

目录

一、概念

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 EQUAL,ref为1,那么模板测试通过的条件就是模板值等于1,可用的选项是:GL_NEVERGL_LEQUALGL_GREATERGL_GEQUALGL_EQUALGL_NOTEQUALGL_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将模板值置为0
GL_REPLACE将模板值设置为用glStencilFunc函数设置的ref
GL_INCR如果模板值不是最大值就将模板值+1
GL_INCR_WRAPGL_INCR一样将模板值+1,如果模板值已经是最大值则设为0
GL_DECR如果模板值不是最小值就将模板值-1
GL_DECR_WRAPGL_DECR一样将模板值-1,如果模板值已经是最小值则设为最大值
GL_INVERT按位反转当前模板缓冲区的值

如设置:

glStencilOp(GL_KEEP,GL_KEEP,GL_REPLACE);

则表示模板测试失败时,保持原有模板值,模板测试成功但是深度测试失败时保持原有模板值,两种都成功,则更新模板值为参考值。

glStencilOp函数默认设置为 (GL_KEEP, GL_KEEP, GL_KEEP) ,所以任何测试的任何结果,模板缓冲都会保留它的值。默认行为不会更新模板缓冲,所以如果你想写入模板缓冲的话,你必须像任意选项指定至少一个不同的动作。

使用glStencilFuncglStencilOp,我们就可以指定在什么时候以及我们打算怎么样去更新模板缓冲了,我们也可以指定何时让测试通过或不通过。什么时候片段会被抛弃。glStencilOp一般只设置一次,而glStencilFunc在绘制每个模型前都会设置一次。

5、模板测试应用——物体轮廓

下面我们使用一个案例来分析模板测试。

看了前面的部分你未必能理解模板测试是如何工作的,所以我们会展示一个用模板测试实现的一个特别的和有用的功能,叫做物体轮廓(Object Outlining)

物体轮廓就像它的名字所描述的那样,它能够给每个(或一个)物体创建一个有颜色的边。在策略游戏中当你打算选择一个单位的时候它特别有用。给物体加上轮廓的步骤如下:

  1. 首先开启模板测试,清空模板缓冲区,开启模板缓冲区写入
  2. 在绘制源物体之前设置模板测试操作为GL_ALWAYS,设置参考值为1,掩码为0xFF,这样可以在每次绘制的时候都可以将模板缓冲区写入1。//使用gIStencil0p选项中的GL_REPLACE将模板值设置成glStencilFunc中的ref值
  3. 绘制源物体,写入模板缓冲
  4. 禁止模板缓冲区写入并禁止深度测试。
  5. 将源物体放大一点。
  6. 使用一个纯颜色的片段着色器对源物体进行绘制。
  7. 绘制源物体,但是此时使用GL_NOTEQUAL 的方式进行模板缓冲区绘制策略
  8. 重新开启深度缓冲区和模板可写入操作。

这个过程将每个物体的片段模板缓冲设置为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博客

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

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

相关文章

QML基础语法2

函数 函数格式: function关键字 函数名(参数名1:参数类型,参数名2:参数类型,...):返回值类型{} 其中: 函数名必须以小写字符开头,后面驼峰可以有多个参数或者没有参数参数类型可以不写返回值类型也可以不写 如何调用:通过id点…

Qt自定义控件:汽车速度表

1、功能 制作一个汽车速度表 2、实现 从外到内进行绘制,初始化画布,画渐变色外圈,画刻度,写刻度文字,画指针,画扇形,画内圈渐变色,画黑色内圈,写当前值 3、效果 4、源…

Rust 力扣 - 1461. 检查一个字符串是否包含所有长度为 K 的二进制子串

文章目录 题目描述题解思路题解代码题目链接 题目描述 题解思路 长度为k的二进制子串所有取值的集合为[0, sum(k)]&#xff0c;其中sum(k)为1 2 4 … 1 << (k - 1) 我们只需要创建一个长度为sum(k) 1的数组 f &#xff0c;其中下标为 i 的元素用来标记字符串中子串…

【2024年11月高质量国际学术会议推荐1】拓展学术视野,点亮学术之路!开启科研新征程!——数学|物理|电离|能源|遥感|交通各大领域...

【2024年11月高质量国际学术会议推荐1】拓展学术视野&#xff0c;点亮学术之路&#xff01;开启科研新征程&#xff01;——数学|物理|电离|能源|遥感|交通各大领域… 【2024年11月高质量国际学术会议推荐1】拓展学术视野&#xff0c;点亮学术之路&#xff01;开启科研新征程&…

沪深A股上市公司数据报告分析

数据分析报 目录 数据分析报告 1.引言 1.1 背景介绍 1.2 报告目的 1.3 报告范围 1.4 关键术语定义 2. 数据收集与预处理 2.1 数据来源概述 2.2 数据收集过程 2.3 数据预处理步骤 3. 数据可视化 3.1分析地区对公司数量的影响 3.2分析行业分类是否影响公…

ImportError: Install xlrd >= 1.0.0 for Excel support

文章目录 一、报错问题二、问题解释三、解决方法 一、报错问题 问题描述&#xff1a; python2.7使用pandas读取excel文件时报错ImportError: Install xlrd > 1.0.0 for Excel support。 问题代码&#xff1a; # codingutf-8import pandas as pddata pd.read_excel(D:\Wo…

【Linux】make/makefile/gdb调试技巧/进度条小程序

目录 一、sudo提权&#xff1a; 二、自动化构建工具make与Makefile makefile&#xff1a; make&#xff1a; 是否重新执行make&#xff1a; 伪目标&#xff1a; 三、进度条小程序&#xff1a; 四、Linux调试器gdb&#xff1a; 1.、前景提要&#xff1a; 2、进入与退出…

Flutter实战短视频课程

1、课程导学 一套代研运行多蜡 体州一致&#xff0c;目胜能优昇 未来大趋势 不改交原生项目的基础上&#xff0c;扩展Flutter能力 Flutter原生灵话切涣 0入侵 最简单、最通用 最新Flutter 3,x新特性讲解 大量flutter官方组件和api学习 最常用的第三方库使用及原理解析 自研组…

程序员的新电脑到手后应该做哪些必要设置?

吃水果要剥皮&#xff0c;用 Windows 也一样&#xff0c;如果是 Win 10 的话&#xff0c;刚装完系统就需要屏蔽一些功能&#xff0c;才能更顺畅快速&#xff1a; 隐藏任务栏上的搜索框和小娜&#xff0c;需要搜索时wins就会出现禁用开始目录的app自动推荐删除人脉图标删除任务…

unocss 添加支持使用本地 svg 预设图标,并支持更改大小

安装 pnpm install iconify/utils 在配置文件 unocss.config.ts&#xff1a; presets > presetIcons 选项中 通过 FileSystemIconLoader 加载本地图标&#xff0c;并指定目录。 import presetWeapp from unocss-preset-weapp import { extractorAttributify, transformer…

Zig 语言通用代码生成器:逻辑,发布冒烟测试版二之二

Zig 语言通用代码生成器&#xff1a;逻辑&#xff0c;发布冒烟测试版二之二 Zig 语言通用代码生成器&#xff1a;逻辑&#xff0c;已发布冒烟测试版二。此版本完善了代码生成物。支持多对多关系。修复了所有单域动词。并有更多缺陷修复。暂时不支持图片类型。暂时不支持日期和…

Vue3的router和Vuex的学习笔记整理

一、路由的基本搭建 1、安装 npm install vue-router --registryhttps://registry.npmmirror.com 2、配置路由模块 第一步&#xff1a;src/router/index.js创建文件 第二步&#xff1a;在src/view下面创建两个vue文件&#xff0c;一个叫Home.vue和About.vue 第三步&#x…

远程连接服务

目录 一、远程连接服务器简介 二、连接加密技术简介 三、认证阶段 四、ssh实验 1.修改ssh服务器的端口号 2.拒绝root账户远程登录 3.允许特定用户ssh登录&#xff0c;其他用户无法登录 4.ssh-keygen 一、远程连接服务器简介 概念&#xff1a; 远程连接服务器通过文字或…

JS中面向对象

一、对象 1.认识对象 在JavaScript中&#xff0c;对象&#xff08;Object&#xff09;是一种复合数据类型&#xff0c;它允许你存储键值对。对象的属性是连接到对象的变量&#xff0c;而函数或方法是属于对象的函数。 JavaScript中的对象类似于哈希表&#xff0c;其中键可以是…

【工具变量】“宽带中国”试点城市名单匹配数据集(2000-2023年)

参照秦文晋&#xff08;2022&#xff09;的《网络基础设施建设对数字经济发展的影响研究——基于"宽带中国"试点政策的准自然实验》一文中的做法&#xff0c;将选为“宽带中国”试点城市的虚拟变量作为核心解释变量&#xff0c;当一个城市被批复成为“宽带中国”试点…

Matlab车牌识别课程设计报告(附源代码)

Matlab车牌识别系统 分院&#xff08;系&#xff09; 信息科学与工程 专业 学生姓名 学号 设计题目 车牌识别系统设计 内容及要求&#xff1a; 车牌定位系统的目的在于正确获取整个图像中车牌的区域&#xff0c; 并识别出车牌号。通过设计实现车牌识别系…

基于OSS搭建在线教育视频课程分享网站

OSS对象存储服务是海量、安全、低成本、高持久的存储服务。适合于存储大规模非结构化数据&#xff0c;如图片、视频、备份文件和容器/虚拟机镜像等。 安装nginx wget https://nginx.org/download/nginx-1.20.2.tar.gz yum -y install zlib zlib-devel gcc-c pcre-devel open…

Edit Data. Create Cell Editors. Validate User Input 编辑数据。创建 Cell Editors。验证用户输入

Goto Data Grid 数据网格 Edit Data. Create Cell Editors. Validate User Input 编辑数据。创建 Cell Editors。验证用户输入 Get and Modify Cell Values in Code 在代码中获取和修改单元格值 仅当 Grid 及其列已完全初始化时&#xff0c;才使用以下方法。如果需要在表单仍…

【JavaEE初阶 — 多线程】Thread的常见构造方法&属性

目录 Thread类的属性 1.Thread 的常见构造方法 2.Thread 的几个常见属性 2.1 前台线程与后台线程 2.2 setDaemon() 2.3 isAlive() Thread类的属性 Thread 类是JVM 用来管理线程的一个类&#xff0c;换句话说&#xff0c;每个线程都有一个唯一的Thread 对象与之关联&am…

【设计模式】如何用C++实现依赖倒置

【设计模式】如何用C实现依赖倒置 一、什么是依赖倒置&#xff1f; 依赖倒置原则&#xff08;Dependency Inversion Principle&#xff0c;DIP&#xff09;是SOLID面向对象设计原则中的一项。它的核心思想是&#xff1a; 高层模块不应该依赖于低层模块&#xff0c;两者都应该…