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

这个模型是来自对GitHub上的LearnOpenGL的一个例子:

准备工作:

  • 用的是OpenGL和glfw,glfw安装配置可见:
    https://blog.csdn.net/derbi123123/article/details/104350624

  • 还有要用到的数学库glm的配置:
    https://blog.csdn.net/derbi123123/article/details/105611148

  • Assimp模型加载库的下载、编译、配置的教学可见:
    https://blog.csdn.net/derbi123123/article/details/105783048

  • 模型的下载地址:
    https://learnopengl-cn.github.io/data/nanosuit.rar

下面给出各部分的源码:

(1)顶点着色器和片段着色器:

当然着色器部分只是对于这个例子而言的,它有自己固定的一套流程,可以让我们随心所欲的改变绘制的效果。

#version 330 core
layout (location = 0) in vec3 aPos;
layout (location = 1) in vec3 aNormal;
layout (location = 2) in vec2 aTexCoords;out vec2 TexCoords;uniform mat4 model;
uniform mat4 view;
uniform mat4 projection;void main()
{TexCoords = aTexCoords;    gl_Position = projection * view * model * vec4(aPos, 1.0);
}
#version 330 core
out vec4 FragColor;in vec2 TexCoords;uniform sampler2D texture_diffuse1;void main()
{    FragColor = texture(texture_diffuse1, TexCoords);
}

(2)头文件mesh.h:

网格(Mesh)代表的是单个的可绘制实体,我们现在先来定义一个我们自己的网格类。
网格:
当使用建模工具对物体建模的时候,艺术家通常不会用单个形状创建出整个模型。通常每个模型都由几个子模型/形状组合而成。组合模型的每个单独的形状就叫做一个网格(Mesh)。比如说有一个人形的角色:艺术家通常会将头部、四肢、衣服、武器建模为分开的组件,并将这些网格组合而成的结果表现为最终的模型。一个网格是我们在OpenGL中绘制物体所需的最小单位(顶点数据、索引和材质属性)。一个模型(通常)会包括多个网格。

#ifndef MESH_H
#define MESH_H#include <glad/glad.h> // holds all OpenGL type declarations#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 {// positionglm::vec3 Position;// normalglm::vec3 Normal;// texCoordsglm::vec2 TexCoords;// tangentglm::vec3 Tangent;// bitangentglm::vec3 Bitangent;
};struct Texture {unsigned int id;string type;string path;
};class Mesh {public:/*  Mesh Data  */vector<Vertex> vertices;vector<unsigned int> indices;vector<Texture> textures;unsigned int VAO;/*  Functions  */// constructorMesh(vector<Vertex> vertices, vector<unsigned int> indices, vector<Texture> textures){this->vertices = vertices;this->indices = indices;this->textures = textures;// now that we have all the required data, set the vertex buffers and its attribute pointers.setupMesh();}// render the meshvoid Draw(Shader shader) {// bind appropriate texturesunsigned 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); // active proper texture unit before binding// retrieve texture number (the N in diffuse_textureN)string number;string name = textures[i].type;if(name == "texture_diffuse")number = std::to_string((long long) diffuseNr++);else if(name == "texture_specular")number = std::to_string((long long)specularNr++); // transfer unsigned int to streamelse if(name == "texture_normal")number = std::to_string((long long)normalNr++); // transfer unsigned int to streamelse if(name == "texture_height")number = std::to_string((long long)heightNr++); // transfer unsigned int to stream// now set the sampler to the correct texture unitglUniform1i(glGetUniformLocation(shader.ID, (name + number).c_str()), i);// and finally bind the textureglBindTexture(GL_TEXTURE_2D, textures[i].id);}// draw meshglBindVertexArray(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);}private:/*  Render data  */unsigned int VBO, EBO;/*  Functions    */// initializes all the buffer objects/arraysvoid setupMesh(){// create buffers/arraysglGenVertexArrays(1, &VAO);glGenBuffers(1, &VBO);glGenBuffers(1, &EBO);glBindVertexArray(VAO);// load data into vertex buffersglBindBuffer(GL_ARRAY_BUFFER, VBO);// A great thing about structs is that their memory layout is sequential for all its items.// The effect is that we can simply pass a pointer to the struct and it translates perfectly to a glm::vec3/2 array which// again translates to 3/2 floats which translates to a byte array.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);// set the vertex attribute pointers// vertex PositionsglEnableVertexAttribArray(0);   glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex), (void*)0);// vertex normalsglEnableVertexAttribArray(1);    glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex), (void*)offsetof(Vertex, Normal));// vertex texture coordsglEnableVertexAttribArray(2);  glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, sizeof(Vertex), (void*)offsetof(Vertex, TexCoords));// vertex tangentglEnableVertexAttribArray(3);glVertexAttribPointer(3, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex), (void*)offsetof(Vertex, Tangent));// vertex bitangentglEnableVertexAttribArray(4);glVertexAttribPointer(4, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex), (void*)offsetof(Vertex, Bitangent));glBindVertexArray(0);}
};
#endif

