前置:OpenGL基础29:深度测试

一、模板测试

前面一章提到过:深度缓冲测试在片段着色器运行、以及模板测试(Stencil Testing)之后,那么这下知道模板测试是在什么时候了吧,模板测试和深度测试逻辑可以说是一致的,它会丢弃一些片段,保留下来的片段将会进入深度测试阶段:

  • glEnable(GL_STENCIL_TEST):开启模板测试
  • glClear(GL_STENCIL_BUFFER_BIT):清空模板缓冲

可以用模板测试实现一些非常有意思的效果,一个很经典的例子就是3D物体的描边(某些游戏中的选中效果):

可以从中看出:3个立方体点光源,都被加上了黄色的描边

对于深度测试,每个片段都有一个浮点值,而对于模板测试,每个片段都有一个8位的模板值(0x00-0xFF),原理也很简单:每次模板测试时,对应的片段都会根据它的模板值以及你的规则来决定是否丢弃对应的片段

我们假设规定模板值为1的话就绘制,为0的话就丢弃,那么整个流程就会是下面这样的:

二、模板函数

理解了模板测试的流程后,再来看如何自定义模板测试的规则

模板测试主要有3个函数:

  1. glStencilMask(GLint 0~255):决定对应的模板值的每一位是否可写入模板缓存
  2. void glStencilFunc(GLenum func, GLint ref, GLuint mask):设置模板测试规则,什么样的片段会被丢弃
  3. void glStencilOp(GLenum sfail, GLenum dpfail, GLenum dppass):模板测试之后进行的操作

对于①glStencilMask:它允许我们给模板值设置一个位遮罩(Bitmask),如果对应位为1,则对应位可写入缓存,如果对应位为0,则对应位不可写入缓存,一般只会进行以下两种设置:

  • glStencilMask(0xFF):默认设置,模板缓冲完全可写
  • glStencilMask(0x00):模板缓冲完全不可写

对于②glStencilFunc的三个参数:

  • func:设置模板测试规则,可用的选项有:GL_NEVER、GL_LEQUAL、GL_GREATER、GL_GEQUAL、GL_EQUAL、GL_NOTEQUAL、GL_ALWAYS,它们的意义在《OpenGL基础29:深度测试》这一章内有介绍
  • ref:指定模板测试的引用值,模板缓冲的内容会与这个值对比
  • mask:指定一个遮罩,在模板测试对比引用值和储存的模板值前,对它们进行按位与操作

例如 glStencilFunc(GL_EQUAL, 1, 0xFF) :如果一个片段模板值等于1,就能通过测试被绘制,否则就会被丢弃

对于③glStencilOp的三个参数:

  • sfail: 如果模板测试失败将采取的动作
  • dpfail: 如果模板测试通过,但是深度测试失败时采取的动作
  • dppass: 如果深度测试和模板测试都通过,将采取的动作

其中有八种不同的动作:

  • GL_KEEP:保持现有的模板值
  • GL_ZERO:将模板值置为0
  • GL_REPLACE:将模板值设置为用glStencilFunc函数设置的ref值
  • GL_INCR:如果模板值不是最大值就将模板值+1
  • GL_INCR_WRAP:与GL_INCR一样将模板值+1,如果模板值已经是最大值则设为0
  • GL_DECR:如果模板值不是最小值就将模板值-1
  • GL_DECR_WRAP:与GL_DECR一样将模板值-1,如果模板值已经是最小值则设为最大值
  • GL_INVERT:将模板值按位反转

例如 glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP) :任何测试的任何结果,模板缓冲都会保留它的值,这也是默认的设置

三、物体描边

好了,如果对上面的3个函数有了解,那么就可以实现物体的描边了,只考虑3个立方体光源,步骤如下:

  1. 开启模板测试、模板写入、通过glStencilOp设置,使得被成功渲染的片段的模板值会被更新为0xFF
  2. 把模板方程设置为GL_ALWAYS,永远绘制
  3. 渲染3个立方体光源,写入模板缓冲
  4. 关闭模板写入
  5. 再次绘制3个立方体光源,把颜色改成黄色(将会是描边的颜色)并且放大一点点,把模板方程设置为GL_NOTEQUAL,只有对应片段的模板值不为0xFF时才绘制
  6. 再次设置GL_ALWAYS,绘制剩下的图形
  7. 开启模板写入,继续下一轮渲染

完整代码:

