Materiales e Iluminación
Los materiales son usados para definir el aspecto de los objetos en escena 3D, estos describen las propiedades físicas de los objetos con respecto a cómo los mismos reflejan la luz que incide sobre ellos, anteriormente habíamos definido las propiedades de la fuente de luz por lo que toda la escena es iluminada del mismo modo, definiendo por separado las propiedades de cada objeto logramos que cada una de ellos responda de manera diferente a la iluminación.
Para el uso de materiales definimos 5 componentes: ambiental, difusa, especular, usamos estas componentes para definir la fuente de luz como lo vimos en el tutorial: modelo de iluminación phong, ahora nos sirven también para definir como los objetos reflejan dichas componentes, indicamos también el coeficiente de brillo (shininess) y el coeficiente de emisión, este último define el color de luz que emite el objeto.
GLSL nos permite utilizar estructuras para organizar los datos, crearemos dos estructuras una para los componentes de los materiales y otra para los componentes de la fuente de luz, las estructuras en GLSL funcionas de manera similar que en el lenguaje C.
Antes de dibujar debemos establecer cada una de las componentes del material que componen el objeto que deseamos dibujar, utilizaremos assimp para obtener cada una de las componentes del material que se aplica a cada mesh.
A la hora de dibujar establecemos cada uno de los valores de los componentes del material obtenidos previamente, para obtener la ubicación de un uniform tipo struct en los shaders accedemos a ello mediante el nombre, punto “.” seguido del nombre de la variable que deseamos establecer, ejm:
El modelo de iluminación Blinn-Phong es una extensión del modelo Phong, se implementa prácticamente de la misma manera solo que remplazamos el vector reflejado R por el vector H que es un vector intermedio entre la dirección de la luz y el vector de la vista o cámara.

