OpenGL之模板测试

橙三吉。 提交于 2020-01-11 06:53:22

模板测试

当片段着色器处理完一个片段之后,模板测试(Stencil Test)会开始执行,和深度测试一样,它也可能会丢弃片段。接下来,被保留的片段会进入深度测试,它可能会丢弃更多的片段。模板测试是根据又一个缓冲来进行的,它叫做模板缓冲(Stencil Buffer)

模板测试需要模板缓存,GLFW默认创建。
如果自己创建的帧缓存没有模板缓存,则模板测试总是通过。

三个相关函数 参考1 参考2

(1)glStencilMask(GLunit mask)设置一个模板缓冲区的位遮罩

glStencilMask允许我们设置一个位掩码(Bitmask),它会与将要写入缓冲的模板值进行与(AND)运算

glStencilMask(0xFF); // 每一位写入模板缓冲时都保持原样

//禁止模板缓冲的写入
glStencilMask(0x00); // 每一位在写入模板缓冲时都会变成0(禁用写入)

glStencilMask(GLunit mask)
设置一个模板缓冲区的位遮罩,默认位遮罩为1。

模板测试最后,当测试通过要写入新的模板值时,新的模板值会与位遮罩进行按位与(AND)运算,不为0的位才会写入(注意不是写入整个模板值)。比如:现在要写入的模板值是2(10),但是位遮罩是5(101),与后每位都为0,所以最后什么都没有写入

(2)设置比较函数、引用值和掩码glStencilFunc
glStencilFunc(GLenum func, GLint ref,Gluint mask);

func:设置模板测试函数。用于比较已储存的模板值上和glStencilFunc函数的ref值上。可用的选项
有:GL_NEVER,GL_LESS,GL_LEQUAL,GL_GREATER,GL_GEQUAL,GL_EQUAL,GL_NOTEQUAL和GL_ALWAYS

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

mask:设置一个掩码,它将会与参考值和储存的模板值在测试比较它们之前进行与(AND)运算。初始情况下所有位都为1.
行为 含义
GL_NEVER Always fails
GL_LESS Passes if ( ref & mask ) < ( stencil & mask )
GL_LEQUAL Passes if ( ref & mask ) <= ( stencil & mask )
GL_GREATER Passes if ( ref & mask ) > ( stencil & mask )
GL_GEQUAL Passes if ( ref & mask ) >= ( stencil & mask )
GL_EQUAL Passes if ( ref & mask ) = ( stencil & mask )
GL_NOTEQUAL Passes if ( ref & mask ) != ( stencil & mask )
GL_ALWAYS Always passes
(3)指示如何更新缓冲,需要glStencilOp函数:
glStencilOp(GLenum sfail,GLenum dpfail,GLenum dppass);

sfail:模板测试失败时采取的行为

dpfail:模板测试通过,但深度测试失败时采取的行为。

dppass:模板测试和深度测试都通过时采取的行为。

在这里插入图片描述

模板缓冲简单例子

在这里插入图片描述
模板缓冲首先会被清除为0,之后在模板缓冲中使用1填充了一个空心矩形。场景中的片段将会只在片段的模板值为1的时候会被渲染(其它的都被丢弃了)

大体步骤
(1)启用模板缓冲的写入。

glEnable(GL_DEPTH_TEST);
glDepthFunc(GL_LESS);
glEnable(GL_STENCIL_TEST);
glStencilFunc(GL_NOTEQUAL, 1, 0xFF);
glStencilOp(GL_KEEP, GL_KEEP, GL_REPLACE)

(2)渲染物体,更新模板缓冲的内容。

glClearColor(0.1f, 0.1f, 0.1f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT); // don't forget to clear the stencil buffer!

(3)禁用模板缓冲的写入。

// draw floor as normal, but don't write the floor to the stencil buffer, we only care about the containers. We set its mask to 0x00 to not write to the stencil buffer.
//正常绘制地板,但不要将地板写入模具缓冲区,我们只关心容器。我们将它的掩码设置为0x00,以避免写入模板缓冲区。
 glStencilMask(0x00);

(4)渲染(其它)物体,这次根据模板缓冲的内容丢弃特定的片段。

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);  

ubuntu下代码中注意事项

完整代码
在这里插入图片描述

(1)#include <boost/filesystem.hpp>的使用
unsigned int cubeTexture = loadTexture(FileSystem::getPath("resources/textures/marble.jpg").c_str());
unsigned int floorTexture = loadTexture(FileSystem::getPath("resources/textures/metal.png").c_str());

修改为

unsigned int cubeTexture = loadTexture(boost::filesystem::path("textures/marble.jpg").c_str());
unsigned int floorTexture = loadTexture(boost::filesystem::path("textures/metal.png").c_str());
(2)有关图片源(网上随意选择下载)

resources/textures/marble.jpg
resources/textures/metal.png

(3)有关着色器

将相应文件放入工程目录下即可(VSCode可在应用商店安装Shader languages support for VS Code)

Shader shader("2.stencil_testing.vs", "2.stencil_testing.fs");
Shader shaderSingleColor("2.stencil_testing.vs", "2.stencil_single_color.fs");
(4)有关CMakeLists.txt和编译所出现的错误
main.cpp:(.text+0x7b4):对‘stbi_load’未定义的引用
...

对‘boost::system::generic_category()’未定义的引用
对‘boost::system::generic_category()’未定义的引用
...

第一个错误std_image.h
解决
include:std_image.h
①、新建src目录:std_image.cpp

#define STB_IMAGE_IMPLEMENTATION
#include <stb_image.h>

②、修改CMakeLists.txt
在这里插入图片描述

第二个错误:boost相关
解决
在CMakeLists.txt里面添加相应配置
在这里插入图片描述

(5)result

运行时图像随鼠标移动太频繁,导致不好控制,且失去鼠标对终端的控制
在这里插入图片描述

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!