Assimp :全称为Open Asset Import Library,这是一个模型加载库,可以导入几十种不同格式的模型文件(同样也可以导出部分模型格式)。只要Assimp加载完了模型文件,我们就可以从Assimp上获取所有我们需要的模型数据。Assimp把不同的模型文件都转换为一个统一的数据结构,所有无论我们导入何种格式的模型文件,都可以用同一个方式去访问我们需要的模型数据。

官网地址:https://www.assimp.org

安装及使用

环境:win7 VS2015

1. 下载Assimp:

地址:https://github.com/assimp/assimp

2. 下载安装assimp源码编译需要用到的其他库directx sdk、boost库

a. 安装 DirectX SDK:assimp view依赖directx sdk
DirectX下载地址:DirectX官方下载

  安装DirectX SDK时,可能遇到一个错误码为s1023的错误,这种情况下,请在安装SDK之前根据这个先卸载C++ Redistributable package(s)

  1. 卸载: Visual C++ 2010 Redistributable Package version 10.0.40219

     在控制面板里像卸游戏一样卸,注意看清楚一定要把x86和x64两个都卸载了(看清楚是2010,还有很多其他年份的名字很相似的,别搞错了)
    
  2. 再次傻瓜式一键安装之前下载好的DirectX安装包

  3. 装完DirectX后,重新把刚刚卸载掉的两个东西装上(Visual C++ 2010 Redistributable Package→下载地址


vcredist X86 x64都下载
这里参照 :https://www.jianshu.com/p/4f3a1271ce0b
(Assimp的安装编译及使用过程全纪录(VS2015))

b.下载Boost:
  assimp是依赖boost库的,如果没有boost库只能编译出一个功能受限的版本。
boost官方链接:www.boost.org

assimp没有用到boost需要编译的部分,所以boost只需要下载解压就可以了。

3. cmake打开生成VS工程:

运行cmake 添加配置信息:点Add Entry,添加BOOST_ROOT变量,type选STRING,value添加boost的目录(此步骤也可以不做,编译出的assimp可能是功能受限的)。

点击configure 、Generate 生成VS 2015工程

打开Assimp.sln 编译可能会出现下面的问题:

对Assimp项目进行下配置即可解决:

我选择debug版本进行编译 生成:assimp-vc140-mtd.lib、assimp-vc140-mtd.dll 、config.h文件
config.h 文件在assimp-master\build\include\assimp 目录下,把config.h放置到assimp-master\include\assimp 目录下
否则项目中使用assimp时会提示找不到 assimp\config.h 文件

4. 项目配置:

a. 项目属性 ----> C/C++ —> 附加包含目录 —> your_path\assimp-master\include
b. 项目属性 ----> 链接器 —> 常规 —> 附加库目录 —> your_path\lib
c. 项目属性 ----> 链接器 —> 输入 —> 附加依赖项 —> assimp-vc140-mtd.lib

5. 代码:

这里代码使用 learnopengl 模型加载
Camera.h

#ifndef CAMERA_H
#define CAMERA_H#include <glad/glad.h>
#include <glm/glm.hpp>
#include <glm/gtc/matrix_transform.hpp>#include <vector>// Defines several possible options for camera movement. Used as abstraction to stay away from window-system specific input methods
enum Camera_Movement {FORWARD,BACKWARD,LEFT,RIGHT
};// Default camera values
const float YAW = -90.0f;
const float PITCH = 0.0f;
const float SPEED = 2.5f;
const float SENSITIVITY = 0.1f;
const float ZOOM = 45.0f;// An abstract camera class that processes input and calculates the corresponding Euler Angles, Vectors and Matrices for use in OpenGL
class Camera
{public:// Camera Attributesglm::vec3 Position;glm::vec3 Front;glm::vec3 Up;glm::vec3 Right;glm::vec3 WorldUp;// Euler Anglesfloat Yaw;float Pitch;// Camera optionsfloat MovementSpeed;float MouseSensitivity;float Zoom;// Constructor with vectorsCamera(glm::vec3 position = glm::vec3(0.0f, 0.0f, 0.0f), glm::vec3 up = glm::vec3(0.0f, 1.0f, 0.0f), float yaw = YAW, float pitch = PITCH) : Front(glm::vec3(0.0f, 0.0f, -1.0f)), MovementSpeed(SPEED), MouseSensitivity(SENSITIVITY), Zoom(ZOOM){Position = position;WorldUp = up;Yaw = yaw;Pitch = pitch;updateCameraVectors();}// Constructor with scalar valuesCamera(float posX, float posY, float posZ, float upX, float upY, float upZ, float yaw, float pitch) : Front(glm::vec3(0.0f, 0.0f, -1.0f)), MovementSpeed(SPEED), MouseSensitivity(SENSITIVITY), Zoom(ZOOM){Position = glm::vec3(posX, posY, posZ);WorldUp = glm::vec3(upX, upY, upZ);Yaw = yaw;Pitch = pitch;updateCameraVectors();}// Returns the view matrix calculated using Euler Angles and the LookAt Matrixglm::mat4 GetViewMatrix(){return glm::lookAt(Position, Position + Front, Up);}// Processes input received from any keyboard-like input system. Accepts input parameter in the form of camera defined ENUM (to abstract it from windowing systems)void ProcessKeyboard(Camera_Movement direction, float deltaTime){float velocity = MovementSpeed * deltaTime;if (direction == FORWARD)Position += Front * velocity;if (direction == BACKWARD)Position -= Front * velocity;if (direction == LEFT)Position -= Right * velocity;if (direction == RIGHT)Position += Right * velocity;}// Processes input received from a mouse input system. Expects the offset value in both the x and y direction.void ProcessMouseMovement(float xoffset, float yoffset, GLboolean constrainPitch = true){xoffset *= MouseSensitivity;yoffset *= MouseSensitivity;Yaw += xoffset;Pitch += yoffset;// Make sure that when pitch is out of bounds, screen doesn't get flippedif (constrainPitch){if (Pitch > 89.0f)Pitch = 89.0f;if (Pitch < -89.0f)Pitch = -89.0f;}// Update Front, Right and Up Vectors using the updated Euler anglesupdateCameraVectors();}// Processes input received from a mouse scroll-wheel event. Only requires input on the vertical wheel-axisvoid ProcessMouseScroll(float yoffset){if (Zoom >= 1.0f && Zoom <= 45.0f)Zoom -= yoffset;if (Zoom <= 1.0f)Zoom = 1.0f;if (Zoom >= 45.0f)Zoom = 45.0f;}private:// Calculates the front vector from the Camera's (updated) Euler Anglesvoid updateCameraVectors(){// Calculate the new Front vectorglm::vec3 front;front.x = cos(glm::radians(Yaw)) * cos(glm::radians(Pitch));front.y = sin(glm::radians(Pitch));front.z = sin(glm::radians(Yaw)) * cos(glm::radians(Pitch));Front = glm::normalize(front);// Also re-calculate the Right and Up vectorRight = glm::normalize(glm::cross(Front, WorldUp));  // Normalize the vectors, because their length gets closer to 0 the more you look up or down which results in slower movement.Up = glm::normalize(glm::cross(Right, Front));}
};
#endif

Mesh.h

#pragma once
#include <glad/glad.h> // 所有头文件
#include <glm/glm.hpp>
#include <glm/gtc/matrix_transform.hpp>
#include "Shader.h"
#include <string>
#include <fstream>
#include <sstream>
#include <iostream>
#include <vector>
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<Vertex> vertices;vector<unsigned int> indices;vector<Texture> textures;unsigned int VAO;/*  函数  */// 构造函数 参数:顶点 索引 纹理Mesh(vector<Vertex> vertices, vector<unsigned int> indices, vector<Texture> 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);}
};

