文章目录

  • 写在前面
  • 和箱子模型告别
  • 坑点
  • 回到主题

写在前面

原文链接。原文应该是github上的一个项目,本文主要用来记录一些知识点和自己遇到的问题。

和箱子模型告别

所以,让我们导入一个由真正的艺术家所创造的模型,替代我这个天才的作品(你要承认,这些箱子可能是你看过的最漂亮的立方体了),测试一下我们的实现吧。由于我不想让我占太多的功劳,我会偶尔让别的艺术家也加入我们,这次我们将会加载Crytek的游戏孤岛危机(Crysis)中的原版纳米装(Nanosuit)。这个模型被输出为一个.obj文件以及一个.mtl文件,.mtl文件包含了模型的漫反射、镜面光和法线贴图(这个会在后面学习到),你可以在这里下载到(稍微修改之后的)模型,注意所有的纹理和模型文件应该位于同一个目录下,以供加载纹理。

现在在代码中,声明一个Model对象,将模型的文件位置传入。接下来模型应该会自动加载并(如果没有错误的话)在渲染循环中使用它的Draw函数来绘制物体,这样就可以了。不再需要缓冲分配、属性指针和渲染指令,只需要一行代码就可以了(真爽!)。接下来如果你创建一系列着色器,其中片段着色器仅仅输出物体的漫反射纹理颜色,最终的结果看上去会是这样的:

你可以在这里找到完整的源码。

这里有一个很坑的问题,可能让你得不到正确结果……事实上我也费了很多时间来debug,下面来说一下。

坑点

主要是由0号纹理单元引起的……

原文这里的代码其实是有点问题的,我们看一下他这里给出的着色器代码:

#version 330 core
out vec4 FragColor;in vec2 TexCoords;uniform sampler2D texture_diffuse1;void main()
{    FragColor = texture(texture_diffuse1, TexCoords);
}

再看我们在Mesh对象的Draw方法中设置的采样器名称:

很明显这里是对不上的,那么为什么我们还能看到模型呢?是因为0号纹理单元默认是一直激活的,在GLSL中的纹理采样器默认都会绑定到0号纹理单元,而我们在代码中恰好又绑定了纹理对象到这个纹理单元,所以能看到模型。那么我们可以改一下这个函数,让其激活并设置非零号纹理单元,就可以解决问题了——实际上并不能,你可以看一下texture对象的构造函数,其内调用了glBindTexture函数,也就是说只要使用了纹理对象,0号纹理单元上就一定绑定了某个纹理对象,那么在GLSL中进行采样时就一定要小心,采样的结果可能并不是黑色!出于这个原因我决定修改命名规则,我们使用sampler2D数组,同时传递两个整数给gpu,告诉它我们使用了多少个漫反射贴图和镜面光贴图。

第二个坑点是图片y轴的翻转问题。我们在纹理章节已经学过了:OpenGL要求y轴0.0坐标是在图片的底部的,但是图片的y轴0.0坐标通常在顶部。那么我们利用这个函数翻转图片的y轴:

但是还记得我们在导入obj文件的时候设置了什么吗?

aiProcess_FlipUVs将在处理的时候翻转y轴的纹理坐标。如果你感觉贴图很奇怪的话,可能就是这里的问题,两个地方都翻转了等于没有翻转。

回到主题

修改后的代码如下:

mesh:mesh:mesh:

#pragma once
#ifndef MESH_H
#define MESH_H#include <glad/glad.h>
#include <glm/glm.hpp>
#include <glm/gtc/matrix_transform.hpp>
#include "shader.h"
#include <vector>
#include "texture.h"
#include <string>
#include <iostream>
using std::vector;
using std::string;
using std::cout;
using std::max;struct Vertex
{// 位置 法向量 纹理坐标glm::vec3 Position;glm::vec3 Normal;glm::vec2 TexCoords;
};class Mesh
{public:/* 网格数据 */vector<Vertex> vertices;vector<unsigned int> indices;vector<Texture> textures;/* 函数 */Mesh(const vector<Vertex>& vertices, const vector<unsigned int>& indices, const vector<Texture>& textures);void Draw(const Shader& shader);
private:/* 渲染数据 */unsigned int VAO, VBO, EBO;void setupMesh();
};#endif // !MESH_H
#include "mesh.h"Mesh::Mesh(const vector<Vertex>& vertices, const vector<unsigned int>& indices, const vector<Texture>& textures) :vertices(vertices), indices(indices), textures(textures)
{setupMesh();
}void Mesh::setupMesh()
{// 初始化VAO VBO EBOglGenVertexArrays(1, &VAO);glGenBuffers(1, &VBO);glGenBuffers(1, &EBO);// 绑定VAOglBindVertexArray(VAO);// 绑定VBO 复制顶点数据glBindBuffer(GL_ARRAY_BUFFER, VBO);glBufferData(GL_ARRAY_BUFFER, vertices.size() * sizeof(Vertex), &vertices[0], GL_STATIC_DRAW);// 绑定EBO 复制索引数据glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBO);glBufferData(GL_ELEMENT_ARRAY_BUFFER, indices.size() * sizeof(unsigned int), &indices[0], GL_STATIC_DRAW);// 顶点位置glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex), (void*)0);glEnableVertexAttribArray(0);// 顶点法线// C++内置的offsetof函数 能自动返回结构对象中 某变量距离结构体对象首地址的偏移值:glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex), (void*)offsetof(Vertex, Normal));glEnableVertexAttribArray(1);// 顶点纹理坐标glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, sizeof(Vertex), (void*)offsetof(Vertex, TexCoords));glEnableVertexAttribArray(2);// 解绑VAOglBindVertexArray(0);
}unsigned int maxDiffuseNr = 0;
unsigned int maxSpecularNr = 0;void Mesh::Draw(const Shader& shader)
{// 当前漫反射纹理和镜面光纹理的编号unsigned int diffuseNr = 0;unsigned int specularNr = 0;for (unsigned int i = 0; i < textures.size(); i++){// 激活纹理单元 并绑定textures[i].use(i);// 获取纹理序号和类型string number;string type = textures[i].getName();if (type == "texture_diffuse")number = std::to_string(diffuseNr++);else if (type == "texture_specular")number = std::to_string(specularNr++);shader.setInt("material." + type + "[" + number + "]", i);}shader.setInt(string("material.") + "texture_diffuse_num", diffuseNr);shader.setInt(string("material.") + "texture_specular_num", specularNr);maxDiffuseNr = max(maxDiffuseNr, diffuseNr);maxSpecularNr = max(maxSpecularNr, specularNr);glBindVertexArray(VAO);glDrawElements(GL_TRIANGLES, indices.size(), GL_UNSIGNED_INT, 0);glBindVertexArray(0);// always good practice to set everything back to defaults once configured.glActiveTexture(GL_TEXTURE0);
}

