ACADEMIC RESEARCH AND PUBLICATIONS
during Graduate and Undergraduate Studies
Doctoral Candidate in Computer Science
Visual Computing Division, Digital Production Arts
Clemson University | School of Computing
INSTANT RADIOSITY:
A COMPUTER GRAPHICS PROJECT
//Returns reflected ray when intersecting with a triangle in the scene. Otherwise, returns same ray.
struct ray castRay(struct ray r)
{
int i, t, f, useTri0 = 0, useTri1 = 0;
struct point tuv0, tuv1, V, endPt;
struct mat3 M;
struct ray newRay;
struct triangle tri0, tri1;
struct vec4 newPt;
struct mat4 transf = //Transformation matrix for the teapot. Since teapot geometry is transformed when rendering, check with those same transformations for intersections.
{
{teaScale, 0.0, 0.0, 0.0},
{0.0, teaScale, 0.0, 0.0},
{0.0, 0.0, teaScale, 0.0},
{0.0, 0.0, 0.0, 1.0}
};
transf = multMat4ByMat4( getTranslMat(teaTrans), transf);
transf = multMat4ByMat4( getRotateZMat(teaRotateZ), transf );
transf = multMat4ByMat4( getRotateXMat(teaRotateX), transf );
transf = multMat4ByMat4( getRotateYMat(teaRotateY), transf );
//ray: startPt*(1-t) + dir*t | t>=0 (if t=1 then at dir)
//triangle: (1-u-v)*v0 + u*v1 + v*v2 | u,v >= 0 && u,v <= 1
for (i=0; i < (numFaces*2 +12); i+=2)
{
//Check teapot intersection first before walls.
if (i >= (numFaces*2))
{
t = i - (numFaces*2);
tri0 = boxTris[t];
tri0 = boxTris[t];
tri1 = boxTris[t+1];
}
//Split each teapot quad {v0,v1,v2,v3} into two triangles: {v0,v1,v2,n}, {v0,v2,v3,n}
else
{
f = i/2;
// First tri from quad: {v0,v1,v2,n}
newPt.x = vertices[faceVerts[f*4]];
newPt.y = vertices[faceVerts[f*4] +1];
newPt.z = vertices[faceVerts[f*4] +2];
newPt.w = 1.0;
newPt = multMat4ByVec4(transf, newPt); //Need 4D vector (with homogeneous coord) to transform.
tri0.v0.x = newPt.x; //Keep the 3D info to use in the triangle.
tri0.v0.y = newPt.y;
tri0.v0.z = newPt.z;
newPt.x = vertices[faceVerts[f*4 +1]];
newPt.y = vertices[faceVerts[f*4 +1] +1];
newPt.z = vertices[faceVerts[f*4 +1] +2];
newPt.w = 1.0;
newPt = multMat4ByVec4(transf, newPt);
tri0.v1.x = newPt.x;
tri0.v1.y = newPt.y;
tri0.v1.z = newPt.z;
newPt.x = vertices[faceVerts[f*4 +2]];
newPt.y = vertices[faceVerts[f*4 +2] +1];
newPt.z = vertices[faceVerts[f*4 +2] +2];
newPt.w = 1.0;
newPt = multMat4ByVec4(transf, newPt);
tri0.v2.x = newPt.x;
tri0.v2.y = newPt.y;
tri0.v2.z = newPt.z;
//Calculate normal coming out of v1, orthogonal to v0-v1 and v2-v1.
tri0.n = normalize(crossProduct( addVecs(tri0.v0, scaleVec(tri0.v1, -1)), addVecs(tri0.v2, scaleVec(tri0.v1, -1)) ));
// 2nd tri from quad: {v0,v2,v3,n}
tri1.v0 = tri0.v0; //Two vertices are shared with the first triangle.
tri1.v1 = tri0.v2;
newPt.x = vertices[faceVerts[f*4 +3]];
newPt.y = vertices[faceVerts[f*4 +3] +1];
newPt.z = vertices[faceVerts[f*4 +3] +2];
newPt.w = 1.0;
newPt = multMat4ByVec4(transf, newPt);
tri1.v2.x = newPt.x;
tri1.v2.y = newPt.y;
tri1.v2.z = newPt.z;
tri1.n = tri0.n;
}
endPt = addVecs(r.startPt, r.dir); //Actual end point (dp) is along start (startPt) in direction (dir) (start + dir).
//Calculate t,u,v for intersection with tri0.
M.vec0 = addVecs(r.startPt, scaleVec(endPt, -1)); //sp - dp
M.vec1 = addVecs(tri0.v1, scaleVec(tri0.v0, -1)); //tri vert1 - tri vert0
M.vec2 = addVecs(tri0.v2, scaleVec(tri0.v0, -1)); //tri vert2 - tri vert0
M = inverseTranspose(M);
tuv0 = multMat3ByVec( M, addVecs(r.startPt, scaleVec(tri0.v0, -1)) ); //M * (startPt - tri vert0)
//Calculate t,u,v for intersection with tri1.
M.vec0 = addVecs(r.startPt, scaleVec(endPt, -1)); //sp - dp
M.vec1 = addVecs(tri1.v1, scaleVec(tri1.v0, -1)); //tri vert1 - tri vert0
M.vec2 = addVecs(tri1.v2, scaleVec(tri1.v0, -1)); //tri vert2 - tri vert0
M = inverseTranspose(M);
tuv1 = multMat3ByVec( M, addVecs(r.startPt, scaleVec(tri1.v0, -1)) ); //M * (startPt - tri vert0)
//t should be 0 or positive. u,v >= 0 and u+v <= 1.
if ( (tuv0.x >= 0) && (tuv0.y >= 0) && (tuv0.z >= 0) && (tuv0.y + tuv0.z <= 1))
{ useTri0 = 1; }
else if ( (tuv1.x >= 0) && (tuv1.y >= 0) && (tuv1.z >= 0) && (tuv1.y + tuv1.z <= 1))
{ useTri1 = 1; }
if ((useTri0) || (useTri1))
{
V = scaleVec(r.dir, -1); //When calculating reflection, starts at same point as normal.
if (i<(numFaces*2))
{ printf("teapot tri\n"); }
else
{ printf("box tri\n"); }
if (useTri0)
{
newRay.startPt = addVecs( scaleVec(r.startPt, 1-tuv0.x), scaleVec(endPt, tuv0.x) ); //sp*(1-t) + dir*t (t defines point of intersection from start along direction)
//Has same result as (1-u-v)*v0 + u*v1 + v*v2.
newRay.dir = scaleVec(tri0.n, 2*dotProduct(V, tri0.n)); //2(V dot N)N
}
else
{
newRay.startPt = addVecs( scaleVec(r.startPt, 1-tuv1.x), scaleVec(endPt, tuv1.x) );
newRay.dir = scaleVec(tri1.n, 2*dotProduct(V, tri1.n));
}
newRay.dir = normalize(addVecs( scaleVec(V, -1.0), newRay.dir ));
if (i > numFaces*2) //If box face, get color of wall to set as light diffuse and specular colors.
{
lightDiffuse = normalize(componentMult(lightDiffuse, boxColors[(i-numFaces*2)/2])); //i-numFaces*2 = tri index. i/2 gives face index.
lightSpecular = lightDiffuse;
}
else //Else, get color from teapot.
{
lightDiffuse = normalize(componentMult(lightDiffuse, teapotMaterials[1]));
lightSpecular = lightDiffuse;
}
return newRay;
}
} //end triangle loop
return r;
}
PROJECT PAGE
Language: C (using OpenGL)
Date: Spring 2017
Class: Computer Graphics (Graduate)
This project is a real-time implementation of Alexander Keller’s 1999 SIGGRAPH paper as a means to achieve global illumination, mimicking light bouncing off of surfaces (for example, capturing the color-bleeding effect of the purple and teal from the left and right walls reflecting onto the other off-white walls). The project is written in C using OpenGL and GLSL. I implemented sampling using the Halton-sequence, ray-casting, and collision detection, along with OBJ and MTL file parsing. I also created a normal map using Substance Painter and read and use it in my lighting calculations.