├── data ├── bunny.stl ├── frame.stl ├── mirror.stl ├── skull.stl ├── triangle.json ├── sphere.json ├── sphere-large-change.json ├── sphere-small-change.json ├── sphere-and-plane.json ├── two-spheres-and-plane.json ├── cube.stl ├── inside-a-sphere.json ├── sphere-packing.json ├── mirror.json └── bunny.json ├── .gitmodules ├── images ├── bunny.png ├── ambient.png ├── sphere-packing.png ├── ambient-diffuse.png ├── sphere-and-plane.gif ├── sphere-and-plane.png ├── ambient-diffuse-specular.png ├── ambient-diffuse-specular-shadows.png └── ambient-diffuse-specular-shadows-reflection.png ├── lib ├── debug │ ├── mac │ │ └── libhw2.a │ ├── linux │ │ └── libhw2.a │ └── windows │ │ └── hw2.lib └── release │ ├── mac │ └── libhw2.a │ ├── linux │ └── libhw2.a │ └── windows │ └── hw2.lib ├── src ├── creative.json ├── PointLight.cpp ├── reflect.cpp ├── DirectionalLight.cpp ├── Sphere.cpp ├── viewing_ray.cpp ├── Plane.cpp ├── TriangleSoup.cpp ├── Triangle.cpp ├── first_hit.cpp ├── write_ppm.cpp ├── blinn_phong_shading.cpp └── raycolor.cpp ├── include ├── Material.h ├── Ray.h ├── reflect.h ├── Camera.h ├── PointLight.h ├── DirectionalLight.h ├── Light.h ├── write_ppm.h ├── Plane.h ├── Sphere.h ├── viewing_ray.h ├── Triangle.h ├── TriangleSoup.h ├── Object.h ├── raycolor.h ├── first_hit.h ├── blinn_phong_shading.h ├── dirname.h ├── read_json.h └── readSTL.h ├── .gitignore ├── main.cpp ├── CMakeLists.txt ├── tex ├── 66937766ec704b70da73bb7d35a355ad.svg ├── 8860040216a2e61c344544a77b5cd2ce.svg ├── 71486f265f83bc1e3d2b6f67704bcc23.svg ├── f58ed17486d1735419372f2b7d091779.svg ├── 70118eb82d4643bd42647f21941136af.svg ├── 22f21af20c3e7f253a35c65bed98680e.svg └── 59064857cf8a4117765652ff2be6d992.svg ├── markdown └── header.md ├── README.tex.md ├── README.md └── css └── github-markdown.css /data/bunny.stl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alecjacobson/computer-graphics-ray-tracing/HEAD/data/bunny.stl -------------------------------------------------------------------------------- /data/frame.stl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alecjacobson/computer-graphics-ray-tracing/HEAD/data/frame.stl -------------------------------------------------------------------------------- /data/mirror.stl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alecjacobson/computer-graphics-ray-tracing/HEAD/data/mirror.stl -------------------------------------------------------------------------------- /data/skull.stl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alecjacobson/computer-graphics-ray-tracing/HEAD/data/skull.stl -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "eigen"] 2 | path = eigen 3 | url = https://github.com/eigenteam/eigen-git-mirror 4 | -------------------------------------------------------------------------------- /images/bunny.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alecjacobson/computer-graphics-ray-tracing/HEAD/images/bunny.png -------------------------------------------------------------------------------- /images/ambient.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alecjacobson/computer-graphics-ray-tracing/HEAD/images/ambient.png -------------------------------------------------------------------------------- /lib/debug/mac/libhw2.a: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alecjacobson/computer-graphics-ray-tracing/HEAD/lib/debug/mac/libhw2.a -------------------------------------------------------------------------------- /images/sphere-packing.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alecjacobson/computer-graphics-ray-tracing/HEAD/images/sphere-packing.png -------------------------------------------------------------------------------- /lib/debug/linux/libhw2.a: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alecjacobson/computer-graphics-ray-tracing/HEAD/lib/debug/linux/libhw2.a -------------------------------------------------------------------------------- /lib/debug/windows/hw2.lib: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alecjacobson/computer-graphics-ray-tracing/HEAD/lib/debug/windows/hw2.lib -------------------------------------------------------------------------------- /lib/release/mac/libhw2.a: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alecjacobson/computer-graphics-ray-tracing/HEAD/lib/release/mac/libhw2.a -------------------------------------------------------------------------------- /images/ambient-diffuse.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alecjacobson/computer-graphics-ray-tracing/HEAD/images/ambient-diffuse.png -------------------------------------------------------------------------------- /images/sphere-and-plane.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alecjacobson/computer-graphics-ray-tracing/HEAD/images/sphere-and-plane.gif -------------------------------------------------------------------------------- /images/sphere-and-plane.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alecjacobson/computer-graphics-ray-tracing/HEAD/images/sphere-and-plane.png -------------------------------------------------------------------------------- /lib/release/linux/libhw2.a: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alecjacobson/computer-graphics-ray-tracing/HEAD/lib/release/linux/libhw2.a -------------------------------------------------------------------------------- /lib/release/windows/hw2.lib: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alecjacobson/computer-graphics-ray-tracing/HEAD/lib/release/windows/hw2.lib -------------------------------------------------------------------------------- /images/ambient-diffuse-specular.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alecjacobson/computer-graphics-ray-tracing/HEAD/images/ambient-diffuse-specular.png -------------------------------------------------------------------------------- /images/ambient-diffuse-specular-shadows.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alecjacobson/computer-graphics-ray-tracing/HEAD/images/ambient-diffuse-specular-shadows.png -------------------------------------------------------------------------------- /images/ambient-diffuse-specular-shadows-reflection.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alecjacobson/computer-graphics-ray-tracing/HEAD/images/ambient-diffuse-specular-shadows-reflection.png -------------------------------------------------------------------------------- /src/creative.json: -------------------------------------------------------------------------------- 1 | { 2 | "camera": { 3 | "type": "perspective", 4 | "focal_length": 3, 5 | "eye": [0,0,5], 6 | "up": [0,1,0], 7 | "look": [0,0,-1], 8 | "height": 1, 9 | "width": 1.7777777778 10 | }, 11 | "materials": [ 12 | ], 13 | "lights":[ 14 | ], 15 | "objects": [ 16 | ] 17 | } 18 | -------------------------------------------------------------------------------- /include/Material.h: -------------------------------------------------------------------------------- 1 | #ifndef MATERIAL_H 2 | #define MATERIAL_H 3 | #include 4 | 5 | // Blinn-Phong Approximate Shading Material Parameters 6 | struct Material 7 | { 8 | // Ambient, Diffuse, Specular, Mirror Color 9 | Eigen::Vector3d ka,kd,ks,km; 10 | // Phong exponent 11 | double phong_exponent; 12 | }; 13 | #endif 14 | -------------------------------------------------------------------------------- /src/PointLight.cpp: -------------------------------------------------------------------------------- 1 | #include "PointLight.h" 2 | 3 | void PointLight::direction( 4 | const Eigen::Vector3d & q, Eigen::Vector3d & d, double & max_t) const 5 | { 6 | //////////////////////////////////////////////////////////////////////////// 7 | // Add your code here: 8 | //////////////////////////////////////////////////////////////////////////// 9 | } 10 | -------------------------------------------------------------------------------- /include/Ray.h: -------------------------------------------------------------------------------- 1 | #ifndef RAY_H 2 | #define RAY_H 3 | 4 | #include 5 | 6 | struct Ray 7 | { 8 | Eigen::Vector3d origin; 9 | // Not necessarily unit-length direction vector. (It is often useful to have 10 | // non-unit length so that origin+t*direction lands on a special point when 11 | // t=1.) 12 | Eigen::Vector3d direction; 13 | }; 14 | 15 | #endif 16 | -------------------------------------------------------------------------------- /src/reflect.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | Eigen::Vector3d reflect(const Eigen::Vector3d & in, const Eigen::Vector3d & n) 4 | { 5 | //////////////////////////////////////////////////////////////////////////// 6 | // Replace with your code here: 7 | return Eigen::Vector3d(0,0,0); 8 | //////////////////////////////////////////////////////////////////////////// 9 | } 10 | -------------------------------------------------------------------------------- /include/reflect.h: -------------------------------------------------------------------------------- 1 | #ifndef REFLECT_H 2 | #define REFLECT_H 3 | #include 4 | // Reflect an incoming ray into an out going ray 5 | // 6 | // Inputs: 7 | // in incoming _unit_ ray direction 8 | // n surface _unit_ normal about which to reflect 9 | // Returns outward _unit_ ray direction 10 | Eigen::Vector3d reflect(const Eigen::Vector3d & in, const Eigen::Vector3d & n); 11 | #endif 12 | -------------------------------------------------------------------------------- /src/DirectionalLight.cpp: -------------------------------------------------------------------------------- 1 | #include "DirectionalLight.h" 2 | #include 3 | 4 | void DirectionalLight::direction( 5 | const Eigen::Vector3d & q, Eigen::Vector3d & d, double & max_t) const 6 | { 7 | //////////////////////////////////////////////////////////////////////////// 8 | // Add your code here 9 | //////////////////////////////////////////////////////////////////////////// 10 | } 11 | 12 | -------------------------------------------------------------------------------- /src/Sphere.cpp: -------------------------------------------------------------------------------- 1 | #include "Sphere.h" 2 | #include "Ray.h" 3 | 4 | bool Sphere::intersect( 5 | const Ray & ray, const double min_t, double & t, Eigen::Vector3d & n) const 6 | { 7 | //////////////////////////////////////////////////////////////////////////// 8 | // Replace with your code here: 9 | return false; 10 | //////////////////////////////////////////////////////////////////////////// 11 | } 12 | 13 | -------------------------------------------------------------------------------- /src/viewing_ray.cpp: -------------------------------------------------------------------------------- 1 | #include "viewing_ray.h" 2 | 3 | void viewing_ray( 4 | const Camera & camera, 5 | const int i, 6 | const int j, 7 | const int width, 8 | const int height, 9 | Ray & ray) 10 | { 11 | //////////////////////////////////////////////////////////////////////////// 12 | // Add your code here: 13 | //////////////////////////////////////////////////////////////////////////// 14 | } 15 | -------------------------------------------------------------------------------- /src/Plane.cpp: -------------------------------------------------------------------------------- 1 | #include "Plane.h" 2 | #include "Ray.h" 3 | #include 4 | bool Plane::intersect( 5 | const Ray & ray, const double min_t, double & t, Eigen::Vector3d & n) const 6 | { 7 | //////////////////////////////////////////////////////////////////////////// 8 | // Replace with your code here: 9 | return false; 10 | //////////////////////////////////////////////////////////////////////////// 11 | } 12 | 13 | -------------------------------------------------------------------------------- /include/Camera.h: -------------------------------------------------------------------------------- 1 | #ifndef CAMERA_H 2 | #define CAMERA_H 3 | 4 | #include "Object.h" 5 | #include 6 | 7 | struct Camera 8 | { 9 | // Origin or "eye" 10 | Eigen::Vector3d e; 11 | // orthonormal frame so that -w is the viewing direction. 12 | Eigen::Vector3d u,v,w; 13 | // image plane distance / focal length 14 | double d; 15 | // width and height of image plane 16 | double width, height; 17 | }; 18 | 19 | #endif 20 | -------------------------------------------------------------------------------- /src/TriangleSoup.cpp: -------------------------------------------------------------------------------- 1 | #include "TriangleSoup.h" 2 | #include "Triangle.h" 3 | #include "first_hit.h" 4 | bool TriangleSoup::intersect( 5 | const Ray & ray, const double min_t, double & t, Eigen::Vector3d & n) const 6 | { 7 | //////////////////////////////////////////////////////////////////////////// 8 | // Replace with your code here: 9 | return false; 10 | //////////////////////////////////////////////////////////////////////////// 11 | } 12 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Compiled Object files 2 | *.slo 3 | *.lo 4 | *.o 5 | *.obj 6 | 7 | # Precompiled Headers 8 | *.gch 9 | *.pch 10 | 11 | # Compiled Dynamic libraries 12 | *.so 13 | *.dylib 14 | *.dll 15 | 16 | # Fortran module files 17 | *.mod 18 | *.smod 19 | 20 | # Compiled Static libraries 21 | *.lai 22 | *.la 23 | *.a 24 | *.lib 25 | 26 | # Executables 27 | *.exe 28 | *.out 29 | *.app 30 | 31 | *.un~ 32 | *.swo 33 | *.swp 34 | build/* 35 | *.DS_Store 36 | -------------------------------------------------------------------------------- /src/Triangle.cpp: -------------------------------------------------------------------------------- 1 | #include "Triangle.h" 2 | #include "Ray.h" 3 | #include 4 | #include 5 | 6 | bool Triangle::intersect( 7 | const Ray & ray, const double min_t, double & t, Eigen::Vector3d & n) const 8 | { 9 | //////////////////////////////////////////////////////////////////////////// 10 | // Replace with your code here: 11 | return false; 12 | //////////////////////////////////////////////////////////////////////////// 13 | } 14 | 15 | 16 | -------------------------------------------------------------------------------- /src/first_hit.cpp: -------------------------------------------------------------------------------- 1 | #include "first_hit.h" 2 | 3 | bool first_hit( 4 | const Ray & ray, 5 | const double min_t, 6 | const std::vector< std::shared_ptr > & objects, 7 | int & hit_id, 8 | double & t, 9 | Eigen::Vector3d & n) 10 | { 11 | //////////////////////////////////////////////////////////////////////////// 12 | // Replace with your code here: 13 | return false; 14 | //////////////////////////////////////////////////////////////////////////// 15 | } 16 | -------------------------------------------------------------------------------- /src/write_ppm.cpp: -------------------------------------------------------------------------------- 1 | #include "write_ppm.h" 2 | #include 3 | #include 4 | #include 5 | 6 | bool write_ppm( 7 | const std::string & filename, 8 | const std::vector & data, 9 | const int width, 10 | const int height, 11 | const int num_channels) 12 | { 13 | //////////////////////////////////////////////////////////////////////////// 14 | // Replace with your code here: 15 | return false; 16 | //////////////////////////////////////////////////////////////////////////// 17 | } 18 | -------------------------------------------------------------------------------- /include/PointLight.h: -------------------------------------------------------------------------------- 1 | #ifndef POINTLIGHT_H 2 | #define POINTLIGHT_H 3 | #include "Light.h" 4 | #include 5 | class PointLight : public Light 6 | { 7 | public: 8 | Eigen::Vector3d p; 9 | // Given a query point return the direction _toward_ the Light. 10 | // 11 | // Input: 12 | // q 3D query point in space 13 | // Outputs: 14 | // d 3D direction from point toward light as a vector. 15 | // max_t parametric distance from q along d to light (may be inf) 16 | void direction( 17 | const Eigen::Vector3d & q, Eigen::Vector3d & d, double & max_t) const; 18 | }; 19 | #endif 20 | 21 | -------------------------------------------------------------------------------- /src/blinn_phong_shading.cpp: -------------------------------------------------------------------------------- 1 | #include "blinn_phong_shading.h" 2 | // Hint: 3 | #include "first_hit.h" 4 | #include 5 | 6 | Eigen::Vector3d blinn_phong_shading( 7 | const Ray & ray, 8 | const int & hit_id, 9 | const double & t, 10 | const Eigen::Vector3d & n, 11 | const std::vector< std::shared_ptr > & objects, 12 | const std::vector > & lights) 13 | { 14 | //////////////////////////////////////////////////////////////////////////// 15 | // Replace with your code here: 16 | return Eigen::Vector3d(0,0,0); 17 | //////////////////////////////////////////////////////////////////////////// 18 | } 19 | -------------------------------------------------------------------------------- /src/raycolor.cpp: -------------------------------------------------------------------------------- 1 | #include "raycolor.h" 2 | #include "first_hit.h" 3 | #include "blinn_phong_shading.h" 4 | #include "reflect.h" 5 | 6 | bool raycolor( 7 | const Ray & ray, 8 | const double min_t, 9 | const std::vector< std::shared_ptr > & objects, 10 | const std::vector< std::shared_ptr > & lights, 11 | const int num_recursive_calls, 12 | Eigen::Vector3d & rgb) 13 | { 14 | //////////////////////////////////////////////////////////////////////////// 15 | // Replace with your code here: 16 | rgb = Eigen::Vector3d(0,0,0); 17 | return false; 18 | //////////////////////////////////////////////////////////////////////////// 19 | } 20 | -------------------------------------------------------------------------------- /include/DirectionalLight.h: -------------------------------------------------------------------------------- 1 | #ifndef DIRECTIONALLIGHT_H 2 | #define DIRECTIONALLIGHT_H 3 | #include "Light.h" 4 | #include 5 | class DirectionalLight : public Light 6 | { 7 | public: 8 | // Direction _from_ light toward scene. 9 | Eigen::Vector3d d; 10 | // Given a query point return the direction _toward_ the Light. 11 | // 12 | // Input: 13 | // q 3D query point in space 14 | // Outputs: 15 | // d 3D direction from point toward light as a vector. 16 | // max_t parametric distance from q along d to light (may be inf) 17 | void direction( 18 | const Eigen::Vector3d & q, Eigen::Vector3d & d, double & max_t) const; 19 | }; 20 | #endif 21 | 22 | 23 | -------------------------------------------------------------------------------- /include/Light.h: -------------------------------------------------------------------------------- 1 | #ifndef LIGHT_H 2 | #define LIGHT_H 3 | #include 4 | class Light 5 | { 6 | public: 7 | // Color (intensities) 8 | Eigen::Vector3d I; 9 | // https://stackoverflow.com/questions/461203/when-to-use-virtual-destructors 10 | virtual ~Light() {}; 11 | // Given a query point return the direction _toward_ the Light. 12 | // 13 | // Input: 14 | // q 3D query point in space 15 | // Outputs: 16 | // d 3D direction from point toward light as a vector. 17 | // max_t parametric distance from q along d to light (may be inf) 18 | virtual void direction( 19 | const Eigen::Vector3d & q, 20 | Eigen::Vector3d & d, 21 | double & max_t) const =0; 22 | }; 23 | #endif 24 | -------------------------------------------------------------------------------- /include/write_ppm.h: -------------------------------------------------------------------------------- 1 | #ifndef WRITE_PPM_H 2 | #define WRITE_PPM_H 3 | 4 | #include 5 | #include 6 | 7 | // Write an rgb or grayscale image to a .ppm file. 8 | // 9 | // Inputs: 10 | // filename path to .ppm file as string 11 | // data width*heigh*num_channels array of image intensity data 12 | // width image width (i.e., number of columns) 13 | // height image height (i.e., number of rows) 14 | // num_channels number of channels (e.g., for rgb 3, for grayscale 1) 15 | // Returns true on success, false on failure (e.g., can't open file) 16 | bool write_ppm( 17 | const std::string & filename, 18 | const std::vector & data, 19 | const int width, 20 | const int height, 21 | const int num_channels); 22 | 23 | #endif 24 | -------------------------------------------------------------------------------- /include/Plane.h: -------------------------------------------------------------------------------- 1 | #ifndef PLANE_H 2 | #define PLANE_H 3 | 4 | #include "Object.h" 5 | #include 6 | 7 | class Plane : public Object 8 | { 9 | public: 10 | // Point on plane 11 | Eigen::Vector3d point; 12 | // Normal of plane 13 | Eigen::Vector3d normal; 14 | // Intersect plane with ray. 15 | // 16 | // Inputs: 17 | // Ray ray to intersect with 18 | // min_t minimum parametric distance to consider 19 | // Outputs: 20 | // t first intersection at ray.origin + t * ray.direction 21 | // n surface normal at point of intersection 22 | // Returns iff there a first intersection is found. 23 | bool intersect( 24 | const Ray & ray, const double min_t, double & t, Eigen::Vector3d & n) const; 25 | }; 26 | 27 | #endif 28 | -------------------------------------------------------------------------------- /include/Sphere.h: -------------------------------------------------------------------------------- 1 | #ifndef SPHERE_H 2 | #define SPHERE_H 3 | 4 | #include "Sphere.h" 5 | #include "Object.h" 6 | #include 7 | 8 | class Sphere : public Object 9 | { 10 | public: 11 | Eigen::Vector3d center; 12 | double radius; 13 | public: 14 | // Intersect sphere with ray. 15 | // 16 | // Inputs: 17 | // Ray ray to intersect with 18 | // min_t minimum parametric distance to consider 19 | // Outputs: 20 | // t first intersection at ray.origin + t * ray.direction 21 | // n surface normal at point of intersection 22 | // Returns iff there a first intersection is found. 23 | bool intersect( 24 | const Ray & ray, const double min_t, double & t, Eigen::Vector3d & n) const; 25 | }; 26 | 27 | #endif 28 | -------------------------------------------------------------------------------- /include/viewing_ray.h: -------------------------------------------------------------------------------- 1 | #ifndef VIEWING_RAY_H 2 | #define VIEWING_RAY_H 3 | 4 | #include "Ray.h" 5 | #include "Camera.h" 6 | 7 | // Construct a viewing ray given a camera and subscripts to a pixel 8 | // 9 | // Inputs: 10 | // camera Perspective camera object 11 | // i pixel row index 12 | // j pixel column index 13 | // width number of pixels width of image 14 | // height number of pixels height of image 15 | // Outputs: 16 | // ray viewing ray starting at camera shooting through pixel. When t=1, then 17 | // ray.origin + t*ray.direction should land exactly on the center of the 18 | // pixel (i,j) 19 | void viewing_ray( 20 | const Camera & camera, 21 | const int i, 22 | const int j, 23 | const int width, 24 | const int height, 25 | Ray & ray); 26 | #endif 27 | -------------------------------------------------------------------------------- /include/Triangle.h: -------------------------------------------------------------------------------- 1 | #ifndef TRIANGLE_H 2 | #define TRIANGLE_H 3 | 4 | #include "Object.h" 5 | #include 6 | 7 | class Triangle : public Object 8 | { 9 | public: 10 | // A triangle has three corners 11 | std::tuple< Eigen::Vector3d, Eigen::Vector3d, Eigen::Vector3d> corners; 12 | // Intersect a triangle with ray. 13 | // 14 | // Inputs: 15 | // Ray ray to intersect with 16 | // min_t minimum parametric distance to consider 17 | // Outputs: 18 | // t first intersection at ray.origin + t * ray.direction 19 | // n surface normal at point of intersection 20 | // Returns iff there a first intersection is found. 21 | bool intersect( 22 | const Ray & ray, const double min_t, double & t, Eigen::Vector3d & n) const; 23 | }; 24 | 25 | #endif 26 | -------------------------------------------------------------------------------- /data/triangle.json: -------------------------------------------------------------------------------- 1 | { 2 | "camera": { 3 | "type": "perspective", 4 | "focal_length": 3, 5 | "eye": [0,0,6], 6 | "up": [0,1,0], 7 | "look": [0,0,-1], 8 | "height": 1, 9 | "width": 1.7777777778 10 | }, 11 | 12 | "objects": [ 13 | { 14 | "type": "triangle", 15 | "corners": [ 16 | [-0.5,0,0], 17 | [0.5,0,0], 18 | [0,0.8660254038,0] 19 | ], 20 | "material": "orange plastic" 21 | } 22 | ], 23 | "lights":[ 24 | { 25 | "type": "directional", 26 | "direction": [0,0,-1], 27 | "color": [0.8,0.8,0.8] 28 | } 29 | ], 30 | "materials": [ 31 | { 32 | "name": "orange plastic", 33 | "ka": [1.0, 0.7, 0.2], 34 | "kd": [1.0, 0.7, 0.2], 35 | "ks": [0.8,0.8,0.8], 36 | "km": [0.05,0.05,0.05], 37 | "phong_exponent": 1000 38 | } 39 | ] 40 | } 41 | -------------------------------------------------------------------------------- /include/TriangleSoup.h: -------------------------------------------------------------------------------- 1 | #ifndef TRIANGLE_SOUP_H 2 | #define TRIANGLE_SOUP_H 3 | 4 | #include "Object.h" 5 | #include 6 | #include 7 | #include 8 | 9 | // Forward declaration 10 | class Triangle; 11 | class TriangleSoup : public Object 12 | { 13 | public: 14 | // A soup is just a set (list) of triangles 15 | std::vector > triangles; 16 | 17 | // Intersect a triangle soup with ray. 18 | // 19 | // Inputs: 20 | // Ray ray to intersect with 21 | // min_t minimum parametric distance to consider 22 | // Outputs: 23 | // t first intersection at ray.origin + t * ray.direction 24 | // n surface normal at point of intersection 25 | // Returns iff there a first intersection is found. 26 | bool intersect( 27 | const Ray & ray, const double min_t, double & t, Eigen::Vector3d & n) const; 28 | }; 29 | 30 | #endif 31 | 32 | -------------------------------------------------------------------------------- /include/Object.h: -------------------------------------------------------------------------------- 1 | #ifndef OBJECT_H 2 | #define OBJECT_H 3 | 4 | #include "Material.h" 5 | #include 6 | #include 7 | 8 | struct Ray; 9 | class Object 10 | { 11 | public: 12 | std::shared_ptr material; 13 | // https://stackoverflow.com/questions/461203/when-to-use-virtual-destructors 14 | virtual ~Object() {} 15 | // Intersect object with ray. 16 | // 17 | // Inputs: 18 | // Ray ray to intersect with 19 | // min_t minimum parametric distance to consider 20 | // Outputs: 21 | // t first intersection at ray.origin + t * ray.direction 22 | // n surface normal at point of intersection 23 | // Returns iff there a first intersection is found. 24 | // 25 | // The funny = 0 just ensures that this function is defined (as a no-op) 26 | virtual bool intersect( 27 | const Ray & ray, const double min_t, double & t, Eigen::Vector3d & n) const = 0; 28 | }; 29 | 30 | #endif 31 | -------------------------------------------------------------------------------- /include/raycolor.h: -------------------------------------------------------------------------------- 1 | #ifndef RAYCOLOR_H 2 | #define RAYCOLOR_H 3 | #include "Ray.h" 4 | #include "Object.h" 5 | #include "Light.h" 6 | #include 7 | #include 8 | 9 | // Shoot a ray into a lit scene and collect color information. 10 | // 11 | // Inputs: 12 | // ray ray along which to search 13 | // min_t minimum t value to consider (for viewing rays, this is typically at 14 | // least the _parametric_ distance of the image plane to the camera) 15 | // objects list of objects (shapes) in the scene 16 | // lights list of lights in the scene 17 | // num_recursive_calls how many times has raycolor been called already 18 | // Outputs: 19 | // rgb collected color 20 | // Returns true iff a hit was found 21 | bool raycolor( 22 | const Ray & ray, 23 | const double min_t, 24 | const std::vector< std::shared_ptr > & objects, 25 | const std::vector< std::shared_ptr > & lights, 26 | const int num_recursive_calls, 27 | Eigen::Vector3d & rgb); 28 | 29 | #endif 30 | -------------------------------------------------------------------------------- /include/first_hit.h: -------------------------------------------------------------------------------- 1 | #ifndef FIRST_HIT_H 2 | #define FIRST_HIT_H 3 | 4 | #include "Ray.h" 5 | #include "Object.h" 6 | #include 7 | #include 8 | #include 9 | 10 | // Find the first (visible) hit given a ray and a collection of scene objects 11 | // 12 | // Inputs: 13 | // ray ray along which to search 14 | // min_t minimum t value to consider (for viewing rays, this is typically at 15 | // least the _parametric_ distance of the image plane to the camera) 16 | // objects list of objects (shapes) in the scene 17 | // Outputs: 18 | // hit_id index into objects of object with first hit 19 | // t _parametric_ distance along ray so that ray.origin+t*ray.direction is 20 | // the hit location 21 | // n surface normal at hit location 22 | // Returns true iff a hit was found 23 | bool first_hit( 24 | const Ray & ray, 25 | const double min_t, 26 | const std::vector< std::shared_ptr > & objects, 27 | int & hit_id, 28 | double & t, 29 | Eigen::Vector3d & n); 30 | 31 | #endif 32 | -------------------------------------------------------------------------------- /include/blinn_phong_shading.h: -------------------------------------------------------------------------------- 1 | #ifndef BLINN_PHONG_SHADING_H 2 | #define BLINN_PHONG_SHADING_H 3 | #include "Ray.h" 4 | #include "Light.h" 5 | #include "Object.h" 6 | #include 7 | #include 8 | #include 9 | 10 | 11 | // Given a ray and its hit in the scene, return the Blinn-Phong shading 12 | // contribution over all _visible_ light sources (e.g., take into account 13 | // shadows). Use a hard-coded value of ia=0.1 for ambient light. 14 | // 15 | // Inputs: 16 | // ray incoming ray 17 | // hit_id index into objects of the object just hit by ray 18 | // t _parametric_ distance along ray to hit 19 | // n unit surface normal at hit 20 | // objects list of objects in the scene 21 | // lights list of lights in the scene 22 | // Returns shaded color collected by this ray as rgb 3-vector 23 | Eigen::Vector3d blinn_phong_shading( 24 | const Ray & ray, 25 | const int & hit_id, 26 | const double & t, 27 | const Eigen::Vector3d & n, 28 | const std::vector< std::shared_ptr > & objects, 29 | const std::vector > & lights); 30 | 31 | #endif 32 | -------------------------------------------------------------------------------- /data/sphere.json: -------------------------------------------------------------------------------- 1 | { 2 | "camera": { 3 | "type": "perspective", 4 | "focal_length": 3, 5 | "eye": [0,0,4], 6 | "up": [0,1,0], 7 | "look": [0,0,-1], 8 | "height": 1, 9 | "width": 1.7777777778 10 | }, 11 | "materials": [ 12 | { 13 | "name": "orange plastic", 14 | "ka": [1.0, 0.7, 0.2], 15 | "kd": [1.0, 0.7, 0.2], 16 | "ks": [0.8,0.8,0.8], 17 | "km": [0.05,0.05,0.05], 18 | "phong_exponent": 1000 19 | }, 20 | { 21 | "name": "light blue", 22 | "ka": [0.1, 0.1, 0.2], 23 | "kd": [0.5, 0.6, 0.8], 24 | "ks": [0.05,0.05,0.1], 25 | "km": [0.2,0.2,0.2], 26 | "phong_exponent": 20 27 | } 28 | ], 29 | "lights":[ 30 | { 31 | "type": "directional", 32 | "direction": [0,0,-1], 33 | "color": [0.8,0.8,0.8] 34 | }, 35 | { 36 | "type": "point", 37 | "position": [-10,20,10], 38 | "color": [0.8,0.8,0.8] 39 | } 40 | ], 41 | 42 | "objects": [ 43 | { 44 | "type": "sphere", 45 | "material": "light blue", 46 | "center": [0,0,0], 47 | "radius": 0.5 48 | } 49 | ] 50 | } 51 | -------------------------------------------------------------------------------- /data/sphere-large-change.json: -------------------------------------------------------------------------------- 1 | { 2 | "camera": { 3 | "type": "perspective", 4 | "focal_length": 3, 5 | "eye": [0,0,4], 6 | "up": [0,1,0], 7 | "look": [0,0,-1], 8 | "height": 1, 9 | "width": 1 10 | }, 11 | "materials": [ 12 | { 13 | "name": "orange plastic", 14 | "ka": [1.0, 0.7, 0.2], 15 | "kd": [1.0, 0.7, 0.2], 16 | "ks": [0.8,0.8,0.8], 17 | "km": [0.05,0.05,0.05], 18 | "phong_exponent": 1000 19 | }, 20 | { 21 | "name": "light blue", 22 | "ka": [0.1, 0.1, 0.2], 23 | "kd": [0.5, 0.6, 0.8], 24 | "ks": [0.05,0.05,0.1], 25 | "km": [0.2,0.2,0.2], 26 | "phong_exponent": 20 27 | } 28 | ], 29 | "lights":[ 30 | { 31 | "type": "directional", 32 | "direction": [0.1,0.1,-1], 33 | "color": [1.0,0.6,0.6] 34 | }, 35 | { 36 | "type": "point", 37 | "position": [10,-10,10], 38 | "color": [0.8,1.0,0.8] 39 | } 40 | ], 41 | 42 | "objects": [ 43 | { 44 | "type": "sphere", 45 | "material": "light blue", 46 | "center": [0,0,0], 47 | "radius": 0.5 48 | } 49 | ] 50 | } 51 | -------------------------------------------------------------------------------- /data/sphere-small-change.json: -------------------------------------------------------------------------------- 1 | { 2 | "camera": { 3 | "type": "perspective", 4 | "focal_length": 3, 5 | "eye": [0,0,4], 6 | "up": [0,1,0], 7 | "look": [0,0,-1], 8 | "height": 1, 9 | "width": 1 10 | }, 11 | "materials": [ 12 | { 13 | "name": "orange plastic", 14 | "ka": [1.0, 0.7, 0.2], 15 | "kd": [1.0, 0.7, 0.2], 16 | "ks": [0.8,0.8,0.8], 17 | "km": [0.05,0.05,0.05], 18 | "phong_exponent": 1000 19 | }, 20 | { 21 | "name": "light blue", 22 | "ka": [0.1, 0.1, 0.2], 23 | "kd": [0.5, 0.6, 0.8], 24 | "ks": [0.05,0.05,0.1], 25 | "km": [0.2,0.2,0.2], 26 | "phong_exponent": 20 27 | } 28 | ], 29 | "lights":[ 30 | { 31 | "type": "directional", 32 | "direction": [0,0,-1], 33 | "color": [0.85,0.8,0.8] 34 | }, 35 | { 36 | "type": "point", 37 | "position": [-5,10,10], 38 | "color": [0.8,0.85,0.8] 39 | } 40 | ], 41 | 42 | "objects": [ 43 | { 44 | "type": "sphere", 45 | "material": "light blue", 46 | "center": [0,0,0], 47 | "radius": 0.5 48 | } 49 | ] 50 | } 51 | -------------------------------------------------------------------------------- /data/sphere-and-plane.json: -------------------------------------------------------------------------------- 1 | { 2 | "camera": { 3 | "type": "perspective", 4 | "focal_length": 3, 5 | "eye": [0,0,5], 6 | "up": [0,1,0], 7 | "look": [0,0,-1], 8 | "height": 1, 9 | "width": 1.7777777778 10 | }, 11 | "materials": [ 12 | { 13 | "name": "orange plastic", 14 | "ka": [1, 0.7, 0.2], 15 | "kd": [1.0, 0.7, 0.2], 16 | "ks": [0.8,0.8,0.8], 17 | "km": [0.05,0.05,0.05], 18 | "phong_exponent": 1000 19 | }, 20 | { 21 | "name": "Lambertian blue", 22 | "ka": [0.2, 0.3, 0.8], 23 | "kd": [0.2, 0.3, 0.8], 24 | "ks": [0.1,0.1,0.1], 25 | "km": [0.3,0.3,0.3], 26 | "phong_exponent": 20 27 | } 28 | ], 29 | "lights":[ 30 | { 31 | "type": "directional", 32 | "direction": [0,0,-1], 33 | "color": [0.8,0.8,0.8] 34 | }, 35 | { 36 | "type": "point", 37 | "position": [-10,20,10], 38 | "color": [0.8,0.8,0.8] 39 | } 40 | ], 41 | 42 | "objects": [ 43 | { 44 | "type": "sphere", 45 | "material": "orange plastic", 46 | "center": [0,0,0], 47 | "radius": 0.5 48 | }, 49 | { 50 | "type": "plane", 51 | "material": "Lambertian blue", 52 | "point": [0,-0.5,0], 53 | "normal": [0,1,0] 54 | } 55 | ] 56 | } 57 | -------------------------------------------------------------------------------- /main.cpp: -------------------------------------------------------------------------------- 1 | #include "Object.h" 2 | #include "Camera.h" 3 | #include "Light.h" 4 | #include "read_json.h" 5 | #include "write_ppm.h" 6 | #include "viewing_ray.h" 7 | #include "raycolor.h" 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | 15 | 16 | int main(int argc, char * argv[]) 17 | { 18 | Camera camera; 19 | std::vector< std::shared_ptr > objects; 20 | std::vector< std::shared_ptr > lights; 21 | // Read a camera and scene description from given .json file 22 | read_json( 23 | argc<=1?"../data/sphere-and-plane.json":argv[1], 24 | camera, 25 | objects, 26 | lights); 27 | 28 | int width = 640; 29 | int height = 360; 30 | std::vector rgb_image(3*width*height); 31 | // For each pixel (i,j) 32 | for(unsigned i=0; i 4 | // 5 | // This Source Code Form is subject to the terms of the Mozilla Public License 6 | // v. 2.0. If a copy of the MPL was not distributed with this file, You can 7 | // obtain one at http://mozilla.org/MPL/2.0/. 8 | #ifndef IGL_DIRNAME_H 9 | #define IGL_DIRNAME_H 10 | #define IGL_INLINE inline 11 | #include 12 | 13 | namespace igl 14 | { 15 | // Function like PHP's dirname: /etc/passwd --> /etc, 16 | // Input: 17 | // path string containing input path 18 | // Returns string containing dirname (see php's dirname) 19 | // 20 | // See also: basename, pathinfo 21 | IGL_INLINE std::string dirname(const std::string & path); 22 | } 23 | // 24 | // This file is part of libigl, a simple c++ geometry processing library. 25 | // 26 | // Copyright (C) 2013 Alec Jacobson 27 | // 28 | // This Source Code Form is subject to the terms of the Mozilla Public License 29 | // v. 2.0. If a copy of the MPL was not distributed with this file, You can 30 | // obtain one at http://mozilla.org/MPL/2.0/. 31 | 32 | #include 33 | 34 | IGL_INLINE std::string igl::dirname(const std::string & path) 35 | { 36 | if(path == "") 37 | { 38 | return std::string(""); 39 | } 40 | #if defined (WIN32) 41 | char del('\\'); 42 | #else 43 | char del('/'); 44 | #endif 45 | // http://stackoverflow.com/questions/5077693/dirnamephp-similar-function-in-c 46 | std::string::const_reverse_iterator last_slash = 47 | std::find( 48 | path.rbegin(), 49 | path.rend(),del); 50 | if( last_slash == path.rend() ) 51 | { 52 | // No slashes found 53 | return std::string("."); 54 | }else if(1 == (last_slash.base() - path.begin())) 55 | { 56 | // Slash is first char 57 | return std::string(&del); 58 | }else if(path.end() == last_slash.base() ) 59 | { 60 | // Slash is last char 61 | std::string redo = std::string(path.begin(),path.end()-1); 62 | return igl::dirname(redo); 63 | } 64 | return std::string(path.begin(),last_slash.base()-1); 65 | } 66 | 67 | 68 | 69 | #endif 70 | 71 | -------------------------------------------------------------------------------- /tex/66937766ec704b70da73bb7d35a355ad.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /markdown/header.md: -------------------------------------------------------------------------------- 1 | css: css/github-markdown.css 2 | html header: 3 | 18 |
19 | 20 | 21 | 24 | 25 | 26 |
27 | $\newcommand{\A}{\mat{A}}$ 28 | $\newcommand{\B}{\mat{B}}$ 29 | $\newcommand{\C}{\mat{C}}$ 30 | $\newcommand{\D}{\mat{D}}$ 31 | $\newcommand{\E}{\mat{E}}$ 32 | $\newcommand{\F}{\mat{F}}$ 33 | $\newcommand{\G}{\mat{G}}$ 34 | $\newcommand{\H}{\mat{H}}$ 35 | $\newcommand{\I}{\mat{I}}$ 36 | $\newcommand{\J}{\mat{J}}$ 37 | $\newcommand{\K}{\mat{K}}$ 38 | $\newcommand{\L}{\mat{L}}$ 39 | $\newcommand{\M}{\mat{M}}$ 40 | $\newcommand{\N}{\mat{N}}$ 41 | $\newcommand{\One}{\mathbf{1}}$ 42 | $\newcommand{\P}{\mat{P}}$ 43 | $\newcommand{\Q}{\mat{Q}}$ 44 | $\newcommand{\Rot}{\mat{R}}$ 45 | $\newcommand{\R}{\mathbb{R}}$ 46 | $\newcommand{\S}{\mathcal{S}}$ 47 | $\newcommand{\T}{\mat{T}}$ 48 | $\newcommand{\U}{\mat{U}}$ 49 | $\newcommand{\V}{\mat{V}}$ 50 | $\newcommand{\W}{\mat{W}}$ 51 | $\newcommand{\X}{\mat{X}}$ 52 | $\newcommand{\Y}{\mat{Y}}$ 53 | $\newcommand{\argmax}{\mathop{\text{argmax}}}$ 54 | $\newcommand{\argmin}{\mathop{\text{argmin}}}$ 55 | $\newcommand{\a}{\vec{a}}$ 56 | $\newcommand{\b}{\vec{b}}$ 57 | $\newcommand{\c}{\vec{c}}$ 58 | $\newcommand{\d}{\vec{d}}$ 59 | $\newcommand{\e}{\vec{e}}$ 60 | $\newcommand{\f}{\vec{f}}$ 61 | $\newcommand{\g}{\vec{g}}$ 62 | $\newcommand{\mat}[1]{\mathbf{#1}}$ 63 | $\newcommand{\min}{\mathop{\text{min}}}$ 64 | $\newcommand{\m}{\vec{m}}$ 65 | $\newcommand{\n}{\vec{n}}$ 66 | $\newcommand{\p}{\vec{p}}$ 67 | $\newcommand{\q}{\vec{q}}$ 68 | $\newcommand{\r}{\vec{r}}$ 69 | $\newcommand{\transpose}{{\mathsf T}}$ 70 | $\newcommand{\tr}[1]{\mathop{\text{tr}}{\left(#1\right)}}$ 71 | $\newcommand{\s}{\vec{s}}$ 72 | $\newcommand{\t}{\vec{t}}$ 73 | $\newcommand{\u}{\vec{u}}$ 74 | $\newcommand{\vec}[1]{\mathbf{#1}}$ 75 | $\newcommand{\x}{\vec{x}}$ 76 | $\newcommand{\y}{\vec{y}}$ 77 | $\newcommand{\z}{\vec{z}}$ 78 | $\newcommand{\0}{\vec{0}}$ 79 | $\renewcommand{\v}{\vec{v}}$ 80 | 81 | $\renewcommand{\hat}[1]{\widehat{#1}}$ 82 |
83 | -------------------------------------------------------------------------------- /data/inside-a-sphere.json: -------------------------------------------------------------------------------- 1 | { 2 | "camera": { 3 | "type": "perspective", 4 | "focal_length": 3, 5 | "eye": [0,0,5], 6 | "up": [0,1,0], 7 | "look": [0,0,-1], 8 | "height": 1, 9 | "width": 1.7777777778 10 | }, 11 | 12 | "materials": [ 13 | { 14 | "name": "red metal", 15 | "ka": [0.894118,0.101961,0.109804], 16 | "kd": [0.894118,0.101961,0.109804], 17 | "ks": [0.2,0.2,0.2], 18 | "km": [0.1,0.1,0.1], 19 | "phong_exponent": 500 20 | }, 21 | { 22 | "name": "blue metal", 23 | "ka": [0.215686,0.494118,0.721569], 24 | "kd": [0.215686,0.494118,0.721569], 25 | "ks": [0.2,0.2,0.2], 26 | "km": [0.1,0.1,0.1], 27 | "phong_exponent": 500 28 | }, 29 | { 30 | "name": "green metal", 31 | "ka": [0.301961,0.686275,0.290196], 32 | "kd": [0.301961,0.686275,0.290196], 33 | "ks": [0.2,0.2,0.2], 34 | "km": [0.1,0.1,0.1], 35 | "phong_exponent": 500 36 | }, 37 | { 38 | "name": "purple metal", 39 | "ka": [0.596078,0.305882,0.639216], 40 | "kd": [0.596078,0.305882,0.639216], 41 | "ks": [0.2,0.2,0.2], 42 | "km": [0.1,0.1,0.1], 43 | "phong_exponent": 500 44 | }, 45 | { 46 | "name": "orange plastic", 47 | "ka": [1, 0.7, 0.2], 48 | "kd": [1.0, 0.7, 0.2], 49 | "ks": [0.8,0.8,0.8], 50 | "km": [0.05,0.05,0.05], 51 | "phong_exponent": 1000 52 | } 53 | ], 54 | 55 | "lights":[ 56 | { 57 | "type": "point", 58 | "color": [0.3,0.3,0.3], 59 | "position": [0,0,6] 60 | }, 61 | { 62 | "type": "point", 63 | "color": [0.3,0.3,0.3], 64 | "position": [-0.9,0,3] 65 | }, 66 | { 67 | "type": "point", 68 | "color": [0.3,0.3,0.3], 69 | "position": [0.9,0,3] 70 | } 71 | ], 72 | 73 | "objects": [ 74 | { 75 | "type": "plane", 76 | "material": "red metal", 77 | "point": [-1,-1,0], 78 | "normal": [1,0,0] 79 | }, 80 | { 81 | "type": "plane", 82 | "material": "purple metal", 83 | "point": [1,-1,0], 84 | "normal": [-1,0,0] 85 | }, 86 | { 87 | "type": "plane", 88 | "material": "blue metal", 89 | "point": [0,1,0], 90 | "normal": [0,-1,0] 91 | }, 92 | { 93 | "type": "plane", 94 | "material": "green metal", 95 | "point": [0,-1,0], 96 | "normal": [0,1,0] 97 | }, 98 | { 99 | "type": "sphere", 100 | "material": "orange plastic", 101 | "center": [0.0,0,1.6], 102 | "radius": 0.5 103 | } 104 | ] 105 | } 106 | 107 | -------------------------------------------------------------------------------- /data/sphere-packing.json: -------------------------------------------------------------------------------- 1 | { 2 | "camera": { 3 | "type": "perspective", 4 | "focal_length": 2.8, 5 | "eye": [9.6,0.3,10], 6 | "up": [-0.7071067812,0,0.7071067812], 7 | "look": [-0.7071067812,0,-0.7071067812], 8 | "height": 1, 9 | "width": 1.7777777778 10 | }, 11 | 12 | "materials": [ 13 | { 14 | "name": "Lambertian gray", 15 | "ka": [0.2, 0.2, 0.2], 16 | "kd": [0.8, 0.8, 0.8], 17 | "ks": [0.1,0.1,0.1], 18 | "km": [0.1,0.1,0.1], 19 | "phong_exponent": 20 20 | }, 21 | { 22 | "name": "red metal", 23 | "ka": [0.894118,0.101961,0.109804], 24 | "kd": [0.894118,0.101961,0.109804], 25 | "ks": [0.894118,0.101961,0.109804], 26 | "km": [0.894118,0.101961,0.109804], 27 | "phong_exponent": 2000 28 | }, 29 | { 30 | "name": "blue metal", 31 | "ka": [0.215686,0.494118,0.721569], 32 | "kd": [0.215686,0.494118,0.721569], 33 | "ks": [0.215686,0.494118,0.721569], 34 | "km": [0.215686,0.494118,0.721569], 35 | "phong_exponent": 2000 36 | }, 37 | { 38 | "name": "green metal", 39 | "ka": [0.301961,0.686275,0.290196], 40 | "kd": [0.301961,0.686275,0.290196], 41 | "ks": [0.301961,0.686275,0.290196], 42 | "km": [0.301961,0.686275,0.290196], 43 | "phong_exponent": 2000 44 | }, 45 | { 46 | "name": "purple metal", 47 | "ka": [0.596078,0.305882,0.639216], 48 | "kd": [0.596078,0.305882,0.639216], 49 | "ks": [0.596078,0.305882,0.639216], 50 | "km": [0.596078,0.305882,0.639216], 51 | "phong_exponent": 2000 52 | } 53 | ], 54 | "lights":[ 55 | { 56 | "type": "point", 57 | "position": [0.3,9,15], 58 | "color": [0.7,0.7,0.7] 59 | }, 60 | { 61 | "type": "point", 62 | "position": [9,-0.3,15], 63 | "color": [0.7,0.7,0.7] 64 | } 65 | ], 66 | 67 | "objects": [ 68 | { 69 | "type": "plane", 70 | "material": "Lambertian gray", 71 | "point": [0,0,-1], 72 | "normal": [0,0,1] 73 | }, 74 | { 75 | "type": "sphere", 76 | "material": "red metal", 77 | "radius": 1, 78 | "center": [0,0,1] 79 | }, 80 | { 81 | "type": "sphere", 82 | "material": "blue metal", 83 | "radius": 1, 84 | "center": [2,0,1] 85 | }, 86 | { 87 | "type": "sphere", 88 | "material": "green metal", 89 | "radius": 1, 90 | "center": [1,1.7321,1] 91 | }, 92 | { 93 | "type": "sphere", 94 | "material": "purple metal", 95 | "radius": 1, 96 | "center": [1,0.57735,2.633] 97 | } 98 | ] 99 | } 100 | 101 | 102 | -------------------------------------------------------------------------------- /tex/8860040216a2e61c344544a77b5cd2ce.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /data/mirror.json: -------------------------------------------------------------------------------- 1 | { 2 | "camera": { 3 | "type": "perspective", 4 | "focal_length": 1.1, 5 | "eye": [1,0,2], 6 | "up": [0,1,0], 7 | "look": [-0.92388,0,-0.38268], 8 | "height": 1, 9 | "width": 1.7777777778 10 | }, 11 | 12 | "lights":[ 13 | { 14 | "type": "point", 15 | "color": [0.3,0.4,0.3], 16 | "position": [ 0,0.5,1.5] 17 | }, 18 | { 19 | "type": "point", 20 | "color": [0.35,0.3,0.35], 21 | "position": [ 0,0.5,-0.5] 22 | } 23 | ], 24 | 25 | "materials": [ 26 | { 27 | "name": "matte gray", 28 | "ka": [0.90, 0.90, 0.90], 29 | "kd": [0.95, 0.95, 0.95], 30 | "ks": [0.3,0.3,0.3], 31 | "km": [0.05,0.05,0.05], 32 | "phong_exponent": 200 33 | }, 34 | { 35 | "name": "black paint", 36 | "ka": [0.1,0.1,0.1], 37 | "kd": [0.1,0.1,0.1], 38 | "ks": [0.1,0.1,0.1], 39 | "km": [0.0,0.0,0.0], 40 | "phong_exponent": 200 41 | }, 42 | { 43 | "name": "brown plastic", 44 | "ka": [0.596078,0.305882,0.639216], 45 | "kd": [0.3,0.25,0.2], 46 | "ks": [0.1,0.1,0.1], 47 | "km": [0.0,0.0,0.0], 48 | "phong_exponent": 200 49 | }, 50 | { 51 | "name": "gray mirror", 52 | "ka": [0.1,0.1,0.1], 53 | "kd": [0.1,0.1,0.1], 54 | "ks": [0.1,0.1,0.1], 55 | "km": [0.999,0.999,0.999], 56 | "phong_exponent": 1000 57 | }, 58 | { 59 | "name": "yellow bone", 60 | "ka": [0.95, 0.95, 0.9], 61 | "kd": [0.95, 0.95, 0.9], 62 | "ks": [0.8,0.8,0.8], 63 | "km": [0.00,0.00,0.00], 64 | "phong_exponent": 200 65 | } 66 | ], 67 | 68 | "objects": [ 69 | { 70 | "type": "plane", 71 | "material": "black paint", 72 | "point": [-1,0,0], 73 | "normal": [1,0,0] 74 | }, 75 | { 76 | "type": "plane", 77 | "material": "black paint", 78 | "point": [0,0,-1], 79 | "normal": [0,0,1] 80 | }, 81 | { 82 | "type": "plane", 83 | "material": "black paint", 84 | "point": [0,1,0], 85 | "normal": [0,-1,0] 86 | }, 87 | { 88 | "type": "plane", 89 | "material": "black paint", 90 | "point": [1,0,0], 91 | "normal": [-1,0,0] 92 | }, 93 | { 94 | "material": "black paint", 95 | "type": "plane", 96 | "point": [0,-1,0], 97 | "normal": [0,1,0] 98 | }, 99 | { 100 | "material": "brown plastic", 101 | "type": "soup", 102 | "stl": "frame.stl" 103 | }, 104 | { 105 | "material": "gray mirror", 106 | "type": "soup", 107 | "stl": "mirror.stl" 108 | }, 109 | { 110 | "material": "yellow bone", 111 | "type": "soup", 112 | "stl": "skull.stl" 113 | } 114 | ] 115 | } 116 | 117 | -------------------------------------------------------------------------------- /data/bunny.json: -------------------------------------------------------------------------------- 1 | { 2 | "camera": { 3 | "type": "perspective", 4 | "focal_length": 1, 5 | "eye": [0,0,2], 6 | "up": [0,1,0], 7 | "look": [0,0,-1], 8 | "height": 1, 9 | "width": 1.7777777778 10 | }, 11 | 12 | "lights":[ 13 | { 14 | "type": "point", 15 | "color": [0.3,0.3,0.3], 16 | "position": [-0.9,0.9,2] 17 | }, 18 | { 19 | "type": "point", 20 | "color": [0.3,0.3,0.3], 21 | "position": [0.9,0.9,2] 22 | }, 23 | { 24 | "type": "point", 25 | "color": [0.3,0.3,0.3], 26 | "position": [0,0.9,2] 27 | }, 28 | { 29 | "type": "point", 30 | "color": [0.3,0.3,0.3], 31 | "position": [0,-0.9,2] 32 | } 33 | ], 34 | 35 | "materials": [ 36 | { 37 | "name": "matte gray", 38 | "ka": [0.90, 0.90, 0.90], 39 | "kd": [0.95, 0.95, 0.95], 40 | "ks": [0.3,0.3,0.3], 41 | "km": [0.05,0.05,0.05], 42 | "phong_exponent": 200 43 | }, 44 | { 45 | "name": "red metal", 46 | "ka": [0.894118,0.101961,0.109804], 47 | "kd": [0.894118,0.101961,0.109804], 48 | "ks": [0.2,0.2,0.2], 49 | "km": [0.1,0.1,0.1], 50 | "phong_exponent": 500 51 | }, 52 | { 53 | "name": "blue metal", 54 | "ka": [0.215686,0.494118,0.721569], 55 | "kd": [0.215686,0.494118,0.721569], 56 | "ks": [0.2,0.2,0.2], 57 | "km": [0.1,0.1,0.1], 58 | "phong_exponent": 500 59 | }, 60 | { 61 | "name": "green metal", 62 | "ka": [0.301961,0.686275,0.290196], 63 | "kd": [0.301961,0.686275,0.290196], 64 | "ks": [0.2,0.2,0.2], 65 | "km": [0.1,0.1,0.1], 66 | "phong_exponent": 500 67 | }, 68 | { 69 | "name": "orange metal", 70 | "ka": [1,0.49804,0], 71 | "kd": [1,0.49804,0], 72 | "ks": [0.2,0.2,0.2], 73 | "km": [0.1,0.1,0.1], 74 | "phong_exponent": 500 75 | }, 76 | { 77 | "name": "purple metal", 78 | "ka": [0.596078,0.305882,0.639216], 79 | "kd": [0.596078,0.305882,0.639216], 80 | "ks": [0.2,0.2,0.2], 81 | "km": [0.1,0.1,0.1], 82 | "phong_exponent": 500 83 | } 84 | ], 85 | 86 | "objects": [ 87 | { 88 | "type": "soup", 89 | "material": "matte gray", 90 | "stl": "bunny.stl" 91 | }, 92 | { 93 | "type": "plane", 94 | "material": "red metal", 95 | "point": [-1,0,0], 96 | "normal": [1,0,0] 97 | }, 98 | { 99 | "type": "plane", 100 | "material": "purple metal", 101 | "point": [0,0,-1], 102 | "normal": [0,0,1] 103 | }, 104 | { 105 | "type": "plane", 106 | "material": "blue metal", 107 | "point": [0,1,0], 108 | "normal": [0,-1,0] 109 | }, 110 | { 111 | "type": "plane", 112 | "material": "orange metal", 113 | "point": [1,0,0], 114 | "normal": [-1,0,0] 115 | }, 116 | { 117 | "material": "green metal", 118 | "type": "plane", 119 | "point": [0,-1,0], 120 | "normal": [0,1,0] 121 | } 122 | ] 123 | } 124 | 125 | -------------------------------------------------------------------------------- /tex/71486f265f83bc1e3d2b6f67704bcc23.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /tex/f58ed17486d1735419372f2b7d091779.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /tex/70118eb82d4643bd42647f21941136af.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /tex/22f21af20c3e7f253a35c65bed98680e.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /include/read_json.h: -------------------------------------------------------------------------------- 1 | #ifndef READ_JSON_H 2 | #define READ_JSON_H 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | // Forward declaration 9 | struct Object; 10 | struct Light; 11 | 12 | // Read a scene description from a .json file 13 | // 14 | // Input: 15 | // filename path to .json file 16 | // Output: 17 | // camera camera looking at the scene 18 | // objects list of shared pointers to objects 19 | // lights list of shared pointers to lights 20 | inline bool read_json( 21 | const std::string & filename, 22 | Camera & camera, 23 | std::vector > & objects, 24 | std::vector > & lights); 25 | 26 | // Implementation 27 | 28 | #include 29 | #include "readSTL.h" 30 | #include "dirname.h" 31 | #include "Object.h" 32 | #include "Sphere.h" 33 | #include "Plane.h" 34 | #include "Triangle.h" 35 | #include "TriangleSoup.h" 36 | #include "Light.h" 37 | #include "PointLight.h" 38 | #include "DirectionalLight.h" 39 | #include "Material.h" 40 | #include 41 | #include 42 | #include 43 | #include 44 | 45 | inline bool read_json( 46 | const std::string & filename, 47 | Camera & camera, 48 | std::vector > & objects, 49 | std::vector > & lights) 50 | { 51 | // Heavily borrowing from 52 | // https://github.com/yig/graphics101-raycasting/blob/master/parser.cpp 53 | using json = nlohmann::json; 54 | 55 | std::ifstream infile( filename ); 56 | if( !infile ) return false; 57 | json j; 58 | infile >> j; 59 | 60 | 61 | // parse a vector 62 | auto parse_Vector3d = [](const json & j) -> Eigen::Vector3d 63 | { 64 | return Eigen::Vector3d(j[0],j[1],j[2]); 65 | }; 66 | // parse camera 67 | auto parse_camera = 68 | [&parse_Vector3d](const json & j, Camera & camera) 69 | { 70 | assert(j["type"] == "perspective" && "Only handling perspective cameras"); 71 | camera.d = j["focal_length"].get(); 72 | camera.e = parse_Vector3d(j["eye"]); 73 | camera.v = parse_Vector3d(j["up"]).normalized(); 74 | camera.w = -parse_Vector3d(j["look"]).normalized(); 75 | camera.u = camera.v.cross(camera.w); 76 | camera.height = j["height"].get(); 77 | camera.width = j["width"].get(); 78 | }; 79 | parse_camera(j["camera"],camera); 80 | 81 | // Parse materials 82 | std::unordered_map > materials; 83 | auto parse_materials = [&parse_Vector3d]( 84 | const json & j, 85 | std::unordered_map > & materials) 86 | { 87 | materials.clear(); 88 | for(const json & jmat : j) 89 | { 90 | std::string name = jmat["name"]; 91 | std::shared_ptr material(new Material()); 92 | material->ka = parse_Vector3d(jmat["ka"]); 93 | material->kd = parse_Vector3d(jmat["kd"]); 94 | material->ks = parse_Vector3d(jmat["ks"]); 95 | material->km = parse_Vector3d(jmat["km"]); 96 | material->phong_exponent = jmat["phong_exponent"]; 97 | materials[name] = material; 98 | } 99 | }; 100 | parse_materials(j["materials"],materials); 101 | 102 | auto parse_lights = [&parse_Vector3d]( 103 | const json & j, 104 | std::vector > & lights) 105 | { 106 | lights.clear(); 107 | for(const json & jlight : j) 108 | { 109 | if(jlight["type"] == "directional") 110 | { 111 | std::shared_ptr light(new DirectionalLight()); 112 | light->d = parse_Vector3d(jlight["direction"]).normalized(); 113 | light->I = parse_Vector3d(jlight["color"]); 114 | lights.push_back(light); 115 | }else if(jlight["type"] == "point") 116 | { 117 | std::shared_ptr light(new PointLight()); 118 | light->p = parse_Vector3d(jlight["position"]); 119 | light->I = parse_Vector3d(jlight["color"]); 120 | lights.push_back(light); 121 | } 122 | } 123 | }; 124 | parse_lights(j["lights"],lights); 125 | 126 | auto parse_objects = [&parse_Vector3d,&filename,&materials]( 127 | const json & j, 128 | std::vector > & objects) 129 | { 130 | objects.clear(); 131 | for(const json & jobj : j) 132 | { 133 | if(jobj["type"] == "sphere") 134 | { 135 | std::shared_ptr sphere(new Sphere()); 136 | sphere->center = parse_Vector3d(jobj["center"]); 137 | sphere->radius = jobj["radius"].get(); 138 | objects.push_back(sphere); 139 | }else if(jobj["type"] == "plane") 140 | { 141 | std::shared_ptr plane(new Plane()); 142 | plane->point = parse_Vector3d(jobj["point"]); 143 | plane->normal = parse_Vector3d(jobj["normal"]).normalized(); 144 | objects.push_back(plane); 145 | }else if(jobj["type"] == "triangle") 146 | { 147 | std::shared_ptr tri(new Triangle()); 148 | tri->corners = std::make_tuple( 149 | parse_Vector3d(jobj["corners"][0]), 150 | parse_Vector3d(jobj["corners"][1]), 151 | parse_Vector3d(jobj["corners"][2])); 152 | objects.push_back(tri); 153 | }else if(jobj["type"] == "soup") 154 | { 155 | std::vector > V; 156 | std::vector > F; 157 | std::vector > N; 158 | { 159 | #if defined(WIN32) || defined(_WIN32) 160 | #define PATH_SEPARATOR std::string("\\") 161 | #else 162 | #define PATH_SEPARATOR std::string("/") 163 | #endif 164 | const std::string stl_path = jobj["stl"]; 165 | igl::readSTL( 166 | igl::dirname(filename)+ 167 | PATH_SEPARATOR + 168 | stl_path, 169 | V,F,N); 170 | } 171 | std::shared_ptr soup(new TriangleSoup()); 172 | for(int f = 0;f tri(new Triangle()); 175 | tri->corners = std::make_tuple( 176 | Eigen::Vector3d( V[F[f][0]][0], V[F[f][0]][1], V[F[f][0]][2]), 177 | Eigen::Vector3d( V[F[f][1]][0], V[F[f][1]][1], V[F[f][1]][2]), 178 | Eigen::Vector3d( V[F[f][2]][0], V[F[f][2]][1], V[F[f][2]][2]) 179 | ); 180 | soup->triangles.push_back(tri); 181 | } 182 | objects.push_back(soup); 183 | } 184 | //objects.back()->material = default_material; 185 | if(jobj.count("material")) 186 | { 187 | if(materials.count(jobj["material"])) 188 | { 189 | objects.back()->material = materials[jobj["material"]]; 190 | } 191 | } 192 | } 193 | }; 194 | parse_objects(j["objects"],objects); 195 | 196 | return true; 197 | } 198 | 199 | #endif 200 | -------------------------------------------------------------------------------- /tex/59064857cf8a4117765652ff2be6d992.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /README.tex.md: -------------------------------------------------------------------------------- 1 | # Computer Graphics – Ray Tracing 2 | 3 | > **To get started:** Clone this repository and its submodule using 4 | > 5 | > git clone --recursive http://github.com/alecjacobson/computer-graphics-ray-tracing.git 6 | > 7 | > **Do not fork:** Clicking "Fork" will create a _public_ repository. If you'd like to use GitHub while you work on your assignment, then mirror this repo as a new _private_ repository: https://stackoverflow.com/questions/10065526/github-how-to-make-a-fork-of-public-repository-private 8 | 9 | ## Background 10 | 11 | ### Read Sections 4.5-4.9 of _Fundamentals of Computer Graphics (4th Edition)_. 12 | 13 | Many of the classes and functions of this assignment are borrowed or adapted 14 | from the previous [ray casting assignment](https://github.com/alecjacobson/computer-graphics-ray-casting). 15 | 16 | Unlike that assignment, this [ray 17 | tracer](https://en.wikipedia.org/wiki/Ray_tracing_(graphics)) will produce 18 | _approximately_ accurate renderings of scenes illuminated with light. 19 | Ultimately, the shading and lighting models here are _useful_ hacks. The basic 20 | [recursive](https://en.wikipedia.org/wiki/Ray_tracing_(graphics)#Recursive_ray_tracing_algorithm) 21 | structure of the program is core to many methods for rendering with [global 22 | illumination](https://en.wikipedia.org/wiki/Global_illumination) effects (e.g., 23 | shadows, reflections, etc.). 24 | 25 | ![Running `./raytracing ../data/sphere-and-plane.json` should produce this image.](images/sphere-and-plane.png) 26 | 27 | ## Floating point numbers 28 | 29 | For this assignment we will use the `Eigen::Vector3d` to represent points and 30 | vectors, but _also_ RGB colors. For all computation (before finally writing the 31 | .ppm file) we will use double precision floating point numbers and `0` will 32 | represent no light and `1` will represent the brightest color we can display. 33 | 34 | [Floating point 35 | numbers](https://en.wikipedia.org/wiki/Floating-point_arithmetic) $\ne $ [real 36 | numbers](https://en.wikipedia.org/wiki/Real_number), they don't even cover all 37 | of the [rational numbers](https://en.wikipedia.org/wiki/Rational_number). This 38 | creates a number of challenges in numerical method and rendering is not immune 39 | to them. We see this in the need for a [fudge 40 | factor](https://en.wikipedia.org/wiki/Fudge_factor) to discard ray-intersections 41 | when computing shadows or reflections that are too close to the originating 42 | surface (i.e., false intersections due to numerical error). 43 | 44 | > **Question:** If we build a ray and a plane with floating point coefficients, 45 | > will the intersection point have floating point coefficients? What if we 46 | > consider rational coefficients? What if we consider a sphere instead of a 47 | > plane? 48 | > 49 | > **Hint:** Can we _exactly_ represent $1/3$ as a `double`? Can we represent 50 | > $\sqrt{2}$ as a rational? 51 | 52 | ### Dynamic Range & Burning 53 | 54 | Light obeys the [superposition 55 | principle](https://en.wikipedia.org/wiki/Superposition_principle). Simply put, 56 | the light reflected of some part of an objects is the _sum_ of contributions 57 | from light coming in all directions (e.g., from all light sources). If there are 58 | many bright lights in the scene and the object has a bright color, it is easy 59 | for this sum to add up to more than one. At first this seems counter-intuitive: 60 | How can we exceed 100% light? But this premise is false, the $1.0$ does not mean 61 | the physically brightest possible light in the world, but rather the brightest 62 | light our screen can display (or the brightest color we can store in our chosen 63 | image format). [High dynamic range (HDR) 64 | images](https://en.wikipedia.org/wiki/High-dynamic-range_imaging) store a larger 65 | range beyond this usual [0,1]. For this assignment, we will simply _clamp_ the 66 | total light values at a pixel to 1. 67 | 68 | This issue is compounded by the problem that the [Blinn-Phong 69 | shading](https://en.wikipedia.org/wiki/Blinn–Phong_shading_model) does not 70 | correctly [conserve energy](https://en.wikipedia.org/wiki/Energy_conservation) 71 | as happens with light in the physical world. 72 | 73 | ![Running `./raytracing ../data/bunny.json` should produce this image. 74 | Notice the ["burned out"](https://en.wikipedia.org/wiki/Burned_(image)) white 75 | regions where the collected light has been clamped to \[1,1,1\] 76 | (white).](images/bunny.png) 77 | 78 | > **Question:** Can we ever get a pixel value _less than zero_? 79 | > 80 | > **Hint:** Can a light be more than off? 81 | > 82 | > **Side note:** This doesn't stop crafty visual effects artists from using 83 | > "negative lights" to manipulate scenes for aesthetic purposes. 84 | 85 | 86 | ## Whitelist 87 | 88 | There are many ways to "multiply" two vectors. One way is to compute the 89 | [component-wise](https://en.wikipedia.org/wiki/Hadamard_product_(matrices)) 90 | multiplication: $\mathbf{c} = \mathbf{a} \circ \mathbf{b}$ or in index notation: 91 | $c_i = a_i b_i$. That is, multiply each corresponding component and store the 92 | result in the corresponding component of the output vector. Using the Eigen 93 | library this is accomplished by telling Eigen to treat each of the vectors as 94 | "array" (where [matrix multiplication](), [dot product](), [cross product]() 95 | would not make sense) and then using the usual `*` multiplication: 96 | 97 | ``` 98 | Eigen::Vector3d a,b; 99 | ... 100 | // Component-wise multiplication 101 | Eigen::Vector3d c = (a.array() * b.array()).matrix(); 102 | ``` 103 | 104 | The `.matrix()` converts the "array" view of the vector back to a "matrix" 105 | (i.e., vector) view of the vector. 106 | 107 | Eigen also has a built in way to normalize a vector (divide a vector by its 108 | length): `a.normalized()`. 109 | 110 | C++ standard library includes a value for $\infty $ via `#include `. For 111 | example, for `double` floating point, use `std::numeric_limits::infinity()`. 112 | 113 | ## Tasks 114 | 115 | ### `src/Plane.cpp`,
`src/Sphere.cpp`,
`src/Triangle.cpp`,
`src/TriangleSoup.cpp`,
`src/first_hit.cpp`,
`src/viewing_ray.cpp`,
`src/write_ppm.cpp` 116 | 117 | See the previous [ray casting 118 | assignment](https://github.com/alecjacobson/computer-graphics-ray-casting). 119 | 120 | ### `PointLight::direction` in `src/PointLight.cpp` 121 | 122 | Compute the direction to a point light source and its _parametric_ distance from 123 | a query point. 124 | 125 | ### `DirectionalLight::direction` in `src/DirectionalLight.cpp` 126 | 127 | Compute the direction to a direction light source and its _parametric_ distance from a 128 | query point (infinity). 129 | 130 | ### `src/raycolor.cpp` 131 | 132 | Make use of `first_hit.cpp` to shoot a ray into the scene, collect hit 133 | information and use this to return a color value. 134 | 135 | ### `src/blinn_phong_shading.cpp` 136 | 137 | Compute the lit color of a hit object in the scene using [Blinn-Phong shading 138 | model](https://en.wikipedia.org/wiki/Blinn–Phong_shading_model). This function 139 | should also shoot an additional ray to each light source to check for shadows. 140 | 141 | ![It is recommended to add and debug each term in your shading model. The 142 | ambient term will look like a faint object-ID image. The diffuse term will 143 | illuminate the scene, and create a dull, Lambertian look to each object. The 144 | specular term will add shiny highlights. Then, mask the diffuse and specular 145 | terms by checking for shadows. Finally, add a recursive call to account for 146 | mirror reflections.](images/sphere-and-plane.gif) 147 | 148 | ### `src/reflect.cpp` 149 | 150 | Given an "incoming" vector and a normal vector, compute the mirror reflected 151 | "outgoing" vector. 152 | 153 | ![`./raytracing ../data/sphere-packing.json` should produce this 154 | image of highly reflective, metallic looking 155 | surfaces.](images/sphere-packing.png) 156 | 157 | ### `src/creative.json` 158 | 159 | Be creative! Design a scene using any of the available Object types (spheres, 160 | planes, triangles, triangle soups), Light types (directional, point), Material 161 | parameters, colors (materials and/or lights), and don't forget about the camera 162 | parameters. 163 | 164 | The [.json format](https://en.wikipedia.org/wiki/JSON) is rather 165 | straightforward. But you may find this [validator](https://jsonlint.com) useful. 166 | 167 | > ### HW2 Solution 168 | > If you don't trust your solutions to the files from HW2: 169 | 170 | > src/Plane.cpp 171 | > src/Sphere.cpp 172 | > src/Triangle.cpp 173 | > src/TriangleSoup.cpp 174 | > src/first_hit.cpp 175 | > src/viewing_ray.cpp 176 | > src/write_ppm.cpp 177 | > 178 | > You can use precompiled binaries (provided for linux, mac, and windows) using a 179 | > the cmake command: 180 | > 181 | > mkdir build 182 | > cd build 183 | > cmake -DCMAKE_BUILD_TYPE=Debug -DHW2LIB_DIR=../lib/debug/linux/ .. 184 | > make 185 | > 186 | > This will use the library at `../lib/debug/linux/libhw2.a` instead of 187 | > compiling the above files in `src/`. 188 | > 189 | 190 | ------------------------------------------------------------------------ 191 | 192 | > **Pro Tip:** After you're confident that your program is working _correctly_, 193 | > you can dramatic improve the performance simply by enabling [compiler 194 | > optimization](https://en.wikipedia.org/wiki/Optimizing_compiler): 195 | > 196 | > ``` 197 | > mkdir build-release 198 | > cd build-release 199 | > cmake ../ -DCMAKE_BUILD_TYPE=Release 200 | > make 201 | > ``` 202 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Computer Graphics – Ray Tracing 2 | 3 | > **To get started:** Clone this repository and its submodule using 4 | > 5 | > git clone --recursive http://github.com/alecjacobson/computer-graphics-ray-tracing.git 6 | > 7 | > **Do not fork:** Clicking "Fork" will create a _public_ repository. If you'd like to use GitHub while you work on your assignment, then mirror this repo as a new _private_ repository: https://stackoverflow.com/questions/10065526/github-how-to-make-a-fork-of-public-repository-private 8 | 9 | ## Background 10 | 11 | ### Read Sections 4.5-4.9 of _Fundamentals of Computer Graphics (4th Edition)_. 12 | 13 | Many of the classes and functions of this assignment are borrowed or adapted 14 | from the previous [ray casting assignment](https://github.com/alecjacobson/computer-graphics-ray-casting). 15 | 16 | Unlike that assignment, this [ray 17 | tracer](https://en.wikipedia.org/wiki/Ray_tracing_(graphics)) will produce 18 | _approximately_ accurate renderings of scenes illuminated with light. 19 | Ultimately, the shading and lighting models here are _useful_ hacks. The basic 20 | [recursive](https://en.wikipedia.org/wiki/Ray_tracing_(graphics)#Recursive_ray_tracing_algorithm) 21 | structure of the program is core to many methods for rendering with [global 22 | illumination](https://en.wikipedia.org/wiki/Global_illumination) effects (e.g., 23 | shadows, reflections, etc.). 24 | 25 | ![Running `./raytracing ../data/sphere-and-plane.json` should produce this image.](images/sphere-and-plane.png) 26 | 27 | ## Floating point numbers 28 | 29 | For this assignment we will use the `Eigen::Vector3d` to represent points and 30 | vectors, but _also_ RGB colors. For all computation (before finally writing the 31 | .ppm file) we will use double precision floating point numbers and `0` will 32 | represent no light and `1` will represent the brightest color we can display. 33 | 34 | [Floating point 35 | numbers](https://en.wikipedia.org/wiki/Floating-point_arithmetic) [real 36 | numbers](https://en.wikipedia.org/wiki/Real_number), they don't even cover all 37 | of the [rational numbers](https://en.wikipedia.org/wiki/Rational_number). This 38 | creates a number of challenges in numerical method and rendering is not immune 39 | to them. We see this in the need for a [fudge 40 | factor](https://en.wikipedia.org/wiki/Fudge_factor) to discard ray-intersections 41 | when computing shadows or reflections that are too close to the originating 42 | surface (i.e., false intersections due to numerical error). 43 | 44 | > **Question:** If we build a ray and a plane with floating point coefficients, 45 | > will the intersection point have floating point coefficients? What if we 46 | > consider rational coefficients? What if we consider a sphere instead of a 47 | > plane? 48 | > 49 | > **Hint:** Can we _exactly_ represent as a `double`? Can we represent 50 | > as a rational? 51 | 52 | ### Dynamic Range & Burning 53 | 54 | Light obeys the [superposition 55 | principle](https://en.wikipedia.org/wiki/Superposition_principle). Simply put, 56 | the light reflected of some part of an objects is the _sum_ of contributions 57 | from light coming in all directions (e.g., from all light sources). If there are 58 | many bright lights in the scene and the object has a bright color, it is easy 59 | for this sum to add up to more than one. At first this seems counter-intuitive: 60 | How can we exceed 100% light? But this premise is false, the does not mean 61 | the physically brightest possible light in the world, but rather the brightest 62 | light our screen can display (or the brightest color we can store in our chosen 63 | image format). [High dynamic range (HDR) 64 | images](https://en.wikipedia.org/wiki/High-dynamic-range_imaging) store a larger 65 | range beyond this usual [0,1]. For this assignment, we will simply _clamp_ the 66 | total light values at a pixel to 1. 67 | 68 | This issue is compounded by the problem that the [Blinn-Phong 69 | shading](https://en.wikipedia.org/wiki/Blinn–Phong_shading_model) does not 70 | correctly [conserve energy](https://en.wikipedia.org/wiki/Energy_conservation) 71 | as happens with light in the physical world. 72 | 73 | ![Running `./raytracing ../data/bunny.json` should produce this image. 74 | Notice the ["burned out"](https://en.wikipedia.org/wiki/Burned_(image)) white 75 | regions where the collected light has been clamped to \[1,1,1\] 76 | (white).](images/bunny.png) 77 | 78 | > **Question:** Can we ever get a pixel value _less than zero_? 79 | > 80 | > **Hint:** Can a light be more than off? 81 | > 82 | > **Side note:** This doesn't stop crafty visual effects artists from using 83 | > "negative lights" to manipulate scenes for aesthetic purposes. 84 | 85 | 86 | ## Whitelist 87 | 88 | There are many ways to "multiply" two vectors. One way is to compute the 89 | [component-wise](https://en.wikipedia.org/wiki/Hadamard_product_(matrices)) 90 | multiplication: or in index notation: 91 | . That is, multiply each corresponding component and store the 92 | result in the corresponding component of the output vector. Using the Eigen 93 | library this is accomplished by telling Eigen to treat each of the vectors as 94 | "array" (where [matrix multiplication](), [dot product](), [cross product]() 95 | would not make sense) and then using the usual `*` multiplication: 96 | 97 | ``` 98 | Eigen::Vector3d a,b; 99 | ... 100 | // Component-wise multiplication 101 | Eigen::Vector3d c = (a.array() * b.array()).matrix(); 102 | ``` 103 | 104 | The `.matrix()` converts the "array" view of the vector back to a "matrix" 105 | (i.e., vector) view of the vector. 106 | 107 | Eigen also has a built in way to normalize a vector (divide a vector by its 108 | length): `a.normalized()`. 109 | 110 | C++ standard library includes a value for via `#include `. For 111 | example, for `double` floating point, use `std::numeric_limits::infinity()`. 112 | 113 | ## Tasks 114 | 115 | ### `src/Plane.cpp`,
`src/Sphere.cpp`,
`src/Triangle.cpp`,
`src/TriangleSoup.cpp`,
`src/first_hit.cpp`,
`src/viewing_ray.cpp`,
`src/write_ppm.cpp` 116 | 117 | See the previous [ray casting 118 | assignment](https://github.com/alecjacobson/computer-graphics-ray-casting). 119 | 120 | ### `PointLight::direction` in `src/PointLight.cpp` 121 | 122 | Compute the direction to a point light source and its _parametric_ distance from 123 | a query point. 124 | 125 | ### `DirectionalLight::direction` in `src/DirectionalLight.cpp` 126 | 127 | Compute the direction to a direction light source and its _parametric_ distance from a 128 | query point (infinity). 129 | 130 | ### `src/raycolor.cpp` 131 | 132 | Make use of `first_hit.cpp` to shoot a ray into the scene, collect hit 133 | information and use this to return a color value. 134 | 135 | ### `src/blinn_phong_shading.cpp` 136 | 137 | Compute the lit color of a hit object in the scene using [Blinn-Phong shading 138 | model](https://en.wikipedia.org/wiki/Blinn–Phong_shading_model). This function 139 | should also shoot an additional ray to each light source to check for shadows. 140 | 141 | ![It is recommended to add and debug each term in your shading model. The 142 | ambient term will look like a faint object-ID image. The diffuse term will 143 | illuminate the scene, and create a dull, Lambertian look to each object. The 144 | specular term will add shiny highlights. Then, mask the diffuse and specular 145 | terms by checking for shadows. Finally, add a recursive call to account for 146 | mirror reflections.](images/sphere-and-plane.gif) 147 | 148 | ### `src/reflect.cpp` 149 | 150 | Given an "incoming" vector and a normal vector, compute the mirror reflected 151 | "outgoing" vector. 152 | 153 | ![`./raytracing ../data/sphere-packing.json` should produce this 154 | image of highly reflective, metallic looking 155 | surfaces.](images/sphere-packing.png) 156 | 157 | ### `src/creative.json` 158 | 159 | Be creative! Design a scene using any of the available Object types (spheres, 160 | planes, triangles, triangle soups), Light types (directional, point), Material 161 | parameters, colors (materials and/or lights), and don't forget about the camera 162 | parameters. 163 | 164 | The [.json format](https://en.wikipedia.org/wiki/JSON) is rather 165 | straightforward. But you may find this [validator](https://jsonlint.com) useful. 166 | 167 | > ### HW2 Solution 168 | > If you don't trust your solutions to the files from HW2: 169 | 170 | > src/Plane.cpp 171 | > src/Sphere.cpp 172 | > src/Triangle.cpp 173 | > src/TriangleSoup.cpp 174 | > src/first_hit.cpp 175 | > src/viewing_ray.cpp 176 | > src/write_ppm.cpp 177 | > 178 | > You can use precompiled binaries (provided for linux, mac, and windows) using a 179 | > the cmake command: 180 | > 181 | > mkdir build 182 | > cd build 183 | > cmake -DCMAKE_BUILD_TYPE=Debug -DHW2LIB_DIR=../lib/debug/linux/ .. 184 | > make 185 | > 186 | > This will use the library at `../lib/debug/linux/libhw2.a` instead of 187 | > compiling the above files in `src/`. 188 | > 189 | 190 | ------------------------------------------------------------------------ 191 | 192 | > **Pro Tip:** After you're confident that your program is working _correctly_, 193 | > you can dramatic improve the performance simply by enabling [compiler 194 | > optimization](https://en.wikipedia.org/wiki/Optimizing_compiler): 195 | > 196 | > ``` 197 | > mkdir build-release 198 | > cd build-release 199 | > cmake ../ -DCMAKE_BUILD_TYPE=Release 200 | > make 201 | > ``` 202 | -------------------------------------------------------------------------------- /include/readSTL.h: -------------------------------------------------------------------------------- 1 | // This file is part of libigl, a simple c++ geometry processing library. 2 | // 3 | // Copyright (C) 2013 Alec Jacobson 4 | // 5 | // This Source Code Form is subject to the terms of the Mozilla Public License 6 | // v. 2.0. If a copy of the MPL was not distributed with this file, You can 7 | // obtain one at http://mozilla.org/MPL/2.0/. 8 | #ifndef IGL_READSTL_H 9 | #define IGL_READSTL_H 10 | # define IGL_INLINE inline 11 | 12 | #ifndef IGL_NO_EIGEN 13 | # include 14 | #endif 15 | #include 16 | #include 17 | #include 18 | 19 | namespace igl 20 | { 21 | // Inputs: 22 | // stl_file pointer to already opened .stl file 23 | // Outputs: 24 | // stl_file closed file 25 | template 26 | IGL_INLINE bool readSTL( 27 | FILE * stl_file, 28 | std::vector > & V, 29 | std::vector > & F, 30 | std::vector > & N); 31 | template 32 | IGL_INLINE bool readSTL( 33 | const std::string & filename, 34 | std::vector > & V, 35 | std::vector > & F, 36 | std::vector > & N); 37 | } 38 | 39 | #include 40 | 41 | template 42 | IGL_INLINE bool igl::readSTL( 43 | const std::string & filename, 44 | std::vector > & V, 45 | std::vector > & F, 46 | std::vector > & N) 47 | { 48 | using namespace std; 49 | // Should test for ascii 50 | 51 | // Open file, and check for error 52 | FILE * stl_file = fopen(filename.c_str(),"rb"); 53 | if(NULL==stl_file) 54 | { 55 | fprintf(stderr,"IOError: %s could not be opened...\n", 56 | filename.c_str()); 57 | return false; 58 | } 59 | return readSTL(stl_file,V,F,N); 60 | } 61 | 62 | template 63 | IGL_INLINE bool igl::readSTL( 64 | FILE * stl_file, 65 | std::vector > & V, 66 | std::vector > & F, 67 | std::vector > & N) 68 | { 69 | using namespace std; 70 | //stl_file = freopen(NULL,"rb",stl_file); 71 | if(NULL==stl_file) 72 | { 73 | fprintf(stderr,"IOError: stl file could not be reopened as binary (1) ...\n"); 74 | return false; 75 | } 76 | 77 | V.clear(); 78 | F.clear(); 79 | N.clear(); 80 | 81 | 82 | // Specifically 80 character header 83 | char header[80]; 84 | char solid[80]; 85 | bool is_ascii = true; 86 | if(fread(header,1,80,stl_file) != 80) 87 | { 88 | cerr<<"IOError: too short (1)."<(buf); 106 | fseek(stl_file,0,SEEK_END); 107 | int file_size = ftell(stl_file); 108 | if(file_size == 80 + 4 + (4*12 + 2) * num_faces) 109 | { 110 | is_ascii = false; 111 | }else 112 | { 113 | is_ascii = true; 114 | } 115 | } 116 | 117 | if(is_ascii) 118 | { 119 | // Rewind to end of header 120 | //stl_file = fopen(filename.c_str(),"r"); 121 | //stl_file = freopen(NULL,"r",stl_file); 122 | fseek(stl_file, 0, SEEK_SET); 123 | if(NULL==stl_file) 124 | { 125 | fprintf(stderr,"IOError: stl file could not be reopened as ascii ...\n"); 126 | return false; 127 | } 128 | // Read 80 header 129 | // Eat file name 130 | #ifndef IGL_LINE_MAX 131 | # define IGL_LINE_MAX 2048 132 | #endif 133 | char name[IGL_LINE_MAX]; 134 | if(NULL==fgets(name,IGL_LINE_MAX,stl_file)) 135 | { 136 | cerr<<"IOError: ascii too short (2)."< n(3); 145 | double nd[3]; 146 | ret = fscanf(stl_file,"%s %s %lg %lg %lg",facet,normal,nd,nd+1,nd+2); 147 | if(string("endsolid") == facet) 148 | { 149 | break; 150 | } 151 | if(ret != 5 || 152 | !(string("facet") == facet || 153 | string("faced") == facet) || 154 | string("normal") != normal) 155 | { 156 | cout<<"facet: "< f; 172 | while(true) 173 | { 174 | char word[IGL_LINE_MAX]; 175 | int ret = fscanf(stl_file,"%s",word); 176 | if(ret == 1 && string("endloop") == word) 177 | { 178 | break; 179 | }else if(ret == 1 && string("vertex") == word) 180 | { 181 | vector v(3); 182 | double vd[3]; 183 | int ret = fscanf(stl_file,"%lg %lg %lg",vd,vd+1,vd+2); 184 | if(ret != 3) 185 | { 186 | cerr<<"IOError: bad format (3)."<(3,0)); 230 | N.resize(num_tri,vector(3,0)); 231 | F.resize(num_tri,vector(3,0)); 232 | for(int t = 0;t<(int)num_tri;t++) 233 | { 234 | // Read normal 235 | float n[3]; 236 | if(fread(n,sizeof(float),3,stl_file)!=3) 237 | { 238 | cerr<<"IOError: bad format (8)."<, Eigen::Matrix, Eigen::Matrix >(std::basic_string, std::allocator > const&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&); 278 | // generated by autoexplicit.sh 279 | template bool igl::readSTL, Eigen::Matrix, Eigen::Matrix >(std::basic_string, std::allocator > const&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&); 280 | // generated by autoexplicit.sh 281 | template bool igl::readSTL, Eigen::Matrix, Eigen::Matrix >(std::basic_string, std::allocator > const&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&); 282 | // generated by autoexplicit.sh 283 | template bool igl::readSTL, Eigen::Matrix, Eigen::Matrix >(std::basic_string, std::allocator > const&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&); 284 | template bool igl::readSTL, Eigen::Matrix, Eigen::Matrix >(std::basic_string, std::allocator > const&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&); 285 | template bool igl::readSTL, Eigen::Matrix, Eigen::Matrix >(std::basic_string, std::allocator > const&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&); 286 | #endif 287 | 288 | 289 | #endif 290 | 291 | 292 | -------------------------------------------------------------------------------- /css/github-markdown.css: -------------------------------------------------------------------------------- 1 | @font-face { 2 | font-family: octicons-link; 3 | src: url(data:font/woff;charset=utf-8;base64,d09GRgABAAAAAAZwABAAAAAACFQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABEU0lHAAAGaAAAAAgAAAAIAAAAAUdTVUIAAAZcAAAACgAAAAoAAQAAT1MvMgAAAyQAAABJAAAAYFYEU3RjbWFwAAADcAAAAEUAAACAAJThvmN2dCAAAATkAAAABAAAAAQAAAAAZnBnbQAAA7gAAACyAAABCUM+8IhnYXNwAAAGTAAAABAAAAAQABoAI2dseWYAAAFsAAABPAAAAZwcEq9taGVhZAAAAsgAAAA0AAAANgh4a91oaGVhAAADCAAAABoAAAAkCA8DRGhtdHgAAAL8AAAADAAAAAwGAACfbG9jYQAAAsAAAAAIAAAACABiATBtYXhwAAACqAAAABgAAAAgAA8ASm5hbWUAAAToAAABQgAAAlXu73sOcG9zdAAABiwAAAAeAAAAME3QpOBwcmVwAAAEbAAAAHYAAAB/aFGpk3jaTY6xa8JAGMW/O62BDi0tJLYQincXEypYIiGJjSgHniQ6umTsUEyLm5BV6NDBP8Tpts6F0v+k/0an2i+itHDw3v2+9+DBKTzsJNnWJNTgHEy4BgG3EMI9DCEDOGEXzDADU5hBKMIgNPZqoD3SilVaXZCER3/I7AtxEJLtzzuZfI+VVkprxTlXShWKb3TBecG11rwoNlmmn1P2WYcJczl32etSpKnziC7lQyWe1smVPy/Lt7Kc+0vWY/gAgIIEqAN9we0pwKXreiMasxvabDQMM4riO+qxM2ogwDGOZTXxwxDiycQIcoYFBLj5K3EIaSctAq2kTYiw+ymhce7vwM9jSqO8JyVd5RH9gyTt2+J/yUmYlIR0s04n6+7Vm1ozezUeLEaUjhaDSuXHwVRgvLJn1tQ7xiuVv/ocTRF42mNgZGBgYGbwZOBiAAFGJBIMAAizAFoAAABiAGIAznjaY2BkYGAA4in8zwXi+W2+MjCzMIDApSwvXzC97Z4Ig8N/BxYGZgcgl52BCSQKAA3jCV8CAABfAAAAAAQAAEB42mNgZGBg4f3vACQZQABIMjKgAmYAKEgBXgAAeNpjYGY6wTiBgZWBg2kmUxoDA4MPhGZMYzBi1AHygVLYQUCaawqDA4PChxhmh/8ODDEsvAwHgMKMIDnGL0x7gJQCAwMAJd4MFwAAAHjaY2BgYGaA4DAGRgYQkAHyGMF8NgYrIM3JIAGVYYDT+AEjAwuDFpBmA9KMDEwMCh9i/v8H8sH0/4dQc1iAmAkALaUKLgAAAHjaTY9LDsIgEIbtgqHUPpDi3gPoBVyRTmTddOmqTXThEXqrob2gQ1FjwpDvfwCBdmdXC5AVKFu3e5MfNFJ29KTQT48Ob9/lqYwOGZxeUelN2U2R6+cArgtCJpauW7UQBqnFkUsjAY/kOU1cP+DAgvxwn1chZDwUbd6CFimGXwzwF6tPbFIcjEl+vvmM/byA48e6tWrKArm4ZJlCbdsrxksL1AwWn/yBSJKpYbq8AXaaTb8AAHja28jAwOC00ZrBeQNDQOWO//sdBBgYGRiYWYAEELEwMTE4uzo5Zzo5b2BxdnFOcALxNjA6b2ByTswC8jYwg0VlNuoCTWAMqNzMzsoK1rEhNqByEyerg5PMJlYuVueETKcd/89uBpnpvIEVomeHLoMsAAe1Id4AAAAAAAB42oWQT07CQBTGv0JBhagk7HQzKxca2sJCE1hDt4QF+9JOS0nbaaYDCQfwCJ7Au3AHj+LO13FMmm6cl7785vven0kBjHCBhfpYuNa5Ph1c0e2Xu3jEvWG7UdPDLZ4N92nOm+EBXuAbHmIMSRMs+4aUEd4Nd3CHD8NdvOLTsA2GL8M9PODbcL+hD7C1xoaHeLJSEao0FEW14ckxC+TU8TxvsY6X0eLPmRhry2WVioLpkrbp84LLQPGI7c6sOiUzpWIWS5GzlSgUzzLBSikOPFTOXqly7rqx0Z1Q5BAIoZBSFihQYQOOBEdkCOgXTOHA07HAGjGWiIjaPZNW13/+lm6S9FT7rLHFJ6fQbkATOG1j2OFMucKJJsxIVfQORl+9Jyda6Sl1dUYhSCm1dyClfoeDve4qMYdLEbfqHf3O/AdDumsjAAB42mNgYoAAZQYjBmyAGYQZmdhL8zLdDEydARfoAqIAAAABAAMABwAKABMAB///AA8AAQAAAAAAAAAAAAAAAAABAAAAAA==) format('woff'); 4 | } 5 | 6 | .markdown-body { 7 | -ms-text-size-adjust: 100%; 8 | -webkit-text-size-adjust: 100%; 9 | line-height: 1.5; 10 | color: #24292e; 11 | font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol"; 12 | font-size: 16px; 13 | line-height: 1.5; 14 | word-wrap: break-word; 15 | } 16 | 17 | .markdown-body .pl-c { 18 | color: #6a737d; 19 | } 20 | 21 | .markdown-body .pl-c1, 22 | .markdown-body .pl-s .pl-v { 23 | color: #005cc5; 24 | } 25 | 26 | .markdown-body .pl-e, 27 | .markdown-body .pl-en { 28 | color: #6f42c1; 29 | } 30 | 31 | .markdown-body .pl-smi, 32 | .markdown-body .pl-s .pl-s1 { 33 | color: #24292e; 34 | } 35 | 36 | .markdown-body .pl-ent { 37 | color: #22863a; 38 | } 39 | 40 | .markdown-body .pl-k { 41 | color: #d73a49; 42 | } 43 | 44 | .markdown-body .pl-s, 45 | .markdown-body .pl-pds, 46 | .markdown-body .pl-s .pl-pse .pl-s1, 47 | .markdown-body .pl-sr, 48 | .markdown-body .pl-sr .pl-cce, 49 | .markdown-body .pl-sr .pl-sre, 50 | .markdown-body .pl-sr .pl-sra { 51 | color: #032f62; 52 | } 53 | 54 | .markdown-body .pl-v, 55 | .markdown-body .pl-smw { 56 | color: #e36209; 57 | } 58 | 59 | .markdown-body .pl-bu { 60 | color: #b31d28; 61 | } 62 | 63 | .markdown-body .pl-ii { 64 | color: #fafbfc; 65 | background-color: #b31d28; 66 | } 67 | 68 | .markdown-body .pl-c2 { 69 | color: #fafbfc; 70 | background-color: #d73a49; 71 | } 72 | 73 | .markdown-body .pl-c2::before { 74 | content: "^M"; 75 | } 76 | 77 | .markdown-body .pl-sr .pl-cce { 78 | font-weight: bold; 79 | color: #22863a; 80 | } 81 | 82 | .markdown-body .pl-ml { 83 | color: #735c0f; 84 | } 85 | 86 | .markdown-body .pl-mh, 87 | .markdown-body .pl-mh .pl-en, 88 | .markdown-body .pl-ms { 89 | font-weight: bold; 90 | color: #005cc5; 91 | } 92 | 93 | .markdown-body .pl-mi { 94 | font-style: italic; 95 | color: #24292e; 96 | } 97 | 98 | .markdown-body .pl-mb { 99 | font-weight: bold; 100 | color: #24292e; 101 | } 102 | 103 | .markdown-body .pl-md { 104 | color: #b31d28; 105 | background-color: #ffeef0; 106 | } 107 | 108 | .markdown-body .pl-mi1 { 109 | color: #22863a; 110 | background-color: #f0fff4; 111 | } 112 | 113 | .markdown-body .pl-mc { 114 | color: #e36209; 115 | background-color: #ffebda; 116 | } 117 | 118 | .markdown-body .pl-mi2 { 119 | color: #f6f8fa; 120 | background-color: #005cc5; 121 | } 122 | 123 | .markdown-body .pl-mdr { 124 | font-weight: bold; 125 | color: #6f42c1; 126 | } 127 | 128 | .markdown-body .pl-ba { 129 | color: #586069; 130 | } 131 | 132 | .markdown-body .pl-sg { 133 | color: #959da5; 134 | } 135 | 136 | .markdown-body .pl-corl { 137 | text-decoration: underline; 138 | color: #032f62; 139 | } 140 | 141 | .markdown-body .octicon { 142 | display: inline-block; 143 | vertical-align: text-top; 144 | fill: currentColor; 145 | } 146 | 147 | .markdown-body a { 148 | background-color: transparent; 149 | } 150 | 151 | .markdown-body a:active, 152 | .markdown-body a:hover { 153 | outline-width: 0; 154 | } 155 | 156 | .markdown-body strong { 157 | font-weight: inherit; 158 | } 159 | 160 | .markdown-body strong { 161 | font-weight: bolder; 162 | } 163 | 164 | .markdown-body h1 { 165 | font-size: 2em; 166 | margin: 0.67em 0; 167 | } 168 | 169 | .markdown-body img { 170 | border-style: none; 171 | } 172 | 173 | .markdown-body code, 174 | .markdown-body kbd, 175 | .markdown-body pre { 176 | font-family: monospace, monospace; 177 | font-size: 1em; 178 | } 179 | 180 | .markdown-body hr { 181 | box-sizing: content-box; 182 | height: 0; 183 | overflow: visible; 184 | } 185 | 186 | .markdown-body input { 187 | font: inherit; 188 | margin: 0; 189 | } 190 | 191 | .markdown-body input { 192 | overflow: visible; 193 | } 194 | 195 | .markdown-body [type="checkbox"] { 196 | box-sizing: border-box; 197 | padding: 0; 198 | } 199 | 200 | .markdown-body * { 201 | box-sizing: border-box; 202 | } 203 | 204 | .markdown-body input { 205 | font-family: inherit; 206 | font-size: inherit; 207 | line-height: inherit; 208 | } 209 | 210 | .markdown-body a { 211 | color: #0366d6; 212 | text-decoration: none; 213 | } 214 | 215 | .markdown-body a:hover { 216 | text-decoration: underline; 217 | } 218 | 219 | .markdown-body strong { 220 | font-weight: 600; 221 | } 222 | 223 | .markdown-body hr { 224 | height: 0; 225 | margin: 15px 0; 226 | overflow: hidden; 227 | background: transparent; 228 | border: 0; 229 | border-bottom: 1px solid #dfe2e5; 230 | } 231 | 232 | .markdown-body hr::before { 233 | display: table; 234 | content: ""; 235 | } 236 | 237 | .markdown-body hr::after { 238 | display: table; 239 | clear: both; 240 | content: ""; 241 | } 242 | 243 | .markdown-body table { 244 | border-spacing: 0; 245 | border-collapse: collapse; 246 | } 247 | 248 | .markdown-body td, 249 | .markdown-body th { 250 | padding: 0; 251 | } 252 | 253 | .markdown-body h1, 254 | .markdown-body h2, 255 | .markdown-body h3, 256 | .markdown-body h4, 257 | .markdown-body h5, 258 | .markdown-body h6 { 259 | margin-top: 0; 260 | margin-bottom: 0; 261 | } 262 | 263 | .markdown-body h1 { 264 | font-size: 32px; 265 | font-weight: 600; 266 | } 267 | 268 | .markdown-body h2 { 269 | font-size: 24px; 270 | font-weight: 600; 271 | } 272 | 273 | .markdown-body h3 { 274 | font-size: 20px; 275 | font-weight: 600; 276 | } 277 | 278 | .markdown-body h4 { 279 | font-size: 16px; 280 | font-weight: 600; 281 | } 282 | 283 | .markdown-body h5 { 284 | font-size: 14px; 285 | font-weight: 600; 286 | } 287 | 288 | .markdown-body h6 { 289 | font-size: 12px; 290 | font-weight: 600; 291 | } 292 | 293 | .markdown-body p { 294 | margin-top: 0; 295 | margin-bottom: 10px; 296 | } 297 | 298 | .markdown-body blockquote { 299 | margin: 0; 300 | } 301 | 302 | .markdown-body ul, 303 | .markdown-body ol { 304 | padding-left: 0; 305 | margin-top: 0; 306 | margin-bottom: 0; 307 | } 308 | 309 | .markdown-body ol ol, 310 | .markdown-body ul ol { 311 | list-style-type: lower-roman; 312 | } 313 | 314 | .markdown-body ul ul ol, 315 | .markdown-body ul ol ol, 316 | .markdown-body ol ul ol, 317 | .markdown-body ol ol ol { 318 | list-style-type: lower-alpha; 319 | } 320 | 321 | .markdown-body dd { 322 | margin-left: 0; 323 | } 324 | 325 | .markdown-body code { 326 | font-family: "SFMono-Regular", Consolas, "Liberation Mono", Menlo, Courier, monospace; 327 | font-size: 12px; 328 | } 329 | 330 | .markdown-body pre { 331 | margin-top: 0; 332 | margin-bottom: 0; 333 | font-family: "SFMono-Regular", Consolas, "Liberation Mono", Menlo, Courier, monospace; 334 | font-size: 12px; 335 | } 336 | 337 | .markdown-body .octicon { 338 | vertical-align: text-bottom; 339 | } 340 | 341 | .markdown-body .pl-0 { 342 | padding-left: 0 !important; 343 | } 344 | 345 | .markdown-body .pl-1 { 346 | padding-left: 4px !important; 347 | } 348 | 349 | .markdown-body .pl-2 { 350 | padding-left: 8px !important; 351 | } 352 | 353 | .markdown-body .pl-3 { 354 | padding-left: 16px !important; 355 | } 356 | 357 | .markdown-body .pl-4 { 358 | padding-left: 24px !important; 359 | } 360 | 361 | .markdown-body .pl-5 { 362 | padding-left: 32px !important; 363 | } 364 | 365 | .markdown-body .pl-6 { 366 | padding-left: 40px !important; 367 | } 368 | 369 | .markdown-body::before { 370 | display: table; 371 | content: ""; 372 | } 373 | 374 | .markdown-body::after { 375 | display: table; 376 | clear: both; 377 | content: ""; 378 | } 379 | 380 | .markdown-body>*:first-child { 381 | margin-top: 0 !important; 382 | } 383 | 384 | .markdown-body>*:last-child { 385 | margin-bottom: 0 !important; 386 | } 387 | 388 | .markdown-body a:not([href]) { 389 | color: inherit; 390 | text-decoration: none; 391 | } 392 | 393 | .markdown-body .anchor { 394 | float: left; 395 | padding-right: 4px; 396 | margin-left: -20px; 397 | line-height: 1; 398 | } 399 | 400 | .markdown-body .anchor:focus { 401 | outline: none; 402 | } 403 | 404 | .markdown-body p, 405 | .markdown-body blockquote, 406 | .markdown-body ul, 407 | .markdown-body ol, 408 | .markdown-body dl, 409 | .markdown-body table, 410 | .markdown-body pre { 411 | margin-top: 0; 412 | margin-bottom: 16px; 413 | } 414 | 415 | .markdown-body hr { 416 | height: 0.25em; 417 | padding: 0; 418 | margin: 24px 0; 419 | background-color: #e1e4e8; 420 | border: 0; 421 | } 422 | 423 | .markdown-body blockquote { 424 | padding: 0 1em; 425 | color: #6a737d; 426 | border-left: 0.25em solid #dfe2e5; 427 | } 428 | 429 | .markdown-body blockquote>:first-child { 430 | margin-top: 0; 431 | } 432 | 433 | .markdown-body blockquote>:last-child { 434 | margin-bottom: 0; 435 | } 436 | 437 | .markdown-body kbd { 438 | display: inline-block; 439 | padding: 3px 5px; 440 | font-size: 11px; 441 | line-height: 10px; 442 | color: #444d56; 443 | vertical-align: middle; 444 | background-color: #fafbfc; 445 | border: solid 1px #c6cbd1; 446 | border-bottom-color: #959da5; 447 | border-radius: 3px; 448 | box-shadow: inset 0 -1px 0 #959da5; 449 | } 450 | 451 | .markdown-body h1, 452 | .markdown-body h2, 453 | .markdown-body h3, 454 | .markdown-body h4, 455 | .markdown-body h5, 456 | .markdown-body h6 { 457 | margin-top: 24px; 458 | margin-bottom: 16px; 459 | font-weight: 600; 460 | line-height: 1.25; 461 | } 462 | 463 | .markdown-body h1 .octicon-link, 464 | .markdown-body h2 .octicon-link, 465 | .markdown-body h3 .octicon-link, 466 | .markdown-body h4 .octicon-link, 467 | .markdown-body h5 .octicon-link, 468 | .markdown-body h6 .octicon-link { 469 | color: #1b1f23; 470 | vertical-align: middle; 471 | visibility: hidden; 472 | } 473 | 474 | .markdown-body h1:hover .anchor, 475 | .markdown-body h2:hover .anchor, 476 | .markdown-body h3:hover .anchor, 477 | .markdown-body h4:hover .anchor, 478 | .markdown-body h5:hover .anchor, 479 | .markdown-body h6:hover .anchor { 480 | text-decoration: none; 481 | } 482 | 483 | .markdown-body h1:hover .anchor .octicon-link, 484 | .markdown-body h2:hover .anchor .octicon-link, 485 | .markdown-body h3:hover .anchor .octicon-link, 486 | .markdown-body h4:hover .anchor .octicon-link, 487 | .markdown-body h5:hover .anchor .octicon-link, 488 | .markdown-body h6:hover .anchor .octicon-link { 489 | visibility: visible; 490 | } 491 | 492 | .markdown-body h1 { 493 | padding-bottom: 0.3em; 494 | font-size: 2em; 495 | border-bottom: 1px solid #eaecef; 496 | } 497 | 498 | .markdown-body h2 { 499 | padding-bottom: 0.3em; 500 | font-size: 1.5em; 501 | border-bottom: 1px solid #eaecef; 502 | } 503 | 504 | .markdown-body h3 { 505 | font-size: 1.25em; 506 | } 507 | 508 | .markdown-body h4 { 509 | font-size: 1em; 510 | } 511 | 512 | .markdown-body h5 { 513 | font-size: 0.875em; 514 | } 515 | 516 | .markdown-body h6 { 517 | font-size: 0.85em; 518 | color: #6a737d; 519 | } 520 | 521 | .markdown-body ul, 522 | .markdown-body ol { 523 | padding-left: 2em; 524 | } 525 | 526 | .markdown-body ul ul, 527 | .markdown-body ul ol, 528 | .markdown-body ol ol, 529 | .markdown-body ol ul { 530 | margin-top: 0; 531 | margin-bottom: 0; 532 | } 533 | 534 | .markdown-body li { 535 | word-wrap: break-all; 536 | } 537 | 538 | .markdown-body li>p { 539 | margin-top: 16px; 540 | } 541 | 542 | .markdown-body li+li { 543 | margin-top: 0.25em; 544 | } 545 | 546 | .markdown-body dl { 547 | padding: 0; 548 | } 549 | 550 | .markdown-body dl dt { 551 | padding: 0; 552 | margin-top: 16px; 553 | font-size: 1em; 554 | font-style: italic; 555 | font-weight: 600; 556 | } 557 | 558 | .markdown-body dl dd { 559 | padding: 0 16px; 560 | margin-bottom: 16px; 561 | } 562 | 563 | .markdown-body table { 564 | display: block; 565 | width: 100%; 566 | overflow: auto; 567 | } 568 | 569 | .markdown-body table th { 570 | font-weight: 600; 571 | } 572 | 573 | .markdown-body table th, 574 | .markdown-body table td { 575 | padding: 6px 13px; 576 | border: 1px solid #dfe2e5; 577 | } 578 | 579 | .markdown-body table tr { 580 | background-color: #fff; 581 | border-top: 1px solid #c6cbd1; 582 | } 583 | 584 | .markdown-body table tr:nth-child(2n) { 585 | background-color: #f6f8fa; 586 | } 587 | 588 | .markdown-body img { 589 | max-width: 100%; 590 | box-sizing: content-box; 591 | background-color: #fff; 592 | } 593 | 594 | .markdown-body img[align=right] { 595 | padding-left: 20px; 596 | } 597 | 598 | .markdown-body img[align=left] { 599 | padding-right: 20px; 600 | } 601 | 602 | .markdown-body code { 603 | padding: 0.2em 0.4em; 604 | margin: 0; 605 | font-size: 85%; 606 | background-color: rgba(27,31,35,0.05); 607 | border-radius: 3px; 608 | } 609 | 610 | .markdown-body pre { 611 | word-wrap: normal; 612 | } 613 | 614 | .markdown-body pre>code { 615 | padding: 0; 616 | margin: 0; 617 | font-size: 100%; 618 | word-break: normal; 619 | white-space: pre; 620 | background: transparent; 621 | border: 0; 622 | } 623 | 624 | .markdown-body .highlight { 625 | margin-bottom: 16px; 626 | } 627 | 628 | .markdown-body .highlight pre { 629 | margin-bottom: 0; 630 | word-break: normal; 631 | } 632 | 633 | .markdown-body .highlight pre, 634 | .markdown-body pre { 635 | padding: 16px; 636 | overflow: auto; 637 | font-size: 85%; 638 | line-height: 1.45; 639 | background-color: #f6f8fa; 640 | border-radius: 3px; 641 | } 642 | 643 | .markdown-body pre code { 644 | display: inline; 645 | max-width: auto; 646 | padding: 0; 647 | margin: 0; 648 | overflow: visible; 649 | line-height: inherit; 650 | word-wrap: normal; 651 | background-color: transparent; 652 | border: 0; 653 | } 654 | 655 | .markdown-body .full-commit .btn-outline:not(:disabled):hover { 656 | color: #005cc5; 657 | border-color: #005cc5; 658 | } 659 | 660 | .markdown-body kbd { 661 | display: inline-block; 662 | padding: 3px 5px; 663 | font: 11px "SFMono-Regular", Consolas, "Liberation Mono", Menlo, Courier, monospace; 664 | line-height: 10px; 665 | color: #444d56; 666 | vertical-align: middle; 667 | background-color: #fafbfc; 668 | border: solid 1px #d1d5da; 669 | border-bottom-color: #c6cbd1; 670 | border-radius: 3px; 671 | box-shadow: inset 0 -1px 0 #c6cbd1; 672 | } 673 | 674 | .markdown-body :checked+.radio-label { 675 | position: relative; 676 | z-index: 1; 677 | border-color: #0366d6; 678 | } 679 | 680 | .markdown-body .task-list-item { 681 | list-style-type: none; 682 | } 683 | 684 | .markdown-body .task-list-item+.task-list-item { 685 | margin-top: 3px; 686 | } 687 | 688 | .markdown-body .task-list-item input { 689 | margin: 0 0.2em 0.25em -1.6em; 690 | vertical-align: middle; 691 | } 692 | 693 | .markdown-body hr { 694 | border-bottom-color: #eee; 695 | } 696 | --------------------------------------------------------------------------------