Kevin Beason's smallpt estimates the pixel radiance by accumulating $2\times2$ subpixel radiance estimates using the following expression:
c[i] = c[i] + Vec(clamp(r.x),clamp(r.y),clamp(r.z))*.25; Here, c[i] represents the radiance estimate of the pixel with flattened index i, r represents the radiance estimate of one of its four subpixels. As one notices a clamp operation is used to clamp the radiance estimate of each subpixel separately to the $[0,1]^3$ range before accumulation. But this seems wrong from an unbiased path tracing perspective, since this effectively introduces bias to the radiance estimate of each pixel. So I wonder if I am missing something (e.g., clever trick at the cost of bias), given that Kevin Beason's smallpt is already around and known for nearly ten years?
Apparently, Kevin Beason also added a presentation by David Cline explaining every line. The aforementioned expression is explained as follows:
Add the gamma-corrected subpixel color estimate to the Pixel color
c[i].
This seems, however, completely unrelated since no gamma correction is involved yet at this stage in the code. And since exponential functions are non-linear transformations. (Obviously clamp does not involve any exponential functions at all. ;-) )
Reference code:
for (int y=0; y<h; y++){ // Loop over image rows fprintf(stderr,"\rRendering (%d spp) %5.2f%%",samps*4,100.*y/(h-1)); for (unsigned short x=0, Xi[3]={0,0,y*y*y}; x<w; x++) // Loop cols for (int sy=0, i=(h-y-1)*w+x; sy<2; sy++) // 2x2 subpixel rows for (int sx=0; sx<2; sx++, r=Vec()){ // 2x2 subpixel cols for (int s=0; s<samps; s++){ double r1=2*erand48(Xi), dx=r1<1 ? sqrt(r1)-1: 1-sqrt(2-r1); double r2=2*erand48(Xi), dy=r2<1 ? sqrt(r2)-1: 1-sqrt(2-r2); Vec d = cx*( ( (sx+.5 + dx)/2 + x)/w - .5) + cy*( ( (sy+.5 + dy)/2 + y)/h - .5) + cam.d; r = r + radiance(Ray(cam.o+d*140,d.norm()),0,Xi)*(1./samps); } // Camera rays are pushed ^^^^^ forward to start in interior c[i] = c[i] + Vec(clamp(r.x),clamp(r.y),clamp(r.z))*.25; } } 