OpenGL学习 跟着官网教程学习(模板测试)
创始人
2025-05-29 20:34:41
0

一,前言

这是对官网的一部分补充,看之前需要看一下官网的文章。

了解一下小小的知识点。

1,模板测试和深度测试的顺序

片段着色器------->模板测试-------->深度测试

2,什么叫模板缓冲?

就是记录了每一个像素点的模板值。(不理解也没关系,看看后面怎么用的就了解了)

如果熟悉RGBA的Alpha通道,可能会将模板缓冲理解为Alpha通道,两者很相似,但是模板缓冲比Alpha通道多了一个比较的环节,也就是glStencilFunc函数。

二,使用方法

直接上结果:

 可以看到笑脸箱子的周围包了一圈红色的框,这个框就是使用模板测试实现的。

我们直接从代码着手,通过分析代码的作用来解释模板测试的相关函数的作用。

1,启动模板测试

既然要使用模板测试,那么就需要开启模板测试(深度测试是上一节的内容),直接上代码:

	/*启用深度测试*/glEnable(GL_DEPTH_TEST);//在片段深度值小于缓冲的深度值时通过测试glDepthFunc(GL_LESS);//启用GL_STENCIL_TEST来启用模板测试glEnable(GL_STENCIL_TEST);

2,设置模板测试的参数

	//( ref & mask ) != ( stencil & mask )则通过。glStencilFunc(GL_NOTEQUAL, 1, 0xFF);

函数原型是:

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

 其中这三个参数表示:

   func:设置模板测试函数(Stencil Test Function)。这个测试函数将会应用到已储存的模板值上和glStencilFunc函数的ref值上。

   ref:设置了模板测试的参考值(Reference Value)。模板缓冲的内容将会与这个值进行比较。

   mask:设置一个掩码,它将会与参考值和储存的模板值在测试比较它们之前进行与(AND)运算。初始情况下所有位都为1。

        下面就是func的值和使用方法:

func接受以下值:GL_NEVER总是失败。GL_LESS( ref & mask ) < ( stencil & mask ) 则通过。GL_LEQUAL( ref & mask ) <= ( stencil & mask )则通过。GL_GREATER( ref & mask ) > ( stencil & mask )则通过。GL_GEQUAL( ref & mask ) >= ( stencil & mask )则通过。GL_EQUAL( ref & mask ) = ( stencil & mask )则通过。GL_NOTEQUAL( ref & mask ) != ( stencil & mask )则通过。GL_ALWAYS总是通过。

 但是glStencilFunc仅仅描述了OpenGL应该对模板缓冲内容做什么,而不是我们应该如何更新缓冲。这就需要glStencilOp这个函数了。

	/*glStencilOp(GLenum sfail, GLenum dpfail, GLenum dppass)一共包含三个选项,我们能够设定每个选项应该采取的行为:sfail:模板测试失败时采取的行为。dpfail:模板测试通过,但深度测试失败时采取的行为。dppass:模板测试和深度测试都通过时采取的行为。*/glStencilOp(GL_KEEP, GL_KEEP, GL_REPLACE);

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一样,但如果模板值超过了最大值则归零
GL_DECR如果模板值大于最小值则将模板值减1
GL_DECR_WRAP与GL_DECR一样,但如果模板值小于0则将其设置为最大值
GL_INVERT按位翻转当前的模板缓冲值

在模板测试和深度测试都通过时设置的参数为GL_REPLACE,也就是说,我们可以通过设置glStencilFunc函数的ref值来设置模板缓冲的值,也就是把下图中的1设置成别的参数。

3,绘制 

 回顾一下之前的结果图,我们是希望绘制箱子和箱子外轮廓,那么如何绘制箱子的外轮廓呢?

我们可以将外轮廓想象一个更大的箱子,所以我们绘制一个比箱子a更大的箱子A,但是,在绘制了箱子A之后,由于深度测试,箱子A会覆盖箱子a,那么我们就希望,在绘制箱子A的时候,不要绘制箱子a存在的片段。

(如果在绘制的时候不开启深度测试,这样只需要先绘制箱子A,在绘制箱子a就行,但是这样有一个问题就是,当箱子一部分埋在地板中的时候,由于没有深度测试,箱子整个会被绘制,显示在窗口上)

