File: ShadingImpl.mesa
Last edited by Bier on May 31, 1984 3:52:21 pm PDT
Author: Eric Bier on May 23, 1985 11:03:44 pm PDT
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
GraphicsColor,
RealFns,
Shading,
SV3d,
SVModelTypes,
SVVector3d;
ShadingImpl: PROGRAM IMPORTS GraphicsColor, RealFns, SVVector3d EXPORTS Shading =
BEGIN
Color: TYPE = GraphicsColor.Color;
Vector: TYPE = SV3d.Vector;
Point3d: TYPE = SV3d.Point3d;
LightSource: TYPE = SVModelTypes.LightSource;
LightSourceList: TYPE = SVModelTypes.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.
DiffuseReflectance: PUBLIC PROCEDURE [surfaceNormal: Vector, surfacePt: Point3d, surfaceColor: Color, lightSources: LightSourceList] RETURNS [completeColor: Color] = {
OPEN SVVector3d;
r, g, b, intensity: REAL;
scaleFactor: REAL;
surfaceToLight, surfaceClr, surfaceClrTemp, liteClr, ambientClr: Vector;
colorSum: Vector ← [0,0,0];
[r,g,b] ← GraphicsColor.ColorToRGB[surfaceColor];
IF lightSources.first.type # ambient THEN ERROR;
[ambientClr[1], ambientClr[2], ambientClr[3]] ← GraphicsColor.ColorToRGB[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] ← GraphicsColor.ColorToRGB[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 ← 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: REAL;
scaleFactor: REAL;
surfaceToLight, surfaceToEye, surfaceClr, surfaceClrTemp: Vector;
ambientClr, finalClr, halfAngle, liteClr: Vector;
colorSum: Vector ← [0,0,0];
[r,g,b] ← GraphicsColor.ColorToRGB[surfaceColor];
IF lightSources.first.type # ambient THEN ERROR;
[ambientClr[1], ambientClr[2], ambientClr[3]] ← GraphicsColor.ColorToRGB[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] ← GraphicsColor.ColorToRGB[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 ← 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.