CS348B - Image Synthesis Techniques

Final Project

Mark Wang mwang@graphics.stanford.edu Erik Haugen haugen@cs.stanford.edu

Texture mapping (Erik Haugen):


For texture mapping, I implemented mapping over spheres and triangles. The images demosphere.ppm and democube.ppm show texture mapping over spheres and polygon meshes, respectively. I made an inventor file of a square with texture coordinates in the correct place, for use in our final images. I used the resulting two triangles for texture mapping. This can be seen in demo.ppm. (The algorithm for spheres was taken from Eric Haines in Glassner's book.)

I have also implemented bump mapping. Here, the intensity of the image loaded in represents the height of the bump at that point. The slope of the bump is computed from examining the surrounding texels to see how the intensity of the image changes. Bump mapping can be seen in demo.ppm in the purple square. (I used Emmanuel Mogenet's algorithm for calculation of the parametric derivatives of the triangular surface, which I got from comp.graphics.algorithms.)

The wood cube and sphere in demo.ppm were made using a 3d procedurally generated function. The coordinates of the point being rendered were passed in, and then a precomputed grid of noise is accessed by a turbulence function which generates a fractal-like pattern (from Pereira, handout #20). This is used with a sin() to make the wood pattern.



Volume rendering (Mark Wang):
I implemented volume rendering using spheres as bounding volumes. However, the nice thing is that the spatial density functions did not have to be parameterized spherically, or otherwise exhibit spherical characteristics, since in our volume shading routine, we had access to full world-space coordinates.

We used a ray-marching algorithm, stepping 0.01 units for each march from when we entered the sphere to when we exited, and at each point, evaluating the density, and then compositing that using the OVER operator with our previous accumulated intensity value (from Levoy, Handout #34, CS348B, 1997.)

The volume density function f(X) maps each point in R3 -> [0, 1].

alpha = (1 - exp(-f(X)) )*0.06

To convert that into an "alpha" value to feed into the composite OVER operator, we used the above formula (the 0.06 was an empirically determined constant)

The color to use of each volume was specified using the emissive color field of each spherical bounding object.

Upon exit of the sphere from the ray, the final alpha value was used as the ktrans of the sphere for rendering.


Distribution ray tracing (Mark Wang):
I implemented distribution ray tracing using a uniform Monte Carlo technique. For our final renderings, we shot 16 rays to form a distribution. The location of our rays was jittered using the following technique: scale a (unit-length) direction ray by a threshold based on our expected "width" of our BRDF/BTDF graph, and then, add a random vector uniformly selected from the unit sphere to it, thus, jittering the direction of our extended ray by some bounded value determined by the relative distance of the sphere (ie, how much we initally scaled up our ray by). We then renormalized our "jittered" directional vector, to form our new ray.

The values for each ray were then weighted according to our BRDF/BTDF to form a weighted average.

The BRDF and BTDF we used for our sample images was a classical Phong cosine-lobe model (cos(theta)^n) where theta was the angle between the specular direction of reflection and our "jittered" ray, and n was a number (we used 128, with a value of 3.25 for our initial "scale factor". This implies that the jittered ray cannot be jitterd more than an angle of arctan(1/3.25) We find that cos(arctan(1/3.25))128.0 is approximately 0.003, which means any contributions from the distribution outside this range most likely will not contribute enough to make a difference in the final ray color).


Adaptive stochastic supersampling (Mark Wang):
For each rectangular pixel region in the image plane, we divided it into four quadrants, and shot a ray into each quadrant, each jittered to a random location within that quadrant. We then computed the arithmetic mean of the four colors of our rays, and compared that average value with each ray. For our heuristic to decide which quadrant(s) to supersample further, we treated each RGB triplet as a 3-D vector, and used the normal Euclidean distance between them. If that distance was greater than some threshold (0.1 for the images we computed), within that quadrant, we would recursively subdivide that into four areas and repeat.

(One area of improvement would be to re-use the original ray in each quadrant in which we decided to sample more, and only shoot 3 new rays, instead of discarding the fourth ray.)

In a separate canvas, we did a simultaneous visualization of the degree each pixel was being supersampled. We used the following formula

(total number of primary rays shot for this pixel) * 5

for each pixel, clamped to a maximum value of 255. We used this value to plot a grayscale pixel with corresponding intensity.

List of images: (coming soon)
---------------
demo.ppm - demonstrates texture mapping and bump mapping

DEMOdist1.ppm - a demo scene we did, to demonstrate our wood textures, and
  our glossy reflection from our object model, and also spherical texture
  mapping

DEMOtranslucency.ppm - the familiar test2, rendered with distribution ray
  tracing enabled to get glossy reflections and translucency.

DEMOobject.ppm - Our final object model.  demonstrates volume rendering,
  bump mapping, texture mapping, and glossy reflection.

DEMOobjectss.ppm - demonstrates supersampling visualization window, during
  render of DEMOobject.ppm

DEMOvolume.ppm - demostrates volume ray tracing, using Erik's fractal
  turbulence function as a spatial density function