-- 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.