decyg12

Simple Ray Tracer

Feb 24th, 2021
1,032
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  1. // include std libraries
  2. #include <chrono>   // for time measurement
  3. #include <cstdio>   // for printf
  4. #include <cstdint>
  5. #undef __STRICT_ANSI__
  6. #include <cmath>
  7. #include <omp.h>
  8.  
  9. // include glm
  10. #include <glm/glm.hpp>
  11. #include <glm/gtc/matrix_transform.hpp>
  12.  
  13. // incldue stbmaster
  14. #define STB_IMAGE_WRITE_IMPLEMENTATION
  15. #include <stb_image_write.h>
  16.  
  17. // include glc
  18. #include <GL/glc.h>
  19.  
  20. // include light
  21. #include "light.hpp"
  22.  
  23. #define SCR_WIDTH   (960 * 4)
  24. #define SCR_HEIGHT  (540 * 4)
  25. #define SCR_ASPECT  ((float)SCR_WIDTH / (float)SCR_HEIGHT)
  26. #define SCR_PIXELS  (SCR_WIDTH * SCR_HEIGHT)
  27. #define NUM_SPHERES 5
  28.  
  29. #define atXY(x, y, stride) \
  30.         (y * stride + x)
  31.  
  32. /* TYPE DEFINITION */
  33.  
  34. struct Color3ui8
  35. {
  36.     uint8_t r, g, b;
  37.  
  38.     Color3ui8& operator= (const Color3ui8& c)
  39.     {
  40.         this->r = c.r;
  41.         this->g = c.g;
  42.         this->b = c.b;
  43.         return *this;
  44.     }
  45.  
  46.     Color3ui8& operator= (const glm::vec3& c)
  47.     {
  48.         this->r = (c.r >= 0.0f && c.r <= 1.0f) ? 255 * c.r : ((c.r > 1.0f) ? 255 : 0);
  49.         this->g = (c.g >= 0.0f && c.g <= 1.0f) ? 255 * c.g : ((c.g > 1.0f) ? 255 : 0);
  50.         this->b = (c.b >= 0.0f && c.r <= 1.0f) ? 255 * c.b : ((c.b > 1.0f) ? 255 : 0);
  51.         return *this;
  52.     }
  53. };
  54.  
  55. struct Ray
  56. {
  57.     glm::vec3 direction;
  58.     glm::vec3 origin;
  59. };
  60.  
  61. struct Sphere
  62. {
  63.     glm::vec3 color;
  64.     glm::vec3 center;
  65.     float radius;
  66. };
  67.  
  68. /* FUNCTION DECLARATION */
  69.  
  70. // Clears the color of the image
  71. // r, g, b represents the clear color
  72. void clear_color(Color3ui8* buff, float r, float g, float b);
  73.  
  74. // Function finds the intersection of the closest sphere
  75. /// @return distance from the origin to the closest hitpoint
  76. float intersection_sphere(const Sphere* spheres, size_t num_spheres, const Ray& ray, float ray_max_length, int* hit_sphere);
  77.  
  78. // Begins the ray-tracing process
  79. glm::vec3 trace_ray(const Ray& ray, int recursions);
  80.  
  81. // The shader computes the color of a pixel at position x, y
  82. glm::vec3 shader(float x, float y);
  83.  
  84. /* GLOBAL VARIABLE DEFINITION */
  85.  
  86. Sphere spheres[NUM_SPHERES] =
  87. {
  88.     {
  89.         {0.0f, 1.0f, 1.0f},
  90.         {0.0f, 0.0f, 4.0f},
  91.         1.0f
  92.     },
  93.     {
  94.         {1.0f, 0.0f, 0.0f},
  95.         {2.0f, 1.0f, 7.0f},
  96.         0.5f
  97.     },
  98.     {
  99.         {0.0f, 0.0f, 1.0f},
  100.         {-1.0f, 2.0f, 5.0f},
  101.         0.7f,
  102.     },
  103.     {
  104.         {0.0f, 1.0f, 0.0f},
  105.         {0.5f, -0.5f, 2.0f},
  106.         0.3f
  107.     },
  108.     {
  109.         {1.0f, 1.0f, 0.0f},
  110.         {-7.0f, -3.5f, 10.0f},
  111.         2.5f
  112.     }
  113. };
  114.  
  115. Light _light =
  116. {
  117.     {-1.0f, 1.0f, -1.0f},
  118.     {3.0f, 3.0f, 3.0f}
  119. };
  120.  
  121. /* FUNCTION IMPLEMANTATION */
  122.  
  123. void clear_color(Color3ui8* buff, float r, float g, float b)
  124. {
  125.     for(size_t i=0; i < SCR_PIXELS; i++)
  126.         buff[i] = glm::vec3(r, g, b);
  127. }
  128.  
  129. float intersection_sphere(const Sphere* spheres, size_t num_spheres, const Ray& ray, float ray_max_length, int* hit_sphere)
  130. {
  131.     float t = ray_max_length;   // distance from the origin to the closest hitpoint
  132.     for(size_t i=0; i<num_spheres; i++)
  133.     {
  134.         // calculate intersection formula of form: ax^2 + bx + c = 0
  135.         glm::vec3 oc = ray.origin - spheres[i].center;
  136.         const float a = 1;
  137.         const float b = 2 * glm::dot(ray.direction, oc);
  138.         const float c = glm::dot(oc, oc) - spheres[i].radius * spheres[i].radius;
  139.         const float delta = b*b - 4*a*c;
  140.  
  141.        
  142.  
  143.         // if ray intersects with the sphere
  144.         if(delta >= 0.0f)
  145.         {
  146.             // calculate both results of the quadratic equation
  147.             float t0, t1;
  148.             if(delta == 0.0f)
  149.                 t0 = t1 = -0.5f * (b / a);
  150.             else
  151.             {
  152.                 t0 = (-b + sqrt(delta)) / (2 * a);
  153.                 t1 = (-b - sqrt(delta)) / (2 * a);
  154.             }
  155.  
  156.             // make t0 always the closest hitpoint
  157.             if(t1 < t0)
  158.                 std::swap(t0, t1);
  159.  
  160.             bool valid = true;
  161.             if(t0 < 0.0f || t1 < 0.0f)      valid = false;  // not valid if sphere is behind or in the origin
  162.             else if(t0 > ray_max_length)    valid = false;  // not valid if sphere is outside the render distance
  163.  
  164.             // update closest hitpoint if result is valid and current hitpoint is closer than the current closest hitpoint
  165.             if(valid && t0 < t)
  166.             {
  167.                 if(hit_sphere != nullptr)
  168.                     *hit_sphere = i;
  169.                 t = t0;
  170.             }
  171.         }
  172.     }
  173.     return t;
  174. }
  175.  
  176. glm::vec3 trace_ray(const Ray& ray, int recursions)
  177. {
  178.     glm::vec3 color = {0.0f, 0.0f, 0.0f};
  179.     Ray new_ray = ray;
  180.  
  181.     if(recursions == 0) // all recursions ran through, so we can end the ray-tracing process
  182.         return color;
  183.  
  184.     constexpr float ray_max_length = 100.0f;
  185.  
  186.     int hit_sphere;
  187.     float t = intersection_sphere(spheres, NUM_SPHERES, ray, ray_max_length, &hit_sphere);  // find intersection
  188.  
  189.     if(t == ray_max_length)             // black color if there is no intersection
  190.         return {0.0f, 0.0f, 0.0f};
  191.     else                                // if there is an intersection
  192.     {
  193.         Material material;
  194.         material.albedo     = spheres[hit_sphere].color;
  195.         material.roughness  = 0.3f;
  196.         material.metallic   = 0.0f;
  197.  
  198.         glm::vec3 I = ray.origin + t * ray.direction;                   // intersection point
  199.         glm::vec3 V = -ray.direction;                                   // view vector
  200.         glm::vec3 N = glm::normalize(I - spheres[hit_sphere].center);   // normal vector
  201.  
  202.         glm::vec3 intensity = light(_light, material, V, N);            // get light intensity
  203.         color = material.albedo * 0.1f + intensity;                     // combute light
  204.  
  205.         new_ray.origin      = I;
  206.         new_ray.direction   = glm::reflect(ray.direction, N);
  207.     }
  208.  
  209.     // trace the next recursion
  210.     color += trace_ray(new_ray, --recursions);
  211.     return color;
  212. }
  213.  
  214. glm::vec3 shader(float x, float y)
  215. {
  216.     // line-image_plane-intersection
  217.     glm::vec3 origin = {0.0f, 0.0f, 0.0f};
  218.     glm::vec3 point_img_plane = {x, y, 1.5f};   // with the used screen ratio (1.77777) -> 1.5 results in ~100° FOV
  219.  
  220.     // generate ray
  221.     Ray ray;
  222.     ray.origin = origin;
  223.     ray.direction = glm::normalize(point_img_plane - origin);
  224.  
  225.     // render the image in hdr
  226.     glm::vec3 hdr_color = trace_ray(ray, 3);    // begin ray-tracing process                              
  227.     glm::vec3 ldr_color = hdr_color / (hdr_color + glm::vec3(1.0f));    // convert to ldr
  228.  
  229.     return ldr_color;
  230. }
  231.  
  232. int main()
  233. {
  234.     Color3ui8* pixelbuff = new Color3ui8[SCR_PIXELS];           // create pixel buffer for image
  235.     clear_color(pixelbuff, 0.0f, 0.0f, 0.0f);                   // set the image to a default color
  236.  
  237.     omp_set_num_threads(omp_get_max_threads());                 // use all aviable processors for rendering
  238.     printf("Using %d processors.\n", omp_get_max_threads());
  239.  
  240.     std::chrono::time_point t0_render = std::chrono::high_resolution_clock::now();                              // time before rendering
  241.     #pragma omp parallel for
  242.     for(size_t y=0; y<SCR_HEIGHT; y++)                          // iterate through the image
  243.     {
  244.         for(size_t x=0; x<SCR_WIDTH; x++)
  245.         {
  246.             float ndc_x = gl::convert::from_pixels_pos_x(x, SCR_WIDTH) * SCR_ASPECT;
  247.             float ndc_y = gl::convert::from_pixels_pos_y(y, SCR_HEIGHT);
  248.             pixelbuff[atXY(x, y, SCR_WIDTH)] = shader(ndc_x, ndc_y);
  249.         }
  250.     }
  251.     std::chrono::time_point t1_render = std::chrono::high_resolution_clock::now();                              // time after rendering
  252.     int64_t t_render = std::chrono::duration_cast<std::chrono::milliseconds>(t1_render - t0_render).count();    // get time duration of image rendering operation
  253.     printf("Rendering time: %I64dms\n", t_render);
  254.  
  255.     std::chrono::time_point t0_out = std::chrono::high_resolution_clock::now();                             // time before writing
  256.     stbi_write_png("rt_output.png", SCR_WIDTH, SCR_HEIGHT, 3, pixelbuff, SCR_WIDTH * sizeof(Color3ui8));    // print image
  257.     std::chrono::time_point t1_out = std::chrono::high_resolution_clock::now();                             // time after writing
  258.     int64_t t_out = std::chrono::duration_cast<std::chrono::milliseconds>(t1_out - t0_out).count();         // get time duration of image writing operation
  259.     printf("Writing time: %I64dms\n", t_out);
  260.  
  261.     delete(pixelbuff);  // delete pixel buffer
  262.     return 0;
  263. }
RAW Paste Data

Adblocker detected! Please consider disabling it...

We've detected AdBlock Plus or some other adblocking software preventing Pastebin.com from fully loading.

We don't have any obnoxious sound, or popup ads, we actively block these annoying types of ads!

Please add Pastebin.com to your ad blocker whitelist or disable your adblocking software.

×