Model.h

#pragma once
#include <glad/glad.h>                 //所有头文件
#include <glm/glm.hpp>
#include <glm/gtc/matrix_transform.hpp>
#define STB_IMAGE_IMPLEMENTATION        //原作者没写
#include <stb_image.h>
#include <assimp/Importer.hpp>        //assimp库头文件
#include <assimp/scene.h>
#include <assimp/postprocess.h>
#include "Mesh.h"
#include "Shader.h"#include <string>
#include <fstream>
#include <sstream>
#include <iostream>
#include <map>
#include <vector>
using namespace std;
//从文件中读取纹理
unsigned int TextureFromFile(const char *path, const string &directory, bool gamma = false);
//Model类
class Model
{public:/*  Model数据 *///存储到目前为止加载的所有纹理,优化以确保纹理不会被加载多次。vector<Texture> textures_loaded;vector<Mesh> meshes;string directory;bool gammaCorrection;/*  函数  */// 构造汉化,需要一个3D模型的文件路径Model(string const &path, bool gamma = false) : gammaCorrection(gamma){loadModel(path);}// 绘制模型,从而绘制所有网格void Draw(Shader shader){for (unsigned int i = 0; i < meshes.size(); i++)meshes[i].Draw(shader);}private:/*  函数   */// 从文件加载支持ASSIMP扩展的模型,并将生成的网格存储在网格矢量中。void loadModel(string const &path){// 通过ASSIMP读文件Assimp::Importer importer;const aiScene* scene = importer.ReadFile(path, aiProcess_Triangulate | aiProcess_FlipUVs | aiProcess_CalcTangentSpace);// 检查错误if (!scene || scene->mFlags & AI_SCENE_FLAGS_INCOMPLETE || !scene->mRootNode) // 如果不是0{cout << "错误::ASSIMP:: " << importer.GetErrorString() << endl;return;}// 检索文件路径的目录路径directory = path.substr(0, path.find_last_of('/'));// 以递归方式处理ASSIMP的根节点processNode(scene->mRootNode, scene);}// 以递归方式处理节点。 处理位于节点处的每个单独网格,并在其子节点(如果有)上重复此过程。void 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 processMesh(aiMesh *mesh, const aiScene *scene){// 要填写的数据vector<Vertex> vertices;vector<unsigned int> indices;vector<Texture> textures;// 遍历每个网格的顶点for (unsigned int i = 0; i < mesh->mNumVertices; i++){Vertex vertex;// 我们声明一个占位符向量,因为assimp使用它自己的向量类,它不直接转换为glm的vec3类,所以我们首先将数据传递给这个占位符glm :: vec3。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;// 顶点最多可包含8个不同的纹理坐标。 因此,我们假设我们不会使用顶点可以具有多个纹理坐标的模型,因此我们总是采用第一个集合(0)。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);// u向量vector.x = mesh->mTangents[i].x;vector.y = mesh->mTangents[i].y;vector.z = mesh->mTangents[i].z;vertex.Tangent = vector;// v向量vector.x = mesh->mBitangents[i].x;vector.y = mesh->mBitangents[i].y;vector.z = mesh->mBitangents[i].z;vertex.Bitangent = vector;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]);}// 加工材料aiMaterial* material = scene->mMaterials[mesh->mMaterialIndex];// 我们假设着色器中的采样器名称约定。 每个漫反射纹理应命名为'texture_diffuseN',其中N是从1到MAX_SAMPLER_NUMBER的序列号。//同样适用于其他纹理,如下列总结:// diffuse: texture_diffuseN// specular: texture_specularN// normal: texture_normalN// 1. 漫反射贴图vector<Texture> diffuseMaps = loadMaterialTextures(material, aiTextureType_DIFFUSE, "texture_diffuse");textures.insert(textures.end(), diffuseMaps.begin(), diffuseMaps.end());// 2. 高光贴图vector<Texture> specularMaps = loadMaterialTextures(material, aiTextureType_SPECULAR, "texture_specular");textures.insert(textures.end(), specularMaps.begin(), specularMaps.end());// 3.法线贴图std::vector<Texture> normalMaps = loadMaterialTextures(material, aiTextureType_HEIGHT, "texture_normal");textures.insert(textures.end(), normalMaps.begin(), normalMaps.end());// 4. 高度贴图std::vector<Texture> heightMaps = loadMaterialTextures(material, aiTextureType_AMBIENT, "texture_height");textures.insert(textures.end(), heightMaps.begin(), heightMaps.end());// 返回从提取的网格数据创建的网格对象return Mesh(vertices, indices, textures);}// 检查给定类型的所有材质纹理,如果尚未加载纹理,则加载纹理。// 所需信息作为Texture结构返回。vector<Texture> loadMaterialTextures(aiMaterial *mat, aiTextureType type, string typeName){vector<Texture> textures;for (unsigned int i = 0; i < mat->GetTextureCount(type); i++){aiString str;mat->GetTexture(type, i, &str);// 检查之前是否加载了纹理,如果是,则继续下一次迭代:跳过加载新纹理bool skip = false;for (unsigned int j = 0; j < textures_loaded.size(); j++){if (std::strcmp(textures_loaded[j].path.data(), 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.C_Str();textures.push_back(texture);textures_loaded.push_back(texture);  //将其存储为整个模型加载的纹理,以确保我们不会加载重复纹理。}}return textures;}
};
//从文件读取纹理函数
unsigned int TextureFromFile(const char *path, const string &directory, bool gamma)
{string filename = string(path);filename = directory + '/' + filename;unsigned int textureID;glGenTextures(1, &textureID);int width, height, nrComponents;unsigned char *data = stbi_load(filename.c_str(), &width, &height, &nrComponents, 0);if (data){GLenum format;if (nrComponents == 1)format = GL_RED;else if (nrComponents == 3)format = GL_RGB;else if (nrComponents == 4)format = GL_RGBA;glBindTexture(GL_TEXTURE_2D, textureID);glTexImage2D(GL_TEXTURE_2D, 0, format, width, height, 0, format, GL_UNSIGNED_BYTE, data);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);stbi_image_free(data);}else{std::cout << "纹理无法从此路径加载: " << path << std::endl;stbi_image_free(data);}return textureID;
}

