最近在使用opengl做学校的大作业,在读取模型时遇到了无法显示材质的问题,在通过研究obj与mtl文件格式时发现了原因。

由于之前一直是使用别人做好的读取类,对于有贴图的模型可以正常处理,但是这次的模型没有贴图,材质都是用属性直接指定的,如

其中,Ka代表环境光,Kd代表漫反射光,Ks代表镜面高光。

为了读取这些数据,在已有的代码中进行了修改(代码在最下)。assimp中其实提供了方法读取这些数据,我们只要保存在mesh信息中,并传入着色器即可使用。

     aiMaterial* material = scene->mMaterials[mesh->mMaterialIndex];Material mat;aiColor3D color;//读取mtl文件顶点数据material->Get(AI_MATKEY_COLOR_AMBIENT, color);mat.Ka = glm::vec4(color.r, color.g, color.b,1.0);

其中AI_MATKEY_COLOR_AMBIENT即对应了环境光的信息。

在将数据传入着色器时,使用了ubo对象,因为vbo中已经保存了模型的顶点和法线等信息,所以单独处理材质。

代码如下:

mesh.h

#pragma once
#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_m.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 Material {//材质颜色光照glm::vec4 Ka;//漫反射glm::vec4 Kd;//镜反射glm::vec4 Ks;
};struct Texture {unsigned int id;string type;string path;
};class Mesh {
public:/*  Mesh Data  */vector<Vertex> vertices;vector<unsigned int> indices;vector<Texture> textures;Material mats;unsigned int VAO;unsigned int uniformBlockIndex;/*  Functions  */// constructorMesh(vector<Vertex> vertices, vector<unsigned int> indices, vector<Texture> textures, Material mat){this->vertices = vertices;this->indices = indices;this->textures = textures;this->mats = mat;// 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(diffuseNr++);else if (name == "texture_specular")number = std::to_string(specularNr++); // transfer unsigned int to streamelse if (name == "texture_normal")number = std::to_string(normalNr++); // transfer unsigned int to streamelse if (name == "texture_height")number = std::to_string(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);glBindBufferRange(GL_UNIFORM_BUFFER,0, uniformBlockIndex,0,sizeof(Material));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);glGenBuffers(1, &uniformBlockIndex);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)+ sizeof(mats), &vertices[0], GL_STATIC_DRAW);glBindBuffer(GL_UNIFORM_BUFFER, uniformBlockIndex);glBufferData(GL_UNIFORM_BUFFER,sizeof(mats),(void*)(&mats), 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));}
};
#endif

model.h

#pragma once
#ifndef MODEL_H
#define MODEL_H#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>
#include <assimp/scene.h>
#include <assimp/postprocess.h>#include "mesh.h"
#include "shader_m.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];Material mat;aiColor3D color;//读取mtl文件顶点数据material->Get(AI_MATKEY_COLOR_AMBIENT, color);mat.Ka = glm::vec4(color.r, color.g, color.b,1.0);material->Get(AI_MATKEY_COLOR_DIFFUSE, color);mat.Kd = glm::vec4(color.r, color.g, color.b,1.0);material->Get(AI_MATKEY_COLOR_SPECULAR, color);mat.Ks = glm::vec4(color.r, color.g, color.b,1.0);// 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, mat);}// 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

顶点着色器:

#version 330 core
layout (location = 0) in vec3 aPos;
layout (location = 1) in vec3 aNormal;uniform Mat{vec4 aAmbient;vec4 aDiffuse;vec4 aSpecular;
};
out vec3 FragPos;
out vec3 Normal;out vec4 Ambient;
out vec4 Diffuse;
out vec4 Specular;uniform mat4 model;
uniform mat4 view;
uniform mat4 projection;void main()
{FragPos = vec3( model * vec4(aPos, 1.0));//Normal =vec3(projection * vec4(mat3(transpose(inverse(view * model))) * aNormal,0.0));  Normal = mat3(transpose(inverse(model))) * aNormal;  Ambient = aAmbient;Diffuse = aDiffuse;Specular = aSpecular;gl_Position = projection * view * vec4(FragPos, 1.0);
}

片段着色器:

#version 330 core
out vec4 FragColor;struct Light {vec3 position;  vec3 ambient;vec3 diffuse;vec3 specular;float constant;float linear;float quadratic;
};in vec3 FragPos;
in vec3 Normal;
in vec2 TexCoords;
//从Mtl中读取的数据
//Material
in vec4 Ambient;
in vec4 Diffuse;
in vec4 Specular;uniform vec3 viewPos;
uniform Light light;uniform float shininess;void main()
{    // ambientvec3 ambient = light.ambient * Diffuse.rgb;// diffuse vec3 norm = normalize(Normal);vec3 lightDir = normalize(light.position - FragPos);float diff = max(dot(norm, lightDir), 0.0);vec3 diffuse =light.diffuse * diff *Diffuse.rgb;  // attenuationfloat distance    = length(light.position - FragPos);float attenuation = 1.0 / (light.constant + light.linear * distance + light.quadratic * (distance * distance));    // specularvec3 viewDir = normalize(viewPos - FragPos);vec3 reflectDir = reflect(-lightDir, norm);  float spec = pow(max(dot(viewDir, reflectDir), 0.0), shininess);vec3 specular = light.specular * spec *   Specular.rgb;  //ambient  *= attenuation;  diffuse   *= attenuation; specular *= attenuation; vec3 result = ambient + diffuse +specular;FragColor = vec4(result ,1.0);
}

