To understand how rendering works it's always a good idea to take an example from one renderer and try to create a similar picture with another renderer. If you can't get away by using standard shaders it might be a good starting point for writing a few shaders.
I started a few years ago a comparison between different renderers and came across a renderer called Radiance27. This simple example is based on a tutorial28 for this renderer.
Have a look at figure 16. You will see a simple room with a floor, a ceiling, and four walls. One of the walls has a hole where a window can be placed. The camera looks at two simple objects in the room: A glass sphere and a blue box. The light is coming from two light sources: There is another sphere in the room which acts as a light source and we can think of daylight coming from outside through the window. Although the scene is quite simple it can give you some headache how to produce certain effects29 with your favourite renderer:
Let's first try to make life easier by using a renderer with ray tracing and a lot of standard shaders coming with it. I reproduced the scene with mental ray and here are some comments about the effects I was talking about before and how they were achieved:
dielectric and described in the Radiance Reference
Manual as: ``A dielectric material is transparent, and
it refracts light as well as reflecting it. Its behaviour is
determined by the index of refraction and transmission
coefficient in each wavelength band per unit length. Common
glass has a index of refraction around 1.5, and a transmission
coefficient of roughly 0.92 over an inch. An additional number,
the Hartmann constant, describes how the index of refraction
changes as a function of wavelength. It is usually zero.''
Mental Ray comes with a physics library and you
should be able to get the source code for the shaders from mental
images' FTP site. If you look into the header file32 of the shader called
dielectric_material you will find all parameters for this
material shader. In this case I used the col parameter for
the light absorption per unit-length and the ior parameter
for the index of refraction:
material "crystal"
opaque
"dielectric_material" (
"ior" 1.5,
"col" 0.5 0.5 0.5,
"lights" [ "Lamp_inst", "Sun_inst" ]
)
end material
Ray tracing can be either turned on in the global options
statement or a shader33 specifies that it can operate only if ray tracing is
enabled. In the later case mental ray will enable ray tracing even if
no ray tracing was specified in the global options statement.
light "Lamp"
"physical_light" (
"color" 60 60 60
)
visible
origin 0.0 0.0 0.0
sphere .125 15 15
end light
For mental ray the type of a light source is determined by the absence
or presence of some parameters. If you define only an origin
you will have a point light. An infinite light
requires only a direction whereas a spot light must be
given an origin, a direction, and a spread value.
Please be aware that the spread value is already the
cosine value of a given angle. Most of the standard shaders
for spot lights will have a cone parameter which is also given
as a cosine value instead of specifying the angle in degrees
or radians. For RenderMan compatible renderers the spotlight
shader requires angles in radians and calculates the cosine
value inside the shader.
There are four different shapes of area light sources: rectangles,
flat discs, spheres, and cylinders. Although mental ray 3.1 adds
arbitrary geometric objects, and a user-defined shape we can simply
use a sphere with an radius of
and
samples in u-
and v-direction. To make the light source sphere visible in the
reflection and refraction of the glass sphere on the blue box we use
the keyword visible. Have a look in the source code of light
source shaders to see how to deal with rays which hit the geometry of
the light source directly. Another aspect of area light sources are
soft shadows. You don't get soft shadows for the light source
inside the room by using Radiance but you do get soft shadows for
mental ray. The difference is that Radiance uses always visible light
sources but this does not necessarily mean that Radiance uses
area lights.
illum material used by Radiance for the window.
Have a look again at figure 17 or
18. There are two aspects of the light coming
through the window. There is a very bright spot of light cast on the
ground and one of the walls, and there is the visibility of the
window itself. If you compare figure 17 and
18 more carefully you might notice that you get a
reflection of the light source inside the room even in the
reflections of the window itself in the image created by Radiance.
This additional reflection is missing in the image I created with
mental ray.
Let's first read what the illum material in Radiance does:
``Illum is used for secondary light sources with broad
distributions. A secondary light source is treated like any other
light source, except when viewed directly. It then acts like it is
made of a different material (indicated by the string argument),
or becomes invisible (if no string argument is given, or the
argument is "void"). Secondary sources are useful when modelling
windows or brightly illuminated surfaces.''
I decided to split the two aspects into two different solutions for
mental ray. The very bright spot of light cast on the ground and one
of the walls is simulated by adding another light source to the
scene which acts like the sun shining from outside through the
window on the floor. The sky simulation in Radiance creates the
following file called sky.rad:
# gensky 3 20 10 -a 40 -o 98 -m 105 # Local solar time: 10.34 # Solar altitude and azimuth: 43.3 -35.4 # Ground ambient level: 17.7 void light solar 0 0 3 6.73e+06 6.73e+06 6.73e+06 solar source sun 0 0 4 0.421409 -0.593560 0.685639 0.5 void brightfunc skyfunc 2 skybr skybright.cal 0 7 1 1.10e+01 2.12e+01 5.45e-01 0.421409 -0.593560 0.685639
I don't want to explain exactly how this works but the important
information from this file is the direction where the sun is. There is
a vector
used twice in this
file. This is the direction vector to the sun. Instead of using a very
distant light with a very high color value representing the energy I
decided to calculate a matrix which will position the light source for
the sun 100 units away from the middle of the window34 along the vector mentioned above and
find some values for the light energy which do look good:
light "Sun"
"physical_light" (
"color" 1e7 1e7 1e7
)
origin 0 0 0
end light
instance "Sun_inst" "Sun"
transform
1.0 0.0 0.0 0.0
0.0 1.0 0.0 0.0
0.0 0.0 1.0 0.0
-45.1409 58.356 -69.5639 1.0
end instance
The reason why I didn't use a distant light source is that in mental ray a distant light source has no origin. But how can I specify that the light source representing the sun is outside the room and does not illuminate other objects directly beside that little portion of the ground and one of the walls? I think 100 units is far away enough to make the light rays which are emitted from a point light and go through the window nearly parallel and the values for the light color a purely found by ``try and error''.
The second aspect is the visibility of the window itself. Because the window is never seen directly in the final image I decided to represent the ``window'' just by the absence of geometry at the hole in the back wall. This hole means that the reflected rays will leave the scene without hitting any geometry. In this case an environment shader is called by mental ray if the MI file representing the scene defines one. I first tried to use a simple phenomenon:
declare phenomenon
color "env_sh" (
)
shader "tex" "mib_opacity" (
"input" 9.0 9.0 10.0,
"opacity" 1.0 1.0 1.0
)
root = "tex"
end declare
camera "Camera"
frame 1
output "rgb" "room.rgb"
focal 35.0
aperture 32.0
aspect 1.0
resolution 510 510
environment "env_sh" ()
end camera
This phenomenon creates an unwanted artefact. The rim of the sphere gets the same slightly blue color as the reflections of the window. Why is that? Well, at the rim of the sphere the refracted and reflected rays hitting the front and back of the sphere might bounce back and forth. This effect can happen a lot with material similar to glass especially if it's attached to geometry with a lot of curvature or local displacements. The solution to this problem is to write a very simple environment shader:
/* myEnvironment.c */
#include <shader.h>
struct myEnvironment
{
miColor color;
};
DLLEXPORT miBoolean
myEnvironment(
miColor* result,
miState* state,
struct myEnvironment* paras
)
{
miColor* color;
/* get parameters */
color = mi_eval_color(¶s->color);
/* result */
if (state->reflection_level >= (state->options->reflection_depth - 1) ||
state->refraction_level >= (state->options->refraction_depth - 1) ||
(state->reflection_level + state->refraction_level) >=
(state->options->trace_depth - 1))
{
result->r = 0.0;
result->g = 0.0;
result->b = 0.0;
}
else
{
*result = *color;
}
return(miTRUE);
}
Basically environment shaders get called for rays that leave the scene entirely, and for rays that would exceed the trace depth limit. If mental ray reaches the maximum number of reflection or refraction rays or a combination of them my simple environment shader will return ``black'' instead of the color which was given to the shader as a parameter.
Now you can change the environment shader attached to the camera to render the same image as shown in figure 18:
...
link "myEnvironment.so"
$include <myEnvironment.mi>
...
camera "Camera"
...
environment "myEnvironment" ("color" 9.0 9.0 10.0)
end camera
...
The high values for the color are not motivated by proper physics. I
used ``try and error'' again to find values which do look good but be
aware that if you would rotate the camera and look straight at the
``window'' the result would be very bright. In Radiance however you
could do that because the illum material takes care of that.
Another thing which is missing in the final image rendered with mental ray are the shadows cast by light which was emitted from the sun, scattered around by hitting the ground, buildings or plants, and finally contributed to the illumination of the room. I thought about adding another area light at the window position which is not visible but does emit a few rays to create a soft shadow on the wall behind the glass sphere. But I leave that as an exercise.
physical_light shader should match the energy used to emit
photons. See section 4.4 for a better example with
caustics.