#include<iostream>
#include<opengl/glew.h>
#define GLEW_STATIC
#include<GLFW/glfw3.h>
#include"Shader.h"
#include"Camera.h"
#include<glm/glm.hpp>
#include<glm/gtc/matrix_transform.hpp>
#include<glm/gtc/type_ptr.hpp>
#include"Mesh.h"
#include"Model.h"
#include<opengl/freeglut.h>
#include<SOIL.h>bool keys[1024];
Camera camera;
GLfloat lastX, lastY;
bool firstMouse = true;
void key_callback(GLFWwindow* window, int key, int scancode, int action, int mode);
void scroll_callback(GLFWwindow* window, double xoffset, double yoffset);
void mouse_callback(GLFWwindow* window, double xpos, double ypos);
void cameraMove();
const GLuint WIDTH = 800, HEIGHT = 600;int main()
{glfwInit();glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);glfwWindowHint(GLFW_RESIZABLE, GL_FALSE);GLFWwindow* window = glfwCreateWindow(WIDTH, HEIGHT, "LearnOpenGL", nullptr, nullptr);glfwMakeContextCurrent(window);glfwSetKeyCallback(window, key_callback);glfwSetCursorPosCallback(window, mouse_callback);glfwSetScrollCallback(window, scroll_callback);glfwSetInputMode(window, GLFW_CURSOR, GLFW_CURSOR_DISABLED);glewExperimental = GL_TRUE;glewInit();int width, height;glfwGetFramebufferSize(window, &width, &height);glViewport(0, 0, width, height);Shader shaderObj("ObjVShader.vert", "ObjFShader.frag");Shader shaderLight("LightVShader.vert", "LightFShader.frag");GLfloat vertices[] = {-0.5f, -0.5f, -0.5f,  0.0f,  0.0f, -1.0f,  0.0f,  0.0f,0.5f, -0.5f, -0.5f,  0.0f,  0.0f, -1.0f,  1.0f,  0.0f,0.5f,  0.5f, -0.5f,  0.0f,  0.0f, -1.0f,  1.0f,  1.0f,0.5f,  0.5f, -0.5f,  0.0f,  0.0f, -1.0f,  1.0f,  1.0f,-0.5f,  0.5f, -0.5f,  0.0f,  0.0f, -1.0f,  0.0f,  1.0f,-0.5f, -0.5f, -0.5f,  0.0f,  0.0f, -1.0f,  0.0f,  0.0f,-0.5f, -0.5f,  0.5f,  0.0f,  0.0f,  1.0f,  0.0f,  0.0f,0.5f, -0.5f,  0.5f,  0.0f,  0.0f,  1.0f,  1.0f,  0.0f,0.5f,  0.5f,  0.5f,  0.0f,  0.0f,  1.0f,  1.0f,  1.0f,0.5f,  0.5f,  0.5f,  0.0f,  0.0f,  1.0f,  1.0f,  1.0f,-0.5f,  0.5f,  0.5f,  0.0f,  0.0f,  1.0f,  0.0f,  1.0f,-0.5f, -0.5f,  0.5f,  0.0f,  0.0f,  1.0f,  0.0f,  0.0f,-0.5f,  0.5f,  0.5f, -1.0f,  0.0f,  0.0f,  1.0f,  0.0f,-0.5f,  0.5f, -0.5f, -1.0f,  0.0f,  0.0f,  1.0f,  1.0f,-0.5f, -0.5f, -0.5f, -1.0f,  0.0f,  0.0f,  0.0f,  1.0f,-0.5f, -0.5f, -0.5f, -1.0f,  0.0f,  0.0f,  0.0f,  1.0f,-0.5f, -0.5f,  0.5f, -1.0f,  0.0f,  0.0f,  0.0f,  0.0f,-0.5f,  0.5f,  0.5f, -1.0f,  0.0f,  0.0f,  1.0f,  0.0f,0.5f,  0.5f,  0.5f,  1.0f,  0.0f,  0.0f,  1.0f,  0.0f,0.5f,  0.5f, -0.5f,  1.0f,  0.0f,  0.0f,  1.0f,  1.0f,0.5f, -0.5f, -0.5f,  1.0f,  0.0f,  0.0f,  0.0f,  1.0f,0.5f, -0.5f, -0.5f,  1.0f,  0.0f,  0.0f,  0.0f,  1.0f,0.5f, -0.5f,  0.5f,  1.0f,  0.0f,  0.0f,  0.0f,  0.0f,0.5f,  0.5f,  0.5f,  1.0f,  0.0f,  0.0f,  1.0f,  0.0f,-0.5f, -0.5f, -0.5f,  0.0f, -1.0f,  0.0f,  0.0f,  1.0f,0.5f, -0.5f, -0.5f,  0.0f, -1.0f,  0.0f,  1.0f,  1.0f,0.5f, -0.5f,  0.5f,  0.0f, -1.0f,  0.0f,  1.0f,  0.0f,0.5f, -0.5f,  0.5f,  0.0f, -1.0f,  0.0f,  1.0f,  0.0f,-0.5f, -0.5f,  0.5f,  0.0f, -1.0f,  0.0f,  0.0f,  0.0f,-0.5f, -0.5f, -0.5f,  0.0f, -1.0f,  0.0f,  0.0f,  1.0f,-0.5f,  0.5f, -0.5f,  0.0f,  1.0f,  0.0f,  0.0f,  1.0f,0.5f,  0.5f, -0.5f,  0.0f,  1.0f,  0.0f,  1.0f,  1.0f,0.5f,  0.5f,  0.5f,  0.0f,  1.0f,  0.0f,  1.0f,  0.0f,0.5f,  0.5f,  0.5f,  0.0f,  1.0f,  0.0f,  1.0f,  0.0f,-0.5f,  0.5f,  0.5f,  0.0f,  1.0f,  0.0f,  0.0f,  0.0f,-0.5f,  0.5f, -0.5f,  0.0f,  1.0f,  0.0f,  0.0f,  1.0f};glm::vec3 positions[] = {glm::vec3(0.0f, 0.0f, 0.0f),glm::vec3(0.0f, 0.89f, 0.0f),glm::vec3(0.0f, 1.78f, 0.0f),glm::vec3(-2.0f, 0.0f, 0.0f),glm::vec3(-2.0f, 0.89f, 0.0f),glm::vec3(-3.0f, 0.0f, 0.0f),glm::vec3(-2.0f, 0.0f, 1.0f),glm::vec3(-1.0f, 0.0f, -4.0f),};glm::vec3 pointLightPositions[] = {glm::vec3(-1.0f, 1.8f, -2.0f),glm::vec3(0.0f, 0.8f, 2.0f),glm::vec3(-5.0f, 0.8f, 1.0f),};GLuint VBO, VAO;glGenVertexArrays(1, &VAO);glGenBuffers(1, &VBO);glBindVertexArray(VAO);glBindBuffer(GL_ARRAY_BUFFER, VBO);glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 8 * sizeof(GLfloat), (GLvoid*)0);glEnableVertexAttribArray(0);glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 8 * sizeof(GLfloat), (GLvoid*)(3 * sizeof(GLfloat)));glEnableVertexAttribArray(1);glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, 8 * sizeof(GLfloat), (GLvoid*)(6 * sizeof(GLfloat)));glEnableVertexAttribArray(2);GLuint lightVAO;glGenVertexArrays(1, &lightVAO);glBindVertexArray(lightVAO);glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 8 * sizeof(GLfloat), (GLvoid*)0);//VBO数据已经绑定且我们就用之前的顶点数据,所以无需再管理VBOglEnableVertexAttribArray(0);glBindBuffer(GL_ARRAY_BUFFER, 0);glBindVertexArray(0);glEnable(GL_DEPTH_TEST);glEnable(GL_STENCIL_TEST);glStencilOp(GL_KEEP, GL_KEEP, GL_REPLACE);             //模板测试和深度测试都成功时,将对应像素的模板值设置为用glStencilFunc函数设置的ref值Model wood("Object/wood/file.fbx", "Object/wood/file.fbx");Model ground("Object/ground/ground.fbx", "Object/ground");while (!glfwWindowShouldClose(window)){glfwPollEvents();glClearColor(0.0f, 0.0f, 0.0f, 1.0f);glStencilMask(0xFF);                  //设置模板缓冲区可写入,如果设置为不可写入之后清空模板缓冲区,将会清空失败!毕竟不可写入了glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);cameraMove();shaderLight.Use();glm::mat4 view = camera.GetViewMatrix();glm::mat4 projection = glm::perspective(glm::radians(camera.Zoom), (GLfloat)WIDTH / (GLfloat)HEIGHT, 0.1f, 100.0f);glm::mat4 model = glm::mat4(1.0f);GLint modelLoc = glGetUniformLocation(shaderLight.Program, "model");GLint viewLoc = glGetUniformLocation(shaderLight.Program, "view");GLint projLoc = glGetUniformLocation(shaderLight.Program, "projection");glUniformMatrix4fv(modelLoc, 1, GL_FALSE, glm::value_ptr(model));glUniformMatrix4fv(viewLoc, 1, GL_FALSE, glm::value_ptr(view));glUniformMatrix4fv(projLoc, 1, GL_FALSE, glm::value_ptr(projection));glBindVertexArray(lightVAO);glStencilFunc(GL_ALWAYS, 0xFF, 0xFF);                   //无论模板测试如何,一定可以绘制;glUniform3f(glGetUniformLocation(shaderLight.Program, "lightColor"), 1.0f, 1.0f, 1.0f);for (int i = 0; i <= 2; i++){model = glm::translate(glm::mat4(1.0f), pointLightPositions[i]);model = glm::scale(model, glm::vec3(0.2f));glUniformMatrix4fv(modelLoc, 1, GL_FALSE, glm::value_ptr(model));glDrawArrays(GL_TRIANGLES, 0, 36);}glStencilFunc(GL_NOTEQUAL, 0xFF, 0xFF);                 //对应像素模板值若等于256,则对应像素不绘制glStencilMask(0x00);                                    //模板缓冲区不再可写glUniform3f(glGetUniformLocation(shaderLight.Program, "lightColor"), 1.0f, 1.0f, 0.0f);for (int i = 0; i <= 2; i++){model = glm::translate(glm::mat4(1.0f), pointLightPositions[i]);model = glm::scale(model, glm::vec3(0.22f));glUniformMatrix4fv(modelLoc, 1, GL_FALSE, glm::value_ptr(model));glDrawArrays(GL_TRIANGLES, 0, 36);}glStencilFunc(GL_ALWAYS, 0xFF, 0xFF);shaderObj.Use();glUniform3f(glGetUniformLocation(shaderObj.Program, "sunLight.direction"), -0.2f, -1.0f, -0.3f);glUniform3f(glGetUniformLocation(shaderObj.Program, "sunLight.diffuse"), 0.4f, 0.4f, 0.4f);glUniform3f(glGetUniformLocation(shaderObj.Program, "sunLight.specular"), 0.5f, 0.5f, 0.5f);glUniform3f(glGetUniformLocation(shaderObj.Program, "pointLights[0].position"), pointLightPositions[0].x, pointLightPositions[0].y, pointLightPositions[0].z);glUniform3f(glGetUniformLocation(shaderObj.Program, "pointLights[0].diffuse"), 0.8f, 0.8f, 0.8f);glUniform3f(glGetUniformLocation(shaderObj.Program, "pointLights[0].specular"), 1.0f, 1.0f, 1.0f);glUniform1f(glGetUniformLocation(shaderObj.Program, "pointLights[0].k0"), 1.0f);glUniform1f(glGetUniformLocation(shaderObj.Program, "pointLights[0].k1"), 0.09);glUniform1f(glGetUniformLocation(shaderObj.Program, "pointLights[0].k2"), 0.032);glUniform3f(glGetUniformLocation(shaderObj.Program, "pointLights[1].position"), pointLightPositions[1].x, pointLightPositions[1].y, pointLightPositions[1].z);glUniform3f(glGetUniformLocation(shaderObj.Program, "pointLights[1].diffuse"), 0.8f, 0.8f, 0.8f);glUniform3f(glGetUniformLocation(shaderObj.Program, "pointLights[1].specular"), 1.0f, 1.0f, 1.0f);glUniform1f(glGetUniformLocation(shaderObj.Program, "pointLights[1].k0"), 1.0f);glUniform1f(glGetUniformLocation(shaderObj.Program, "pointLights[1].k1"), 0.09);glUniform1f(glGetUniformLocation(shaderObj.Program, "pointLights[1].k2"), 0.032);glUniform3f(glGetUniformLocation(shaderObj.Program, "pointLights[2].position"), pointLightPositions[2].x, pointLightPositions[2].y, pointLightPositions[2].z);glUniform3f(glGetUniformLocation(shaderObj.Program, "pointLights[2].diffuse"), 0.8f, 0.8f, 0.8f);glUniform3f(glGetUniformLocation(shaderObj.Program, "pointLights[2].specular"), 1.0f, 1.0f, 1.0f);glUniform1f(glGetUniformLocation(shaderObj.Program, "pointLights[2].k0"), 1.0f);glUniform1f(glGetUniformLocation(shaderObj.Program, "pointLights[2].k1"), 0.09);glUniform1f(glGetUniformLocation(shaderObj.Program, "pointLights[2].k2"), 0.032);glUniform3f(glGetUniformLocation(shaderObj.Program, "spotLight.position"), camera.Position.x, camera.Position.y, camera.Position.z);glUniform3f(glGetUniformLocation(shaderObj.Program, "spotLight.direction"), camera.Front.x, camera.Front.y, camera.Front.z);glUniform3f(glGetUniformLocation(shaderObj.Program, "spotLight.diffuse"), 1.0f, 1.0f, 1.0f);glUniform3f(glGetUniformLocation(shaderObj.Program, "spotLight.specular"), 1.0f, 1.0f, 1.0f);glUniform1f(glGetUniformLocation(shaderObj.Program, "spotLight.k0"), 1.0f);glUniform1f(glGetUniformLocation(shaderObj.Program, "spotLight.k1"), 0.09);glUniform1f(glGetUniformLocation(shaderObj.Program, "spotLight.k2"), 0.032);glUniform1f(glGetUniformLocation(shaderObj.Program, "spotLight.cutOff"), glm::cos(glm::radians(12.5f)));glUniform1f(glGetUniformLocation(shaderObj.Program, "spotLight.outCutOff"), glm::cos(glm::radians(16.0f)));glUniform3f(glGetUniformLocation(shaderObj.Program, "viewPos"), camera.Position.x, camera.Position.y, camera.Position.z);modelLoc = glGetUniformLocation(shaderObj.Program, "model");viewLoc = glGetUniformLocation(shaderObj.Program, "view");projLoc = glGetUniformLocation(shaderObj.Program, "projection");glUniformMatrix4fv(viewLoc, 1, GL_FALSE, glm::value_ptr(view));glUniformMatrix4fv(projLoc, 1, GL_FALSE, glm::value_ptr(projection));for (int i = 0; i <= 7; i++){model = glm::translate(glm::mat4(1.0f), positions[i]);model = glm::scale(model, glm::vec3(0.01f));glUniformMatrix4fv(modelLoc, 1, GL_FALSE, glm::value_ptr(model));wood.Draw(shaderObj);}for (int i = -2; i <= 1; i++){for(int j = -2; j <= 1; j++){model = glm::translate(glm::mat4(1.0f), glm::vec3(i * 3.52f, -0.05f, j * 3.72f));model = glm::scale(model, glm::vec3(0.1f));model = glm::rotate(model, glm::radians(90.0f), glm::vec3(-1.0f, 0.0f, 0.0f));glUniformMatrix4fv(modelLoc, 1, GL_FALSE, glm::value_ptr(model));ground.Draw(shaderObj);}}glBindVertexArray(0);glfwSwapBuffers(window);}glDeleteVertexArrays(1, &VAO);glDeleteBuffers(1, &VBO);glfwTerminate();return 0;
}GLfloat deltaTime = 0.0f;
GLfloat lastFrame = 0.0f;
void cameraMove()
{GLfloat currentFrame = glfwGetTime();deltaTime = currentFrame - lastFrame;lastFrame = currentFrame;GLfloat cameraSpeed = 1.0f * deltaTime;if (keys[GLFW_KEY_W])camera.ProcessKeyboard(Camera_Movement(FORWARD), deltaTime);if (keys[GLFW_KEY_S])camera.ProcessKeyboard(Camera_Movement(BACKWARD), deltaTime);if (keys[GLFW_KEY_A])camera.ProcessKeyboard(Camera_Movement(LEFT), deltaTime);if (keys[GLFW_KEY_D])camera.ProcessKeyboard(Camera_Movement(RIGHT), deltaTime);
}void key_callback(GLFWwindow* window, int key, int scancode, int action, int mode)
{if (key == GLFW_KEY_ESCAPE && action == GLFW_PRESS)glfwSetWindowShouldClose(window, GL_TRUE);if (action == GLFW_PRESS)           //如果当前是按下操作keys[key] = true;else if (action == GLFW_RELEASE)            //松开键盘keys[key] = false;
}void scroll_callback(GLFWwindow* window, double xoffset, double yoffset)
{camera.ProcessMouseScroll(yoffset);
}void mouse_callback(GLFWwindow* window, double xpos, double ypos)
{if (firstMouse){lastX = xpos;lastY = ypos;firstMouse = false;}GLfloat xoffset = xpos - lastX;GLfloat yoffset = lastY - ypos;lastX = xpos;lastY = ypos;GLfloat sensitivity = 0.05;xoffset *= sensitivity;yoffset *= sensitivity;camera.ProcessMouseMovement(xoffset, yoffset);
}

