www.pudn.com > simpleraytracer_v1_0.zip > world.cpp


/*=================================================================== 
 
   
  digital liberation front 2002 
   
  _______    ______      _______ 
 /______/\  |______|    /\______\   
|       \ \ |      |   / /       |     
|	      \| |      |  |/         |   
|_____    \ |      |_ /    ______|        
 ____|    | |      |_||    |_____           
     |____| |________||____|                 
            
 
 
 
Code by Nicholas Chapman[/ Ono-Sendai] 
nickamy@paradise.net.nz 
 
You may use this code for any non-commercial project, 
as long as you do not remove this description. 
 
You may not use this code for any commercial project. 
====================================================================*/ 
#include "world.h" 
 
 
#include "object.h" 
#include "colour.h" 
#include "ray.h" 
#include "light.h" 
#include "geometry.h" 
#include  
 
 
const Colour background_colour(0.2f, 0.2f, 0.2f); 
const Colour ambient_lighting(0.3f, 0.3f, 0.3f); 
 
World::World() 
{ 
} 
 
World::~World() 
{ 
	for(int i=0; igetGeometry().getDistanceUntilHit(ray); 
	 
		if(distance >= 0 && distance < length) 
		{ 
			//ray hit the object within the specified length! 
			return true; 
		} 
	} 
 
	return false; 
} 
 
 
 
 
 
 
 
//gets the colour of a certain ray. 
void World::getColourForRay(const Ray& ray, Colour& colour_out) 
{ 
 
	Object* closest_object = NULL;//the closest object hit by the ray 
	float smallest_dist = 1e9f; //the smallest dist the trace got. 
 
 
	for(int i=0; igetGeometry().getDistanceUntilHit(ray); 
	 
		//----------------------------------------------------------------- 
		//if this is the closest object hit by ray so far.. 
		//----------------------------------------------------------------- 
		if(tracedist >= 0.0f && tracedist < smallest_dist) 
		{ 
			//----------------------------------------------------------------- 
			//remember it 
			//----------------------------------------------------------------- 
			smallest_dist = tracedist; 
			closest_object = objects[i]; 
		} 
	} 
 
 
	if(closest_object != NULL) 
	{ 
 
		//----------------------------------------------------------------- 
		//calc intersection point. 
		//also nudge of the surface a little 
		//----------------------------------------------------------------- 
		Vec3 intersect_point = ray.startpos; 
		intersect_point.addMult(ray.unitdir, smallest_dist * 0.999f); 
 
		closest_object->getColourForRay(intersect_point, ray.unitdir, *this, colour_out); 
 
		return; 
	} 
 
	//----------------------------------------------------------------- 
	//if the ray didn't hit anything, just return the background colour. 
	//----------------------------------------------------------------- 
	colour_out = background_colour; 
} 
 
 
 
 
 
 
 
