rendering soft shadows and glossy reflections with cone...
TRANSCRIPT
Rendering Soft Shadows and Glossy Reflections with Cone Tracing Keaton Brandt – [email protected]
FIGURE 1: A 200 polygon Stanford Bunny with soft shadows rendered using traditional ray tracing (left, 32 shadow samples) and cone tracing (right, 64x64 cone buffer resolution).
Abstract In this paper we demonstrate a method of rendering scenes
using cone tracing, a variant of ray tracing. Specifically, we
use cones to render realistic shadows from non-‐‑point light
sources (soft shadows), and to simulate reflections from
surfaces that are not perfect mirrors (glossy reflections).
These effects are possible with traditional ray tracing, but
require a great deal of compute power to get good results.
Our cone tracing algorithm is able to out-‐‑perform ray
tracing in most scenarios.
1 Introduction Ray tracers model light using infinitely thin straight lines,
which results in a relatively accurate physical simulation of
photons but requires massive oversampling of the scene in
order to produce clean results. To render things like soft
shadows and glossy reflections, ray tracers ‘distribute’
single beams into dozens or even hundreds of secondary
beams that emanate outwards from intersection points.
Cone tracing simplifies this step by modeling these
secondary rays as a single volumetric cone shape that can
be intersected with the scene geometry. In other words,
instead of modeling the actual straight-‐‑line photons, the
renderer models the entire region in which those photons
could potentially exist.
Such a system has two major advantages. First, it speeds up
rendering by reducing the number of intersections that
need to be calculated. Second, it does not involve any
randomness and therefore reduces the amount of noise in
the scene. It can, theoretically, replicate any effect currently
accomplished with distributed ray tracing.
The biggest downside is that it adds a huge amount of
complication to the rendering engine, which makes the
software harder to extend and optimize. It can also be very
difficult to debug due to the number of sub-‐‑systems
involved (cone intersection, scan-‐‑line conversion, cone
generation, geometry division, etcetera). For these reasons,
cone tracing is rarely used in production environments.
However, Occam’s Razor is not an immutable law of the
universe; sometimes more complicated solutions are more
effective than simpler ones. For this reason, we believe that
cone tracing should be revisited, and have demonstrated
that it can work alongside ray tracing to produce fast,
excellent results in a wide variety of situations.
1.1 Prior Work Cone tracing was first proposed in “Ray Tracing with
Cones” [Amanatides 1984]. The paper outlines the basic
concept and a variety of approximate intersection
algorithms. The results are particularly impressive for 1984,
however he does not compare them to traditional ray
tracing for quality or speed. His system also models
everything as cones, even in situations where rays would
suffice and be faster. Still, it is easily the foundational paper
of the field, and most of the algorithms in this paper are
based off of his work.
A similar method, beam tracing, was proposed the same
year in “Beam Tracing Polygonal Objects” [Heckbert &
Hanrahan 1984]. It is a similar concept, but it uses
rectangular prism or square pyramid shapes instead of
circular cones. Their work involved optimizing non-‐‑glossy
reflection and refraction by taking advantage of the spacial
coherence of a scene using a ‘beam tree’, instead of the
traditional recursive approach to ray tracing. The paper
provides a much more detailed approach to fast CPU-‐‑based
scan conversion of polygons that acempts to minimize gaps
between shapes.
The beam tracing approach is expanded to handle scene
antialiasing in “A Beam Tracing Method with Precise
Antialiasing for Polyhedral Scenes” [Ghazenfarpour &
Hasenfraf, 1998]. Their method is able to work on any
convex polyhedra, and includes many optimizations not
possible for more complex scenes. They also briefly explore
applying these same optimizations to soft shadows, and
compare those results to traditional ray tracing. They were
the first to prove that volumetric samples (beams or cones)
can be more computationally efficient than rays.
Cone tracing has not achieved widespread adoption,
however with the advent of programmable GPUs there has
been a resurgence of interest in the topic. For example,
“Interactive Indirect Illumination Using Voxel Cone
Tracing” [Crassin et. al. 2011] explores its use as a way to
achieve effects such as radiosity in real time on the GPU (it
was co-‐‑sponsored by Nvidia). They do this by representing
the scene as a grid of cubic ‘voxels’, rather than polygon
meshes or surface geometry. This modern research proves
that cone tracing is worth revisiting, and that its usefulness
may actually extend beyond what distributed ray tracing is
capable of.
2 Approach This paper describes a system capable of using both rays
and cones to render a scene. The rendering of each pixel
starts with a ray being cast outwards from the camera.
When that ray hits an object, the ray tracer calls on the cone
tracer to determine the shadow and reflection colors at the
intersection points, and factors those results into those final
color calculations for the pixel. The cone tracer can also call
itself to create tertiary cones for reflections and shadows
inside of reflections. This modular system, diagrammed in
figure 2, lays out a clear separation of concerns for the
different classes.
FIGURE 2: A high-‐‑level view of the architecture of the dual-‐‑mode rendering engine.
Render Thread
Ray Tracer
Shadow Occlusion Cone Tracer
Reflection Cone Tracer
Pixel Location Pixel Color
% OcclusionPoint, Normal Reflection
ColorPoint, Light
Reflection Intersection Point -‐‑> % Occlusion
The cone tracer itself is broken up into several different
modules. The core class creates cone objects, lists and sorts
their intersections with objects in the scene, and then has
the cone buffer class determine a ‘cones eye view’ and
average the results. The cone-‐‑object intersection algorithms,
discussed in detail in section 3, are included in the
representative classes for each object to allow for easy
extension. The cone buffer class includes a scan-‐‑line
renderer that works for circle, plane and polygon shapes,
and can draw either greyscale or color data. Each
component can be debugged individually, which goes a
long way towards addressing the complexity problems.
The ray tracing class can also be toggled to use distributed
ray tracing instead of cone tracing. That system includes
stratified sampling to reduce noise. Scene-‐‑wide antialiasing
is also accomplished using distributed ray tracing. The
rendering engine does not include a volumetric data
structure such as a k-‐‑d tree for faster intersection lookup,
which dramatically slows down scenes with a large number
of polygons.
2.1 Cone Representation
The cone representation class is a simple extension of a
standard ray representation, which includes an origin point
and a direction vector. Cones are modeled as rays with a
non-‐‑zero width that changes linearly with the distance
along the ray. In other words, the radius of the cone is
represented as a linear equation:
Where t is a parameter distance along the center line of the
cone. Because all cones use this linear function, their width
can be represented with only the spread coefficient, h.
Therefore, cones can be entirely modeled by two vectors
(origin and direction) and one scalar (h).
3 Intersection Algorithms Determining intersections between 3D shapes and cones is
a licle more challenging than using rays. For one thing, ray-‐‑
object intersections always result in a finite number of
points, of which the closest one can be chosen. Cone-‐‑object
intersections generally result in 3D lines, generally modeled
using cubic equations. As a result, direct system-‐‑of-‐‑
equations solutions are inefficient and unnecessary. Instead,
a number of different approximations are used, which
achieve accurate results in most situations.
3.1 Cone-Sphere Intersection
If it is assumed that the entirety of the sphere is in front of
the cone’s origin point, intersections can be determined by
simply finding the shortest distance from the center point of
the sphere to the center ray of the cone. An intersection
exists if this distance, d, is less than the sum of the radius of
the sphere and the radius of the cone at that point. Figure 3
demonstrates this graphically.
Finding d first involves finding a plane orthogonal to the
center line of the cone that includes the center point of the
sphere.
Where is the center point of the sphere and is the
direction vector of the cone. The plane is defined implicitly,
so finding the distance from the origin point of the cone to
the plane (the t parameter) is a simple macer of algebra.
Knowing t is not only useful for determining the radius of
the cone, it also means that d can be calculated using the
pythagorean theorem.
Where c is the origin point of the cone. The cone and sphere
intersect if and only if d < cr + r(t). Center RayOrigin
r(t)
t
3.2 Cone-Polygon Intersection
Finding the actual 3D intersection between a cone and an
arbitrary polygon is a very complicated problem. It is also a
very common problem, since 3D models are usually treated
as collections of triangles or quads. Luckily, it can be
reduced to a 2D problem using the same perspective
transformations that simulate cameras [Amanatides 1984].
In other words, instead of dealing with each point of the
polygon in 3D space, each point is mapped to a 2D location
in the “cone’s eye view” and the intersection algorithm
operates on that.
The transformation matrix is calculated the same way as it
would be for a camera. The focal length, f, is equal to 1/h.
The near clipping plane is set as close to zero as possible
without encountering floating point error, and the far plane
is set to infinity. With only these variables, an intrinsic
camera matrix can be created.
This matrix can then be multiplied by rotation and
translation matrices to fully represent the point of view of
the cone. Then 3D points can be mapped onto the cone’s 2D
viewport by multiplying them by the transformation
matrix. A point that lies on the cone’s center line will map
to (0,0). Any point whose distance from (0,0) is less than 0.5
is considered to be within the circular viewport.
Even with the points reduced to 2D, several cases have to be
considered when checking for intersections.
3.2.1 Vertex in View
The simplest, and most common, case occurs when one of
the vertices of the polygon is within view of the cone. If the
cone can see a vertex, it can obviously see the shape.
3.2.2 Cone Center inside Polygon
If a polygon blocks the entire view of the cone, none of its
points will be within the viewport. To detect this situation,
2D ray casting is used. If every ray passing from the center
of the viewport through one of the edges of the polygon
intersects an odd number of edges, the circle is inside the
polygon. An even number of intersections means the ray
both entered and exited the polygon, which means that it
was not inside the shape to begin with. This is the most
compute intensive check, so it is performed last.
3.2.3 Edge Intersection
Even if both of those tests fail, there could still be an
intersection. As Figure 4 demonstrates, it is possible to have
an edge of the polygon intersect with an edge of the
viewport. These intersection points can be found using a
similar method to the sphere intersection algorithm
described in Section 3.1. The code finds the closest point on
the edge to (0,0), the center of the viewport. If the point is
closer than 0.5 units away, there is an intersection.
FIGURE 3: A 2D diagram representing the logic behind cone-‐‑sphere intersection. The left cone has an intersection, the right one does not.
dcr
r(t)
dcr
r(t)
FIGURE 4: Different types of cone-‐‑triangle intersections.
Vertex in View Cone Center Inside
Edge Intersection
3.3 Cone-Plane Intersection
The third intersection algorithm came in handy for the
simple test scenes used in this paper, but is less common in
production applications. It models a plane that extends
infinitely in all directions, which is useful in part because it
is so easy to find intersections.
The sine of the angle between the center of the cone and the
normal vector of the plane is determined using a dot
product. The sine of the spread angle of the cone is also
determined as:
If the angle between the cone and the normal is less than
sin(π/2) -‐‑ a then there is no intersection. If it is greater than
sin(π/2) + a then there is a full intersection (the plane fully
occludes the cone). The percent occlusion of the cone varies
linearly from sin(π/2) -‐‑ a to sin(π/2) + a.
4 Scan-line Rendering The viewpoint of each cone is rendered using a simple
CPU-‐‑based scan-‐‑line renderer. Only the average value of
this viewport actually macers, but it is important to draw
the shapes correctly in a 2D buffer because some objects
may partially or entirely occlude other objects.
To cut out the relatively costly averaging step at the end,
the renderer keeps a running total of every pixel in view.
Whenever a pixel is changed, the sum is updated. The soft
shadow cone tracer determines occlusion by comparing the
sum of the buffer after the light is drawn, and then again
after every object in front of the light is drawn. The light has
a value of 255 and the objects have a value of 0, so any
objects blocking the light will decrease the sum.
Every polygon shape can be reduced to triangles. Plane
intersections are represented as quads that face the correct
direction and occlude the correct amount of the view.
Therefore, only two scan-‐‑line rendering algorithms are
needed for the buffer.
4.1 Circle Rendering
Scan-‐‑line renderers are so named because they draw shapes
one line at a time. Therefore, scan-‐‑line rendering of a circle
essentially boils down to figuring out where each line starts
and ends (because a circle has no holes in the middle).
Using the pythagorean theorem, it is easy to find the width
of a circle at a given y value above or below its center.
Where r is the radius of the circle and cy is the y component
of its center in viewport coordinate.
A line is drawn from xstart to xend for every scan-‐‑line,
clipping both values to be within range of the buffer.
4.2 Triangle Rendering
Triangles are easy to render in scan-‐‑lines when 2 of the
vertices are lined up on the y axis. These axis-‐‑oriented
triangles can be rendered by simply interpolating the start
and end x values from the 2 aligned points to the 1 outlier
point. All triangles can be converted into axis-‐‑aligned
triangles by splicing them in half. The split point passes
through the vertex with the median y-‐‑value. Then, both
halves are rendered separately. Anti-‐‑aliasing can be added
by blending pixels with non-‐‑integer x or y values.
FIGURE 5: Scan-‐‑line rendering process for triangles.
4.3 Rendering Glitches
It was very difficult to achieve clean results due to the
number of different edge cases present when working with
complex 3D geometry. For example, when some points on
the polygon are behind the camera and some are in front,
the 2D mapping is unreliable. The algorithm currently does
not account for this problem (OpenGL solves it with
frustum culling).
Another problem is that floating point error causes triangles
to not perfectly line up, which often causes some scan-‐‑line
to incorrectly be marked on or off. Two examples of this,
caused by slightly different phenomena, are shown in
Figure 6.
5 Reflections Glossy Reflections proved to be a very difficult challenge,
mostly due to the difficulty of debugging them. Even
simple scenes can end up with dozens of reflections and
shadow cones per pixel, each contributing to one another in
non-‐‑obvious ways. Figure 7 shows one such scene. For this
reason, we were never able to achieve desirable results from
cone-‐‑traced reflections. However, much of the math behind
the reflection algorithm is solid, at least for the cases
examined by this paper.
5.1 Generating Secondary Reflection Cones
Secondary reflection cones are cones that are created when
a reflection cone intersects a reflective object. The secondary
cone must cover the entire intersection region, and reflect
across the normal of the intersection point. This is easy for
flat geometry that has only one normal and a clear
boundary for intersection. Spherical geometry is more
complicated, but can be approximated by simply taking the
normal at the center point of the intersection.
When reflecting off of flat, non-‐‑glossy surfaces, the spread
value of the secondary cone remains the same. Glossy
surfaces will increase the spread value, since glossy objects
are able to reflect a slightly larger area of the scene. Curved
surfaces will also affect the spread value; Convex curves
will expand it, concave curves will decrease or even negate
it. Objects with both concave and convex regions will need
to be split into multiple shapes (this is beyond the scope of
the paper).
The intersection is represented with a circumcircle, a flat
circle that contains the entire intersection. A cone is then
created with the desired spread value that passes through
this circle. The t parameter of the cone is offset so that the
original circle is the cone’s slice at t=0. In other words, the
cone extends behind the intersection, but that part is
ignored.
FIGURE 6: Two examples of rendering bugs. Left: A shadow cone buffer from the Stanford Bunny scene showing horizontal line artifacts. Right: A shadow buffer demonstrating poorly anti-‐‑aliased lines.
FIGURE 7: A debugging view of all the cones created for a single pixel, each represented by their center line. Red = Reflection Cone. Blue = Shadow Cone.
6 Results In most cases, our cone tracing algorithm was able to out-‐‑
perform ray tracing on the same scene. In some cases, such
as the Stanford Bunny in Figure 1, the differences are
dramatic. The cone tracer was able to achieve a nearly
perfect render (a very smooth shadow with no visible
graininess), whereas the ray traced render is noticeably
noisy and blotchy. It’s worth noting that both of the renders
contain a small amount of noise caused by glitches in the
multithreaded renderer. These are likely due to one of the
libraries or classes not being entirely thread safe.
The cone tracer also won out in speed. The ray tracer took 1
hour, 31 minutes, versus a mere 19 minutes taken by the
cone tracer. In other words, our cone tracing algorithm was
able to produce dramatically becer results, 5 times faster.
This may not be an entirely fair comparison though, mostly
due to the fact that our rendering engine does not include a
volumetric search data structure for intersections. Both the
ray tracer and the cone tracer are forced to check each of the
200 polygons in the scene for an intersection. However, the
cone tracer only needs to do this once per shadow pixel,
where the ray tracer must do it once for each of the 32
samples. The ability to work well in environments like this
is certainly an advantage of cone tracing, but presenting
these results as a general case would be disingenuous.
The simpler test scene with 3 spheres (Figure 8) is a much
fairer test case. Finding intersections with spheres is very
easy, and with only 3 of them it is reasonable to assume that
checking each of them has similar performance to the
overhead of a data structure such as an octree or kd-‐‑tree. In
this case, the renders are set up to achieve similar results, so
the number of shadow samples has been doubled on the
ray tracer and halved on the cone tracer. As Figure 8
demonstrates, the results are indeed very similar, although
cone tracing still has a slight edge in noise and smoothness.
It also still has an edge on performance. Ray tracing took
approximately 5 minutes, while cone tracing took just over
1 minute. Again, a 5x speed increase.
FIGURE 8: A scene rendered with Distributed Ray Tracing (top) and Cone Tracing (bocom).
Ray Tracing. 64 Shadow Samples. 4:59 Render Time
Cone Tracing. 32x32 Buffer. 1:03 Render Time
FIGURE 9: Detail view of the Stanford Bunny render in Figure 1. Left: Ray Tracing, Right: Cone Tracing.
The reflection results are decidedly less impressive. Our
implementation suffers from a myriad of bugs common to
GPU rendering, such as epsilon problems, z-‐‑fighting, and
floating point error. It also has the problem discussed in
Section 5 where geometry with points on both sides of the
cone cannot be properly mapped into the cone buffer. This
leads to the bug demonstrated in Figure 11. This bug was
never fixed, which is why the scene in Figure 10 has an
infinite plane instead of a ground quad.
Figure 10 shows that our algorithm clearly needs more
work when it comes to glossy reflections. Most of the detail
is lost, and the elements that remain appear to be blown
out. There are also some even more basic bugs, such as the
reflection of the red sphere gecing randomly cut off on the
blue sphere (likely due to an incorrectly calculated z-‐‑value
for the ground plane). Our implementation also does not
work with textured surfaces, and doing so would likely
slow it down significantly since the cone buffer would need
to sample the texture at each pixel.
Still, the cone tracer was able to produce at least slightly
accurate results in just 2.5% of the time it took the ray tracer
to produce grainy results. Even with the texture sampling
in place, it is likely that cone tracing could handily beat ray
tracing. However, we cannot prove that conclusively with
these results.
7 Conclusion Cone tracing does turn out to be very complicated and
difficult to debug. The code for this project took
approximately 42 hours to write, and is based off of an
existing ray tracer. The most time consuming parts were
debugging the scan-‐‑line renderer and figuring out all of the
different intersection algorithms. There are certainly many
more things that can go wrong.
However, cone tracing has also proven itself to be far more
efficient than ray tracing in a number of different scenarios.
Even if cones are used for nothing but soft shadows, it can
speed up a render by as much as 500%. With glossy
reflections working, that speed-‐‑up could be even greater.
FIGURE 10: A glossy scene rendered with Distributed Ray Tracing (top) and Cone Tracing (bocom).
Ray Tracing. 32 Glossy Samples. 05:24:52 Render Time
Cone Tracing. 32x32 Buffer. 00:08:04 Render Time
FIGURE 11: A bug that occurs when the cone traced scene is placed on a non-‐‑infinite ground plane
Although cone tracing was first proposed over 30 years ago,
there has been licle research. Optimizations in traditional
ray tracing have made it viable in most situations, so there
has not been much demand for a different strategy.
However, physically accurate real-‐‑time rendering is starting
to become achievable thanks to the dramatic advances in
GPU technology over the past few years. Teams at
companies like Nvidia are starting to demonstrate ray
tracers that are capable of running entirely on the GPU.
These systems generally cannot scale up very well, partly
because of the difficulty of maintaining spacial data
structures for dynamic scenes, and partly due to
inefficiency inherent in the implementation.
Modern GPUs are programmable, but they have limits.
They are still designed around the traditional rendering
pipeline, so everything on top of that relies on hacks like
pucing data into textures. Many GPUs cap the size of
textures due to memory constraints, which in turn
constrains the size of the scene that can be processed. We
propose a hybrid method of real-‐‑time rendering that uses
the CPU to generate rays and cones in a scene, and uses the
GPU to render the cone buffers using its normal pipeline.
Unfortunately, such an idea is well beyond the scope of this
paper, but it shows why research into cone tracing may be
important to the future of computer graphics.
8 References AMANATIDES, J. 1984. Ray tracing with cones. In
Proceedings of the 11th Annual Conference on
Computer Graphics and Interactive Techniques,
ACM, New York, NY, USA, SIGGRAPH ’84,
129-‐‑135.
CRASSIN, C., NEYRET, F., SAINZ, M., GREEN, S. AND
EISEMANN, E. 2011. Interactive Indirect
Illumination Using Voxel Cone Tracing. In
Computer Graphics Forum, 30, 1921–1930.
GHAZANFARPOUR, D. AND HASENFRATZ, J.M. 1998. A
Beam Tracing Method with Precise Antialiasing
for Polyhedral Scenes. Laboratoire MSI -‐‑ Université
de Limoges.
HECKBERT, P. AND HANRAHAN, P. 1984. Beam tracing
polygonal objects. In Proceedings of the 11th Annual
Conference on Computer Graphics and Interactive
Techniques , ACM, New York, NY, USA,
SIGGRAPH ’84, 119-‐‑127