您当前的位置: 首页 >  3d

鱼儿-1226

暂无认证

  • 2浏览

    0关注

    1100博文

    0收益

  • 0浏览

    0点赞

    0打赏

    0留言

私信
关注
热门博文

OpenGL Shader 加载3DMax模型

鱼儿-1226 发布时间:2020-07-21 11:02:30 ,浏览量:2

OpenGL Shader 加载3DMax模型

原创yulinxx 最后发布于2016-12-20 14:50:12 阅读数 2919  收藏

展开

 

 

 

 

 

 

 

 

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

 

当导入一个模型文件时,即Assimp加载一整个包含所有模型和场景数据的模型文件到一个scene对象时,Assimp会为这个模型文件中的所有场景节点、模型节点都生成一个具有对应关系的数据结构,且将这些场景中的各种元素与模型数据对应起来。

 

http://learnopengl-cn.readthedocs.io/zh/latest/03%20Model%20Loading/01%20Assimp/

 

 

Assimp:

 

下载,解压, 然后用CMake加载,指定输出路径后

Configure,再Generate,

然后用VS2015打开 H:\assimp-3.3.1\bin\Assimp.sln 编译

在 H:\assimp-3.3.1\bin\code\Debug 中,即可生成 

assimp-vc140-mt.lib assimp-vc140-mt.dll

然后在项目中即可使用

 

VS2015 可用:

 

包含: lib , dll 以及头文件, 可以直接使用. .VS2015....assimp-3.3.1

 

http://download.csdn.net/detail/yulinxx/9714936

 

 

 

代码:

 

main.cpp

 

 

// Std. Includes

#include

 

// GLEW

#define GLEW_STATIC

#include

 

// GLFW

#include

 

// GL includes

#include "Shader.h"

#include "Camera.h"

#include "Model.h"

 

// GLM Mathemtics

#include

#include

#include

 

// Other Libs

#include

 

 

#pragma comment(lib, "./SOIL.lib")

 

#pragma comment (lib, "opengl32.lib")

#pragma comment (lib, "glew32s.lib")

#pragma comment (lib, "glfw3.lib") 

#pragma comment (lib, "glfw3dll.lib") 

#pragma comment (lib, "glew32mxs.lib")

#pragma comment (lib, "assimp.lib")

 

 

// Properties

GLuint screenWidth = 800, screenHeight = 600;

 

// Function prototypes

void key_callback(GLFWwindow* window, int key, int scancode, int action, int mode);

void scroll_callback(GLFWwindow* window, double xoffset, double yoffset);

void mouse_callback(GLFWwindow* window, double xpos, double ypos);

void Do_Movement();

 

// Camera

Camera camera(glm::vec3(0.0f, 0.0f, 3.0f));

bool keys[1024];

GLfloat lastX = 400, lastY = 300;

bool firstMouse = true;

 

GLfloat deltaTime = 0.0f;

GLfloat lastFrame = 0.0f;

 

// The MAIN function, from here we start our application and run the Game loop

int main()

{

// Init GLFW

glfwInit();

glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);

glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);

glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);

glfwWindowHint(GLFW_RESIZABLE, GL_FALSE);

 

GLFWwindow* window = glfwCreateWindow(screenWidth, screenHeight, "LearnOpenGL", nullptr, nullptr); // Windowed

glfwMakeContextCurrent(window);

 

// Set the required callback functions

glfwSetKeyCallback(window, key_callback);

glfwSetCursorPosCallback(window, mouse_callback);

glfwSetScrollCallback(window, scroll_callback);

 

// Options

//glfwSetInputMode(window, GLFW_CURSOR, GLFW_CURSOR_DISABLED);

 

// Initialize GLEW to setup the OpenGL Function pointers

glewExperimental = GL_TRUE;

glewInit();

 

// Define the viewport dimensions

glViewport(0, 0, screenWidth, screenHeight);

 

// Setup some OpenGL options

glEnable(GL_DEPTH_TEST);

 

// Setup and compile our shaders

Shader shader("./Shader/obj_vertex", "./Shader/obj_fragement");

Shader lampShader("./Shader/lamp_vertex", "./Shader/lamp_fragement");

 

// Load models

Model ourModel("./nanosuit.obj");

// Used a lamp object here. Find one yourself on the internet, or create your own one ;) (or be oldschool and set the VBO and VAO yourselves)

//Model lightBulb("./Bulb.obj");

Model lightBulb("./nanosuit.obj");

 

// Draw in wireframe

//glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);

 

// Point light positions

