<> <> <> <> <> <> DIRECTORY Atom, G3dBasic, G3dLight, G3dRender, G3dShade, G3dVector, Real; G3dShadeImpl: CEDAR MONITOR IMPORTS Atom, G3dLight, G3dRender, G3dVector, Real EXPORTS G3dShade ~ BEGIN <> <> RGB: TYPE ~ G3dRender.RGB; PropList: TYPE ~ Atom.PropList; Shape: TYPE ~ G3dShade.Shape; Light: TYPE ~ G3dLight.Light; RenderData: TYPE ~ G3dRender.RenderData; RenderStyle: TYPE ~ G3dRender.RenderStyle; Triple: TYPE ~ G3dRender.Triple; <> NatSequence: TYPE ~ G3dBasic.NatSequence; NatSequenceRep: TYPE ~ G3dBasic.NatSequenceRep; PairSequence: TYPE ~ G3dBasic.PairSequence; TripleSequence: TYPE ~ G3dBasic.TripleSequence; LightSequence: TYPE ~ G3dLight.LightSequence; <> ShadingClass: TYPE ~ G3dShade.ShadingClass; <> defaultWhite: RGB _ [1.0, 1.0, 1.0]; registeredShadingClasses: PropList _ NIL; -- keeps active shading classes <> 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]]]; }; <> RegisterShadingClass: PUBLIC PROC [class: ShadingClass, type: ATOM] ~ { registeredShadingClasses _ Atom.PutPropOnList[registeredShadingClasses, type, NEW[ShadingClass _class]]; }; GetShadingClass: PUBLIC PROC [type: ATOM] RETURNS [class: ShadingClass] ~ { ref: REF ShadingClass _ NARROW[Atom.GetPropFromList[registeredShadingClasses, type]]; IF ref # NIL THEN class _ ref^ ELSE G3dRender.Error[$Unimplemented, "Unregistered shading class"]; }; LoadShadingClass: PUBLIC PROC [shape: Shape, type: ATOM _ $Default ] ~ { renderData: REF RenderData; class: REF ShadingClass _ NARROW[Atom.GetPropFromList[registeredShadingClasses, type]]; IF class = NIL THEN G3dRender.Error[$Unimplemented, "Unregistered shading class"]; renderData _ G3dRender.RenderDataFrom[shape]; IF renderData.shadingClass = NIL THEN renderData.shadingClass _ NEW[ShadingClass _ class^] ELSE { new: REF ShadingClass _ NEW[ShadingClass _ class^]; IF new.renderMethod = NIL THEN new.renderMethod _ renderData.shadingClass.renderMethod; new.color _ renderData.shadingClass.color; new.diffuseReflectivity _ renderData.shadingClass.diffuseReflectivity; new.specularReflectivity _ renderData.shadingClass.specularReflectivity; new.metallicity _ renderData.shadingClass.metallicity; new.shininess _ renderData.shadingClass.shininess; new.clientShadingData _ renderData.shadingClass.clientShadingData; new.transmittance _ renderData.shadingClass.transmittance; IF new.texture = NIL THEN new.texture _ renderData.shadingClass.texture; new.textureScale _ renderData.shadingClass.textureScale; new.bumpScale _ renderData.shadingClass.bumpScale; IF new.cnvrtVtx = NIL THEN new.cnvrtVtx _ renderData.shadingClass.cnvrtVtx; IF new.getColor = NIL THEN new.getColor _ renderData.shadingClass.getColor; IF new.shadeVtx = NIL THEN new.shadeVtx _ renderData.shadingClass.shadeVtx; renderData.shadingClass _ new; }; }; InitStandardShadeClasses: PROC[] ~ { -- register procedures for basic shade types defaultShadingClass: ShadingClass _ [ -- procs for standard shading (no texture) type: $Default, renderMethod: NEW[RenderStyle _ faceted], shadeVtx: ShadeVtx ]; RegisterShadingClass[defaultShadingClass, $Default]; defaultShadingClass.type _ $NoShading; defaultShadingClass.shadeVtx _ NoShadeVtx; RegisterShadingClass[defaultShadingClass, $NoShading]; }; <> Ambient: PUBLIC PROC [normal: Triple, data: REF _ NIL] RETURNS [ambient: RGB] ~ { <> 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]; }; <> NoShadeVtx: PUBLIC G3dRender.CtlPtInfoProc ~ { 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 G3dRender.CtlPtInfoProc ~ { <> <> 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 ]; <> ambient _ Ambient[[vtx.shade.exn, vtx.shade.eyn, vtx.shade.ezn], context.environment]; 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 <> lightClr: RGB _ context.lightSources[i].illuminationProc[ context.lightSources[i], [vtx.coord.ex, vtx.coord.ey, vtx.coord.ez] ]; center: Triple _ context.lightSources[i].eyePosition; <> toLightSrc _ G3dVector.Unit[ -- vector to light source from surface vtx. [ center.x - vtx.coord.ex, center.y - vtx.coord.ey, center.z - vtx.coord.ez ] ]; <> 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; <> 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"]; }; <> 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; }; <> 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 <> 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]; }; <> InitStandardShadeClasses[]; END.