NDK OpenGLES3.0 开发(十一):模板测试

Stella981
• 阅读 304

NDK OpenGLES3.0 开发(十一):模板测试

OpenGL ES 模板测试

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

模板测试与深度测试类似,主要作用是利用模板缓冲区(Stencil Buffer)所保存的模板值决定当前片段是否被丢弃,且发生于深度测试之前。

NDK OpenGLES3.0 开发(十一):模板测试

模板测试过程

图片来源:

https://learnopengl.com/Advanced-OpenGL/Stencil-testing

模板测试一般步骤:

1. 启用模板测试,开启模板缓冲写入 glStencilMask(0xFF)

2. 执行渲染操作,更新模板缓冲区;

3. 关闭模板缓冲写入****glStencilMask(0x00)****

4. 执行渲染操作,利用模板缓冲区所保存的模板值确定是否丢弃特定片段。

启用模板测试

glEnable(GL_STENCIL_TEST);

清空模板缓冲区

glClear( GL_STENCIL_BUFFER_BIT);

控制模板缓冲区是否可以进行写入:

// 0xFF == 0b11111111// 模板值与它进行按位与运算结果是模板值,模板缓冲可写glStencilMask(0xFF); // 0x00 == 0b00000000 == 0// 模板值与它进行按位与运算结果是0,模板缓冲不可写glStencilMask(0x00); 

模板测试的配置函数****glStencilFunc 和 glStencilOp。****

void glStencilFunc(GLenum func, GLint ref, GLuint mask);

参数说明:

  • func:设置模板测试操作。这个测试操作应用到已经储存的模板值和****glStencilFunc**** 的 ref 值上,可用的选项是:
    GL_NEVER、GL_LEQUAL、GL_GREATER、GL_GEQUAL、GL_EQUAL、GL_NOTEQUAL、GL_ALWAYS ;

  • ref:指定模板测试的引用值。模板缓冲区中的模板值会与这个值对比;

  • mask:指定一个遮罩,在模板测试对比引用值和储存的模板值前,对它们进行按位与(and)操作,初始设置为 1 。

    glStencilFunc(GL_EQUAL, 1, 0xFF);// 表示当一个片段模板值等于(GL_EQUAL)引用值1,片段就能通过测试被绘制了,否则就会被丢弃。glStencilFunc(GL_ALWAYS, 1, 0xFF);// 表示所有片段模板测试总是通过。

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_WRAP

与 GL_INCR 一样将模板值 +1 ,如果模板值已经是最大值则设为 0

GL_DECR

如果模板值不是最小值就将模板值 -1

GL_DECR_WRAP

与 GL_DECR 一样将模板值 -1 ,如果模板值已经是最小值则设为最大值

GL_INVERT

按位反转当前模板缓冲区的值

绘制物体轮廓是模板测试的常见应用,其步骤一般如下:

  • 启动深度测试和模板测试,清空模板缓冲和深度缓冲;

  • 在绘制物体前,用 1 更新物体将被渲染的片段对应的模板值;

  • 渲染物体,写入模板缓冲区;

  • 关闭模板写入和深度测试;

  • 将物体放大一定比例;

  • 使用一个不同的片段着色器用来输出一个纯颜色(物体轮廓颜色);

  • 再次绘制物体,当片段的模板值不为 1 时,片段通过测试进行渲染;

  • 开启模板写入和深度测试。

关键实现的代码片段:

//启动深度测试和模板测试,清空模板和深度缓冲glClear(GL_STENCIL_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);glEnable(GL_DEPTH_TEST);glEnable(GL_STENCIL_TEST);glStencilFunc(GL_ALWAYS, 1, 0xFF); //所有片段都要写入模板缓冲glStencilOp(GL_KEEP, GL_KEEP, GL_REPLACE);//若模板测试和深度测试都通过了,将片段对应的模板值替换为1glStencilMask(0xFF);//绘制物体glBindVertexArray(m_VaoId);glUseProgram(m_ProgramObj);glUniform3f(m_ViewPosLoc, 0.0f, 0.0f, 3.0f);glActiveTexture(GL_TEXTURE0);glBindTexture(GL_TEXTURE_2D, m_TextureId);glUniform1i(m_SamplerLoc, 0);UpdateMatrix(m_MVPMatrix, m_ModelMatrix, m_AngleX, m_AngleY , 1.0, glm::vec3(0.0f,  0.0f,  0.0f), ratio);glUniformMatrix4fv(m_MVPMatLoc, 1, GL_FALSE, &m_MVPMatrix[0][0]);glUniformMatrix4fv(m_ModelMatrixLoc, 1, GL_FALSE, &m_ModelMatrix[0][0]);glDrawArrays(GL_TRIANGLES, 0, 36);glBindVertexArray(0);glStencilFunc(GL_NOTEQUAL, 1, 0xFF);//当片段的模板值不为 1 时,片段通过测试进行渲染//禁用模板写入和深度测试glStencilMask(0x00);glDisable(GL_DEPTH_TEST);//绘制物体轮廓glBindVertexArray(m_VaoId);glUseProgram(m_OutlineProgramObj);//放大 1.05 倍UpdateMatrix(m_MVPMatrix, m_ModelMatrix, m_AngleX, m_AngleY, 1.05, glm::vec3(0.0f,  0.0f,  0.0f), ratio);glUniformMatrix4fv(m_MVPMatLoc, 1, GL_FALSE, &m_MVPMatrix[0][0]);glUniformMatrix4fv(m_ModelMatrixLoc, 1, GL_FALSE, &m_ModelMatrix[0][0]);glDrawArrays(GL_TRIANGLES, 0, 36);glBindVertexArray(0);//开启模板写入和深度测试glStencilMask(0xFF);glEnable(GL_DEPTH_TEST);glDisable(GL_STENCIL_TEST);