glm::vec3 pointLightPositions[] = {

glm::vec3(2.3f, -1.6f, -3.0f),

glm::vec3(-1.7f, 0.9f, 1.0f)

};

 

// Game loop

while (!glfwWindowShouldClose(window))

{

// Set frame time

GLfloat currentFrame = glfwGetTime();

deltaTime = currentFrame - lastFrame;

lastFrame = currentFrame;

 

// Check and call events

glfwPollEvents();

Do_Movement();

 

// Clear the colorbuffer

glClearColor(0.1f, 0.1f, 0.1f, 1.0f);

glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

 

shader.useShaderPrograme();   //

vertices = vertices;

this->indices = indices;

this->textures = textures;

 

// Now that we have all the required data, set the vertex buffers and its attribute pointers.

this->setupMesh();

}

 

// Render the mesh

void Draw(Shader shader)

{

// Bind appropriate textures

GLuint diffuseNr = 1;

GLuint specularNr = 1;

for (GLuint i = 0; i < this->textures.size(); i++)

{

glActiveTexture(GL_TEXTURE0 + i); // Active proper texture unit before binding

  // Retrieve texture number (the N in diffuse_textureN)

stringstream ss;

string number;

string name = this->textures[i].type;

if (name == "texture_diffuse")

ss VAO);

glDrawElements(GL_TRIANGLES, this->indices.size(), GL_UNSIGNED_INT, 0);

glBindVertexArray(0);

 

// Always good practice to set everything back to defaults once configured.

for (GLuint i = 0; i < this->textures.size(); i++)

{

glActiveTexture(GL_TEXTURE0 + i);

glBindTexture(GL_TEXTURE_2D, 0);

}

}

 

private:

/*  Render data  */

GLuint VAO, VBO, EBO;

 

/*  Functions    */

// Initializes all the buffer objects/arrays

void setupMesh()

{

// Create buffers/arrays

glGenVertexArrays(1, &this->VAO);

glGenBuffers(1, &this->VBO);

glGenBuffers(1, &this->EBO);

 

glBindVertexArray(this->VAO);

// Load data into vertex buffers

glBindBuffer(GL_ARRAY_BUFFER, this->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, this->vertices.size() * sizeof(Vertex), &this->vertices[0], GL_STATIC_DRAW);

 

glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, this->EBO);

glBufferData(GL_ELEMENT_ARRAY_BUFFER, this->indices.size() * sizeof(GLuint), &this->indices[0], GL_STATIC_DRAW);

 

// Set the vertex attribute pointers

// Vertex Positions

glEnableVertexAttribArray(0);

glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex), (GLvoid*)0);

// Vertex Normals

glEnableVertexAttribArray(1);

glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex), (GLvoid*)offsetof(Vertex, Normal));

// Vertex Texture Coords

glEnableVertexAttribArray(2);

glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, sizeof(Vertex), (GLvoid*)offsetof(Vertex, TexCoords));

 

glBindVertexArray(0);

}

};

 

 

 

 

 

 

model.h

 

 

 

 

#pragma once

// Std. Includes

#include

#include

#include

#include

#include

#include

using namespace std;

// GL Includes

#include // Contains all the necessery OpenGL includes

#include

#include

#include

#include

#include

#include

 

#include "Mesh.h"

 

GLint TextureFromFile(const char* path, string directory);

 

class Model

{

public:

/*  Functions   */

// Constructor, expects a filepath to a 3D model.

Model(GLchar* path)

{

this->loadModel(path);

}

 

// Draws the model, and thus all its meshes

void Draw(Shader shader)

{

for (GLuint i = 0; i < this->meshes.size(); i++)

this->meshes[i].Draw(shader);

}

 

private:

/*  Model Data  */

vector meshes;

string directory;

vector textures_loaded; // Stores all the textures loaded so far, optimization to make sure textures aren't loaded more than once.

 

/*  Functions   */

// Loads a model with supported ASSIMP extensions from file and stores the resulting meshes in the meshes vector.

void loadModel(string path)

{

// Read file via ASSIMP

Assimp::Importer importer;

const aiScene* scene = importer.ReadFile(path, aiProcess_Triangulate | aiProcess_FlipUVs);

// Check for errors

if (!scene || scene->mFlags == AI_SCENE_FLAGS_INCOMPLETE || !scene->mRootNode) // if is Not Zero

{

cout 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 node

for (GLuint 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]];

this->meshes.push_back(this->processMesh(mesh, scene));

}

// After we've processed all of the meshes (if any) we then recursively process each of the children nodes

for (GLuint i = 0; i < node->mNumChildren; i++)

{

this->processNode(node->mChildren[i], scene);

}

 

}

 