void World::calcLightingForSurfPoint(const Object& ob, const Vec3& unit_cam_ray, const Vec3& ob_intersect_pos, 
		const Vec3& ob_normal, Colour& colour_out) 
{ 
	colour_out.set(0.0f,0.0f,0.0f); 
 
	//----------------------------------------------------------------- 
	//calc direction of reflected camera ray. 
	//this is used for the reflected colour and also for specular lighting 
	//----------------------------------------------------------------- 
	const float neg_raydir_on_normal = -dot(unit_cam_ray, ob_normal); 
 
	Vec3 unit_reflectray_dir = unit_cam_ray; 
	unit_reflectray_dir.addMult(ob_normal, neg_raydir_on_normal + neg_raydir_on_normal); 
	 
	//assert(unit_reflectray_dir.dot(ob_normal) >= 0); 
	 
 
 
	//----------------------------------------------------------------- 
	//calc reflected colour 
	//----------------------------------------------------------------- 
	if(ob.getMaterial().reflect_fraction != 0.0f) 
	{ 
		const Ray reflected_ray(ob_intersect_pos, unit_reflectray_dir); 
 
		//----------------------------------------------------------------- 
		//recurse - do the trace and get the colour 
		//----------------------------------------------------------------- 
		Colour reflected_colour; 
		getColourForRay(reflected_ray, reflected_colour); 
 
		colour_out.addMult(reflected_colour, ob.getMaterial().reflect_fraction); 
	} 
 
 
	if(ob.getMaterial().reflect_fraction == 1.0f) 
		return;//don't do diffuse/specular lighting, reflected lighting is all we need 
 
 
	//----------------------------------------------------------------- 
	//do non-reflected lighting 
	//----------------------------------------------------------------- 
	Colour nonreflected_lighting(0.0f,0.0f,0.0f); 
	//this will be scaled by the nonreflect fraction at end of func 
 
	//----------------------------------------------------------------- 
	//add ambient lighting term 
	//----------------------------------------------------------------- 
	nonreflected_lighting += ob.getMaterial().diffuse_colour * ambient_lighting; 
								 
 
 
	for(int i=0; i != lights.size(); ++i) 
	{ 
		//for each light... 
 
		Colour light_illum(0.0f,0.0f,0.0f);//illumination from this light 
 
		//----------------------------------------------------------------- 
		//get a ref to the light 
		//----------------------------------------------------------------- 
		const Light& light = *lights[i]; 
 
		//----------------------------------------------------------------- 
		//get vector from light to point on object surface 
		//----------------------------------------------------------------- 
		Vec3 unit_light_to_intersect = ob_intersect_pos - light.getPos();  
			//this will be normalised later. 
 
 
		//----------------------------------------------------------------- 
		//check to see surface is orientated towards light 
		//----------------------------------------------------------------- 
		float dot = -dotProduct(unit_light_to_intersect, ob_normal); 
		 
		if(dot < 0.0f) 
		{ 
			//surface was facing away from light, so can't be illuminated by it. 
			continue; 
		} 
 
		 
		//----------------------------------------------------------------- 
		//normalise 'unit_light_to_intersect' vector and get dist squared 
		//----------------------------------------------------------------- 
		float inverse_light_dist; 
		const float light_dist = unit_light_to_intersect.normalise_ret_length(inverse_light_dist); 
		 
		const float inverse_light_dist_sqrd = inverse_light_dist * inverse_light_dist; 
 
 
		dot *= inverse_light_dist;//dot is now the *actual* dot. 
		 
 
		//----------------------------------------------------------------- 
		//test to see if this light illuminates this point, or if it is obstructed 
		//----------------------------------------------------------------- 
		if(this->doesFiniteRayHitAnything( 
			Ray(ob_intersect_pos, unit_light_to_intersect * -1.0f), light_dist)) 
		{	 
			continue;//light was blocked, so go on to next light 
		} 
 
		//else this light does illuminate this point. 
 
		//------------------------------------------------------------------------ 
		//add the diffuse lighting contribution 
		//------------------------------------------------------------------------ 
		const float	diffuse_intensity = dot * inverse_light_dist_sqrd; 
 
		light_illum.addMult(ob.getMaterial().diffuse_colour, diffuse_intensity); 
 
 
		//----------------------------------------------------------------- 
		//now do the specular lighting 
		//----------------------------------------------------------------- 
		if(ob.getMaterial().specular_amount != 0.0f) 
		{ 
			float spec_dot = -dotProduct(unit_reflectray_dir, unit_light_to_intersect); 
 
			if(spec_dot > 0.0f) 
			{ 
				const float specular_intensity = spec_dot * inverse_light_dist_sqrd *  
					pow(spec_dot, ob.getMaterial().specular_coeff) *  
					ob.getMaterial().specular_amount; 
			 
				//assume the 'specular colour of the material is white'. 
				//eg assume a red sphere under a white light will specularly reflect  
				//white light. 
 
				light_illum.add(Colour(specular_intensity, specular_intensity, specular_intensity)); 
			} 
 
		}//end 'if use specular lighting' 
 
		//------------------------------------------------------------------------ 
		//add the contribution from this light, modulating the whole thing by the light's colour 
		//------------------------------------------------------------------------ 
		nonreflected_lighting += light_illum * light.getColour(); 
	 
	}//end light loop 
	 
	//----------------------------------------------------------------- 
	//scale by nonreflected fraction, and add to the final returned colour. 
	//----------------------------------------------------------------- 
	colour_out.addMult(nonreflected_lighting, 1.0f - ob.getMaterial().reflect_fraction); 
 
}