Graphics Reference
In-Depth Information
1. Multipass Rendering: Make one pass per light over all geometry, sum-
ming the individual results. This works because light combines by superposition.
However, one has to be careful to resolve visibility correctly on the first pass and
then never alter the depth buffer. This is the simplest and most elegant solution.
It is also the slowest because the overhead of launching a pixel shader may be
significant, so launching it multiple times to shade the same point is inefficient.
2. Übershader: Bound the total number of lights, write a shader for that max-
imum number, and set the unused lights to have zero power. This is one of the
most common solutions. If the overhead of launching the pixel shader is high and
there is significant work involved in reading the BSDF parameters, the added cost
of including a few unused lights may be low. This is a fairly straightforward mod-
ification to the base shader and is a good compromise between performance and
code clarity.
3. Code Generation: Generate a set of shading programs, one for each num-
ber of lights. These are typically produced by writing another program that auto-
matically generates the shader code. Load all of these shaders at runtime and bind
whichever one matches the number of lights affecting a particular object. This
achieves high performance if the shader only needs to be swapped a few times
per frame, and is potentially the fastest method. However, it requires significant
infrastructure for managing both the source code and the compiled versions of all
the shaders, and may actually be slower than the conservative solution if changing
shaders is an expensive operation.
If there are different BSDF terms for different surfaces, then we have to deal
with all the permutations of the number of lights and the BSDF variations. We
again choose between the above three options. This combinatorial explosion is one
of the primary drawbacks of current shading languages, and it arises directly from
the requirement that the shading compiler produce efficient code. It is not hard to
design more flexible languages and to write compilers for them. But our motiva-
tion for moving to a hardware API was largely to achieve increased performance,
so we are unlikely to accept a more general shading language if it significantly
degrades performance.
4. Deferred Lighting: A deferred approach that addresses these problems but
requires more memory is to separate the computation of which point will color
each pixel from illumination computation. An initial rendering pass renders many
parallel buffers that encode the shading coefficients, surface normal, and location
of each point (often, assuming an übershader). Subsequent passes then iterate over
the screen-space area conservatively affected by each light, computing and sum-
ming illumination. Two common structures for those lighting passes are multiple
lights applied to large screen-space tiles and ellipsoids for individual lights that
cover the volume within which their contribution is non-negligible.
For the single-light case, moving from our own software rasterizer to a hard-
ware API did not change our perspectiveProject and shade functions sub-
stantially.
However, our shade function was not particularly powerful. Although we did
not choose to do so, in our software rasterizer, we could have executed arbitrary
code inside the shade function. For example, we could have written to locations
other than the current pixel in the frame buffer, or cast rays for shadows or
reflections. Such operations are typically disallowed in a hardware API. That is
because they interfere with the implementation's ability to efficiently schedule
parallel instances of the shading programs in the absence of explicit (inefficient)
memory locks.
 
Search WWH ::




Custom Search