www.pudn.com > Ray_Tracing_Materials.rar > RayTracer.cpp, change:2008-01-03,size:12343b
/*
File Name:
RayTracer.cpp
Created by:
Allen Sherrod (Programming Ace of www.UltimateGameProgramming.com).
Description:
This file has the source to the ray tracing functions used to create the scene.
*/
#include"RayTracer.h"
int g_RecurseLevel = 0;
CVector4* GenerateRayDirections(int width, int height, int depth)
{
// Create variables to hold half the screen width and height.
float halfWidth = (float)(width >> 1);
float halfHeight = (float)(height >> 1);
// Allocate memory for the list of directions.
CVector4 *direction = new CVector4[width * height];
// Loop through every pixel of the screen and create a ray direction.
for(int y = 0; y < height; y++)
{
for(int x = 0; x < width; x++)
{
// Get this current array element.
CVector4 ¤tDir = direction[x + y * width];
// Create the vector from it.
currentDir = CVector4(x - halfWidth, y - halfHeight, (float)depth);
// We must normalize the ray direction.
currentDir.Normal();
}
}
// return our newly created ray directions array.
return direction;
}
void TraceScene(Shape *shapeList, int totalShapes, CLight *lights, int totalLights,
Ray ray, float &r, float &g, float &b)
{
// This will hold the result of the test of each shape against the ray.
TraceResult intersectResult;
// Used to determine which shape is closest and where it is.
int closestIntersectedShape = -1;
float closestIntersectedDist = 1000000;
// Loop through all shapes and determine which hits this ray.
for(int i = 0; i < totalShapes; i++)
{
// Test if this ray hits this shape while going towards this pixel.
if(shapeList[i].type == SPHERE && shapeList[i].sphere != 0)
intersectResult = shapeList[i].sphere->Intersect(ray);
else if(shapeList[i].type == TRIANGLE && shapeList[i].triangle != 0)
intersectResult = shapeList[i].triangle->Intersect(ray);
else if(shapeList[i].type == PLANE && shapeList[i].plane != 0)
intersectResult = shapeList[i].plane->Intersect(ray);
// If the ray hits this object...
if(intersectResult.hit == true)
{
// Only care about the closest object.
if(intersectResult.distance < closestIntersectedDist)
{
closestIntersectedDist = intersectResult.distance;
closestIntersectedShape = i;
}
}
}
// If no shape hits this ray, don't calculate the color.
if(closestIntersectedShape != -1)
{
LightShadeShape(shapeList, closestIntersectedShape, totalShapes,
ray, closestIntersectedDist, lights,
totalLights, r, g, b);
}
}
void LightShadeShape(Shape *shape, int shapeIndex, int totalShapes, Ray &ray,
float dist, CLight *lights, int totalLights,
float &r, float &g, float &b)
{
CVector4 RayShapeIntersect;
// Get the point of interection.
RayShapeIntersect.x = ray.origin.x + dist * ray.direction.x;
RayShapeIntersect.y = ray.origin.y + dist * ray.direction.y;
RayShapeIntersect.z = ray.origin.z + dist * ray.direction.z;
CVector4 normal;
// Grab this object's normal.
if(shape[shapeIndex].type == SPHERE && shape[shapeIndex].sphere != 0)
normal = shape[shapeIndex].sphere->GetNormal(RayShapeIntersect);
else if(shape[shapeIndex].type == TRIANGLE && shape[shapeIndex].triangle != 0)
normal = shape[shapeIndex].triangle->GetNormal();
else if(shape[shapeIndex].type == PLANE && shape[shapeIndex].plane != 0)
normal = shape[shapeIndex].plane->GetNormal();
// Get Light data.
CVector4 lightDir;
float lightDist = 0;
// Shape color, shape's reflective amount, and the current light color.
float sr = 0, sg = 0, sb = 0;
float diffuseMatVal = 0;
float reflectVal = 0;
float refractVal = 0;
float lightColR = 0, lightColG = 0, lightColB = 0;
// Grab the shape color based on which object type we have.
if(shape[shapeIndex].type == SPHERE && shape[shapeIndex].sphere != 0)
{
sr = shape[shapeIndex].sphere->r;
sg = shape[shapeIndex].sphere->g;
sb = shape[shapeIndex].sphere->b;
reflectVal = shape[shapeIndex].sphere->reflect;
refractVal = shape[shapeIndex].sphere->refract;
diffuseMatVal = shape[shapeIndex].sphere->diffuseMat;
}
else if(shape[shapeIndex].type == TRIANGLE && shape[shapeIndex].triangle != 0)
{
sr = shape[shapeIndex].triangle->r;
sg = shape[shapeIndex].triangle->g;
sb = shape[shapeIndex].triangle->b;
reflectVal = shape[shapeIndex].triangle->reflect;
refractVal = shape[shapeIndex].triangle->refract;
diffuseMatVal = shape[shapeIndex].triangle->diffuseMat;
}
else if(shape[shapeIndex].type == PLANE && shape[shapeIndex].plane != 0)
{
sr = shape[shapeIndex].plane->r;
sg = shape[shapeIndex].plane->g;
sb = shape[shapeIndex].plane->b;
reflectVal = shape[shapeIndex].plane->reflect;
refractVal = shape[shapeIndex].plane->refract;
diffuseMatVal = shape[shapeIndex].plane->diffuseMat;
}
// Loop through all the lights adding up the contribution of each.
for(int l = 0; l < totalLights; l++)
{
// Get the light direction from the light pos and the point of intersection.
lightDir = lights[l].position - RayShapeIntersect;
// Normalize the light direction.
lightDist = lightDir.GetLength();
lightDir.Normal();
// Set the light color to the color of the current light.
lightColR = lights[l].r; lightColG = lights[l].g; lightColB = lights[l].b;
// Determine if we are in shadow. If so lightCol becomes black.
Ray shadowRay;
TraceResult shadowIntersect;
shadowRay.origin = RayShapeIntersect;
shadowRay.direction = lightDir;
// Test to see if the point is in a shadow.
for(int i = 0; i < totalShapes; i++)
{
if(shape[i].type == SPHERE && shape[i].sphere != 0)
shadowIntersect = shape[i].sphere->Intersect(shadowRay);
else if(shape[i].type == TRIANGLE && shape[i].triangle != 0)
shadowIntersect = shape[i].triangle->Intersect(shadowRay);
else if(shape[i].type == PLANE && shape[i].plane != 0)
shadowIntersect = shape[i].plane->Intersect(shadowRay);
// If the ray hits this object...
if(shadowIntersect.hit == true)
{
// ...and the object is in front of the light.
if(shadowIntersect.distance < lightDist)
{
// Then we are in shadow. No need to continue
// checking if we are already in a shadow so break.
lightColR = lightColG = lightColB = 0;
break;
}
}
}
// Calculate the light coefficient (diffuse light) and attenuation (for
// point lights) of this current light source.
float lightCoef;
float attenuation = 1;
float d = 0;
lightCoef = lightDir.DotProduct3(normal);
// If the light coefficient is less than 0 make it = to 0.
if(lightCoef < 0) lightCoef = 0;
// For attenuation I normally use one of the following in
// OpenGL/DirectX:
// attenuation = 1 / (lights[l].radius + lights[l].radius * d +
// lights[l].radius * (d * d));
// attenuation = 1 - ((d * d) / (r * r));
// For some reason I have to change that 1 - to 1 / with the
// second one to get the correct results. I must look into this.
if(lights[l].type == POINT_LIGHT)
{
d = (RayShapeIntersect - lights[l].position).GetLength();
attenuation = 1 / ((d * d) / (lights[l].radius * lights[l].radius));
}
// Add the diffuse contribution with the current final color.
r += attenuation * lightColR * lightCoef * diffuseMatVal * sr;
g += attenuation * lightColG * lightCoef * diffuseMatVal * sg;
b += attenuation * lightColB * lightCoef * diffuseMatVal * sb;
}
// Next up is to add the reflective color to the object.
float reflectR = 0, reflectG = 0, reflectB = 0;
float k = 0;
Ray reflectRay;
reflectRay.origin = RayShapeIntersect;
k = ray.direction.DotProduct3(normal);
k *= -2.0f;
// Reflect this vector in the opposite direction.
reflectRay. direction.x = k * normal.x + ray.direction.x;
reflectRay. direction.y = k * normal.y + ray.direction.y;
reflectRay. direction.z = k * normal.z + ray.direction.z;
// Only do reflection if this object is reflective.
if(reflectVal > 0)
{
// Don't want infinite loops when dealing with more than 1 reflective
// surface so we need some kind of check.
if(g_RecurseLevel <= MAX_RECURSION)
{
// Trace the scene using the reflected vector.
g_RecurseLevel++;
TraceScene(shape, totalShapes, lights, totalLights, reflectRay,
reflectR, reflectG, reflectB);
g_RecurseLevel--;
// Calculate final reflective contribution.
r += reflectR * reflectVal;
g += reflectG * reflectVal;
b += reflectB * reflectVal;
}
}
// Next up is to add the refractive color to the object.
float refractR = 0, refractG = 0, refractB = 0;
Ray refractRay;
CVector4 I, negI, newNormal;
float cosI = 0;
float cosT = 0;
// In order to get the correct results I had to invert the normal.
// Normally when I do refraction in OpenGL or DirectX I don't have to do
// this. Other than that the refraction equation is the same.
newNormal = normal;
newNormal.x *= -1; newNormal.y *= -1; newNormal.z *= -1;
I = RayShapeIntersect - ray.origin;
negI = CVector4(-I.x, -I.y, -I.z);
I.Normal();
negI.Normal();
refractRay.origin = ray.origin;
cosI = negI.DotProduct3(newNormal);
cosT = 1.0f - refractVal * refractVal * (1.0f - cosI * cosI);
refractRay.direction.x = refractVal * I.x +
((refractVal * cosI - sqrt(fabs(cosT))) * newNormal.x);
refractRay.direction.y = refractVal * I.y +
((refractVal * cosI - sqrt(fabs(cosT))) * newNormal.y);
refractRay.direction.z = refractVal * I.z +
((refractVal * cosI - sqrt(fabs(cosT))) * newNormal.z);
refractRay.direction.Normal();
// Only do refraction if this object is refractive.
if(refractVal > 0)
{
// Don't want infinite loops when dealing with more than 1 refractive
// surface so we need some kind of check.
if(g_RecurseLevel <= MAX_RECURSION)
{
// Trace the scene using the refracted vector.
g_RecurseLevel++;
TraceScene(shape, totalShapes, lights, totalLights, refractRay,
refractR, refractG, refractB);
g_RecurseLevel--;
// Calculate final refractive contribution.
r += refractR * refractVal;
g += refractG * refractVal;
b += refractB * refractVal;
}
}
// Multiplying by 0.7 is like saying the object's diffuse material is 0.7.
/*r *= 0.7f;
g *= 0.7f;
b *= 0.7f;*/
}
// Copyright February 2005
// All Rights Reserved!
// Allen Sherrod
// ProgrammingAce@UltimateGameProgramming.com
// www.UltimateGameProgramming.com