目录
加载前的准备
下载并编译Assimp
配置Assimp
.dll文件的配置
.lib文件的配置
assimp文件的配置
举个栗子
代码
Mesh.h
Model.h
main.cpp
注意项
结果截图
资源下载
参考:LearnOpenGL
最终结果
注:由于CSDN上传的gif不能超过5兆,所以比较模糊,最后有清晰结果截图。
见上一篇文章:下载并编译Assimp
将生成的.dll文件放在.exe文件的同级目录下
将生成的.lib文件放到你的库目录下,并在项目的附加依赖中添加.lib文件
在include下的assimmp文件放到你的库目录下
在你的模型加载类中包含头文件
#include //assimp库头文件
#include
#include
#pragma once
#include // 所有头文件
#include
#include
#include "Shader.h"
#include
#include
#include
#include
#include
using namespace std;
//顶点
struct Vertex {
// 位置
glm::vec3 Position;
// 法向量
glm::vec3 Normal;
// 纹理坐标
glm::vec2 TexCoords;
// u向量
glm::vec3 Tangent;
// v向量
glm::vec3 Bitangent;
};
//纹理
struct Texture {
unsigned int id;
string type;
string path;
};
//Mesh类
class Mesh {
public:
/* Mesh 数据 */
vector vertices;
vector indices;
vector textures;
unsigned int VAO;
/* 函数 */
// 构造函数 参数:顶点 索引 纹理
Mesh(vector vertices, vector indices, vector textures)
{
this->vertices = vertices;
this->indices = indices;
this->textures = textures;
// 现在我们拥有了所有必需的数据,设置顶点缓冲区及其属性指针。
setupMesh();
}
// 画网格模型
void Draw(Shader shader)
{
// 绑定适当的纹理
unsigned int diffuseNr = 1;
unsigned int specularNr = 1;
unsigned int normalNr = 1;
unsigned int heightNr = 1;
for (unsigned int i = 0; i < textures.size(); i++)
{
glActiveTexture(GL_TEXTURE0 + i); // 绑定前激活适当的纹理单元
// 获取纹理编号(diffuse_textureN中的N)
string number;
string name = textures[i].type;
if (name == "texture_diffuse")
number = std::to_string(diffuseNr++);
else if (name == "texture_specular")
number = std::to_string(specularNr++);
else if (name == "texture_normal")
number = std::to_string(normalNr++);
else if (name == "texture_height")
number = std::to_string(heightNr++);
// 现在将采样器设置为正确的纹理单元
glUniform1i(glGetUniformLocation(shader.ID, (name + number).c_str()), i);
// 最后绑定纹理
glBindTexture(GL_TEXTURE_2D, textures[i].id);
}
// 画网格
glBindVertexArray(VAO);
glDrawElements(GL_TRIANGLES, indices.size(), GL_UNSIGNED_INT, 0);
glBindVertexArray(0);
// 一旦配置完毕将一切设置回默认值总是很好的做法,。
glActiveTexture(GL_TEXTURE0);
}
private:
/* 渲染数据 */
unsigned int VBO, EBO;
/* 函数 */
// 初始化所有缓冲区对象/数组
void setupMesh()
{
// 创建缓冲区/数组
glGenVertexArrays(1, &VAO);
glGenBuffers(1, &VBO);
glGenBuffers(1, &EBO);
glBindVertexArray(VAO);
//将数据加载到顶点缓冲区中
glBindBuffer(GL_ARRAY_BUFFER, VBO);
// 关于结构的一个好处是它们的内存布局对于它的所有项都是顺序的。
// 结果是我们可以简单地将指针传递给结构,并且它完美地转换为glm :: vec3 / 2数组,该数组再次转换为3/2浮点数,转换为字节数组。
glBufferData(GL_ARRAY_BUFFER, vertices.size() * sizeof(Vertex), &vertices[0], GL_STATIC_DRAW);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBO);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, indices.size() * sizeof(unsigned int), &indices[0], GL_STATIC_DRAW);
// 设置顶点属性指针
// 顶点位置
glEnableVertexAttribArray(0);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex), (void*)0);
// 顶点法线
glEnableVertexAttribArray(1);
glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex), (void*)offsetof(Vertex, Normal));
// 顶点纹理坐标
glEnableVertexAttribArray(2);
glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, sizeof(Vertex), (void*)offsetof(Vertex, TexCoords));
// u向量
glEnableVertexAttribArray(3);
glVertexAttribPointer(3, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex), (void*)offsetof(Vertex, Tangent));
// v向量
glEnableVertexAttribArray(4);
glVertexAttribPointer(4, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex), (void*)offsetof(Vertex, Bitangent));
glBindVertexArray(0);
}
};
#pragma once
#include //所有头文件
#include
#include
#define STB_IMAGE_IMPLEMENTATION //原作者没写
#include
#include //assimp库头文件
#include
#include
#include "Mesh.h"
#include "Shader.h"
#include
#include
#include
#include
#include
//头文件
#include
#include
#include
#include
#include
#include "Shader.h"
#include "Camera.h"
#include "Model.h"
#include
//-----------------------------------函数声明-------------------------------------------
void framebuffer_size_callback(GLFWwindow* window, int width, int height);
void mouse_callback(GLFWwindow* window, double xpos, double ypos);
void scroll_callback(GLFWwindow* window, double xoffset, double yoffset);
void processInput(GLFWwindow *window);
//-------------------------------------全局变量-------------------------------------------
//窗体宽高
const unsigned int SCR_WIDTH = 800;
const unsigned int SCR_HEIGHT = 600;
//摄像机相关
Camera camera(glm::vec3(0.0f, 0.0f, 3.0f));
float lastX = SCR_WIDTH / 2.0f;
float lastY = SCR_HEIGHT / 2.0f;
bool firstMouse = true;
// 时间
float deltaTime = 0.0f;
float lastFrame = 0.0f;
//主函数
int main()
{
// glfw: 初始化和配置
glfwInit();
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
#ifdef __APPLE__
glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE); // uncomment this statement to fix compilation on OS X
#endif
// glfw 窗体创建
GLFWwindow* window = glfwCreateWindow(SCR_WIDTH, SCR_HEIGHT, "ModelDemo", NULL, NULL);
if (window == NULL)
{
std::cout << "创建GLFW窗体失败" << std::endl;
glfwTerminate();
return -1;
}
glfwMakeContextCurrent(window);
glfwSetFramebufferSizeCallback(window, framebuffer_size_callback);
glfwSetCursorPosCallback(window, mouse_callback);
glfwSetScrollCallback(window, scroll_callback);
// 鼠标滑动回调函数
glfwSetInputMode(window, GLFW_CURSOR, GLFW_CURSOR_DISABLED);
// glad: load all OpenGL function pointers
if (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress))
{
std::cout << "Failed to initialize GLAD" << std::endl;
return -1;
}
// 设置全局opengl状态
glEnable(GL_DEPTH_TEST);//开启深度测试
//创建并编译shader
Shader ourShader("vertexSource.txt", "fragmentSource.txt");
// 加载模型
//FileSystem::getPath("resources/objects/nanosuit/nanosuit.obj")
//修改为相对路径
Model ourModel("../Debug/model/nanosuit/nanosuit.obj");
//Model ourModel("../Debug/model/warrior/arakkoa_warrior.obj");
// draw in wireframe
//glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
//循环渲染
while (!glfwWindowShouldClose(window))
{
//获取时间
float currentFrame = glfwGetTime();
deltaTime = currentFrame - lastFrame;
lastFrame = currentFrame;
// 键盘输入
processInput(window);
// 渲染
glClearColor(0.05f, 0.05f, 0.05f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
// 设置uniforms前使用Shader
ourShader.use();
// view/projection矩阵
glm::mat4 projection = glm::perspective(glm::radians(camera.Zoom), (float)SCR_WIDTH / (float)SCR_HEIGHT, 0.1f, 100.0f);
glm::mat4 view = camera.GetViewMatrix();
ourShader.setMat4("projection", projection);
ourShader.setMat4("view", view);
// 渲染加载的3d模型
glm::mat4 model = glm::mat4(1.0f);
//使其位于场景的中心
model = glm::translate(model, glm::vec3(0.0f, -1.75f, 0.0f));
//缩小它
model = glm::scale(model, glm::vec3(0.2f, 0.2f, 0.2f));
ourShader.setMat4("model", model);
ourModel.Draw(ourShader);
// glfw: 交换缓冲区和轮询IO事件(按下/释放按键,移动鼠标等)
glfwSwapBuffers(window);
glfwPollEvents();
}
// glfw: 终止,清除所有先前分配的GLFW资源。
glfwTerminate();
return 0;
}
//键盘按键控制
void processInput(GLFWwindow *window)
{
if (glfwGetKey(window, GLFW_KEY_ESCAPE) == GLFW_PRESS)
glfwSetWindowShouldClose(window, true);
if (glfwGetKey(window, GLFW_KEY_W) == GLFW_PRESS)
camera.ProcessKeyboard(Camera::FORWARD, deltaTime);
if (glfwGetKey(window, GLFW_KEY_S) == GLFW_PRESS)
camera.ProcessKeyboard(Camera::BACKWARD, deltaTime);
if (glfwGetKey(window, GLFW_KEY_A) == GLFW_PRESS)
camera.ProcessKeyboard(Camera::LEFT, deltaTime);
if (glfwGetKey(window, GLFW_KEY_D) == GLFW_PRESS)
camera.ProcessKeyboard(Camera::RIGHT, deltaTime);
}
// glfw: 窗口改变回调函数
void framebuffer_size_callback(GLFWwindow* window, int width, int height)
{
glViewport(0, 0, width, height);
}
// glfw: 鼠标滑动回调函数
void mouse_callback(GLFWwindow* window, double xpos, double ypos)
{
if (firstMouse)
{
lastX = xpos;
lastY = ypos;
firstMouse = false;
}
float xoffset = xpos - lastX;
float yoffset = lastY - ypos;
lastX = xpos;
lastY = ypos;
camera.ProcessMouseMovement(xoffset, yoffset);
}
// glfw: 鼠标滚轮回调函数
void scroll_callback(GLFWwindow* window, double xoffset, double yoffset)
{
camera.ProcessMouseScroll(yoffset);
}
采用相对路径,.obj文件及所用图片放在同一文件夹内
着色器类增加了一个setMat4函数,照之前的坐标系统那一篇文章加上就好
Model类中,包含stb_image.h前需要宏定义 #define STB_IMAGE_IMPLEMENTATION
https://download.csdn.net/download/lady_killer9/11139745
更多OpenGL知识:现代OpenGL入门教程
有问题请下方评论,转载请注明出处,并附有原文链接,谢谢!如有侵权,请及时联系。