OpenGL学习 跟着官网教程学习(模板测试)

一,前言

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

了解一下小小的知识点。

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);
		// cubes
		glBindVertexArray(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;
		// cubes
		glBindVertexArray(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

猜你喜欢

转载自blog.csdn.net/asiwxy/article/details/129595314