1 /* $Id: shade.c,v 1.10 1997/12/18 02:54:48 brianp Exp $ */ 2 3 /* 4 * Mesa 3-D graphics library 5 * Version: 2.6 6 * Copyright (C) 1995-1997 Brian Paul 7 * 8 * This library is free software; you can redistribute it and/or 9 * modify it under the terms of the GNU Library General Public 10 * License as published by the Free Software Foundation; either 11 * version 2 of the License, or (at your option) any later version. 12 * 13 * This library is distributed in the hope that it will be useful, 14 * but WITHOUT ANY WARRANTY; without even the implied warranty of 15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 16 * Library General Public License for more details. 17 * 18 * You should have received a copy of the GNU Library General Public 19 * License along with this library; if not, write to the Free 20 * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 21 */ 22 23 24 /* 25 * $Log: shade.c,v $ 26 * Revision 1.10 1997/12/18 02:54:48 brianp 27 * now using FloatToInt() macro for better performance on x86 28 * 29 * Revision 1.9 1997/07/24 01:21:56 brianp 30 * changed precompiled header symbol from PCH to PC_HEADER 31 * 32 * Revision 1.8 1997/07/09 03:04:44 brianp 33 * fixed bug in gl_color_shade_vertices() with GL_COLOR_MATERIAL 34 * 35 * Revision 1.7 1997/07/05 16:24:26 brianp 36 * fixed FP underflow problem in pow(). Renamed h[xyz] to h_[xyz]. 37 * 38 * Revision 1.6 1997/06/20 04:15:43 brianp 39 * optimized changing of SHININESS (Henk Kok) 40 * 41 * Revision 1.5 1997/06/20 02:28:40 brianp 42 * changed color components from GLfixed to GLubyte 43 * 44 * Revision 1.4 1997/05/28 03:26:29 brianp 45 * added precompiled header (PCH) support 46 * 47 * Revision 1.3 1997/05/23 03:01:18 brianp 48 * commented out a few const keywords because IRIX cc chokes on them 49 * 50 * Revision 1.2 1997/05/09 02:41:08 brianp 51 * call GL_SQRT() instead of sqrt() 52 * 53 * Revision 1.1 1997/04/01 04:11:04 brianp 54 * Initial revision 55 * 56 */ 57 58 59 #ifdef PC_HEADER 60 #include "all.h" 61 #else 62 #include <math.h> 63 #include "macros.h" 64 #include "mmath.h" 65 #include "shade.h" 66 #include "types.h" 67 #endif 68 69 70 71 /* 72 * Return x^y. 73 */ 74 static GLfloat gl_pow( GLfloat x, GLfloat y ) 75 { 76 GLdouble z = pow(x, y); 77 if (z<1.0e-10) 78 return 0.0F; 79 else 80 return (GLfloat) z; 81 } 82 83 84 85 /* 86 * Use current lighting/material settings to compute the RGBA colors of 87 * an array of vertexes. 88 * Input: side - 0=use front material, 1=use back material 89 * n - number of vertexes to process 90 * vertex - array of vertex positions in eye coordinates 91 * normal - array of surface normal vectors 92 * Output: color - array of resulting colors 93 */ 94 void gl_color_shade_vertices( GLcontext *ctx, 95 GLuint side, 96 GLuint n, 97 /*const*/ GLfloat vertex[][4], 98 /*const*/ GLfloat normal[][3], 99 GLubyte color[][4] ) 100 { 101 GLint j; 102 GLfloat rscale, gscale, bscale, ascale; 103 GLfloat baseR, baseG, baseB, baseA; 104 GLint sumA; 105 struct gl_light *light; 106 struct gl_material *mat; 107 108 /* Compute scale factor to go from floats in [0,1] to integers or fixed 109 * point values: 110 */ 111 rscale = ctx->Visual->RedScale; 112 gscale = ctx->Visual->GreenScale; 113 bscale = ctx->Visual->BlueScale; 114 ascale = ctx->Visual->AlphaScale; 115 116 mat = &ctx->Light.Material[side]; 117 118 /*** Compute color contribution from global lighting ***/ 119 baseR = mat->Emission[0] + ctx->Light.Model.Ambient[0] * mat->Ambient[0]; 120 baseG = mat->Emission[1] + ctx->Light.Model.Ambient[1] * mat->Ambient[1]; 121 baseB = mat->Emission[2] + ctx->Light.Model.Ambient[2] * mat->Ambient[2]; 122 baseA = mat->Diffuse[3]; /* Alpha is simple, same for all vertices */ 123 124 sumA = (GLint) (CLAMP( baseA, 0.0F, 1.0F ) * ascale); 125 126 for (j=0;j<n;j++) { 127 GLfloat sumR, sumG, sumB; 128 GLfloat nx, ny, nz; 129 130 if (side==0) { 131 /* shade frontside */ 132 nx = normal[j][0]; 133 ny = normal[j][1]; 134 nz = normal[j][2]; 135 } 136 else { 137 /* shade backside */ 138 nx = -normal[j][0]; 139 ny = -normal[j][1]; 140 nz = -normal[j][2]; 141 } 142 143 sumR = baseR; 144 sumG = baseG; 145 sumB = baseB; 146 147 /* Add contribution from each enabled light source */ 148 for (light=ctx->Light.FirstEnabled; light; light=light->NextEnabled) { 149 GLfloat ambientR, ambientG, ambientB; 150 GLfloat attenuation, spot; 151 GLfloat VPx, VPy, VPz; /* unit vector from vertex to light */ 152 GLfloat n_dot_VP; /* n dot VP */ 153 154 /* compute VP and attenuation */ 155 if (light->Position[3]==0.0) { 156 /* directional light */ 157 VPx = light->VP_inf_norm[0]; 158 VPy = light->VP_inf_norm[1]; 159 VPz = light->VP_inf_norm[2]; 160 attenuation = 1.0F; 161 } 162 else { 163 /* positional light */ 164 GLfloat d; /* distance from vertex to light */ 165 VPx = light->Position[0] - vertex[j][0]; 166 VPy = light->Position[1] - vertex[j][1]; 167 VPz = light->Position[2] - vertex[j][2]; 168 d = (GLfloat) GL_SQRT( VPx*VPx + VPy*VPy + VPz*VPz ); 169 if (d>0.001F) { 170 GLfloat invd = 1.0F / d; 171 VPx *= invd; 172 VPy *= invd; 173 VPz *= invd; 174 } 175 attenuation = 1.0F / (light->ConstantAttenuation 176 + d * (light->LinearAttenuation 177 + d * light->QuadraticAttenuation)); 178 } 179 180 /* spotlight factor */ 181 if (light->SpotCutoff==180.0F) { 182 /* not a spot light */ 183 spot = 1.0F; 184 } 185 else { 186 GLfloat PVx, PVy, PVz, PV_dot_dir; 187 PVx = -VPx; 188 PVy = -VPy; 189 PVz = -VPz; 190 PV_dot_dir = PVx*light->NormDirection[0] 191 + PVy*light->NormDirection[1] 192 + PVz*light->NormDirection[2]; 193 if (PV_dot_dir<=0.0F || PV_dot_dir<light->CosCutoff) { 194 /* outside of cone */ 195 spot = 0.0F; 196 } 197 else { 198 double x = PV_dot_dir * (EXP_TABLE_SIZE-1); 199 int k = (int) x; 200 spot = light->SpotExpTable[k][0] 201 + (x-k)*light->SpotExpTable[k][1]; 202 } 203 } 204 205 ambientR = mat->Ambient[0] * light->Ambient[0]; 206 ambientG = mat->Ambient[1] * light->Ambient[1]; 207 ambientB = mat->Ambient[2] * light->Ambient[2]; 208 209 /* Compute dot product or normal and vector from V to light pos */ 210 n_dot_VP = nx * VPx + ny * VPy + nz * VPz; 211 212 /* diffuse and specular terms */ 213 if (n_dot_VP<=0.0F) { 214 /* surface face away from light, no diffuse or specular */ 215 GLfloat t = attenuation * spot; 216 sumR += t * ambientR; 217 sumG += t * ambientG; 218 sumB += t * ambientB; 219 /* done with this light */ 220 } 221 else { 222 GLfloat diffuseR, diffuseG, diffuseB; 223 GLfloat specularR, specularG, specularB; 224 GLfloat h_x, h_y, h_z, n_dot_h, t; 225 226 /* diffuse term */ 227 diffuseR = n_dot_VP * mat->Diffuse[0] * light->Diffuse[0]; 228 diffuseG = n_dot_VP * mat->Diffuse[1] * light->Diffuse[1]; 229 diffuseB = n_dot_VP * mat->Diffuse[2] * light->Diffuse[2]; 230 231 /* specular term */ 232 if (ctx->Light.Model.LocalViewer) { 233 GLfloat vx, vy, vz, vlen; 234 vx = vertex[j][0]; 235 vy = vertex[j][1]; 236 vz = vertex[j][2]; 237 vlen = GL_SQRT( vx*vx + vy*vy + vz*vz ); 238 if (vlen>0.0001F) { 239 GLfloat invlen = 1.0F / vlen; 240 vx *= invlen; 241 vy *= invlen; 242 vz *= invlen; 243 } 244 /* h = VP + VPe */ 245 h_x = VPx - vx; 246 h_y = VPy - vy; 247 h_z = VPz - vz; 248 } 249 else { 250 /* h = VP + <0,0,1> */ 251 h_x = VPx; 252 h_y = VPy; 253 h_z = VPz + 1.0F; 254 } 255 256 /* attention: h is not normalized, done later if needed */ 257 n_dot_h = nx*h_x + ny*h_y + nz*h_z; 258 259 if (n_dot_h<=0.0F) { 260 specularR = 0.0F; 261 specularG = 0.0F; 262 specularB = 0.0F; 263 } 264 else { 265 GLfloat spec_coef; 266 /* now `correct' the dot product */ 267 n_dot_h = n_dot_h / GL_SQRT( h_x*h_x + h_y*h_y + h_z*h_z ); 268 if (n_dot_h>1.0F) { 269 /* only happens if normal vector length > 1.0 */ 270 spec_coef = pow( n_dot_h, mat->Shininess ); 271 } 272 else { 273 /* use table lookup approximation */ 274 int k = (int) (n_dot_h * (GLfloat) (SHINE_TABLE_SIZE-1)); 275 if (mat->ShineTable[k] < 0.0F) 276 mat->ShineTable[k] = gl_pow( n_dot_h, mat->Shininess ); 277 spec_coef = mat->ShineTable[k]; 278 } 279 if (spec_coef<1.0e-10) { 280 specularR = 0.0F; 281 specularG = 0.0F; 282 specularB = 0.0F; 283 } 284 else { 285 specularR = spec_coef * mat->Specular[0]*light->Specular[0]; 286 specularG = spec_coef * mat->Specular[1]*light->Specular[1]; 287 specularB = spec_coef * mat->Specular[2]*light->Specular[2]; 288 } 289 } 290 291 t = attenuation * spot; 292 sumR += t * (ambientR + diffuseR + specularR); 293 sumG += t * (ambientG + diffuseG + specularG); 294 sumB += t * (ambientB + diffuseB + specularB); 295 } 296 297 } /*loop over lights*/ 298 299 /* clamp and convert to integer or fixed point */ 300 color[j][0] = FloatToInt(CLAMP( sumR, 0.0F, 1.0F ) * rscale); 301 color[j][1] = FloatToInt(CLAMP( sumG, 0.0F, 1.0F ) * gscale); 302 color[j][2] = FloatToInt(CLAMP( sumB, 0.0F, 1.0F ) * bscale); 303 color[j][3] = sumA; 304 305 } /*loop over vertices*/ 306 } 307 308 309 310 /* 311 * This is an optimized version of the above function. 312 */ 313 void gl_color_shade_vertices_fast( GLcontext *ctx, 314 GLuint side, 315 GLuint n, 316 /*const*/ GLfloat normal[][3], 317 GLubyte color[][4] ) 318 { 319 GLint j; 320 GLfloat rscale, gscale, bscale, ascale; 321 GLint sumA; 322 GLfloat *baseColor = ctx->Light.BaseColor[side]; 323 324 /* Compute scale factor to go from floats in [0,1] to integers or fixed 325 * point values: 326 */ 327 rscale = ctx->Visual->RedScale; 328 gscale = ctx->Visual->GreenScale; 329 bscale = ctx->Visual->BlueScale; 330 ascale = ctx->Visual->AlphaScale; 331 332 /* Alpha is easy to compute, same for all vertices */ 333 sumA = (GLint) (baseColor[3] * ascale); 334 335 /* Loop over vertices */ 336 for (j=0;j<n;j++) { 337 GLfloat sumR, sumG, sumB; 338 GLfloat nx, ny, nz; 339 struct gl_light *light; 340 341 /* the normal vector */ 342 if (side==0) { 343 nx = normal[j][0]; 344 ny = normal[j][1]; 345 nz = normal[j][2]; 346 } 347 else { 348 nx = -normal[j][0]; 349 ny = -normal[j][1]; 350 nz = -normal[j][2]; 351 } 352 353 #ifdef SPEED_HACK 354 if (nz<0.0F) { 355 color[j][0] = 0.0F; 356 color[j][1] = 0.0F; 357 color[j][2] = 0.0F; 358 color[j][3] = A; 359 continue; 360 } 361 #endif 362 363 /* base color from global illumination and enabled light's ambient */ 364 sumR = baseColor[0]; 365 sumG = baseColor[1]; 366 sumB = baseColor[2]; 367 368 /* Add contribution from each light source */ 369 for (light=ctx->Light.FirstEnabled; light; light=light->NextEnabled) { 370 GLfloat n_dot_VP; /* n dot VP */ 371 372 n_dot_VP = nx * light->VP_inf_norm[0] 373 + ny * light->VP_inf_norm[1] 374 + nz * light->VP_inf_norm[2]; 375 376 /* diffuse and specular terms */ 377 if (n_dot_VP>0.0F) { 378 GLfloat n_dot_h; 379 GLfloat *lightMatDiffuse = light->MatDiffuse[side]; 380 381 /** add diffuse term **/ 382 sumR += n_dot_VP * lightMatDiffuse[0]; 383 sumG += n_dot_VP * lightMatDiffuse[1]; 384 sumB += n_dot_VP * lightMatDiffuse[2]; 385 386 /** specular term **/ 387 /* dot product of n and h_inf_norm */ 388 n_dot_h = nx * light->h_inf_norm[0] 389 + ny * light->h_inf_norm[1] 390 + nz * light->h_inf_norm[2]; 391 if (n_dot_h>0.0F) { 392 if (n_dot_h>1.0F) { 393 /* only happens if Magnitude(n) > 1.0 */ 394 GLfloat spec_coef = pow( n_dot_h, 395 ctx->Light.Material[side].Shininess ); 396 if (spec_coef>1.0e-10F) { 397 sumR += spec_coef * light->MatSpecular[side][0]; 398 sumG += spec_coef * light->MatSpecular[side][1]; 399 sumB += spec_coef * light->MatSpecular[side][2]; 400 } 401 } 402 else { 403 /* use table lookup approximation */ 404 int k = (int) (n_dot_h * (GLfloat) (SHINE_TABLE_SIZE-1)); 405 struct gl_material *m = &ctx->Light.Material[side]; 406 GLfloat spec_coef; 407 if (m->ShineTable[k] < 0.0F) 408 m->ShineTable[k] = gl_pow( n_dot_h, m->Shininess ); 409 spec_coef = m->ShineTable[k]; 410 sumR += spec_coef * light->MatSpecular[side][0]; 411 sumG += spec_coef * light->MatSpecular[side][1]; 412 sumB += spec_coef * light->MatSpecular[side][2]; 413 } 414 } 415 } 416 417 } /*loop over lights*/ 418 419 /* clamp and convert to integer or fixed point */ 420 color[j][0] = FloatToInt(MIN2( sumR, 1.0F ) * rscale); 421 color[j][1] = FloatToInt(MIN2( sumG, 1.0F ) * gscale); 422 color[j][2] = FloatToInt(MIN2( sumB, 1.0F ) * bscale); 423 color[j][3] = sumA; 424 425 } /*loop over vertices*/ 426 } 427 428 429 430 /* 431 * Use current lighting/material settings to compute the color indexes 432 * for an array of vertices. 433 * Input: n - number of vertices to shade 434 * side - 0=use front material, 1=use back material 435 * vertex - array of [n] vertex position in eye coordinates 436 * normal - array of [n] surface normal vector 437 * Output: indexResult - resulting array of [n] color indexes 438 */ 439 void gl_index_shade_vertices( GLcontext *ctx, 440 GLuint side, 441 GLuint n, 442 GLfloat vertex[][4], 443 GLfloat normal[][3], 444 GLuint indexResult[] ) 445 { 446 struct gl_material *mat = &ctx->Light.Material[side]; 447 GLint j; 448 449 /* loop over vertices */ 450 for (j=0;j<n;j++) { 451 GLfloat index; 452 GLfloat diffuse, specular; /* accumulated diffuse and specular */ 453 GLfloat nx, ny, nz; /* normal vector */ 454 struct gl_light *light; 455 456 if (side==0) { 457 /* shade frontside */ 458 nx = normal[j][0]; 459 ny = normal[j][1]; 460 nz = normal[j][2]; 461 } 462 else { 463 /* shade backside */ 464 nx = -normal[j][0]; 465 ny = -normal[j][1]; 466 nz = -normal[j][2]; 467 } 468 469 diffuse = specular = 0.0F; 470 471 /* Accumulate diffuse and specular from each light source */ 472 for (light=ctx->Light.FirstEnabled; light; light=light->NextEnabled) { 473 GLfloat attenuation; 474 GLfloat lx, ly, lz; /* unit vector from vertex to light */ 475 GLfloat l_dot_norm; /* dot product of l and n */ 476 477 /* compute l and attenuation */ 478 if (light->Position[3]==0.0) { 479 /* directional light */ 480 /* Effectively, l is a vector from the origin to the light. */ 481 lx = light->VP_inf_norm[0]; 482 ly = light->VP_inf_norm[1]; 483 lz = light->VP_inf_norm[2]; 484 attenuation = 1.0F; 485 } 486 else { 487 /* positional light */ 488 GLfloat d; /* distance from vertex to light */ 489 lx = light->Position[0] - vertex[j][0]; 490 ly = light->Position[1] - vertex[j][1]; 491 lz = light->Position[2] - vertex[j][2]; 492 d = (GLfloat) GL_SQRT( lx*lx + ly*ly + lz*lz ); 493 if (d>0.001F) { 494 GLfloat invd = 1.0F / d; 495 lx *= invd; 496 ly *= invd; 497 lz *= invd; 498 } 499 attenuation = 1.0F / (light->ConstantAttenuation 500 + d * (light->LinearAttenuation 501 + d * light->QuadraticAttenuation)); 502 } 503 504 l_dot_norm = lx*nx + ly*ny + lz*nz; 505 506 if (l_dot_norm>0.0F) { 507 GLfloat spot_times_atten; 508 509 /* spotlight factor */ 510 if (light->SpotCutoff==180.0F) { 511 /* not a spot light */ 512 spot_times_atten = attenuation; 513 } 514 else { 515 GLfloat v[3], dot; 516 v[0] = -lx; /* v points from light to vertex */ 517 v[1] = -ly; 518 v[2] = -lz; 519 dot = DOT3( v, light->NormDirection ); 520 if (dot<=0.0F || dot<light->CosCutoff) { 521 /* outside of cone */ 522 spot_times_atten = 0.0F; 523 } 524 else { 525 double x = dot * (EXP_TABLE_SIZE-1); 526 int k = (int) x; 527 GLfloat spot = light->SpotExpTable[k][0] 528 + (x-k)*light->SpotExpTable[k][1]; 529 spot_times_atten = spot * attenuation; 530 } 531 } 532 533 /* accumulate diffuse term */ 534 diffuse += l_dot_norm * light->dli * spot_times_atten; 535 536 /* accumulate specular term */ 537 { 538 GLfloat h_x, h_y, h_z, n_dot_h, spec_coef; 539 540 /* specular term */ 541 if (ctx->Light.Model.LocalViewer) { 542 GLfloat vx, vy, vz, vlen; 543 vx = vertex[j][0]; 544 vy = vertex[j][1]; 545 vz = vertex[j][2]; 546 vlen = GL_SQRT( vx*vx + vy*vy + vz*vz ); 547 if (vlen>0.0001F) { 548 GLfloat invlen = 1.0F / vlen; 549 vx *= invlen; 550 vy *= invlen; 551 vz *= invlen; 552 } 553 h_x = lx - vx; 554 h_y = ly - vy; 555 h_z = lz - vz; 556 } 557 else { 558 h_x = lx; 559 h_y = ly; 560 h_z = lz + 1.0F; 561 } 562 /* attention: s is not normalized, done later if necessary */ 563 n_dot_h = h_x*nx + h_y*ny + h_z*nz; 564 565 if (n_dot_h <= 0.0F) { 566 spec_coef = 0.0F; 567 } 568 else { 569 /* now `correct' the dot product */ 570 n_dot_h = n_dot_h / GL_SQRT(h_x*h_x + h_y*h_y + h_z*h_z); 571 if (n_dot_h>1.0F) { 572 spec_coef = pow( n_dot_h, mat->Shininess ); 573 } 574 else { 575 int k = (int) (n_dot_h * (GLfloat)(SHINE_TABLE_SIZE-1)); 576 if (mat->ShineTable[k] < 0.0F) 577 mat->ShineTable[k] = gl_pow( n_dot_h, mat->Shininess ); 578 spec_coef = mat->ShineTable[k]; 579 } 580 } 581 specular += spec_coef * light->sli * spot_times_atten; 582 } 583 } 584 585 } /*loop over lights*/ 586 587 /* Now compute final color index */ 588 if (specular>1.0F) { 589 index = mat->SpecularIndex; 590 } 591 else { 592 GLfloat d_a, s_a; 593 d_a = mat->DiffuseIndex - mat->AmbientIndex; 594 s_a = mat->SpecularIndex - mat->AmbientIndex; 595 596 index = mat->AmbientIndex 597 + diffuse * (1.0F-specular) * d_a 598 + specular * s_a; 599 if (index>mat->SpecularIndex) { 600 index = mat->SpecularIndex; 601 } 602 } 603 indexResult[j] = (GLuint) (GLint) index; 604 605 } /*for vertex*/ 606 } 607 608