前置:OpenGL基础29:深度测试
前面一章提到过:深度缓冲测试在片段着色器运行、以及模板测试(Stencil Testing)之后,那么这下知道模板测试是在什么时候了吧,模板测试和深度测试逻辑可以说是一致的,它会丢弃一些片段,保留下来的片段将会进入深度测试阶段:
glEnable(GL_STENCIL_TEST):开启模板测试glClear(GL_STENCIL_BUFFER_BIT):清空模板缓冲可以用模板测试实现一些非常有意思的效果,一个很经典的例子就是3D物体的描边(某些游戏中的选中效果):
可以从中看出:3个立方体点光源,都被加上了黄色的描边
对于深度测试,每个片段都有一个浮点值,而对于模板测试,每个片段都有一个8位的模板值(0x00-0xFF),原理也很简单:每次模板测试时,对应的片段都会根据它的模板值以及你的规则来决定是否丢弃对应的片段
我们假设规定模板值为1的话就绘制,为0的话就丢弃,那么整个流程就会是下面这样的:
理解了模板测试的流程后,再来看如何自定义模板测试的规则
模板测试主要有3个函数:
glStencilMask(GLint 0~255):决定对应的模板值的每一位是否可写入模板缓存void glStencilFunc(GLenum func, GLint ref, GLuint mask):设置模板测试规则,什么样的片段会被丢弃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:将模板值置为0GL_REPLACE:将模板值设置为用glStencilFunc函数设置的ref值GL_INCR:如果模板值不是最大值就将模板值+1GL_INCR_WRAP:与GL_INCR一样将模板值+1,如果模板值已经是最大值则设为0GL_DECR:如果模板值不是最小值就将模板值-1GL_DECR_WRAP:与GL_DECR一样将模板值-1,如果模板值已经是最小值则设为最大值GL_INVERT:将模板值按位反转例如 glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP) :任何测试的任何结果,模板缓冲都会保留它的值,这也是默认的设置
好了,如果对上面的3个函数有了解,那么就可以实现物体的描边了,只考虑3个立方体光源,步骤如下:
开启模板测试、模板写入、通过glStencilOp设置,使得被成功渲染的片段的模板值会被更新为0xFF把模板方程设置为GL_ALWAYS,永远绘制渲染3个立方体光源,写入模板缓冲关闭模板写入再次绘制3个立方体光源,把颜色改成黄色(将会是描边的颜色)并且放大一点点,把模板方程设置为GL_NOTEQUAL,只有对应片段的模板值不为0xFF时才绘制再次设置GL_ALWAYS,绘制剩下的图形开启模板写入,继续下一轮渲染完整代码:
#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数据已经绑定且我们就用之前的顶点数据,所以无需再管理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<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; } 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<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