model:model:model:

#pragma once
#ifndef MODEL_H
#define MODEL_H
#include <assimp/Importer.hpp>
#include <assimp/scene.h>
#include <assimp/postprocess.h>
#include "mesh.h"
#include <unordered_map>using std::unordered_map;class Model
{/* 函数 */
public:Model(const string& path){loadModel(path);}void Draw(const Shader& shader);
private:/* 模型数据 */vector<Mesh> meshes;/* 模型数据所在目录 */string directory;/* 记录已经加载过的纹理 */vector<Texture> texturesLoaded;/* 纹理文件到索引的哈希表 */unordered_map<string, unsigned int> texturesHashTable;void loadModel(const string& path);void processNode(aiNode* node, const aiScene* scene);Mesh processMesh(aiMesh* mesh, const aiScene* scene);vector<Texture> loadMaterialTextures(aiMaterial* mat, aiTextureType type, const string& typeName);
};#endif // !MODEL_H
#include "model.h"
#include <iostream>
using std::cout;
using std::endl;extern unsigned int maxDiffuseNr;
extern unsigned int maxSpecularNr;
bool firstDraw = false;void Model::Draw(const Shader& shader)
{for (unsigned int i = 0; i < meshes.size(); i++)meshes[i].Draw(shader);if (!firstDraw){cout << "First Draw finish\n";cout << "Max diffuse texture number is " << maxDiffuseNr << '\n';cout << "Max specular texture number is " << maxSpecularNr << '\n';firstDraw = 1;}
}void Model::loadModel(const string& path)
{Assimp::Importer import;// 读取模型文件 第二个参数用于后期处理const aiScene* scene = import.ReadFile(path, aiProcess_Triangulate | aiProcess_FlipUVs);// 是否读取成功if (!scene || scene->mFlags & AI_SCENE_FLAGS_INCOMPLETE || !scene->mRootNode){cout << "ERROR::ASSIMP::" << import.GetErrorString() << endl;return;}// 获取文件目录directory = path.substr(0, path.find_last_of('/'));// 从场景根节点开始递归处理所有节点processNode(scene->mRootNode, scene);// 打印obj的信息cout << "Loading success!\n";cout << "This model have " << meshes.size() << " meshes\n\n";
}void Model::processNode(aiNode* node, const aiScene* scene)
{// 处理节点的所有网格for (unsigned int i = 0; i < node->mNumMeshes; i++){// 依据索引从场景对象中拿到真正的数据aiMesh* mesh = scene->mMeshes[node->mMeshes[i]];meshes.push_back(processMesh(mesh,scene));}// 递归处理子节点for (unsigned int i = 0; i < node->mNumChildren; i++)processNode(node->mChildren[i], scene);
}Mesh Model::processMesh(aiMesh* mesh, const aiScene* scene)
{// 一个Mesh对象包括 Vertex(位置 法向量 纹理坐标) 绘制所需的索引数组 绘制所需的纹理vector<Vertex> vertices;vector<unsigned int> indices;vector<Texture> textures;// 处理顶点位置 法线 纹理坐标for (unsigned int i = 0; i < mesh->mNumVertices; i++){Vertex vertex;// 顶点位置 该数组永远存在vertex.Position = glm::vec3(mesh->mVertices[i].x, mesh->mVertices[i].y, mesh->mVertices[i].z);// 法线 不一定存在vertex.Normal = glm::vec3(mesh->mNormals[i].x, mesh->mNormals[i].y, mesh->mNormals[i].z);// 纹理坐标 不一定存在if (mesh->mTextureCoords[0])vertex.TexCoords = glm::vec2(mesh->mTextureCoords[0][i].x, mesh->mTextureCoords[0][i].y);elsevertex.TexCoords = glm::vec2(0.0f, 0.0f);vertices.push_back(vertex);}// 处理索引for (unsigned int i = 0; i < mesh->mNumFaces; i++){// 处理该网格的每个面aiFace face = mesh->mFaces[i];for (unsigned int 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 = loadMaterialTextures(material, aiTextureType_DIFFUSE, "texture_diffuse");vector<Texture> specularMaps = loadMaterialTextures(material, aiTextureType_SPECULAR, "texture_specular");textures.insert(textures.end(), diffuseMaps.begin(), diffuseMaps.end());textures.insert(textures.end(), specularMaps.begin(), specularMaps.end());}return Mesh(vertices, indices, textures);
}vector<Texture> Model::loadMaterialTextures(aiMaterial* mat, aiTextureType type, const string& typeName)
{vector<Texture> textures;// 类型为type的纹理个数for (unsigned int i = 0; i < mat->GetTextureCount(type); i++){aiString textureFilePosition;// 得到纹理文件的位置 存储在textureFilePosition中mat->GetTexture(type, i, &textureFilePosition);string str(textureFilePosition.C_Str());auto it = texturesHashTable.find(str);if (it == texturesHashTable.end()){// 未加载过texturesHashTable[str] = texturesLoaded.size();// 读取obj时已经翻转了纹理坐标 此处不需要再翻转图片y轴了Texture texture(typeName, directory + "/" + str, false);texturesLoaded.push_back(texture);textures.push_back(texture);}else{// 已经加载过了textures.push_back(texturesLoaded[it->second]);}}return textures;
}