Shader.h

#ifndef SHADER_H
#define SHADER_H#include <glad/glad.h>
#include <glm/glm.hpp>#include <string>
#include <fstream>
#include <sstream>
#include <iostream>class Shader
{public:unsigned int ID;// constructor generates the shader on the fly// ------------------------------------------------------------------------Shader(const char* vertexPath, const char* fragmentPath, const char* geometryPath = nullptr){// 1. retrieve the vertex/fragment source code from filePathstd::string vertexCode;std::string fragmentCode;std::string geometryCode;std::ifstream vShaderFile;std::ifstream fShaderFile;std::ifstream gShaderFile;// ensure ifstream objects can throw exceptions:vShaderFile.exceptions(std::ifstream::failbit | std::ifstream::badbit);fShaderFile.exceptions(std::ifstream::failbit | std::ifstream::badbit);gShaderFile.exceptions(std::ifstream::failbit | std::ifstream::badbit);try{// open filesvShaderFile.open(vertexPath);fShaderFile.open(fragmentPath);std::stringstream vShaderStream, fShaderStream;// read file's buffer contents into streamsvShaderStream << vShaderFile.rdbuf();fShaderStream << fShaderFile.rdbuf();// close file handlersvShaderFile.close();fShaderFile.close();// convert stream into stringvertexCode = vShaderStream.str();fragmentCode = fShaderStream.str();// if geometry shader path is present, also load a geometry shaderif (geometryPath != nullptr){gShaderFile.open(geometryPath);std::stringstream gShaderStream;gShaderStream << gShaderFile.rdbuf();gShaderFile.close();geometryCode = gShaderStream.str();}}catch (std::ifstream::failure& e){std::cout << "ERROR::SHADER::FILE_NOT_SUCCESFULLY_READ" << std::endl;}const char* vShaderCode = vertexCode.c_str();const char * fShaderCode = fragmentCode.c_str();// 2. compile shadersunsigned int vertex, fragment;// vertex shadervertex = glCreateShader(GL_VERTEX_SHADER);glShaderSource(vertex, 1, &vShaderCode, NULL);glCompileShader(vertex);checkCompileErrors(vertex, "VERTEX");// fragment Shaderfragment = glCreateShader(GL_FRAGMENT_SHADER);glShaderSource(fragment, 1, &fShaderCode, NULL);glCompileShader(fragment);checkCompileErrors(fragment, "FRAGMENT");// if geometry shader is given, compile geometry shaderunsigned int geometry;if (geometryPath != nullptr){const char * gShaderCode = geometryCode.c_str();geometry = glCreateShader(GL_GEOMETRY_SHADER);glShaderSource(geometry, 1, &gShaderCode, NULL);glCompileShader(geometry);checkCompileErrors(geometry, "GEOMETRY");}// shader ProgramID = glCreateProgram();glAttachShader(ID, vertex);glAttachShader(ID, fragment);if (geometryPath != nullptr)glAttachShader(ID, geometry);glLinkProgram(ID);checkCompileErrors(ID, "PROGRAM");// delete the shaders as they're linked into our program now and no longer necesseryglDeleteShader(vertex);glDeleteShader(fragment);if (geometryPath != nullptr)glDeleteShader(geometry);}// activate the shader// ------------------------------------------------------------------------void use(){glUseProgram(ID);}// utility uniform functions// ------------------------------------------------------------------------void setBool(const std::string &name, bool value) const{glUniform1i(glGetUniformLocation(ID, name.c_str()), (int)value);}// ------------------------------------------------------------------------void setInt(const std::string &name, int value) const{glUniform1i(glGetUniformLocation(ID, name.c_str()), value);}// ------------------------------------------------------------------------void setFloat(const std::string &name, float value) const{glUniform1f(glGetUniformLocation(ID, name.c_str()), value);}// ------------------------------------------------------------------------void setVec2(const std::string &name, const glm::vec2 &value) const{glUniform2fv(glGetUniformLocation(ID, name.c_str()), 1, &value[0]);}void setVec2(const std::string &name, float x, float y) const{glUniform2f(glGetUniformLocation(ID, name.c_str()), x, y);}// ------------------------------------------------------------------------void setVec3(const std::string &name, const glm::vec3 &value) const{glUniform3fv(glGetUniformLocation(ID, name.c_str()), 1, &value[0]);}void setVec3(const std::string &name, float x, float y, float z) const{glUniform3f(glGetUniformLocation(ID, name.c_str()), x, y, z);}// ------------------------------------------------------------------------void setVec4(const std::string &name, const glm::vec4 &value) const{glUniform4fv(glGetUniformLocation(ID, name.c_str()), 1, &value[0]);}void setVec4(const std::string &name, float x, float y, float z, float w){glUniform4f(glGetUniformLocation(ID, name.c_str()), x, y, z, w);}// ------------------------------------------------------------------------void setMat2(const std::string &name, const glm::mat2 &mat) const{glUniformMatrix2fv(glGetUniformLocation(ID, name.c_str()), 1, GL_FALSE, &mat[0][0]);}// ------------------------------------------------------------------------void setMat3(const std::string &name, const glm::mat3 &mat) const{glUniformMatrix3fv(glGetUniformLocation(ID, name.c_str()), 1, GL_FALSE, &mat[0][0]);}// ------------------------------------------------------------------------void setMat4(const std::string &name, const glm::mat4 &mat) const{glUniformMatrix4fv(glGetUniformLocation(ID, name.c_str()), 1, GL_FALSE, &mat[0][0]);}private:// utility function for checking shader compilation/linking errors.// ------------------------------------------------------------------------void checkCompileErrors(GLuint shader, std::string type){GLint success;GLchar infoLog[1024];if (type != "PROGRAM"){glGetShaderiv(shader, GL_COMPILE_STATUS, &success);if (!success){glGetShaderInfoLog(shader, 1024, NULL, infoLog);std::cout << "ERROR::SHADER_COMPILATION_ERROR of type: " << type << "\n" << infoLog << "\n -- --------------------------------------------------- -- " << std::endl;}}else{glGetProgramiv(shader, GL_LINK_STATUS, &success);if (!success){glGetProgramInfoLog(shader, 1024, NULL, infoLog);std::cout << "ERROR::PROGRAM_LINKING_ERROR of type: " << type << "\n" << infoLog << "\n -- --------------------------------------------------- -- " << std::endl;}}}
};
#endif