Mesh processMesh(aiMesh* mesh, const aiScene* scene)

{

// Data to fill

vector vertices;

vector indices;

vector textures;

 

// Walk through each of the mesh's vertices

for (GLuint 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.

  // Positions

vector.x = mesh->mVertices[i].x;

vector.y = mesh->mVertices[i].y;

vector.z = mesh->mVertices[i].z;

vertex.Position = vector;

// Normals

vector.x = mesh->mNormals[i].x;

vector.y = mesh->mNormals[i].y;

vector.z = mesh->mNormals[i].z;

vertex.Normal = vector;

// Texture Coordinates

if (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;

}

else

vertex.TexCoords = glm::vec2(0.0f, 0.0f);

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 (GLuint i = 0; i < mesh->mNumFaces; i++)

{

aiFace face = mesh->mFaces[i];

// Retrieve all indices of the face and store them in the indices vector

for (GLuint j = 0; j < face.mNumIndices; j++)

indices.push_back(face.mIndices[j]);

}

// Process materials

if (mesh->mMaterialIndex >= 0)

{

aiMaterial* 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 maps

vector diffuseMaps = this->loadMaterialTextures(material, aiTextureType_DIFFUSE, "texture_diffuse");

textures.insert(textures.end(), diffuseMaps.begin(), diffuseMaps.end());

// 2. Specular maps

vector specularMaps = this->loadMaterialTextures(material, aiTextureType_SPECULAR, "texture_specular");

textures.insert(textures.end(), specularMaps.begin(), specularMaps.end());

}

 

// Return a mesh object created from the extracted mesh data

return 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 loadMaterialTextures(aiMaterial* mat, aiTextureType type, string typeName)

{

vector textures;

for (GLuint 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 texture

GLboolean skip = false;

for (GLuint j = 0; j < textures_loaded.size(); j++)

{

if (textures_loaded[j].path == str)

{

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 it

Texture texture;

texture.id = TextureFromFile(str.C_Str(), this->directory);

texture.type = typeName;

texture.path = str;

textures.push_back(texture);

this->textures_loaded.push_back(texture);  // Store it as texture loaded for entire model, to ensure we won't unnecesery load duplicate textures.

}

}

return textures;

}

};

 

 

 

 

GLint TextureFromFile(const char* path, string directory)

{

//Generate texture ID and load texture data 

string filename = string(path);

filename = directory + '/' + filename;

GLuint textureID;

glGenTextures(1, &textureID);

int width, height;

unsigned char* image = SOIL_load_image(filename.c_str(), &width, &height, 0, SOIL_LOAD_RGB);

// Assign texture to ID

glBindTexture(GL_TEXTURE_2D, textureID);

glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, image);

glGenerateMipmap(GL_TEXTURE_2D);

 

// Parameters

glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);

glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);

glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);

glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);

glBindTexture(GL_TEXTURE_2D, 0);

SOIL_free_image_data(image);

return textureID;

}

 

 

 

 

 

 

 

 

camera.h

 

 

//Camera.h 

 

#pragma once

 

// Std. Includes

#include

 

// GL Includes

#include

#include

#include

 

 

// 摄像机移动方向  程序中用WSAD控制

enum Camera_Movement {

FORWARD,

BACKWARD,

LEFT,

RIGHT

};

 

// Default camera values

const GLfloat YAW = -90.0f;

const GLfloat PITCH = 0.0f;

const GLfloat SPEED = 3.0f;

const GLfloat SENSITIVTY = 0.25f;

const GLfloat ZOOM = 45.0f;

 

 

class Camera