N (vector normal), V (vector de la vista), L (vector de la luz).
Ahora podemos cargar un modelo soportado por assimp que incluya materiales, utilizaremos una fuente de luz direccional, al ejecutar la aplicación veremos algo similar a esto:
En el tutorial: cargar modelos con assimp aprendimos a cargar modelos almacenados en diversos formatos, se han creado las clases Model y Mesh para encapsular las funcionalidades de la biblioteca assimp, ambas clases se encuentran en el archivo OpenGLModel.hpp.
La clase Mesh nos servirá para almacenar cada una de las partes que componen el modelo, la clase Model es el contenedor principal que maneja cada uno de los meshes que conforman en modelo o escena, aplicando lo que aprendimos en el tutorial anterior nuestras dos clases se ven de este modo:
Para inicializar o cargar el modelo usaremos el método
GitHub: Materiales en OpenGL con Assimp
Para el uso de materiales definimos 5 componentes: ambiental, difusa, especular, usamos estas componentes para definir la fuente de luz como lo vimos en el tutorial: modelo de iluminación phong, ahora nos sirven también para definir como los objetos reflejan dichas componentes, indicamos también el coeficiente de brillo (shininess) y el coeficiente de emisión, este último define el color de luz que emite el objeto.
GLSL nos permite utilizar estructuras para organizar los datos, crearemos dos estructuras una para los componentes de los materiales y otra para los componentes de la fuente de luz, las estructuras en GLSL funcionas de manera similar que en el lenguaje C.
struct Light { vec3 direction; vec3 ambient; vec3 diffuse; vec3 specular; }; struct Material { vec3 ambient; vec3 diffuse; vec3 emissive; vec3 specular; float shininess; float shininess_strength; }; uniform Light light; uniform Material material;
Antes de dibujar debemos establecer cada una de las componentes del material que componen el objeto que deseamos dibujar, utilizaremos assimp para obtener cada una de las componentes del material que se aplica a cada mesh.
if (mesh->mMaterialIndex >= 0) { // obtener el material correspondiente a este Mesh const aiMaterial* material = model->scene->mMaterials[mesh->mMaterialIndex]; if (material->Get(AI_MATKEY_SHININESS, shininess) != AI_SUCCESS) shininess = 128.0; if (material->Get(AI_MATKEY_SHININESS_STRENGTH, shininess_strength) != AI_SUCCESS) shininess_strength = 1.0; aiColor4D diffuse, ambient, specular, emisive; if (aiGetMaterialColor(material, AI_MATKEY_COLOR_DIFFUSE, &diffuse) == AI_SUCCESS) { aiColorToFloat(diffuse, color_diffuse); } if (aiGetMaterialColor(material, AI_MATKEY_COLOR_SPECULAR, &specular) == AI_SUCCESS) { aiColorToFloat(specular, color_specular); } if (aiGetMaterialColor(material, AI_MATKEY_COLOR_AMBIENT, &ambient) == AI_SUCCESS) { aiColorToFloat(ambient, color_ambient); } if (aiGetMaterialColor(material, AI_MATKEY_COLOR_EMISSIVE, &emisive) == AI_SUCCESS) { aiColorToFloat(emisive, color_emissive); } }
A la hora de dibujar establecemos cada uno de los valores de los componentes del material obtenidos previamente, para obtener la ubicación de un uniform tipo struct en los shaders accedemos a ello mediante el nombre, punto “.” seguido del nombre de la variable que deseamos establecer, ejm:
glGetUniformLocation(program, "material.ambient");
para obtener la ubicación de la componente ambiental del material.glBindVertexArray(vao); glUniform3fv(glGetUniformLocation(program, "material.ambient"), 1, color_ambient); glUniform3fv(glGetUniformLocation(program, "material.diffuse"), 1, color_diffuse); glUniform3fv(glGetUniformLocation(program, "material.specular"), 1, color_specular); glUniform3fv(glGetUniformLocation(program, "material.emissive"), 1, color_emissive); glUniform1f(glGetUniformLocation(program, "material.shininess"), shininess); glUniform1f(glGetUniformLocation(program, "material.shininess_strength"), shininess_strength); glDrawElements(GL_TRIANGLES, indices.size(), GL_UNSIGNED_INT, NULL); glBindVertexArray(0);
Iluminación Blinn-Phong
El modelo de iluminación Blinn-Phong es una extensión del modelo Phong, se implementa prácticamente de la misma manera solo que remplazamos el vector reflejado R por el vector H que es un vector intermedio entre la dirección de la luz y el vector de la vista o cámara.
N (vector normal), V (vector de la vista), L (vector de la luz).
vec3 N = normalize(N1); vec3 L = normalize(light.direction); vec3 V = normalize(V1); vec3 H = normalize(L + V); float diff = max(dot(N, L), 0); float spec = pow(max(dot(N, H), 0), material.shininess) * material.shininess_strength; vec3 emissive = material.emissive; vec3 ambient = material.ambient * light.ambient; vec3 diffuse = material.diffuse * light.diffuse * diff; vec3 specular = material.specular * light.specular * spec; color = vec4(emissive + ambient + diffuse + specular, 1.0);
Ahora podemos cargar un modelo soportado por assimp que incluya materiales, utilizaremos una fuente de luz direccional, al ejecutar la aplicación veremos algo similar a esto:
En el tutorial: cargar modelos con assimp aprendimos a cargar modelos almacenados en diversos formatos, se han creado las clases Model y Mesh para encapsular las funcionalidades de la biblioteca assimp, ambas clases se encuentran en el archivo OpenGLModel.hpp.
Clases Model y Mesh
La clase Mesh nos servirá para almacenar cada una de las partes que componen el modelo, la clase Model es el contenedor principal que maneja cada uno de los meshes que conforman en modelo o escena, aplicando lo que aprendimos en el tutorial anterior nuestras dos clases se ven de este modo:
class Mesh { private: Model* model; public: Mesh(const aiMesh* mesh, Model* model) { this->model = model; load(mesh); create(); } ~Mesh() { glDeleteBuffers(4, buffer); glDeleteVertexArrays(1, &vao); } // dibujar el mesh void draw() { glBindVertexArray(vao); glDrawElements(GL_TRIANGLES, indices.size(), GL_UNSIGNED_INT, NULL); glBindVertexArray(0); }; // inicializar el mesh void init(const aiMesh* mesh) { load(mesh); create(); }; private: vector<glm::vec3> vertex; vector<glm::vec3> normal; vector<glm::vec2> uv; vector<unsigned int> indices; GLuint buffer[4]; GLuint vao; // obtener los datos de cada mesh void load(const aiMesh* mesh) { vertex.reserve(mesh->mNumVertices); uv.reserve(mesh->mNumVertices); normal.reserve(mesh->mNumVertices); indices.reserve(3 * mesh->mNumFaces); for (unsigned int i = 0; i < mesh->mNumVertices; i++) { // Obtener la posicion de cada vertice const aiVector3D* pos = &(mesh->mVertices[i]); vertex.push_back(glm::vec3(pos->x, pos->y, pos->z)); // Obtener las coordenadas de textura if (mesh->HasTextureCoords(0)) { const aiVector3D* UVW = &(mesh->mTextureCoords[0][i]); uv.push_back(glm::vec2(UVW->x, UVW->y)); } // Obtener los vectores normales if (mesh->HasNormals()) { const aiVector3D* n = &(mesh->mNormals[i]); normal.push_back(glm::vec3(n->x, n->y, n->z)); } } // Obtener los indices for (unsigned int i = 0; i < mesh->mNumFaces; i++) { indices.push_back(mesh->mFaces[i].mIndices[0]); indices.push_back(mesh->mFaces[i].mIndices[1]); indices.push_back(mesh->mFaces[i].mIndices[2]); } } void create() { // generar y activar el VAO glGenVertexArrays(1, &vao); glBindVertexArray(vao); // generar dos ids para los buffer glGenBuffers(4, buffer); // buffer de vertices glBindBuffer(GL_ARRAY_BUFFER, buffer[0]); glBufferData(GL_ARRAY_BUFFER, vertex.size() * sizeof(glm::vec3), &vertex[0], GL_STATIC_DRAW); glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, NULL); glEnableVertexAttribArray(0); // buffer de textura if (!uv.empty()) { glBindBuffer(GL_ARRAY_BUFFER, buffer[1]); glBufferData(GL_ARRAY_BUFFER, uv.size() * sizeof(glm::vec2), &uv[0], GL_STATIC_DRAW); glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 0, NULL); glEnableVertexAttribArray(1); } // buffer de normales if (!normal.empty()) { glBindBuffer(GL_ARRAY_BUFFER, buffer[2]); glBufferData(GL_ARRAY_BUFFER, normal.size() * sizeof(glm::vec3), &normal[0], GL_STATIC_DRAW); glVertexAttribPointer(2, 3, GL_FLOAT, GL_FALSE, 0, NULL); glEnableVertexAttribArray(2); } // buffer de indices glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, buffer[3]); glBufferData(GL_ELEMENT_ARRAY_BUFFER, indices.size() * sizeof(unsigned int), &indices[0], GL_STATIC_DRAW); // desactivar el VAO glBindVertexArray(0); } };
class Model { public: map<string, GLuint> textures; const aiScene* scene; private: vector<shared_ptr<Mesh>> meshes; // procesar recusivamente cada nodo de la escena void processNode(const aiNode* node, const aiScene* scene) { // obtener los mesh de esta escena for (unsigned int i = 0; i < node->mNumMeshes; i++) { shared_ptr<Mesh> mesh(new Mesh(scene->mMeshes[node->mMeshes[i]], this)); meshes.push_back(mesh); } // procesar los hijos del nodo for (unsigned int i = 0; i < node->mNumChildren; i++) this->processNode(node->mChildren[i], scene); } public: // cargar el archivo deseado void init(const std::string& file_name) { Assimp::Importer importer; scene = importer.ReadFile(file_name, aiProcess_Triangulate); if (scene && scene->mRootNode) processNode(scene->mRootNode, scene); else cout << importer.GetErrorString() << endl; } // dibujar la escena completa void draw() { for (auto m : meshes) m->draw(); } };
Para inicializar o cargar el modelo usaremos el método
model.init("model/deadpool/dead.obj");
solo debemos indicar la ruta donde se encuentra el archivo que deseamos cargar, para dibujar el modelo usaremos model.draw();
, model es un objeto de la clase Model. GitHub: Materiales en OpenGL con Assimp
cómo funciona aiColorToFloat? estoy intentando sacar del modelo Assimp sus constantes ambiental, difusa, especular y emisiva pero luego nose cómo asignarlas a glm::vec3
ResponderEliminarMe podrías ayudar?
Muchas gracias
La función aiColorToFloat se encarga de convertir la estructura aiColor4D usada por ASSIMP para almacenar el color en un arreglo float[] que necesita OpenGL, este arreglo debe contener las componentes de color RGBA.
Eliminar