修改光源着色器shaderLight如下:

#version 330 core
layout (location = 0) in vec3 position;
uniform mat4 model;             //模型矩阵
uniform mat4 view;              //观察矩阵
uniform mat4 projection;        //投影矩阵
void main()
{gl_Position = projection * view * model * vec4(position, 1.0);
}//#version 330 core
uniform vec3 lightColor;
out vec4 color;
void main()
{color = vec4(lightColor, 1.0f);
}

不过之前为了生成地面的模型,model.h和mesh.h也进行了小小的修改,也放出来吧

#ifndef MODEL_H
#define MODEL_H
#include<vector>
#include<string>
#include"Shader.h"
#include"Mesh.h"
#include<opengl/glew.h>
#include<SOIL.h>
#include<glm/glm.hpp>
#include<glm/gtc/matrix_transform.hpp>
#include<assimp/Importer.hpp>
#include<assimp/scene.h>
#include<assimp/postprocess.h>
using namespace std;
GLint TextureFromFile(const char* path, string directory);class Model
{public:Model(const GLchar* path, const GLchar* texPath = ""){this->loadModel(path, texPath);}void Draw(Shader shader){for (GLuint i = 0; i < this->meshes.size(); i++)this->meshes[i].Draw(shader);}private:vector<Mesh> meshes;string directory;vector<Texture> textures_loaded;void loadModel(string path, string texPath){Assimp::Importer importer;const aiScene* scene = importer.ReadFile(path, aiProcess_Triangulate | aiProcess_FlipUVs);if (!scene || scene->mFlags == AI_SCENE_FLAGS_INCOMPLETE || !scene->mRootNode){cout << "ERROR::ASSIMP:: " << importer.GetErrorString() << endl;return;}this->directory = path.substr(0, path.find_last_of('/'));if (texPath != "")this->directory = texPath;this->processNode(scene->mRootNode, scene);}//依次处理所有的场景节点void processNode(aiNode* node, const aiScene* scene){for (GLuint i = 0; i < node->mNumMeshes; i++){aiMesh* mesh = scene->mMeshes[node->mMeshes[i]];this->meshes.push_back(this->processMesh(mesh, scene));}for (GLuint i = 0; i < node->mNumChildren; i++)this->processNode(node->mChildren[i], scene);}//将所有原始的aimesh对象全部转换成我们自己定义的网格对象Mesh processMesh(aiMesh* mesh, const aiScene* scene){vector<Vertex> vertices;vector<GLuint> indices;vector<Texture> textures;//处理顶点坐标、法线和纹理坐标for (GLuint i = 0; i < mesh->mNumVertices; i++){Vertex vertex;glm::vec3 vector;vector.x = mesh->mVertices[i].x;vector.y = mesh->mVertices[i].y;vector.z = mesh->mVertices[i].z;vertex.Position = vector;vector.x = mesh->mNormals[i].x;vector.y = mesh->mNormals[i].y;vector.z = mesh->mNormals[i].z;vertex.Normal = vector;if (mesh->mTextureCoords[0])            //不一定有纹理坐标{glm::vec2 vec;//暂时只考虑第一组纹理坐标,Assimp允许一个模型的每个顶点有8个不同的纹理坐标,只是可能用不到vec.x = mesh->mTextureCoords[0][i].x;vec.y = mesh->mTextureCoords[0][i].y;vertex.TexCoords = vec;}elsevertex.TexCoords = glm::vec2(0.0f, 0.0f);vertices.push_back(vertex);}//处理顶点索引for (GLuint i = 0; i < mesh->mNumFaces; i++){aiFace face = mesh->mFaces[i];for (GLuint j = 0; j < face.mNumIndices; j++){indices.push_back(face.mIndices[j]);}}//处理材质if (mesh->mMaterialIndex >= 0){aiMaterial* material = scene->mMaterials[mesh->mMaterialIndex];vector<Texture> diffuseMaps = this->loadMaterialTextures(material, aiTextureType_DIFFUSE, "texture_diffuse");textures.insert(textures.end(), diffuseMaps.begin(), diffuseMaps.end());vector<Texture> specularMaps = this->loadMaterialTextures(material, aiTextureType_SPECULAR, "texture_specular");textures.insert(textures.end(), specularMaps.begin(), specularMaps.end());}return Mesh(vertices, indices, textures);}//遍历所有给定纹理类型的纹理位置,获取纹理的文件位置,然后加载生成纹理vector<Texture> loadMaterialTextures(aiMaterial* mat, int type, string typeName){vector<Texture> textures;cout << typeName << ": " << endl;for (GLuint i = 0; i < mat->GetTextureCount((aiTextureType)type); i++){aiString str;mat->GetTexture((aiTextureType)type, i, &str);GLboolean skip = false;for (GLuint j = 0; j < textures_loaded.size(); j++){if (std::strcmp(textures_loaded[j].path.C_Str(), str.C_Str()) == 0){textures.push_back(textures_loaded[j]);skip = true;break;}}//对应材质已存在就没必要再次读取了if (!skip){Texture texture;texture.id = TextureFromFile(str.C_Str(), this->directory);texture.type = typeName;texture.path = str;textures.push_back(texture);this->textures_loaded.push_back(texture);}}return textures;}
};GLint TextureFromFile(const char* path, string directory)
{string filename = string(path);filename = directory + '/' + filename;cout << filename << endl;GLuint textureID;glGenTextures(1, &textureID);int width, height;unsigned char* image = SOIL_load_image(filename.c_str(), &width, &height, 0, SOIL_LOAD_RGB);glBindTexture(GL_TEXTURE_2D, textureID);glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, image);glGenerateMipmap(GL_TEXTURE_2D);glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);glBindTexture(GL_TEXTURE_2D, 0);SOIL_free_image_data(image);return textureID;
}
#endif
#ifndef MESH_H
#define MESH_H
#include<vector>
#include<string>
#include<fstream>
#include<sstream>
#include"Shader.h"
#include<opengl/glew.h>
#include<glm/glm.hpp>
#include<glm/gtc/matrix_transform.hpp>
#include<assimp/Importer.hpp>
#include<assimp/scene.h>
#include<assimp/postprocess.h>
using namespace std;
struct Vertex
{glm::vec3 Position;         //顶点glm::vec3 Normal;           //法线glm::vec2 TexCoords;        //贴图
};struct Material
{glm::vec4 Ka;               //材质颜色glm::vec4 Kd;               //漫反射glm::vec4 Ks;               //镜面反射
};struct Texture
{GLuint id;string type;                //贴图类型:漫反射贴图还是镜面贴图(后面还有法线贴图、错位贴图等)aiString path;              //贴图路径
};class Mesh
{public:vector<Vertex> vertices;vector<GLuint> indices;             //索引vector<Texture> textures;Mesh(vector<Vertex> vertices, vector<GLuint> indices, vector<Texture> textures){this->vertices = vertices;this->indices = indices;this->textures = textures;this->setupMesh();}void Draw(Shader shader){GLuint diffuseNr = 1;GLuint specularNr = 1;for (GLuint i = 0; i < this->textures.size(); i++){glActiveTexture(GL_TEXTURE0 + i);stringstream ss;string name = this->textures[i].type;if (name == "texture_diffuse")ss << diffuseNr++;else if (name == "texture_specular")ss << specularNr++;name = name + ss.str();glUniform1i(glGetUniformLocation(shader.Program, name.c_str()), i);//这样的话,着色器中的纹理名就必须有一个对应的规范,例如“texture_diffuse3”代表第三个漫反射贴图//方法不唯一,这是最好理解/最简单的一种规范/写法glBindTexture(GL_TEXTURE_2D, this->textures[i].id);}glUniform1f(glGetUniformLocation(shader.Program, "material.shininess"), 16.0f);     //暂时写死反光度,也可配置glBindVertexArray(this->VAO);glDrawElements(GL_TRIANGLES, this->indices.size(), GL_UNSIGNED_INT, 0);             //EBO绘制for (GLuint i = 0; i < this->textures.size(); i++){glActiveTexture(GL_TEXTURE0 + i);glBindTexture(GL_TEXTURE_2D, 0);}glBindVertexArray(0);}private:GLuint VAO, VBO, EBO;void setupMesh(){glGenVertexArrays(1, &this->VAO);glGenBuffers(1, &this->VBO);glGenBuffers(1, &this->EBO);glBindVertexArray(this->VAO);glBindBuffer(GL_ARRAY_BUFFER, this->VBO);glBufferData(GL_ARRAY_BUFFER, this->vertices.size() * sizeof(Vertex), &this->vertices[0], GL_STATIC_DRAW);glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, this->EBO);glBufferData(GL_ELEMENT_ARRAY_BUFFER, this->indices.size() * sizeof(GLuint), &this->indices[0], GL_STATIC_DRAW);glEnableVertexAttribArray(0);//别忘了struct中内存是连续的//offsetof():获取结构体属性的偏移量glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex), (GLvoid*)0);glEnableVertexAttribArray(1);glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex), (GLvoid*)offsetof(Vertex, Normal));glEnableVertexAttribArray(2);glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, sizeof(Vertex), (GLvoid*)offsetof(Vertex, TexCoords));glBindVertexArray(0);}
};
#endif

