OpenGL基础30:模板测试

 

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

一、模板测试

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

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

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

OpenGL基础30:模板测试_第1张图片

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

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

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

OpenGL基础30:模板测试_第2张图片

 

二、模板函数

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

模板测试主要有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
#include
#define GLEW_STATIC
#include
#include"Shader.h"
#include"Camera.h"
#include
#include
#include
#include"Mesh.h"
#include"Model.h"
#include
#include

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数据已经绑定且我们就用之前的顶点数据,所以无需再管理VBO
    glEnableVertexAttribArray(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
#include
#include"Shader.h"
#include"Mesh.h"
#include
#include
#include
#include
#include
#include
#include
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 meshes;
        string directory;
        vector 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 vertices;
            vector indices;
            vector 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;
                }
                else
                    vertex.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 diffuseMaps = this->loadMaterialTextures(material, aiTextureType_DIFFUSE, "texture_diffuse");
                textures.insert(textures.end(), diffuseMaps.begin(), diffuseMaps.end());
                vector specularMaps = this->loadMaterialTextures(material, aiTextureType_SPECULAR, "texture_specular");
                textures.insert(textures.end(), specularMaps.begin(), specularMaps.end());
            }

            return Mesh(vertices, indices, textures);
        }

        //遍历所有给定纹理类型的纹理位置,获取纹理的文件位置,然后加载生成纹理
        vector loadMaterialTextures(aiMaterial* mat, int type, string typeName)
        {
            vector 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
#include
#include
#include
#include"Shader.h"
#include
#include
#include
#include
#include
#include
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 vertices;
        vector indices;             //索引
        vector textures;
        Mesh(vector vertices, vector indices, vector 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)