#include #include #include #include #include #include struct vec3 { float x=0, y=0, z=0; 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); } vec3 operator*(const float v) const { return {x*v, y*v, z*v}; } float operator*(const vec3& v) const { return x*v.x + y*v.y + z*v.z; } vec3 operator+(const vec3& v) const { return {x+v.x, y+v.y, z+v.z}; } vec3 operator-(const vec3& v) const { return {x-v.x, y-v.y, z-v.z}; } vec3 operator-() const { return {-x, -y, -z}; } float norm() const { return std::sqrt(x*x+y*y+z*z); } vec3 normalized() const { return (*this)*(1.f/norm()); } }; vec3 cross(const vec3 v1, const 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 }; } struct Light { vec3 position; float intensity; }; struct Material { float refractive_index = 1; float albedo[4] = {1,0,0,0}; vec3 diffuse_color = {0,0,0}; float specular_exponent = 0; }; struct Sphere { vec3 center; float radius; Material material; }; static const Material ivory = {1.0, {0.6, 0.3, 0.1, 0.0}, {0.4, 0.4, 0.3}, 50.}; static const Material glass = {1.5, {0.0, 0.5, 0.1, 0.8}, {0.6, 0.7, 0.8}, 125.}; static const Material red_rubber = {1.0, {0.9, 0.1, 0.0, 0.0}, {0.3, 0.1, 0.1}, 10.}; static const Material mirror = {1.0, {0.0, 10.0, 0.8, 0.0}, {1.0, 1.0, 1.0}, 1425.}; static const std::vector 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} }; static const std::vector lights = { {{-20, 20, 20}, 1.5}, {{ 30, 50, -25}, 1.8}, {{ 30, 20, 30}, 1.7} }; 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 = std::sqrt(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); } 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 ? 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 vec3 &orig, const vec3 &dir, vec3 &hit, vec3 &N, Material &material) { float spheres_dist = std::numeric_limits::max(); for (const Sphere &s : spheres) { float dist_i; if (ray_sphere_intersect(orig, dir, s, dist_i) && dist_i < spheres_dist) { spheres_dist = dist_i; hit = orig + dir*dist_i; N = (hit - s.center).normalized(); material = s.material; } } float checkerboard_dist = std::numeric_limits::max(); 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 vec3 pt = orig + dir*d; if (d>1e-3 && fabs(pt.x)<10 && pt.z<-10 && pt.z>-30 && d4 || !scene_intersect(orig, dir, point, N, material)) return vec3{0.2, 0.7, 0.8}; // background color vec3 reflect_dir = reflect(dir, N).normalized(); vec3 refract_dir = refract(dir, N, material.refractive_index).normalized(); vec3 reflect_color = cast_ray(point, reflect_dir, depth + 1); vec3 refract_color = cast_ray(point, refract_dir, depth + 1); float diffuse_light_intensity = 0, specular_light_intensity = 0; for (const Light light : lights) { vec3 light_dir = (light.position - point).normalized(); vec3 shadow_pt, trashnrm; Material trashmat; if (scene_intersect(point, light_dir, 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 += 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] + vec3{1., 1., 1.}*specular_light_intensity * material.albedo[1] + reflect_color*material.albedo[2] + refract_color*material.albedo[3]; } int main() { const int width = 1024; const int height = 768; const float fov = M_PI/3.; std::vector framebuffer(width*height); #pragma omp parallel for for (size_t j = 0; j1) c = c*(1./max); ofs << (char)(255 * c[0]) << (char)(255 * c[1]) << (char)(255 * c[2]); } ofs.close(); return 0; }