模板测试就可以做到这一点,流程如下:

  1. 运行一下函数:                                                                                            glStencilFunc(GL_NOTEQUAL, 1, 0xFF);                                                      glStencilOp(GL_KEEP, GL_KEEP, GL_REPLACE);
  2. 在绘制(需要添加轮廓的)物体(箱子a)之前,将模板函数设置为GL_ALWAYS,每当物体的片段被渲染时,将模板缓冲更新为1。
  3. 渲染物体。
  4. 禁用模板写入以及深度测试。
  5. 将每个物体缩放一点点(箱子A)。
  6. 使用一个不同的片段着色器,输出一个单独的(边框)颜色。
  7. 再次绘制物体,但只在它们片段的模板值不等于1时才绘制。
  8. 再次启用模板写入和深度测试。

我们一步一步来解释:

第一步的作用是在模板测试失败时采取 和 模板测试通过,但深度测试失败时采取时保持模板缓冲内部的值不变。模板测试和深度测试都通过时更新模板缓冲的对应模板值为1(glStencilFunc由这个函数设置模板值为多少)。

第二步就是将绘制箱子a那部分模板值设置为1.因为箱子a可以通过模板测试和深度测试。

代码如下:

		glStencilFunc(GL_ALWAYS, 1, 0xFF);glStencilMask(0xFF);// cubesglBindVertexArray(VAO);glActiveTexture(GL_TEXTURE0);glBindTexture(GL_TEXTURE_2D, cubeTexture);model = glm::translate(model, glm::vec3(-1.0f, 0.0f, -1.0f));shader.setMat4("model", model);glDrawArrays(GL_TRIANGLES, 0, 36);model = glm::mat4(1.0f);model = glm::translate(model, glm::vec3(2.0f, 0.0f, 0.0f));shader.setMat4("model", model);glDrawArrays(GL_TRIANGLES, 0, 36);

第四,五,六,七步的作用:禁止写入模板测试,也就是说在绘制箱子A的时候,模板值不变,而在绘制箱子A和箱子a相互重叠的部分的时候,箱子A会进行模板测试,在模板缓冲为1的时候,箱子A无法通过模板测试,不进行绘制。

代码如下:

		glStencilFunc(GL_NOTEQUAL, 2, 0xFF);glStencilMask(0x00);//glDisable(GL_DEPTH_TEST);shaderSingleColor.use();float scale = 1.1f;// cubesglBindVertexArray(VAO);glBindTexture(GL_TEXTURE_2D, cubeTexture);model = glm::mat4(1.0f);model = glm::translate(model, glm::vec3(-1.0f, 0.0f, -1.0f));model = glm::scale(model, glm::vec3(scale, scale, scale));shaderSingleColor.setMat4("model", model);glDrawArrays(GL_TRIANGLES, 0, 36);model = glm::mat4(1.0f);model = glm::translate(model, glm::vec3(2.0f, 0.0f, 0.0f));model = glm::scale(model, glm::vec3(scale, scale, scale));shaderSingleColor.setMat4("model", model);glDrawArrays(GL_TRIANGLES, 0, 36);glBindVertexArray(0);glStencilMask(0xFF);glStencilFunc(GL_ALWAYS, 0, 0xFF);glEnable(GL_DEPTH_TEST);

那么有个问题,如果在绘制箱子A的时候,不关闭深度测试会怎么样?结果如下:

放大的箱子A,即边框,会被地板所覆盖。

 4,完整代码:

Code Viewer. Source code: src/4.advanced_opengl/2.stencil_testing/stencil_testing.cpp

相关内容

热门资讯

监控摄像头接入GB28181平... 流程简介将监控摄像头的视频在网站和APP中直播,要解决的几个问题是:1&...
Windows10添加群晖磁盘... 在使用群晖NAS时,我们需要通过本地映射的方式把NAS映射成本地的一块磁盘使用。 通过...
protocol buffer... 目录 目录 什么是protocol buffer 1.protobuf 1.1安装  1.2使用...
Fluent中创建监测点 1 概述某些仿真问题,需要创建监测点,用于获取空间定点的数据࿰...
educoder数据结构与算法...                                                   ...
MySQL下载和安装(Wind... 前言:刚换了一台电脑,里面所有东西都需要重新配置,习惯了所...
MFC文件操作  MFC提供了一个文件操作的基类CFile,这个类提供了一个没有缓存的二进制格式的磁盘...
在Word、WPS中插入AxM... 引言 我最近需要写一些文章,在排版时发现AxMath插入的公式竟然会导致行间距异常&#...
有效的括号 一、题目 给定一个只包括 '(',')','{','}'...
【Ctfer训练计划】——(三... 作者名:Demo不是emo  主页面链接:主页传送门 创作初心ÿ...