facelift
parent
769d9c952a
commit
a9a1d11af2
|
@ -17,7 +17,7 @@ endfunction()
|
|||
enable_cxx_compiler_flag_if_supported("-Wall")
|
||||
enable_cxx_compiler_flag_if_supported("-Wextra")
|
||||
enable_cxx_compiler_flag_if_supported("-pedantic")
|
||||
enable_cxx_compiler_flag_if_supported("-std=c++11")
|
||||
enable_cxx_compiler_flag_if_supported("-std=c++14")
|
||||
enable_cxx_compiler_flag_if_supported("-O3")
|
||||
enable_cxx_compiler_flag_if_supported("-fopenmp")
|
||||
|
||||
|
|
105
geometry.h
105
geometry.h
|
@ -1,84 +1,61 @@
|
|||
#ifndef __GEOMETRY_H__
|
||||
#define __GEOMETRY_H__
|
||||
|
||||
#include <cmath>
|
||||
#include <vector>
|
||||
#include <cassert>
|
||||
#include <iostream>
|
||||
|
||||
template <size_t DIM, typename T> struct vec {
|
||||
vec() { for (size_t i=DIM; i--; data_[i] = T()); }
|
||||
T& operator[](const size_t i) { assert(i<DIM); return data_[i]; }
|
||||
const T& operator[](const size_t i) const { assert(i<DIM); return data_[i]; }
|
||||
private:
|
||||
T data_[DIM];
|
||||
template <size_t DIM> struct vec {
|
||||
float& operator[](const size_t i) { assert(i<DIM); return data[i]; }
|
||||
const float& operator[](const size_t i) const { assert(i<DIM); return data[i]; }
|
||||
float data[DIM] = {};
|
||||
};
|
||||
|
||||
typedef vec<2, float> Vec2f;
|
||||
typedef vec<3, float> Vec3f;
|
||||
typedef vec<3, int > Vec3i;
|
||||
typedef vec<4, float> Vec4f;
|
||||
|
||||
template <typename T> struct vec<2,T> {
|
||||
vec() : x(T()), y(T()) {}
|
||||
vec(T X, T Y) : x(X), y(Y) {}
|
||||
template <class U> vec<2,T>(const vec<2,U> &v);
|
||||
T& operator[](const size_t i) { assert(i<2); return i<=0 ? x : y; }
|
||||
const T& operator[](const size_t i) const { assert(i<2); return i<=0 ? x : y; }
|
||||
T x,y;
|
||||
};
|
||||
|
||||
template <typename T> struct vec<3,T> {
|
||||
vec() : x(T()), y(T()), z(T()) {}
|
||||
vec(T X, T Y, T Z) : x(X), y(Y), z(Z) {}
|
||||
T& operator[](const size_t i) { assert(i<3); return i<=0 ? x : (1==i ? y : z); }
|
||||
const T& operator[](const size_t i) const { assert(i<3); return i<=0 ? x : (1==i ? y : z); }
|
||||
float norm() { return std::sqrt(x*x+y*y+z*z); }
|
||||
vec<3,T> & normalize(T l=1) { *this = (*this)*(l/norm()); return *this; }
|
||||
T x,y,z;
|
||||
};
|
||||
|
||||
template <typename T> struct vec<4,T> {
|
||||
vec() : x(T()), y(T()), z(T()), w(T()) {}
|
||||
vec(T X, T Y, T Z, T W) : x(X), y(Y), z(Z), w(W) {}
|
||||
T& operator[](const size_t i) { assert(i<4); return i<=0 ? x : (1==i ? y : (2==i ? z : w)); }
|
||||
const T& operator[](const size_t i) const { assert(i<4); return i<=0 ? x : (1==i ? y : (2==i ? z : w)); }
|
||||
T x,y,z,w;
|
||||
};
|
||||
|
||||
template<size_t DIM,typename T> T operator*(const vec<DIM,T>& lhs, const vec<DIM,T>& rhs) {
|
||||
T ret = T();
|
||||
for (size_t i=DIM; i--; ret+=lhs[i]*rhs[i]);
|
||||
return ret;
|
||||
}
|
||||
|
||||
template<size_t DIM,typename T>vec<DIM,T> operator+(vec<DIM,T> lhs, const vec<DIM,T>& rhs) {
|
||||
for (size_t i=DIM; i--; lhs[i]+=rhs[i]);
|
||||
return lhs;
|
||||
}
|
||||
|
||||
template<size_t DIM,typename T>vec<DIM,T> operator-(vec<DIM,T> lhs, const vec<DIM,T>& rhs) {
|
||||
for (size_t i=DIM; i--; lhs[i]-=rhs[i]);
|
||||
return lhs;
|
||||
}
|
||||
|
||||
template<size_t DIM,typename T,typename U> vec<DIM,T> operator*(const vec<DIM,T> &lhs, const U& rhs) {
|
||||
vec<DIM,T> ret;
|
||||
template<size_t DIM> vec<DIM> operator*(const vec<DIM> &lhs, const float rhs) {
|
||||
vec<DIM> ret;
|
||||
for (size_t i=DIM; i--; ret[i]=lhs[i]*rhs);
|
||||
return ret;
|
||||
}
|
||||
|
||||
template<size_t DIM,typename T> vec<DIM,T> operator-(const vec<DIM,T> &lhs) {
|
||||
return lhs*T(-1);
|
||||
template<size_t DIM> float operator*(const vec<DIM>& lhs, const vec<DIM>& rhs) {
|
||||
float ret = 0;
|
||||
for (size_t i=DIM; i--; ret+=lhs[i]*rhs[i]);
|
||||
return ret;
|
||||
}
|
||||
|
||||
template <typename T> vec<3,T> cross(vec<3,T> v1, vec<3,T> v2) {
|
||||
return vec<3,T>(v1.y*v2.z - v1.z*v2.y, v1.z*v2.x - v1.x*v2.z, v1.x*v2.y - v1.y*v2.x);
|
||||
template<size_t DIM> vec<DIM> operator+(vec<DIM> lhs, const vec<DIM>& rhs) {
|
||||
for (size_t i=DIM; i--; lhs[i]+=rhs[i]);
|
||||
return lhs;
|
||||
}
|
||||
|
||||
template <size_t DIM, typename T> std::ostream& operator<<(std::ostream& out, const vec<DIM,T>& v) {
|
||||
for(unsigned int i=0; i<DIM; i++) out << v[i] << " " ;
|
||||
template<size_t DIM> vec<DIM> operator-(vec<DIM> lhs, const vec<DIM>& rhs) {
|
||||
for (size_t i=DIM; i--; lhs[i]-=rhs[i]);
|
||||
return lhs;
|
||||
}
|
||||
|
||||
template<size_t DIM> vec<DIM> operator-(const vec<DIM> &lhs) {
|
||||
return lhs*(-1.f);
|
||||
}
|
||||
|
||||
template <> struct vec<3> {
|
||||
float& operator[](const size_t i) { assert(i<3); return i==0 ? x : (1==i ? y : z); }
|
||||
const float& operator[](const size_t i) const { assert(i<3); return i==0 ? x : (1==i ? y : z); }
|
||||
float norm() { return std::sqrt(x*x+y*y+z*z); }
|
||||
vec<3> & normalize(float l=1) { *this = (*this)*(l/norm()); return *this; }
|
||||
float x = 0, y = 0, z = 0;
|
||||
};
|
||||
|
||||
typedef vec<3> vec3;
|
||||
typedef vec<4> vec4;
|
||||
|
||||
vec3 cross(vec3 v1, vec3 v2) {
|
||||
return { v1.y*v2.z - v1.z*v2.y, v1.z*v2.x - v1.x*v2.z, v1.x*v2.y - v1.y*v2.x };
|
||||
}
|
||||
|
||||
template <size_t DIM> std::ostream& operator<<(std::ostream& out, const vec<DIM>& v) {
|
||||
for (size_t i=0; i<DIM; i++)
|
||||
out << v[i] << " " ;
|
||||
return out ;
|
||||
}
|
||||
|
||||
#endif //__GEOMETRY_H__
|
||||
|
||||
|
|
|
@ -1,123 +1,112 @@
|
|||
#define _USE_MATH_DEFINES
|
||||
#include <cmath>
|
||||
#include "geometry.h"
|
||||
|
||||
#include <limits>
|
||||
#include <iostream>
|
||||
#include <fstream>
|
||||
#include <vector>
|
||||
#include <algorithm>
|
||||
#include "geometry.h"
|
||||
|
||||
struct Light {
|
||||
Light(const Vec3f &p, const float i) : position(p), intensity(i) {}
|
||||
Vec3f position;
|
||||
vec3 position;
|
||||
float intensity;
|
||||
};
|
||||
|
||||
struct Material {
|
||||
Material(const float r, const Vec4f &a, const Vec3f &color, const float spec) : refractive_index(r), albedo(a), diffuse_color(color), specular_exponent(spec) {}
|
||||
Material() : refractive_index(1), albedo(1,0,0,0), diffuse_color(), specular_exponent() {}
|
||||
float refractive_index;
|
||||
Vec4f albedo;
|
||||
Vec3f diffuse_color;
|
||||
float specular_exponent;
|
||||
float refractive_index = 1;
|
||||
vec4 albedo = {1,0,0,0};
|
||||
vec3 diffuse_color = {0,0,0};
|
||||
float specular_exponent = 0;
|
||||
};
|
||||
|
||||
struct Sphere {
|
||||
Vec3f center;
|
||||
vec3 center;
|
||||
float radius;
|
||||
Material material;
|
||||
|
||||
Sphere(const Vec3f &c, const float r, const Material &m) : center(c), radius(r), material(m) {}
|
||||
|
||||
bool ray_intersect(const Vec3f &orig, const Vec3f &dir, float &t0) const {
|
||||
Vec3f L = center - orig;
|
||||
float tca = L*dir;
|
||||
float d2 = L*L - tca*tca;
|
||||
if (d2 > radius*radius) return false;
|
||||
float thc = sqrtf(radius*radius - d2);
|
||||
t0 = tca - thc;
|
||||
float t1 = tca + thc;
|
||||
if (t0 < 0) t0 = t1;
|
||||
if (t0 < 0) return false;
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
Vec3f reflect(const Vec3f &I, const Vec3f &N) {
|
||||
bool ray_sphere_intersect(const vec3 &orig, const vec3 &dir, const Sphere &s, float &t0) {
|
||||
vec3 L = s.center - orig;
|
||||
float tca = L*dir;
|
||||
float d2 = L*L - tca*tca;
|
||||
if (d2 > s.radius*s.radius) return false;
|
||||
float thc = sqrtf(s.radius*s.radius - d2);
|
||||
t0 = tca - thc;
|
||||
float t1 = tca + thc;
|
||||
if (t0 < 1e-3) t0 = t1; // offset the original point to avoid occlusion by the object itself
|
||||
if (t0 < 1e-3) return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
vec3 reflect(const vec3 &I, const vec3 &N) {
|
||||
return I - N*2.f*(I*N);
|
||||
}
|
||||
|
||||
Vec3f refract(const Vec3f &I, const Vec3f &N, const float eta_t, const float eta_i=1.f) { // Snell's law
|
||||
vec3 refract(const vec3 &I, const vec3 &N, const float eta_t, const float eta_i=1.f) { // Snell's law
|
||||
float cosi = - std::max(-1.f, std::min(1.f, I*N));
|
||||
if (cosi<0) return refract(I, -N, eta_i, eta_t); // if the ray comes from the inside the object, swap the air and the media
|
||||
float eta = eta_i / eta_t;
|
||||
float k = 1 - eta*eta*(1 - cosi*cosi);
|
||||
return k<0 ? Vec3f(1,0,0) : I*eta + N*(eta*cosi - sqrtf(k)); // k<0 = total reflection, no ray to refract. I refract it anyways, this has no physical meaning
|
||||
return k<0 ? vec3{1,0,0} : I*eta + N*(eta*cosi - std::sqrt(k)); // k<0 = total reflection, no ray to refract. I refract it anyways, this has no physical meaning
|
||||
}
|
||||
|
||||
bool scene_intersect(const Vec3f &orig, const Vec3f &dir, const std::vector<Sphere> &spheres, Vec3f &hit, Vec3f &N, Material &material) {
|
||||
bool scene_intersect(const vec3 &orig, const vec3 &dir, const std::vector<Sphere> &spheres, vec3 &hit, vec3 &N, Material &material) {
|
||||
float spheres_dist = std::numeric_limits<float>::max();
|
||||
for (size_t i=0; i < spheres.size(); i++) {
|
||||
for (const Sphere &s : spheres) {
|
||||
float dist_i;
|
||||
if (spheres[i].ray_intersect(orig, dir, dist_i) && dist_i < spheres_dist) {
|
||||
if (ray_sphere_intersect(orig, dir, s, dist_i) && dist_i < spheres_dist) {
|
||||
spheres_dist = dist_i;
|
||||
hit = orig + dir*dist_i;
|
||||
N = (hit - spheres[i].center).normalize();
|
||||
material = spheres[i].material;
|
||||
N = (hit - s.center).normalize();
|
||||
material = s.material;
|
||||
}
|
||||
}
|
||||
|
||||
float checkerboard_dist = std::numeric_limits<float>::max();
|
||||
if (fabs(dir.y)>1e-3) {
|
||||
if (std::abs(dir.y)>1e-3) { // avoid division by zero
|
||||
float d = -(orig.y+4)/dir.y; // the checkerboard plane has equation y = -4
|
||||
Vec3f pt = orig + dir*d;
|
||||
if (d>0 && fabs(pt.x)<10 && pt.z<-10 && pt.z>-30 && d<spheres_dist) {
|
||||
vec3 pt = orig + dir*d;
|
||||
if (d>1e-3 && fabs(pt.x)<10 && pt.z<-10 && pt.z>-30 && d<spheres_dist) {
|
||||
checkerboard_dist = d;
|
||||
hit = pt;
|
||||
N = Vec3f(0,1,0);
|
||||
material.diffuse_color = (int(.5*hit.x+1000) + int(.5*hit.z)) & 1 ? Vec3f(.3, .3, .3) : Vec3f(.3, .2, .1);
|
||||
N = vec3{0,1,0};
|
||||
material.diffuse_color = (int(.5*hit.x+1000) + int(.5*hit.z)) & 1 ? vec3{.3, .3, .3} : vec3{.3, .2, .1};
|
||||
}
|
||||
}
|
||||
return std::min(spheres_dist, checkerboard_dist)<1000;
|
||||
}
|
||||
|
||||
Vec3f cast_ray(const Vec3f &orig, const Vec3f &dir, const std::vector<Sphere> &spheres, const std::vector<Light> &lights, size_t depth=0) {
|
||||
Vec3f point, N;
|
||||
vec3 cast_ray(const vec3 &orig, const vec3 &dir, const std::vector<Sphere> &spheres, const std::vector<Light> &lights, size_t depth=0) {
|
||||
vec3 point, N;
|
||||
Material material;
|
||||
|
||||
if (depth>4 || !scene_intersect(orig, dir, spheres, point, N, material)) {
|
||||
return Vec3f(0.2, 0.7, 0.8); // background color
|
||||
}
|
||||
if (depth>4 || !scene_intersect(orig, dir, spheres, point, N, material))
|
||||
return vec3{0.2, 0.7, 0.8}; // background color
|
||||
|
||||
Vec3f reflect_dir = reflect(dir, N).normalize();
|
||||
Vec3f refract_dir = refract(dir, N, material.refractive_index).normalize();
|
||||
Vec3f reflect_orig = reflect_dir*N < 0 ? point - N*1e-3 : point + N*1e-3; // offset the original point to avoid occlusion by the object itself
|
||||
Vec3f refract_orig = refract_dir*N < 0 ? point - N*1e-3 : point + N*1e-3;
|
||||
Vec3f reflect_color = cast_ray(reflect_orig, reflect_dir, spheres, lights, depth + 1);
|
||||
Vec3f refract_color = cast_ray(refract_orig, refract_dir, spheres, lights, depth + 1);
|
||||
vec3 reflect_dir = reflect(dir, N).normalize();
|
||||
vec3 refract_dir = refract(dir, N, material.refractive_index).normalize();
|
||||
vec3 reflect_color = cast_ray(point, reflect_dir, spheres, lights, depth + 1);
|
||||
vec3 refract_color = cast_ray(point, refract_dir, spheres, lights, depth + 1);
|
||||
|
||||
float diffuse_light_intensity = 0, specular_light_intensity = 0;
|
||||
for (size_t i=0; i<lights.size(); i++) {
|
||||
Vec3f light_dir = (lights[i].position - point).normalize();
|
||||
float light_distance = (lights[i].position - point).norm();
|
||||
for (const Light light : lights) {
|
||||
vec3 light_dir = (light.position - point).normalize();
|
||||
|
||||
Vec3f shadow_orig = light_dir*N < 0 ? point - N*1e-3 : point + N*1e-3; // checking if the point lies in the shadow of the lights[i]
|
||||
Vec3f shadow_pt, shadow_N;
|
||||
Material tmpmaterial;
|
||||
if (scene_intersect(shadow_orig, light_dir, spheres, shadow_pt, shadow_N, tmpmaterial) && (shadow_pt-shadow_orig).norm() < light_distance)
|
||||
vec3 shadow_pt, trashnrm;
|
||||
Material trashmat;
|
||||
if (scene_intersect(point, light_dir, spheres, shadow_pt, trashnrm, trashmat) &&
|
||||
(shadow_pt-point).norm() < (light.position-point).norm()) // checking if the point lies in the shadow of the light
|
||||
continue;
|
||||
|
||||
diffuse_light_intensity += lights[i].intensity * std::max(0.f, light_dir*N);
|
||||
specular_light_intensity += powf(std::max(0.f, -reflect(-light_dir, N)*dir), material.specular_exponent)*lights[i].intensity;
|
||||
diffuse_light_intensity += light.intensity * std::max(0.f, light_dir*N);
|
||||
specular_light_intensity += std::pow(std::max(0.f, -reflect(-light_dir, N)*dir), material.specular_exponent)*light.intensity;
|
||||
}
|
||||
return material.diffuse_color * diffuse_light_intensity * material.albedo[0] + Vec3f(1., 1., 1.)*specular_light_intensity * material.albedo[1] + reflect_color*material.albedo[2] + refract_color*material.albedo[3];
|
||||
return material.diffuse_color * diffuse_light_intensity * material.albedo[0] + vec3{1., 1., 1.}*specular_light_intensity * material.albedo[1] + reflect_color*material.albedo[2] + refract_color*material.albedo[3];
|
||||
}
|
||||
|
||||
void render(const std::vector<Sphere> &spheres, const std::vector<Light> &lights) {
|
||||
const int width = 1024;
|
||||
const int height = 768;
|
||||
const float fov = M_PI/3.;
|
||||
std::vector<Vec3f> framebuffer(width*height);
|
||||
std::vector<vec3> framebuffer(width*height);
|
||||
|
||||
#pragma omp parallel for
|
||||
for (size_t j = 0; j<height; j++) { // actual rendering loop
|
||||
|
@ -125,43 +114,41 @@ void render(const std::vector<Sphere> &spheres, const std::vector<Light> &lights
|
|||
float dir_x = (i + 0.5) - width/2.;
|
||||
float dir_y = -(j + 0.5) + height/2.; // this flips the image at the same time
|
||||
float dir_z = -height/(2.*tan(fov/2.));
|
||||
framebuffer[i+j*width] = cast_ray(Vec3f(0,0,0), Vec3f(dir_x, dir_y, dir_z).normalize(), spheres, lights);
|
||||
framebuffer[i+j*width] = cast_ray(vec3{0,0,0}, vec3{dir_x, dir_y, dir_z}.normalize(), spheres, lights);
|
||||
}
|
||||
}
|
||||
|
||||
std::ofstream ofs; // save the framebuffer to file
|
||||
ofs.open("./out.ppm",std::ios::binary);
|
||||
ofs.open("./out.ppm", std::ios::binary);
|
||||
ofs << "P6\n" << width << " " << height << "\n255\n";
|
||||
for (size_t i = 0; i < height*width; ++i) {
|
||||
Vec3f &c = framebuffer[i];
|
||||
for (vec3 &c : framebuffer) {
|
||||
float max = std::max(c[0], std::max(c[1], c[2]));
|
||||
if (max>1) c = c*(1./max);
|
||||
for (size_t j = 0; j<3; j++) {
|
||||
ofs << (char)(255 * std::max(0.f, std::min(1.f, framebuffer[i][j])));
|
||||
}
|
||||
ofs << (char)(255 * c[0]) << (char)(255 * c[1]) << (char)(255 * c[2]);
|
||||
}
|
||||
ofs.close();
|
||||
}
|
||||
|
||||
int main() {
|
||||
Material ivory(1.0, Vec4f(0.6, 0.3, 0.1, 0.0), Vec3f(0.4, 0.4, 0.3), 50.);
|
||||
Material glass(1.5, Vec4f(0.0, 0.5, 0.1, 0.8), Vec3f(0.6, 0.7, 0.8), 125.);
|
||||
Material red_rubber(1.0, Vec4f(0.9, 0.1, 0.0, 0.0), Vec3f(0.3, 0.1, 0.1), 10.);
|
||||
Material mirror(1.0, Vec4f(0.0, 10.0, 0.8, 0.0), Vec3f(1.0, 1.0, 1.0), 1425.);
|
||||
const Material ivory = {1.0, {0.6, 0.3, 0.1, 0.0}, {0.4, 0.4, 0.3}, 50.};
|
||||
const Material glass = {1.5, {0.0, 0.5, 0.1, 0.8}, {0.6, 0.7, 0.8}, 125.};
|
||||
const Material red_rubber = {1.0, {0.9, 0.1, 0.0, 0.0}, {0.3, 0.1, 0.1}, 10.};
|
||||
const Material mirror = {1.0, {0.0, 10.0, 0.8, 0.0}, {1.0, 1.0, 1.0}, 1425.};
|
||||
|
||||
std::vector<Sphere> spheres;
|
||||
spheres.push_back(Sphere(Vec3f(-3, 0, -16), 2, ivory));
|
||||
spheres.push_back(Sphere(Vec3f(-1.0, -1.5, -12), 2, glass));
|
||||
spheres.push_back(Sphere(Vec3f( 1.5, -0.5, -18), 3, red_rubber));
|
||||
spheres.push_back(Sphere(Vec3f( 7, 5, -18), 4, mirror));
|
||||
std::vector<Sphere> spheres = {
|
||||
Sphere{vec3{-3, 0, -16}, 2, ivory},
|
||||
Sphere{vec3{-1.0, -1.5, -12}, 2, glass},
|
||||
Sphere{vec3{ 1.5, -0.5, -18}, 3, red_rubber},
|
||||
Sphere{vec3{ 7, 5, -18}, 4, mirror}
|
||||
};
|
||||
|
||||
std::vector<Light> lights;
|
||||
lights.push_back(Light(Vec3f(-20, 20, 20), 1.5));
|
||||
lights.push_back(Light(Vec3f( 30, 50, -25), 1.8));
|
||||
lights.push_back(Light(Vec3f( 30, 20, 30), 1.7));
|
||||
std::vector<Light> lights = {
|
||||
{{-20, 20, 20}, 1.5},
|
||||
{{ 30, 50, -25}, 1.8},
|
||||
{{ 30, 20, 30}, 1.7}
|
||||
};
|
||||
|
||||
render(spheres, lights);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue