|
Notes for February 25 class -- Even More Ray Tracing
Refraction
In the real world many materials, such as oil, water, plastic, glass and diamond, are transparent.
A transparent material has an index of refraction which measures how much light
slows down as it enters that medium.
For example, the index of refraction of water is about 1.333,
of glass is about 1.5. The index of refraction of diamond, the substance with
the highest known index of refraction, is 2.42.
As in the diagram to the right,
you can add refraction to your ray tracing by following Snell's law:
n1 / n2 = sin(θ2) / sin(θ1)
to determine how much the ray should bend as it enters or exits a transparent object.
Note that you will need to change your ray tracing model to incorporate refraction.
In addition to your initial incoming ray, and any shadow or reflection rays, you also
need to add a refraction ray, which starts just inside the surface, and continues
inward.
Note that if you have ray traced to a sphere,
and are now computing where the refracting ray will exit that sphere,
you will want to compute the second root of the quadratic equation.
Then, after this refracting ray has exited out the back of the transparent sphere,
you will want to compute how much it refracts on its way out, and then
shoot a ray from there into the scene behind the sphere.
In general, you use the results of refraction by mixing the color it returns
together with whatever surface color you have computed due to pure reflection or
blinn/phong reflectance.
|
|
|
Ray / plane intersection
Plane P = (a,b,c,d) describes all points in 3D such that ax + by + cz + d = 0
Half-space (a,b,c,d) with the above bounding plane, describes all points in 3D such that ax + by + cz + d ≤ 0
We can shoot ray (V + tW) to this half space, by solving for t:
a (vx + t wx) + b (vy + t wy) + c (vz + t wz) + d = 0
t (a wx + b wy + c wz) = - (a vx + b vy + c vz + d)
t = - (a vx + b vy + c vz + d) / (a wx + b wy + c wz)
The unit length vector in the direction of (a,b,c) is the surface normal of the half plane.
Knowing this, we can figure out whether the ray is going into the half space or out of it.
There are three cases:
Ray is going into half space:
|
|
(a wx + b wy + c wz) < 0
|
Ray is going out of half space:
|
|
(a wx + b wy + c wz) > 0
|
Ray is parallel to half space:
|
|
(a wx + b wy + c wz) = 0
|
The last case has two sub-cases:
Ray is completely inside half space:
|
|
(a vx + b vy + c vz + d) ≤ 0
|
Ray is completely outside half space:
|
|
(a vx + b vy + c vz + d) > 0
|
|
|
|
Ray tracing to a unit cube
Once you can ray trace to a half space, you can also ray trace to an
intersection of half spaces.
One such intersection is a unit cube, which is defined by the
intersections of the six planes:
( 1, 0, 0,-1) x ≥ -1
(-1, 0, 0,-1) x ≤ 1
( 0, 1, 0,-1) y ≥ -1
( 0,-1, 0,-1) y ≤ 1
( 0, 0, 1,-1) z ≥ -1
( 0, 0,-1,-1) z ≤ 1
The figure to the right shows the two dimensional analogous shape -- a square formed
by the intersection of four half planes.
As always, you find the intersection by taking the maximum of all of the entering
values of t and the minimum of all the exiting values of t.
To compute how each half space contributes to thie intersection, consider each of the four possible cases:
If the ray enters a half space: the exiting value of t is positive infinity.
If the ray exits a half space: the entering value of t is 0.
If the ray is parallel to and entirely inside a half space: we can just ignore that half space.
If the ray is parallel to and entirely outside a half space: the ray misses the entire cube.
|
|
|
Ray tracing to an axis-aligned cylinder
The equation of an infinitely high vertical
cylindrical volume
of radius r, centered at (cx,cz) is:
(x - cx)2 +
(z - cz)2 - r2 ≤ 0
So to ray trace to a vertical cylinder, we just need to ray trace to this shape
intersected with bottom and top
end caps, which can be
described by the two respective equations:
y ≥ ymin
y ≤ ymax
These two equations can be rewritten as:
-y + ymin ≤ 0
y - ymax ≤ 0
Therefore they can be
described by the respective sets of coefficients:
(0, -1, 0, ymin)
(0, 1, 0, -ymax)
You can use a similar approach to form the equations of the three shapes to be intersected
(an infinite tube and two end caps) for
an x-axis or z-axis aligned cylinder.
|
|
History and algorithm for the noise function
In order to do interesting solid procedural textures,
we are going to use the noise function.
We went through a brief history of
and algorithmic overview of the
noise function, by stepping
through the on-line lecture notes at:
http://www.noisemachine.com/talk1/.
Noise is a coherent, space filling pseudo-random
function defined over all values of (x,y,z),
which returns values betweeo -1 and +1.
When used in shaders,
it is very useful for making more natural using textures.
The on-line lecture notes cover how it was first used in movies,
gives a brief overview of how it is constructed,
and gives some examples of how to make
interesting textures using expressions containing noise.
|
|
|
Fractals, turbulence, marble
We then showed that there are a number of
ways of using the noise function to make
visually useful textual "idioms".
One of these is a fractal sum of noise functions:
fractal(p) = ∑i ( noise(2i p) / 2i )
where p is a point in space (px, py, pz )
Another useful function, which simulates the visual appearance
of the onset of turbulence by creating discontinuities in
gradient at all scales, is:
turbulence(p) = ∑i (
abs(noise(2i p)) / 2i )
Note that the only difference between fractal(p) and turbulence(p)
is that turbulence uses the absolute value of noise,
thereby creating discontinuities in gradient where
the value of the noise function crosses zero.
Turbulence can be used in all sorts of textures.
For example, to make the marble vase on the right,
I used the turbulence function to create
a phase shift in a stripe pattern
made using a cosine function.
Described as a GLSL this would be:
float t = 0.4 * (turbulence(vec3(x * 1.5, y * 1.5, 10.)) + 1.8);
float s = pow(0.5 + 0.5 * cos(7. * x + 6. * t), 0.1);
gl_FragColor = vec4(s, s*s, s*s*s, 1.);
|
|
|
Fragment shader for the noise function
If you want to try out noise in your own textures,
like we did in class, you can include this code in
your fragment shader:
vec3 mod289(vec3 x) { return x - floor(x * (1.0 / 289.0)) * 289.0; }
vec4 mod289(vec4 x) { return x - floor(x * (1.0 / 289.0)) * 289.0; }
vec4 permute(vec4 x) { return mod289(((x*34.0)+1.0)*x); }
vec4 taylorInvSqrt(vec4 r) { return 1.79284291400159 - 0.85373472095314 * r; }
vec3 fade(vec3 t) { return t*t*t*(t*(t*6.0-15.0)+10.0); }
float noise(vec3 P) {
vec3 i0 = mod289(floor(P)), i1 = mod289(i0 + vec3(1.0));
vec3 f0 = fract(P), f1 = f0 - vec3(1.0), f = fade(f0);
vec4 ix = vec4(i0.x, i1.x, i0.x, i1.x), iy = vec4(i0.yy, i1.yy);
vec4 iz0 = i0.zzzz, iz1 = i1.zzzz;
vec4 ixy = permute(permute(ix) + iy), ixy0 = permute(ixy + iz0), ixy1 = permute(ixy + iz1);
vec4 gx0 = ixy0 * (1.0 / 7.0), gy0 = fract(floor(gx0) * (1.0 / 7.0)) - 0.5;
vec4 gx1 = ixy1 * (1.0 / 7.0), gy1 = fract(floor(gx1) * (1.0 / 7.0)) - 0.5;
gx0 = fract(gx0); gx1 = fract(gx1);
vec4 gz0 = vec4(0.5) - abs(gx0) - abs(gy0), sz0 = step(gz0, vec4(0.0));
vec4 gz1 = vec4(0.5) - abs(gx1) - abs(gy1), sz1 = step(gz1, vec4(0.0));
gx0 -= sz0 * (step(0.0, gx0) - 0.5); gy0 -= sz0 * (step(0.0, gy0) - 0.5);
gx1 -= sz1 * (step(0.0, gx1) - 0.5); gy1 -= sz1 * (step(0.0, gy1) - 0.5);
vec3 g0 = vec3(gx0.x,gy0.x,gz0.x), g1 = vec3(gx0.y,gy0.y,gz0.y),
g2 = vec3(gx0.z,gy0.z,gz0.z), g3 = vec3(gx0.w,gy0.w,gz0.w),
g4 = vec3(gx1.x,gy1.x,gz1.x), g5 = vec3(gx1.y,gy1.y,gz1.y),
g6 = vec3(gx1.z,gy1.z,gz1.z), g7 = vec3(gx1.w,gy1.w,gz1.w);
vec4 norm0 = taylorInvSqrt(vec4(dot(g0,g0), dot(g2,g2), dot(g1,g1), dot(g3,g3)));
vec4 norm1 = taylorInvSqrt(vec4(dot(g4,g4), dot(g6,g6), dot(g5,g5), dot(g7,g7)));
g0 *= norm0.x; g2 *= norm0.y; g1 *= norm0.z; g3 *= norm0.w;
g4 *= norm1.x; g6 *= norm1.y; g5 *= norm1.z; g7 *= norm1.w;
vec4 nz = mix(vec4(dot(g0, vec3(f0.x, f0.y, f0.z)), dot(g1, vec3(f1.x, f0.y, f0.z)),
dot(g2, vec3(f0.x, f1.y, f0.z)), dot(g3, vec3(f1.x, f1.y, f0.z))),
vec4(dot(g4, vec3(f0.x, f0.y, f1.z)), dot(g5, vec3(f1.x, f0.y, f1.z)),
dot(g6, vec3(f0.x, f1.y, f1.z)), dot(g7, vec3(f1.x, f1.y, f1.z))), f.z);
return 2.2 * mix(mix(nz.x,nz.z,f.y), mix(nz.y,nz.w,f.y), f.x);
}
Homework (due before class on Wednesday Mar 4)
This week I'm just going to give you an array of possible things
to work on. If you are feeling very ambitious you can attempt
all of them, but it's also just fine to focus on just two or three of them,
diving deep to produce something wonderful:
- Implement refraction.
- Generalize cube to boxes of arbitrary center and extent.
- Figure out equations for other polyhedra such as
prism, pyramid, tetrahedron, octahedron, dodecahedron and icosahedron.
- Generalize to non-axis aligned cylinders.
- Make solid texture using noise. Use it to vary different aspects of surface reflectance.
As always, make things that are cool and fun,
and try to create something interactive (using uCursor) and/or animated (using uTime).
|
| |