View raw
#include <stdlib.h>
#include <stdio.h>
#include <time.h>
//defines
#include "./src/defines.hpp"
#include "./src/bmp.hpp"
//raytracer engine
#include "./src/vec3.hpp"
#include "./src/ray.hpp"
#include "./src/camera.hpp"
#include "./src/color.hpp"
//geom primitives
#include "./src/object.hpp"
#include "./src/plane.hpp"
#include "./src/sphere.hpp"
#include "./src/box.hpp"
#include "./src/sphereflake.hpp"
#include "./src/material.hpp"
using namespace raytracer;
//const
static const int max_scene_obj= 10;
static const vec3 I= vec3(1.0f, 1.0f, 1.0f);
static const double pi= 3.14;
double rand_01(){ return ((double)rand()/(RAND_MAX)); };
object* intersect(const ray& r, double* wt, vec3* wn, material* wmat);
color radiance(const ray &r, int depth);
//SCENE
camera* sceneCamera;
plane* floorPlane;
sphereflake* sphereFlake;
sphere** spheres;
box* box;
object** sceneObjects;
color* pixelColors;
//ESSENTIALS
int imWidth= 800, imHeight= 600;
double aspectRatio= imWidth/(double)imHeight;
static const double ambient_add= 0.2;
//vectors
vec3 origin= vec3(0.0f, 0.0f, 0.0f)
, X= vec3(1.0f, 0.0f, 0.0f)
, Y= vec3(0.0f, 1.0f, 0.0f)
, Z= vec3(0.0f, 0.0f, 1.0f)
//light
, lPos= vec3(1.0, 2.0, 4.0)
//cameras
, cPos= vec3(2.9f, 0.5f, 1.2f)
, cLookAt= origin;
//colors
color background(135/255.0f, 206/255.0f, 250/255.0f, 0.0f);//135-206-250
color white(1.0f, 1.0f, 1.0f, 0.0f);
color wheatMilk(242/255.0f, 218/255.0f, 145/255.0f, 0.0f);
color maroon(0.5f, 0.25f, 0.25f, 0.0f);
color orang(1.0f, 0.5f, 0.0f, 0.0f);
color blue(0.0f, 0.0f, 0.7f, 0.0f);
color green(0.9f, 0.9f, 0.9f, 0.0f);
color darkGreen(0.0f, 0.80f, 0.0f, 0.0f);
material floorMat(maroon, material::diffuse);
material whiteMat(white, material::diffuse);
material milkyMat(wheatMilk, material::diffuse);
material whiteLight(5.0f, white);//emissive
material reflOrang(orang, material::specular);
material reflBlue(blue, material::specular);
material refrGreen(darkGreen, material::refractive, 1.52);
int main()
{
srand(time(0));
pixelColors= new color[imWidth*imHeight];
//lights and camera
sceneCamera= new camera(cPos, Y, cLookAt);
double uncertLen= 1/((double)((imWidth> imHeight)? imWidth: imHeight));
//scene objects
floorPlane= new plane(Y, -1, floorMat);
spheres= new sphere*[4];
spheres[0]= new sphere(vec3(-2.0f, 0.0f, 0.75f), 1.0f, reflOrang);
spheres[1]= new sphere(vec3(0.0f, -0.65f, 0.25f), 0.35f, refrGreen);
spheres[2]= new sphere(vec3(-1.0f, 1.2f, -0.65f), 0.2f, whiteLight);
//light source
//spheres[3]= new sphere(vec3(0.0f,0.5f,0.0f), 0.05f, whiteLight);
spheres[4]= new sphere(vec3(-2.0f, 0.0f, -2.75f), 1, whiteMat);
spheres[3]= new sphere(vec3(-1.0f, -0.6f, -0.65f), 0.4, reflBlue);
//box= new Box(vec3(-4.0f, 0.5f, -0.25f), 3.0f, reflorang);
sphereFlake= new sphereflake(vec3(-2.0f, 0.0f, -2.75f), Y, uncertLen, 1, whiteMat);
sceneObjects= new object*[::max_scene_obj];
//sceneObjects[1]= //spheres[4];//sphereFlake;
sceneObjects[0]= floorPlane;
sceneObjects[2]= spheres[0];
sceneObjects[3]= spheres[1];
sceneObjects[4]= spheres[2];
sceneObjects[1]= sphereFlake;//spheres[4];
sceneObjects[5]= spheres[3];
//sceneObjects[5]= box;
float jitter[4 * 2] = {
-1.0/4.0, -1.0/4.0,
3.0/4.0, -1.0/4.0,
-1.0/4.0, 3.0/4.0,
3.0/4.0, 3.0/4.0,
};
int numOfSamples= 3;
vec3 camRayOrigin= sceneCamera->pos();
vec3 camDir= sceneCamera->dir();
vec3 camRight= sceneCamera->right();
vec3 camDown= sceneCamera->down();
double bigDim= (imWidth> imHeight)? imWidth: imHeight;
for (int j= 0; j < imHeight; j++)
{
for (int i= 0; i < imWidth; i++)
{
for (int k = 0; k < 4; k++)
{
color sampledCol;
for (int s= 0; s < numOfSamples; s++) {
double l1= rand_01()*0.5;
double l2= rand_01()*0.5;
vec3 offsetVec= vec3(
2.0 * (i + jitter[2*k]+l1)/bigDim - 1.0,
2.0 * (imHeight-j + jitter[2*k+1]+l2)/bigDim - 1.0,
0);
//construct camera ray
vec3 camRayDirection= camDir.add(camRight.scale(offsetVec.x).add(camDown.scale(offsetVec.y)));
ray camRay(camRayOrigin, camRayDirection.normalize());
sampledCol= sampledCol.add(radiance(camRay, 0).scale(1/(double)numOfSamples));
}
pixelColors[imWidth*j+i]= pixelColors[imWidth*j+i].add(sampledCol.clamp().scale(1/(4.0f)));
}
//.scale(255);
}
system("clear");
printf("P: %d \% \n", 100*(j*imWidth)/(imWidth*imHeight));
}
outputbmp("./test.bmp", imWidth, imHeight, 72, pixelColors);
delete[] pixelColors, sceneObjects, spheres;
delete sceneCamera, floorPlane, sphereFlake;
return 0;
};
object* intersect(const ray& r,double* wt= NULL, vec3* wn= NULL, material* wmat= NULL)
{
double t= -1;
double tMin= -1;
object* winObject= NULL;
vec3 n;
material mat;
bool first= true;
int oi= 0;
while(sceneObjects[oi]!= NULL){
bool inters= sceneObjects[oi]->intersects(r, t, n, mat);
if(inters && (first || t - tMin < ACCURACY)){
winObject= sceneObjects[oi];
tMin= t;
if(wt!= NULL) *wt= tMin;
if(wn!= NULL) *wn= n;
if(wmat!= NULL) *wmat= mat;
first= false;
}
oi++;
}
return winObject;
};
color radiance(const ray &r, int depth)
{
double t;
vec3 n, x, nl;
material mat;
//intersects a scene object?
object* winObject= intersect(r, &t, &n, &mat);
if (winObject== NULL) return background;
//calc intersect world coord
x= r.origin.add(r.direction.scale(t));
//normal vector on the side on the ray
nl= (n.dot(r.direction)< 0)? n: n.scale(-1);
color& col= mat.material_color;
color emmitIntensity= col.scale(mat.mult_emission);
//max intensity
double maxIntensity = (col.red> col.green && col.red> col.blue)? col.red : (col.green> col.blue)? col.green : col.blue; // max refl
//break the path tracing, with probability= maxIntensity, when the depth is large enough
if(++depth> 5)
{
if(rand_01()<0.5){col= col.scale(1.0f/maxIntensity);}
else return emmitIntensity;
}
//return col;
if(mat.material_type == material::diffuse){
//printf("here;\n");
//gen uvw basis, w = nl
vec3 w= nl;
vec3 u= ((fabs(w.x)>0.1f? vec3(0.0f, 1.0f, 0.0f): vec3(1.0f, 0.0f, 0.0f)).cross(w)).normalize();
vec3 v= w.cross(u);
color diffcol;
//generate new random direction vec3, based on a hemisphere distribution
double r1= 2*pi*rand_01();
double r2= rand_01();
double r2s= sqrt(r2);
vec3 d = (u.scale(cos(r1)*r2s)//u
.add(v.scale(sin(r1)*r2s))//v
.add(w.scale(sqrt(1-r2))))//w
.normalize();
color e;
//for (int i=0; i<numSpheres; i++){
int h= 0;
object* s= NULL;
while((s = sceneObjects[h])!= NULL){
if(s->object_material()->mult_emission<= ACCURACY){h++; continue;}
const material* sMat= s->object_material();
vec3 sw= s->pos().add(x.negative());
vec3 su= ((fabs(sw.x)>.1?vec3(0.0f, 1.0f, 0.0f): vec3(1.0f, 0.0f, 0.0f)).cross(sw)).normalize();
vec3 sv= sw.cross(su);
double cos_a_max = sqrt(1-0.25/(sw.negative().dot(sw.negative())));
double eps1= rand_01(), eps2 = rand_01();
double cos_a = 1-eps1+eps1*cos_a_max;
double sin_a= sqrt(1-cos_a*cos_a);
double phi= 2*pi*eps2;
vec3 l= su.scale(cos(phi)*sin_a).add( sv.scale(sin(phi)*sin_a)).add(sw.scale(cos_a)).normalize();
if(intersect(ray(x, l)) == s)
{
double omega = 2*pi*(1-cos_a_max);
e= sMat->material_color.add(col.mult(e.scale(l.dot(nl)*omega))).scale(1.0f/pi);
}
h++;
}
return emmitIntensity.add(e).add(col.mult(radiance(ray(x, d), depth)).clamp());
}
else if(mat.material_type == material::specular){
vec3 reflDir= r.direction.reflect(n);
return emmitIntensity.add(col.mult(radiance(ray(x, reflDir),depth)));
}
vec3 reflDir= r.direction.reflect(n);
ray reflRay(x, reflDir);
bool into = n.dot(nl)>0;
double nc=1;
double nt= mat.refract_index;
double nnt= into? nc/nt: nt/nc;
double ddn=r.direction.dot(nl);
double cos2t= 1-nnt*nnt*(1-ddn*ddn);
if (cos2t<0)//total internal reflection
return emmitIntensity.add(col.mult(radiance(reflRay, depth)));
vec3 tDir = (r.direction.scale(nnt).add(n.scale(((into?1:-1)*(ddn*nnt+sqrt(cos2t)))).scale(-1.0f))).normalize();
double a= nt-nc;
double b= nt+nc;
double R0=a*a/(b*b);
double c = 1-(into? -ddn: tDir.dot(n));
double Re= R0+(1-R0)*c*c*c*c*c;
double Tr= 1-Re;
double P= 0.25+ 0.5*Re;
double RP= Re/P;
double TP=Tr/(1-P);
if(depth> 2)
return emmitIntensity.add(col.mult(
rand_01()< P? radiance(reflRay, depth).scale(RP)
: radiance(ray(x, tDir), depth).scale(TP)));
else
return emmitIntensity.add(col.mult(radiance(reflRay, depth).scale(Re).add(radiance(ray(x, tDir), depth).scale(Tr))));
}