Graphics Reference
In-Depth Information
instead of reference, for example. In the process of redeclaring a parameter to
make this syntax change, you should think about why the parameter was passed
by value in the first place, and whether the computational overhead or software
abstraction of doing so is justified.
To avoid distracting details, for the low-level renderers we'll write the image
to an array in memory and then stop. Beyond a trivial PPM-file writing routine, we
will not discuss the system-specific methods for saving that image to disk or dis-
playing it on-screen in this chapter. Those are generally straightforward, but ver-
bose to read and tedious to configure. The PPM routine is a proof of concept, but
it is for an inefficient format and requires you to use an external viewer to check
each result. G3D and many other platforms have image-display and image-writing
procedures that can present the images that you've rendered more conveniently.
For the API-based hardware rasterizer, we will use a lightly abstracted subset
of the OpenGL API that is representative of most other hardware APIs. We'll
intentionally skip the system-specific details of initializing a hardware context
and exploiting features of a particular API or GPU. Those transient aspects can be
found in your favorite API or GPU vendor's manuals.
Although we can largely ignore the surrounding platform, we must still choose
a programming language. It is wise to choose a language with reasonably high-
level abstractions like classes and operator overloading. These help the algorithm
shine through the source code notation.
It is also wise to choose a language that can be compiled to efficient native
code. That is because even though performance should not be the ultimate con-
sideration in graphics, it is a fairly important one. Even simple video game scenes
contain millions of polygons and are rendered for displays with millions of pix-
els. We'll start with one triangle and one pixel to make debugging easier and then
quickly grow to hundreds of each in this chapter. The constant overhead of an
interpreted language or a managed memory system cannot affect the asymptotic
behavior of our program. However, it can be the difference between our renderer
producing an image in two seconds or two hours . . . and debugging a program that
takes two hours to run is very unpleasant.
Computer graphics code tends to combine high-level classes containing sig-
nificant state, such as those representing scenes and objects, with low-level classes
(a.k.a. “records”, “structs”) for storing points and colors that have little state and
often expose that which they do contain directly to the programmer. A real-time
renderer can easily process billions of those low-level classes per second. To sup-
port that, one typically requires a language with features for efficiently creating,
destroying, and storing such classes. Heap memory management for small classes
tends to be expensive and thwart cache efficiency, so stack allocation is typically
the preferred solution. Language features for passing by value and by constant
reference help the programmer to control cloning of both large and small class
instances.
Finally, hardware APIs tend to be specified at the machine level, in terms of
bytes and pointers (as abstracted by the C language). They also often require man-
ual control over memory allocation, deallocation, types, and mapping to operate
efficiently.
To satisfy the demands of high-level abstraction, reasonable performance for
hundreds to millions of primitives and pixels, and direct manipulation of memory,
we work within a subset of C++. Except for some minor syntactic variations, this
subset should be largely familiar to Java and Objective C++ programmers. It is
Search WWH ::




Custom Search