00001 //=========================================================================== 00002 //= raytrace.cc Marzo de 1999 = 00003 //=-------------------------------------------------------------------------= 00004 //= Especifica el raytracer "criollo" que genera IMAGENes de una manera = 00005 //= generica (independiente del disenno de la aplicacion), basandose en los = 00006 //= elementos basicos GEOMETRIA, LUZ y MATERIAL del framework AQUYNZA. = 00007 //=-------------------------------------------------------------------------= 00008 //= ADVERTENCIA: ESTE SOFTWARE NO ESTA CONCEBIDO NI DISENNADO PARA EL USO = 00009 //= EN EQUIPO DE CONTROL EN LINEA EN ENTORNOS PELIGROSOS QUE REQUIERAN UN = 00010 //= DESEMPENNO LIBRE DE FALLAS, COMO LA OPERACION DE PLANTAS NUCLEARES, = 00011 //= SISTEMAS DE NAVEGACION O COMUNICACION EN AVIONES, TRAFICO AEREO, = 00012 //= EQUIPO MEDICO DEL CUAL DEPENDAN VIDAS HUMANAS O SISTEMAS DE ARMAMENTO, = 00013 //= EN LOS CUALES UNA FALLA EN EL SOFTWARE PUEDA IMPLICAR DIRECTAMENTE LA = 00014 //= MUERTE, DANNOS PERSONALES O DANNOS FISICOS Y/O AMBIENTALES GRAVES = 00015 //= ("ACTIVIDADES DE ALGO RIESGO"). = 00016 //=-------------------------------------------------------------------------= 00017 //= Autor original: Oscar J. Chavarro G. A.K.A. JEDILINK. Copyright (c), = 00018 //= 1997 - 2003, oscarchavarro@hotmail.com = 00019 //= AQUYNZA es software libre, y se rige bajo los terminos de la licencia = 00020 //= LGPL de GNU (http://www.gnu.org). Para mayor informacion respecto a la = 00021 //= licencia de uso, consulte el archivo ./doc/LICENCIA en la distribucion. = 00022 //=========================================================================== 00023 00024 #include "framework/visual/raytrace.h" 00025 #include "arreglo.cc" 00026 00027 //=========================================================================== 00028 //= CLASE RAYTRACER_CRIOLLO = 00029 //=========================================================================== 00030 00031 RAYTRACER_CRIOLLO::RAYTRACER_CRIOLLO() 00032 { 00033 ; 00034 } 00035 00036 RAYTRACER_CRIOLLO::~RAYTRACER_CRIOLLO() 00037 { 00038 ; 00039 } 00040 00041 void 00042 RAYTRACER_CRIOLLO::ejecutar(IMAGEN_RGB *Resultado, 00043 ARREGLO <OBJETO_RAYABLE *> &arr_objetos, 00044 LUZ **LUces, CAMARA *Camara) 00058 { 00059 //----------------------------------------------------------------------- 00060 //#define DEBUG_RAYTRACER 00061 #ifdef DEBUG_RAYTRACER 00062 static BOOLEAN primiparo = TRUE; 00063 int i; 00064 00065 if ( primiparo ) { 00066 printf("<RAYTRACER> Tengo estos objetos:\n"); 00067 for ( i = 0; i < arr_objetos.tam(); i++ ) { 00068 printf("[%d]:%p\n", i, arr_objetos[i]->Geometria); 00069 } 00070 fflush(stdout);; 00071 primiparo = FALSE; 00072 } 00073 #endif 00074 00075 //----------------------------------------------------------------------- 00076 int x, y; 00077 RAYO rayo; 00078 // No queremos que se llame mucho la constructora, por eso "static"... 00079 static COLOR color; 00080 00081 printf("["); 00082 nivel_de_recursion = 0; 00083 Camara->preprocesar_vista(); 00084 for ( y = 0; y < Resultado->ytam(); y++ ) { 00085 if ( (y % 10) == 0 ) { printf("."); fflush(stdout); } 00086 for ( x = 0; x < Resultado->xtam(); x++ ) { 00087 //- Trazado individual de un rayo ------------------------------ 00088 rayo = Camara->generar_rayo(x, y); 00089 color = seguimiento_rayo(&rayo, arr_objetos, LUces); 00090 00091 //- Proceso de control de la saturacion de energia luminica ---- 00092 // OJO: Esto deberia mejorarse para lograr efectos de camara... 00093 if ( color.r > 1 ) color.r = 1.0; 00094 if ( color.g > 1 ) color.g = 1.0; 00095 if ( color.b > 1 ) color.b = 1.0; 00096 00097 //- Exporto el resultado de color del pixel -------------------- 00098 Resultado->putpixel(x, y, (BYTE)(255 * color.r), 00099 (BYTE)(255 * color.g), 00100 (BYTE)(255 * color.b)); 00101 } 00102 } 00103 printf("]"); 00104 fflush(stdout); 00105 } 00106 00107 COLOR 00108 RAYTRACER_CRIOLLO::modelo_de_iluminacion(const OBJETO_RAYABLE *Objeto, 00109 ARREGLO <OBJETO_RAYABLE *> &objetos, RAYO *Rayo, const VECTOR &punto, 00110 VECTOR &normal, LUZ **LUces) 00132 { 00133 COLOR color; // OJO: Depende del valor por defecto! 00134 double K; 00135 RAYO rayo_reflejado; 00136 00137 /*---------------------------------------------------------------------- 00138 Notese que respecto a la figura 16.12 de [FOLE92], `*Rayo` (que es 00139 un rayo que va de la camara al objeto) es equivalente a -V (un rayo 00140 con la direccion inversa a V). 00141 00142 Se calcula el rayo reflejado R con la normal N y el rayo incidente V. 00143 La formula para el rayo reflejado es generalmente : 00144 R = 2(V dot N)N - V 00145 Asumiendo que V apunta hacia afuera del punto de interseccion, 00146 cosa que no es cierta en este modelo ya que el rayo se origina 00147 en el ojo. Por lo tanto se debe invertir V para que cuadre en el 00148 modelo. Para lograrlo escalamos V por -1.0. El resultado es: 00149 R = -2N(V dot N) + V 00150 que es la formula que usaremos aqui. 00151 El rayo reflejado es aquel que sera rastreado para encontrar las 00152 contribuciones de color para el punto que sera iluminado si la 00153 superficie que sera iluminada es reflectiva. El rayo reflejado comienza 00154 en el punto `punto` y tiene direccion fuera de la superficie. 00155 ----------------------------------------------------------------------*/ 00156 00157 // Notese que no es necesario normalizar la direccion del rayo reflejado 00158 // debido a que V y N son vectores unitarios... 00159 K = Rayo->direccion.producto_punto(normal); 00160 K *= -2.0; 00161 rayo_reflejado.origen = punto; 00162 rayo_reflejado.direccion = normal * K + Rayo->direccion; 00163 00164 /*- 1. ----------------------------------------------------------------- 00165 Comienza el calculo del color con la contribucion del ambiente. 00166 Esta luz es la que esta en todas partes en la escena. 00167 ----------------------------------------------------------------------*/ 00168 double Ia = 0.3; // OJO: Deberia ser configurable! 00169 00170 color.r = (FLOAT32)(Objeto->ambiente.r * Ia); 00171 color.g = (FLOAT32)(Objeto->ambiente.g * Ia); 00172 color.b = (FLOAT32)(Objeto->ambiente.b * Ia); 00173 00174 /*- 2. ----------------------------------------------------------------- 00175 Aqui se calcula la sumatoria de las contribuciones de todas las fuentes 00176 de luz. 00177 ----------------------------------------------------------------------*/ 00178 int i; 00179 LUZ *Luz; 00180 double NpuntoL, Ks; //, DistanceT; 00181 // No queremos que se llame mucho la constructora, por eso "static"... 00182 static COLOR color_de_la_luz; 00183 RAYO L; // El rayo unitario con direccion del punto de l objeto a a 00184 // la luz, como se muestra en la figura 16.1 de [FOLE92] 00185 00186 for ( i = 0; LUces[i]; i++ ) { 00187 Luz = LUces[i]; 00188 00189 /* 2.1. Generamos un rayo desde el punto `punto` a la luz de interes. 00190 DistanceT es la longitud del rayo desde punto a la fuente de luz. 00191 Se llama GetLightColor para ver si hay objetos que bloquean a 00192 lo largo de este rayo. */ 00193 00194 L.origen = punto; 00195 L.direccion = Luz->posicion() - punto; 00196 //DistanceT = MAGNITUD(L.direccion); 00197 L.direccion.normalizar(); 00198 00199 // OJO: Sin esto no saldran sombras! 00200 //color_de_la_luz = GetLightColor(Luz, Objeto, 00201 //&L, DistanceT); 00202 color_de_la_luz.r = color_de_la_luz.g = color_de_la_luz.b = 1.0; 00203 00204 /* 2.2. Acumulacion de la contribucion de reflexion difusa 00205 (Lambertiana) */ 00206 00207 // Se chequea para ver si el objeto da la cara a la luz 00208 NpuntoL = normal.producto_punto(L.direccion); 00209 00210 // OJO: En el modelo de iluminacion de referencia elaborado en 00211 // Pixar / Renderman, se considera una operacion "faceforward" 00212 // que hace que se muestren los dos lados de un objeto... 00213 if ( NpuntoL > 0.0 ) { 00214 // Respecto a la ecuacion (16.4) de [FOLE92], color_de_la_luz.p 00215 // corresponde a Ip (para p en `r`, `g` o `b`), y 00216 // Objeto->difusa.* por cada canal RGB corresponde a Kd. 00217 // La contribucion de reflexion difusa color.* corresponde a I 00218 color.r += (FLOAT32)(color_de_la_luz.r*Objeto->difusa.r*NpuntoL); 00219 color.g += (FLOAT32)(color_de_la_luz.g*Objeto->difusa.g*NpuntoL); 00220 color.b += (FLOAT32)(color_de_la_luz.b*Objeto->difusa.b*NpuntoL); 00221 } 00222 else { 00223 // Caso de normal invertida ... 00224 color.r -= (FLOAT32)(color_de_la_luz.r*Objeto->difusa.r*NpuntoL); 00225 color.g -= (FLOAT32)(color_de_la_luz.g*Objeto->difusa.g*NpuntoL); 00226 color.b -= (FLOAT32)(color_de_la_luz.b*Objeto->difusa.b*NpuntoL); 00227 } 00228 00229 /* Usualmente el componente especular observando como el rayo inci- 00230 dente V se alinea con el rayo reflejado. Entre mas cercanos, 00231 mayor la contribucion hecha al color del objeto en punto. 00232 En este caso haremos la misma cosa comparando como el rayo 00233 incidente reflejado calulado antes y llamado rayo_reflejado 00234 se alinea con L. Esto requiere menos trabajo ya que 00235 rayo_reflejado y L apuntan en la misma direccion. Esto 00236 funciona por que el angulo entre los dos conjuntos de rayos 00237 es el mismo. */ 00238 double afinidad; 00239 afinidad = rayo_reflejado.direccion.producto_punto(L.direccion); 00240 if ( afinidad > 0.001 ) { // OJO: 0.001 porque hay lios de presicion 00241 Ks = Objeto->phong_coef * pow(afinidad, Objeto->phong_exp); 00242 color.r += (FLOAT32)(color_de_la_luz.r * Objeto->especular.r * Ks); 00243 color.g += (FLOAT32)(color_de_la_luz.g * Objeto->especular.g * Ks); 00244 color.b += (FLOAT32)(color_de_la_luz.b * Objeto->especular.b * Ks); 00245 } 00246 } 00247 00248 /*- 3. Ahora se considera reflexion ---------------------------------*/ 00249 // No queremos que se llame mucho la constructora, por eso "static"... 00250 static COLOR color_recursion; 00251 double Krl; 00252 00253 Krl = Objeto->coeficiente_de_reflexion; 00254 if ( K > 0.0 ) { 00255 nivel_de_recursion++; 00256 // Recordar de mas arriba que rayo_reflejado es unitario, loego se 00257 // cumple la precondicion de `seguimiento_rayo` 00258 color_recursion = seguimiento_rayo(&rayo_reflejado, objetos, LUces); 00259 color.r += (FLOAT32)(color_recursion.r * Krl); 00260 color.g += (FLOAT32)(color_recursion.g * Krl); 00261 color.b += (FLOAT32)(color_recursion.b * Krl); 00262 nivel_de_recursion--; 00263 } 00264 00265 return color; 00266 } 00267 00268 COLOR 00269 RAYTRACER_CRIOLLO::seguimiento_rayo(RAYO *Rayo, 00270 ARREGLO <OBJETO_RAYABLE *> &objetos, 00271 LUZ **LUces) 00302 { 00303 VECTOR punto, normal, p, n; 00304 // No queremos que se llame mucho la constructora, por eso "static"... 00305 static COLOR color; 00306 00307 //- Caso 1: limite de la recursion (Fuerza Bruta) ---------------------- 00308 if ( nivel_de_recursion > MAXRECURSELEVEL ) { 00309 color.r = color.g = color.b = 0; // Notese que "negro" significa que 00310 return color; // "no hay contribucion de color". 00311 } 00312 00313 //- Busqueda de el objeto mas cercano que intersecte el rayo ----------- 00314 //- NOTA: Si T == 0 significa que no lo intersecto. - 00315 OBJETO_RAYABLE *Objeto_mas_cercano = NULL; 00316 double T, distancia_minima = INFINITO; 00317 int i; 00318 VECTOR po; 00319 RAYO mi_rayo; 00320 00321 // OJO: No ayudaria usar QUATERNIONs? 00322 00323 for ( i = 0; i < objetos.tam(); i++ ) { 00324 po = objetos[i]->posicion; 00325 mi_rayo.origen = Rayo->origen - po; 00326 mi_rayo.origen = objetos[i]->R_i * mi_rayo.origen; 00327 mi_rayo.direccion = objetos[i]->R_i * Rayo->direccion; 00328 // Notese mi_rayo.direccion es unitario si Rayo->direccion es 00329 // unitario (y lo es pues ese vector no cambia dentro de este ciclo y 00330 // es unitario dada la precondicion) y si R_i es una matriz ortogonal 00331 // juiciosa (debe venir normalizada!) 00332 00333 // OJO: Las operaciones de interseccion deben calcular normales 00334 // unitarias! 00335 T = objetos[i]->Geometria->interseccion(&mi_rayo, p, n); 00336 if ( (T > EPSILON) && (T < distancia_minima) ) { 00337 Objeto_mas_cercano = objetos[i]; 00338 distancia_minima = T; 00339 punto = objetos[i]->R * p; 00340 punto = punto + po; 00341 normal = objetos[i]->R * n; 00342 // OJO: La normal debe ser unitaria para poder evitar esta linea! 00343 //normal.normalizar(); 00344 } 00345 } 00346 00347 //- Caso 2: Si nada fue intersectado retorna el color del fondo --------- 00348 if ( !Objeto_mas_cercano ) { 00349 // OJO: Deberia mejorarse para enmascarar otra imagen, que contiene 00350 // un fondo complejo preprocesado... (tal vez con depth-buffer?) 00351 color.r = 0; // Tenga en cuenta que este color sera reflejado por 00352 color.g = 0; // los objetos, alterando la apariencia de los mismos. 00353 color.b = 0; 00354 return color; 00355 } 00356 00357 //- Caso 3: Se intersecto un objeto ------------------------------------- 00358 double direccion_normal; 00359 00360 //- Chequeamos para ver si la normal apunta hacia el Rayo. Si no, se --- 00361 //- invierte la direccion de la normal para hacer a las superficies - 00362 //- visibles por sus dos caras. - 00363 direccion_normal = normal.producto_punto(Rayo->direccion); 00364 if ( direccion_normal > EPSILON ) normal = normal * -1; 00365 00366 // OJO: Este proceso se aislo por claridad, y llama a este metodo, 00367 // haciendo el proceso recursivo 00368 return modelo_de_iluminacion(Objeto_mas_cercano, objetos, 00369 Rayo, punto, normal, LUces); 00370 } 00371 00372 //=========================================================================== 00373 //= EOF = 00374 //=========================================================================== 00375