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