Advertisement
Guest User

Untitled

a guest
Oct 31st, 2014
175
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C++ 10.04 KB | None | 0 0
  1. /*
  2. A very basic raytracer example.
  3. Copyright (C) 2012  www.scratchapixel.com
  4.  
  5. This program is free software: you can redistribute it and/or modify
  6. it under the terms of the GNU General Public License as published by
  7. the Free Software Foundation, either version 3 of the License, or
  8. (at your option) any later version.
  9.  
  10. This program is distributed in the hope that it will be useful,
  11. but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  13. GNU General Public License for more details.
  14.  
  15. You should have received a copy of the GNU General Public License
  16. along with this program.  If not, see <http://www.gnu.org/licenses/>.
  17.  
  18. - changes 02/04/13: fixed flag in ofstream causing a bug under Windows,
  19. added default values for M_PI and INFINITY
  20. - changes 24/05/13: small change to way we compute the refraction direction
  21. vector (eta=ior if we are inside and 1/ior if we are outside the sphere)
  22.  
  23. Compile with the following command: c++ -o raytracer -O3 -Wall raytracer.cpp
  24.  
  25. */
  26.  
  27. #include <cstdlib>
  28. #include <cstdio>
  29. #include <cmath>
  30. #include <fstream>
  31. #include <vector>
  32. #include <iostream>
  33. #include <cassert>
  34.  
  35. #ifdef __linux__
  36. // "Compiled for Linux
  37. #else
  38. // Windows doesn't define these values by default, Linux does
  39. #define M_PI 3.141592653589793
  40. #define INFINITY 1e8
  41. #endif
  42.  
  43. template<typename T>
  44. class Vec3
  45. {
  46. public:
  47.     T x, y, z;
  48.     Vec3() : x(T(0)), y(T(0)), z(T(0)) {}
  49.     Vec3(T xx) : x(xx), y(xx), z(xx) {}
  50.     Vec3(T xx, T yy, T zz) : x(xx), y(yy), z(zz) {}
  51.     Vec3& normalize()
  52.     {
  53.         T nor2 = length2();
  54.         if (nor2 > 0) {
  55.             T invNor = 1 / sqrt(nor2);
  56.             x *= invNor, y *= invNor, z *= invNor;
  57.         }
  58.         return *this;
  59.     }
  60.     Vec3<T> operator * (const T &f) const { return Vec3<T>(x * f, y * f, z * f); }
  61.     Vec3<T> operator * (const Vec3<T> &v) const { return Vec3<T>(x * v.x, y * v.y, z * v.z); }
  62.     T dot(const Vec3<T> &v) const { return x * v.x + y * v.y + z * v.z; }
  63.     Vec3<T> operator - (const Vec3<T> &v) const { return Vec3<T>(x - v.x, y - v.y, z - v.z); }
  64.     Vec3<T> operator + (const Vec3<T> &v) const { return Vec3<T>(x + v.x, y + v.y, z + v.z); }
  65.     Vec3<T>& operator += (const Vec3<T> &v) { x += v.x, y += v.y, z += v.z; return *this; }
  66.     Vec3<T>& operator *= (const Vec3<T> &v) { x *= v.x, y *= v.y, z *= v.z; return *this; }
  67.     Vec3<T> operator - () const { return Vec3<T>(-x, -y, -z); }
  68.     T length2() const { return x * x + y * y + z * z; }
  69.     T length() const { return sqrt(length2()); }
  70.     friend std::ostream & operator << (std::ostream &os, const Vec3<T> &v)
  71.     {
  72.         os << "[" << v.x << " " << v.y << " " << v.z << "]";
  73.         return os;
  74.     }
  75. };
  76.  
  77. template<typename T>
  78. class Sphere
  79. {
  80. public:
  81.     Vec3<T> center;                         /// position of the sphere
  82.     T radius, radius2;                      /// sphere radius and radius^2
  83.     Vec3<T> surfaceColor, emissionColor;    /// surface color and emission (light)
  84.     T transparency, reflection;             /// surface transparency and reflectivity
  85.     Sphere(const Vec3<T> &c, const T &r, const Vec3<T> &sc,
  86.         const T &refl = 0, const T &transp = 0, const Vec3<T> &ec = 0) :
  87.         center(c), radius(r), radius2(r * r), surfaceColor(sc), emissionColor(ec),
  88.         transparency(transp), reflection(refl)
  89.     {}
  90.     // compute a ray-sphere intersection using the geometric solution
  91.     bool intersect(const Vec3<T> &rayorig, const Vec3<T> &raydir, T *t0 = NULL, T *t1 = NULL) const
  92.     {
  93.         Vec3<T> l = center - rayorig;
  94.         T tca = l.dot(raydir);
  95.         if (tca < 0) return false;
  96.         T d2 = l.dot(l) - tca * tca;
  97.         if (d2 > radius2) return false;
  98.         T thc = sqrt(radius2 - d2);
  99.         if (t0 != NULL && t1 != NULL) {
  100.             *t0 = tca - thc;
  101.             *t1 = tca + thc;
  102.         }
  103.  
  104.         return true;
  105.     }
  106. };
  107.  
  108. #define MAX_RAY_DEPTH 5
  109.  
  110. template<typename T>
  111. T mix(const T &a, const T &b, const T &mix)
  112. {
  113.     return b * mix + a * (T(1) - mix);
  114. }
  115.  
  116. // This is the main trace function. It takes a ray as argument (defined by its origin
  117. // and direction). We test if this ray intersects any of the geometry in the scene.
  118. // If the ray intersects an object, we compute the intersection point, the normal
  119. // at the intersection point, and shade this point using this information.
  120. // Shading depends on the surface property (is it transparent, reflective, diffuse).
  121. // The function returns a color for the ray. If the ray intersects an object that
  122. // is the color of the object at the intersection point, otherwise it returns
  123. // the background color.
  124.  
  125. float min(float i, float j) { return (i < j ? i : j); }
  126. float max(float i, float j) { return (i > j ? i : j); }
  127.  
  128. template<typename T>
  129. Vec3<T> trace(const Vec3<T> &rayorig, const Vec3<T> &raydir,
  130.     const std::vector<Sphere<T> *> &spheres, const int &depth)
  131. {
  132.     //if (raydir.length() != 1) std::cerr << "Error " << raydir << std::endl;
  133.     T tnear = INFINITY;
  134.     const Sphere<T> *sphere = NULL;
  135.     // find intersection of this ray with the sphere in the scene
  136.     for (unsigned i = 0; i < spheres.size(); ++i) {
  137.         T t0 = INFINITY, t1 = INFINITY;
  138.         if (spheres[i]->intersect(rayorig, raydir, &t0, &t1)) {
  139.             if (t0 < 0) t0 = t1;
  140.             if (t0 < tnear) {
  141.                 tnear = t0;
  142.                 sphere = spheres[i];
  143.             }
  144.         }
  145.     }
  146.     // if there's no intersection return black or background color
  147.     if (!sphere) return Vec3<T>(2);
  148.     Vec3<T> surfaceColor = 0; // color of the ray/surfaceof the object intersected by the ray
  149.     Vec3<T> phit = rayorig + raydir * tnear; // point of intersection
  150.     Vec3<T> nhit = phit - sphere->center; // normal at the intersection point
  151.     nhit.normalize(); // normalize normal direction
  152.     // If the normal and the view direction are not opposite to each other
  153.     // reverse the normal direction. That also means we are inside the sphere so set
  154.     // the inside bool to true. Finally reverse the sign of IdotN which we want
  155.     // positive.
  156.     T bias = 1e-4; // add some bias to the point from which we will be tracing
  157.     bool inside = false;
  158.     if (raydir.dot(nhit) > 0) nhit = -nhit, inside = true;
  159.     if ((sphere->transparency > 0 || sphere->reflection > 0) && depth < MAX_RAY_DEPTH) {
  160.         T facingratio = -raydir.dot(nhit);
  161.         // change the mix value to tweak the effect
  162.         T fresneleffect = mix<T>(pow(1 - facingratio, 3), 1, 0.1);
  163.         // compute reflection direction (not need to normalize because all vectors
  164.         // are already normalized)
  165.         Vec3<T> refldir = raydir - nhit * 2 * raydir.dot(nhit);
  166.         refldir.normalize();
  167.         Vec3<T> reflection = trace(phit + nhit * bias, refldir, spheres, depth + 1);
  168.         Vec3<T> refraction = 0;
  169.         // if the sphere is also transparent compute refraction ray (transmission)
  170.         if (sphere->transparency) {
  171.             T ior = 1.1, eta = (inside) ? ior : 1 / ior; // are we inside or outside the surface?
  172.             T cosi = -nhit.dot(raydir);
  173.             T k = 1 - eta * eta * (1 - cosi * cosi);
  174.             Vec3<T> refrdir = raydir * eta + nhit * (eta *  cosi - sqrt(k));
  175.             refrdir.normalize();
  176.             refraction = trace(phit - nhit * bias, refrdir, spheres, depth + 1);
  177.         }
  178.         // the result is a mix of reflection and refraction (if the sphere is transparent)
  179.         surfaceColor = (reflection * fresneleffect +
  180.             refraction * (1 - fresneleffect) * sphere->transparency) * sphere->surfaceColor;
  181.     }
  182.     else {
  183.         // it's a diffuse object, no need to raytrace any further
  184.         for (unsigned i = 0; i < spheres.size(); ++i) {
  185.             if (spheres[i]->emissionColor.x > 0) {
  186.                 // this is a light
  187.                 Vec3<T> transmission = 1;
  188.                 Vec3<T> lightDirection = spheres[i]->center - phit;
  189.                 lightDirection.normalize();
  190.                 for (unsigned j = 0; j < spheres.size(); ++j) {
  191.                     if (i != j) {
  192.                         T t0, t1;
  193.                         if (spheres[j]->intersect(phit + nhit * bias, lightDirection, &t0, &t1)) {
  194.                             transmission = 0;
  195.                             break;
  196.                         }
  197.                     }
  198.                 }
  199.                 surfaceColor += sphere->surfaceColor * transmission *
  200.                     max(T(0), nhit.dot(lightDirection)) * spheres[i]->emissionColor;
  201.             }
  202.         }
  203.     }
  204.  
  205.     return surfaceColor + sphere->emissionColor;
  206. }
  207.  
  208. // Main rendering function. We compute a camera ray for each pixel of the image
  209. // trace it and return a color. If the ray hits a sphere, we return the color of the
  210. // sphere at the intersection point, else we return the background color.
  211. template<typename T>
  212. void render(const std::vector<Sphere<T> *> &spheres)
  213. {
  214.     unsigned width = 64, height = 64;
  215.     Vec3<T> *image = new Vec3<T>[width * height], *pixel = image;
  216.     T invWidth = 1 / T(width), invHeight = 1 / T(height);
  217.     T fov = 90, aspectratio = width / T(height);
  218.     T angle = tan(M_PI * 0.5 * fov / T(180));
  219.     // Trace rays
  220.     for (unsigned y = 0; y < height; ++y) {
  221.         for (unsigned x = 0; x < width; ++x, ++pixel) {
  222.             T xx = (2 * ((x + 0.5) * invWidth) - 1) * angle * aspectratio;
  223.             T yy = (1 - 2 * ((y + 0.5) * invHeight)) * angle;
  224.             Vec3<T> raydir(xx, yy, -1);
  225.             raydir.normalize();
  226.             *pixel = trace(Vec3<T>(0), raydir, spheres, 0);
  227.         }
  228.     }
  229.     // Save result to a PPM image (keep these flags if you compile under Windows)
  230.     std::ofstream ofs("./untitled.ppm", std::ios::out | std::ios::binary);
  231.     ofs << "P6\n" << width << " " << height << "\n255\n";
  232.     for (unsigned i = 0; i < width * height; ++i) {
  233.         ofs << (unsigned char)(min(T(1), image[i].x) * 255) <<
  234.             (unsigned char)(min(T(1), image[i].y) * 255) <<
  235.             (unsigned char)(min(T(1), image[i].z) * 255);
  236.     }
  237.     ofs.close();
  238.     delete[] image;
  239. }
  240.  
  241. int main(int argc, char **argv)
  242. {
  243.     //srand48(13);
  244.     std::vector<Sphere<float> *> spheres;
  245.     // position, radius, surface color, reflectivity, transparency, emission color
  246.     spheres.push_back(new Sphere<float>(Vec3<float>(0, -10004, -20), 10000, Vec3<float>(0.2), 0, 0.0));
  247.     spheres.push_back(new Sphere<float>(Vec3<float>(0, 0, -20), 4, Vec3<float>(1.00, 0.32, 0.36), 0, 0.0));
  248.     spheres.push_back(new Sphere<float>(Vec3<float>(5, -1, -15), 2, Vec3<float>(0.90, 0.76, 0.46), 0, 0.0));
  249.     spheres.push_back(new Sphere<float>(Vec3<float>(5, 0, -25), 3, Vec3<float>(0.65, 0.77, 0.97), 0, 0.0));
  250.     spheres.push_back(new Sphere<float>(Vec3<float>(-5.5, 0, -15), 3, Vec3<float>(0.90, 0.90, 0.90), 0, 0.0));
  251.     // light
  252.     spheres.push_back(new Sphere<float>(Vec3<float>(0, 20, -30), 3, Vec3<float>(0), 0, 0, Vec3<float>(3)));
  253.     render<float>(spheres);
  254.     while (!spheres.empty()) {
  255.         Sphere<float> *sph = spheres.back();
  256.         spheres.pop_back();
  257.         delete sph;
  258.     }
  259.  
  260.     return 0;
  261. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement