Principal | Gráficos 3D | Gráficos 2D | Fractales | Math | Códigos | Tutoriales | Links

Smooth Shading usando OpenGL

Cada poligono del modelo, tiene un vector normal asociado, el que me sirve para saber de que color debo "pintarlo", estos vectores tiene que tener modulo 1, es decir estan normalizados, por ejemplo en esta imagen se utilizaron estos vectores para "pintar" los poligonos, se pueden ver las facetas, es decir los poligonos, para que el modelo tenga una apariencia mas "suave", voy a utilizar lo que se llama "Smooth Shading", este metodo me elimina la diferencia de color entre poligonos y el modelo toma una apariencia mas "suave", como si estaria compuesto por una gran cantidad de poligonos, el codigo es similar al del ejemplo anterior.

El siguiente codigo me calcula los vectores normales para cada uno de los vertices, las promedia y despues normaliza el resultado con lo que ese vertice presentara un vector normal al cual han contribuido todas las caras a las que pertenece.

...
for (i=0; i<NumVertices; i++) {
    for (j=0; j<NumPoligonos; j++) {
       if (Poli[j][0]==i || Poli[j][1]==i || Poli[j][2]==i)
           {
           sumx = sumx + Tri[j].N[0];
           sumy = sumy + Tri[j].N[1];
           sumz = sumz + Tri[j].N[2];
           shared ++;
           }
       }
    vnormales[i][0] = sumx / (float) shared;
    vnormales[i][1] = sumy / (float) shared;
    vnormales[i][2] = sumz / (float) shared;
    Normalizar(&vnormales[i][0], &vnormales[i][1], &vnormales[i][2]);
    sumx=0.0;
    sumy=0.0;
    sumz=0.0;
    shared=0.0;
    }
...

El codigo que me dibuja las caras es el siguiente, alli utilizo los vectores normales para cada uno de los vertices del poligono.

glBegin(GL_TRIANGLES);
 for (i=0; i<NumPoligonos; i++)
        {
        glNormal3fv(vnormales[Poli[i][0]]);
        glVertex3fv(Tri[i].V1);
        glNormal3fv(vnormales[Poli[i][1]]);
        glVertex3fv(Tri[i].V2);
        glNormal3fv(vnormales[Poli[i][2]]);
        glVertex3fv(Tri[i].V3);
        }
 glEnd();

Codigo Fuente

Codigo fuente, ejecutable (Win95), makefile smooth.zip

Archivo de texto que contiene, los datos del modelo Head.zip

#include <GL/glut.h>
#include <stdlib.h>
#include <math.h>
#include <stdio.>

#define MaxVertices     2467
#define MaxPoligonos    4834

struct Faces{
        GLfloat V1[3];
        GLfloat V2[3];
        GLfloat V3[3];
        GLfloat N[3];
       };

struct  Faces Tri[MaxPoligonos];
GLfloat Vertices[MaxVertices][3];
GLfloat vnormales[MaxVertices][3];
GLint   Poli[MaxPoligonos][3];

GLint   NumPoligonos=0;
GLint   NumVertices=0;

float LightPos[] = { 0.8f, 0.8f, 1.0f, 0.0f};   // Light Position
float LightAmb[] = { 0.4f, 0.4f, 0.4f, 1.0f};   // Ambient Light Values
float LightDif[] = { 1.0f, 1.0f, 1.0f, 1.0f};   // Diffuse Light Values
float LightSpc[] = { 1.0f, 1.0f, 1.0f, 1.0f};   // Specular Light Values

void GargarPoligonos(char *nombre)
{
 int i;

 FILE *in = fopen(nombre, "r");
 if (!in)
	return;
 fscanf(in, "%d\n", &NumVertices);
 for (i = 0; i<NumVertices; i++)
    fscanf(in, "%f %f %f\n", &Vertices[i][0], &Vertices[i][1], &Vertices[i][2]);
 fscanf(in, "%d\n", &NumPoligonos);
 for (i = 0; i<NumPoligonos; i++)
    fscanf(in, "%d %d %d\n", &Poli[i][0], &Poli[i][1], &Poli[i][2]);
 fclose(in);
}

