Tuesday, February 12, 2019
Adding point and directional lights to a physically based renderer
Suppose you have a physically based renderer with area lights. Along comes a model with point and directional lights. Perhaps the easiest way to deal with them is convert them to a very small spherical light source. But how do you do that in a way that gets the color right? IF you have things set up so they tend to be tone mapped (a potential big no in physically-based renderer), meaning that a color of (1,1,1) will be white, and (0.2,0.2,0.2) a mid-grey (gamma-- so not 0.5-- the eye does not see intensities linearly), then it is not so bad.
Assume you have a spherical light with radius R at position C and emitted radiance (E,E,E). When it is straight up from a white surface (so the sun at noon at the equator), you get this equation (about) for the color at a point P
reflected_color = (E,E,E)*solid_angle_of_light / PI
The solid angle of the light is its projected area on the unit sphere around the illuminated point, or approximately:
solid_angle_of_light = PI*R^2 /distance_squared(P,C)
reflected_color = (E,E,E)*(R / distance(P,C))^2
If we want the white surface to be exactly white then
E = (distance(P,C) / R)^2
So pick a small R (say 0.001), pick a point in your scene (say the one the viewer is looking at, or 0,0,0), and and set E as in the green equation
Suppose the RGB "color" given for the point source is (cr, cg, cb). Then just multiply the (E,E,E) by those components.
Directional lights are a similar hack, but a little less prone to problems of whther falloff was intended. A directional light is usually the sun, and it's very far away. Assume the direction is D = (dx,dy,dz)
Pick a big distance (one that wont break your renderer) and place the center in that direction:
C = big_constant*D
Pick a small radius. Again one that wont break your renderer. Then use the green equation above.
Now sometimes the directional sources are just the Sun and will look better if you give them the same angular size for the Sun. If your model is in meters, then just use distance = 150,000,000,000m. OK now your program will break due to floating point. Instead pick a somewhat big distance and use the right ratio of Sun radius to distance:
R = distance *(695,700km / 149,597,870km)
And your shadows will look ok