├── .gitignore └── src ├── ray.h ├── hitable.h ├── hitable_list.h ├── utils.h ├── sphere.h ├── camera.h ├── material.h ├── vec3.h ├── Raytracer.cpp └── RaytracerGPU.cu /.gitignore: -------------------------------------------------------------------------------- 1 | .cproject 2 | .project 3 | .settings 4 | Debug 5 | Debug-GPU 6 | Release 7 | Release-GPU 8 | *.ppm 9 | -------------------------------------------------------------------------------- /src/ray.h: -------------------------------------------------------------------------------- 1 | #ifndef RAY_H_ 2 | #define RAY_H_ 3 | 4 | #include "vec3.h" 5 | 6 | class ray { 7 | public: 8 | ray() {} 9 | ray(const vec3& a, const vec3& b) { A = a; B = b;} 10 | vec3 origin() const { return A; } 11 | vec3 direction() const { return B; } 12 | vec3 point_at_parameter(float t) const { return A + t*B; } 13 | 14 | vec3 A; 15 | vec3 B; 16 | }; 17 | 18 | #endif /* RAY_H_ */ 19 | -------------------------------------------------------------------------------- /src/hitable.h: -------------------------------------------------------------------------------- 1 | #ifndef HITABLE_H_ 2 | #define HITABLE_H_ 3 | 4 | #include "ray.h" 5 | 6 | class material; 7 | 8 | struct hit_record { 9 | float t; 10 | vec3 p; 11 | vec3 normal; 12 | material *mat_ptr; 13 | }; 14 | 15 | class hitable { 16 | public: 17 | virtual bool hit(const ray& r, float t_min, float t_max, hit_record& rec) const = 0; 18 | }; 19 | 20 | 21 | 22 | 23 | #endif /* HITABLE_H_ */ 24 | -------------------------------------------------------------------------------- /src/hitable_list.h: -------------------------------------------------------------------------------- 1 | #ifndef HITABLE_LIST_H_ 2 | #define HITABLE_LIST_H_ 3 | 4 | #include "hitable.h" 5 | 6 | class hitable_list: public hitable { 7 | public: 8 | hitable_list(hitable **l, int n) { list = l; list_size = n; } 9 | virtual bool hit(const ray& r, float tmin, float tmax, hit_record& rec) const; 10 | hitable **list; 11 | int list_size; 12 | }; 13 | 14 | bool hitable_list::hit(const ray& r, float tmin, float tmax, hit_record& rec) const { 15 | hit_record temp_rec; 16 | bool hit_anything = false; 17 | double closest_so_far = tmax; 18 | for (int i = 0; i < list_size; ++i) { 19 | if (list[i]->hit(r, tmin, closest_so_far, temp_rec)) { 20 | hit_anything = true; 21 | closest_so_far = temp_rec.t; 22 | rec = temp_rec; 23 | } 24 | } 25 | return hit_anything; 26 | } 27 | 28 | 29 | #endif /* HITABLE_LIST_H_ */ 30 | -------------------------------------------------------------------------------- /src/utils.h: -------------------------------------------------------------------------------- 1 | #ifndef UTILS_H_ 2 | #define UTILS_H_ 3 | 4 | #include "vec3.h" 5 | 6 | vec3 random_in_unit_sphere() { 7 | vec3 p; 8 | do { 9 | p = 2.0*vec3(drand48(), drand48(), drand48()) - vec3(1,1,1); 10 | } while (p.squared_length() >= 1.0); 11 | return p; 12 | } 13 | 14 | vec3 reflect(const vec3& v, const vec3& n) { 15 | return v - 2*dot(v, n)*n; 16 | } 17 | 18 | bool refract(const vec3& v, const vec3& n, float ni_over_nt, vec3& refracted) { 19 | vec3 uv = unit_vector(v); 20 | float dt = dot(uv, n); 21 | float discriminant = 1.0 - ni_over_nt*ni_over_nt*(1-dt*dt); 22 | if (discriminant > 0) { 23 | refracted = ni_over_nt*(uv - n*dt) - n*sqrtf(discriminant); 24 | return true; 25 | } 26 | return false; 27 | } 28 | 29 | float schlick(float cosine, float ref_idx) { 30 | float r0 = (1-ref_idx) / (1+ref_idx); 31 | r0 = r0*r0; 32 | return r0 + (1-r0)*pow((1 - cosine), 5); 33 | } 34 | 35 | vec3 random_in_unit_disk() { 36 | vec3 p; 37 | do { 38 | p = 2.0*vec3(drand48(), drand48(), 0) - vec3(1,1,0); 39 | } while (dot(p,p) >= 1.0); 40 | return p; 41 | } 42 | 43 | #endif /* UTILS_H_ */ 44 | -------------------------------------------------------------------------------- /src/sphere.h: -------------------------------------------------------------------------------- 1 | #ifndef SPHERE_H_ 2 | #define SPHERE_H_ 3 | 4 | #include "hitable.h" 5 | #include "material.h" 6 | 7 | class sphere: public hitable { 8 | public: 9 | sphere() { radius = 1; mat_ptr = new lambertian(vec3(0.4, 0.4, 0.4)); } 10 | sphere(vec3 cen, float r, material *mat) { center = cen; radius = r; mat_ptr = mat; } 11 | virtual bool hit(const ray& r, float t_min, float t_max, hit_record& rec) const; 12 | 13 | vec3 center; 14 | float radius; 15 | material *mat_ptr; 16 | }; 17 | 18 | bool sphere::hit(const ray& r, float t_min, float t_max, hit_record& rec) const { 19 | vec3 oc = r.origin() - center; 20 | float a = r.direction().squared_length(); 21 | float b = 2.0 * dot(oc, r.direction()); 22 | float c = oc.squared_length() - radius*radius; 23 | float discriminant = b*b - 4*a*c; 24 | if (discriminant > 0) { 25 | float temp = (-b - sqrtf(discriminant)) / (2.0*a); 26 | if (temp < t_max && temp > t_min) { 27 | rec.t = temp; 28 | rec.p = r.point_at_parameter(temp); 29 | rec.normal = (rec.p - center) / radius; 30 | rec.mat_ptr = mat_ptr; 31 | return true; 32 | } 33 | } 34 | return false; 35 | } 36 | 37 | 38 | 39 | #endif /* SPHERE_H_ */ 40 | -------------------------------------------------------------------------------- /src/camera.h: -------------------------------------------------------------------------------- 1 | #ifndef CAMERA_H_ 2 | #define CAMERA_H_ 3 | 4 | #include "utils.h" 5 | #include "ray.h" 6 | 7 | class camera { 8 | public: 9 | camera(vec3 lookfrom, vec3 lookat, vec3 vup, float vfov, float aspect, float aperture, float focus_dist) { // vfov is top to bottom in degrees 10 | lens_radius = aperture / 2; 11 | float theta = vfov*M_PI/180; 12 | float half_height = tan(theta/2); 13 | float half_width = aspect * half_height; 14 | origin = lookfrom; 15 | w = unit_vector(lookfrom - lookat); 16 | u = unit_vector(cross(vup, w)); 17 | v = cross(w, u); 18 | lower_left_corner = origin - half_width*focus_dist*u - half_height*focus_dist*v - focus_dist*w; 19 | horizontal = 2*half_width*focus_dist*u; 20 | vertical = 2*half_height*focus_dist*v; 21 | } 22 | 23 | void get_ray(float s, float t, ray& r) const { 24 | vec3 rd = lens_radius*random_in_unit_disk(); 25 | vec3 offset = u*rd.x() + v*rd.y(); 26 | 27 | r.A = origin + offset; 28 | r.B = lower_left_corner + s*horizontal + t*vertical - origin - offset; 29 | } 30 | 31 | vec3 origin; 32 | vec3 lower_left_corner; 33 | vec3 horizontal; 34 | vec3 vertical; 35 | vec3 u, v, w; 36 | float lens_radius; 37 | }; 38 | 39 | 40 | 41 | 42 | #endif /* CAMERA_H_ */ 43 | -------------------------------------------------------------------------------- /src/material.h: -------------------------------------------------------------------------------- 1 | #ifndef MATERIAL_H_ 2 | #define MATERIAL_H_ 3 | 4 | #include "ray.h" 5 | #include "hitable.h" 6 | #include "utils.h" 7 | 8 | class material { 9 | public: 10 | virtual bool scatter(const ray& r_in, const hit_record& rec, vec3& attenuation, ray& scattered) const = 0; 11 | }; 12 | 13 | class lambertian: public material { 14 | public: 15 | lambertian(const vec3& a): albedo(a) {} 16 | virtual bool scatter(const ray& ray_in, const hit_record& rec, vec3& attenuation, ray& scattered) const { 17 | vec3 target = rec.p + rec.normal + random_in_unit_sphere(); 18 | scattered = ray(rec.p, target-rec.p); 19 | attenuation = albedo; 20 | return true; 21 | } 22 | 23 | vec3 albedo; 24 | }; 25 | 26 | class metal: public material { 27 | public: 28 | metal(const vec3& a, float f): albedo(a) { if (f < 1) fuzz = f; else fuzz = 1; } 29 | virtual bool scatter(const ray& r_in, const hit_record& rec, vec3& attenuation, ray& scattered) const { 30 | vec3 reflected = reflect(unit_vector(r_in.direction()), rec.normal); 31 | scattered = ray(rec.p, reflected + fuzz*random_in_unit_sphere()); 32 | attenuation = albedo; 33 | return (dot(scattered.direction(), rec.normal) > 0); 34 | } 35 | 36 | vec3 albedo; 37 | float fuzz; 38 | }; 39 | 40 | class dielectric: public material { 41 | public: 42 | dielectric(float ri) : ref_idx(ri) {} 43 | virtual bool scatter(const ray& r_in, const hit_record& rec, vec3& attenuation, ray& scattered) const { 44 | vec3 outward_normal; 45 | vec3 reflected = reflect(r_in.direction(), rec.normal); 46 | float ni_over_nt; 47 | attenuation = vec3(1,1,1); 48 | vec3 refracted; 49 | float reflect_probe; 50 | float cosine; 51 | if (dot(r_in.direction(), rec.normal) > 0) { 52 | outward_normal = -rec.normal; 53 | ni_over_nt = ref_idx; 54 | cosine = ref_idx * dot(r_in.direction(), rec.normal) / r_in.direction().length(); 55 | } else { 56 | outward_normal = rec.normal; 57 | ni_over_nt = 1.0 / ref_idx; 58 | cosine = -dot(r_in.direction(), rec.normal) / r_in.direction().length(); 59 | } 60 | if (refract(r_in.direction(), outward_normal, ni_over_nt, refracted)) { 61 | reflect_probe = schlick(cosine, ref_idx); 62 | } else { 63 | reflect_probe = 1.0; 64 | } 65 | if (drand48() < reflect_probe) { 66 | scattered = ray(rec.p, reflected); 67 | } else { 68 | scattered = ray(rec.p, refracted); 69 | } 70 | return true; 71 | } 72 | float ref_idx; 73 | }; 74 | #endif /* MATERIAL_H_ */ 75 | -------------------------------------------------------------------------------- /src/vec3.h: -------------------------------------------------------------------------------- 1 | #ifndef VEC3H 2 | #define VEC3H 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | class vec3 { 10 | 11 | 12 | public: 13 | vec3() {} 14 | vec3(float e0, float e1, float e2) { e[0] = e0; e[1] = e1; e[2] = e2; } 15 | vec3(float3 v) { e[0] = v.x; e[1] = v.y; e[2] = v.z; } 16 | inline float x() const { return e[0]; } 17 | inline float y() const { return e[1]; } 18 | inline float z() const { return e[2]; } 19 | inline float r() const { return e[0]; } 20 | inline float g() const { return e[1]; } 21 | inline float b() const { return e[2]; } 22 | 23 | inline const vec3& operator+() const { return *this; } 24 | inline vec3 operator-() const { return vec3(-e[0], -e[1], -e[2]); } 25 | inline float operator[](int i) const { return e[i]; } 26 | inline float& operator[](int i) { return e[i]; }; 27 | 28 | inline vec3& operator+=(const vec3 &v2); 29 | inline vec3& operator-=(const vec3 &v2); 30 | inline vec3& operator*=(const vec3 &v2); 31 | inline vec3& operator/=(const vec3 &v2); 32 | inline vec3& operator*=(const float t); 33 | inline vec3& operator/=(const float t); 34 | 35 | inline float length() const { return sqrt(e[0]*e[0] + e[1]*e[1] + e[2]*e[2]); } 36 | inline float squared_length() const { return e[0]*e[0] + e[1]*e[1] + e[2]*e[2]; } 37 | inline void make_unit_vector(); 38 | inline float3 to_float3() const { return make_float3(e[0], e[1], e[2]); } 39 | inline char* to_string(char* str) const 40 | { 41 | sprintf(str, "(%.4f, %.4f, %.4f)", e[0], e[1], e[2]); 42 | return str; 43 | } 44 | 45 | float e[3]; 46 | }; 47 | 48 | 49 | 50 | inline std::istream& operator>>(std::istream &is, vec3 &t) { 51 | is >> t.e[0] >> t.e[1] >> t.e[2]; 52 | return is; 53 | } 54 | 55 | inline std::ostream& operator<<(std::ostream &os, const vec3 &t) { 56 | os << t.e[0] << " " << t.e[1] << " " << t.e[2]; 57 | return os; 58 | } 59 | 60 | inline void vec3::make_unit_vector() { 61 | float k = 1.0 / sqrt(e[0]*e[0] + e[1]*e[1] + e[2]*e[2]); 62 | e[0] *= k; e[1] *= k; e[2] *= k; 63 | } 64 | 65 | inline vec3 operator+(const vec3 &v1, const vec3 &v2) { 66 | return vec3(v1.e[0] + v2.e[0], v1.e[1] + v2.e[1], v1.e[2] + v2.e[2]); 67 | } 68 | 69 | inline vec3 operator-(const vec3 &v1, const vec3 &v2) { 70 | return vec3(v1.e[0] - v2.e[0], v1.e[1] - v2.e[1], v1.e[2] - v2.e[2]); 71 | } 72 | 73 | inline vec3 operator*(const vec3 &v1, const vec3 &v2) { 74 | return vec3(v1.e[0] * v2.e[0], v1.e[1] * v2.e[1], v1.e[2] * v2.e[2]); 75 | } 76 | 77 | inline vec3 operator/(const vec3 &v1, const vec3 &v2) { 78 | return vec3(v1.e[0] / v2.e[0], v1.e[1] / v2.e[1], v1.e[2] / v2.e[2]); 79 | } 80 | 81 | inline vec3 operator*(float t, const vec3 &v) { 82 | return vec3(t*v.e[0], t*v.e[1], t*v.e[2]); 83 | } 84 | 85 | inline vec3 operator/(vec3 v, float t) { 86 | return vec3(v.e[0]/t, v.e[1]/t, v.e[2]/t); 87 | } 88 | 89 | inline vec3 operator*(const vec3 &v, float t) { 90 | return vec3(t*v.e[0], t*v.e[1], t*v.e[2]); 91 | } 92 | 93 | inline float dot(const vec3 &v1, const vec3 &v2) { 94 | return v1.e[0] *v2.e[0] + v1.e[1] *v2.e[1] + v1.e[2] *v2.e[2]; 95 | } 96 | 97 | inline vec3 cross(const vec3 &v1, const vec3 &v2) { 98 | return vec3( (v1.e[1]*v2.e[2] - v1.e[2]*v2.e[1]), 99 | (-(v1.e[0]*v2.e[2] - v1.e[2]*v2.e[0])), 100 | (v1.e[0]*v2.e[1] - v1.e[1]*v2.e[0])); 101 | } 102 | 103 | 104 | inline vec3& vec3::operator+=(const vec3 &v){ 105 | e[0] += v.e[0]; 106 | e[1] += v.e[1]; 107 | e[2] += v.e[2]; 108 | return *this; 109 | } 110 | 111 | inline vec3& vec3::operator*=(const vec3 &v){ 112 | e[0] *= v.e[0]; 113 | e[1] *= v.e[1]; 114 | e[2] *= v.e[2]; 115 | return *this; 116 | } 117 | 118 | inline vec3& vec3::operator/=(const vec3 &v){ 119 | e[0] /= v.e[0]; 120 | e[1] /= v.e[1]; 121 | e[2] /= v.e[2]; 122 | return *this; 123 | } 124 | 125 | inline vec3& vec3::operator-=(const vec3& v) { 126 | e[0] -= v.e[0]; 127 | e[1] -= v.e[1]; 128 | e[2] -= v.e[2]; 129 | return *this; 130 | } 131 | 132 | inline vec3& vec3::operator*=(const float t) { 133 | e[0] *= t; 134 | e[1] *= t; 135 | e[2] *= t; 136 | return *this; 137 | } 138 | 139 | inline vec3& vec3::operator/=(const float t) { 140 | float k = 1.0/t; 141 | 142 | e[0] *= k; 143 | e[1] *= k; 144 | e[2] *= k; 145 | return *this; 146 | } 147 | 148 | inline vec3 unit_vector(vec3 v) { 149 | return v / v.length(); 150 | } 151 | 152 | #endif 153 | -------------------------------------------------------------------------------- /src/Raytracer.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | #include "hitable_list.h" 7 | #include "sphere.h" 8 | #include "ray.h" 9 | #include "camera.h" 10 | #include "material.h" 11 | #include "utils.h" 12 | 13 | using namespace std; 14 | 15 | hitable *random_scene() { 16 | int n = 500; 17 | hitable **list = new hitable*[n+1]; 18 | list[0] = new sphere(vec3(0,-1000,0), 1000, new lambertian(vec3(0.5, 0.5, 0.5))); 19 | int i = 1; 20 | for (int a = -11; a < 11; a++) { 21 | for (int b = -11; b < 11; b++) { 22 | float choose_mat = drand48(); 23 | vec3 center(a+0.9*drand48(),0.2,b+0.9*drand48()); 24 | if ((center-vec3(4,0.2,0)).length() > 0.9) { 25 | if (choose_mat < 0.8) { // diffuse 26 | list[i++] = new sphere(center, 0.2, new lambertian(vec3(drand48()*drand48(), drand48()*drand48(), drand48()*drand48()))); 27 | } 28 | else if (choose_mat < 0.95) { // metal 29 | list[i++] = new sphere(center, 0.2, 30 | new metal(vec3(0.5*(1 + drand48()), 0.5*(1 + drand48()), 0.5*(1 + drand48())), 0.5*drand48())); 31 | } 32 | else { // glass 33 | list[i++] = new sphere(center, 0.2, new dielectric(1.5)); 34 | } 35 | } 36 | } 37 | } 38 | 39 | list[i++] = new sphere(vec3(0, 1, 0), 1.0, new dielectric(1.5)); 40 | list[i++] = new sphere(vec3(-4, 1, 0), 1.0, new lambertian(vec3(0.4, 0.2, 0.1))); 41 | list[i++] = new sphere(vec3(4, 1, 0), 1.0, new metal(vec3(0.7, 0.6, 0.5), 0.0)); 42 | 43 | return new hitable_list(list,i); 44 | } 45 | 46 | bool color(const ray& r, hitable *world, vec3& sample_clr, ray& scattered) { 47 | hit_record rec; 48 | if (!world->hit(r, 0.001, MAXFLOAT, rec)) { 49 | // no intersection with spheres, return sky color 50 | vec3 unit_direction = unit_vector(r.direction()); 51 | float t = 0.5*(unit_direction.y() + 1.0); 52 | sample_clr *= (1 - t)*vec3(1.0, 1.0, 1.0) + t*vec3(0.5, 0.7, 1.0); 53 | return false; 54 | } 55 | 56 | vec3 attenuation; 57 | if (rec.mat_ptr->scatter(r, rec, attenuation, scattered)) { 58 | sample_clr *= attenuation; 59 | return true; 60 | } 61 | 62 | sample_clr = vec3(0, 0, 0); 63 | return false; 64 | } 65 | 66 | int main() { 67 | int nx = 600; 68 | int ny = 300; 69 | int ns = 100; 70 | int max_depth = 50; 71 | hitable *world = random_scene(); 72 | 73 | // init camera 74 | vec3 lookfrom(13,2,3); 75 | vec3 lookat(0,0,0); 76 | float dist_to_focus = 10.0; 77 | float aperture = 0.1; 78 | camera cam(lookfrom, lookat, vec3(0,1,0), 20, float(nx)/float(ny), aperture, dist_to_focus); 79 | 80 | const unsigned int all_rays = nx*ny; 81 | ray *rays[all_rays]; 82 | vec3 *colors[all_rays]; 83 | unsigned int ray_sample_ids[all_rays]; 84 | vec3 *sample_colors[all_rays]; 85 | 86 | // init cumulated colors 87 | for (int i = 0; i < all_rays; i++) 88 | colors[i] = new vec3(0, 0, 0); 89 | 90 | // init sample colors 91 | for (int i = 0; i < all_rays; i++) 92 | sample_colors[i] = new vec3(1, 1, 1); 93 | 94 | clock_t begin = clock(); 95 | for (unsigned int s = 0; s < ns; ++s) 96 | { 97 | cout << "sample " << s << "/" << ns << "\r"; 98 | cout.flush(); 99 | 100 | // reset samples 101 | for (int i = 0; i < all_rays; i++) 102 | *(sample_colors[i]) = vec3(1, 1, 1); 103 | 104 | // generate all camera rays, but just for one sample per pixel 105 | unsigned int ray_idx = 0; 106 | for (int j = ny-1; j >= 0; j--) 107 | { 108 | for (int i = 0; i < nx; ++i, ++ray_idx) 109 | { 110 | float u = float(i + drand48()) / float(nx); 111 | float v = float(j + drand48()) / float(ny); 112 | rays[ray_idx] = new ray(); 113 | cam.get_ray(u, v, *(rays[ray_idx])); 114 | ray_sample_ids[ray_idx] = ray_idx; 115 | } 116 | } 117 | 118 | // compute ray-world intersections 119 | unsigned int depth = 0; 120 | unsigned int num_rays = all_rays; 121 | while (depth < max_depth && num_rays > 0) 122 | { 123 | ray_idx = 0; 124 | for (unsigned int i = 0; i < num_rays; ++i) 125 | { 126 | if (color(*rays[i], world, *sample_colors[ray_sample_ids[i]], *rays[ray_idx])) 127 | { 128 | ray_sample_ids[ray_idx] = ray_sample_ids[i]; 129 | ++ray_idx; 130 | } 131 | } 132 | num_rays = ray_idx; 133 | ++depth; 134 | } 135 | 136 | // cumulate sample colors 137 | for (unsigned int i = 0; i < all_rays; ++i) 138 | *(colors[i]) += *(sample_colors[i]); 139 | } 140 | 141 | clock_t end = clock(); 142 | printf("rendering duration %.2f seconds", double(end - begin) / CLOCKS_PER_SEC); 143 | 144 | // generate final image 145 | ofstream image; 146 | image.open("picture.ppm"); 147 | image << "P3\n" << nx << " " << ny << "\n255\n"; 148 | unsigned int sample_idx = 0; 149 | for (int j = ny-1; j >= 0; j--) 150 | { 151 | for (int i = 0; i < nx; ++i, sample_idx++) 152 | { 153 | vec3 col = *(colors[sample_idx]) / float(ns); 154 | col = vec3( sqrtf(col[0]), sqrtf(col[1]), sqrtf(col[2]) ); 155 | int ir = int(255.99*col.r()); 156 | int ig = int(255.99*col.g()); 157 | int ib = int(255.99*col.b()); 158 | 159 | image << ir << " " << ig << " " << ib << "\n"; 160 | } 161 | } 162 | return 0; 163 | } 164 | -------------------------------------------------------------------------------- /src/RaytracerGPU.cu: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 1993-2015 NVIDIA Corporation. All rights reserved. 3 | * 4 | * Please refer to the NVIDIA end user license agreement (EULA) associated 5 | * with this source code for terms and conditions that govern your use of 6 | * this software. Any use, reproduction, disclosure, or distribution of 7 | * this software and related documentation outside the terms of the EULA 8 | * is strictly prohibited. 9 | * 10 | */ 11 | 12 | /** 13 | * Vector addition: C = A + B. 14 | * 15 | * This sample is a very basic sample that implements element by element 16 | * vector addition. It is the same as the sample illustrating Chapter 2 17 | * of the programming guide with some additions like error checking. 18 | */ 19 | 20 | #include 21 | #include 22 | #include 23 | #include 24 | 25 | // For the CUDA runtime routines (prefixed with "cuda_") 26 | #include 27 | 28 | #include 29 | 30 | #include "sphere.h" 31 | #include "camera.h" 32 | #include "hitable_list.h" 33 | 34 | //#define DBG_ID (150*600+300) 35 | char buffer[100]; 36 | 37 | using namespace std; 38 | 39 | struct cu_sphere { 40 | float3 center; 41 | float radius; 42 | }; 43 | 44 | struct cu_ray { 45 | float3 origin; 46 | float3 direction; 47 | }; 48 | 49 | struct cu_hit { 50 | int hit_idx; 51 | float hit_t; 52 | }; 53 | 54 | hitable_list *random_scene() 55 | { 56 | int n = 500; 57 | hitable **list = new hitable*[n+1]; 58 | list[0] = new sphere(vec3(0,-1000,0), 1000, new lambertian(vec3(0.5, 0.5, 0.5))); 59 | int i = 1; 60 | for (int a = -11; a < 11; a++) { 61 | for (int b = -11; b < 11; b++) { 62 | float choose_mat = drand48(); 63 | vec3 center(a+0.9*drand48(),0.2,b+0.9*drand48()); 64 | if ((center-vec3(4,0.2,0)).length() > 0.9) { 65 | if (choose_mat < 0.8) { // diffuse 66 | list[i++] = new sphere(center, 0.2, new lambertian(vec3(drand48()*drand48(), drand48()*drand48(), drand48()*drand48()))); 67 | } 68 | else if (choose_mat < 0.95) { // metal 69 | list[i++] = new sphere(center, 0.2, 70 | new metal(vec3(0.5*(1 + drand48()), 0.5*(1 + drand48()), 0.5*(1 + drand48())), 0.5*drand48())); 71 | } 72 | else { // glass 73 | list[i++] = new sphere(center, 0.2, new dielectric(1.5)); 74 | } 75 | } 76 | } 77 | } 78 | 79 | list[i++] = new sphere(vec3(0, 1, 0), 1.0, new dielectric(1.5)); 80 | list[i++] = new sphere(vec3(-4, 1, 0), 1.0, new lambertian(vec3(0.4, 0.2, 0.1))); 81 | list[i++] = new sphere(vec3(4, 1, 0), 1.0, new metal(vec3(0.7, 0.6, 0.5), 0.0)); 82 | 83 | return new hitable_list(list,i); 84 | } 85 | 86 | cu_sphere* 87 | init_cu_scene(const hitable_list* world) 88 | { 89 | const unsigned int size = world->list_size; 90 | cu_sphere* scene = (cu_sphere*) malloc(size*sizeof(cu_sphere)); 91 | for (int i = 0; i < size; i++) 92 | { 93 | const sphere *s = (sphere*) world->list[i]; 94 | scene[i].center = make_float3(s->center.x(), s->center.y(), s->center.z()); 95 | scene[i].radius = s->radius; 96 | } 97 | 98 | return scene; 99 | } 100 | 101 | cu_ray* 102 | generate_rays(const camera* cam, cu_ray* rays, const unsigned int nx, const unsigned int ny) 103 | { 104 | unsigned int ray_idx = 0; 105 | ray r; 106 | for (int j = ny-1; j >= 0; j--) 107 | { 108 | for (int i = 0; i < nx; ++i, ++ray_idx) 109 | { 110 | float u = float(i + drand48()) / float(nx); 111 | float v = float(j + drand48()) / float(ny); 112 | cam->get_ray(u, v, r); 113 | rays[ray_idx].origin = make_float3(r.A.x(), r.A.y(), r.A.z()); 114 | rays[ray_idx].direction = make_float3(r.B.x(), r.B.y(), r.B.z()); 115 | } 116 | } 117 | 118 | return rays; 119 | } 120 | 121 | camera* 122 | init_camera(unsigned int nx, unsigned int ny) 123 | { 124 | vec3 lookfrom(13,2,3); 125 | vec3 lookat(0,0,0); 126 | float dist_to_focus = 10.0; 127 | float aperture = 0.1; 128 | 129 | return new camera(lookfrom, lookat, vec3(0,1,0), 20, float(nx)/float(ny), aperture, dist_to_focus); 130 | } 131 | 132 | __global__ void 133 | hit_scene(const cu_ray* rays, const unsigned int num_rays, const cu_sphere* scene, const unsigned int scene_size, float t_min, float t_max, cu_hit* hits) 134 | { 135 | int i = blockDim.x * blockIdx.x + threadIdx.x; 136 | if (i >= num_rays) 137 | return; 138 | 139 | const cu_ray r = rays[i]; 140 | const float3 ro = r.origin; 141 | const float3 rd = r.direction; 142 | 143 | float closest_hit = t_max; 144 | int hit_idx = -1; 145 | 146 | // if (i == DBG_ID) printf("hit_scene: ro = (%.2f, %.2f, %.2f) rd = (%.2f, %.2f, %.2f) \n", ro.x, ro.y, ro.z, rd.x, rd.y, rd.z); 147 | 148 | for (int s = 0; s < scene_size; s++) 149 | { 150 | const cu_sphere sphere = scene[s]; 151 | const float3 sc = sphere.center; 152 | const float sr = sphere.radius; 153 | 154 | float3 oc = make_float3(ro.x-sc.x, ro.y-sc.y, ro.z-sc.z); 155 | float a = rd.x*rd.x + rd.y*rd.y + rd.z*rd.z; 156 | float b = 2.0f * (oc.x*rd.x + oc.y*rd.y + oc.z*rd.z); 157 | float c = oc.x*oc.x + oc.y*oc.y + oc.z*oc.z - sr*sr; 158 | float discriminant = b*b - 4*a*c; 159 | if (discriminant > 0) 160 | { 161 | float t = (-b - sqrtf(discriminant)) / (2.0f*a); 162 | if (t < closest_hit && t > t_min) { 163 | closest_hit = t; 164 | hit_idx = s; 165 | } 166 | } 167 | } 168 | 169 | hits[i].hit_t = closest_hit; 170 | hits[i].hit_idx = hit_idx; 171 | 172 | // if (i == DBG_ID) printf("hit_scene: hit_idx = %d, hit_t = %.2f\n", hit_idx, closest_hit); 173 | } 174 | 175 | bool color(const unsigned int sample_id, const cu_ray& cu_r, const cu_hit& hit, const hitable_list *world, vec3& sample_clr, cu_ray& scattered) { 176 | ray r = ray(vec3(cu_r.origin), vec3(cu_r.direction)); 177 | 178 | if (hit.hit_idx == -1) { 179 | // no intersection with spheres, return sky color 180 | vec3 unit_direction = unit_vector(r.direction()); 181 | float t = 0.5*(unit_direction.y() + 1.0); 182 | sample_clr *= (1 - t)*vec3(1.0, 1.0, 1.0) + t*vec3(0.5, 0.7, 1.0); 183 | // if (sample_id == DBG_ID) printf("no_hit: %s\n", sample_clr.to_string(buffer)); 184 | return false; 185 | } 186 | 187 | hit_record rec; 188 | sphere *s = (sphere*) (world->list[hit.hit_idx]); 189 | rec.t = hit.hit_t; 190 | rec.p = r.point_at_parameter(hit.hit_t); 191 | rec.normal = (rec.p - s->center) / s->radius; 192 | rec.mat_ptr = s->mat_ptr; 193 | 194 | vec3 attenuation; 195 | if (rec.mat_ptr->scatter(r, rec, attenuation, r)) { 196 | scattered.origin = r.origin().to_float3(); 197 | scattered.direction = r.direction().to_float3(); 198 | 199 | sample_clr *= attenuation; 200 | // if (sample_id == DBG_ID) printf("scatter: %s\n", sample_clr.to_string(buffer)); 201 | return true; 202 | } 203 | 204 | sample_clr = vec3(0, 0, 0); 205 | // if (sample_id == DBG_ID) printf("no_scatter: %s\n", sample_clr.to_string(buffer)); 206 | return false; 207 | } 208 | 209 | /** 210 | * Host main routine 211 | */ 212 | int 213 | main(void) 214 | { 215 | const unsigned int scene_size = 500; 216 | 217 | printf("preparing renderer...\n"); 218 | 219 | // Error code to check return values for CUDA calls 220 | cudaError_t err = cudaSuccess; 221 | int nx = 600; 222 | int ny = 300; 223 | int ns = 1000; 224 | int max_depth = 50; 225 | const hitable_list *world = random_scene(); 226 | cu_sphere *h_scene = init_cu_scene(world); 227 | const camera *cam = init_camera(nx, ny); 228 | 229 | const unsigned int all_rays = nx*ny; 230 | cu_ray h_rays[all_rays]; 231 | vec3 *h_colors[all_rays]; 232 | unsigned int h_ray_sample_ids[all_rays]; 233 | vec3 *h_sample_colors[all_rays]; 234 | 235 | // init cumulated colors 236 | for (int i = 0; i < all_rays; i++) 237 | h_colors[i] = new vec3(0, 0, 0); 238 | 239 | // init sample colors 240 | for (int i = 0; i < all_rays; i++) 241 | h_sample_colors[i] = new vec3(1, 1, 1); 242 | 243 | cu_hit *h_hits = (cu_hit*) malloc(all_rays*sizeof(cu_hit)); 244 | 245 | // allocate device memory for input 246 | cu_sphere *d_scene = NULL; 247 | err = cudaMalloc((void **)&d_scene, scene_size*sizeof(cu_sphere)); 248 | if (err != cudaSuccess) 249 | { 250 | fprintf(stderr, "Failed to allocate device d_scene (error code %s)!\n", cudaGetErrorString(err)); 251 | exit(EXIT_FAILURE); 252 | } 253 | 254 | cu_ray *d_rays = NULL; 255 | err = cudaMalloc((void **)&d_rays, all_rays*sizeof(cu_ray)); 256 | if (err != cudaSuccess) 257 | { 258 | fprintf(stderr, "Failed to allocate device d_rays (error code %s)!\n", cudaGetErrorString(err)); 259 | exit(EXIT_FAILURE); 260 | } 261 | 262 | cu_hit *d_hits = NULL; 263 | err = cudaMalloc((void **)&d_hits, all_rays*sizeof(cu_hit)); 264 | if (err != cudaSuccess) 265 | { 266 | fprintf(stderr, "Failed to allocate device d_hits (error code %s)!\n", cudaGetErrorString(err)); 267 | exit(EXIT_FAILURE); 268 | } 269 | 270 | // Copy the host input in host memory to the device input in device memory 271 | printf("Copy input data from the host memory to the CUDA device\n"); 272 | err = cudaMemcpy(d_scene, h_scene, world->list_size*sizeof(cu_sphere), cudaMemcpyHostToDevice); 273 | if (err != cudaSuccess) 274 | { 275 | fprintf(stderr, "Failed to copy scene from host to device (error code %s)!\n", cudaGetErrorString(err)); 276 | exit(EXIT_FAILURE); 277 | } 278 | 279 | clock_t begin = clock(); 280 | for (unsigned int s = 0; s < ns; ++s) 281 | { 282 | cout << "sample " << s << "/" << ns << "\r"; 283 | cout.flush(); 284 | 285 | // reset temporary variables 286 | for (int i = 0; i < all_rays; i++) 287 | { 288 | *(h_sample_colors[i]) = vec3(1, 1, 1); 289 | h_ray_sample_ids[i] = i; 290 | } 291 | 292 | generate_rays(cam, h_rays, nx, ny); 293 | 294 | // compute ray-world intersections 295 | unsigned int depth = 0; 296 | unsigned int num_rays = all_rays; 297 | while (depth < max_depth && num_rays > 0) 298 | { 299 | err = cudaMemcpy(d_rays, h_rays, num_rays*sizeof(cu_ray), cudaMemcpyHostToDevice); 300 | if (err != cudaSuccess) 301 | { 302 | fprintf(stderr, "Failed to copy rays from host to device (error code %s)!\n", cudaGetErrorString(err)); 303 | exit(EXIT_FAILURE); 304 | } 305 | 306 | // Launch the CUDA Kernel 307 | int threadsPerBlock = 256; 308 | int blocksPerGrid =(num_rays + threadsPerBlock - 1) / threadsPerBlock; 309 | hit_scene<<>>(d_rays, num_rays, d_scene, scene_size, 0.001, FLT_MAX, d_hits); 310 | err = cudaGetLastError(); 311 | if (err != cudaSuccess) 312 | { 313 | fprintf(stderr, "Failed to launch vectorAdd kernel (error code %s)!\n", cudaGetErrorString(err)); 314 | exit(EXIT_FAILURE); 315 | } 316 | 317 | // Copy the device result in device memory to the host result vector 318 | err = cudaMemcpy(h_hits, d_hits, num_rays*sizeof(cu_hit), cudaMemcpyDeviceToHost); 319 | if (err != cudaSuccess) 320 | { 321 | fprintf(stderr, "Failed to copy vector C from device to host (error code %s)!\n", cudaGetErrorString(err)); 322 | exit(EXIT_FAILURE); 323 | } 324 | 325 | // compact active rays 326 | unsigned int ray_idx = 0; 327 | for (unsigned int i = 0; i < num_rays; ++i) 328 | { 329 | if (color(h_ray_sample_ids[i], h_rays[i], h_hits[i], world, *h_sample_colors[h_ray_sample_ids[i]], h_rays[ray_idx])) 330 | { 331 | h_ray_sample_ids[ray_idx] = h_ray_sample_ids[i]; 332 | ++ray_idx; 333 | } 334 | } 335 | num_rays = ray_idx; 336 | ++depth; 337 | } 338 | 339 | // cumulate sample colors 340 | for (unsigned int i = 0; i < all_rays; ++i) 341 | *(h_colors[i]) += *(h_sample_colors[i]); 342 | } 343 | 344 | clock_t end = clock(); 345 | printf("rendering duration %.2f seconds", double(end - begin) / CLOCKS_PER_SEC); 346 | 347 | // Free device global memory 348 | err = cudaFree(d_scene); 349 | if (err != cudaSuccess) 350 | { 351 | fprintf(stderr, "Failed to free device d_scene (error code %s)!\n", cudaGetErrorString(err)); 352 | exit(EXIT_FAILURE); 353 | } 354 | 355 | err = cudaFree(d_rays); 356 | if (err != cudaSuccess) 357 | { 358 | fprintf(stderr, "Failed to free device d_rays (error code %s)!\n", cudaGetErrorString(err)); 359 | exit(EXIT_FAILURE); 360 | } 361 | 362 | err = cudaFree(d_hits); 363 | if (err != cudaSuccess) 364 | { 365 | fprintf(stderr, "Failed to free device d_hits (error code %s)!\n", cudaGetErrorString(err)); 366 | exit(EXIT_FAILURE); 367 | } 368 | 369 | // Free host memory 370 | free(h_scene); 371 | free(h_hits); 372 | 373 | // generate final image 374 | ofstream image; 375 | image.open("picture.ppm"); 376 | image << "P3\n" << nx << " " << ny << "\n255\n"; 377 | unsigned int sample_idx = 0; 378 | for (int j = ny-1; j >= 0; j--) 379 | { 380 | for (int i = 0; i < nx; ++i, sample_idx++) 381 | { 382 | vec3 col = *(h_colors[sample_idx]) / float(ns); 383 | col = vec3( sqrtf(col[0]), sqrtf(col[1]), sqrtf(col[2]) ); 384 | int ir = int(255.99*col.r()); 385 | int ig = int(255.99*col.g()); 386 | int ib = int(255.99*col.b()); 387 | 388 | image << ir << " " << ig << " " << ib << "\n"; 389 | } 390 | } 391 | 392 | return 0; 393 | } 394 | 395 | --------------------------------------------------------------------------------