OpenGL基础30:模板测试相关推荐

  1. OpenGL stencil test模板测试的实例

    OpenGL stencil test模板测试 先上图,再解答. 完整主要的源代码 源代码剖析 先上图,再解答. 完整主要的源代码 #include <glad/glad.h> #incl ...

  2. UnityShader29:模板测试

    一.什么是模板测试 OpenGL基础30:模板测试 Unity官方文档:https://docs.unity3d.com/Manual/SL-Stencil.html 一个很经典的模板测试例子就是 U ...

  3. opengl模板测试实例

    在opengl中, 可以开启模板测试功能,来限定某一部分是可画的,某一部分是不可画的.这可通过设置模板模式来控制哪部分是可画的, 有点类似在墙上喷字. 虽说剪刀测试也可以限定蓝屏的某一部分可画,但不适 ...

  4. (zt)OpenGL中的Alpha测试,深度测试,模板测试,裁减测试

    转自http://www.cppblog.com/flashboy/archive/2009/09/01/94974.html 大家好.现在因为参加工作的关系,又是长时间没有更新.趁着国庆的空闲,总算 ...

  5. OpenGL学习笔记(七)-深度测试-模板测试-混合

    参考网址:LearnOpenGL 中文版 第四章 高级OpenGL 4.1 深度测试 4.1.1 深度缓冲 1.深度缓冲用来防止被阻挡的面渲染到其它面的前面,由窗口系统自动创建,在每个片段中储存了它的 ...

  6. 模板测试(Stencil Test)的基础知识

    本文分享模板测试(Stencil Test)的基础知识 在渲染管线中, 模板测试发生在片元着色器处理和透明度测试之后, 深度测试之前. 模板测试最常见的应用就是各种遮罩, 特别是有形状的遮罩, 如Un ...

  7. OpenGL模板测试通俗理解

    看书籍或资料往往还是云里雾里的,这里写一下自己的理解.希望对需要的人有帮助. 简而言之: 模板缓冲区和帧缓冲区一样大 模板缓冲区初始状态是什么? 在glClearBuffer的时候清空,所以初始状态是 ...

  8. OpenGL学习二十九:模板缓冲区与模板测试

    帧缓冲区有许多缓冲区构成,这些缓冲区大致分为: 颜色缓冲区:用于绘图的缓冲区,它包含了颜色索引或者RGBA颜色数据. 深度缓冲区:存储每个像素的深度值,当启动深度测试时,片段像素深度值和深度缓冲区深度 ...

  9. opengl基础学习

    转自:http://www.cnblogs.com/crazyxiaom/articles/2073586.html 说起编程作图,大概还有很多人想起TC的#include <graphics. ...

