Tuesday, April 14, 2015

Sampling a disk revisited

In almost all variants of Monte Carlo renderers there are various point samples that are averaged.  The most common example is a square pixel with box filter (i.e., the average within the pixel is taken).  Underlying whatever screen coordinates you use, there is some
"get ith point in square" or "get N points in square" function.   The square is usually [0,1)^2.  These always seem to add more ugliness to programs than I would expect, but we'll save that for another time.   Additionally, it's really a multidimensional sampling (i.e., antialised motion-blur is 3D) a topic we will also defer.

There are five basic strategies for getting 2D samples on the unit square:
  1. Regular (lattice, sometimes rotated.)
  2. Random
  3. Jittering (stratified sampling)
  4. QMC
  5. Optimized
Of these I suspect optimized (introduced by Don Mitchell back in the day) is the best, but that is still a topic of research.  I'm going for easy so will do jittered.

Jittered is usually done for perfect square number of samples because it is a SxS grid that "jittered" or perturbed.  Pseudocode for Jittered for S^2 samples (e.g, for S = 4, 16 samples) is:

Vec2 get_sample(int s, int sqrt_num_samples)
        float i = s % sqrt_num_samples
        float j = s /  sqrt_num_samples
        return Vec2((i+drand48())/ sqrt_num_samples, (i+drand48())/ sqrt_num_samples)

If we are going to sample a disk (like a lens) people typically sample a unit disk centered at (0,0) with radius 1.    If we transform a sample (u,v) on [0,1)^2 to a disk we can't do this:

Vec2 transform_to_disk(Vec2 on_square)
        theta = 2*PI*on_square.x
        r =   on_square.y
        return (r*cos(theta), r*sin(theta))

Because this will clump up samples near r=0.  Instead we need  r =   sqrt(on_square.y) which compensates for the distortion.

However, there is a "concentric" mapping on the square that some say is better and Dave Cline sent me (see previous blog post) a nicely small code for that so I finally tried it.

Here is an image with random sampling and 25 samples per pixel.
Random sampling 25 samples per pixel
I also tried 45 and 100 samples per pixel with the two different mappings to the disk discussed above.  I zoomed in (using cut and paste so not a very precise test-- in my experience if a precise test is needed it is not a big effect).  This are zoomed in with 5X and pixel duplication

25 samples per pixel, random, jittered, and jittered with concentric map.

49 samples per pixel, random, jittered, and jittered with concentric map.

100 samples per pixel, random, jittered, and jittered with concentric map.

 All in all for this case I do think the concentric map does make a SLIGHT improvement.  But the big leap is clearly jittering at all.   Still, Dave Cline's code is so simple I think it is worth it.

No comments: