-- File: ShadingImpl.mesa -- Last edited by Bier on December 18, 1982 1:26 am -- Author: Eric Bier on July 3, 1983 1:33 pm -- Contents: A camera model for shading surfaces, given surface type, lighting conditions and aperture size. No attempt is made to provide finite depth of field or other artifacts of a real camera. -- Frank Crow added Utah-style highlights on September 23, 1982 12:22 pm DIRECTORY CGColor, GraphicsColor, Matrix3d, RealFns, Shading, SVVector3d; ShadingImpl: PROGRAM IMPORTS GraphicsColor, RealFns, SVVector3d EXPORTS Shading = BEGIN Color: TYPE = GraphicsColor.Color; Vector: TYPE = SVVector3d.Vector; Point3d: TYPE = Matrix3d.Point3d; LightSource: TYPE = Shading.LightSource; LightSourceList: TYPE = Shading.LightSourceList; CollimatedSpecularRadiance: PUBLIC PROCEDURE [surfaceNormal: Vector, lightSource: LightSource, albedo: REAL _ 1] RETURNS [radiance: REAL] = {radiance _ 180}; -- Collimated light source at lightSource. Specular surface with given albedo oriented by given surfaceNormal. ElementWiseProduct: PRIVATE PROC [v1, v2: Vector] RETURNS [prod: Vector] = { prod[1] _ v1[1] * v2[1]; prod[2] _ v1[2] * v2[2]; prod[3] _ v1[3] * v2[3]; }; DiffuseReflectance: PUBLIC PROCEDURE [surfaceNormal: Vector, surfacePt: Point3d, surfaceColor: Color, lightSources: LightSourceList] RETURNS [completeColor: Color] = { OPEN SVVector3d; r, g, b, intensity, ambientScalar: REAL; scaleFactor: REAL; surfaceToLight, surfaceClr, surfaceClrTemp, liteClr: Vector; colorSum: Vector _ [0,0,0]; [r,g,b] _ GraphicsColor.ColorToRGB[surfaceColor]; surfaceClr _ [r,g,b]; surfaceNormal _ Normalize[surfaceNormal]; -- all vectors must be kept normalized ambientScalar _ DotProduct[surfaceNormal,[0,1,0]]; -- overhead ambient light IF ambientScalar > 0 THEN colorSum _ Scale[surfaceClr, 0.25 * DotProduct[surfaceNormal,[0,1,0]]]; -- colorSum _ Add[colorSum, Scale[surfaceClr, 0.25 ... ] is clearer but slower. FOR lite: LightSourceList _ lightSources, lite.rest UNTIL lite = NIL -- for each light source DO [r,g,b] _ GraphicsColor.ColorToRGB[lite.first.color]; liteClr _ [r,g,b]; surfaceToLight _ Normalize[Difference[lite.first.position, surfacePt]]; -- vector from surface to light source intensity _ DotProduct[surfaceNormal, surfaceToLight]; -- lambertian reflectance IF intensity < 0.0 THEN intensity _ 0.0; -- no negative reflectance allowed surfaceClrTemp _ Scale[surfaceClr, intensity]; -- scale surface color by reflectance surfaceClrTemp _ ElementWiseProduct[surfaceClrTemp, liteClr]; colorSum _ Add[colorSum, surfaceClrTemp]; -- sum reflectances for all lights ENDLOOP; scaleFactor _ MAX[colorSum[1], colorSum[2], colorSum[3]]; -- scale sum to max. value to avoid IF scaleFactor > 1.0 THEN colorSum _ Scale[colorSum, 1.0/scaleFactor]; -- desaturation at bright spots r _ colorSum[1]; g _ colorSum[2]; b _ colorSum[3]; completeColor _ GraphicsColor.RGBToColor[r,g,b]; }; DiffuseAndSpecularReflectance: PUBLIC PROCEDURE [eyePoint: Point3d, surfaceNormal: Vector, surfacePt: Point3d, surfaceColor: Color, lightSources: LightSourceList] RETURNS [completeColor: Color] = { OPEN SVVector3d; r, g, b, intensity, hilitValue, ambientScalar: REAL; scaleFactor: REAL; surfaceToLight, surfaceToEye, surfaceClr, surfaceClrTemp, finalClr, halfAngle, liteClr: Vector; colorSum: Vector _ [0,0,0]; [r,g,b] _ GraphicsColor.ColorToRGB[surfaceColor]; surfaceClr _ [r,g,b]; surfaceNormal _ Normalize[surfaceNormal]; -- all vectors must be kept normalized ambientScalar _ DotProduct[surfaceNormal,[0,1,0]]; -- overhead ambient light IF ambientScalar > 0 THEN colorSum _ Scale[surfaceClr, 0.25 * DotProduct[surfaceNormal,[0,1,0]]]; -- colorSum _ Add[colorSum, Scale[surfaceClr, 0.25 ... ] is clearer but slower. FOR lite: LightSourceList _ lightSources, lite.rest UNTIL lite = NIL -- for each light source DO [r,g,b] _ GraphicsColor.ColorToRGB[lite.first.color]; liteClr _ [r,g,b]; surfaceToLight _ Normalize[Difference[lite.first.position, surfacePt]]; -- vector from surface to light source intensity _ DotProduct[surfaceNormal, surfaceToLight]; -- lambertian reflectance IF intensity < 0.0 THEN intensity _ 0.0; -- no negative reflectance allowed surfaceClrTemp _ Scale[surfaceClr, intensity]; -- scale surface color by reflectance surfaceClrTemp _ ElementWiseProduct[surfaceClrTemp, liteClr]; surfaceToEye _ Normalize[Difference[eyePoint, surfacePt]]; halfAngle _ Normalize[Add[surfaceToLight, surfaceToEye]]; hilitValue _ DotProduct[surfaceNormal, halfAngle]; -- specular reflectance IF hilitValue < 0.95 THEN hilitValue _ 0.0 ELSE hilitValue _ RealFns.Power[hilitValue, 100]; -- all surfaces have same specular power IF hilitValue > .01 THEN -- add in light source color for highlight finalClr _ Add[surfaceClrTemp, Scale[Difference[liteClr, surfaceClrTemp], hilitValue]] ELSE finalClr _ surfaceClrTemp; colorSum _ Add[colorSum, finalClr]; -- sum reflectances for all lights ENDLOOP; scaleFactor _ MAX[colorSum[1], colorSum[2], colorSum[3]]; -- scale sum to max. value to avoid IF scaleFactor > 1.0 THEN colorSum _ Scale[colorSum, 1.0/scaleFactor]; -- desaturation at bright spots r _ colorSum[1]; g _ colorSum[2]; b _ colorSum[3]; completeColor _ GraphicsColor.RGBToColor[r,g,b]; }; Power: PRIVATE PROC [base: REAL, exponent: NAT] RETURNS [baseToExp: REAL] = { baseToExp _ 1.0; FOR i: NAT IN [1..exponent] DO baseToExp _ baseToExp * base; ENDLOOP; }; SimpleFilmIrradiance: PUBLIC PROCEDURE [radiance: REAL, omegaAperture: REAL] RETURNS [irradiance: REAL] = { irradiance _ radiance*omegaAperture; }; SolidAngle: PUBLIC PROCEDURE [surfacePoint: Point3d, areaAperture: REAL] RETURNS [omegaAperture: REAL] = { -- solid angle is just Acos(theta)/r^2, where r is the distance from surface point to aperture (at [0,0,0], A is the areaAperture, and theta is the angle between the aperture normal [0,0,-1] and the line from surfacePoint to [0,0,0]; fromApertureToSurface: Vector _ surfacePoint; -- same as surfacePoint because aperture at [0,0,0] r: REAL _ SVVector3d.Magnitude[fromApertureToSurface]; cosTheta: REAL _ -fromApertureToSurface[3]/r; omegaAperture _ (areaAperture*cosTheta)/(r*r); }; SpecFilmIrradiance: PUBLIC PROCEDURE [surfaceNormal: Vector, lightSource: LightSource, albedo: REAL _ 1, omegaAperture: REAL] RETURNS [irradiance: REAL] = { -- A convenience routine which combines CollimatedSpecularRadiance and SimpleFilmIrradiance. radiance: REAL _ CollimatedSpecularRadiance[surfaceNormal,lightSource,albedo]; irradiance _ SimpleFilmIrradiance[radiance,omegaAperture]; }; END.