File: ShadingImpl.mesa
Last edited by Bier on May 31, 1984 3:52:21 pm PDT
Author: Eric Bier on February 18, 1987 6:27:52 pm PST
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
Imager, ImagerColor, ImagerColorPrivate, RealFns, Shading, SV3d, SVModelTypes, SVVector3d;
ShadingImpl: CEDAR PROGRAM
IMPORTS ImagerColor, ImagerColorPrivate, RealFns, SVVector3d
EXPORTS Shading =
BEGIN
Color: TYPE = Imager.Color;
Vector3d: TYPE = SV3d.Vector3d;
Point3d: TYPE = SV3d.Point3d;
LightSource: TYPE = SVModelTypes.LightSource;
LightSourceList: TYPE = SVModelTypes.LightSourceList;
ExtractRGB: PUBLIC PROC [color: Color] RETURNS [r,g,b: REAL] = {
rgb: ImagerColor.RGB;
rgb ← ImagerColorPrivate.RGBFromColor[NARROW[color]];
r ← rgb.R; g ← rgb.G; b ← rgb.B;
};
NormalizeRGB: PUBLIC PROC [r,g,b: REAL] RETURNS [color: Color] = {
scaleFactor: REAL;
Scale sum to max. value to avoid desaturation at bright spots.
scaleFactor ← MAX[r,g,b];
IF scaleFactor > 1.0 THEN {
r ← r/scaleFactor; g ← g/scaleFactor; b ← b/scaleFactor;
};
color ← ImagerColor.ColorFromRGB[[r,g,b]];
};
CollimatedSpecularRadiance: PUBLIC PROC [surfaceNormal: Vector3d, lightSource: LightSource, albedo: REAL ← 1] RETURNS [radiance: REAL] = {radiance ← 180};
Collimated light source at lightSource. Specular surface with given albedo oriented by given surfaceNormal.
DiffuseReflectance: PUBLIC PROC [surfaceNormal: Vector3d, surfacePt: Point3d, surfaceColor: Color, lightSources: LightSourceList] RETURNS [r, g, b: REAL] = {
OPEN SVVector3d;
intensity: REAL;
surfaceToLight, surfaceClr, surfaceClrTemp, liteClr, ambientClr: Vector3d;
colorSum: Vector3d ← [0,0,0];
[r, g, b] ← ExtractRGB[NARROW[surfaceColor]];
IF lightSources.first.type # ambient THEN ERROR;
[ambientClr[1], ambientClr[2], ambientClr[3]] ← ExtractRGB[NARROW[lightSources.first.color]];
surfaceClr ← [r,g,b];
surfaceNormal ← Normalize[surfaceNormal]; -- all vectors must be kept normalized
IF ambientClr[1] > 0.0 OR ambientClr[2] > 0. OR ambientClr[3] > 0.0 THEN
colorSum ← ElementwiseProduct[surfaceClr, ambientClr]; -- initialize colorSum.
FOR lite: LightSourceList ← lightSources.rest, lite.rest UNTIL lite = NIL -- for each light source
DO
[r, g, b] ← ExtractRGB[NARROW[lite.first.color]];
liteClr ← [r,g,b];
surfaceToLight ← Normalize[Sub[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 desaturation at bright spots.
IF scaleFactor > 1.0 THEN colorSum ← Scale[colorSum, 1.0/scaleFactor];
r ← colorSum[1];
g ← colorSum[2];
b ← colorSum[3];
completeColor ← ImagerColor.ColorFromRGB[[r,g,b]];
};
DiffuseAndSpecularReflectance: PUBLIC PROCEDURE [eyePoint: Point3d, surfaceNormal: Vector3d, surfacePt: Point3d, surfaceColor: Color, lightSources: LightSourceList] RETURNS [r, g, b: REAL] = {
OPEN SVVector3d;
intensity, hilitValue: REAL;
surfaceToLight, surfaceToEye, surfaceClr, surfaceClrTemp: Vector3d;
ambientClr, finalClr, halfAngle, liteClr: Vector3d;
colorSum: Vector3d ← [0,0,0];
[r,g,b] ← ExtractRGB[surfaceColor];
IF lightSources.first.type # ambient THEN ERROR;
[ambientClr[1], ambientClr[2], ambientClr[3]] ← ExtractRGB[lightSources.first.color];
surfaceClr ← [r,g,b];
surfaceNormal ← Normalize[surfaceNormal]; -- all vectors must be kept normalized
ambientScalar ← DotProduct[surfaceNormal,[0,1,0]]; -- overhead ambient light
IF ambientClr[1] > 0.0 OR ambientClr[2] > 0. OR ambientClr[3] > 0.0 THEN
colorSum ← ElementwiseProduct[surfaceClr, ambientClr];
FOR lite: LightSourceList ← lightSources.rest, lite.rest UNTIL lite = NIL -- for each light source
DO
[r,g,b] ← ExtractRGB[lite.first.color];
liteClr ← [r,g,b];
surfaceToLight ← Normalize[Sub[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[Sub[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[Sub[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 ← ImagerColor.ColorFromRGB[[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: Vector3d ← 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: Vector3d, 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.