//Calcula el modulo de un vector  (longitud)
GLfloat Modulo(GLfloat x, GLfloat y, GLfloat z)
{
 GLfloat len;

 len = x*x + y*y + z*z;
 return (sqrt(len));
}

//Normaliza el vector a mudulo 1
GLvoid Normalizar(GLfloat *x, GLfloat *y, GLfloat *z)
{
 GLfloat len;

 len = Modulo(*x, *y, *z);
 len = 1.0/len;
 (*x) *= len;
 (*y) *= len;
 (*z) *= len;
}

//Calcula el producto vectorial de dos vectores
GLvoid VectorNormal(GLfloat V1[], GLfloat V2[], GLfloat V3[],
                             GLfloat *NormalX, GLfloat *NormalY, GLfloat *NormalZ)
{
 GLfloat Qx, Qy, Qz, Px, Py, Pz;

 Px = V2[0]-V1[0];
 Py = V2[1]-V1[1];
 Pz = V2[2]-V1[2];
 Qx = V3[0]-V1[0];
 Qy = V3[1]-V1[1];
 Qz = V3[2]-V1[2];
 *NormalX = Py*Qz - Pz*Qy;
 *NormalY = Pz*Qx - Px*Qz;
 *NormalZ = Px*Qy - Py*Qx;
}

//Calculo el vector normal a cada uno de los poligonos
void CalcularNormales(void)
{
 GLfloat NormalX, NormalY, NormalZ;
 GLfloat sumx=0.0, sumy=0.0, sumz=0.0;
 int shared=0;
 GLint i,j;

 for (i=0; i<NumPoligonos; i++)
     {
      //Vertice 1
      j=Poli[i][0];
      Tri[i].V1[0] = Vertices[j][0];
      Tri[i].V1[1] = Vertices[j][1];
      Tri[i].V1[2] = Vertices[j][2];
      //Verice 2
      j=Poli[i][1];
      Tri[i].V2[0] = Vertices[j][0];
      Tri[i].V2[1] = Vertices[j][1];
      Tri[i].V2[2] = Vertices[j][2];
      //Vertice 3
      j=Poli[i][2];
      Tri[i].V3[0] = Vertices[j][0];
      Tri[i].V3[1] = Vertices[j][1];
      Tri[i].V3[2] = Vertices[j][2];
      //Calcula el vector Normal
      VectorNormal(Tri[i].V1, Tri[i].V2, Tri[i].V3, &NormalX, &NormalY, &NormalZ);
      //nos retorna un vector unitario, es decir de modulo 1
      Normalizar(&NormalX, &NormalY, &NormalZ);
      //almacena los vectores normales, para cada poligono
      Tri[i].N[0] = NormalX;
      Tri[i].N[1] = NormalY;
      Tri[i].N[2] = NormalZ;
    };

 for (i=0; i<NumVertices; i++) {
    for (j=0; j<NumPoligonos; j++) {
       if (Poli[j][0]==i || Poli[j][1]==i || Poli[j][2]==i)
           {
           sumx = sumx + Tri[j].N[0];
           sumy = sumy + Tri[j].N[1];
           sumz = sumz + Tri[j].N[2];
           shared ++;
           }
       }
    vnormales[i][0] = sumx / (float) shared;
    vnormales[i][1] = sumy / (float) shared;
    vnormales[i][2] = sumz / (float) shared;
    Normalizar(&vnormales[i][0], &vnormales[i][1], &vnormales[i][2]);
    sumx=0.0;
    sumy=0.0;
    sumz=0.0;
    shared=0.0;
    }
}