shader:shader:shader:

#version 330 core
layout (location = 0) in vec3 aPos;   // 位置变量的属性位置值为 0
layout (location = 1) in vec3 aNormal;
layout (location = 2) in vec2 aTexCoords;uniform mat4 model;   //模型
uniform mat4 view;  //观察
uniform mat4 projection;    //投影out vec2 TexCoords;void main()
{TexCoords = aTexCoords;// 注意乘法要从右向左读gl_Position = projection * view * model * vec4(aPos, 1.0);
}
#version 330 core#define MAX_TEXTURE_NUM 8struct Material
{sampler2D texture_diffuse[MAX_TEXTURE_NUM];sampler2D texture_specular[MAX_TEXTURE_NUM];int texture_diffuse_num;int texture_specular_num;
};uniform Material material;in vec2 TexCoords;out vec4 FragColor;  void main()
{FragColor = vec4(0.0,0.0,0.0,0.0);for(int i = 0; i < material.texture_diffuse_num; i++){FragColor += texture(material.texture_diffuse[i], TexCoords);}FragColor.a = 1.0;
}

main:main:main:

#include <glad/glad.h>
#include <GLFW/glfw3.h>
#include <glm/glm.hpp>
#include <glm/gtc/matrix_transform.hpp>
#include <glm/gtc/type_ptr.hpp>
#include <iostream>
#include "shader.h"
#include "stb_image.h"
#include "camera.h"
#include "texture.h"
#include "model.h"
using std::cout;//窗口回调函数
void framebuffer_size_callback(GLFWwindow* window, int width, int height)
{//绘图视口 3D坐标到2D坐标的转换(映射)和这些参数(宽高)有关glViewport(0, 0, width, height);
}//键盘回调
void key_callback(GLFWwindow* window, int key, int scancode, int action, int mode);//鼠标回调
void mouse_callback(GLFWwindow* window, double xpos, double ypos);//滚轮回调
void scroll_callback(GLFWwindow* window, double xoffset, double yoffset);//窗口初始大小
const unsigned int SCR_WIDTH = 800;
const unsigned int SCR_HEIGHT = 600;//物体着色器
const char* vShaderPath = "ShaderFiles/shader.vert";
const char* fShaderPath = "ShaderFiles/shader.frag";
//光源着色器
const char* lightvShaderPath = "ShaderFiles/light_shader.vert";
const char* lightfShaderPath = "ShaderFiles/light_shader.frag";//混合颜色的插值
float mixValue = 0.2f;
//记录鼠标坐标
float lastX, lastY;
bool firstMouse = true;//摄像机
Camera camera(glm::vec3(0.0f, 0.0f, 3.0f));//光源位置
glm::vec3 lightPos(1.2f, 1.0f, 2.0f);int main()
{//glfw初始化glfwInit();//告诉glfw我们所使用的opengl版本 此处为3.3glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);//创建窗口GLFWwindow* window = glfwCreateWindow(SCR_WIDTH, SCR_HEIGHT, "LearnOpenGL", NULL, NULL);if (window == NULL){cout << "Failed to create GLFW window\n";glfwTerminate();return -1;}glfwMakeContextCurrent(window);glfwSetInputMode(window, GLFW_CURSOR, GLFW_CURSOR_DISABLED);//设置窗口回调函数glfwSetFramebufferSizeCallback(window, framebuffer_size_callback);//键盘回调函数glfwSetKeyCallback(window, key_callback);//鼠标回调glfwSetCursorPosCallback(window, mouse_callback);//滚轮回调glfwSetScrollCallback(window, scroll_callback);if (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress)){cout << "Failed to initialize GLAD\n";return -1;}//开启深度测试glEnable(GL_DEPTH_TEST);//着色器对象Shader shaderProgram = Shader(vShaderPath, fShaderPath);Shader lightShaderProgram = Shader(lightvShaderPath, lightfShaderPath);// light positionsfloat vertices[] = {-0.5f, -0.5f, -0.5f,0.5f, -0.5f, -0.5f,0.5f,  0.5f, -0.5f,0.5f,  0.5f, -0.5f,-0.5f,  0.5f, -0.5f,-0.5f, -0.5f, -0.5f,-0.5f, -0.5f,  0.5f,0.5f, -0.5f,  0.5f,0.5f,  0.5f,  0.5f,0.5f,  0.5f,  0.5f,-0.5f,  0.5f,  0.5f,-0.5f, -0.5f,  0.5f,-0.5f,  0.5f,  0.5f,-0.5f,  0.5f, -0.5f,-0.5f, -0.5f, -0.5f,-0.5f, -0.5f, -0.5f,-0.5f, -0.5f,  0.5f,-0.5f,  0.5f,  0.5f,0.5f,  0.5f,  0.5f,0.5f,  0.5f, -0.5f,0.5f, -0.5f, -0.5f,0.5f, -0.5f, -0.5f,0.5f, -0.5f,  0.5f,0.5f,  0.5f,  0.5f,-0.5f, -0.5f, -0.5f,0.5f, -0.5f, -0.5f,0.5f, -0.5f,  0.5f,0.5f, -0.5f,  0.5f,-0.5f, -0.5f,  0.5f,-0.5f, -0.5f, -0.5f,-0.5f,  0.5f, -0.5f,0.5f,  0.5f, -0.5f,0.5f,  0.5f,  0.5f,0.5f,  0.5f,  0.5f,-0.5f,  0.5f,  0.5f,-0.5f,  0.5f, -0.5f,};//物体(obj)Model modelObj("Obj/Nanosuit/nanosuit.obj");//光源unsigned int lightVAO;unsigned int lightVBO;glGenVertexArrays(1, &lightVAO);glBindVertexArray(lightVAO);glGenBuffers(1, &lightVBO);glBindBuffer(GL_ARRAY_BUFFER, lightVBO);glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), (void*)0);glEnableVertexAttribArray(0);//线框模式//glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);//这些uniform不会更新 可以放到循环外面while (!glfwWindowShouldClose(window)){glClearColor(0.05f, 0.05f, 0.05f, 1.0f);glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);//矩阵运算glm::mat4 view = camera.GetViewMatrix();glm::mat4 projection = glm::perspective(glm::radians(camera.Fov), SCR_WIDTH * 1.0f / SCR_HEIGHT, 0.1f, 100.0f);glm::mat4 model(1.0f);model = glm::scale(model, glm::vec3(0.25f, 0.25f, 0.25f));//激活着色器shaderProgram.use();shaderProgram.setMat4("model", model);shaderProgram.setMat4("view", view);shaderProgram.setMat4("projection", projection);glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);modelObj.Draw(shaderProgram);model = glm::translate(glm::mat4(1.0f), glm::vec3(0.0f, 0.0f, 2.0f));model = glm::rotate(model, glm::radians(180.f), glm::vec3(0.0f, 1.0f, 0.0f));model = glm::scale(model, glm::vec3(0.25f, 0.25f, 0.25f));shaderProgram.setMat4("model", model);glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);modelObj.Draw(shaderProgram);glfwSwapBuffers(window);glfwPollEvents();}//这一步是可选的glDeleteVertexArrays(1, &lightVAO);glDeleteBuffers(1, &lightVBO);//glDeleteBuffers(1, &EBO);//释放资源glfwTerminate();return 0;
}void key_callback(GLFWwindow* window, int key, int scancode, int action, int mode)
{if (action == GLFW_REPEAT || action == GLFW_PRESS){if (key == GLFW_KEY_ESCAPE){glfwSetWindowShouldClose(window, GL_TRUE);return;}switch (key){case GLFW_KEY_UP:mixValue += 0.1f;if (mixValue >= 1.0f)mixValue = 1.0f;break;case GLFW_KEY_DOWN:mixValue -= 0.1f;if (mixValue <= 0.0f)mixValue = 0.0f;break;case GLFW_KEY_W:camera.ProcessKeyboard(FORWARD);break;case GLFW_KEY_S:camera.ProcessKeyboard(BACKWARD);break;case GLFW_KEY_A:camera.ProcessKeyboard(LEFT);break;case GLFW_KEY_D:camera.ProcessKeyboard(RIGHT);break;default:break;}}
}void mouse_callback(GLFWwindow* window, double xpos, double ypos)
{if (firstMouse){firstMouse = false;lastX = xpos, lastY = ypos;}camera.ProcessMouseMovement(xpos - lastX, lastY - ypos);lastX = xpos;lastY = ypos;
}void scroll_callback(GLFWwindow* window, double xoffset, double yoffset)
{camera.ProcessMouseScroll(yoffset);
}