最新文章

  1. jquery.autocomplete自动补全功能
  2. 获取AFP共享的文件夹及其权限
  3. Bootstrap相关优质项目学习清单
  4. 软件工程——理论、方法与实践 第六章
  5. TP6返回插入数据的自增ID
  6. 安装及管理程序(yum搭建本地源,了解rmp命令,查询卸载软件包,编译安装的过程)
  7. c语言做一个小程序报告,《C语言程序设计实践》课程报告30个小程序组合成一个大程序.doc...
  8. [常用命令]Git命令
  9. C#中BackgroundWorker的介绍
  10. Style后台动态定义[转]
  11. 反射型XSS+文件上传+CSRF—DVWA
  12. jq实现图片拖动滑块验证码
  13. 【3dmax千千问】初学3dmax插件神器第18课|VRAY渲染教程|疯狂模渲大师用3dmax插件神器的扫描线渲染器该怎么表现效果图的写实效果?
  14. H3C网络流量镜像配置
  15. coreldraw x5安装视频教程_图形设计必备软件:CorelDRAW
  16. 新项目六之集成新版友盟统计
  17. 卷积总结篇(普通卷积、转置卷积、膨胀卷积、分组卷积和深度可分离卷积)
  18. mysql ndb_MySQL NDB Cluster概述
  19. python面向对象的编程_python面向对象的编程
  20. java @Scheduled注解执行定时任务

热门文章

  1. python工资一般多少西安-干货|python人工智能工程师工资多少钱
  2. python学了有什么用-让孩子学了Python编程有什么用
  3. 简单python脚本实例-你用 Python 写过哪些有趣的脚本?
  4. python 菜鸟-Python 运算符
  5. python刚出来多少薪资-Python薪资待遇到底是多少?老男孩python学习
  6. sap 双计量单位_[原创]浅谈MM模块的双计量单位(二)
  7. 后台系统-新增和编辑共用一个弹框(基于vue和element-ui)
  8. 关于javascript跳转与返回和刷新页面
  9. linux中用at命令5分钟后执行,我使用过的Linux命令之at - 在指定时间执行一次任务...
  10. 企业class类命名规范