G3dShadeImpl.mesa
Copyright Ó 1985, 1989, 1992 by Xerox Corporation. All rights reserved.
Bloomenthal, July 15, 1992 10:42 pm PDT
Heckbert, August 9, 1988 4:50:32 pm PDT
Crow, June 10, 1989 2:48:39 pm PDT
Glassner, July 19, 1989 12:14:25 pm PDT
DIRECTORY G3dBasic, G3dLight, G3dShade, G3dVector, G3dRender;
G3dShadeImpl: CEDAR MONITOR
IMPORTS G3dLight, G3dVector
EXPORTS G3dShade
~ BEGIN
Types
Light:     TYPE ~ G3dLight.Light;
LightSequence:  TYPE ~ G3dLight.LightSequence;
RGB:     TYPE ~ G3dRender.RGB;
Triple:    TYPE ~ G3dBasic.Triple;
Shading
TotalSurfaceIntensity: PUBLIC PROC [
normal, eyeView: Triple, lights: LightSequence, portionSpecular: REAL]
RETURNS [i: REAL ¬ 0.0]
~ {
portionDiffuse: REAL ¬ 1.0-portionSpecular;
IF lights # NIL THEN FOR n: NAT IN [0..lights.length) DO
nDotE, nDotH, nDotL, specular: REAL;
l: Light ¬ lights[n];
[nDotE, nDotH, nDotL] ¬ G3dLight.LightDots[l, normal, eyeView];
specular ¬ G3dLight.SpecularFromDots[l, nDotE, nDotH, nDotL];
i ¬ i+portionSpecular*specular+portionDiffuse*MAX[0.0, nDotL];
ENDLOOP;
};
SurfaceIntensity: PUBLIC PROC [light: Light, normal, eyeView: Triple, portionSpecular: REAL]
RETURNS [REAL]
~ {
nDotE, nDotH, nDotL, specular: REAL;
[nDotE, nDotH, nDotL] ¬ G3dLight.LightDots[light, normal, eyeView];
specular ¬ G3dLight.SpecularFromDots[light, nDotE, nDotH, nDotL];
RETURN[portionSpecular*specular+(1.0-portionSpecular)*MAX[0.0, nDotL]];
};
SpecularIntensity: PUBLIC PROC [light: Light, normal, eyeView: Triple] RETURNS [REAL] ~ {
nDotE, nDotH, nDotL: REAL;
[nDotE, nDotH, nDotL] ¬ G3dLight.LightDots[light, normal, eyeView];
RETURN[G3dLight.SpecularFromDots[light, nDotE, nDotH, nDotL]];
};
DiffuseIntensity: PUBLIC PROC [light: Light, normal: Triple] RETURNS [REAL] ~ {
RETURN[MAX[0, G3dVector.Dot[normal, light.direction]]];
};
Ambient Light
Ambient: PUBLIC PROC [normal: Triple, data: REF ¬ NIL] RETURNS [ambient: RGB] ~ {
Get an ambient light value from the eyespace normal to the surface
IF data = NIL THEN RETURN[[.2, .2, .2]];
WITH data SELECT FROM
clr: REF RGB => ambient ¬ clr­;
dir: REF Triple => {
direction: Triple ¬ dir­;
dotNL: REAL ¬ G3dVector.Dot[
G3dVector.Unit[direction], G3dVector.Unit[ normal ]
];
dotNL ¬ (dotNL + 1.0) / 2.0;   -- range ambient light over shadowed portions too
ambient.R ¬ ambient.G ¬ ambient.B ¬ dotNL;
};
ENDCASE => ambient ¬ [.2, .2, .2];
};
Procedures for Shading Patches
END.
..
ShadeVtx: PUBLIC G3dRender.CtlPtInfoProc ~ {
PROC[context3d: Context3d, vtx: CtlPtInfo, data: REF ANYNIL ] RETURNS[CtlPtInfo]
Calculate shade at vertices of polygon
shadingClass: REF ShadingClass ¬ NARROW[data];
shapeClr: RGB ¬ IF shadingClass # NIL THEN shadingClass.color ELSE defaultWhite;
shapeTrans: REAL ¬ IF shadingClass # NIL THEN shadingClass.transmittance ELSE 0.0;
kDiffuse: REAL ¬ IF shadingClass # NIL THEN shadingClass.diffuseReflectivity ELSE 1.0;
kSpecular: REAL ¬ IF shadingClass # NIL THEN shadingClass.specularReflectivity ELSE 1.0;
metallicity: REAL ¬ IF shadingClass # NIL THEN shadingClass.metallicity ELSE 0.0;
shininess: REAL ¬ IF shadingClass # NIL THEN shadingClass.shininess ELSE 0.0;
shinyPwr: NAT ¬ Real.Round[shininess];
partShiny: REAL ¬ 1.0;
toLightSrc, toEye: Triple;
dotNL, dotNE, sumHilite: REAL ¬ 0.0;
ambient, diffuse, specular, result: RGB ¬ [0.0, 0.0, 0.0];
vtxClr: RGB ¬ [vtx.shade.r * shapeClr.R, vtx.shade.g * shapeClr.G, vtx.shade.b * shapeClr.B];
toEye ¬ G3dVector.Unit[[-vtx.coord.ex, -vtx.coord.ey, -vtx.coord.ez]]; -- direction to eye
[ [vtx.shade.exn, vtx.shade.eyn, vtx.shade.ezn] ] ¬ G3dVector.Unit[
[vtx.shade.exn, vtx.shade.eyn, vtx.shade.ezn]    -- often not unitd
];
Get ambient component of light
ambient ¬ Ambient[[vtx.shade.exn, vtx.shade.eyn, vtx.shade.ezn], context3d.environment];
ambient.R ¬ ambient.R * vtxClr.R;
ambient.G ¬ ambient.G * vtxClr.G;
ambient.B ¬ ambient.B * vtxClr.B;
IF context3d.lightSources # NIL THEN FOR i: NAT IN [0..context3d.lightSources.length) DO
Do for each light source
light: Light ¬ context3d.lightSources[i];
vtxCoord: Triple ¬ [vtx.coord.ex, vtx.coord.ey, vtx.coord.ez];
lightClr: RGB ¬ light.lightProc[light, vtxCoord];
center: Triple ¬ light.eyePosition;
Get Light Direction from Surface (vector to light source from surface vertex):
toLightSrc ¬ G3dVector.Unit[G3dVector.Sub[center, vtxCoord]];
Get Basic Lambertian Shade
dotNL ¬ G3dVector.Dot[toLightSrc, [vtx.shade.exn, vtx.shade.eyn, vtx.shade.ezn]];
IF dotNL <= 0. THEN LOOP;       -- surface faces away from light, skip
diffuse.R ¬ (1. - ambient.R) * dotNL * lightClr.R * vtxClr.R; -- surface facing the light
diffuse.G ¬ (1. - ambient.G) * dotNL * lightClr.G * vtxClr.G;
diffuse.B ¬ (1. - ambient.B) * dotNL * lightClr.B * vtxClr.B;
Get Highlight Contribution
IF vtx.data # NIL THEN WITH vtx.data SELECT FROM-- extract partial shinyness
ptShiny: REF REAL => partShiny ¬ ptShiny­;
ENDCASE;
IF shinyPwr > 0 AND partShiny > 0.0 THEN {   -- compute Phong specular component
pctHilite: REAL ¬ 0.0;
halfWay: Triple ¬ G3dVector.Unit[  -- unitd average of vectors
G3dVector.Mul[ G3dVector.Add[toEye, toLightSrc], 0.5 ]
];
dotNormHalfWay: REAL ¬ G3dVector.Dot[ -- cos angle betw. normal and average
[vtx.shade.exn, vtx.shade.eyn, vtx.shade.ezn],
halfWay
];
IF dotNormHalfWay > 0. THEN {
binaryCount: NAT ¬ shinyPwr;
pctHilite ¬ partShiny;
WHILE binaryCount > 0 DO    -- compute power by repeated squares
IF (binaryCount MOD 2) = 1 THEN pctHilite ¬ pctHilite*dotNormHalfWay;
dotNormHalfWay ¬ dotNormHalfWay*dotNormHalfWay;
binaryCount ¬ binaryCount/2;
ENDLOOP;
IF pctHilite < 0.0 OR pctHilite > 1.0
THEN SIGNAL G3dRender.Error[$MisMatch, "Highlight error"];
};
Add in Highlight, based on headroom left after diffuse and ambient light included
specular.R ¬ (1.0 - diffuse.R - ambient.R) * pctHilite * lightClr.R;
specular.G ¬ (1.0 - diffuse.G - ambient.G) * pctHilite * lightClr.G;
specular.B ¬ (1.0 - diffuse.B - ambient.B) * pctHilite * lightClr.B;
sumHilite ¬ sumHilite + pctHilite;
};
Accumulate diffuse and specular contributions from each light
result.R ¬ result.R + (kDiffuse * diffuse.R) + (kSpecular * specular.R);
result.G ¬ result.G + (kDiffuse * diffuse.G) + (kSpecular * specular.G);
result.B ¬ result.B + (kDiffuse * diffuse.B) + (kSpecular * specular.B);
ENDLOOP;        -- end loop for each light source
result.R ¬ result.R + ambient.R;   -- add in ambient light
result.G ¬ result.G + ambient.G;
result.B ¬ result.B + ambient.B;
vtx.shade.er ¬ MAX[0.0, MIN[result.R, 1.]];
vtx.shade.eg ¬ MAX[0.0, MIN[result.G, 1.]];
vtx.shade.eb ¬ MAX[0.0, MIN[result.B, 1.]];
IF shapeTrans > 0.0
THEN {      -- compute transmittance if transparent
Transmittance is cosine of angle between to eye and normal (modified for effect)
dotNE ¬ G3dVector.Dot[toEye, [vtx.shade.exn, vtx.shade.eyn, vtx.shade.ezn]];
dotNE ¬ 1.0 - ABS[dotNE]; dotNE ¬ 1.0 - (dotNE * dotNE); -- invert, square, invert
vtx.shade.et ¬ dotNE * vtx.shade.t * shapeTrans; -- Transmittance as seen from eyepoint
vtx.shade.et ¬ MIN[1.0 - sumHilite, vtx.shade.et];   -- make highlights more opaque
vtx.shade.et ¬ MAX[0.0, MIN[vtx.shade.et, 1.]];
}
ELSE vtx.shade.et ¬ 0.0;
RETURN[vtx];
};