main.cpp


//头文件
#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 "Shader.h"
#include "Camera.h"
#include "Model.h"
#include <iostream>//-----------------------------------函数声明-------------------------------------------
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 pointersif (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress)){std::cout << "Failed to initialize GLAD" << std::endl;return -1;}// 设置全局opengl状态glEnable(GL_DEPTH_TEST);//开启深度测试//创建并编译shaderShader ourShader("../shader/vertexSource.txt", "../shader/fragmentSource.txt");// 加载模型//FileSystem::getPath("resources/objects/nanosuit/nanosuit.obj")//修改为相对路径//Model ourModel("../res/model/nanosuit/nanosuit.obj");//Model ourModel("../res/model/table/table.obj");Model ourModel("../res/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前使用ShaderourShader.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_Movement::FORWARD, deltaTime);if (glfwGetKey(window, GLFW_KEY_S) == GLFW_PRESS)camera.ProcessKeyboard(Camera_Movement::BACKWARD, deltaTime);if (glfwGetKey(window, GLFW_KEY_A) == GLFW_PRESS)camera.ProcessKeyboard(Camera_Movement::LEFT, deltaTime);if (glfwGetKey(window, GLFW_KEY_D) == GLFW_PRESS)camera.ProcessKeyboard(Camera_Movement::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);
}

