The
RealSoft3D ray-tracing process - Part 5:
Secondary Rays
By David Coombes
d.g.coombes@btopenworld.com
Foreword to Part 5
This part explains the way reflections
and refractions are calculated using secondary
rays, and hence why RealSoft3D
is termed a recursive ray-tracer. This concept
is fundamental to a good "mental image"
of RealSoft3D.
Hopefully this should open up the big picture
and make tapping it's power that much easier.
What is a reflection
Diagram showing
reflected light
Here's a very common representation
of a reflection. It shows a yellow sphere
above a mirror. The sphere is illuminated
by what light sources surround it. The arrows
show the direction the light is travelling.
Light bounces from the sphere and travels
to the camera, and to the mirror where it
is reflected towards the camera. The blue
line is the surface normal - the
line perpendicular to the surface. With
all reflections, the angle of the light
from the reflected surface (sphere)
relative to the surface normal is equal
to the angle of the light from the reflecting
surface (floor) relative to the surface
normal.
If the floor is matte white, with
one directional light from the front-left,
the image in the camera would look
like this.
|
With the floor as a perfect mirror,
100% of the light from the sphere
is reflected
|
If the floor is a perfect green
mirror, 100% of the green light
from the surface is reflected and
none of the red or blue...
|
How to produce a reflection
Let's have a look at that diagram again. We can already see how tracing
rays is used to create the scene. For the
light travelling from the sphere directly
to the camera, RealSoft3D
traces a ray from the camera to the sphere.
The values of the sphere's properties, such
as colour, direction of the surface, amount
of illumination, etc., are then stored in
channels and used to create the final image
colour. When examining the floor, also a
ray is cast. But in this instance we need
to show not just the floor but also what
the floor is reflecting. RealSoft3D
has an elegant solution for this.
We already possess a procedure for evaluating
a surface by casting a ray. What happens
with a reflection is that this procedure
is invoked again, only this time tracing
a ray from the point on the mirror that
the camera sees, in the direction that the
ray is reflected. The default actions of
RealSoft3D
are to add the Surface:Illumination
of the reflection to the Surface:Illumination
of the mirror. When a ray cast
invokes another ray, this other ray is called
the Secondary Ray.
The Secondary Ray shader
Referring back to the render pipeline diagram
of Part 4, you'll see the presence of the
secondary ray
shader after surface
illumination. Whenever a material
involves reflections or refractions, this
shader is invoked. It casts a secondary
ray which evaluates a surface exactly as
normal, and then this data is made available
to the secondary ray
shader in the Traced
Ray sample. This sample's channels
holds the information regarding the reflection,
such as Traced Ray:Illumination
storing the illumination of the reflected
surface, and Traced
Ray:Distance
storing the distance from mirrored surface
to reflected surface. Secondary
ray adds reflections to a surface
by adding the amount of reflected light
(Traced Ray:Illumination
* Surface:Reflection)
to the surface's illumination, Surface:Illumination. Lets look at this process by walking
through the tracing of the reflected ray
shown in the above diagram.
This shows some of the shaders
(most of the usually executed ones) in order
of execution, listing the results from those
shaders if they change any of the channel
data. As you can see, the first ray trace
process does not finish until the second
process has because it is waiting for the
results of that secondary
ray shader. When the second ray
has finished, the result of it's Surface:Illumination becomes the value of Traced
Ray:Illumination
in the secondary
ray shader.
How to produce a refraction
Though the term refraction implies the splitting of light
into different colours, in RealSoft3D
it relates to creating transparency effects
by letting light pass through a surface.
Such refractions are created in the same
way as reflections, casting a secondary
ray, only with refractions the secondary
ray is cast the other side of the surface.
Also, the direction of the ray is affected
by the refractive index of the material.
By default refracted rays travel in
the same direction as the initial ray. Instead
of multiplying the illumination of the secondary
ray by Surface:Reflection, transparency secondary rays are multiplied
by Surface:Transparency.
To recap, when a surface is reflective
(Surface:Reflection is not 0), a secondary ray is cast in
the direction the ray would be reflected
off the surface. When a surface
is transparent (Surface:Transparency
is not 0), a secondary ray is
cast in the direction the ray would be refracted,
usually the same direction as the first
ray. The default actions of secondary
ray are to add to Surface:Illumination the following...
Reflection: Surface:Illumination
+= Traced Ray:Illumination
* Surface:Reflection
Transparency: Surface:Illumination += Traced
Ray:Illumination
* Surface:Transparency
If a surface is both reflective and transparent,
the secondary ray
shader is invoked twice, once for each ray
cast.
Recursion
I
called this method 'elegant' because it
doesn't need complicated layers of extra
procedures to produce reflections and refractions.
The same procedure of casting a ray is used
recursively, calling itself again when needed.
This does throw up the question as to what
happens when you have reflections of reflections?
The diagram opposite shows such a
situation. Two parallel mirrors are viewed
from an angle such that a traced ray is
reflected from one side to the other and
back again. As you can see, the first
ray cast invokes another secondary ray,
which in turn invokes another secondary
ray, which in turn...six rays are shown
in the diagram. When a non-reflective surface
is hit, it's illumination is added to Surface:Illumination
of the mirror, which then becomes the Traced
Ray:Illumination of the previous ray cast. Imagine
a non-reflective object where the 6th ray
ends (such as, to be really original, a
yellow sphere). The rays are invoked like
this...
Camera Cast Ray 1
hits reflective surface
Cast Secondary
Ray 2
hits
reflective surface
Cast
Secondary Ray 3
hits
reflective surface
Cast
Secondary Ray 4
hits reflective surface
Cast
Secondary Ray 5
hits reflective surface
Cast
Secondary Ray 6
hits non-reflective surface
Surface:Illumination
+= Surface:Illumination
of non-reflective surface
Traced Ray:Illumination =
Surface:Illumination
of previous ray (6)
Surface:Illumination
+= Traced Ray:Illumination
* Surface:Reflection
Traced Ray:Illumination =
Surface:Illumination
of previous ray (5)
Surface:Illumination
+= Traced Ray:Illumination
* Surface:Reflection
Traced Ray:Illumination =
Surface:Illumination
of previous ray (4)
Surface:Illumination += Traced Ray:Illumination
* Surface:Reflection
Traced
Ray:Illumination = Surface:Illumination of previous ray (3)
Surface:Illumination += Traced Ray:Illumination
* Surface:Reflection
Traced
Ray:Illumination
= Surface:Illumination
of previous ray (2)
Surface:Illumination
+= Traced Ray:Illumination
* Surface:Reflection
Ray Termination
Now imagine these mirrors are very long,
and you start to see that it could happen
that the ray tracing process will get caught
up tracing reflections of reflections of
reflections. There even arise situations
where the rays never meet a non-reflective
surface, such as when tracing internal
reflections in a transparent, reflective
object. In such circumstances RealSoft3D
would get caught in a never ending loop. The
solution is to set a limit as to the number
of recursions that will be allowed. This
is the Recursion Depth setting of
the current render settings. When the
threshold is reached, no subsequent secondary
ray shaders are executed. For
example, if the recursion threshold for
the above diagram were set at three, only
the first three rays would be traced. When
the third ray hit the mirrored surface,
instead of calling the secondary
ray shader and casting another
ray, the shader ray
termination is called. By default
this shader does nothing and rays terminated
because of recursion depth are black (no
illumination), but ray termination can be used for adding to the illumination
of the ray. I'll illustrate the results
of recursion depth and ray
termination with an example scene.
|
The scene is of two
mirrored surfaces viewed along their
lengths. At the end of the mirrors
is a plane with a striped pattern.
There are no light sources; the pattern
is output to the Surface:Illumination
channel. This pattern is reflected
along the mirrors towards the camera.
The mirrors are a slight-blue colour
to show the different recursions. |
|
This render shows
the scene with a high enough recursion
depth (depth = 7) to show all the
recursive reflections. |
|
With a recursion depth
of three, secondary rays are not traced
for higher levels of recursion. The
result is dark areas where no surface
illumination exists. |
|
In the mirror material's
ray termination
shader I've placed the line Surface:Illumination = (1,1,1). Now when the
ray tracing is terminated beyond 3
levels of recursion, the final surface
illumination is white. This is then
reflected in the mirrors. You can
still see the mirrors are showing
only a limited number of reflections
but it does not look as bad as having
no illumination during ray termination. |
|
To show that reflection
termination isn't limited to producing
black or white illumination, here's
the same scene with ray
termination's Surface:Illumination as a yellow colour. One
could just as easily have ray
termination end with a
texture map, procedural texture, or
any other VSL effect. |
In most scenes without transparent surfaces
a recursion depth of three is adequate for
convincing results. When using transparent
materials, overlapping layers of material
can quickly increase the recursion depth
requirement. If for speed reasons you wish
to keep recursion depth to a minimum, using
the ray termination shader can help conceal the rendering
inaccuracies and produce presentable images.
The other cause of premature ray termination
is the render setting Recursion Threshold.
The idea here is that the level of illumination
from some reflections and refractions can
be too low to make any noticable difference
to the rendered scene. In such cases there's
no point in calculating the low-intensity
secondary rays as they won't be visible.
If the result of a secondary ray's Illumination
channel is below the user specified threshold,
the ray termination
shader is invoked and no subsequent recursions
are entered into. This is of particular
use in glass materials where internal reflections
add heavily to recursion calculation requirements
but often don't generate visible results.
A suitable recursion threshold would prevent
these recursions being calculated, thereby
saving on render time.
The Finale
I'll conclude with a look at the recursions
involved when tracing a glass like sphere.
Glass includes both reflections and refractions
and exhibits internal reflections. Each
level of recursion is shown in a different
colour. Reflected rays are shown in the
darker colours, refractions are paler.
The End
This concludes the explanation of ray-trace
recursion, and I guess that wraps up the
RealSoft3D Rendering
Pipeline. You should now be pretty
clued up on samples, channels, shaders and
recursion. What I haven't covered, how
you use this information in creating custom
materials, controlling reflections, and
making use of RealSoft3D's
phenominal post-process capabilities to
generate unique images, is the subject of
future tutorials and explanations.
The next topic I'll tackle will be VSL,
it's understanding and usage. Without a
good groundwork knowledge of the basics
it's easy to get lost trying to figure out
VSL code or create your own, which is why
I've kept it to a minimum so far.
Until then, if you have any questions the
RealSoft3D users
mailing list is the place to ask them.
There are plenty of tutorials
available in the RenderDaemon
pages and if there's enough need you'll
often get a tutorial being created by the
excellent user community to help you out.
Many thanks goes to David Coombs for investing
so much time in writing these terrific articles
- Bernie
|