1 /* $Id: shade.c,v 1.10 1997/12/18 02:54:48 brianp Exp $ */
4 * Mesa 3-D graphics library
6 * Copyright (C) 1995-1997 Brian Paul
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.
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.
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.
26 * Revision 1.10 1997/12/18 02:54:48 brianp
27 * now using FloatToInt() macro for better performance on x86
29 * Revision 1.9 1997/07/24 01:21:56 brianp
30 * changed precompiled header symbol from PCH to PC_HEADER
32 * Revision 1.8 1997/07/09 03:04:44 brianp
33 * fixed bug in gl_color_shade_vertices() with GL_COLOR_MATERIAL
35 * Revision 1.7 1997/07/05 16:24:26 brianp
36 * fixed FP underflow problem in pow(). Renamed h[xyz] to h_[xyz].
38 * Revision 1.6 1997/06/20 04:15:43 brianp
39 * optimized changing of SHININESS (Henk Kok)
41 * Revision 1.5 1997/06/20 02:28:40 brianp
42 * changed color components from GLfixed to GLubyte
44 * Revision 1.4 1997/05/28 03:26:29 brianp
45 * added precompiled header (PCH) support
47 * Revision 1.3 1997/05/23 03:01:18 brianp
48 * commented out a few const keywords because IRIX cc chokes on them
50 * Revision 1.2 1997/05/09 02:41:08 brianp
51 * call GL_SQRT() instead of sqrt()
53 * Revision 1.1 1997/04/01 04:11:04 brianp
74 static GLfloat
gl_pow( GLfloat x
, GLfloat y
)
76 GLdouble z
= pow(x
, y
);
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
94 void gl_color_shade_vertices( GLcontext
*ctx
,
97 /*const*/ GLfloat vertex
[][4],
98 /*const*/ GLfloat normal
[][3],
102 GLfloat rscale
, gscale
, bscale
, ascale
;
103 GLfloat baseR
, baseG
, baseB
, baseA
;
105 struct gl_light
*light
;
106 struct gl_material
*mat
;
108 /* Compute scale factor to go from floats in [0,1] to integers or fixed
111 rscale
= ctx
->Visual
->RedScale
;
112 gscale
= ctx
->Visual
->GreenScale
;
113 bscale
= ctx
->Visual
->BlueScale
;
114 ascale
= ctx
->Visual
->AlphaScale
;
116 mat
= &ctx
->Light
.Material
[side
];
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 */
124 sumA
= (GLint
) (CLAMP( baseA
, 0.0F
, 1.0F
) * ascale
);
127 GLfloat sumR
, sumG
, sumB
;
131 /* shade frontside */
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 */
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];
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
);
170 GLfloat invd
= 1.0F
/ d
;
175 attenuation
= 1.0F
/ (light
->ConstantAttenuation
176 + d
* (light
->LinearAttenuation
177 + d
* light
->QuadraticAttenuation
));
180 /* spotlight factor */
181 if (light
->SpotCutoff
==180.0F
) {
182 /* not a spot light */
186 GLfloat PVx
, PVy
, PVz
, PV_dot_dir
;
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 */
198 double x
= PV_dot_dir
* (EXP_TABLE_SIZE
-1);
200 spot
= light
->SpotExpTable
[k
][0]
201 + (x
-k
)*light
->SpotExpTable
[k
][1];
205 ambientR
= mat
->Ambient
[0] * light
->Ambient
[0];
206 ambientG
= mat
->Ambient
[1] * light
->Ambient
[1];
207 ambientB
= mat
->Ambient
[2] * light
->Ambient
[2];
209 /* Compute dot product or normal and vector from V to light pos */
210 n_dot_VP
= nx
* VPx
+ ny
* VPy
+ nz
* VPz
;
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 */
222 GLfloat diffuseR
, diffuseG
, diffuseB
;
223 GLfloat specularR
, specularG
, specularB
;
224 GLfloat h_x
, h_y
, h_z
, n_dot_h
, t
;
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];
232 if (ctx
->Light
.Model
.LocalViewer
) {
233 GLfloat vx
, vy
, vz
, vlen
;
237 vlen
= GL_SQRT( vx
*vx
+ vy
*vy
+ vz
*vz
);
239 GLfloat invlen
= 1.0F
/ vlen
;
250 /* h = VP + <0,0,1> */
256 /* attention: h is not normalized, done later if needed */
257 n_dot_h
= nx
*h_x
+ ny
*h_y
+ nz
*h_z
;
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
);
269 /* only happens if normal vector length > 1.0 */
270 spec_coef
= pow( n_dot_h
, mat
->Shininess
);
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
];
279 if (spec_coef
<1.0e-10) {
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];
291 t
= attenuation
* spot
;
292 sumR
+= t
* (ambientR
+ diffuseR
+ specularR
);
293 sumG
+= t
* (ambientG
+ diffuseG
+ specularG
);
294 sumB
+= t
* (ambientB
+ diffuseB
+ specularB
);
297 } /*loop over lights*/
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
);
305 } /*loop over vertices*/
311 * This is an optimized version of the above function.
313 void gl_color_shade_vertices_fast( GLcontext
*ctx
,
316 /*const*/ GLfloat normal
[][3],
320 GLfloat rscale
, gscale
, bscale
, ascale
;
322 GLfloat
*baseColor
= ctx
->Light
.BaseColor
[side
];
324 /* Compute scale factor to go from floats in [0,1] to integers or fixed
327 rscale
= ctx
->Visual
->RedScale
;
328 gscale
= ctx
->Visual
->GreenScale
;
329 bscale
= ctx
->Visual
->BlueScale
;
330 ascale
= ctx
->Visual
->AlphaScale
;
332 /* Alpha is easy to compute, same for all vertices */
333 sumA
= (GLint
) (baseColor
[3] * ascale
);
335 /* Loop over vertices */
337 GLfloat sumR
, sumG
, sumB
;
339 struct gl_light
*light
;
341 /* the normal vector */
363 /* base color from global illumination and enabled light's ambient */
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 */
372 n_dot_VP
= nx
* light
->VP_inf_norm
[0]
373 + ny
* light
->VP_inf_norm
[1]
374 + nz
* light
->VP_inf_norm
[2];
376 /* diffuse and specular terms */
379 GLfloat
*lightMatDiffuse
= light
->MatDiffuse
[side
];
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];
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];
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];
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
];
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];
417 } /*loop over lights*/
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
);
425 } /*loop over vertices*/
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
439 void gl_index_shade_vertices( GLcontext
*ctx
,
444 GLuint indexResult
[] )
446 struct gl_material
*mat
= &ctx
->Light
.Material
[side
];
449 /* loop over vertices */
452 GLfloat diffuse
, specular
; /* accumulated diffuse and specular */
453 GLfloat nx
, ny
, nz
; /* normal vector */
454 struct gl_light
*light
;
457 /* shade frontside */
469 diffuse
= specular
= 0.0F
;
471 /* Accumulate diffuse and specular from each light source */
472 for (light
=ctx
->Light
.FirstEnabled
; light
; light
=light
->NextEnabled
) {
474 GLfloat lx
, ly
, lz
; /* unit vector from vertex to light */
475 GLfloat l_dot_norm
; /* dot product of l and n */
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];
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
);
494 GLfloat invd
= 1.0F
/ d
;
499 attenuation
= 1.0F
/ (light
->ConstantAttenuation
500 + d
* (light
->LinearAttenuation
501 + d
* light
->QuadraticAttenuation
));
504 l_dot_norm
= lx
*nx
+ ly
*ny
+ lz
*nz
;
506 if (l_dot_norm
>0.0F
) {
507 GLfloat spot_times_atten
;
509 /* spotlight factor */
510 if (light
->SpotCutoff
==180.0F
) {
511 /* not a spot light */
512 spot_times_atten
= attenuation
;
516 v
[0] = -lx
; /* v points from light to vertex */
519 dot
= DOT3( v
, light
->NormDirection
);
520 if (dot
<=0.0F
|| dot
<light
->CosCutoff
) {
521 /* outside of cone */
522 spot_times_atten
= 0.0F
;
525 double x
= dot
* (EXP_TABLE_SIZE
-1);
527 GLfloat spot
= light
->SpotExpTable
[k
][0]
528 + (x
-k
)*light
->SpotExpTable
[k
][1];
529 spot_times_atten
= spot
* attenuation
;
533 /* accumulate diffuse term */
534 diffuse
+= l_dot_norm
* light
->dli
* spot_times_atten
;
536 /* accumulate specular term */
538 GLfloat h_x
, h_y
, h_z
, n_dot_h
, spec_coef
;
541 if (ctx
->Light
.Model
.LocalViewer
) {
542 GLfloat vx
, vy
, vz
, vlen
;
546 vlen
= GL_SQRT( vx
*vx
+ vy
*vy
+ vz
*vz
);
548 GLfloat invlen
= 1.0F
/ vlen
;
562 /* attention: s is not normalized, done later if necessary */
563 n_dot_h
= h_x
*nx
+ h_y
*ny
+ h_z
*nz
;
565 if (n_dot_h
<= 0.0F
) {
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
);
572 spec_coef
= pow( n_dot_h
, mat
->Shininess
);
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
];
581 specular
+= spec_coef
* light
->sli
* spot_times_atten
;
585 } /*loop over lights*/
587 /* Now compute final color index */
589 index
= mat
->SpecularIndex
;
593 d_a
= mat
->DiffuseIndex
- mat
->AmbientIndex
;
594 s_a
= mat
->SpecularIndex
- mat
->AmbientIndex
;
596 index
= mat
->AmbientIndex
597 + diffuse
* (1.0F
-specular
) * d_a
599 if (index
>mat
->SpecularIndex
) {
600 index
= mat
->SpecularIndex
;
603 indexResult
[j
] = (GLuint
) (GLint
) index
;