下一步:加入镜面光贴图和光源!

shader:shader:shader:

#version 330 core
layout (location = 0) in vec3 aPos;   // 位置变量的属性位置值为 0
layout (location = 1) in vec3 aNormal;
layout (location = 2) in vec2 aTexCoords;uniform mat4 model;   //模型
uniform mat4 view;  //观察
uniform mat4 projection;    //投影out vec3 Normal;
out vec3 FragPos;
out vec2 TexCoords;void main()
{Normal = mat3(transpose(inverse(model))) * aNormal;FragPos = vec3(model * vec4(aPos, 1.0));TexCoords = aTexCoords;// 注意乘法要从右向左读gl_Position = projection * view * model * vec4(aPos, 1.0);
}
#version 330 core#define MAX_TEXTURE_NUM 8struct Material
{sampler2D texture_diffuse[MAX_TEXTURE_NUM];sampler2D texture_specular[MAX_TEXTURE_NUM];int texture_diffuse_num;int texture_specular_num;float shininess;
};// 方向光
struct DirLight
{vec3 direction;vec3 ambient;vec3 diffuse;vec3 specular;
};// 点光源
struct PointLight
{vec3 position;vec3 ambient;vec3 diffuse;vec3 specular;float constant;float linear;float quadratic;
};// 聚光-手电筒
struct SpotLight
{vec3 position;vec3 direction;vec3 ambient;vec3 diffuse;vec3 specular;// 内圆锥余弦值float cutOff;// 外圆锥余弦值float outerCutOff;float constant;float linear;float quadratic;
};in vec3 Normal;
in vec3 FragPos;
in vec2 TexCoords;out vec4 FragColor;  uniform vec3 viewPos;uniform Material material;uniform DirLight dirLight;
#define NR_POINT_LIGHTS 2
uniform PointLight pointLights[NR_POINT_LIGHTS];
uniform SpotLight spotLight;vec3 CalcDirLight(DirLight light, vec3 normal, vec3 viewDir);
vec3 CalPointLight(PointLight light, vec3 normal, vec3 fragPos, vec3 viewDir);
vec3 CalSpotLight(SpotLight light, vec3 normal, vec3 fragPos, vec3 viewDir);
vec3 CalLightSum(vec3 lightAmbient, vec3 lightDiffuse, vec3 lightSpecular, float diff, float spec);void main()
{vec3 norm = normalize(Normal);vec3 viewDir = normalize(viewPos - FragPos);// 定向光照vec3 result = CalcDirLight(dirLight, norm, viewDir);// 点光源for(int i = 0; i < NR_POINT_LIGHTS; i++)result += CalPointLight(pointLights[i], norm, FragPos, viewDir);// 聚光result += CalSpotLight(spotLight, norm, FragPos, viewDir);FragColor = vec4(result, 1.0);
}// 计算某个方向光源对该片段颜色的贡献
vec3 CalcDirLight(DirLight light, vec3 normal, vec3 viewDir)
{vec3 lightDir = normalize(-light.direction);// 漫反射着色float diff = max(dot(normal,lightDir),0.0);// 镜面光着色vec3 reflectDir = reflect(-lightDir,normal);float spec = pow(max(dot(viewDir,reflectDir),0.0),material.shininess);// 合并return CalLightSum(light.ambient, light.diffuse, light.specular, diff, spec);
}// 计算某个点光源对该片段颜色的贡献
vec3 CalPointLight(PointLight light, vec3 normal, vec3 fragPos, vec3 viewDir)
{vec3 lightDir = normalize(light.position - fragPos);// 漫反射着色float diff = max(dot(normal,lightDir),0.0);// 镜面光着色vec3 reflectDir = reflect(-lightDir,normal);float spec = pow(max(dot(viewDir,reflectDir),0.0),material.shininess);// 距离float dis = length(light.position - fragPos);// 衰减float attenuation = 1.0 / (light.constant + light.linear * dis + light.quadratic * dis *dis);// 合并return CalLightSum(light.ambient, light.diffuse, light.specular, diff, spec) * attenuation;
}// 计算某个聚光灯源对该片段颜色的贡献
vec3 CalSpotLight(SpotLight light, vec3 normal, vec3 fragPos, vec3 viewDir)
{vec3 lightDir = normalize(light.position - fragPos);// 漫反射着色float diff = max(dot(normal,lightDir),0.0);// 镜面光着色vec3 reflectDir = reflect(-lightDir,normal);float spec = pow(max(dot(viewDir,reflectDir),0.0),material.shininess);// 距离float dis = length(light.position - fragPos);// 衰减float attenuation = 1.0 /(light.constant + light.linear * dis + light.quadratic * dis * dis);// 内外光切角插值 实现平滑过度效果float cosTheta = dot(lightDir, normalize(-light.direction));float epsilon = light.cutOff - light.outerCutOff;float intensity = clamp((cosTheta  - light.outerCutOff) / epsilon, 0.0, 1.0);// 合并return CalLightSum(light.ambient, light.diffuse, light.specular, diff, spec) * attenuation * intensity;
}// 计算环境光、漫反射、镜面光的颜色和
// 没有计算衰减
vec3 CalLightSum(vec3 lightAmbient, vec3 lightDiffuse, vec3 lightSpecular, float diff, float spec)
{vec3 diffuse = vec3(0.0,0.0,0.0);for(int i = 0; i < material.texture_diffuse_num; i++){diffuse += texture(material.texture_diffuse[i], TexCoords).rgb;}vec3 ambient = lightAmbient * diffuse;diffuse = lightDiffuse * diff * diffuse;vec3 specular = vec3(0.0,0.0,0.0);for(int i = 0; i < material.texture_specular_num; i++){specular += texture(material.texture_specular[i], TexCoords).rgb;}specular = lightSpecular * spec * specular;return ambient + diffuse + specular;
}

