A simple pathtracer program, capable of producing renderings of simple bodies. The program computes global illumination, by intersecting scene objects, such as luminaries and solid bodies, with idealised surface properies.
#include <stdlib.h>
#include <math.h>
#include <vector>
#include "./defines.hpp"
#include "./vec3.hpp"
#include "./sphere.hpp"
#include "./sphereflake.hpp"
namespace raytracer
{
sphereflake::sphereflake(const vec3& pos, const vec3& firstUp, const double uncertLength, const double firstRadius, const material& mat): object(mat)
{
this->pos_= pos;
this->first_up_= firstUp;
this->uncert_length_= uncertLength;
this->first_radius_= firstRadius;
//generate basic pattern
float t = 1.0f;
float a = t*12.0f*PI, daL = 2.0f*PI/6.0f, daU = 2.0f*PI/3.0f;
// lower ring
for (int i= 0; i< 6; i++)
{
pattern_[i]= vec3(0, sin(a), cos(a));
pattern_[i]= pattern_[i].normalize();
a += daL;
}
// upper ring
a -= daL / 2.0f;//shift by 30 degrees
for (int i= 6; i< 9; i++)
{
pattern_[i]= vec3(sin(daL), sin(a), cos(a));
pattern_[i]= pattern_[i].normalize();
a += daU;
}
};
bool sphereflake::intersects(const ray& ray, double& length, vec3& normal, material& material) const
{
vec3 norm;
double len= find_intersection_length(ray, this->pos_, this->first_radius_, this->first_up_, norm);
if(len< ACCURACY) return false;
length= len;
material= *this->material_;
normal= norm;
return true;
};
double sphereflake::find_intersection_length(const ray& ray, const vec3& pPos, const double pRadius, const vec3& sDir, vec3& intersNormal) const
{
double distOfCamera= ray.origin.add(pPos.negative()).length();
double seenPRadius= pRadius/distOfCamera;
//if the radius, seen from the camera is smaller than a pixel - do not check (bottom condition)
if(seenPRadius< (uncert_length_*0.5f)) { return -1; }
//construct children coordinates
double oldRadius= pRadius, boundRadius= pRadius, radius= pRadius/3.0f;
//generate coordinates of new spheres
double dx = sDir.x, dy = sDir.y, dz = sDir.z;
//find a vector int the plane perpendicular to s_dir
vec3 b1;
if(dx*dx < dy*dy && dx*dx < dz*dz)//dx is smallest
b1= vec3(0, dz, -dy);
else if(dy*dy < dz*dz)//dy is smallest
b1 = vec3(dz, 0, -dx);
else//dz is smallest
b1 = vec3(dy, -dx, 0);
//normalize the new vector
b1= b1.normalize();
//find another perpendicular vector by doing the cross product
vec3 b2= sDir.cross(b1);
//recalculate b1 using b2, and we have a new set of basis vectors
b1= sDir.cross(b2);
//first find the intersection length with the parent sphere
double intrlen= sphere::find_sphere_intersection_length(ray, pPos, pRadius);
bool initMinInters= true;
if(intrlen== -1) initMinInters= false;
else intersNormal= sphere::get_sphere_normal_at(ray.origin.add(ray.direction.scale(intrlen)), pPos);//compute normal at contact - for lighting
vec3 lintersNormal;//normal at intersection, for children spheres
for(int i= 0; i< 9; i++)
{
//get coordinate scalars from pattern
double r1 = pattern_[i].x,
r2 = pattern_[i].y,
r3 = pattern_[i].z;
//construct new child sphere dir for new coordinates
double nx = r1*sDir.x + r2*b1.x + r3*b2.x,
ny = r1*sDir.y + r2*b1.y + r3*b2.y,
nz = r1*sDir.z + r2*b1.z + r3*b2.z;
vec3 spdir(nx, ny, nz);//sphere dir vector
double sprad= oldRadius+boundRadius;//calc bounding sphere offset
vec3 boundcoord= pPos.add(spdir.scale(sprad));//coordinates for bounding sphere
vec3 spcoord= pPos.add(spdir.scale(oldRadius+radius));//real sphere coordinates
//find if there is an intersection with the bounding sphere, enveloping this child
double lintrLen= sphere::find_sphere_intersection_length(ray, boundcoord, boundRadius);
if(lintrLen>= 0)//if there is bounding inters, then recursively find the length of true intersect (this is performed on the child spheres of this child sphere)
{
//also calculate normal of intersection with true child sphere - this is usefull, if there are no other low level intersections
lintersNormal = sphere::get_sphere_normal_at(ray.origin.add(ray.direction.scale(intrlen)), spcoord);
lintrLen= this->find_intersection_length(ray, spcoord, radius, spdir, lintersNormal);
}
//find min intersection length, among all child spheres
if(((initMinInters== false) || (intrlen> lintrLen)) && (lintrLen>= 0))
{
intrlen= lintrLen;
intersNormal= lintersNormal;
initMinInters= true;
}
}
return intrlen;//finally return min intersection length
};
}