void reshape(int w, int h)
{
 if (!h)
	return;
 glViewport(0, 0,  (GLsizei) w, (GLsizei) h);
 /* Activamos la matriz de proyeccion. */
 glMatrixMode(GL_PROJECTION);
 /* "limpiamos" esta con la matriz identidad.*/
 glLoadIdentity();
 gluPerspective( 45.0, 1.0, 1.0, 100.0);
 /* Activamos la matriz de modelado/visionado. */
 glMatrixMode(GL_MODELVIEW);
 /* "Limpiamos" la matriz  */
 glLoadIdentity();
}

void display(void)
{
 /* Propiedades del material del Objeto*/
 GLfloat mat_ambient[] = { 0.3945f, 0.1718f, 0.1289f, 1.0f };
 GLfloat mat_diffuse[] = { 0.8231f, 0.67f, 0.574f, 1.0f };
 GLfloat mat_specular[] = { 0.8125f, 0.6406f, 0.5898f, 1.0f };
 GLfloat mat_shininess[] = { 128.0f };
 int i;

 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
 glPushMatrix();
 gluLookAt(30.0, 12.5, 10.0, 0.0, 12.5, 0.0, 0.0, 1.0, 0.0);
 //Dibujo el objeto
 glMaterialfv(GL_FRONT, GL_AMBIENT, mat_ambient);
 glMaterialfv(GL_FRONT, GL_DIFFUSE, mat_diffuse);
 glMaterialfv(GL_FRONT, GL_SPECULAR, mat_specular);
 glMaterialfv(GL_FRONT, GL_SHININESS, mat_shininess);

 glBegin(GL_TRIANGLES);
 for (i=0; i<NumPoligonos; i++)
        {
        glNormal3fv(vnormales[Poli[i][0]]);
        glVertex3fv(Tri[i].V1);
        glNormal3fv(vnormales[Poli[i][1]]);
        glVertex3fv(Tri[i].V2);
        glNormal3fv(vnormales[Poli[i][2]]);
        glVertex3fv(Tri[i].V3);
        }
 glEnd();

 glPopMatrix();
 glFlush();
}

void init(void)
{
 glClearColor(0.0, 0.0, 0.0, 0.0);
 glShadeModel(GL_SMOOTH);
 glCullFace(GL_BACK);
 glEnable(GL_DEPTH_TEST);
 glEnable(GL_CULL_FACE);

 /* Activo la fuente de luz */
 glLightfv(GL_LIGHT0, GL_POSITION, LightPos);        // Set Light1 Position
 glLightfv(GL_LIGHT0, GL_AMBIENT, LightAmb);         // Set Light1 Ambience
 glLightfv(GL_LIGHT0, GL_DIFFUSE, LightDif);         // Set Light1 Diffuse
 glLightfv(GL_LIGHT0, GL_SPECULAR, LightSpc);        // Set Light1 Specular
 glEnable(GL_LIGHT0);                                // Enable Light1
 glEnable(GL_LIGHTING);
 /* Uso depth buffering para la eliminacion de superficies ocultas */
 glEnable(GL_DEPTH_TEST);
 GargarPoligonos("head.txt");
 CalcularNormales();
}

 /* Termina la ejecucion del programa cuando se presiona ESC */
void keyboard(unsigned char key, int x, int y)
{
 switch (key)
   {
   case 27: exit(0);
             break;
   }
}

int main(int argc, char **argv)
{
  glutInit(&argc, argv);
  glutInitWindowSize(400, 400);
  glutInitDisplayMode(GLUT_SINGLE | GLUT_RGB | GLUT_DEPTH);
  glutCreateWindow("Smooth Shading");
  init();
  glutDisplayFunc(display);
  glutReshapeFunc(reshape);
  glutKeyboardFunc(keyboard);
  glutMainLoop();
  return 0;
 }

valcoey@hotmail.com

Ramiro Alcocer, 2001

Principal | Gráficos 3D | Gráficos 2D | Fractales | Math | Códigos | Tutoriales | Links