main:main:main:

#include <glad/glad.h>
#include <GLFW/glfw3.h>
#include <glm/glm.hpp>
#include <glm/gtc/matrix_transform.hpp>
#include <glm/gtc/type_ptr.hpp>
#include <iostream>
#include "shader.h"
#include "stb_image.h"
#include "camera.h"
#include "light.h"
#include "texture.h"
#include "model.h"
using std::cout;//窗口回调函数
void framebuffer_size_callback(GLFWwindow* window, int width, int height)
{//绘图视口 3D坐标到2D坐标的转换(映射)和这些参数(宽高)有关glViewport(0, 0, width, height);
}//键盘回调
void key_callback(GLFWwindow* window, int key, int scancode, int action, int mode);//鼠标回调
void mouse_callback(GLFWwindow* window, double xpos, double ypos);//滚轮回调
void scroll_callback(GLFWwindow* window, double xoffset, double yoffset);//窗口初始大小
const unsigned int SCR_WIDTH = 800;
const unsigned int SCR_HEIGHT = 600;//物体着色器
const char* vShaderPath = "ShaderFiles/shader.vert";
const char* fShaderPath = "ShaderFiles/shader.frag";
//光源着色器
const char* lightvShaderPath = "ShaderFiles/shader.vert";
const char* lightfShaderPath = "ShaderFiles/light_shader.frag";//混合颜色的插值
float mixValue = 0.2f;
//记录鼠标坐标
float lastX, lastY;
bool firstMouse = true;//摄像机
Camera camera(glm::vec3(0.0f, 0.0f, 3.0f));//光源位置
glm::vec3 lightPos(1.2f, 1.0f, 2.0f);int main()
{//glfw初始化glfwInit();//告诉glfw我们所使用的opengl版本 此处为3.3glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);//创建窗口GLFWwindow* window = glfwCreateWindow(SCR_WIDTH, SCR_HEIGHT, "LearnOpenGL", NULL, NULL);if (window == NULL){cout << "Failed to create GLFW window\n";glfwTerminate();return -1;}glfwMakeContextCurrent(window);glfwSetInputMode(window, GLFW_CURSOR, GLFW_CURSOR_DISABLED);//设置窗口回调函数glfwSetFramebufferSizeCallback(window, framebuffer_size_callback);//键盘回调函数glfwSetKeyCallback(window, key_callback);//鼠标回调glfwSetCursorPosCallback(window, mouse_callback);//滚轮回调glfwSetScrollCallback(window, scroll_callback);if (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress)){cout << "Failed to initialize GLAD\n";return -1;}//开启深度测试glEnable(GL_DEPTH_TEST);//着色器对象Shader shaderProgram = Shader(vShaderPath, fShaderPath);Shader lightShaderProgram = Shader(lightvShaderPath, lightfShaderPath);// light positionsfloat vertices[] = {-0.5f, -0.5f, -0.5f,0.5f, -0.5f, -0.5f,0.5f,  0.5f, -0.5f,0.5f,  0.5f, -0.5f,-0.5f,  0.5f, -0.5f,-0.5f, -0.5f, -0.5f,-0.5f, -0.5f,  0.5f,0.5f, -0.5f,  0.5f,0.5f,  0.5f,  0.5f,0.5f,  0.5f,  0.5f,-0.5f,  0.5f,  0.5f,-0.5f, -0.5f,  0.5f,-0.5f,  0.5f,  0.5f,-0.5f,  0.5f, -0.5f,-0.5f, -0.5f, -0.5f,-0.5f, -0.5f, -0.5f,-0.5f, -0.5f,  0.5f,-0.5f,  0.5f,  0.5f,0.5f,  0.5f,  0.5f,0.5f,  0.5f, -0.5f,0.5f, -0.5f, -0.5f,0.5f, -0.5f, -0.5f,0.5f, -0.5f,  0.5f,0.5f,  0.5f,  0.5f,-0.5f, -0.5f, -0.5f,0.5f, -0.5f, -0.5f,0.5f, -0.5f,  0.5f,0.5f, -0.5f,  0.5f,-0.5f, -0.5f,  0.5f,-0.5f, -0.5f, -0.5f,-0.5f,  0.5f, -0.5f,0.5f,  0.5f, -0.5f,0.5f,  0.5f,  0.5f,0.5f,  0.5f,  0.5f,-0.5f,  0.5f,  0.5f,-0.5f,  0.5f, -0.5f,};//物体(obj)Model modelObj("Obj/Nanosuit/nanosuit.obj");//光源unsigned int lightVAO;unsigned int lightVBO;glGenVertexArrays(1, &lightVAO);glBindVertexArray(lightVAO);glGenBuffers(1, &lightVBO);glBindBuffer(GL_ARRAY_BUFFER, lightVBO);glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), (void*)0);glEnableVertexAttribArray(0);DirLight dirLight(glm::vec3(-0.2f, -1.0f, -0.3f), glm::vec3(0.05f, 0.05f, 0.05f), glm::vec3(0.4f, 0.4f, 0.4f), glm::vec3(0.5f, 0.5f, 0.5f));glm::vec3 pointLightPositions[] = {glm::vec3(-2.0f,4.0f,1.0f),glm::vec3(2.0f,1.0f,0.0f)};PointLight pointLights[] = {PointLight(pointLightPositions[0],glm::vec3(0.05f,0.05f,0.05f),glm::vec3(0.8f,0.8f,0.8f),glm::vec3(1.0f,1.0f,1.0f)),PointLight(pointLightPositions[1],glm::vec3(0.05f,0.05f,0.05f),glm::vec3(0.8f,0.8f,0.8f),glm::vec3(1.0f,1.0f,1.0f)),};SpotLight spotLight(camera.Position, camera.Front, glm::vec3(0.2f, 0.2f, 0.2f), glm::vec3(0.5f, 0.5f, 0.5f), glm::vec3(1.0f, 1.0f, 1.0f));//线框模式//glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);//这些uniform不会更新 可以放到循环外面shaderProgram.use();shaderProgram.setFloat("material.shininess", 32.0f);setLightAllAttribute(shaderProgram, "dirLight", &dirLight);setLightAllAttribute(shaderProgram, "pointLights", pointLights, 2);lightShaderProgram.use();lightShaderProgram.setVec3("lightColor", glm::vec3(1.0, 1.0, 1.0));while (!glfwWindowShouldClose(window)){glClearColor(0.05f, 0.05f, 0.05f, 1.0f);glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);//矩阵运算glm::mat4 view = camera.GetViewMatrix();glm::mat4 projection = glm::perspective(glm::radians(camera.Fov), SCR_WIDTH * 1.0f / SCR_HEIGHT, 0.1f, 100.0f);glm::mat4 model(1.0f);model = glm::scale(model, glm::vec3(0.25f, 0.25f, 0.25f));//激活着色器shaderProgram.use();shaderProgram.setMat4("model", model);shaderProgram.setMat4("view", view);shaderProgram.setMat4("projection", projection);shaderProgram.setVec3("viewPos", camera.Position);spotLight.position = camera.Position;spotLight.direction = camera.Front;setLightAllAttribute(shaderProgram, "spotLight", &spotLight);modelObj.Draw(shaderProgram);model = glm::translate(glm::mat4(1.0f), glm::vec3(0.0f, 0.0f, 2.0f));model = glm::rotate(model, glm::radians(180.f), glm::vec3(0.0f, 1.0f, 0.0f));model = glm::scale(model, glm::vec3(0.25f, 0.25f, 0.25f));shaderProgram.setMat4("model", model);modelObj.Draw(shaderProgram);lightShaderProgram.use();lightShaderProgram.setMat4("view", view);lightShaderProgram.setMat4("projection", projection);glBindVertexArray(lightVAO);for (unsigned int i = 0; i < 2; i++){glm::mat4 lightModel(1.0f);lightModel = glm::translate(lightModel, pointLightPositions[i]);lightModel = glm::scale(lightModel, glm::vec3(0.2f, 0.2f, 0.2f));lightShaderProgram.setMat4("model", lightModel);glDrawArrays(GL_TRIANGLES, 0, 36);}glfwSwapBuffers(window);glfwPollEvents();}//这一步是可选的glDeleteVertexArrays(1, &lightVAO);glDeleteBuffers(1, &lightVBO);//glDeleteBuffers(1, &EBO);//释放资源glfwTerminate();return 0;
}void key_callback(GLFWwindow* window, int key, int scancode, int action, int mode)
{if (action == GLFW_REPEAT || action == GLFW_PRESS){if (key == GLFW_KEY_ESCAPE){glfwSetWindowShouldClose(window, GL_TRUE);return;}switch (key){case GLFW_KEY_UP:mixValue += 0.1f;if (mixValue >= 1.0f)mixValue = 1.0f;break;case GLFW_KEY_DOWN:mixValue -= 0.1f;if (mixValue <= 0.0f)mixValue = 0.0f;break;case GLFW_KEY_W:camera.ProcessKeyboard(FORWARD);break;case GLFW_KEY_S:camera.ProcessKeyboard(BACKWARD);break;case GLFW_KEY_A:camera.ProcessKeyboard(LEFT);break;case GLFW_KEY_D:camera.ProcessKeyboard(RIGHT);break;default:break;}}
}void mouse_callback(GLFWwindow* window, double xpos, double ypos)
{if (firstMouse){firstMouse = false;lastX = xpos, lastY = ypos;}camera.ProcessMouseMovement(xpos - lastX, lastY - ypos);lastX = xpos;lastY = ypos;
}void scroll_callback(GLFWwindow* window, double xoffset, double yoffset)
{camera.ProcessMouseScroll(yoffset);
}

