Professional Documents
Culture Documents
En primer lugar se necesita una base de datos o un arreglo con las coordenadas
(x,y,z) de los vértices y ademas los polígonos que forman el objeto.
En segundo lugar el objeto primero se rota y luego se traslada hasta la localización
adecuada, con lo que se obtienen unas nuevas coordenadas (x,y,z) para los vértices.
Finalmente, se eliminan los polígonos que no son visibles por el observador, se
aplica la perspectiva y se dibuja el objeto en la pantalla.
Mi objeto esta compuesto por polígonos de tres lados, y cada poligono tiene 3 vértices
cuyas coordenadas son (x,y,z).
Definición de un polígono
typedef struct TRI
{
int a, b, c;
};
Matrices de Transformación 3D
Matriz Identidad
1 0 0 0
0 1 0 0
0 0 1 0
0 0 0 1
Escalado
El escalado consiste en multiplicar las coordenadas del objeto por una constante, para
modificar su tamaño. Se describe el escalamiento cuando el punto fijo es el origen. Para
obtener un escalamiento con un punto fijo arbitrario, este se debe trasladar al origen, escalar
el objeto, y despues realizar el inverso de la traslacion original.
La matriz de escalamiento con factores de escala Ex, Ey, Ez en las direcciones x, y, z
respectivamente, esta dada por la matriz Es(Ex, Ey, Ez) :
Ex 0 0 0
0 Ey 0 0
0 0 Ez 0
0 0 0 1
1 0 0 0
0 cos(a) sin(a) 0
0 -sin(a) cos(a) 0
0 0 0 1
Código en C
tmpsin=sin(angulo);
tmpcos=cos(angulo);
*m = matriz_identidad;
m->v[1][1] = tmpcos;
m->v[1][2] = tmpsin;
m->v[2][1] = -tmpsin;
m->v[2][2] = tmpcos;
}
cos(a) 0 -sin(a) 0
0 1 0 0
sin(a) 0 cos(a) 0
0 0 0 1
Código en C.
tmpsin=sin(angulo);
tmpcos=cos(angulo);
*m = matriz_identidad;
m->v[0][0] = tmpcos;
m->v[0][2] = -tmpsin;
m->v[2][0] = tmpsin;
m->v[2][2] = tmpcos;
}
Ejemplo : matriz_rotacion_y(&Ry, 0.7)
donde Ry es la matriz de rotación 4x4, y 0.7 es el angulo de rotacióon.
cos(a) sin(a) 0 0
-sin(a) cos(a) 0 0
0 0 1 0
0 0 0 1
Código en C
tmpsin=sin(angulo);
tmpcos=cos(angulo);
*m = matriz_identidad;
m->v[0][0] = tmpcos;
m->v[0][1] = tmpsin;
m->v[1][0] = -tmpsin;
m->v[1][1] = tmpcos;
}
1 0 0 0
0 1 0 0
0 0 1 0
Tx Ty Tz 1
Código en C
Importante
Hay que tener en cuenta que no es lo mismo rotar y luego trasladar, que trasladar y luego
rotar: Si se rota el objeto y luego se traslada, aparecerá el objeto (rotado sobre si mismo) en
el punto correspondiente a la traslación, en cambio, si primero se traslada y luego se rota, se
obtiene algo totalmente distinto, pues el objeto no rota sobre si mismo, sino que lo hace
respecto al origen de coordenadas de la escena. Esto es debido a que el eje sobre el que se
realiza la rotación pasa por el origen de coordenadas, que será el origen relativo del objeto
o el global de las escena. Si se traslada el objeto, el origen de coordenadas correspondera a
un punto distinto del objeto.
Todo esta listo para construir la matriz de transformación, para ello utilizo las matrices Rx,
Ry, Rz y Tr :
void matriz_multiplicacion(MATRIZ *m1, MATRIZ *m2, MATRIZ *out)
que efectua el producto de dos matrices m1, m2, de 4x4 y almacena el resultado en una
matriz out de 4x4.
temp = Rx x Ry
matriz_multiplicacion(&Rx, &Ry, &temp)
Rot = temp x Rz
matriz_multiplicacion(&temp, &Rz, &Rot)
Mat = Rot x Tr
matriz_multiplicacion(&Rot, &Tr, &Mat)
donde temp, Rot y Mat son matrices de 4x4, Mat es la matriz de transformación.
Proyección en Perspectiva
Una vez construida la matriz de transformación Mat, tenemos que aplicarla a todos los
vertices del objeto, utilizamos para ello :
aplicar_matriz(&Mat, points[i].x, points[i].y, points[i].z, &outpoints[i].x,
&outpoints[i].y, &outpoints[i].z)
me multiplica cada uno de los vertices del objeto (points[]) por la matriz de transformación
Mat, el resultado es el vertice transformado (outpoints[]).
Proyección en Perspectiva
Primero realizó una clasificación por profundidad para cada polígono utilizando qsort,
ordenación rapida, y usando las coordenadas z. Luego para cada uno de estos polígonos uso
el algoritmo de Backface-Culling para determinar si es visible o no por el observador.
Backface-Culling de poligonos
Para determinar que polígonos son visibles desde el punto de vista del observador debemos
realizar los siguientes pasos :
A partir de los 3 vertices del poligono (V1, V2, V3)se calcula el (vector normal) al
poligono (mediante el producto vectorial).
Calcular el producto escalar de dicho vector con el vector que va desde el centro del
poligono hasta el punto de observacion (Vector de la vista del poligono) el cual es
un vector untitario.
Si el producto escalar es positivo, el angulo entre dichos vectores es inferior a 90
grados, luego la superficie es visible.
producto_vectorial(u, v, &out);
temp = producto_escalar(out.x, out.y, out.z, 0.0, 0.0, -1.0);
return temp;
}
Solo funciona bien si el modelo consiste en un unico solido convexo (esfera), pues si hay
mas de un solido, o si este no es convexo (toro), un poligono puede tapar parcialmente a
otro poligono (ambos visibles), por lo que es necesario utilizar otro metodo
complementario.
Consiste en trazar primero los poligonos situados más lejos. Para ello se ordenan los
poligonos en función de su profundidad media, utilizo para ello el metodo de ordenacion
rapida qsort.
No siempre funciona bien, porque no siempre todos los vertices de un poligono estan mas
cerca o mas lejos que todos los vertices de otro poligono. Aunque es inexacto
(especialmente en poligono de gran tamaño).
Iluminación
Este efecto se logra pintando los poligonos con diferentes tonos de grises, para ello utilizo
el vector normal al poligono, el cual tiene que ser normalizado, es decir dividir el vector por
su modulo, para que sea unitario,y el vector de componentes xlight, ylight, zlight que son
las coordenadas de la fuente de luz, este vector tambien tiene que ser normalizado, realizo
el producto_escalar entre ambos vectores el valor que me da como resultado me indica la
cantidad de luz que recibe cada poligono, 0.0 intensidad minima y 1.0 maxima
intensidad.
Utilizo una paleta de 256 colores, en este caso son niveles de grises 64 en total, como el
nivel de luz varia entre 0.0 y 1.0, lo escalo multiplicandolo por 256.0, que es la cantidad de
colores que tiene mi paleta.
La siguiente funcion me calcula el nivel de luz, que recibe un poligono.
Implementación en C
Estructura del archivo de texto toro.dat que contiene los datos del objeto.
. . .....
#include <graphics.h>
#include <stdlib.h>
#include <math.h>
#include <stdio.h>
#include <conio.h>
#include <dos.h>
#include "svga256.h"
VTX points[NUM_VERTICES];
VTX outpoints[NUM_VERTICES];
TRI faces[NUM_FACES];
LSTRI ListFaces[NUM_FACES];
int vertices, poligonos;
//posicion de la fuente de luz
double xlight=1.0, ylight=-1.0, zlight=1.0;
unsigned char paleta[256][3];
MATRIZ matriz_identidad = {
{
{ 1.0, 0.0, 0.0, 0.0},
{ 0.0, 1.0, 0.0, 0.0},
{ 0.0, 0.0, 1.0, 0.0},
{ 0.0, 0.0, 0.0, 1.0}
}
};
double persp_xoffset;
double persp_yoffset;
double D;
clrscr();
printf("Selecciona un modo de video...\n");
printf(" 0) 320x200x256 VGA\n");
printf(" 1) 640x400x256 SVGA\n");
printf(" 2) 640x480x256 SVGA\n");
printf(" 3) 800x600x256 SVGA\n");
printf(" 4) 1024x768x256 SVGA\n\n>");
scanf("%d", &modo);
return modo;
}
tmpsin=sin(angulo);
tmpcos=cos(angulo);
*m = matriz_identidad;
m->v[1][1] = tmpcos;
m->v[1][2] = tmpsin;
m->v[2][1] = -tmpsin;
m->v[2][2] = tmpcos;
}
tmpsin=sin(angulo);
tmpcos=cos(angulo);
*m = matriz_identidad;
m->v[0][0] = tmpcos;
m->v[0][2] = -tmpsin;
m->v[2][0] = tmpsin;
m->v[2][2] = tmpcos;
}
tmpsin=sin(angulo);
tmpcos=cos(angulo);
*m = matriz_identidad;
m->v[0][0] = tmpcos;
m->v[0][1] = tmpsin;
m->v[1][0] = -tmpsin;
m->v[1][1] = tmpcos;
}
double producto_escalar(double x1, double y1, double z1, double x2, double y2, double z2)
{
return ((x1 * x2) + (y1 * y2) + (z1 * z2));
}
*x *= length;
*y *= length;
*z *= length;
}
i=ListFaces[num].index;
A=faces[i].a;
B=faces[i].b;
C=faces[i].c;
//verifico si el poligono es visible o no
if (Hidden(outpoints[A], outpoints[B], outpoints[C])<0.0)
{
//se llama a la funci¢n que calcula la luz de un pol¡gono
color = Light(outpoints[A], outpoints[B], outpoints[C]);
poly[0]=(int) outpoints[A].x;
poly[1]=(int) outpoints[A].y;
poly[2]=(int) outpoints[B].x;
poly[3]=(int) outpoints[B].y;
poly[4]=(int) outpoints[C].x;
poly[5]=(int) outpoints[C].y;
setfillstyle(SOLID_FILL, color);
setcolor(color);
fillpoly(3, poly);
}
}
d1=q1->d;
d2=q2->d;
return (d2-d1);
}
reg.r_ax = 0x1012;
reg.r_bx = 0;
reg.r_cx = 256;
reg.r_es = FP_SEG(PalBuf);
reg.r_dx = FP_OFF(PalBuf);
intr(0x10,®);
}
int main(void)
{
MATRIZ Rx, Ry, Rz, Rot;
MATRIZ Tr, Mat;
MATRIZ temp;
int i;
set_projeccion_viewport(getmaxx(), getmaxy());
getch();
closegraph();
return 0;
}