Graphics Reference
In-Depth Information
actually stores radiance values is informally said to store linear radiance, indi-
cating that the pixel value varies linearly with the radiance (see Chapter 17). Since
the radiance range of a typical outdoor scene with shadows might span six orders
of magnitude, the data would suffer from perceptible quantization artifacts were
it reduced to eight bits per channel. However, human perception of brightness is
roughly logarithmic. This means that distributing precision nonlinearly can reduce
the perceptual error of a small bit-depth approximation. Gamma encoding is
a common practice for distributing values according to a fractional power law,
where 1
is the power. This encoding curve roughly matches the logarithmic
response curve of the human visual system. Most computer displays accept input
already gamma-encoded along the sRGB standard curve, which is about
= 2.2.
Many image file formats, such as PPM, also default to this gamma encoding. A
routine that maps a radiance value to an 8-bit display value with a gamma value
of 2.2 is:
γ
1
2
3
4
int Image ::PPMGammaEncode( float radiance, float d) const {
return int (pow(std::min(1.0f, std::max(0.0f, radiance * d)),
1.0f / 2.2f) * 255.0f);
}
x . Because they are faster than arbitrary exponentiation on
most hardware, square root and square are often employed in real-time rendering
as efficient
Note that x 1 / 2.2
= 2.0 encoding and decoding methods.
The save routine is our bare-bones method for exporting data from the ren-
derer for viewing. It saves the image in human-readable PPM format [P + 10] and
is implemented in Listing 15.6.
γ
Listing 15.6: Saving an image to an ASCII RGB PPM file.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
void Image ::save( const std::string& filename, float d) const {
FILE * file = fopen(filename.c_str(), "wt" );
fprintf(file, "P3 %d %d 255 \ n" , m_width, m_height);
for ( int y = 0; y < m_height; ++y) {
fprintf(file, " \ n# y = %d \ n", y) ;
for ( int x = 0; x < m_width; ++x) {
const Radiance3 & c(get(x, y));
fprintf(file, "%d %d %d \ n" ,
PPMGammaEncode(c.r, d),
PPMGammaEncode(c.g, d),
PPMGammaEncode(c.b, d));
}
}
fclose(file);
}
This is a useful snippet of code beyond its immediate purpose of saving an
image. The structure appears frequently in 2D graphics code. The outer loop iter-
ates over rows. It contains any kind of per-row computation (in this case, printing
the row number). The inner loop iterates over the columns of one row and per-
forms the per-pixel operations. Note that if we wished to amortize the cost of
computing y * m_width inside the get routine, we could compute that as a per-
row operation and merely accumulate the 1-pixel offsets in the inner loop. We do
not do so in this case because that would complicate the code without providing a
measurable performance increase, since writing a formatted text file would remain
slow compared to performing one multiplication per pixel.
 
 
Search WWH ::




Custom Search