mirror of
https://github.com/ssloy/tinyraytracer.git
synced 2024-04-18 07:30:54 +08:00
256 => 153 lines
This commit is contained in:
parent
efdec8133d
commit
d379242317
|
@ -1,15 +1,13 @@
|
|||
#include <limits>
|
||||
#include <tuple>
|
||||
#include <fstream>
|
||||
#include <vector>
|
||||
#include <fstream>
|
||||
#include <algorithm>
|
||||
#include <cassert>
|
||||
#include <cmath>
|
||||
|
||||
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); }
|
||||
float& operator[](const int i) { return i==0 ? x : (1==i ? y : z); }
|
||||
const float& operator[](const int i) const { 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}; }
|
||||
|
@ -23,14 +21,9 @@ 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};
|
||||
float albedo[4] = {2,0,0,0};
|
||||
vec3 diffuse_color = {0,0,0};
|
||||
float specular_exponent = 0;
|
||||
};
|
||||
|
@ -41,36 +34,24 @@ struct Sphere {
|
|||
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 Material ivory = {1.0, {0.9, 0.5, 0.1, 0.0}, {0.4, 0.4, 0.3}, 50.};
|
||||
static const Material glass = {1.5, {0.0, 0.9, 0.1, 0.8}, {0.6, 0.7, 0.8}, 125.};
|
||||
static const Material red_rubber = {1.0, {1.4, 0.3, 0.0, 0.0}, {0.3, 0.1, 0.1}, 10.};
|
||||
static const Material mirror = {1.0, {0.0, 16.0, 0.8, 0.0}, {1.0, 1.0, 1.0}, 1425.};
|
||||
|
||||
static const 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}
|
||||
static const Sphere spheres[] = {
|
||||
{{-3, 0, -16}, 2, ivory},
|
||||
{{-1.0, -1.5, -12}, 2, glass},
|
||||
{{ 1.5, -0.5, -18}, 3, red_rubber},
|
||||
{{ 7, 5, -18}, 4, mirror}
|
||||
};
|
||||
|
||||
static const std::vector<Light> lights = {
|
||||
{{-20, 20, 20}, 1.5},
|
||||
{{ 30, 50, -25}, 1.8},
|
||||
{{ 30, 20, 30}, 1.7}
|
||||
static const vec3 lights[] = {
|
||||
{-20, 20, 20},
|
||||
{ 30, 50, -25},
|
||||
{ 30, 20, 30}
|
||||
};
|
||||
|
||||
std::tuple<bool,float> ray_sphere_intersect(const vec3 &orig, const vec3 &dir, const Sphere &s) { // ret value is a pair [intersection found, distance]
|
||||
vec3 L = s.center - orig;
|
||||
float tca = L*dir;
|
||||
float d2 = L*L - tca*tca;
|
||||
if (d2 > s.radius*s.radius) return {false, 0.f};
|
||||
float thc = std::sqrt(s.radius*s.radius - d2);
|
||||
float t0 = tca-thc, t1 = tca+thc;
|
||||
if (t0>1e-3) return {true, t0}; // offset the original point by 1e-3 to avoid occlusion by the object itself
|
||||
if (t1>1e-3) return {true, t1};
|
||||
return {false, 0.f};
|
||||
}
|
||||
|
||||
vec3 reflect(const vec3 &I, const vec3 &N) {
|
||||
return I - N*2.f*(I*N);
|
||||
}
|
||||
|
@ -83,37 +64,49 @@ vec3 refract(const vec3 &I, const vec3 &N, const float eta_t, const float eta_i=
|
|||
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) { // TODO: move output values to a return tuple
|
||||
float spheres_dist = std::numeric_limits<float>::max();
|
||||
for (const Sphere &s : spheres) {
|
||||
if (auto [intersection, dist] = ray_sphere_intersect(orig, dir, s); intersection && dist < spheres_dist) {
|
||||
spheres_dist = dist;
|
||||
hit = orig + dir*dist;
|
||||
N = (hit - s.center).normalized();
|
||||
material = s.material;
|
||||
}
|
||||
}
|
||||
|
||||
float checkerboard_dist = std::numeric_limits<float>::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 && d<spheres_dist) {
|
||||
checkerboard_dist = d;
|
||||
hit = pt;
|
||||
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;
|
||||
std::tuple<bool,float> ray_sphere_intersect(const vec3 &orig, const vec3 &dir, const Sphere &s) { // ret value is a pair [intersection found, distance]
|
||||
vec3 L = s.center - orig;
|
||||
float tca = L*dir;
|
||||
float d2 = L*L - tca*tca;
|
||||
if (d2 > s.radius*s.radius) return {false, 0};
|
||||
float thc = std::sqrt(s.radius*s.radius - d2);
|
||||
float t0 = tca-thc, t1 = tca+thc;
|
||||
if (t0>.001) return {true, t0}; // offset the original point by .001 to avoid occlusion by the object itself
|
||||
if (t1>.001) return {true, t1};
|
||||
return {false, 0};
|
||||
}
|
||||
|
||||
vec3 cast_ray(const vec3 &orig, const vec3 &dir, size_t depth=0) {
|
||||
vec3 point, N;
|
||||
std::tuple<bool,vec3,vec3,Material> scene_intersect(const vec3 &orig, const vec3 &dir) {
|
||||
vec3 pt, N;
|
||||
Material material;
|
||||
|
||||
if (depth>4 || !scene_intersect(orig, dir, point, N, material))
|
||||
return vec3{0.2, 0.7, 0.8}; // background color
|
||||
float nearest_dist = 1e10;
|
||||
if (std::abs(dir.y)>.001) { // intersect the ray with the checkerboard, avoid division by zero
|
||||
float d = -(orig.y+4)/dir.y; // the checkerboard plane has equation y = -4
|
||||
vec3 p = orig + dir*d;
|
||||
if (d>.001 && d<nearest_dist && std::abs(p.x)<10 && p.z<-10 && p.z>-30) {
|
||||
nearest_dist = d;
|
||||
pt = p;
|
||||
N = {0,1,0};
|
||||
material.diffuse_color = (int(.5*pt.x+1000) + int(.5*pt.z)) & 1 ? vec3{.3, .3, .3} : vec3{.3, .2, .1};
|
||||
}
|
||||
}
|
||||
|
||||
for (const Sphere &s : spheres) { // intersect the ray with all spheres
|
||||
auto [intersection, d] = ray_sphere_intersect(orig, dir, s);
|
||||
if (!intersection || d > nearest_dist) continue;
|
||||
nearest_dist = d;
|
||||
pt = orig + dir*nearest_dist;
|
||||
N = (pt - s.center).normalized();
|
||||
material = s.material;
|
||||
}
|
||||
return {nearest_dist<1000, pt, N, material};
|
||||
}
|
||||
|
||||
vec3 cast_ray(const vec3 &orig, const vec3 &dir, int depth=0) {
|
||||
auto [hit, point, N, material] = scene_intersect(orig, dir);
|
||||
if (depth>4 || !hit)
|
||||
return {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();
|
||||
|
@ -121,17 +114,12 @@ vec3 cast_ray(const vec3 &orig, const vec3 &dir, size_t depth=0) {
|
|||
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;
|
||||
for (const vec3 light : lights) { // checking if the point lies in the shadow of the light
|
||||
vec3 light_dir = (light - point).normalized();
|
||||
auto [hit, shadow_pt, trashnrm, trashmat] = scene_intersect(point, light_dir);
|
||||
if (hit && (shadow_pt-point).norm() < (light-point).norm()) continue;
|
||||
diffuse_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);
|
||||
}
|
||||
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];
|
||||
}
|
||||
|
@ -139,11 +127,11 @@ vec3 cast_ray(const vec3 &orig, const vec3 &dir, size_t depth=0) {
|
|||
int main() {
|
||||
const int width = 1024;
|
||||
const int height = 768;
|
||||
const float fov = M_PI/3.;
|
||||
const float fov = 1.05; // 60 degrees field of view in radians
|
||||
std::vector<vec3> framebuffer(width*height);
|
||||
#pragma omp parallel for
|
||||
for (size_t j = 0; j<height; j++) { // actual rendering loop
|
||||
for (size_t i = 0; i<width; i++) {
|
||||
for (int j = 0; j<height; j++) { // actual rendering loop
|
||||
for (int i = 0; i<width; i++) {
|
||||
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.));
|
||||
|
@ -154,10 +142,10 @@ int main() {
|
|||
std::ofstream ofs; // save the framebuffer to file
|
||||
ofs.open("./out.ppm", std::ios::binary);
|
||||
ofs << "P6\n" << width << " " << height << "\n255\n";
|
||||
for (vec3 &c : framebuffer) {
|
||||
float max = std::max(c[0], std::max(c[1], c[2]));
|
||||
if (max>1) c = c*(1./max);
|
||||
ofs << (char)(255 * c[0]) << (char)(255 * c[1]) << (char)(255 * c[2]);
|
||||
for (vec3 &color : framebuffer) {
|
||||
float max = std::max(1.f, std::max(color[0], std::max(color[1], color[2])));
|
||||
for (int chan : {0,1,2})
|
||||
ofs << (char)(255 * color[chan]/max);
|
||||
}
|
||||
ofs.close();
|
||||
return 0;
|
||||
|
|
Loading…
Reference in New Issue
Block a user