NDK OpenGL ES 3.0 开发(十一):模板测试

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

作者:字节流动

来源:https://blog.csdn.net/Kennethdroid/article/details/102533260


OpenGL ES 模板测试

image.png

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

图片来源:https://learnopengl.com/Advanced-OpenGL/Stencil-testing

模板测试一般步骤:

  • 启用模板测试,开启模板缓冲写入glStencilMask(0xFF);
  • 执行渲染操作,更新模板缓冲区;
  • 关闭模板缓冲写入glStencilMask(0x00);
  • 执行渲染操作,利用模板缓冲区所保存的模板值确定是否丢弃特定片段。

启用模板测试 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 按位反转当前模板缓冲区的值

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

  • 启动深度测试和模板测试,清空模板缓冲和深度缓冲;
  • 在绘制物体前,设置 glStencilFunc(GL_ALWAYS, 1, 0xFF);,用 1 更新物体将被渲染的片段对应的模板值;
  • 渲染物体,写入模板缓冲区;
  • 关闭模板写入和深度测试;
  • 将物体放大一定比例;
  • 使用一个不同的片段着色器用来输出一个纯颜色(物体轮廓颜色);
  • 再次绘制物体,设置 glStencilFunc(GL_NOTEQUAL, 1, 0xFF) 当片段的模板值不为 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);//若模板测试和深度测试都通过了,将片段对应的模板值替换为1
glStencilMask(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);
}

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

联系与交流

技术交流/获取源码可以添加我的微信:Byte-Flow


视频云技术」你最值得关注的音视频技术公众号,每周推送来自阿里云一线的实践技术文章,在这里与音视频领域一流工程师交流切磋。

阿里云社区.png

相关文章
|
机器学习/深度学习 人工智能 并行计算
AI部署架构:A100、H100、A800、H800、H20的差异以及如何选型?开发、测试、生产环境如何进行AI大模型部署架构?
AI部署架构:A100、H100、A800、H800、H20的差异以及如何选型?开发、测试、生产环境如何进行AI大模型部署架构?
AI部署架构:A100、H100、A800、H800、H20的差异以及如何选型?开发、测试、生产环境如何进行AI大模型部署架构?
|
传感器 人工智能 JavaScript
鸿蒙开发:DevEcoTesting中的稳定性测试
DevEcoTesting主要的目的也是用于软件的测试,可以让开发者无需复杂的配置,即可一键执行测试任务,同时提供了测试报告和分析,无论是对于开发者还是测试同学来说,都是一个非常方便的工具。
479 3
鸿蒙开发:DevEcoTesting中的稳定性测试
|
存储 测试技术 API
数据驱动开发软件测试脚本
今天刚提交了我的新作《带着ChatGPT玩转软件开发》给出版社,在写作期间跟着ChatGPT学到许多新知识。下面分享数据驱动开发软件测试脚本。
502 0
|
敏捷开发 运维 数据可视化
DevOps看板工具中的协作功能:如何打破开发、测试与运维之间的沟通壁垒
在DevOps实践中,看板工具通过可视化任务管理和自动化流程,提升开发与运维团队的协作效率。它支持敏捷开发、持续交付,助力团队高效应对需求变化,实现跨职能协作与流程优化。
|
运维 jenkins 测试技术
"还在苦等开发部署环境?3步教你用Jenkins拿回测试主动权"
测试工程师最头疼的问题是什么?依赖开发部署环境! 开发延期→测试时间被压缩→紧急上线后BUG频出→测试背锅。传统流程中,测试被动等待部署,效率低下。而Jenkins自动化部署让测试人员自主搭建环境,实现: ✅ 随时触发测试,不再苦等开发 ✅ 部署效率提升10倍,抢回测试时间 ✅ 改善团队协作,减少互相甩锅 学习Jenkins部署能力,成为高效测试工程师,告别被动等待!
|
数据采集 算法 数据安全/隐私保护
【硬件测试】基于FPGA的4ASK调制解调通信系统开发与硬件片内测试,包含信道模块,误码统计模块,可设置SNR
本文介绍了基于FPGA的4ASK调制解调系统的硬件测试版本,该系统包括testbench、高斯信道模块和误码率统计模块,并新增了ILA在线数据采集和VIO在线SNR设置功能。通过VIO设置不同SNR(如15dB和25dB),实现了对系统性能的实时监测与调整。4ASK是一种通过改变载波幅度表示数据的数字调制方式,适用于多种通信场景。FPGA平台的高效性和灵活性使其成为构建高性能通信系统的理想选择。
537 17
|
数据采集 算法 数据安全/隐私保护
【硬件测试】基于FPGA的4FSK调制解调通信系统开发与硬件片内测试,包含信道模块,误码统计模块,可设置SNR
本文基于之前的文章《基于FPGA的4FSK调制解调系统》,增加了ILA在线数据采集模块和VIO在线SNR设置模块,实现了硬件测试版本。通过VIO设置不同SNR(如10dB和20dB),并展示了ILA采集的数据结果。四频移键控(4FSK)是一种数字调制方法,利用四个不同频率传输二进制数据,具有较高的频带利用率和抗干扰性能。输入的二进制数据分为两组,每组两个比特,对应四个频率f1、f2、f3、f4,分别代表二进制组合00、01、10、11。调制过程中选择相应频率输出,并进行幅度调制以增强抗干扰能力。接收端通过带通滤波器提取信号并还原为原始二进制数据。
526 7
|
XML 小程序 Java
【Android App】三维投影OpenGL ES的讲解及着色器实现(附源码和演示 超详细)
【Android App】三维投影OpenGL ES的讲解及着色器实现(附源码和演示 超详细)
690 0
|
数据安全/隐私保护 开发者
OpenGL ES 多目标渲染(MRT)
Opengl ES连载系列
979 0
|
缓存 C++
Opengl ES之FBO
Opengl ES连载系列
611 0