使用Assimp库读取mtl文件数据-光照模型Shader相关推荐

  1. 使用Assimp库读取mtl文件数据

    最近在使用opengl做学校的大作业,在读取模型时遇到了无法显示材质的问题,在通过研究obj与mtl文件格式时发现了原因. 由于之前一直是使用别人做好的读取类,对于有贴图的模型可以正常处理,但是这次的 ...

  2. Assimp库调用mtl加载obj模型

    网上查阅了很多资料,通过测试都未通过,后来在两位大神博客的帮助下最终完成了obj及mtl的加载. 参考博客链接: OpenGL学习: uniform blocks(UBO)在着色器中的使用_arag2 ...

  3. QT Creator使用matlab库文件读取.mat文件数据

    QT Creator使用matlab库文件读取.mat文件数据 一.环境配置 二.关于编程介绍 三.关于使用函数的介绍 1:关于假设数据类型介绍 2:关于使用函数介绍 一.环境配置 第一步先点开我的电 ...

  4. python读取csv某一列存入数组_python 读取.csv文件数据到数组(矩阵)的实例讲解

    利用numpy库 (缺点:有缺失值就无法读取) 读: import numpy my_matrix = numpy.loadtxt(open("1.csv","rb&qu ...

  5. python读取nc文件转成img_使用python的netCDF4库读取.nc文件 和 创建.nc文件[转]

    使用python netCDF4库读取.nc文件 和 创建.nc文件 1. 介绍 .nc(network Common Data Format)文件是气象上常用的数据格式,python上读取.nc使用 ...

  6. pandas读取csv写入mysql_使用python的pandas库读取csv文件保存至mysql数据库

    第一:pandas.read_csv读取本地csv文件为数据框形式 data=pd.read_csv('G:\data_operation\python_book\chapter5\\sales.cs ...

  7. 读取EXCEL文件数据,再调用第三方接口,将第三方数据重新写入到EXCEL文件

    读取EXCEL文件数据,再调用第三方接口,将第三方数据重新写入到EXCEL文件 工作中涉及很多提供文档数据,少则几条,多则上万,少的可以自己编辑一个,静态final来自己定义,一旦数太多得话,就得使用 ...

  8. Python用pydicom库读取dicom文件

    Python用pydicom库读取dicom文件并调用 方法1:通过group.element读取 方法2:通过tag读取 方法3:get()方法 通用 import pydicompath = 'x ...

  9. VC6.0读取Excel文件数据

    VC6.0读取Excel文件数据 文件存储在Excel文件中,因此第一步是能够在程序中方便地读取表格数据,这里用的是VC6.0 MFC.文章内容仅供参考,程序不完整. 完整的VC6.0相关程序,需要的 ...

最新文章

  1. 如何实现windows和linux之间的文件传输
  2. 数据结构:二分查找算法
  3. Android源码分析--MediaServer源码分析(一)
  4. matlab错误原因,matlab常见错误分析
  5. Go + Excel 学习 Excelize rows.go
  6. loj10200. 「一本通 6.2 练习 3」Goldbach's Conjecture
  7. jsonschema php 例子,[宜配屋]听图阁
  8. Python数据分析之全球人口数据
  9. git 撤销全部的commit_git如何撤销commit的方法(未push)
  10. Windows server资源分享
  11. 加速Android Studio/Gradle构建
  12. 总体设计(五个基本原理、软件结构图)
  13. 学生版计算机隐藏游戏,玩了近15年的QQ,才发现这3个隐藏功能,学生党看完炸锅了!...
  14. 某图书管理系统的类图
  15. 【洛谷】P1567 统计天数
  16. 惠普HP Deskjet F378 多功能一体机驱动
  17. js xlsx获取表头
  18. RAID各个级别的优缺点
  19. 两家新三板CRM公司公布业绩 中国SaaS市场能否出个Salesforce?
  20. 定制智慧交通(二)--抓取封包软件charles安装配置以及激活

热门文章

  1. 什么是BS 架构(一)
  2. LumaQQ2006的安装
  3. 学校标准化计算机室的设备配置,幸福小学创建标准化学校汇报材料
  4. 计算机系统基础崔丽群答案,2017届部分优秀教师风采展示——崔丽群
  5. 共模电感的原理、作用和使用示例
  6. oracle 大表删除数据后,回收空间的问题。
  7. 主轴弹簧、OTT拉簧、双螺旋弹簧、ROEHRS
  8. 吾征:通过认知智能技术把中医的“望闻问切”搬上互联网 | 百万人学AI评选
  9. 多用户博客BLOG系统大全
  10. AAAI 2021-TextGAIL:Generative Adversarial Imitation Learning for Text Generation