(3)头文件camera.h:

为了在OpenGL中模拟出摄像机,产生一种我们在移动的感觉,而不是场景在移动。

#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

(4)头文件model.h:

创建另一个类来完整地表示一个模型。

#ifndef MODEL_H
#define MODEL_H
#define STB_IMAGE_IMPLEMENTATION
#include <glad/glad.h> #include <glm/glm.hpp>
#include <glm/gtc/matrix_transform.hpp>
#include "stb_image.h"
#include <assimp/Importer.hpp>
#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);class Model
{public:/*  Model Data */vector<Texture> textures_loaded;  // stores all the textures loaded so far, optimization to make sure textures aren't loaded more than once.vector<Mesh> meshes;string directory;bool gammaCorrection;/*  Functions   */// constructor, expects a filepath to a 3D model.Model(string const &path, bool gamma = false) : gammaCorrection(gamma){loadModel(path);}// draws the model, and thus all its meshesvoid Draw(Shader shader){for(unsigned int i = 0; i < meshes.size(); i++)meshes[i].Draw(shader);}private:/*  Functions   */// loads a model with supported ASSIMP extensions from file and stores the resulting meshes in the meshes vector.void loadModel(string const &path){// read file via ASSIMPAssimp::Importer importer;const aiScene* scene = importer.ReadFile(path, aiProcess_Triangulate | aiProcess_FlipUVs | aiProcess_CalcTangentSpace);// check for errorsif(!scene || scene->mFlags & AI_SCENE_FLAGS_INCOMPLETE || !scene->mRootNode) // if is Not Zero{cout << "ERROR::ASSIMP:: " << importer.GetErrorString() << endl;return;}// retrieve the directory path of the filepathdirectory = path.substr(0, path.find_last_of('/'));// process ASSIMP's root node recursivelyprocessNode(scene->mRootNode, scene);}// processes a node in a recursive fashion. Processes each individual mesh located at the node and repeats this process on its children nodes (if any).void processNode(aiNode *node, const aiScene *scene){// process each mesh located at the current nodefor(unsigned int i = 0; i < node->mNumMeshes; i++){// the node object only contains indices to index the actual objects in the scene. // the scene contains all the data, node is just to keep stuff organized (like relations between nodes).aiMesh* mesh = scene->mMeshes[node->mMeshes[i]];meshes.push_back(processMesh(mesh, scene));}// after we've processed all of the meshes (if any) we then recursively process each of the children nodesfor(unsigned int i = 0; i < node->mNumChildren; i++){processNode(node->mChildren[i], scene);}}Mesh processMesh(aiMesh *mesh, const aiScene *scene){// data to fillvector<Vertex> vertices;vector<unsigned int> indices;vector<Texture> textures;// Walk through each of the mesh's verticesfor(unsigned int i = 0; i < mesh->mNumVertices; i++){Vertex vertex;glm::vec3 vector; // we declare a placeholder vector since assimp uses its own vector class that doesn't directly convert to glm's vec3 class so we transfer the data to this placeholder glm::vec3 first.// positionsvector.x = mesh->mVertices[i].x;vector.y = mesh->mVertices[i].y;vector.z = mesh->mVertices[i].z;vertex.Position = vector;// normalsvector.x = mesh->mNormals[i].x;vector.y = mesh->mNormals[i].y;vector.z = mesh->mNormals[i].z;vertex.Normal = vector;// texture coordinatesif(mesh->mTextureCoords[0]) // does the mesh contain texture coordinates?{glm::vec2 vec;// a vertex can contain up to 8 different texture coordinates. We thus make the assumption that we won't // use models where a vertex can have multiple texture coordinates so we always take the first set (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);// tangentvector.x = mesh->mTangents[i].x;vector.y = mesh->mTangents[i].y;vector.z = mesh->mTangents[i].z;vertex.Tangent = vector;// bitangentvector.x = mesh->mBitangents[i].x;vector.y = mesh->mBitangents[i].y;vector.z = mesh->mBitangents[i].z;vertex.Bitangent = vector;vertices.push_back(vertex);}// now wak through each of the mesh's faces (a face is a mesh its triangle) and retrieve the corresponding vertex indices.for(unsigned int i = 0; i < mesh->mNumFaces; i++){aiFace face = mesh->mFaces[i];// retrieve all indices of the face and store them in the indices vectorfor(unsigned int j = 0; j < face.mNumIndices; j++)indices.push_back(face.mIndices[j]);}// process materialsaiMaterial* material = scene->mMaterials[mesh->mMaterialIndex];    // we assume a convention for sampler names in the shaders. Each diffuse texture should be named// as 'texture_diffuseN' where N is a sequential number ranging from 1 to MAX_SAMPLER_NUMBER. // Same applies to other texture as the following list summarizes:// diffuse: texture_diffuseN// specular: texture_specularN// normal: texture_normalN// 1. diffuse mapsvector<Texture> diffuseMaps = loadMaterialTextures(material, aiTextureType_DIFFUSE, "texture_diffuse");textures.insert(textures.end(), diffuseMaps.begin(), diffuseMaps.end());// 2. specular mapsvector<Texture> specularMaps = loadMaterialTextures(material, aiTextureType_SPECULAR, "texture_specular");textures.insert(textures.end(), specularMaps.begin(), specularMaps.end());// 3. normal mapsstd::vector<Texture> normalMaps = loadMaterialTextures(material, aiTextureType_HEIGHT, "texture_normal");textures.insert(textures.end(), normalMaps.begin(), normalMaps.end());// 4. height mapsstd::vector<Texture> heightMaps = loadMaterialTextures(material, aiTextureType_AMBIENT, "texture_height");textures.insert(textures.end(), heightMaps.begin(), heightMaps.end());// return a mesh object created from the extracted mesh datareturn Mesh(vertices, indices, textures);}// checks all material textures of a given type and loads the textures if they're not loaded yet.// the required info is returned as a Texture struct.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);// check if texture was loaded before and if so, continue to next iteration: skip loading a new texturebool 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; // a texture with the same filepath has already been loaded, continue to next one. (optimization)break;}}if(!skip){   // if texture hasn't been loaded already, load itTexture 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);  // store it as texture loaded for entire model, to ensure we won't unnecesery load duplicate textures.}}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 << "Texture failed to load at path: " << path << std::endl;stbi_image_free(data);}return textureID;
}
#endif

