1、本次试验是参考LearnOpenGL网站进行学习总结。有关模板测试的概念和原理请查看学习网站即可,这里就不做解释。
2、再啰嗦一下绘制轮廓的主要步骤:
(1).在绘制物体前,把模板方程设置为GL_ALWAYS,用1更新物体将被渲染的片段。
(2).渲染物体,写入模板缓冲。
(3).关闭模板写入和深度测试。
(4).每个物体放大一点点。
(5).使用一个不同的片段着色器用来输出一个纯颜色。
(6).再次绘制物体,但只是当它们的片段的模板值不为1时才进行。
(7).开启模板写入和深度测试。
上面是网站上的作者给定的创建步骤,在这里我再详细加一下注解,因为要绘制物体轮廓那么在场景中分别画了两遍模型,第一次是原始的模型,第二遍是带有模板写入并且是带有纯色模型。最后是对原物体关闭深度测试,不写入模板测试,而对第二遍绘制的模型对其缩放值,假设为scale。将其缩放值改为scale+0.1即可。
3、下面在用代码演示上面的整个过程。
头文件:
GLuint m_cubeVBO;
GLuint m_cubeVAO;
CShader* m_cubeShader;
CTexture* m_cubeTexture;
实现文件:下面代码时创建立方体盒子,并设置纹理,加了两张贴图进行融合,并且用颜色再次融合。
glGenVertexArrays(1, &m_cubeVAO);
glBindVertexArray(m_cubeVAO);
glGenBuffers(1, &m_cubeVBO);
glBindBuffer(GL_ARRAY_BUFFER, m_cubeVBO);
//Make a cube out of triangles (two triangles per side)
GLfloat vertexData[] = {
// X Y Z U V R G B
// bottom
-1.0f,-1.0f,-1.0f, 0.0f, 0.0f, 1.0, 0.0, 0.0,
1.0f,-1.0f,-1.0f, 1.0f, 0.0f, 0.0, 1.0, 0.0,
-1.0f,-1.0f, 1.0f, 0.0f, 1.0f, 0.0, 0.0, 1.0,
1.0f,-1.0f,-1.0f, 1.0f, 0.0f, 1.0, 1.0, 0.0,
1.0f,-1.0f, 1.0f, 1.0f, 1.0f, 0.0, 1.0, 1.0,
-1.0f,-1.0f, 1.0f, 0.0f, 1.0f, 1.0, 0.0, 1.0,
// top
-1.0f, 1.0f,-1.0f, 0.0f, 0.0f, 1.0, 0.0, 0.0,
-1.0f, 1.0f, 1.0f, 0.0f, 1.0f, 0.0, 1.0, 0.0,
1.0f, 1.0f,-1.0f, 1.0f, 0.0f, 0.0, 0.0, 1.0,
1.0f, 1.0f,-1.0f, 1.0f, 0.0f, 1.0, 1.0, 0.0,
-1.0f, 1.0f, 1.0f, 0.0f, 1.0f, 0.0, 1.0, 1.0,
1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0, 0.0, 1.0,
// front
-1.0f,-1.0f, 1.0f, 1.0f, 0.0f, 1.0, 0.0, 0.0,
1.0f,-1.0f, 1.0f, 0.0f, 0.0f, 0.0, 1.0, 0.0,
-1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 0.0, 0.0, 1.0,
1.0f,-1.0f, 1.0f, 0.0f, 0.0f, 1.0, 1.0, 0.0,
1.0f, 1.0f, 1.0f, 0.0f, 1.0f, 0.0, 1.0, 1.0,
-1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0, 0.0, 1.0,
// back
-1.0f,-1.0f,-1.0f, 0.0f, 0.0f, 1.0, 0.0, 0.0,
-1.0f, 1.0f,-1.0f, 0.0f, 1.0f, 0.0, 1.0, 0.0,
1.0f,-1.0f,-1.0f, 1.0f, 0.0f, 0.0, 0.0, 1.0,
1.0f,-1.0f,-1.0f, 1.0f, 0.0f, 1.0, 1.0, 0.0,
-1.0f, 1.0f,-1.0f, 0.0f, 1.0f, 0.0, 1.0, 1.0,
1.0f, 1.0f,-1.0f, 1.0f, 1.0f, 1.0, 0.0, 1.0,
// left
-1.0f,-1.0f, 1.0f, 0.0f, 1.0f, 1.0, 0.0, 0.0,
-1.0f, 1.0f,-1.0f, 1.0f, 0.0f, 0.0, 1.0, 0.0,
-1.0f,-1.0f,-1.0f, 0.0f, 0.0f, 0.0, 0.0, 1.0,
-1.0f,-1.0f, 1.0f, 0.0f, 1.0f, 1.0, 1.0, 0.0,
-1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 0.0, 1.0, 1.0,
-1.0f, 1.0f,-1.0f, 1.0f, 0.0f, 1.0, 0.0, 1.0,
// right
1.0f,-1.0f, 1.0f, 1.0f, 1.0f, 1.0, 0.0, 0.0,
1.0f,-1.0f,-1.0f, 1.0f, 0.0f, 0.0, 1.0, 0.0,
1.0f, 1.0f,-1.0f, 0.0f, 0.0f, 0.0, 0.0, 1.0,
1.0f,-1.0f, 1.0f, 1.0f, 1.0f, 1.0, 1.0, 0.0,
1.0f, 1.0f,-1.0f, 0.0f, 0.0f, 0.0, 1.0, 1.0,
1.0f, 1.0f, 1.0f, 0.0f, 1.0f, 1.0, 0.0, 1.0
};
m_cubeShader->loadShader("../res/shader/cube.vs", "../res/shader/cube.ps");
GLint attrib = glGetAttribLocation(m_cubeShader->getProgram(), "position");
std::cout << "Position Location: " << attrib << std::endl;
GLint arribTex = glGetAttribLocation(m_cubeShader->getProgram(), "texCoords");
std::cout << "TexCoords Location: " << attrib << std::endl;
GLint arribcolor = glGetAttribLocation(m_cubeShader->getProgram(), "outColor");
std::cout << "arribcolor Location: " << arribcolor << std::endl;
m_uniformBlockCube = glGetUniformBlockIndex(m_cubeShader->getProgram(), "Matrices");
glBufferData(GL_ARRAY_BUFFER, sizeof(vertexData), vertexData, GL_STATIC_DRAW);
// 启用顶点属性数组,并设置顶点的属性指针
glEnableVertexAttribArray(attrib);
glVertexAttribPointer(attrib, 3, GL_FLOAT, GL_FALSE, 8 * sizeof(GLfloat), 0);
// 启用UV属性数组,并设置UV属性指针
glEnableVertexAttribArray(arribTex);
glVertexAttribPointer(arribTex, 2, GL_FLOAT, GL_TRUE, 8 * sizeof(GLfloat), (const GLvoid*)(3 * sizeof(GLfloat)));
//启用颜色属性数组,并设置颜色属性指针
glEnableVertexAttribArray(arribcolor);
glVertexAttribPointer(arribcolor, 3, GL_FLOAT, GL_TRUE, 8 * sizeof(GLfloat), (const GLvoid*)(5 * sizeof(GLfloat)));
// 解绑 VAO
glBindVertexArray(0);
//Load Texture1
m_cubeTexture->loadTexture("../res/image/wooden-crate.jpg");
//Load Texture2
m_cubeTexture->loadTexture("../res/image/awesomeface.png");
创建完之后记得开启模板测试及深度测试,模板测试函数,深度测试函数
glEnable(GL_DEPTH_TEST); //启用深度测试
glDepthFunc(GL_LESS);
glEnable(GL_STENCIL_TEST); //开启模板测试
glStencilFunc(GL_NOTEQUAL, 1, 0xFF);
glStencilOp(GL_KEEP, GL_KEEP, GL_REPLACE);
好了下面准备着色器代码,首先是为立方体盒子提供的着色器代码
cube.vs代码:
#version 330 core
layout (location = 0) in vec3 position;
layout (location = 1) in vec3 normal;
layout (location = 2) in vec2 texCoords;
layout (location = 3) in vec3 outColor;
out VS_OUT
{
vec2 TexCoords;
vec3 OutColor;
}vs_out;
layout (std140) uniform Matrices
{
mat4 projection;
mat4 view;
};
uniform mat4 model ;
void main()
{
gl_Position = projection * view * model * vec4(position, 1.0f);
vs_out.TexCoords = vec2(texCoords.x , 1-texCoords.y);
vs_out.OutColor = outColor;
}
cube.ps代码:
#version 330 core
in VS_OUT
{
vec2 TexCoords;
vec3 OutColor;
}fs_in;
out vec4 color;
uniform sampler2D texture_diffuse1;
uniform sampler2D texture_diffuse2;
void main()
{
//color = vec4(texture(texture_diffuse1,TexCoords)) * vec4(outcolor,1.0f);
color = mix(texture(texture_diffuse1,fs_in.TexCoords),texture(texture_diffuse2,fs_in.TexCoords),0.5) * vec4(fs_in.OutColor,1.0);
}
下面再准备模板写入的着色器代码:
stencil.vs
#version 330 core
layout (location = 0) in vec3 position;
uniform mat4 model;
layout (std140) uniform Matrices
{
mat4 projection;
mat4 view;
};
void main()
{
gl_Position = projection * view * model * vec4(position, 1.0f);
}
stencil.ps
#version 330 core
out vec4 color;
void main()
{
color = vec4(1.0, 1.0, 0.0, 1.0);
}
好了,着色器代码准备完毕之后下面就看如何设置让物体显示轮廓了。
首先设置相机的投影和视图矩阵给着色器,代码如下:
这种写法,可以一次设置避免可多次传入数据的作用,你可以参考高级GLSL
glm::mat4 projection = m_pCamera->getCameraProjectMatrix();
glm::mat4 view = m_pCamera->getCameraViewMatrix();
glBindBuffer(GL_UNIFORM_BUFFER, m_uboMatrices);
glBufferSubData(GL_UNIFORM_BUFFER, 0, sizeof(glm::mat4), glm::value_ptr(projection));
glBindBuffer(GL_UNIFORM_BUFFER, 0);
glBindBuffer(GL_UNIFORM_BUFFER, m_uboMatrices);
glBufferSubData(GL_UNIFORM_BUFFER, sizeof(glm::mat4), sizeof(glm::mat4), glm::value_ptr(view));
glBindBuffer(GL_UNIFORM_BUFFER, 0);
加载着色器:
m_cubeShader->loadShader("../res/shader/cube.vs", "../res/shader/cube.ps");
m_modelStencilShader->loadShader("../res/shader/stencil.vs", "../res/shader/stencil.ps");
好了下面是模板写入代码:
//模板测试
glStencilFunc(GL_ALWAYS, 1, 0xFF);
glStencilMask(0xFF);
m_modelStencilShader->ProgramUse();
glm::mat4 cubeScale = glm::scale(glm::vec3(1.1,1.1,1.1));//记得要修改缩放值
m_modelStencilShader->setShaderUniform(m_cubeShader->getProgram(), "model", cubeScale, GL_FALSE);
// bind the VAO (the triangle)
glBindVertexArray(m_cubeVAO);
// draw the VAO
glDrawArrays(GL_TRIANGLES, 0, 36);
// unbind the VAO, the program and the texture
glBindVertexArray(0);
m_modelStencilShader->stopProgrameUse();
关闭模板写入,关闭深度测试 再次绘制模型开启深度测试。
//启用着色器
m_cubeShader->ProgramUse();
glm::mat4 model;
model = glm::scale(glm::vec3(1.0,1.0, 1.0));
//给模型设置缩放值大小
m_cubeShader->setShaderUniform(m_cubeShader->getProgram(), "model", model, GL_FALSE);
//绑定材质
m_cubeTexture->bindTexture();
//给片段设置纹理采样值
m_cubeShader->setShaderUniform(m_cubeShader->getProgram(), "texture_diffuse1",(GLuint) 0);
m_cubeShader->setShaderUniform(m_cubeShader->getProgram(), "texture_diffuse2", (GLuint)1);
glStencilFunc(GL_NOTEQUAL, 1, 0xFF);
glStencilMask(0x00);
//关闭深度测试
glDisable(GL_DEPTH_TEST);
//绑定VAO
glBindVertexArray(m_cubeVAO);
//绘制VAO
glDrawArrays(GL_TRIANGLES, 0, 36);
//解绑VAO
glBindVertexArray(0);
//开启深度测试
glEnable(GL_DEPTH_TEST);
//关闭着色器
m_cubeShader->stopProgrameUse();