6. 运行结果:

引擎开发五: Assimp库及使用相关推荐

  1. LayaAir引擎开发HTML5最简单教程(面向JS开发者)

    LayaAir引擎开发HTML5最简单教程(面向JS开发者) 一.总结 一句话总结:开发游戏还是得用游戏引擎来开发,其实很简单啦 切记:开发游戏还是得用游戏引擎来开发,其实很简单,引擎很多东西都帮你做 ...

  2. iOS开发常用三方库、插件、知名博客

    TimLiu-iOS iOS开发常用三方库.插件.知名博客等等,期待大家和我们一起共同维护,同时也期望大家随时能提出宝贵的意见(直接提交Issues即可). 持续更新... 版本:Objective- ...

  3. 用C++实现跨平台游戏引擎开发

    游戏开发系列 用C++实现跨平台游戏引擎开发 你是否梦想写一部格斗游戏但却无从着手呢?是否你只因游戏开发好玩而对之感兴趣?本文我们将分析一个通用的跨平台游戏引擎,每个游戏开发新手都可以自由地使用它. ...

  4. LayaAir引擎开发(基于JS开发)

    入门教程:一篇学会LayaAir引擎开发HTML5(面向JS开发者) charley  · 8 天前 序 HTML5是一种热门的跨平台开发技术,随着引擎技术的发展,如今LayaAir引擎的产品性能已达 ...

  5. 软件开发:界面库详细对比,开发工具的选择指导

    说起界面,那真是百家争鸣.C++里面其他的都好说,像什么XML解析顶多也就十几个著名开源库而已.Office 操作的开源库仅有几个,更可怜的是有个很著名的Office开源库只有JAVA和C#版本,但是 ...

  6. 游戏引擎开发和物理引擎_视频游戏开发的最佳游戏引擎

    游戏引擎开发和物理引擎 In this article, we'll look at some of the most popular game engines for video game deve ...

  7. 【转载】浅析游戏引擎开发

    浅析游戏引擎开发 1 引言 电脑游戏作为一种娱乐方式越来越为人们所接受.即时通讯开发对于电脑游戏来说, 游戏引擎是用于控制游戏功能的 主程序, 如接受玩家控制信息的输入, 选择合适的声音以合适的音量播 ...

  8. 使用模块化工具Rollup打包自己开发的JS库

    使用模块化工具Rollup打包自己开发的JS库 打包JS库demo项目地址:https://github.com/Miazzy/xdata-utils-btools 背景 最近有个需求,需要为小程序写 ...

  9. OpenGL基础26:Assimp库

    一.模型文件 游戏中有很多复杂的模型往往都是美术通过3D建模工具构建出来的,当然不是程序将顶点写死在代码里的,想想看一个简单的人物模型可能就有上千个顶点,这个时候按之前"生成木箱子" ...

  10. Unreal4引擎开发学习日志

    Unreal4引擎开发学习笔记1 相关说明 本游戏引擎学习日志是笔者根据网上相关教学视频所写,是对视频内容的精炼与总结.如果您认为笔者所写不清晰明确,可以访问以下链接: Unreal入门第一季 - 虚 ...