(5)头文件Shader.h:

为了便于简化我们的主程序,我们把shader的配置、链接放到一个类中

#ifndef SHADER_H
#define SHADER_H#include <glad/glad.h>
#include <string>
#include <fstream>
#include <sstream>
#include <iostream>
#include <glm/glm.hpp>
#include <glm/gtc/matrix_transform.hpp>
#include <glm/gtc/type_ptr.hpp>
class Shader
{public:unsigned int ID;// constructor generates the shader on the fly// ------------------------------------------------------------------------Shader(const char* vertexPath, const char* fragmentPath){// 1. retrieve the vertex/fragment source code from filePathstd::string vertexCode;std::string fragmentCode;std::ifstream vShaderFile;std::ifstream fShaderFile;// ensure ifstream objects can throw exceptions:vShaderFile.exceptions (std::ifstream::failbit | std::ifstream::badbit);fShaderFile.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();}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");// shader ProgramID = glCreateProgram();glAttachShader(ID, vertex);glAttachShader(ID, fragment);glLinkProgram(ID);checkCompileErrors(ID, "PROGRAM");// delete the shaders as they're linked into our program now and no longer necessaryglDeleteShader(vertex);glDeleteShader(fragment);}// 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 setMat4(const std::string &name, glm::mat4 value) const{ glUniformMatrix4fv(glGetUniformLocation(ID, name.c_str()), 1, GL_FALSE, glm::value_ptr(value));}void setVec3(const std::string &name,float x,float y,float z) const{ glUniform3f(glGetUniformLocation(ID, name.c_str()),x,y,z);}void setVec3(const std::string &name,glm::vec3 value) const{ glUniform3f(glGetUniformLocation(ID, name.c_str()),value.x,value.y,value.z);}
private:// utility function for checking shader compilation/linking errors.// ------------------------------------------------------------------------void checkCompileErrors(unsigned int shader, std::string type){int success;char 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/*使用举例:
Shader ourShader("path/to/shaders/shader.vs", "path/to/shaders/shader.fs");
...
while(...)
{ourShader.use();ourShader.setFloat("someUniform", 1.0f);DrawStuff();
}*/

(6)头文件stb_image.h:

stb_image.h是Sean Barrett的一个非常流行的单头文件图像加载库,它能够加载大部分流行的文件格式,并且能够很简单得整合到你的工程之中。其下载和配置可见:
https://blog.csdn.net/derbi123123/article/details/105610298

最后就是我们的主程序了:

#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);// settings
const unsigned int SCR_WIDTH = 800;
const unsigned int SCR_HEIGHT = 600;// camera
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;// timing
float deltaTime = 0.0f;
float lastFrame = 0.0f;int main()
{// glfw: initialize and configure// ------------------------------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);
#endif// glfw window creation// --------------------GLFWwindow* window = glfwCreateWindow(SCR_WIDTH, SCR_HEIGHT, "LearnOpenGL", NULL, NULL);if (window == NULL){std::cout << "Failed to create GLFW window" << std::endl;glfwTerminate();return -1;}glfwMakeContextCurrent(window);glfwSetFramebufferSizeCallback(window, framebuffer_size_callback);glfwSetCursorPosCallback(window, mouse_callback);glfwSetScrollCallback(window, scroll_callback);// tell GLFW to capture our mouse//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;}// configure global opengl state// -----------------------------glEnable(GL_DEPTH_TEST);// build and compile shaders// -------------------------Shader ourShader("1.model_loading.vs", "1.model_loading.frag");// load models// -----------Model ourModel("G:/新建文件夹/计算机图形学/LearnOpenGL/导入模型/导入模型/nanosuit.obj");// draw in wireframe//glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);// render loop// -----------while (!glfwWindowShouldClose(window)){// per-frame time logic// --------------------float currentFrame = glfwGetTime();deltaTime = currentFrame - lastFrame;lastFrame = currentFrame;// input// -----processInput(window);// render// ------glClearColor(0.05f, 0.05f, 0.05f, 1.0f);glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);// don't forget to enable shader before setting uniformsourShader.use();// view/projection transformationsglm::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);// render the loaded modelglm::mat4 model = glm::mat4(1.0f);model = glm::translate(model, glm::vec3(0.0f, -1.75f, 0.0f)); // translate it down so it's at the center of the scenemodel = glm::scale(model, glm::vec3(0.2f, 0.2f, 0.2f));    // it's a bit too big for our scene, so scale it downourShader.setMat4("model", model);ourModel.Draw(ourShader);// glfw: swap buffers and poll IO events (keys pressed/released, mouse moved etc.)// -------------------------------------------------------------------------------glfwSwapBuffers(window);glfwPollEvents();}// glfw: terminate, clearing all previously allocated GLFW resources.// ------------------------------------------------------------------glfwTerminate();return 0;
}// process all input: query GLFW whether relevant keys are pressed/released this frame and react accordingly
// ---------------------------------------------------------------------------------------------------------
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(FORWARD, deltaTime);if (glfwGetKey(window, GLFW_KEY_S) == GLFW_PRESS)camera.ProcessKeyboard(BACKWARD, deltaTime);if (glfwGetKey(window, GLFW_KEY_A) == GLFW_PRESS)camera.ProcessKeyboard(LEFT, deltaTime);if (glfwGetKey(window, GLFW_KEY_D) == GLFW_PRESS)camera.ProcessKeyboard(RIGHT, deltaTime);
}// glfw: whenever the window size changed (by OS or user resize) this callback function executes
// ---------------------------------------------------------------------------------------------
void framebuffer_size_callback(GLFWwindow* window, int width, int height)
{// make sure the viewport matches the new window dimensions; note that width and // height will be significantly larger than specified on retina displays.glViewport(0, 0, width, height);
}// glfw: whenever the mouse moves, this callback is called
// -------------------------------------------------------
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; // reversed since y-coordinates go from bottom to toplastX = xpos;lastY = ypos;camera.ProcessMouseMovement(xoffset, yoffset);
}// glfw: whenever the mouse scroll wheel scrolls, this callback is called
// ----------------------------------------------------------------------
void scroll_callback(GLFWwindow* window, double xoffset, double yoffset)
{camera.ProcessMouseScroll(yoffset);
}

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

  1. Compose-jb图片加载库load-the-image,适用于KMM Compose desktop桌面端(Windows,Linux,MacOs)

    前言 Android平台上有很多优秀的图片加载框架,比如Glide,Picasso,Fresco 而Compose-jetpack上的图片加载框架有Coil 但Compose-jb上却暂时没有图片加载 ...

  2. 一通百通之Linux加载库文件

    刚才在网上看到一篇文章讲Linux下库文件的动态加载,代码很简单: linux下动态加载库主要使用以下几个函数: void *dlopen(const char *filename, int flag ...

  3. OpenGL Assimp模型加载库

    OpenGL Assimp库 前言 模型加载库 构建Assimp 前言 到目前为止的所有场景中,我们一直都在滥用我们的箱子朋友,但时间久了甚至是我们最好的朋友也会感到无聊.在日常的图形程序中,通常都会 ...

  4. 模型加载库 Assimp

    大家好,接下来将为大家介绍模型加载库 Assimp . 1.Assimp介绍 Assimp 全称为 Open Asset Import Library,可以支持几十种不同格式的模型文件的解析(同样也可 ...

  5. OpenGL学习脚印:模型加载初步-加载obj模型(load obj model)

    写在前面 前面介绍了光照基础内容,以及材质和lighting maps,和光源类型,我们对使用光照增强场景真实感有了一定了解.但是到目前为止,我们通过在程序中指定的立方体数据,绘制立方体,看起来还是很 ...

  6. keras/tensorflow 模型保存后重新加载准确率为0 model.save and load giving different result

    我在用别人的代码跑程序的时候遇到了这个问题: keras 模型保存后重新加载准确率为0 GitHub上有个issue:model.save and load giving different resu ...

  7. 解析OBJ模型并将其加载到Unity3D场景中

    版权声明:本文由秦元培创作和发表,采用署名(BY)-非商业性使用(NC)-相同方式共享(SA)国际许可协议进行许可,转载请注明作者及出处,本文作者为秦元培,本文标题为解析OBJ模型并将其加载到Unit ...

  8. [Pytorch系列-41]:卷积神经网络 - 模型参数的恢复/加载 - 搭建LeNet-5网络与MNIST数据集手写数字识别

    作者主页(文火冰糖的硅基工坊):文火冰糖(王文兵)的博客_文火冰糖的硅基工坊_CSDN博客 本文网址:https://blog.csdn.net/HiWangWenBing/article/detai ...

  9. FaceBook推出的Android图片加载库-Fresco

    源文件:http://www.mamicode.com/info-detail-544084.html 在Android设备上面,快速高效的显示图片是极为重要的.过去的几年里,我们在如何高效的存储图像 ...

最新文章

  1. 2019.03.18 连接my sql
  2. 设计模式(五)责任链模式
  3. STM32 基础系列教程 13 – ADC DMA
  4. 微型计算机系统性能优化及测试,微型计算机系统测试、优化和升级基础知识习题...
  5. javaandroid知识点汇总整理(不定期更新)
  6. 人人商城小程序用户授权问题
  7. 锐动SDK应用于行车记录仪
  8. 方法引用、构造器引用和数组引用
  9. 软件构架则是软件之上的 飞鸽传书 软件
  10. 什么是spooling技术 他有哪几部分组成_气调保鲜冷库有哪几部分组成?
  11. 用lua实现ByteArray和ByteArrayVarint
  12. Android Textview实现文字颜色渐变效果
  13. java队列 notify_java使用线程做一个消息队列,wait,notify
  14. js计算字典的个数_第11天:Python 字典
  15. (1)CC3200学习之blinky导入后报编译器版本错误
  16. 删除参考文献后的空行
  17. 记一次网站漏洞修复经历
  18. 使用CCleaner删除系统还原点
  19. Java SE菜鸟之异常
  20. 《乱音盒子》之《闪耀的星》

热门文章

  1. MPV播放器 快捷键
  2. 过拟合现象,原因,以及降低过拟合的方法
  3. 库 01_EOS 普元 EOS Platform 7.6 开发版安装时没装coframe,后续再部署coframe源码的操作步骤
  4. MySql 别犯糊涂了! LEFT JOIN 的 ON 后接上筛选条件,多个条件会出事!
  5. ASP.NET在线学习系统
  6. C++基础(17)——继承
  7. 设计模式循序渐进(9)解释器模式 Interpreter
  8. 使用CoreLocation定位
  9. 改善编程体验: IdeaVimExtension介绍
  10. 瑞吉外卖第一篇(1):搭建环境之创建数据库