James Bigler: CS6650 Homework 2




Using this function L(x,y) = 0.5 * (1 + sin(0.01 * x*x + 0.01 * y*y)) plotted over the domain of x:[0,255], y:[0,255] the artifacts of aliasing is immediately noticed. The image can be antialiased by using a filter. The distribution of points can have a big impact on the quality of the sampling. Using only 64 samples per pixels I examine the absolute error and average relative error of different distribution of points as compared to an image generated by a cubic bspline filter with 100,000 samples per pixel.

The sampled image is on the left and the difference image is on the right. Error is reported on the image space of [0,1].



Original image samples with 100,000 cubic bspline samples per pixel

Regular

Absolute error = 1.7099
Average relative error = 0.106389
Maximum relative error = 0.247058

Random

Absolute error = -5.10579
Average relative error = 0.107357
Maximum relative error = 0.313725

Jitter

Absolute error = 1.66678
Average relative error = 0.10527
Maximum relative error = 0.25098

Multi-jitter

Absolute error = -0.360733
Average relative error = 0.0678371
Maximum relative error = 0.243137

Hammersley base 2

Absolute error = 2.42366
Average relative error = 0.0949455
Maximum relative error = 0.239215

Hammersley base 5

Absolute error = 2.05893
Average relative error = 0.0990796
Maximum relative error = 0.243137

Hammersley base 7

Absolute error = 1.7413
Average relative error = 0.0958783
Maximum relative error = 0.239215


// this is sample code

// the function that evalulates L
static float L(float x, float y) {
  return (0.5 * (1 + sinf(0.01*x*x + 0.01*y*y))); 
}

// genfrand is a random number [0,1)

RGB RandomSample::render(const int x, const int y, Worker * wrk) {
  float val = 0;
  for (int index = 0; index < 64; index++) {
    float i = -0.5 + wrk->get_rng()->genfrand();
    float j = -0.5 + wrk->get_rng()->genfrand();
    val += L(x + i, y + j);
  }
  val /= 64;
  return RGB(val,val,val);
}

RGB RegularSample::render(const int x, const int y, Worker * /*wrk*/) {
  float val = 0;
  for (float i = -0.5; i < 0.5; i+=0.125)
    for (float j = -0.5; j < 0.5; j+=0.125) {
      val += L(x + i, y + j);
    }
  val /= 64;
  return RGB(val,val,val);
}

RGB JitterSample::render(const int x, const int y, Worker * wrk) {
  float val = 0;
  for (float i = -0.5; i < 0.5; i+=0.125)
    for (float j = -0.5; j < 0.5; j+=0.125) {
      // [-0.5,0.5] * 0.125 = [-0.0625, 0.0625]
      float i_jitter = (-0.5 + wrk->get_rng()->genfrand()) * 0.125;
      float j_jitter = (-0.5 + wrk->get_rng()->genfrand()) * 0.125;
      val += L(x + i + i_jitter, y + j + j_jitter);
    }
  val /= 64;
  return RGB(val,val,val);
}

void MultiJitterSample::fill_points(Point *p, int m, int n, MT_RNG *rng) {
  // code adapted from Graphics Gems IV pg.370-374 by Chui et.al.
  int i,j;
  float subcell_width = 1./(m*n);
  // Initialize points to the "canonical" multi-jittered pattern
  for (i = 0; i < m; i++)
    for (j = 0; j < n; j++) {
      p[i*n + j].x = (i*n + j + rng->genfrand()) * subcell_width;
      p[i*n + j].y = (j*m + i + rng->genfrand()) * subcell_width;
    }
  // Shuffle x coordinates within each column of cells
  for (i = 0; i < m; i++)
    for (j = 0; j < n; j++) {
      int k = (int) (rng->genfrand() * (n-1-j) + j);
      float t = p[i*n + j].x;
      p[i*n + j].x = p[i*n + k].x;
      p[i*n + k].x = t;
    }  
  for (i = 0; i < m; i++)
    for (j = 0; j < n; j++) {
      int k = (int) (rng->genfrand() * (m-1-j) + j);
      float t = p[j*n + i].y;
      p[j*n + i].y = p[k*n + i].y;
      p[k*n + i].y = t;
    }  
}

RGB MultiJitterSample::render(const int x, const int y, Worker * wrk) {
#define MULTI_M 8
#define MULTI_N 8
  Point p[MULTI_M * MULTI_N];
  fill_points(p,MULTI_M, MULTI_N, wrk->get_rng());
  float val = 0;
  for (int i = 0; i < MULTI_M; i++)
    for (int j = 0; j < MULTI_N; j++) {
      val += L(x + p[i*MULTI_N + j].x - 0.5, y + p[i*MULTI_N + j].y - 0.5);
    }
  val /= 64;
  return RGB(val,val,val);
}

/* Based on code taken from 
 * Copyright (c) Tien-Tsin Wong, 1997.  
 * All Rights Reserved.
 * 
 * 11 Feb 1997
 *
 * ip = 1.0/base;
 */
RGB HammersleySample::render(const int x, const int y, Worker * wrk) {
  float val = 0;

  float a, p;
  int k, kk;
  
  for (k=0; k< iters ; k++)
  {
    float u = 0;
    for (p=ip, kk=k ; kk ; p*=ip, kk/=base)  // kk = (int)(kk/base)
      if ((a = kk % base))
	u += a * p;

    float v = (k + 0.5) / iters;

    val += L(x + u - 0.5, y + v - 0.5);
  }
  
  val /= iters;
  return RGB(val,val,val);
}