Graphics Reference
In-Depth Information
sense to allow direct access to their representation. The classes that protect their
representation are ones whose representation we may (and truthfully, will) later
want to change. For example, the internal representation of
Triangle
in this list-
ing is an array of vertices. If we found that we computed the edge vectors or face
normal frequently, then it might be more efficient to extend the representation to
explicitly store those values.
For images, we choose the underlying representation to be an array of
Radiance3
, each array entry representing the radiance incident at the center of
one pixel in the image. We then wrap this array in a class to present it as a 2D
structure with appropriate utility methods in Listing 15.5.
Listing 15.5: An
Image
class.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
class
Image
{
private
:
int
m_width;
int
m_height;
std::vector<
Radiance3
> m_data;
int PPMGammaEncode(
float
radiance,
float
displayConstant)
const
;
public
:
Image
(
int
width,
int
height) :
m_width(width), m_height(height), m_data(width
*
height) {}
int
width()
const
{
return
m_width; }
int
height()
const
{
return
m_height; }
void
set(
int
x,
int
y,
const
Radiance3
& value) {
m_data[x + y
*
m_width] = value;
}
const
Radiance3
& get(
int
x,
int
y) const {
return
m_data[x + y
*
m_width];
}
void
save(
const
std::string& filename,
float
displayConstant=15.0f)
const
;
};
Under C++ conventions and syntax, the
&
following a type in a declaration
indicates that the corresponding variable or return value will be passed by ref-
erence. The
m_
prefix avoids confusion between member variables and methods
or parameters with similar names. The
std::vector
class is the dynamic array
from the standard library.
One could imagine a more feature-rich image class with bounds checking,
documentation, and utility functions. Extending the implementation with these is
a good exercise.
The
set
and
get
methods follow the historical row-major mapping from a
2D to a 1D array. Although we do not need it here, note that the reverse mapping
from a 1D index
i
to the 2D indices
(x, y)
is
x = i % width; y = i / width
where
%
is the C++ integer modulo operation.