甚至我都必须要承认这个可能是比一直使用的箱子要好看多了。使用Assimp,你能够加载互联网上的无数模型。有很多资源网站都提供了多种格式的免费3D模型供你下载。但还是要注意,有些模型会不能正常地载入,纹理的路径会出现问题,或者Assimp并不支持它的格式。

LearnOpenGL 模型加载—模型(二 绘制模型)相关推荐

  1. 【Tensorflow 2.0 正式版教程】ImageNet(二)模型加载与训练

    前面的教程都只在小模型.小数据库上进行了演示,这次来真正实战一个大型数据库ImageNet.教程会分为三部分:数据增强.模型加载与训练.模型测试,最终在ResNet50上可以达到77.72%的top- ...

  2. nuScenes自动驾驶数据集:格式转换,模型的数据加载(二)

    文章目录 一.nuScenes数据集格式精解 二.nuScenes数据格式转换(To COCO) 数据格式转换框架 2.1 核心:convert_nuScenes.py解析 其他格式转换文件 2.1. ...

  3. OpenGL OBJ模型加载.

    在我们前面绘制一个屋,我们可以看到,需要每个立方体一个一个的自己来推并且还要处理位置信息.代码量大并且要时间.现在我们通过加载模型文件的方法来生成模型文件,比较流行的3D模型文件有OBJ,FBX,da ...

  4. 如何将深度学习模型加载到android环境中

    承接上一篇的内容,考虑如何将深度学习的模型加载到android app中 文章目录 前言 一.使用工具 二.使用步骤 1.模型格式的转换 2.配置文件修改 3. 应用程序 前言 将图片学习的模型加载到 ...

  5. 用Assimp模型加载库加载一个Crytek的游戏孤岛危机(Crysis)中的原版纳米装(Nanosuit)

    用这个例子来对GitHub上的LearnOpenGL教程前四个单元用到的所有自定义或者引入的各种头文件和资源进行一个总结,不得不说这个教程简直太美妙了. 这个模型是来自对GitHub上的LearnOp ...

  6. allegro 3D模型怎么找? PCB的DFA如何设计?如何加载PCB的3D模型?如何避免器件之间的干涉?PCB的3D设计 DFA设计的概念

    一些群友问杨老师,allegro的3D模型模型怎么找? PCB的DFA如何设计?如何加载PCB的3D模型?如何避免器件之间的干涉?这里杨老师对PCB中DFA设计常见的几种方法进行分析下 这些问题来自群 ...

  7. opengl加载多个3ds模型失败记

    VC6 下载 http://blog.csdn.net/bcbobo21cn/article/details/44200205 opengl环境配置 http://blog.csdn.net/bcbo ...

  8. TensorFlow——在web.py、Django环境下TensorFlow(Keras、tf.keras)加载和使用多模型失败解决方案

    问题描述 Cannot interpret feed_dict key as Tensor: Tensor Tensor("Placeholder_8:0", shape=(3, ...

  9. PyTorch框架学习十九——模型加载与保存

    PyTorch框架学习十九--模型加载与保存 一.序列化与反序列化 二.PyTorch中的序列化与反序列化 1.torch.save 2.torch.load 三.模型的保存 1.方法一:保存整个Mo ...

  10. 【TensorFlow】TensorFlow从浅入深系列之十三 -- 教你深入理解模型持久化(模型保存、模型加载)

    本文是<TensorFlow从浅入深>系列之第13篇 TensorFlow从浅入深系列之一 -- 教你如何设置学习率(指数衰减法) TensorFlow从浅入深系列之二 -- 教你通过思维 ...

最新文章

  1. 关于Spring中的context:annotation-config/配置(开启注解)
  2. Ubuntu18.04 安装 gnuplot
  3. 具有ELK的APIGEE API网关日志管理(Elastic Search,Logstash和Kibana)
  4. mysql error 1201_ERROR 1201 (HY000): Could not initialize master info structure; .....
  5. polymorphism java_Java基础-面向对象第三大特性之多态(polymorphism)
  6. 宝塔linux 做负载均衡,利用BT宝塔面板做网站多服务器负载均衡图文教程
  7. 盲视频超分辨率:南理工提出不用HR参与也能训练的自监督学习方法
  8. 【新书推荐】Silverlight 4教程书籍推荐
  9. Dynamics 365 Customer Engagement中插件的调试
  10. LOJ2874 JOISC2014 历史研究 分块、莫队
  11. AppUse学习笔记
  12. 新手淘客你必须知道的秘密
  13. 华为 OSPF虚链路出现环路了,如何解决?
  14. 清华大学计算机科学学院刘钊,姚 骏-清华大学生命学院
  15. Zephyr pinctrl
  16. “大数据”挖出老鼠仓的政治价值
  17. Play Framework 2.5.x 测试环境搭建
  18. 反恐精英服务器维修,China.com 反恐精英专区
  19. openfeign调用异常:feign.FeignException: [405] during [GET]
  20. windows7系统C盘AppData可以删除吗,对电脑操作系统有影响吗?对操作软件有影响吗?

热门文章

  1. 办公自动化基础知识题一
  2. 华为无线AC,AC案例
  3. 【区块链 | DAPP】Dapp应该怎么开发?分享一篇Dapp开发的逻辑(分享) 更新中~
  4. 记录电脑蓝屏错误0x00000019怎么解决
  5. CyberLink YouCam 新人求教一个绿色背景提取红色小球的问题
  6. Vista系统登录密码破解
  7. 做产品的正确姿势:理性与感性
  8. 制作网页版简易计算器(Calculator)
  9. 怀孕女子缺钱拒绝进医院 在街头生下双胞胎女儿
  10. 野狗 Sync 分析3 - 数据筛选