另外需要注意,在使用 GLSurfaceView 时,新的 API 默认没有配置模板缓冲区,需要使用setEGLConfigChooser配置模板缓冲区。

public MyGLSurfaceView(Context context, AttributeSet attrs) {    super(context, attrs);    this.setEGLContextClientVersion(2);    mGLRender = new MyGLRender();    /*If no setEGLConfigChooser method is called,    then by default the view will choose an RGB_888 surface with a depth buffer depth of at least 16 bits.*/    setEGLConfigChooser(8, 8, 8, 8, 16, 16);//最后 2 个参数表示分别配置 16 位的深度缓冲区和模板缓冲区    setRenderer(mGLRender);    setRenderMode(RENDERMODE_WHEN_DIRTY);}

利用模板测试绘制物体轮廓效果图:

NDK OpenGLES3.0 开发(十一):模板测试

利用模板测试绘制物体轮廓效果图

实现代码见阅读原文。

-- END --

NDK OpenGLES3.0 开发(十一):模板测试

字节流动

本文分享自微信公众号 - 字节流动(google_developer)。
如有侵权,请联系 support@oschina.cn 删除。
本文参与“OSC源创计划”,欢迎正在阅读的你也加入,一起分享。

点赞
收藏
评论区
推荐文章
秃头王路飞 秃头王路飞
2个月前
webpack5手撸vue2脚手架
webpack5手撸vue相信工作个12年的小伙伴们在面试的时候多多少少怕被问到关于webpack方面的知识,本菜鸟最近闲来无事,就尝试了手撸了下vue2的脚手架,第一次发帖实在是没有经验,望海涵。languageJavaScript"name":"vuecliversion2","version":"1.0.0","desc
光头强的博客 光头强的博客
2个月前
Java面向对象试题
1、请创建一个Animal动物类,要求有方法eat()方法,方法输出一条语句“吃东西”。创建一个接口A,接口里有一个抽象方法fly()。创建一个Bird类继承Animal类并实现接口A里的方法输出一条有语句“鸟儿飞翔”,重写eat()方法输出一条语句“鸟儿吃虫”。在Test类中向上转型创建b对象,调用eat方法。然后向下转型调用eat()方
刚刚好 刚刚好
2个月前
css问题
1、在IOS中图片不显示(给图片加了圆角或者img没有父级)<div<imgsrc""/</divdiv{width:20px;height:20px;borderradius:20px;overflow:h
blmius blmius
1年前
MySQL:[Err] 1292 - Incorrect datetime value: ‘0000-00-00 00:00:00‘ for column ‘CREATE_TIME‘ at row 1
文章目录问题用navicat导入数据时,报错:原因这是因为当前的MySQL不支持datetime为0的情况。解决修改sql\mode:sql\mode:SQLMode定义了MySQL应支持的SQL语法、数据校验等,这样可以更容易地在不同的环境中使用MySQL。全局s
小森森 小森森
2个月前
校园表白墙微信小程序V1.0 SayLove -基于微信云开发-一键快速搭建,开箱即用
后续会继续更新,敬请期待2.0全新版本欢迎添加左边的微信一起探讨!项目地址:(https://www.aliyun.com/activity/daily/bestoffer?userCodesskuuw5n)\2.Bug修复更新日历2.情侣脸功能大家不要使用了,现在阿里云的接口已经要收费了(土豪请随意),\\和注意
晴空闲云 晴空闲云
2个月前
css中box-sizing解放盒子实际宽高计算
我们知道传统的盒子模型,如果增加内边距padding和边框border,那么会撑大整个盒子,造成盒子的宽度不好计算,在实务中特别不方便。boxsizing可以设置盒模型的方式,可以很好的设置固定宽高的盒模型。盒子宽高计算假如我们设置如下盒子:宽度和高度均为200px,那么这会这个盒子实际的宽高就都是200px。但是当我们设置这个盒子的边框和内间距的时候,那
艾木酱 艾木酱
1个月前
快速入门|使用MemFire Cloud构建React Native应用程序
MemFireCloud是一款提供云数据库,用户可以创建云数据库,并对数据库进行管理,还可以对数据库进行备份操作。它还提供后端即服务,用户可以在1分钟内新建一个应用,使用自动生成的API和SDK,访问云数据库、对象存储、用户认证与授权等功能,可专
Wesley13 Wesley13
1年前
cobol学习之十数据库的增删改查模板
这次连接数据库使用的是ODBC连接access数据库,里面主要是一个增删改查的模板备份,方便以后查询。000001IDENTIFICATIONDIVISION.000002PROGRAMID.SAMPLEDB2.00
Stella981 Stella981
1年前
Android So动态加载 优雅实现与原理分析
背景:漫品Android客户端集成适配转换功能(基于目标识别(So库35M)和人脸识别库(5M)),导致apk体积50M左右,为优化客户端体验,决定实现So文件动态加载.!(https://oscimg.oschina.net/oscnet/00d1ff90e4b34869664fef59e3ec3fdd20b.png)点击上方“蓝字”关注我
helloworld_28799839 helloworld_28799839
2个月前
常用知识整理
Javascript判断对象是否为空jsObject.keys(myObject).length0经常使用的三元运算我们经常遇到处理表格列状态字段如status的时候可以用到vue