模版测试的作用就是给我们提供一种可以控制片段绘制的方法,让我们的绘图更为灵活。本节中绘制边框的例子的设计思想也很容易理解,具体可以看下面代码的中文注释。
原版教程中有这么一句话:“我们将模板函数设置为GL_NOTEQUAL,它会保证我们只绘制箱子上模板值不为1的部分,即只绘制箱子在之前绘制的箱子之外的部分。注意我们也禁用了深度测试,让放大的箱子,即边框,不会被地板所覆盖。”
他的想法我理解,可为什么能达到这个效果呢?想了好久,又翻了翻以前的章节,总算是想明白了,顺便想明白了之前不明白的一些问题。
原版教程在创建窗口章节介绍了渲染循环,每次绘图都会更新颜色缓冲,每次循环都要清除上一次绘图的痕迹,否则会一直在。三角形章节介绍了渲染管线,我们知道了光栅器会把集合着色器生成的图元光栅化,生成片段,每一个片段都是渲染一个像素的数据集合。同时介绍了glDrawArrays函数,每执行一次该函数就会画一次图。在坐标系章节中,我们知道了光栅器工作在视口变换(Viewport Transform)之后的,也就是说在光栅器生成片段时,已经确定每个片段所渲染的是屏幕上的哪个点了。
在深度测试章节,我们知道了每个片段中都有一个深度值,还知道了有一个深度缓冲记录着每个被绘制的片段的深度值。当图像绘制的时候,会用本次绘图的片段的深度值和该片段所对应的像素点的深度缓冲值对比,对比通过就用当前片段覆盖之前的片段,同时更新该像素的深度缓冲值。
回顾深度缓冲的程序,在渲染循环中,我们首先要设置一个背景颜色,然后就是清空颜色缓存和深度缓存。在我们第一次使用glDrawArrays绘制箱子时,颜色缓存和深度缓存都是空的,一定会通过测试,当绘图结束时,箱子所在的像素所对应的颜色缓存和深度缓存被更新
第二次使用glDrawArrays绘制第二个箱子时,由于我们没有清空颜色和深度缓存,也没有设置背景颜色,同时也没有和第一次绘制的箱子有像素重叠,因此第一个箱子所在的像素点的缓存信息没有变,只是更新了第二个箱子对应的像素点的缓存信息。
第三次使用glDrawArrays绘制地板时,没有重叠的部分同第二个箱子一样,有重叠的部分会用地板的片段的深度值和缓存中的深度值进行比较,如果比较通过就绘制更新,不通过就会被抛弃
这里要注意,片段的深度值是包含在片段信息内的,缓冲深度值是用来标识像素的,虽然说每个片段都是一个像素的数据集合,但是像素的数据是真的被绘制到屏幕上了,而片段的数据则不一定,所以不能把片段和像素完全等价看待
模版测试略微不同,片段中并没有一个叫做模版值的数据,我们比较的时候也并没有从片段中取数据,而用我们设置的一个参考值。我们可以把模版缓冲理解成一个和屏幕对应的二维数组,这个二维数组就想是一个模版一样罩在屏幕上,每个像素就是缓冲数据,控制着本次绘图时要不要绘制其对应的像素
在下面的代码中,我们首先打开了模版测试,然后设置所有比较都通过,并设置参考值为1。在渲染循环中,首先绘制了地板,并设置了模版掩码为0,原版教程的代码中虽然注释的内容是禁止更新缓冲,其实并不是不向缓冲中写数据,而是会将所有写入的数据在写入之前都会先和0与,再将结果写入,也就是说,当地板被绘制的时候,地板所对应的所有像素点的模版缓冲值都是0
然后是绘制两个箱子,和深度测试一样,由于我们没有清空各种缓存数据,地板的信息只有与箱子覆盖的位置才会被更新,其他位置会被保留。也就是说,当绘制完箱子后,我们理解的那个二维数组中,箱子所对应的像素的模版缓冲值是1,其他像素的模版缓冲值是0
接下来是绘制边框,我们首先设置测试条件是模版缓冲值不等于1,也就是说,本次绘图的时候,二维数组中的所有值为1的元素所对应的像素都不会被绘制,由于我们也没有清空缓存数据,所以之前绘制的箱子还在,并没有被覆盖掉。我们实际上并不是给箱子进行描边,而画了一个比箱子更大的立方体,只不过在渲染的时候,与箱子重合的部分被丢弃了,造成一种绘制边框的效果
#include <glad/glad.h>
#include <GLFW/glfw3.h>
#include <stb_image.h>
#include <glm/glm.hpp>
#include <glm/gtc/matrix_transform.hpp>
#include <glm/gtc/type_ptr.hpp>
#include <shader.h>
#include <camera.h>
#include <model.h>
#include <iostream>
#include <windows.h>
const char *vertexShaderSource = R"1234(#version 330 core
layout (location = 0) in vec3 aPos;
layout (location = 1) in vec2 aTexCoords;
out vec2 TexCoords;
uniform mat4 model;
uniform mat4 view;
uniform mat4 projection;
void main()
{
TexCoords = aTexCoords;
gl_Position = projection * view * model * vec4(aPos, 1.0);
}
)1234";
const char *fragmentShaderSource = R"1234(#version 330 core
out vec4 FragColor;
in vec2 TexCoords;
uniform sampler2D texture1;
void main()
{
//FragColor = vec4(vec3(gl_FragCoord.z), 1.0);
FragColor = texture