|
Grado en Ingeniería Informática
Realidad Virtual
Curso 2023/2024
|
Práctica 8
|
|
Importando
modelos externos
|
|
Objetivo
|
|
Los objetos que hemos representado hasta ahora son figuras
geométricas cuyos atributos pueden ser calculados por medio de
fórmulas matemáticas. Para representar objetos más complejos es
necesario conocer los valores de los atributos y utilizarlos de la
misma forma en que hemos representado las figuras geométricas.
Existen numerosas herramientas de diseño gráfico dedicadas a modelar
objetos 3D. En esta práctica vamos a estudiar un formato de
representación de figuras 3D utilizado por muchas herramientas y
vamos a desarrollar un analizador para poder incorporar estos
diseños gráficos a nuestras aplicaciones.

|
|
Figuras compuestas de piezas
|
|
Los modelos representados en las prácticas anteriores estaban
formados por objetos sencillos. Estos objetos, descritos por medio
de figuras geométricas, utilizaban un único color y un único fichero
de textura. Para considerar modelos más complejos hay que tener en
cuenta que las figuras pueden estar compuestas de diferentes piezas.
Estas piezas pueden tener colores y texturas diferentes y pueden
estar colocadas en su propia posición descrita respecto del sistema
de coordenadas de la figura a la que pertenecen.
Por ejemplo, podríamos generar una figura que describa un muñeco de
nieve por medio de tres esferas de color blanco (colocadas una encima
de otra). El sombrero lo podríamos generar por medio de un cilindro
y un disco de color negro. La nariz podría ser un cono de color
naranja.
Para considerar todas estas cuestiones vamos a modificar un poco las
clases que hemos utilizado hasta ahora. Para describir las
piezas vamos a defnir una nueva clase llamada CGPiece que
contenga la lista de vértices incluidos en la pieza, así como una
referencia al material. Esta clase debe incluir también una matriz
de posición que realice la transformación entre las coordenadas
locales de la pieza y las coordenadas locales de la figura. Asímismo vamos a definir la clase
CGObject para representar
figuras compuestas de piezas. Esta clase contendrá una lista de
piezas y de materiales, así como la matriz de transformación entre
el sistema de coordenadas de la figura y el sistema de coordenadas
de la escena.
El fichero de cabecera de
la clase CGPiece es el siguiente.
#pragma once
#include <GL/glew.h>
#include <glm/glm.hpp>
#include "CGMaterial.h"
#include "CGShaderProgram.h"
#define VERTEX_DATA 0
#define INDEX_DATA 1
#define NORMAL_DATA 2
#define TEXTURE_DATA 3
//
// CLASE: CGPiece
//
// DESCRIPCIÓN: Clase abstracta que representa una pieza descrita mediante
// VAO para su renderizado mediante shaders
//
class CGPiece {
protected:
GLushort* indexes; // Array of indexes
GLfloat* vertices; // Array of vertices
GLfloat* normals; // Array of normals
GLfloat* textures; // Array of texture coordinates
GLuint numFaces; // Number of faces
GLuint numVertices; // Number of vertices
GLuint VBO[4];
GLuint VAO;
glm::mat4 location; // Model matrix
CGMaterial* material;
public:
~CGPiece();
void InitBuffers();
void SetMaterial(CGMaterial* m);
void SetLocation(glm::mat4 loc);
glm::mat4 GetLocation();
void Translate(glm::vec3 t);
void Rotate(GLfloat angle, glm::vec3 axis);
void Draw(CGShaderProgram* program, glm::mat4 projection,
glm::mat4 view, glm::mat4 model);
};
|
El código de la clase CGPiece es muy parecido al de la
clase CGFigure presentado en la práctica anterior pero
añade la matriz model al método Draw().
#include "CGPiece.h"
#include <GL/glew.h>
#include <glm/glm.hpp>
#include <glm/gtc/matrix_transform.hpp>
#include "CGMaterial.h"
//
// FUNCIÓN: CGPiece::~CGPiece()
//
// PROPÓSITO: Destructor de la pieza
//
Piece3D::~Piece3D()
{
if (vertices != NULL) delete[] vertices;
if (indexes != NULL) delete[] indexes;
if (normals != NULL) delete[] normals;
if (textures != NULL) delete[] textures;
// Delete vertex buffer objects
glDeleteBuffers(4, VBO);
glDeleteVertexArrays(1, &VAO);
}
//
// FUNCIÓN: CGPiece::InitBuffers()
//
// PROPÓSITO: Crea el VAO y los VBO y almacena todos los datos
// en la GPU.
//
void CGPiece::InitBuffers()
{
// Create the Vertex Array Object
glGenVertexArrays(1, &VAO);
glBindVertexArray(VAO);
// Create the Vertex Buffer Objects
glGenBuffers(4, VBO);
// Copy data to video memory
// Vertex data
int buffsize = sizeof(GLfloat)*numVertices * 3;
glBindBuffer(GL_ARRAY_BUFFER, VBO[VERTEX_DATA]);
glBufferData(GL_ARRAY_BUFFER, buffsize, vertices, GL_STATIC_DRAW);
// Normal data
buffsize = sizeof(GLfloat) * numVertices * 3;
glBindBuffer(GL_ARRAY_BUFFER, VBO[NORMAL_DATA]);
glBufferData(GL_ARRAY_BUFFER, buffsize, normals, GL_STATIC_DRAW);
// Texture coordinates
buffsize = sizeof(GLfloat) * numVertices * 2;
glBindBuffer(GL_ARRAY_BUFFER, VBO[TEXTURE_DATA]);
glBufferData(GL_ARRAY_BUFFER, buffsize, textures, GL_STATIC_DRAW);
// Indexes
buffsize = sizeof(GLushort)*numFaces * 3;
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, VBO[INDEX_DATA]);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, buffsize, indexes, GL_STATIC_DRAW);
delete[] vertices;
delete[] indexes;
delete[] normals;
delete[] textures;
vertices = NULL;
indexes = NULL;
normals = NULL;
textures = NULL;
glEnableVertexAttribArray(0); // Vertex position
glBindBuffer(GL_ARRAY_BUFFER, VBO[VERTEX_DATA]);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, 0);
glEnableVertexAttribArray(1); // Vertex normals
glBindBuffer(GL_ARRAY_BUFFER, VBO[NORMAL_DATA]);
glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 0, 0);
glEnableVertexAttribArray(2); // Vertex textures
glBindBuffer(GL_ARRAY_BUFFER, VBO[TEXTURE_DATA]);
glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, 0, 0);
location = glm::mat4(1.0f);
}
//
// FUNCIÓN: CGPiece::SetMaterial(CGMaterial* m)
//
// PROPÓSITO: Asigna el material de la pieza
//
void CGPiece::SetMaterial(CGMaterial* m)
{
material = m;
}
//
// FUNCIÓN: CGPiece::SetLocation(glm::mat4 loc)
//
// PROPÓSITO: Asigna la matriz de posición de la pieza respecto a la posición de la
// figura a la que pertenece.
//
void CGPiece::SetLocation(glm::mat4 loc)
{
location = loc;
}
//
// FUNCIÓN: CGPiece::GetLocation()
//
// PROPÓSITO: Obtiene la matriz de posición de la pieza respecto a la posición de la
// figura a la que pertenece.
//
glm::mat4 CGPiece::GetLocation()
{
return location;
}
//
// FUNCIÓN: CGPiece::Translate(glm::vec3 t)
//
// PROPÓSITO: Añade un desplazamiento a la matriz de posición de la pieza
// respecto a la posición de la figura a la que pertenece.
//
void CGPiece::Translate(glm::vec3 t)
{
location = glm::translate(location, t);
}
//
// FUNCIÓN: CGPiece::Rotate(GLfloat angle, glm::vec3 axis)
//
// PROPÓSITO: Añade una rotación a la matriz de posición de la pieza
// respecto a la posición de la figura a la que pertenece.
//
void CGPiece::Rotate(GLfloat angle, glm::vec3 axis)
{
location = glm::rotate(location, glm::radians(angle), axis);
}
//
// FUNCIÓN: CGPiece::Draw()
//
// PROPÓSITO: Dibuja la pieza
//
void CGPiece::Draw(CGShaderProgram * program, glm::mat4 projection,
glm::mat4 view, glm::mat4 model)
{
glm::mat4 mvp = projection*view*model*location;
program->SetUniformMatrix4("MVP", mvp);
program->SetUniformMatrix4("ViewMatrix", view);
program->SetUniformMatrix4("ModelViewMatrix", view*model*location);
material->SetUniforms(program);
glBindVertexArray(VAO);
glDrawElements(GL_TRIANGLES, numFaces * 3, GL_UNSIGNED_SHORT, NULL);
}
|
Por su parte, la clase CGObject se define de la siguiente
forma.
#pragma once
#include <GL/glew.h>
#include <glm/glm.hpp>
#include "CGShaderProgram.h"
#include "CGPiece.h"
//
// CLASE: CGObject
//
// DESCRIPCIÓN: Clase abstracta que representa un objeto
// formado por varias piezas
//
class CGObject {
protected:
glm::mat4 model; // Model matrix
public:
void ResetLocation();
void Translate(glm::vec3 t);
void Rotate(GLfloat angle, glm::vec3 axis);
void SetLocation(glm::mat4 loc);
glm::mat4 GetLocation();
void Draw(CGShaderProgram* program, glm::mat4 projection, glm::mat4 view);
virtual int GetNumPieces()=0;
virtual CGPiece* GetPiece(int i)=0;
};
|
El código de la clase CGObject se muestra a continuación.
#include "CGObject.h"
#include <GL/glew.h>
#include <FreeImage.h>
#include <glm/glm.hpp>
#include <glm/gtc/matrix_transform.hpp>
//
// FUNCIÓN: CGObject::ResetLocation()
//
// PROPÓSITO: Asigna la posición inicial del objecto
//
void CGObject::ResetLocation()
{
model = glm::mat4(1.0f);
}
//
// FUNCIÓN: CGObject::SetLocation(glm::mat4 loc)
//
// PROPÓSITO: Asigna la posición del objecto
//
void CGObject::SetLocation(glm::mat4 loc)
{
model = loc;
}
//
// FUNCIÓN: CGObject::GetLocation()
//
// PROPÓSITO: Obtiene la posición del objecto
//
glm::mat4 CGObject::GetLocation()
{
return model;
}
//
// FUNCIÓN: CGObject::Translate(glm::vec3 t)
//
// PROPÓSITO: Añade un desplazamiento a la matriz de posición del objeto
//
void CGObject::Translate(glm::vec3 t)
{
model = glm::translate(model, t);
}
//
// FUNCIÓN: CGObject::Rotate(GLfloat angle, glm::vec3 axis)
//
// PROPÓSITO: Añade una rotación a la matriz de posición del objeto
//
void CGObject::Rotate(GLfloat angle, glm::vec3 axis)
{
model = glm::rotate(model, glm::radians(angle), axis);
}
//
// FUNCIÓN: CGObject::Draw(CGShaderProgram * program,
// glm::mat4 projection, glm::mat4 view)
//
// PROPÓSITO: Dibuja el objeto
//
void CGObject::Draw(CGShaderProgram * program, glm::mat4 projection, glm::mat4 view)
{
int num = GetNumPieces();
for (int i = 0; i < num; i++)
{
GetPiece(i)->Draw(program, projection, view, model);
}
}
|
|
|
El formato Wavefront OBJ
|
|
La compañía Wavefront Technologies Ltd. fue fundada en 1984 y se
dedicaba al desarrollo de software de diseño gráfico. En 1995
Silicon Graphics adquirió las empresas Wavefront Technologies y
Alias Research formando una nueva empresa llamada Alias/Wavefront.
En 2006 la empresa fue absorbida por Autodesk. Actualmente el
producto más conocido de esta empreesa es Maya, considerado como una
de las mejores aplicaciones de diseño gráfico.
El formato OBJ fue desarrollado por Wavefront para su software
Advanced Visualizer. Existen varios formatos de descripción de
modelos 3D que corresponden a distintas aplicaciones gráficas (3ds,
c4d, blend, max, ...). El formato OBJ tiene la ventaja de ser un
formato libre (no propietario), estar descrito en modo texto y estar
definido por medio de un lenguaje formal, lo que facilita su
incorporación en muchas aplicaciones gráficas. El siguiente enlace
contiene la definición formal del formato OBJ (enlace).
A continuación se presenta un ejemplo de especificación en este
formato.
# List of geometric vertices, with (x,y,z[,w]) coordinates,
# w is optional and defaults to 1.0.
v 0.123 0.234 0.345 1.0
v ...
...
# List of texture coordinates, in (u, v [,w]) coordinates, these will vary
# between 0 and 1, w is optional and defaults to 0.
vt 0.500 1 [0]
vt ...
...
# List of vertex normals in (x,y,z) form; normals might not be unit vectors.
vn 0.707 0.000 0.707
vn ...
...
# Parameter space vertices in ( u [,v] [,w] ) form; free form geometry statement
vp 0.310000 3.210000 2.100000
vp ...
...
# Polygonal face element
f 1 2 3
f 3/1 4/2 5/3
f 6/4/1 3/5/3 7/6/5
f 7//1 8//2 9//3
f ...
...
# Line element
l 5 8 1 2 4 9
l ...
...
|
Algunas características del formato son las siguientes:
-
Los comentarios comienzan con el símbolo # y
terminan en el final de línea.
-
Las posiciones de los vértices comienzan con el símbolo
v seguido de las componentes x,y,z y w.
La cuarta componente es opcional y por defecto su valor es 1.0.
-
Las coordenadas de textura de los vértices comienzan con el
símbolo vt seguido de las componentes s,t
y r. La tercera componente es opcional.
-
Los vectores normales comienzan con el símbolo vn
seguido de las componentes x,y,z.
-
La directiva vp permite definir parámetros
asociados a vértices, que suelen utilizarse en las primitivas
patch utilizadas en el shader de teselado.
-
La directiva f describe polígonos. Generalmente
se utiliza para definir triángulos aunque el número de vértices
puede ser mayor. En ese caso se puede interpretar la directiva
como un triangle_fan que genera el polígono.
-
Los vértices se describen mediante índices a la lista de
posiciones, coordenadas de textura y normales. Los índices
pueden ser positivos (indicando una numeración desde el primer
elemento) o negativos (indicando una numeración desde el último
elemento). El índice del primer elemento es 1. Los índices a la
coordenada de textura y a la normal son opcionales.
-
La directiva l describe líneas formadas por una
lista de vértices. Se interpreta como primitivas LINE_STRIP.
-
La directiva p describe puntos. Se interpreta
como primitivas POINTS.
-
El formato permite definir también formas libres. Estas formas
pueden ser curvas, superficies y curvas sobre superficies. Para
generar estas formas libres se pueden utilizar cinco métodos
diferentes: Bezier, basis matrix, B-spline, Cardinal y Taylor.
-
Los diferentes elementos (polígonos, líneas, puntos y formas
libres) se pueden agrupar en objetos (definidos mediante una
directiva o seguida de un nombre opcional) y
grupos (definidos mediante una directiva g
seguida de un nombre opcional).
-
La especificación del material de un objeto se introduce por
medio de la directiva usemtl seguida de un
identificador. El identificador debe corresponder a alguno de
los materiales incluidos en la biblioteca de materiales.
-
La biblioteca de materiales se define por medio da la directiva
mtllib seguida del nombre del fichero en el que
se definen los materiales.
Los materiales se describen en ficheros externos que suelen tener la
extensión .mtl. El formato MTL es también un formato libre
en modo texto. A continuación se presenta un ejemplo de descripción
de materiales en este formato:
newmtl Textured
Ka 1.000 1.000 1.000
Kd 1.000 1.000 1.000
Ks 0.000 0.000 0.000
d 1.0
illum 2
map_Ka lemur.tga # the ambient texture map
map_Kd lemur.tga # the diffuse texture map (most of the time, it will
# be the same as the ambient texture map)
map_Ks lemur.tga # specular color texture map
map_Ns lemur_spec.tga # specular highlight component
map_d lemur_alpha.tga # the alpha texture map
map_bump lemur_bump.tga # normal mapping
|
Las directivas utilizadas para definir materiales son las
siguientes:
-
newmtl nombre: Crea un nuevo material. Las
directivas introducidas a continuación (hasta la declaración del
siguiente material o el final del fichero) están dedicadas a
configurar las propiedades del material. Para utilizar el
material en los archivos OBJ se utiliza la directiva usemtl
indicando el mismo nombre.
-
Ka r g b: Define el valor de la
reflectividad ambiental. Los valores (r,g,b) definen las
componentes de color con valores entre 0 y 1.
-
Kd r g b: Define el valor de la reflectividad
difusa. Los valores (r,g,b) definen el color.
-
Ks r g b: Define el valor dela reflectividad
especular. Los valores (r,g,b) definen el color.
-
d valor: Define el valor de la opacidad,
es decir, del canal alpha. Este campo se conoce como
dissolved.
-
Tr valor: Define el valor de la transparencia.
Es una forma alternativa de definir la opacidad. El valor del
canal alpha se define como 1-Tr.
-
Ns valor: Define el valor del factor de brillo,
es decir, del exponente aplicado al efecto especular.
-
Ni valor: Define el índice de refracción.
-
Tf r g b: Define el color de un filtro de
transmisión.
-
illum código: Define el modelo de ilumniación.
-
map_Ka
fichero: Define el fichero utilizado como textura sobre
el efecto ambiental.
-
map_Kd fichero: Define el fichero
utilizado como textura sobre el efecto difuso. Suele coincidir
con el ambiental.
-
map_Ks fichero: Define el fichero
utilizado como textura sobre el efecto especular.
-
map_Ns fichero: Define una textura que
contiene el factor de brillo.
-
map_d fichero: Define una textura
utilizada como valores de transparencia.
-
map_bump fichero: Define el fichero de
textura utilizado como normal map.
-
bump fichero: Equivalente a la directiva
map_bump.
-
disp fichero: Define una textura
utilizada como mapa de desplazamiento.
-
decal fichero: Define una textura
utilizada como plantilla para ciertos efectos.
-
refl options fichero: Define el modelo y
la imagen utilizada para incorporar efectos de reflexión.
La directiva illum define el modelo de iluminación
asignado al material. Los valores posibles son los siguientes:
0. Color on and Ambient off
1. Color on and Ambient on
2. Highlight on
3. Reflection on and Ray trace on
4. Transparency: Glass on, Reflection: Ray trace on
5. Reflection: Fresnel on and Ray trace on
6. Transparency: Refraction on, Reflection: Fresnel off and Ray trace on
7. Transparency: Refraction on, Reflection: Fresnel on and Ray trace on
8. Reflection on and Ray trace off
9. Transparency: Glass on, Reflection: Ray trace off
10. Casts shadows onto invisible surfaces
|
Las directivas de textura y reflexión pueden incluir diferentes
opciones. Una descripción detallada del formato MTL con todas sus
opciones puede encontrarse en este enlace (formato
mtl).
|
|
Analizadores para archivos OBJ
y MTL
|
|
Para desarrollar los analizadores de los ficheros OBJ y MTL vamos a
seguir el método descrito en la asignatura "Procesadores de
Lenguajes". En esta asignatura se explica como desarrollar
analizadores semánticos para lenguajes formales y se utiliza la
herramienta JavaCC para desarrollar estos analizadores en lenguaje
Java. Siguiendo esta técnica vamos a crear una aplicación Java que
lea un fichero OBJ y genere los archivos ".cpp" y ".h" compatibles
con las definiciones que hemos desarrollado para nuestros proyectos
gráficos.
Los archivos contenidos en esta aplicación Java son los siguientes:
-
VertexPosition: Clase que describe la posición
de un vértice. Se trata de un contenedor con los campos (x,y,z).
Corresponde a la información incluida en una declaración "v
x y z [w]" de un fichero OBJ.
-
VertexNormal: Clase que describe el vector
normal de un vértice. Se trata de un contenedor con los campos (x,y,z).
Corresponde a la información incluida en una declaración "vn
x y z" de un fichero OBJ.
-
VertexTexCoord: Clase que describe las
coordenadas de textura de un vértice. Se trata de un contenedor
con los campos (s,t,r). Corresponde a la información
incluida en una declaración "vt s t [r]"
de un fichero OBJ.
-
VertexIndex: Clase que describe la referencia a
un vértice. En el formato OBJ los vértices se referencian con
los índices a la posición, coordenadas de textura y normal
descritos previamente. Los índices pueden ser positivos
(indicando una numeración desde el primer elemento) o negativos
(indicando una numeración desde el último elemento). Además los
índices a la coordenada de textura y a la normal son opcionales.
La clase VertexIndex almacena la información incluida
en declaraciones de tipo "v/vt/vn" o "v/vt"
o "v//vn" o "v".
-
AttribVertex: Clase que describe los atributos
de un vértice. Dado que OpenGL utiliza un único índice para
referenciar toda la información de un vértice, es necesario
transformar las listas de posiciones, normales y texturas en
listas de atributos (que posiblemente repitan información). Esta
clase contiene la información de la posición, coordenadas de
textura y vector normal correspondiente a un vértice y añade el
índice asociado al vértice (que será utilizado en el array de
índices de la pieza).
-
Face: Clase que describe una primitiva (un
triángulo). Es un contenedor que almacena los tres índices
correspondientes a los objetos VertexIndex que
describen los vértices del triángulo. Corresponde a la
información incluida en una declaración de tipo "f
v1/vt1/vn1 v2/vt2/vn2 v3/vt3/vn3" de un fichero
OBJ.
-
Piece: Clase que describe una pieza. Está
formada por una lísta de vértices (objetos AttribVertex)
y una lista de primitivas (objetos Face). Contiene
además una referencia al material (objeto Material) que
compone la pieza. Los objetos Piece corresponden a las
declaraciones de grupo "g nombre" o de objeto "o
nombre" incluidas en un fichero OBJ.
-
Material: Clase que describe un material. Se
trata de un contenedor que almacena los campos Ka
(reflectividad ambiental), Kd (reflectividad difusa),
Ks (reflectividad especular), Ns (factor
especular), d (opacidad), illum (tipo de
iluminación), texture (fichero de textura) y
normalmap (fichero de textura normalmap). Contiene además
dos identificadores: name (identificador utilizado en
los ficheros OBJ y MTL) y code (identificador utilizado
en el código C++ a generar). Los materiales se describen en los
ficheros MTL (directiva newmtl) y se utilizan
en los ficheros OBJ (directiva usemtl).
-
MaterialLib: Clase que describe una biblioteca
de materiales. Contiene la lista de materiales descritos en un
fichero MTL.
-
Figure: Clase que describe una figura definida
en un fichero OBJ. Está compuesta por una lista de objetos
VertexPosition (correspondiente a las directivas v),
una lista de objetos VertexNormal (correspondiente a
las directivas vn), una lista de objetos
VertexTexCoord (correspondiente a las directivas vt),
una lista de objetos MaterialLib (correspondiente a las
directivas mtllib) y una lista de objetos
Piece (correspondientes a las directivas o
y g). La clase Figure
contiene una indicación de la última pieza declarada (campo
active) de manera que las directivas f
permiten añadir una primitiva a la pieza activa y la directiva
usemtl permite asignar el material a la pieza
activa.
-
MtlParser.jj: Analizador de ficheros MTL
descrito en el formato de la herramienta JavaCC. El analizador
generado permite leer un archivo MTL y crear el objeto
MaterialLib con la biblioteca de materiales descrita en ese
archivo.
-
ObjParser.jj: Analizador de ficheros OBJ
descrito en el formato de la herrameinta JavaCC. El analizador
generado permite leer un archivo OBJ y crear el objeto
Figure con la figura descrita en ese archivo.
-
CGObjectParser: Clase principal de la
aplicación. Contiene el método main() que toma como
argumento de entrada en nombre del fichero OBJ a analizar y la
escala. Este método crea un analizador ObjParser,
obtiene el objeto Figure correspondiente al modelo
descrito en el fichero OBJ y genera los archivos ".cpp"
y ".h" que pueden incluirse en los proyectos
que estamos desarrollando.
La aplicación se distribuye como un archivo llamado ObjParser.jar.
El formato de llamada de la aplicación es el siguiente:
java -jar ObjParser.jar Figura.obj escala
|
El
argumento escala es un número real que se aplicará como
factor de escala en los valores de las posiciones de los vértices.
Por ejemplo, si el fichero OBJ describe un vehículo en escala real y
queremos un objeto en escala 1:32 hay que realizar la llamada con un
factor de escala de 0.03125. El resultado de la aplicación consiste
en cuatro archivos: Figura.h (fichero de cabecera de la
figura descrita en el archivo OBJ), Figura.cpp (fichero de
código de la figura descrita en el archivo OBJ), Figura_pieces.h
(fichero de cabecera del conjunto de piezas de la figura) y
Figura_pieces.cpp (fichero de código del conjunto de piezas de
la figura).
|
|
El programa gráfico
|
|
El VertexShader utilizado para mostrar los objetos es el
mismo que ya se introdujo al presentar las texturas.
#version 400
layout(location = 0) in vec3 VertexPosition;
layout(location = 1) in vec3 VertexNormal;
layout(location = 2) in vec2 VertexTexCoord;
uniform mat4 MVP;
uniform mat4 ViewMatrix;
uniform mat4 ModelViewMatrix;
out vec3 Position;
out vec3 Normal;
out vec2 TexCoord;
void main()
{
vec4 n4 = ModelViewMatrix*vec4(VertexNormal, 0.0);
vec4 v4 = ModelViewMatrix*vec4(VertexPosition, 1.0);
Normal = vec3(n4);
Position = vec3(v4);
TexCoord = VertexTexCoord;
gl_Position = MVP * vec4(VertexPosition, 1.0);
}
|
Por su parte el FragmentShader
se ha modificado para tener en cuenta el canal de transparencia de
las texturas. Cuando la textura de una pieza incluye un valor
distinto de 1.0 en el canal alpha hay que considerar que la pieza es
traslúcida e incorporar este valor al color final de cada fragmento.
Para evitar que un valor 0 se traduzca en una pieza totalmente
transparente (que, por tanto, no se vería) se ha escalado la
transparencia entre 0.5 y 1.0.
#version 400
in vec3 Position;
in vec3 Normal;
in vec2 TexCoord;
uniform sampler2D BaseTex;
uniform mat4 ViewMatrix;
struct LightInfo {
vec3 Ldir;
vec3 La;
vec3 Ld;
vec3 Ls;
};
uniform LightInfo Light;
struct MaterialInfo{
vec3 Ka;
vec3 Kd;
vec3 Ks;
float Shininess;
};
uniform MaterialInfo Material;
out vec4 FragColor;
vec3 ads(vec3 TexColor) {
vec4 s4 = ViewMatrix*vec4(Light.Ldir, 0.0);
vec3 n = normalize(Normal);
vec3 v = normalize(-Position);
vec3 s = normalize(-vec3(s4));
vec3 r = reflect(-s, n);
float dRate = max(dot(s, n), 0.0);
float sRate = pow(max(dot(r, v), 0.0), Material.Shininess);
vec3 ambient = Light.La * Material.Ka;
vec3 difusse = Light.Ld * Material.Kd * dRate;
vec3 specular = Light.Ls * Material.Ks * sRate;
return (ambient + difusse)*TexColor + specular;
}
void main()
{
vec4 TexColor = texture(BaseTex,TexCoord);
vec3 Color = ads(TexColor.rgb);
FragColor = vec4(Color, (TexColor.a+1)/2);
}
|
La clase CGModel también se ha tenido que modificar para poder
tratar las transparencias. Para ello simplemente se ha actualizado
el método initialize(), activando la opción GL_BLEND y
configurando la función de mezcla.
//
// FUNCIÓN: CGModel::initialize(int, int)
//
// PROPÓSITO: Initializa el modelo 3D
//
void CGModel::initialize(int w, int h)
{
// Crea el programa
program = new CGShaderProgram(IDR_SHADER1, IDR_SHADER2, -1, -1, -1);
// program = new CGShaderProgram("shaders/VertexShader.glsl",
// "shaders/FragmentShader.glsl", NULL, NULL, NULL);
if (program->IsLinked() == GL_FALSE) return;
program->Use();
// Crea la cámara
camera = new CGCamera();
camera->SetPosition(0.0f, 5.0f, 30.0f);
// Crea la escena
scene = new CGScene();
// Asigna el viewport y el clipping volume
resize(w, h);
// Opciones de dibujo
glEnable(GL_DEPTH_TEST);
glEnable(GL_CULL_FACE);
glFrontFace(GL_CCW);
glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
}
|
|
|