{

public:

// Camera Attributes

glm::vec3 Position;

glm::vec3 Front;

glm::vec3 Up;

glm::vec3 Right;

glm::vec3 WorldUp;

// Eular Angles

GLfloat Yaw;

GLfloat Pitch;

// Camera options

GLfloat MovementSpeed;

GLfloat MouseSensitivity;

GLfloat Zoom;

 

// Constructor with vectors

Camera(glm::vec3 position = glm::vec3(0.0f, 0.0f, 0.0f), 

glm::vec3 up = glm::vec3(0.0f, 1.0f, 0.0f), GLfloat yaw = YAW, 

GLfloat pitch = PITCH) : Front(glm::vec3(0.0f, 0.0f, -1.0f)), 

MovementSpeed(SPEED), MouseSensitivity(SENSITIVTY), Zoom(ZOOM)

{

this->Position = position;

this->WorldUp = up;

this->Yaw = yaw;

this->Pitch = pitch;

this->updateCameraVectors();

}

// Constructor with scalar values

Camera(GLfloat posX, GLfloat posY, GLfloat posZ, GLfloat upX, GLfloat upY, 

GLfloat upZ, GLfloat yaw, GLfloat pitch) : Front(glm::vec3(0.0f, 0.0f, -1.0f)), 

MovementSpeed(SPEED), MouseSensitivity(SENSITIVTY), Zoom(ZOOM)

{

this->Position = glm::vec3(posX, posY, posZ);

this->WorldUp = glm::vec3(upX, upY, upZ);

this->Yaw = yaw;

this->Pitch = pitch;

this->updateCameraVectors();

}

 

// Returns the view matrix calculated using Eular Angles and the LookAt Matrix

glm::mat4 GetViewMatrix()

{

return glm::lookAt(this->Position, this->Position + this->Front, this->Up);

}

 

// 按键处理

void ProcessKeyboard(Camera_Movement direction, GLfloat deltaTime)

{

GLfloat velocity = this->MovementSpeed * deltaTime;

if (direction == FORWARD)

this->Position += this->Front * velocity;

if (direction == BACKWARD)

this->Position -= this->Front * velocity;

if (direction == LEFT)

this->Position -= this->Right * velocity;

if (direction == RIGHT)

this->Position += this->Right * velocity;

}

 

// 鼠标移动处理

void ProcessMouseMovement(GLfloat xoffset, GLfloat yoffset, 

GLboolean constrainPitch = true)

{

xoffset *= this->MouseSensitivity;

yoffset *= this->MouseSensitivity;

 

this->Yaw += xoffset;

this->Pitch += yoffset;

 

// Make sure that when pitch is out of bounds, screen doesn't get flipped

if (constrainPitch)

{

if (this->Pitch > 89.0f)

this->Pitch = 89.0f;

if (this->Pitch < -89.0f)

this->Pitch = -89.0f;

}

 

// Update Front, Right and Up Vectors using the updated Eular angles

this->updateCameraVectors();

}

 

// Processes input received from a mouse scroll-wheel event. 

// Only requires input on the vertical wheel-axis

void ProcessMouseScroll(GLfloat yoffset)

{

if (this->Zoom >= 1.0f && this->Zoom Zoom -= yoffset;

if (this->Zoom Zoom = 1.0f;

if (this->Zoom >= 45.0f)

this->Zoom = 45.0f;

}

 

private:

// Calculates the front vector from the Camera's (updated) Eular Angles

void updateCameraVectors()

{

// Calculate the new Front vector

glm::vec3 front;

front.x = cos(glm::radians(this->Yaw)) * cos(glm::radians(this->Pitch));

front.y = sin(glm::radians(this->Pitch));

front.z = sin(glm::radians(this->Yaw)) * cos(glm::radians(this->Pitch));

this->Front = glm::normalize(front);

// Also re-calculate the Right and Up vector

// Normalize the vectors, because their length gets closer to 0 the more 

// you look up or down which results in slower movement.

this->Right = glm::normalize(glm::cross(this->Front, this->WorldUp));  

this->Up = glm::normalize(glm::cross(this->Right, this->Front));

}

};

 

 

 

 

shader.h

 

 

 

 

//Shader.h 

#pragma once

 

#ifndef TEXTURE_SHADER_H_

#define TEXTURE_SHADER_H_

 

#include

#include

#include

#include

 

#include

 

#include

#include

#include

#include

 

#include

 

class Shader

{

public:

Shader(const GLchar* vertexPath, const GLchar* fragmentPath);

~Shader();

 

public:

void useShaderPrograme();

 

GLuint getPrograme() {

return this->m_nProgram;

}

 

private:

GLuint  m_nProgram;

};

 

Shader::Shader(const GLchar* vertexPath, const GLchar* fragmentPath)

{

std::string vertexCode;

std::string fragmentCode;

std::ifstream vertexShaderF;

std::ifstream fragementShaderF;

 

vertexShaderF.exceptions(std::ifstream::badbit);

fragementShaderF.exceptions(std::ifstream::badbit);

 

try

{

vertexShaderF.open(vertexPath); // 打开文件

fragementShaderF.open(fragmentPath);

 

std::stringstream vertexShaderStream, fragementShaderStream;

vertexShaderStream

关注
打赏
1604459285
查看更多评论
立即登录/注册

微信扫码登录

0.0393s