最新文章

  1. 推荐几个小而美的原创公众号!
  2. java异步框架feed,Java:IO流里面的BuffeedReader
  3. django models索引_Django开发者常犯的7种错误
  4. linux 后端存储,配置NFS网络存储作为cinder的后端存储
  5. Win7/Win8.1升级Win10后屏幕一直闪烁怎么办?
  6. WEB前端 基础知识汇总
  7. python知识图谱可视化_知识图谱可视化
  8. 第65课 采访报道 《小学生C++趣味编程》
  9. 网络工程师必知的几款网络故障排除工具
  10. 台式计算机可以连接蓝牙吗,蓝牙耳机怎么连接台式电脑?台式电脑连接蓝牙耳机的方法...
  11. java 不能回滚_在Java 8中回滚()语句执行失败的最佳方法是什么?
  12. pip install pyinstaller安装报错
  13. 你若安好便是晴天nbsp;---------…
  14. android 高德地图 定位蓝点消失,高德地图蓝点定位不显示蓝点
  15. 英语流利说 第13天
  16. 漫步者蓝牙自动断开_情人节礼物 就连宋轶都推荐的高颜值真无线蓝牙耳机
  17. 【Spring Cloud Alibaba 温故而知新】(五)SpringCloud Sleuth + Zipkin:分布式日志追踪
  18. 中国偶氮二异丁腈市场供需动态与投资前景展望报告(新版)2022-2027年
  19. 关于for循环遍历列表的几个用法--python
  20. 高级语言.汇编语言和机器语言

热门文章

  1. linux返回上一步命令
  2. 微信小程序input禁止输入特殊符号
  3. python朋友圈头像_Python之微信-微信好友头像合成
  4. 微信接入之获取用户头像
  5. 数据结构——p = new Lnode和Lnode *p有什么区别
  6. sap系统和服务器的关系,erp系统和sap系统的区别
  7. 虚拟机安装win7时遇到的问题及解决方案
  8. UID_PR_01_基础操作
  9. 计算机系固态硬盘机械硬盘,固态硬盘和机械硬盘可以在一起使用吗?
  10. Java工具类之Base64Utils实现base64码转换为文件流-yellowcong