【OpenGL】模板测试(StencilTest)

目录

一、模板测试(StencilTest)的引入

二、相关API

1、模板测试的开启/关闭

2、模板测试通过/失败后

3.效果实现

三、模板测试的封装


前言:总结于b站赵新政老师的OpenGL课程,含个人理解,如有错误,很高兴大家可以指出,我会尽快修改的OwO(赵新政的个人空间-赵新政个人主页-哔哩哔哩视频

一、模板测试(StencilTest)的引入

现在,我们想要为一个物体进行"勾边",让他更为突出。

该效果实际上是由两个盒体进行实现,首先先渲染box本体,然后边框特效则是将原本的盒体扩大一点,替换成白色材质进行渲染。

可是呢,当我们进行渲染的时候,很显然后渲染的边框效果会遮盖住前面渲染的本体。而为了解决这个问题,就要确保边框效果不会覆盖掉本体内容。因而,就出现了模板测试(StencilTest)

首先了解模板缓冲(StencilBuffer):与深度缓冲相似(一张画布),记录了当前绘制的物体覆盖了哪些像素区域(8bit,一般而言8bit就足够使用了)。

借助模板缓冲,可以知道那些像素被绘制过,从而指定规则,只有通过本规则测试的像素才能绘制出来。

在上一物体进行渲染的时候,我们将会根据颜色缓冲 更新模板缓冲 ,在进行下一个物体的渲染的时候,会将当前的颜色缓冲模板缓冲进行比较,根据设置的模板测试类型来决定是否需要对当前颜色进行渲染。

二、相关API

1、模板测试的开启/关闭

void glEnable(GL_STENCIL_TEST);//开启模板测试

void glDisable(GL_STENCIL_TEST);//关闭模板测试

模板测试条件:设置当前渲染Mesh通过模板测试的条件

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

++func:使用模板缓存中的数值与ref如何对比才算通过测试;++

++ref:参考数值++

++mask:缓存数值与参考值进行比较前,都要先与mask做AND(&)操作++

func:

GL_NEVER:总是不允许通过测试

GL_ALWAYS:总是通过测试

GL_EQUAL:相等才通过测试

GL_NOTEQUAL:不等才通过测试

GL_LESS:小于才通过测试

GL_GREATER:大于才通过测试

假如我们使用GL_EQUAL

那么假如当前的模板缓冲中的值为value=3,mask=1。

此时如果我们的模板数值ref=1

那么value&mask=1 , ref&mask=1

两个结果相同,那么当前通过了模板测试,可以进行渲染。

2、模板测试通过/失败后

void glStencilOp(GLenum sfail,GLenum zfail,GLenum zpass);

  1. sfail:模板测试失败的时候执行什么操作
  2. zfail:模板测试通过但深度测试失败时执行的操作
  3. zpass:模板测试和深度测试都通过时执行的操作

GL_KEEP:保持当前的模板值

GL_ZERO:将模板值设置为0

GL_REPLACE:将模板值设置为glStencilFunc中指定的参考值

GL_INCR:如果当前模板值小于最大值,则将模板值+1

GL_INCR_WRAP:类似于GL_INCR,但是如果增加后超过了最大值则会绕回0。

GL_DECR:如果当前模板值大于0,则将模板值减1

GL_DECR_WRAP:类似于GL_DECR,但是如果减小后小于0则会绕回最大值。

GL_INVERT:按位翻转当前的模板值

类似于深度缓冲写入,我们也可以决定是否对模板缓冲进行写入。

·开启模板测试情况下,禁止模板缓冲写入

void glStencilMask(GLuint mask)

mask:掩码的每一位对应于模板缓冲区中的每一位,,每当一个位是1时,模板缓冲区的相应位可以被写入;当一个位是0时,相应位不能被写入。

0x00:任何bit位都不允许写入

0xFF:任何bit位都允许写入

举例来讲,如果当前模板缓冲的参考值ref=3,且已经通过了模板测试,若mask=2,则其进行模板缓冲写入的时候,当前新的模板缓冲值=ref&mask=2,所以,如果mask=0x00,那么模板缓冲写入始终为0,而当mask=0xFF的时候,就会直接写入ref。

3.效果实现

因而,想要实现上述的效果,对于box本体而言,其应当开启模板测试之后,设置总是可以通过模板测试(GL_ALWAYS),并设置好ref值,模板测试mask和模板缓冲写入的mask设置为0xFF,其通过模板测试和深度测试的操作应当设置为替换(GL_REPLACE),其余为保持(GL_KEEP)

cpp 复制代码
glEnable(GL_STENCIL_TEST);
glStencilFunc(GL_ALWAYS,1,0xFF);
glStencilMask(0xFF);
glStencilOp(GL_KEEP,GL_KEEP,GL_REPLACE);

而对于白色的框体而言,就应该在开启模板测试之后设置只有当模板测试值不等(GL_NOTEQUAL)的时候才进行渲染,ref值取1,模板测试的mask设置为0xFF,模板缓冲写入的mask设置为0x00(不进行写入)

cpp 复制代码
glEnable(GL_STENCIL_TEST);
glStencilFunc(GL_NOTEQUAL,1,0xFF);
glStencilMask(0x00);

三、模板测试的封装

从设置模板测试的变量去看,我们需要为每一个材质material加入如下变量

cpp 复制代码
//Stencil模板测试
bool m_stencil_test{ false };
GLenum m_stencil_func{ GL_ALWAYS };
GLint m_stencil_ref{ 0 };
GLuint m_stencil_func_mask{ 0xFF };

GLenum m_fail{ GL_KEEP };
GLenum m_zfail{ GL_KEEP };
GLenum m_zpass{ GL_KEEP };
	
GLuint m_stencil_mask{ 0xFF };

于是,在渲染阶段,就需要根据材质material中的变量,来对模板测试进行设置,包括:

(1)是否要开启模板检测 glEnable/glDisable(GL_STENCIL_TEST)

(2)通过测试的操作设置 gl_StencilOp(fail,zfail,zpass)

(3)模板测试的比较方式 gl_StencilFunc(func,ref,mask)

(4)是否写入模板缓冲 gl_StencilMask(mask)

cpp 复制代码
void Renderer::set_stencil_test(Material* material)
{
    if (material->m_stencil_test)
    {
        glEnable(GL_STENCIL_TEST);
        glStencilOp(material->m_fail, material->m_zfail, material->m_zpass);
        glStencilFunc(material->m_stencil_func, material->m_stencil_ref, material->m_stencil_func_mask);
        glStencilMask(material->m_stencil_mask);
    }
    else
    {
        glDisable(GL_STENCIL_TEST);
    }
}

我们可以考虑去实现如下场景,对两个粘连在一起的正方体进行勾边:

在这种情况下,我们会发现,其中一个正方体的勾边遮住了另外一个正方体,在正常情况下我们是不希望这样的。

所以,为了解决这个问题,就需要关闭勾边的深度检测,这样,就可以确保勾边不会影响到原来的像素。

相关推荐
卡提西亚18 小时前
C++笔记-25-函数模板
c++·笔记·算法
ghie909018 小时前
MATLAB/Simulink水箱水位控制系统实现
开发语言·算法·matlab
多多*18 小时前
分布式系统中的CAP理论和BASE理论
java·数据结构·算法·log4j·maven
yuan1999718 小时前
基于粒子群优化(PSO)算法的PID控制器参数整定
算法
R&L_2018100119 小时前
C++之内联变量(Inline Variables)
c++·c++新特性
小白程序员成长日记19 小时前
2025.11.10 力扣每日一题
数据结构·算法·leetcode
hoiii18719 小时前
基于交替方向乘子法(ADMM)的RPCA MATLAB实现
人工智能·算法·matlab
fengfuyao98519 小时前
MATLAB的加权K-means(Warp-KMeans)聚类算法
算法·matlab·kmeans
IT阳晨。20 小时前
【QT开发】交叉编译QT程序在ARMLinux平台上运行
c++·qt·交叉编译·armlinux·代码移植
派大星爱吃猫20 小时前
C++隐藏的this指针(详解)
c++·this指针