NoShadeVtx: ThreeDBasics.VertexInfoProc ~ {
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;
vtx.shade.er ← vtx.shade.r * shapeClr.R;
vtx.shade.eg ← vtx.shade.g * shapeClr.G;
vtx.shade.eb ← vtx.shade.b * shapeClr.B;
IF shapeTrans > 0.0
THEN {
-- compute transmittance if transparent
vtx.shade.et ← vtx.shade.t * shapeTrans; -- Transmittance
vtx.shade.et ← MAX[0.0, MIN[vtx.shade.et, 1.]];
};
RETURN[vtx]; -- avoids shading calculations for background polygons, shadows, etc.
};
ShadeVtx:
PUBLIC ThreeDBasics.VertexInfoProc ~ {
PROC[ context: REF Context, vtx: VertexInfo, data: REF ANY ← NIL ] RETURNS[VertexInfo]
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;
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];
IF vtx.props #
NIL
THEN {
ref: REF ← GetProp[vtx.props, $PartShiny];
IF ref # NIL THEN partShiny ← NARROW[ref, REF REAL]^;
};
toEye ← G3dVector.Normalize[[-vtx.coord.ex, -vtx.coord.ey, -vtx.coord.ez]]; -- direction to eye
[ [vtx.shade.exn, vtx.shade.eyn, vtx.shade.ezn] ] ← G3dVector.Normalize[
[vtx.shade.exn, vtx.shade.eyn, vtx.shade.ezn] -- often not normalized
];
Get ambient component of light
ambient ← GetAmbientLight[ context, [vtx.shade.exn, vtx.shade.eyn, vtx.shade.ezn] ];
ambient.R ← ambient.R * vtxClr.R;
ambient.G ← ambient.G * vtxClr.G;
ambient.B ← ambient.B * vtxClr.B;
IF context.lightSources #
NIL
THEN
FOR i:
NAT
IN [0..context.lightSources.length)
DO
Do for each light source
lightClr: RGB ← context.lightSources[i].shadingClass.color;
Get Light Direction from Surface
toLightSrc ← G3dVector.Normalize[ [
-- vector to light source from surface vtx.
context.lightSources[i].centroid.ex - vtx.coord.ex,
context.lightSources[i].centroid.ey - vtx.coord.ey,
context.lightSources[i].centroid.ez - vtx.coord.ez
] ];
Get Light Strength
IF context.lightSources[i].orientation # [0.0, 0.0, 0.0]
THEN {
-- spotlight, get direction
dotLS, intensity: REAL;
shineDirection: Triple ← TransformVec[ context.lightSources[i].orientation,
context.eyeSpaceXfm ];
spotSpread: NAT ← Real.Fix[ context.lightSources[i].shadingClass.shininess ];
dotLS ← -G3dVector.Dot[toLightSrc, shineDirection];
IF dotLS > 0.
THEN {
-- compute spotlight factor
binaryCount: NAT ← spotSpread;
intensity ← 1.;
WHILE binaryCount > 0
DO
-- compute power by repeated squares
IF (binaryCount MOD 2) = 1 THEN intensity ← intensity*dotLS;
dotLS ← dotLS*dotLS;
binaryCount ← binaryCount/2;
ENDLOOP;
}
ELSE intensity ← 0.;
IF intensity < ScanConvert.justNoticeable THEN LOOP; -- no effect, skip this light
lightClr.R ← lightClr.R*intensity;
lightClr.G ← lightClr.G*intensity;
lightClr.B ← lightClr.B*intensity;
};
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 shinyPwr > 0
AND partShiny > 0.0
THEN {
-- compute Phong specular component
pctHilite: REAL ← 0.0;
halfWay: Triple ← G3dVector.Normalize[
-- normalized 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 ThreeDBasics.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 + diffuse.R + specular.R;
result.G ← result.G + diffuse.G + specular.G;
result.B ← result.B + diffuse.B + 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];
};