├── .gitignore
├── CMakeLists.txt
├── LICENSE.md
├── README.md
├── cmake
└── FindSDL2.cmake
├── doc
├── Filters.md
├── Geometry.md
├── Lights.md
├── Materials.md
├── Primitives.md
├── Renderers.md
├── Samplers.md
├── Textures.md
└── Volumes.md
├── include
├── accelerators
│ ├── bvh.h
│ └── kd_point_tree.h
├── args.h
├── block_queue.h
├── cache.h
├── driver.h
├── film
│ ├── camera.h
│ ├── cie_vals.h
│ ├── color.h
│ └── render_target.h
├── filters
│ ├── box_filter.h
│ ├── filter.h
│ ├── gaussian_filter.h
│ ├── lanczos_sinc_filter.h
│ ├── mitchell_filter.h
│ └── triangle_filter.h
├── geometry
│ ├── bbox.h
│ ├── cone.h
│ ├── cylinder.h
│ ├── differential_geometry.h
│ ├── disk.h
│ ├── geometry.h
│ ├── plane.h
│ ├── sphere.h
│ └── tri_mesh.h
├── integrator
│ ├── bidir_path_integrator.h
│ ├── emission_integrator.h
│ ├── path_integrator.h
│ ├── photon_map_integrator.h
│ ├── single_scattering_integrator.h
│ ├── surface_integrator.h
│ ├── volume_integrator.h
│ └── whitted_integrator.h
├── lights
│ ├── area_light.h
│ ├── light.h
│ ├── occlusion_tester.h
│ └── point_light.h
├── linalg
│ ├── animated_transform.h
│ ├── matrix4.h
│ ├── point.h
│ ├── quaternion.h
│ ├── ray.h
│ ├── transform.h
│ ├── util.h
│ └── vector.h
├── loaders
│ ├── async_loader.h
│ ├── load_filter.h
│ ├── load_light.h
│ ├── load_material.h
│ ├── load_renderer.h
│ ├── load_sampler.h
│ ├── load_scene.h
│ ├── load_texture.h
│ └── load_volume.h
├── material
│ ├── anisotropic_distribution.h
│ ├── blinn_distribution.h
│ ├── bsdf.h
│ ├── btdf_adapter.h
│ ├── bxdf.h
│ ├── fresnel.h
│ ├── glass_material.h
│ ├── lambertian.h
│ ├── material.h
│ ├── matte_material.h
│ ├── merl_brdf.h
│ ├── merl_material.h
│ ├── metal_material.h
│ ├── microfacet_distribution.h
│ ├── mix_material.h
│ ├── oren_nayer.h
│ ├── plastic_material.h
│ ├── scaled_bxdf.h
│ ├── specular_metal_material.h
│ ├── specular_reflection.h
│ ├── specular_transmission.h
│ ├── torrance_sparrow.h
│ └── translucent_material.h
├── memory_pool.h
├── mesh_preprocess.h
├── monte_carlo
│ ├── distribution1d.h
│ └── util.h
├── renderer
│ └── renderer.h
├── samplers
│ ├── adaptive_sampler.h
│ ├── ld_sampler.h
│ ├── sampler.h
│ └── stratified_sampler.h
├── scene.h
├── textures
│ ├── checkerboard_texture.h
│ ├── constant_texture.h
│ ├── image_texture.h
│ ├── mipmap.h
│ ├── remapped_texture.h
│ ├── scale_texture.h
│ ├── spherical_mapping.h
│ ├── texture.h
│ ├── texture_mapping.h
│ ├── uv_mapping.h
│ └── uv_texture.h
└── volume
│ ├── exponential_volume.h
│ ├── geometry_volume.h
│ ├── grid_volume.h
│ ├── homogeneous_volume.h
│ ├── varying_density_volume.h
│ ├── volume.h
│ └── volume_node.h
├── previewer
├── CMakeLists.txt
├── gl_core_3_3.c
├── gl_core_3_3.h
├── previewer.cpp
├── previewer.h
├── util.cpp
└── util.h
├── scenes
├── cornell.xml
└── smallpt_cornell.xml
└── src
├── CMakeLists.txt
├── accelerators
├── CMakeLists.txt
└── bvh.cpp
├── args.cpp
├── block_queue.cpp
├── driver.cpp
├── film
├── CMakeLists.txt
├── camera.cpp
├── cie_vals.cpp
├── color.cpp
└── render_target.cpp
├── filters
├── CMakeLists.txt
├── box_filter.cpp
├── gaussian_filter.cpp
├── lanczos_sinc_filter.cpp
├── mitchell_filter.cpp
└── triangle_filter.cpp
├── geometry
├── CMakeLists.txt
├── cone.cpp
├── cylinder.cpp
├── differential_geometry.cpp
├── disk.cpp
├── geometry.cpp
├── plane.cpp
├── sphere.cpp
└── tri_mesh.cpp
├── integrator
├── CMakeLists.txt
├── bidir_path_integrator.cpp
├── emission_integrator.cpp
├── path_integrator.cpp
├── photon_map_integrator.cpp
├── single_scattering_integrator.cpp
├── surface_integrator.cpp
└── whitted_integrator.cpp
├── lights
├── CMakeLists.txt
├── area_light.cpp
├── light.cpp
├── occlusion_tester.cpp
└── point_light.cpp
├── linalg
├── CMakeLists.txt
├── animated_transform.cpp
├── matrix4.cpp
├── quaternion.cpp
└── transform.cpp
├── loaders
├── CMakeLists.txt
├── load_filter.cpp
├── load_light.cpp
├── load_material.cpp
├── load_renderer.cpp
├── load_sampler.cpp
├── load_scene.cpp
├── load_texture.cpp
└── load_volume.cpp
├── main.cpp
├── material
├── CMakeLists.txt
├── anisotropic_distribution.cpp
├── blinn_distribution.cpp
├── bsdf.cpp
├── btdf_adapter.cpp
├── bxdf.cpp
├── fresnel.cpp
├── glass_material.cpp
├── lambertian.cpp
├── matte_material.cpp
├── merl_brdf.cpp
├── merl_material.cpp
├── metal_material.cpp
├── microfacet_distribution.cpp
├── mix_material.cpp
├── oren_nayer.cpp
├── plastic_material.cpp
├── scaled_bxdf.cpp
├── specular_metal_material.cpp
├── specular_reflection.cpp
├── specular_transmission.cpp
├── torrance_sparrow.cpp
└── translucent_material.cpp
├── memory_pool.cpp
├── mesh_preprocess.cpp
├── monte_carlo
├── CMakeLists.txt
├── distribution1d.cpp
└── util.cpp
├── renderer
├── CMakeLists.txt
└── renderer.cpp
├── samplers
├── CMakeLists.txt
├── adaptive_sampler.cpp
├── ld_sampler.cpp
├── sampler.cpp
└── stratified_sampler.cpp
├── scene.cpp
├── textures
├── CMakeLists.txt
├── checkerboard_texture.cpp
├── constant_texture.cpp
├── image_texture.cpp
├── mipmap.cpp
├── remapped_texture.cpp
├── scale_texture.cpp
├── spherical_mapping.cpp
├── texture_mapping.cpp
├── uv_mapping.cpp
└── uv_texture.cpp
└── volume
├── CMakeLists.txt
├── exponential_volume.cpp
├── geometry_volume.cpp
├── grid_volume.cpp
├── homogeneous_volume.cpp
├── varying_density_volume.cpp
├── volume.cpp
└── volume_node.cpp
/.gitignore:
--------------------------------------------------------------------------------
1 | # Compiled Object files
2 | *.slo
3 | *.lo
4 | *.o
5 | *.dll
6 | build/
7 | bin/
8 | *.bobj
9 | external/
10 | scenes/textures/
11 | scenes/models/
12 | scenes/brdfs
13 | scenes/volumes
14 | *.png
15 | *.ppm
16 | *.pgm
17 | *.bmp
18 | *.png
19 | tags
20 |
21 | # Ignored ycm compiled python file
22 | *.pyc
23 | *.ycm*.py
24 |
25 | # Swap files
26 | *.swp
27 |
28 | # external projects
29 | external
30 |
31 | # Compiled Dynamic libraries
32 | *.so
33 |
34 | # Compiled Static libraries
35 | *.lai
36 | *.la
37 | *.a
38 |
39 | *.exe
40 | *.pdb
41 | *.ilk
42 | *.tlog
43 | *.obj
44 | *.log
45 | *.lastbuildstate
46 | *.opensdf
47 | *.sdf
48 | *.suo
49 | *.out
50 | *_i.c
51 | *_p.c
52 | *.ilk
53 | *.meta
54 | *.obj
55 | *.pch
56 | *.pdb
57 | *.pgc
58 | *.pgd
59 | *.rsp
60 | *.sbr
61 | *.tlb
62 | *.tli
63 | *.tlh
64 | *.tmp
65 | *.vspscc
66 | .builds
67 | *.dotCover
68 | # Visual C++ cache files
69 | ipch/
70 | *.aps
71 | *.ncb
72 | *.opensdf
73 | *.sdf
74 |
75 | # Visual Studio profiler
76 | *.psess
77 | *.vsp
78 |
79 | Lesson*/Debug
80 | Lesson*/Release
81 | out/
82 |
--------------------------------------------------------------------------------
/CMakeLists.txt:
--------------------------------------------------------------------------------
1 | cmake_minimum_required(VERSION 2.6)
2 | project(TRAY)
3 |
4 | # Bump up warning levels appropriately for each compiler
5 | if (${CMAKE_CXX_COMPILER_ID} STREQUAL "GNU" OR ${CMAKE_CXX_COMPILER_ID} STREQUAL "Clang")
6 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wextra -pedantic -std=c++14")
7 | set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS} -m64 -ggdb -DDEBUG")
8 | set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS} -m64 -O3 -march=native -flto -fno-use-linker-plugin -DNDEBUG -fno-exceptions")
9 | set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -flto -fno-exceptions")
10 | elseif (${CMAKE_CXX_COMPILER_ID} STREQUAL "MSVC")
11 | if(CMAKE_CXX_FLAGS MATCHES "/W[0-4]")
12 | string(REGEX REPLACE "/W[0-4]" "/W4" CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /Zi")
13 | else()
14 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /W4 /Zi")
15 | endif()
16 | endif()
17 |
18 | set(TRAY_INSTALL_DIR "${TRAY_SOURCE_DIR}/bin")
19 |
20 | set(tinyxml2_DIR "${TRAY_SOURCE_DIR}/external/tinyxml2/")
21 | file(DOWNLOAD "https://raw.githubusercontent.com/leethomason/tinyxml2/master/tinyxml2.cpp"
22 | "${tinyxml2_DIR}/tinyxml2.cpp")
23 | file(DOWNLOAD "https://raw.githubusercontent.com/leethomason/tinyxml2/master/tinyxml2.h"
24 | "${tinyxml2_DIR}/tinyxml2.h")
25 | # Use stb_image for wider range of image format support
26 | set(stb_image_INCLUDE_DIR "${TRAY_SOURCE_DIR}/external/stb/")
27 | file(DOWNLOAD "https://raw.githubusercontent.com/nothings/stb/master/stb_image.h"
28 | "${stb_image_INCLUDE_DIR}/stb_image.h")
29 | include_directories(include ${tinyxml2_DIR} ${stb_image_INCLUDE_DIR})
30 |
31 | # If we're building the live previewer find SDL2
32 | if (BUILD_PREVIEWER)
33 | add_definitions(-DBUILD_PREVIEWER)
34 | # Use our modified FindSDL2 modules for windows/etc
35 | set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${TRAY_SOURCE_DIR}/cmake")
36 | find_package(SDL2 REQUIRED)
37 | find_package(OpenGL REQUIRED)
38 | include_directories(${SDL2_INCLUDE_DIR} ${OPENGL_INCLUDE_DIR} previewer)
39 | add_subdirectory(previewer)
40 | endif()
41 |
42 | # If user has request trilinear texture filtering
43 | if (TEX_TRILINEAR)
44 | add_definitions(-DTEX_TRILINEAR)
45 | endif()
46 |
47 | find_package(Threads)
48 | add_subdirectory(src)
49 |
50 |
--------------------------------------------------------------------------------
/LICENSE.md:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2014 Will Usher
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in
13 | all copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21 | THE SOFTWARE.
22 |
--------------------------------------------------------------------------------
/doc/Primitives.md:
--------------------------------------------------------------------------------
1 | Primitives
2 | ===
3 | This documentation covers how to specify the various basic types that are used throughout the scene files to specify attributes such as colors, points, vectors and transformations.
4 |
5 | Points
6 | ---
7 | Points are specified by passing their coordinates as attributes. Any unspecified coordinates are assumed to be zero.
8 |
9 | ```XML
10 |
11 |
12 | ```
13 |
14 | Vectors
15 | ---
16 | Vectors are specified identically to points but can also take an additional scaling value to multiply the components with. As with points unspecified components are assumed to be 0.
17 |
18 | ```XML
19 |
20 |
21 | ```
22 |
23 | Colors
24 | ---
25 | Colors are specified identical to vectors but the attributes specified are the RGB components. You can also specify a grayscale color by only providing the value attribute. Examples of both are given below.
26 |
27 | ```XML
28 |
29 |
30 |
31 | ```
32 |
33 | Transformations
34 | ---
35 | Transformations are specified by passing scale, rotation and translation parameters. The order in which the tags are read in is the order in which the transformations will be applied to the object. Both the scale and translate tags tags take the vector of scale/translation values for x, y and z. For scaling you can also specify a value attribute to have a uniform scaling applied to all axes, unspecified axes for scaling are assumed to be 1. Rotation takes an angle in degrees and a vector to rotate about, treated as described in the vector section. Some example transformations are given below.
36 |
37 | ```XML
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 | ```
55 |
56 |
--------------------------------------------------------------------------------
/doc/Samplers.md:
--------------------------------------------------------------------------------
1 | Samplers
2 | ===
3 | This doc covers the various samplers that can be selected. Samplers are used when finding sample positions to send rays from on the image plane and can have a big effect on image quality if they're not well distributed.
4 |
5 | Stratified Sampler
6 | ---
7 | This sampler partitions the pixel into a grid and takes jittered samples in each grid cell. It takes a single attribute to specify the number of samples per axis, spp, and will take spp \* spp samples after splitting the grid.
8 |
9 | ```XML
10 |
11 |
12 | ```
13 |
14 | Low Discrepancy Sampler
15 | ---
16 | The low discrepency sampler chooses samples using sequences known to give well distributed samples, combining samples from a Van der Corput and Sobol sequence. The sampler also takes a parameter to select the number of samples to take per pixel which must also be a power of two. If a non-power of two number is passed it will be rounded up and a warning printed.
17 |
18 | ```XML
19 |
20 | ```
21 |
22 | Adaptive Sampler
23 | ---
24 | The adaptive sampler determines the number of samples to take based on the variance in the contrast of samples taken previously. To choose sample positions it uses the low discrepancy sampler described previously and as such also requires power of two sample counts. The sampler takes two parameters to specify the min and max samples to take per pixel and will begin by taking min samples and use their results to determine if more samples are needed. If it's determined more are required it will step up the sample count in powers of two until reaching the max.
25 |
26 | ```XML
27 |
28 | ```
29 |
30 |
--------------------------------------------------------------------------------
/doc/Textures.md:
--------------------------------------------------------------------------------
1 | Textures
2 | ===
3 | The ray tracer supports PNG (8bit channel only), BMP (non-1bpp, non-RLE), and PPM image formats for textures. Note that texture names beginning with \_\_ are reserved for generated texture names the ray tracer may need to create and should not be used. Textures can also have transformations specified which will result in a transformation being applied to the texture coordinates when looking up the texel.
4 |
5 | Checkerboard Texture
6 | ---
7 | The checkerboard texture is a regular two color checkerboard and takes two colors to be selected for the colors of the checks. Below is an example of a checkerboard with some non-uniform scaling applied.
8 |
9 | ```XML
10 |
11 |
12 |
13 |
14 |
15 | ```
16 |
17 | Image Texture
18 | ---
19 | The image texture loads an image and applies it to the object using its texture coordinates. TRay supports PNG (8bit channel only), BMP (non-1bpp, non-RLE), and PPM image formats for textures. The name used for an image texture should be the path to the file to load, relative to the scene file.
20 |
21 | ```XML
22 |
23 |
24 |
25 | ```
26 |
27 | UV Texture
28 | ---
29 | The UV texture just returns the texture coordinates passed in as the "color" of the texture, useful for debugging.
30 |
31 | ```XML
32 |
33 | ```
34 |
35 |
--------------------------------------------------------------------------------
/include/args.h:
--------------------------------------------------------------------------------
1 | #ifndef ARGS_H
2 | #define ARGS_h
3 |
4 | #include
5 | #include
6 | #include
7 |
8 | /*
9 | * Some utilities for parsing command line arguments and
10 | * conveniently checking selected flags and getting parameter
11 | * values
12 | */
13 | /*
14 | * Check if a flag was passed
15 | * @param beg The beginning of the args array
16 | * @param end One past the end of the args array
17 | * @param f The flag to check for, ex. -f
18 | * @return true if the flag was passed
19 | */
20 | bool flag(char **beg, char **end, const std::string &f);
21 | /*
22 | * Get the parameter passed for some flag. It's best to make sure
23 | * the flag was passed before trying to retrieve its argument
24 | * if the flag isn't found T's default value will be returned
25 | * @param beg The beginning of the args array
26 | * @param end One past the end of the args array
27 | * @param f The flag to get the parameter for, ex. -f
28 | * @return The value of the parameter
29 | */
30 | template
31 | T get_param(char **beg, char **end, const std::string &f){
32 | char **it = std::find(beg, end, f);
33 | if (it != end && ++it != end){
34 | std::stringstream ss;
35 | ss << *it;
36 | T t;
37 | ss >> t;
38 | return t;
39 | }
40 | return T();
41 | }
42 | //Some quicker versions of getParam for certain types
43 | template<>
44 | std::string get_param(char **beg, char **end, const std::string &g);
45 | template<>
46 | int get_param(char **beg, char **end, const std::string &g);
47 | template<>
48 | float get_param(char **beg, char **end, const std::string &g);
49 |
50 | #endif
51 |
52 |
--------------------------------------------------------------------------------
/include/block_queue.h:
--------------------------------------------------------------------------------
1 | #ifndef BLOCK_QUEUE_H
2 | #define BLOCK_QUEUE_H
3 |
4 | #include
5 | #include
6 | #include
7 | #include "samplers/sampler.h"
8 |
9 | /*
10 | * A queue that hands out blocks of pixels to be worked on
11 | * by threads in the form of samplers for the block of pixels
12 | */
13 | class BlockQueue {
14 | std::vector> samplers;
15 | //The index of the next sampler to be handed out
16 | std::atomic_uint sampler_idx, loops;
17 | //Total time and previous time we printed out timing info
18 | std::chrono::milliseconds total_time;
19 | std::chrono::time_point prev;
20 |
21 | public:
22 | /*
23 | * Create a queue of work blocks by subsampling the sampler
24 | * into blocks subsamplers
25 | */
26 | BlockQueue(const Sampler &sampler, int bwidth, int bheight);
27 | /*
28 | * Return the next block to be worked on, returns nullptrt
29 | * when all samplers have been completed
30 | */
31 | Sampler* get_block();
32 | };
33 |
34 | #endif
35 |
36 |
--------------------------------------------------------------------------------
/include/cache.h:
--------------------------------------------------------------------------------
1 | #ifndef CACHE_H
2 | #define CACHE_H
3 |
4 | #include
5 | #include
6 |
7 | /*
8 | * Caches types of T, really just a hash table of name -> T
9 | */
10 | template
11 | class Cache {
12 | std::unordered_map> cache;
13 |
14 | public:
15 | using iter = typename std::unordered_map>::iterator;
16 | using citer = typename std::unordered_map>::const_iterator;
17 | /*
18 | * Add some T to the cache
19 | */
20 | T* add(const std::string &name, std::unique_ptr t){
21 | auto it = cache.emplace(std::make_pair(name, std::move(t)));
22 | return it.first->second.get();
23 | }
24 | /*
25 | * Get a non-owning pointer to some T in the cache
26 | * nullptr is returned if the name isn't found
27 | */
28 | const T* get(const std::string &name) const {
29 | auto fnd = cache.find(name);
30 | if (fnd != cache.end()){
31 | return fnd->second.get();
32 | }
33 | return nullptr;
34 | }
35 | T* get(const std::string &name){
36 | auto fnd = cache.find(name);
37 | if (fnd != cache.end()){
38 | return fnd->second.get();
39 | }
40 | return nullptr;
41 | }
42 | iter begin(){
43 | return cache.begin();
44 | }
45 | citer begin() const {
46 | return cache.begin();
47 | }
48 | iter end(){
49 | return cache.end();
50 | }
51 | citer end() const {
52 | return cache.cend();
53 | }
54 | size_t size() const {
55 | return cache.size();
56 | }
57 | bool empty() const {
58 | return cache.empty();
59 | }
60 | };
61 |
62 | #endif
63 |
64 |
--------------------------------------------------------------------------------
/include/driver.h:
--------------------------------------------------------------------------------
1 | #ifndef DRIVER_H
2 | #define DRIVER_H
3 |
4 | #include
5 | #include
6 | #include
7 | #include "scene.h"
8 | #include "geometry/geometry.h"
9 | #include "linalg/ray.h"
10 | #include "samplers/sampler.h"
11 | #include "block_queue.h"
12 |
13 | //Status of a worker thread
14 | enum STATUS { NOT_STARTED, WORKING, DONE, CANCELED, JOINED };
15 |
16 | /*
17 | * A worker thread that renders some subsection of the scene
18 | */
19 | class Worker {
20 | Scene &scene;
21 | BlockQueue &queue;
22 |
23 | public:
24 | //The thread the worker is on
25 | std::thread thread;
26 | //The thread status so we can report whether we're done or should cancel
27 | std::atomic_int status;
28 |
29 | /*
30 | * Create the worker to get samplers from the sampler
31 | * and use them to render the scene
32 | */
33 | Worker(Scene &scene, BlockQueue &queue);
34 | Worker(Worker &&w);
35 | void render();
36 | };
37 |
38 | /*
39 | * A driver that distributes the work of rendering the scene
40 | * among some number of threads
41 | */
42 | class Driver {
43 | //The worker threads rendering the scene
44 | std::vector workers;
45 | Scene &scene;
46 | //Queue of blocks of pixels to be worked on
47 | BlockQueue queue;
48 |
49 | public:
50 | /*
51 | * Create a driver to render the scene with some number of worker threads
52 | * to work on the scene partitioned into blocks with the desired dimensions
53 | */
54 | Driver(Scene &scene, int nworkers, int bwidth, int bheight);
55 | ~Driver();
56 | void render();
57 | bool done();
58 | /*
59 | * Abort all threads and cancel rendering
60 | */
61 | void cancel();
62 | /*
63 | * Get the scene being rendered
64 | */
65 | const Scene& get_scene() const;
66 | };
67 |
68 | #endif
69 |
70 |
--------------------------------------------------------------------------------
/include/film/camera.h:
--------------------------------------------------------------------------------
1 | #ifndef CAMERA_H
2 | #define CAMERA_H
3 |
4 | #include
5 | #include "linalg/ray.h"
6 | #include "linalg/transform.h"
7 | #include "samplers/sampler.h"
8 |
9 | class Camera {
10 | const float dof, focal_dist, open, close;
11 | //Transform to go from camera to world space, eg. camera look at matrix
12 | Transform cam_world;
13 | //Transforms from raster to camera space
14 | Transform raster_cam;
15 | //dx and dy for pixels, for computing ray differentials
16 | Vector dx, dy;
17 |
18 | public:
19 | /*
20 | * Create the camera at some position in the world. Default is looking down
21 | * the z axis but this can be changed with the cam_world transformation
22 | * xres & yres correspond to the render target resolution
23 | */
24 | Camera(const Transform &cam_world, float fov, float dof, float focal_dist,
25 | float open, float close, int xres, int yres);
26 | /*
27 | * Generate a ray to sample the scene through x,y. Pixel coordinates should be
28 | * in raster space
29 | */
30 | Ray generate_ray(const Sample &sample) const;
31 | /*
32 | * Generate a ray with differentials to sample the scene through x,y.
33 | * The differentials will be +1 in x and y, pixel coords passed should
34 | * be in raster space
35 | */
36 | RayDifferential generate_raydifferential(const Sample &sample) const;
37 | };
38 |
39 | #endif
40 |
41 |
--------------------------------------------------------------------------------
/include/film/cie_vals.h:
--------------------------------------------------------------------------------
1 | #ifndef CIE_VALS_H
2 | #define CIE_VALS_H
3 |
4 | #include
5 |
6 | //We use PBRT's values for the CIE function values
7 | static const size_t NUM_CIE_SAMPLES = 471;
8 | extern const std::array CIE_X;
9 | extern const std::array CIE_Y;
10 | extern const std::array CIE_Z;
11 | extern const std::array CIE_LAMBDA;
12 |
13 | #endif
14 |
15 |
--------------------------------------------------------------------------------
/include/film/render_target.h:
--------------------------------------------------------------------------------
1 | #ifndef RENDER_TARGET_H
2 | #define RENDER_TARGET_H
3 |
4 | #include
5 | #include
6 | #include
7 | #include
8 | #include "color.h"
9 | #include "filters/filter.h"
10 |
11 | const int FILTER_TABLE_SIZE = 16;
12 |
13 | /*
14 | * A pixel stored in the image being rendered to track pixel
15 | * luminance and weight for reconstruction
16 | * Because we need to deal with multi-thread synchronization the
17 | * rgb values and weights are stored as atomics
18 | */
19 | struct Pixel {
20 | std::atomic r, g, b, weight;
21 |
22 | Pixel();
23 | Pixel(const Pixel &p);
24 | };
25 |
26 | /*
27 | * The render target where pixel data is stored for the rendered scene
28 | * along with for some reason a depth buffer is required for proj1?
29 | */
30 | class RenderTarget {
31 | size_t width, height;
32 | std::unique_ptr filter;
33 | std::vector pixels;
34 | //Pre-computed filter values to save time when storing pixels
35 | std::array filter_table;
36 |
37 | public:
38 | /*
39 | * Create a render target with width * height pixels that will
40 | * reconstruct the image from the samples using the desired filter
41 | * Default is a box filter with single pixel extent
42 | */
43 | RenderTarget(size_t width, size_t height, std::unique_ptr f);
44 | /*
45 | * Write a color value to the image at pixel(x, y)
46 | */
47 | void write_pixel(float x, float y, const Colorf &c);
48 | //Save the image or depth buffer to the desired file
49 | bool save_image(const std::string &file) const;
50 | size_t get_width() const;
51 | size_t get_height() const;
52 | /*
53 | * Get a snapshot of the color buffer at the moment
54 | * stored in img
55 | */
56 | void get_colorbuf(std::vector &img) const;
57 |
58 | private:
59 | /*
60 | * Save color data as a PPM image to the file, data should be
61 | * RGB8 data and have width * height elements
62 | */
63 | bool save_ppm(const std::string &file, const uint8_t *data) const;
64 | /*
65 | * Save color data as a BMP image to the file, data should be
66 | * RGBA8 data and have width * height elements
67 | * The image data should be flipped appropriately already for the BMP
68 | * file format (eg. starting at bottom left)
69 | */
70 | bool save_bmp(const std::string &file, const uint8_t *data) const;
71 | };
72 |
73 | #endif
74 |
75 |
--------------------------------------------------------------------------------
/include/filters/box_filter.h:
--------------------------------------------------------------------------------
1 | #ifndef BOX_FILTER_H
2 | #define BOX_FILTER_H
3 |
4 | #include "filter.h"
5 |
6 | /*
7 | * A simple box filter
8 | */
9 | struct BoxFilter : Filter {
10 | BoxFilter(float w, float h);
11 | float weight(float x, float y) const override;
12 | };
13 |
14 | #endif
15 |
16 |
--------------------------------------------------------------------------------
/include/filters/filter.h:
--------------------------------------------------------------------------------
1 | #ifndef FILTER_H
2 | #define FILTER_H
3 |
4 | /*
5 | * Base filter used when reconstructing the rendered image
6 | * from samples taken of the scene
7 | * Built off the technique from PBR
8 | */
9 | struct Filter {
10 | //The filters width, height and corresponding inverses
11 | const float w, h, inv_w, inv_h;
12 |
13 | /*
14 | * Construct a filter with the desired extent in x & y
15 | */
16 | Filter(float w, float h) : w(w), h(h), inv_w(1 / w), inv_h(1 / h){}
17 | /*
18 | * Evaluate the weight of this filter at some position
19 | */
20 | virtual float weight(float x, float y) const = 0;
21 | };
22 |
23 | #endif
24 |
25 |
--------------------------------------------------------------------------------
/include/filters/gaussian_filter.h:
--------------------------------------------------------------------------------
1 | #ifndef GAUSSIAN_FILTER_H
2 | #define GAUSSIAN_FILTER_H
3 |
4 | #include "filter.h"
5 |
6 | struct GaussianFilter : Filter {
7 | const float alpha, exp_x, exp_y;
8 |
9 | GaussianFilter(float w, float h, float alpha);
10 | float weight(float x, float y) const override;
11 |
12 | private:
13 | /*
14 | * Compute the 1d Gaussian value using
15 | * std::exp(-alpha * x * x) - expv
16 | * where expv should be a pre-computed value
17 | * for std::exp(-alpha * w * w)
18 | */
19 | float gaussian_1d(float x, float expv) const;
20 | };
21 |
22 | #endif
23 |
24 |
--------------------------------------------------------------------------------
/include/filters/lanczos_sinc_filter.h:
--------------------------------------------------------------------------------
1 | #ifndef LANCZOS_SINC_FILTER
2 | #define LANCZOS_SINC_FILTER
3 |
4 | #include "filter.h"
5 |
6 | struct LanczosSincFilter : Filter {
7 | const float a;
8 |
9 | LanczosSincFilter(float w, float h, float a);
10 | float weight(float x, float y) const override;
11 |
12 | private:
13 | float lanczos_sinc1d(float x) const;
14 | };
15 |
16 | #endif
17 |
18 |
--------------------------------------------------------------------------------
/include/filters/mitchell_filter.h:
--------------------------------------------------------------------------------
1 | #ifndef MITCHELL_FILTER_H
2 | #define MITCHELL_FILTER_H
3 |
4 | #include "filter.h"
5 |
6 | struct MitchellFilter : Filter {
7 | const float b, c;
8 |
9 | MitchellFilter(float w, float h, float b, float c);
10 | float weight(float x, float y) const override;
11 |
12 | private:
13 | float mitchell_1d(float x) const;
14 | };
15 |
16 | #endif
17 |
18 |
--------------------------------------------------------------------------------
/include/filters/triangle_filter.h:
--------------------------------------------------------------------------------
1 | #ifndef TRIANGLE_FILTER_H
2 | #define TRIANGLE_FILTER_H
3 |
4 | #include "filter.h"
5 |
6 | /*
7 | * A simple triangle filter
8 | */
9 | struct TriangleFilter : Filter {
10 | TriangleFilter(float w, float h);
11 | float weight(float x, float y) const override;
12 | };
13 |
14 | #endif
15 |
16 |
--------------------------------------------------------------------------------
/include/geometry/cone.h:
--------------------------------------------------------------------------------
1 | #ifndef CONE_H
2 | #define CONE_H
3 |
4 | #include "geometry.h"
5 |
6 | /*
7 | * A cone with some radius and height, described as cone
8 | * from [0, height] along the z axis
9 | */
10 | class Cone : public Geometry {
11 | float radius, height;
12 |
13 | public:
14 | /*
15 | * Construct the cone with some radius and height
16 | */
17 | Cone(float radius = 1, float height = 1);
18 | bool intersect(Ray &ray, DifferentialGeometry &dg) const override;
19 | BBox bound() const override;
20 | void refine(std::vector &prims) override;
21 | /*
22 | * Compute the surface area of the sphere
23 | */
24 | float surface_area() const override;
25 | };
26 |
27 | #endif
28 |
29 |
--------------------------------------------------------------------------------
/include/geometry/cylinder.h:
--------------------------------------------------------------------------------
1 | #ifndef CYLINDER_H
2 | #define CYLINDER_H
3 |
4 | #include "geometry.h"
5 |
6 | /*
7 | * A cylinder with some radius and height along the z axis
8 | */
9 | class Cylinder : public Geometry {
10 | float radius, height;
11 |
12 | public:
13 | /*
14 | * Construct the cylinder with some radius and height
15 | */
16 | Cylinder(float radius = 1, float height = 1);
17 | bool intersect(Ray &ray, DifferentialGeometry &dg) const override;
18 | BBox bound() const override;
19 | void refine(std::vector &prims) override;
20 | /*
21 | * Compute the surface area of the sphere
22 | */
23 | float surface_area() const override;
24 | /*
25 | * Sample a position on the geometry and return the point and normal
26 | */
27 | Point sample(const GeomSample &gs, Normal &normal) const override;
28 | bool attach_light(const Transform &to_world) override;
29 | };
30 |
31 | #endif
32 |
33 |
--------------------------------------------------------------------------------
/include/geometry/differential_geometry.h:
--------------------------------------------------------------------------------
1 | #ifndef DIFFERENTIAL_GEOMETRY_H
2 | #define DIFFERENTIAL_GEOMETRY_H
3 |
4 | #include "linalg/point.h"
5 | #include "linalg/vector.h"
6 | #include "linalg/ray.h"
7 |
8 | class Node;
9 | class Geometry;
10 |
11 | enum HITSIDE { NONE, FRONT, BACK };
12 |
13 | /*
14 | * Stores information about the differential piece of geometry
15 | * that was hit and a pointer to the node that was hit so we can
16 | * grab the material to shade this hit point
17 | */
18 | struct DifferentialGeometry {
19 | Point point;
20 | //normal is the surface normal to be used for shading while
21 | //geom_normal is the true normal of the original geometry we hit
22 | Normal normal, geom_normal;
23 | const Node *node;
24 | const Geometry *geom;
25 | HITSIDE hit_side;
26 | //Various derivatives and info we need for texture mapping and filtering
27 | Vector dp_du, dp_dv, dp_dx, dp_dy;
28 | Normal dn_du, dn_dv;
29 | float u, v, du_dx, dv_dx, du_dy, dv_dy;
30 |
31 | DifferentialGeometry();
32 | DifferentialGeometry(const Point &point, const Vector &dp_du, const Vector &dp_dv,
33 | const Normal &dn_du, const Normal &dn_dv, const Normal &geom_normal,
34 | float u, float v, const Node *node, HITSIDE hit_side);
35 | /*
36 | * Compute the (u, v) derivatives using information about the
37 | * change in pixel position from the ray differential
38 | */
39 | void compute_differentials(const RayDifferential &r);
40 | };
41 |
42 | #endif
43 |
44 |
--------------------------------------------------------------------------------
/include/geometry/disk.h:
--------------------------------------------------------------------------------
1 | #ifndef DISK_H
2 | #define DISK_H
3 |
4 | #include "geometry.h"
5 |
6 | /*
7 | * A disk with some inner and outer radius allowing it to
8 | * have a hole in the center placed at the origin with normal
9 | * along +z
10 | */
11 | class Disk : public Geometry {
12 | float radius, inner_radius;
13 |
14 | public:
15 | /*
16 | * Construct the disk with some radius and inner radius
17 | */
18 | Disk(float radius = 1, float inner_radius = 0);
19 | bool intersect(Ray &ray, DifferentialGeometry &dg) const override;
20 | BBox bound() const override;
21 | void refine(std::vector &prims) override;
22 | /*
23 | * Compute the surface area of the sphere
24 | */
25 | float surface_area() const override;
26 | /*
27 | * Sample a position on the geometry and return the point and normal
28 | */
29 | Point sample(const GeomSample &gs, Normal &normal) const override;
30 | bool attach_light(const Transform &to_world) override;
31 | };
32 |
33 | #endif
34 |
35 |
--------------------------------------------------------------------------------
/include/geometry/plane.h:
--------------------------------------------------------------------------------
1 | #ifndef PLANE_H
2 | #define PLANE_H
3 |
4 | #include "geometry.h"
5 |
6 | /*
7 | * A plane centered at the origin spanning
8 | * [-1, -1] to [1, 1] with normal of {0, 0, 1}
9 | */
10 | class Plane : public Geometry {
11 | public:
12 | bool intersect(Ray &ray, DifferentialGeometry &diff_geom) const override;
13 | BBox bound() const override;
14 | void refine(std::vector &prims) override;
15 | };
16 |
17 | #endif
18 |
19 |
--------------------------------------------------------------------------------
/include/geometry/sphere.h:
--------------------------------------------------------------------------------
1 | #ifndef SPHERE_H
2 | #define SPHERE_H
3 |
4 | #include
5 | #include "linalg/point.h"
6 | #include "geometry.h"
7 |
8 | /*
9 | * A simple unit sphere centered at the origin
10 | */
11 | class Sphere : public Geometry {
12 | float radius;
13 |
14 | public:
15 | /*
16 | * Construct the sphere with some radius
17 | */
18 | Sphere(float radius = 1);
19 | bool intersect(Ray &ray, DifferentialGeometry &diff_geom) const override;
20 | BBox bound() const override;
21 | void refine(std::vector &prims) override;
22 | /*
23 | * Compute the surface area of the sphere
24 | */
25 | float surface_area() const override;
26 | /*
27 | * Sample a position on the geometry and return the point and normal
28 | */
29 | Point sample(const GeomSample &gs, Normal &normal) const override;
30 | /*
31 | * Sample the shape using the probability density of the solid angle from
32 | * point p to the point on the surface
33 | */
34 | Point sample(const Point &p, const GeomSample &gs, Normal &normal) const override;
35 | /*
36 | * Compute the pdf that the ray from p with direction w_i intersects the shape
37 | */
38 | float pdf(const Point &p, const Vector &w_i) const override;
39 | bool attach_light(const Transform &to_world) override;
40 | };
41 |
42 | #endif
43 |
44 |
--------------------------------------------------------------------------------
/include/integrator/bidir_path_integrator.h:
--------------------------------------------------------------------------------
1 | #ifndef BIDIR_PATH_INTEGRATOR_H
2 | #define BIDIR_PATH_INTEGRATOR_H
3 |
4 | #include "surface_integrator.h"
5 | #include "renderer/renderer.h"
6 |
7 | /*
8 | * Surface integrator that uses bidirectional path tracing for computing illumination
9 | * at a a point on the surface
10 | */
11 | class BidirPathIntegrator : public SurfaceIntegrator {
12 | const int min_depth, max_depth;
13 |
14 | /*
15 | * Struct used to store information about a vertex along a camera or light path
16 | * stores the vertex differential geometry, outgoing and incident vectors (both point out of surface)
17 | * the BSDF, specularity and throughput information
18 | */
19 | struct PathVertex {
20 | DifferentialGeometry dg;
21 | Vector w_o, w_i;
22 | BSDF *bsdf;
23 | bool specular_bounce;
24 | int num_specular_comp;
25 | Colorf throughput;
26 | };
27 |
28 | public:
29 | /*
30 | * Create the path tracing integrator and set the min and max depth for paths
31 | * rays are randomly stopped by Russian roulette after reaching min_depth and are stopped
32 | * at max_depth
33 | */
34 | BidirPathIntegrator(int min_depth, int max_depth);
35 | /*
36 | * Compute the illumination at a point on a surface in the scene
37 | */
38 | Colorf illumination(const Scene &scene, const Renderer &renderer, const RayDifferential &ray,
39 | DifferentialGeometry &dg, Sampler &sampler, MemoryPool &pool) const override;
40 |
41 | private:
42 | /*
43 | * Generate a path of length at least min_depth and at most max_depth, returning the actual
44 | * length of the path generated and filling out the path vertex array passed. There must be
45 | * room for at least max_depth vertices in the array. Weight is the starting weight of the
46 | * path, eg. weight from the camera or light source generating the ray
47 | */
48 | int trace_path(const Scene &scene, const Renderer &renderer, const RayDifferential &ray, const Colorf &weight,
49 | Sampler &sampler, MemoryPool &pool, PathVertex *path_vertices) const;
50 | /*
51 | * Compute the luminance along the camera path using the regular camera path tracing lighting
52 | * computation but with the path vertices for path of length path_len provided in path_vertices
53 | */
54 | Colorf camera_luminance(const Scene &scene, const Renderer &renderer, const PathVertex *path_vertices,
55 | int path_len, Sampler &sampler, MemoryPool &pool) const;
56 | /*
57 | * Compute the luminance coming back along the camera path by combining the camera and light
58 | * paths that were traced previously
59 | */
60 | Colorf bidir_luminance(const Scene &scene, const Renderer &renderer, PathVertex *cam_path, int cam_path_len,
61 | PathVertex *light_path, int light_path_len, Sampler &sampler, MemoryPool &pool) const;
62 | };
63 |
64 | #endif
65 |
66 |
--------------------------------------------------------------------------------
/include/integrator/emission_integrator.h:
--------------------------------------------------------------------------------
1 | #ifndef EMISSION_INTEGRATOR_H
2 | #define EMISSION_INTEGRATOR_H
3 |
4 | #include "volume_integrator.h"
5 |
6 | /*
7 | * The EmissionIntegrator is a volume integrator that only
8 | * accounts for absoprtion and emission effects of the volumes
9 | * being traversed
10 | * See: PBR
11 | */
12 | class EmissionIntegrator : public VolumeIntegrator {
13 | //Distance between samples to take of the volume when integrating
14 | float step_size;
15 |
16 | public:
17 | /*
18 | * Construct the emission integrator specifying the distance between
19 | * samples when integrating over the volumes in the scene
20 | */
21 | EmissionIntegrator(float step_size);
22 | /*
23 | * Compute the radiance arriving at the point p along the direction w
24 | * due to participating media in the scene. Also returns the transmittance
25 | * along the ray through transmit
26 | */
27 | virtual Colorf radiance(const Scene &scene, const Renderer &renderer, const RayDifferential &ray,
28 | Sampler &sampler, MemoryPool &pool, Colorf &transmit) const override;
29 | /*
30 | * Compute the beam transmittance for line segment along the ray from min_t to max_t
31 | */
32 | virtual Colorf transmittance(const Scene &scene, const Renderer &renderer, const RayDifferential &ray,
33 | Sampler &sampler, MemoryPool &pool) const override;
34 | };
35 |
36 | #endif
37 |
38 |
--------------------------------------------------------------------------------
/include/integrator/path_integrator.h:
--------------------------------------------------------------------------------
1 | #ifndef PATH_INTEGRATOR_H
2 | #define PATH_INTEGRATOR_H
3 |
4 | #include "surface_integrator.h"
5 | #include "renderer/renderer.h"
6 |
7 | /*
8 | * Surface integrator that uses Path tracing for computing illumination at a point on the surface
9 | */
10 | class PathIntegrator : public SurfaceIntegrator {
11 | const int min_depth, max_depth;
12 |
13 | public:
14 | /*
15 | * Create the path tracing integrator and set the min and max depth for paths
16 | * rays are randomly stopped by Russian roulette after reaching min_depth and are stopped
17 | * at max_depth
18 | */
19 | PathIntegrator(int min_depth, int max_depth);
20 | /*
21 | * Compute the illumination at a point on a surface in the scene
22 | */
23 | Colorf illumination(const Scene &scene, const Renderer &renderer, const RayDifferential &ray,
24 | DifferentialGeometry &dg, Sampler &sampler, MemoryPool &pool) const override;
25 | };
26 |
27 | #endif
28 |
29 |
--------------------------------------------------------------------------------
/include/integrator/single_scattering_integrator.h:
--------------------------------------------------------------------------------
1 | #ifndef SINGLE_SCATTERING_INTEGRATOR_H
2 | #define SINGLE_SCATTERING_INTEGRATOR_H
3 |
4 | #include "volume_integrator.h"
5 |
6 | /*
7 | * The SingleScatteringIntegrator is a volume integrator that
8 | * accounts for absoprtion and emission effects of the volumes
9 | * being traversed along with in scattering effects
10 | * See: PBR
11 | */
12 | class SingleScatteringIntegrator : public VolumeIntegrator {
13 | //Distance between samples to take of the volume when integrating
14 | float step_size;
15 |
16 | public:
17 | /*
18 | * Construct the single scattering integrator specifying the distance between
19 | * samples when integrating over the volumes in the scene
20 | */
21 | SingleScatteringIntegrator(float step_size);
22 | /*
23 | * Compute the radiance arriving at the point p along the direction w
24 | * due to participating media in the scene. Also returns the transmittance
25 | * along the ray through transmit
26 | */
27 | virtual Colorf radiance(const Scene &scene, const Renderer &renderer, const RayDifferential &ray,
28 | Sampler &sampler, MemoryPool &pool, Colorf &transmit) const override;
29 | /*
30 | * Compute the beam transmittance for line segment along the ray from min_t to max_t
31 | */
32 | virtual Colorf transmittance(const Scene &scene, const Renderer &renderer, const RayDifferential &ray,
33 | Sampler &sampler, MemoryPool &pool) const override;
34 | };
35 |
36 | #endif
37 |
38 |
39 |
--------------------------------------------------------------------------------
/include/integrator/volume_integrator.h:
--------------------------------------------------------------------------------
1 | #ifndef VOLUME_INTEGRATOR_H
2 | #define VOLUME_INTEGRATOR_H
3 |
4 | #include "samplers/sampler.h"
5 | #include "renderer/renderer.h"
6 | #include "linalg/ray.h"
7 | #include "memory_pool.h"
8 | #include "film/color.h"
9 |
10 | class Scene;
11 |
12 | /*
13 | * Interface for volume integrators to implement
14 | */
15 | class VolumeIntegrator {
16 | public:
17 | /*
18 | * Compute the radiance arriving at the point p along the direction w
19 | * due to participating media in the scene. Also returns the transmittance
20 | * along the ray through transmit
21 | */
22 | virtual Colorf radiance(const Scene &scene, const Renderer &renderer, const RayDifferential &ray,
23 | Sampler &sampler, MemoryPool &pool, Colorf &transmit) const = 0;
24 | /*
25 | * Compute the beam transmittance for line segment along the ray from min_t to max_t
26 | */
27 | virtual Colorf transmittance(const Scene &scene, const Renderer &renderer, const RayDifferential &ray,
28 | Sampler &sampler, MemoryPool &pool) const = 0;
29 | };
30 |
31 | #endif
32 |
33 |
--------------------------------------------------------------------------------
/include/integrator/whitted_integrator.h:
--------------------------------------------------------------------------------
1 | #ifndef WHITTED_INTEGRATOR_H
2 | #define WHITTED_INTEGRATOR_H
3 |
4 | #include "surface_integrator.h"
5 | #include "renderer/renderer.h"
6 |
7 | /*
8 | * Surface integrator that uses Whitted recursive ray tracing algorithm
9 | * for computing illumination at a point on the surface
10 | */
11 | class WhittedIntegrator : public SurfaceIntegrator {
12 | const int max_depth;
13 |
14 | public:
15 | /*
16 | * Create the Whitted integrator and set the max recursion depth for rays
17 | */
18 | WhittedIntegrator(int max_depth);
19 | /*
20 | * Compute the illumination at a point on a surface in the scene
21 | */
22 | Colorf illumination(const Scene &scene, const Renderer &renderer, const RayDifferential &ray,
23 | DifferentialGeometry &dg, Sampler &sampler, MemoryPool &pool) const override;
24 | };
25 |
26 | #endif
27 |
28 |
--------------------------------------------------------------------------------
/include/lights/area_light.h:
--------------------------------------------------------------------------------
1 | #ifndef AREA_LIGHT_H
2 | #define AREA_LIGHT_H
3 |
4 | #include
5 | #include "light.h"
6 |
7 | class Geometry;
8 |
9 | /*
10 | * Diffuse area light that can be attached to some arbitrary geometry
11 | * to create an emissive version of the object
12 | * TODO: Currently only spheres are supported
13 | */
14 | class AreaLight : public Light {
15 | Colorf emit;
16 | Geometry *geometry;
17 | float surface_area;
18 |
19 | public:
20 | /*
21 | * Construct the area light with some transformation to be applied to
22 | * the sphere it's emitting from
23 | */
24 | AreaLight(const Transform &to_world, const Colorf &emit, Geometry *geometry, int n_samples = 6);
25 | /*
26 | * Compute the light's emitted radiance in the outgoing direction, w
27 | * from the point on the surface with normal n
28 | */
29 | Colorf radiance(const Point &p, const Normal &n, const Vector &w) const;
30 | /*
31 | * Sample the illumination from the light arriving at the point
32 | * returns the color along with the incident light direction for the point,
33 | * the PDF of the point that was sampled and fills out the occlusion tester
34 | * for shadow testing if the color returned wasn't black
35 | */
36 | Colorf sample(const Point &p, const LightSample &lsample, Vector &w_i,
37 | float &pdf_val, OcclusionTester &occlusion) const override;
38 | /*
39 | * Sample a light ray from the distribution of rays leaving the light
40 | * returning the ray, normal on the light surface where the ray was emitted
41 | * and pdf
42 | */
43 | Colorf sample(const Scene &scene, const LightSample &lsample, const std::array &b,
44 | Ray &ray, Normal &normal, float &pdf_val) const override;
45 | /*
46 | * Compute the total power emitted by the light in the scene
47 | */
48 | Colorf power(const Scene &scene) const override;
49 | /*
50 | * Check if this light is a delta light, eg. a point or directional light
51 | */
52 | bool delta_light() const override;
53 | /*
54 | * Compute the PDF for sampling the point with the incident direction wi
55 | */
56 | float pdf(const Point &p, const Vector &w_i) const override;
57 | };
58 |
59 | #endif
60 |
61 |
--------------------------------------------------------------------------------
/include/lights/light.h:
--------------------------------------------------------------------------------
1 | #ifndef LIGHT_H
2 | #define LIGHT_H
3 |
4 | #include
5 | #include "cache.h"
6 | #include "linalg/transform.h"
7 | #include "film/color.h"
8 | #include "occlusion_tester.h"
9 |
10 | class Scene;
11 |
12 | /*
13 | * Struct to easily pass light samples for choosing a light and then sampling the light
14 | */
15 | struct LightSample {
16 | std::array u;
17 | float light;
18 | };
19 |
20 | /*
21 | * Base class for lights, stores the transformation from world space to light space
22 | * and the number of samples that should be taken when sampling this light
23 | */
24 | class Light {
25 | protected:
26 | //Transforms from light to world space and world to light space
27 | const Transform to_world, to_light;
28 |
29 | public:
30 | //Specify number of samples to take of the light when uniformly sampling all lights
31 | const int n_samples;
32 |
33 | /*
34 | * Create the light with some transformation to world space
35 | * and desired number of samples to be taken
36 | */
37 | Light(const Transform &to_world, int n_samples = 1);
38 | /*
39 | * Sample the illumination from the light arriving at the point
40 | * returns the color along with the incident light direction for the point,
41 | * the PDF of the point that was sampled and fills out the occlusion tester
42 | * for shadow testing if the color returned wasn't black
43 | */
44 | virtual Colorf sample(const Point &p, const LightSample &lsample,
45 | Vector &w_i, float &pdf_val, OcclusionTester &occlusion) const = 0;
46 | /*
47 | * Sample a light ray from the distribution of rays leaving the light
48 | * returning the ray, normal on the light surface where the ray was emitted
49 | * and pdf
50 | */
51 | virtual Colorf sample(const Scene &scene, const LightSample &lsample,
52 | const std::array &b, Ray &ray, Normal &normal, float &pdf_val) const = 0;
53 | /*
54 | * Compute the total power emitted by the light in the scene
55 | */
56 | virtual Colorf power(const Scene &scene) const = 0;
57 | /*
58 | * Check if this light is a delta light, eg. a point or directional light
59 | */
60 | virtual bool delta_light() const = 0;
61 | /*
62 | * Compute the PDF for sampling the point with the incident direction wi
63 | */
64 | virtual float pdf(const Point &p, const Vector &w_i) const = 0;
65 | };
66 |
67 | typedef Cache LightCache;
68 |
69 | #endif
70 |
71 |
--------------------------------------------------------------------------------
/include/lights/occlusion_tester.h:
--------------------------------------------------------------------------------
1 | #ifndef OCCLUSION_TESTER_H
2 | #define OCCLUSION_TESTER_H
3 |
4 | #include "linalg/point.h"
5 | #include "linalg/ray.h"
6 | #include "renderer/renderer.h"
7 | #include "samplers/sampler.h"
8 | #include "memory_pool.h"
9 |
10 | class Scene;
11 |
12 | /*
13 | * A structure used to setup and test lights for occlusion
14 | */
15 | struct OcclusionTester {
16 | Ray ray;
17 |
18 | /*
19 | * Set the occlusion test to be performed between two points
20 | */
21 | void set_points(const Point &a, const Point &b);
22 | /*
23 | * Set the occlusion test to be performed along a ray
24 | */
25 | void set_ray(const Point &p, const Vector &d);
26 | /*
27 | * Test if the ray or segment set is occluded by some object in the scene
28 | */
29 | bool occluded(const Scene &scene);
30 | /*
31 | * Compute the transmittance along the ray/segment that the occlusion test
32 | * was performed on
33 | */
34 | Colorf transmittance(const Scene &scene, const Renderer &renderer, Sampler &sampler,
35 | MemoryPool &pool);
36 | };
37 |
38 | #endif
39 |
40 |
--------------------------------------------------------------------------------
/include/lights/point_light.h:
--------------------------------------------------------------------------------
1 | #ifndef POINT_LIGHT_H
2 | #define POINT_LIGHT_H
3 |
4 | #include "film/color.h"
5 | #include "light.h"
6 |
7 | /*
8 | * Describes a simple point light
9 | */
10 | class PointLight : public Light {
11 | const Point position;
12 | const Colorf intensity;
13 |
14 | public:
15 | /*
16 | * Create a point light with some transformation to world space
17 | * and the desired intensity
18 | */
19 | PointLight(const Transform &to_world, const Colorf &intensity);
20 | /*
21 | * Sample the illumination from the light arriving at the point
22 | * returns the color along with the incident light direction for the point,
23 | * the PDF of the point that was sampled and fills out the occlusion tester
24 | * for shadow testing if the color returned wasn't black
25 | */
26 | Colorf sample(const Point &p, const LightSample &lsample, Vector &w_i,
27 | float &pdf_val, OcclusionTester &occlusion) const override;
28 | /*
29 | * Sample a light ray from the distribution of rays leaving the light
30 | * returning the ray, normal on the light surface where the ray was emitted
31 | * and pdf
32 | */
33 | Colorf sample(const Scene &scene, const LightSample &lsample, const std::array &b,
34 | Ray &ray, Normal &normal, float &pdf_val) const override;
35 | /*
36 | * Compute the total power emitted by the light in the scene
37 | */
38 | Colorf power(const Scene &scene) const override;
39 | /*
40 | * Check if this light is a delta light, eg. a point or directional light
41 | */
42 | bool delta_light() const override;
43 | /*
44 | * Compute the PDF for sampling the point with the incident direction wi
45 | * since there's no chance of sampling the delta distribution randomly this
46 | * just returns 0
47 | */
48 | float pdf(const Point &p, const Vector &w_i) const override;
49 | };
50 |
51 | #endif
52 |
53 |
--------------------------------------------------------------------------------
/include/linalg/animated_transform.h:
--------------------------------------------------------------------------------
1 | #ifndef ANIMATED_TRANSFORM_H
2 | #define ANIMATED_TRANSFORM_H
3 |
4 | #include
5 | #include "linalg/transform.h"
6 | #include "linalg/quaternion.h"
7 |
8 | /*
9 | * Animated transformation based on interpolating two transforms
10 | * between the given times, based on Shoemake and Duff (1992) and
11 | * PBR
12 | */
13 | class AnimatedTransform {
14 | const float start_time, end_time;
15 | const bool animated;
16 | Transform start_transform, end_transform;
17 | //The decomposed start and end transformations
18 | std::array translation;
19 | std::array rotation;
20 | std::array scaling;
21 |
22 | public:
23 | /*
24 | * Create a transform that interpolates between the start and
25 | * end transformations over the start and end times
26 | */
27 | AnimatedTransform(const Transform &start_transform, float start_time,
28 | const Transform &end_transform, float end_time);
29 | /*
30 | * Compute the interpolated transform at some point
31 | */
32 | Transform interpolate(float t) const;
33 | void interpolate(float t, Transform &transform) const;
34 | /*
35 | * Compute the bounds the BBox covers when animated with this transformation
36 | */
37 | BBox motion_bound(const BBox &b) const;
38 | Point operator()(float t, const Point &p) const;
39 | void operator()(float t, const Point &p, Point &out) const;
40 | Vector operator()(float t, const Vector &v) const;
41 | void operator()(float t, const Vector &v, Vector &out) const;
42 | Ray operator()(const Ray &r) const;
43 | void operator()(const Ray &r, Ray &out) const;
44 | RayDifferential operator()(const RayDifferential &r) const;
45 | void operator()(const RayDifferential &r, RayDifferential &out) const;
46 |
47 | private:
48 | /*
49 | * Decompose a transformation into its translation, rotation
50 | * and scaling components as descibed by Shoemake and Duff & PBR
51 | */
52 | static void decompose(const Matrix4 &m, Vector &trans, Quaternion &rot,
53 | Matrix4 &scale);
54 | };
55 |
56 | #endif
57 |
58 |
--------------------------------------------------------------------------------
/include/linalg/matrix4.h:
--------------------------------------------------------------------------------
1 | #ifndef MATRIX4_H
2 | #define MATRIX4_H
3 |
4 | #include
5 | #include
6 |
7 | class Matrix4 {
8 | std::array mat;
9 |
10 | public:
11 | //Initializes the matrix to the identity
12 | Matrix4();
13 | //Create from some existing matrix, m should be row-major
14 | Matrix4(const std::array &m);
15 | Matrix4 inverse() const;
16 | Matrix4 transpose() const;
17 | //Get the value of the matrix at row i, col j
18 | inline const float& at(size_t i, size_t j) const {
19 | return mat[4 * i + j];
20 | }
21 | inline float& at(size_t i, size_t j){
22 | return mat[4 * i + j];
23 | }
24 | //Get a pointer to row i of the matrix
25 | inline const float* operator[](size_t i) const {
26 | return &mat[4 * i];
27 | }
28 | inline float* operator[](size_t i){
29 | return &mat[4 * i];
30 | }
31 | bool operator==(const Matrix4 &m) const;
32 | bool operator!=(const Matrix4 &m) const;
33 | Matrix4 operator+(const Matrix4 &m) const;
34 | Matrix4 operator-(const Matrix4 &m) const;
35 | Matrix4 operator*(const Matrix4 &m) const;
36 | Matrix4 operator*(float s) const;
37 | Matrix4& operator+=(const Matrix4 &m);
38 | Matrix4& operator-=(const Matrix4 &m);
39 | Matrix4& operator*=(const Matrix4 &m);
40 | void print(std::ostream &os) const;
41 | };
42 |
43 | Matrix4 operator*(float s, const Matrix4 &m);
44 | std::ostream& operator<<(std::ostream &os, const Matrix4 &m);
45 |
46 | #endif
47 |
48 |
--------------------------------------------------------------------------------
/include/linalg/quaternion.h:
--------------------------------------------------------------------------------
1 | #ifndef QUATERNION_H
2 | #define QUATERNION_H
3 |
4 | #include
5 | #include "vector.h"
6 | #include "transform.h"
7 |
8 | struct Quaternion;
9 | inline Quaternion operator+(const Quaternion &a, const Quaternion &b);
10 | inline Quaternion operator-(const Quaternion &a, const Quaternion &b);
11 | inline Quaternion operator*(const Quaternion &a, const Quaternion &b);
12 | inline Quaternion operator*(const Quaternion &q, float f);
13 | inline Quaternion operator*(float f, const Quaternion &q);
14 | inline Quaternion operator/(const Quaternion &q, float f);
15 |
16 | /*
17 | * A Quaternion that can be operated on and converted into a transform
18 | */
19 | struct Quaternion {
20 | Vector v;
21 | float w;
22 |
23 | Quaternion(const Vector &v = Vector{0, 0, 0}, float w = 0) : v(v), w(w)
24 | {}
25 | Quaternion(const Transform &t);
26 | /*
27 | * Spherically interpolate between this quaternion and another
28 | */
29 | Quaternion slerp(const Quaternion &q, float t) const;
30 | /*
31 | * Compute the dot product of two quaternions
32 | */
33 | inline float dot(const Quaternion &b) const {
34 | return v.dot(b.v) + w * b.w;
35 | }
36 | inline Quaternion normalized() const {
37 | return *this / std::sqrt(dot(*this));
38 | }
39 | Transform to_transform() const;
40 | inline Quaternion& operator+=(const Quaternion &q){
41 | v += q.v;
42 | w += q.w;
43 | return *this;
44 | }
45 | inline Quaternion& operator-=(const Quaternion &q){
46 | v -= q.v;
47 | w -= q.w;
48 | return *this;
49 | }
50 | inline Quaternion& operator*=(const Quaternion &q){
51 | v = v.cross(q.v) + w * q.v + q.w * v;
52 | w = w * q.w - v.dot(q.v);
53 | return *this;
54 | }
55 | inline Quaternion& operator*=(float f){
56 | v *= f;
57 | w *= f;
58 | return *this;
59 | }
60 | inline Quaternion& operator/=(float f){
61 | v /= f;
62 | w /= f;
63 | return *this;
64 | }
65 | };
66 |
67 | inline Quaternion operator+(const Quaternion &a, const Quaternion &b){
68 | return Quaternion{a.v + b.v, a.w + b.w};
69 | }
70 | inline Quaternion operator-(const Quaternion &a, const Quaternion &b){
71 | return Quaternion{a.v - b.v, a.w - b.w};
72 | }
73 | inline Quaternion operator*(const Quaternion &a, const Quaternion &b){
74 | return Quaternion{a.v.cross(b.v) + a.w * b.v + b.w * a.v,
75 | a.w * b.w - a.v.dot(b.v)};
76 | }
77 | inline Quaternion operator*(const Quaternion &q, float f){
78 | return Quaternion{q.v * f, q.w * f};
79 | }
80 | inline Quaternion operator*(float f, const Quaternion &q){
81 | return Quaternion{q.v * f, q.w * f};
82 | }
83 | inline Quaternion operator/(const Quaternion &q, float f){
84 | return Quaternion{q.v / f, q.w / f};
85 | }
86 |
87 | #endif
88 |
89 |
--------------------------------------------------------------------------------
/include/linalg/ray.h:
--------------------------------------------------------------------------------
1 | #ifndef RAY_H
2 | #define RAY_H
3 |
4 | #include
5 | #include
6 | #include "vector.h"
7 | #include "point.h"
8 |
9 | class DifferentialGeometry;
10 |
11 | /*
12 | * A ray in 3D space starting at o and in direction d
13 | */
14 | struct Ray {
15 | Point o;
16 | Vector d;
17 | //min/max t range of the ray
18 | float min_t, max_t;
19 | //recursion depth of this ray, needed for some algorithms
20 | int depth;
21 | float time;
22 |
23 | inline Ray(const Point &o = Point{}, const Vector &d = Vector{}, float min_t = 0,
24 | float max_t = std::numeric_limits::infinity(), int depth = 0, float time = 0)
25 | : o(o), d(d), min_t(min_t), max_t(max_t), depth(depth), time(time)
26 | {}
27 | /*
28 | * Use to indicate that some ray has spawned this one,
29 | * increasing the recursion depth
30 | */
31 | inline Ray(const Point &o, const Vector &d, const Ray &parent, float min_t = 0,
32 | float max_t = std::numeric_limits::infinity())
33 | : o(o), d(d), min_t(min_t), max_t(max_t), depth(parent.depth + 1), time(parent.time)
34 | {}
35 | //Get a point at some t along the ray
36 | inline Point operator()(float t) const {
37 | return o + t * d;
38 | }
39 | };
40 | inline std::ostream& operator<<(std::ostream &os, const Ray &r){
41 | os << "r ( o = " << r.o << ", d = " << r.d
42 | << ", t = { " << r.min_t << ", " << r.max_t
43 | << "}, depth = " << r.depth << ")";
44 | return os;
45 | }
46 |
47 | /*
48 | * A ray + 2 offset rays (x, y) for use in computing texture anti-aliasing
49 | */
50 | struct RayDifferential : Ray {
51 | //Rays one sample over in x/y
52 | Ray rx, ry;
53 |
54 | inline RayDifferential(const Point &o = Point{}, const Vector &d = Vector{}, float min_t = 0,
55 | float max_t = std::numeric_limits::infinity(), int depth = 0, float time = 0)
56 | : Ray(o, d, min_t, max_t, depth, time)
57 | {}
58 | /*
59 | * Use to indicate that some ray has spawned this one,
60 | * increasing the recursion depth
61 | */
62 | inline RayDifferential(const Point &o, const Vector &d, const Ray &parent, float min_t = 0,
63 | float max_t = std::numeric_limits::infinity())
64 | : Ray(o, d, parent, min_t, max_t)
65 | {}
66 | inline explicit RayDifferential(const Ray &r) : Ray{r} {}
67 | inline void scale_differentials(float s){
68 | rx.o = o + (rx.o - o) * s;
69 | ry.o = o + (ry.o - o) * s;
70 | rx.d = d + (rx.d - d) * s;
71 | ry.d = d + (ry.d - d) * s;
72 | }
73 | inline bool has_differentials() const {
74 | return rx.d.length_sqr() > 0 && ry.d.length_sqr() > 0;
75 | }
76 | };
77 | inline std::ostream& operator<<(std::ostream &os, const RayDifferential &r){
78 | os << "rdiff ( " << static_cast(r) << "\n\trx = "
79 | << r.rx << "\n\try = " << r.ry << " )";
80 | return os;
81 | }
82 |
83 | #endif
84 |
85 |
--------------------------------------------------------------------------------
/include/loaders/async_loader.h:
--------------------------------------------------------------------------------
1 | #ifndef ASYNC_LOADER_H
2 | #define ASYNC_LOADER_H
3 |
4 | #include
5 | #include
6 | #include
7 |
8 | /*
9 | * An asynchronous resource loader: takes a task name,
10 | * the loading function (which should return bool indicating task status)
11 | * and the args for that function
12 | * You can wait for the tasks to finish via the wait function
13 | */
14 | class AsyncLoader {
15 | std::unordered_map> tasks;
16 |
17 | public:
18 | /*
19 | * Add a task to be executed asynchronously and monitored
20 | * by the async loader
21 | */
22 | template
23 | void run_task(const std::string &name, F &&f, Args&&... args){
24 | tasks[name] = std::async(std::launch::async, std::forward(f), std::forward(args)...);
25 | }
26 | /*
27 | * Wait for all tasks to be completed and report their status
28 | */
29 | inline void wait(){
30 | for (auto &task : tasks){
31 | if (task.second.get()){
32 | std::cout << "Task " << task.first << " completed successfully" << std::endl;
33 | }
34 | else {
35 | std::cout << "Task " << task.first << " failed to complete" << std::endl;
36 | }
37 | }
38 | }
39 | };
40 |
41 | #endif
42 |
43 |
--------------------------------------------------------------------------------
/include/loaders/load_filter.h:
--------------------------------------------------------------------------------
1 | #ifndef LOAD_FILTER_H
2 | #define LOAD_FILTER_H
3 |
4 | #include
5 | #include
6 | #include "filters/filter.h"
7 |
8 | /*
9 | * Load the filter for the scene from the tag
10 | * XMLElement passed should be the element for the tag
11 | */
12 | std::unique_ptr load_filter(tinyxml2::XMLElement *elem);
13 |
14 | #endif
15 |
16 |
--------------------------------------------------------------------------------
/include/loaders/load_light.h:
--------------------------------------------------------------------------------
1 | #ifndef LOAD_LIGHT_H
2 | #define LOAD_LIGHT_H
3 |
4 | #include
5 | #include "lights/light.h"
6 |
7 | /*
8 | * Load all the light information into the light cache.
9 | * elem should be the first light XML element in the file
10 | */
11 | void load_lights(tinyxml2::XMLElement *elem, LightCache &cache);
12 |
13 | #endif
14 |
15 |
--------------------------------------------------------------------------------
/include/loaders/load_material.h:
--------------------------------------------------------------------------------
1 | #ifndef LOAD_MATERIAL_H
2 | #define LOAD_MATERIAL_H
3 |
4 | #include
5 | #include "material/material.h"
6 | #include "textures/texture.h"
7 |
8 | /*
9 | * Load all the material information into the material cache. elem should
10 | * be the first material XML element in the file
11 | * We do this in a pre-pass to avoid having to store the material
12 | * names on the nodes and do a post-load pass to set up the node
13 | * materials like is done in Cem's loading code, due to how the XML
14 | * file is layed out with materials coming after objects
15 | * Loaded textures for the materials will be placed in the texture cache
16 | */
17 | void load_materials(tinyxml2::XMLElement *elem, MaterialCache &cache, TextureCache &tcache,
18 | const std::string &file);
19 |
20 | #endif
21 |
22 |
--------------------------------------------------------------------------------
/include/loaders/load_renderer.h:
--------------------------------------------------------------------------------
1 | #ifndef LOAD_RENDERER_H
2 | #define LOAD_RENDERER_H
3 |
4 | #include
5 | #include
6 | #include "renderer/renderer.h"
7 | #include "integrator/surface_integrator.h"
8 | #include "integrator/volume_integrator.h"
9 |
10 | /*
11 | * Load the surface integrator from the renderer child of the config element passed
12 | */
13 | std::unique_ptr load_surface_integrator(tinyxml2::XMLElement *elem);
14 | /*
15 | * Load the volume integrator from the vol_integrator child of the config element passed
16 | */
17 | std::unique_ptr load_volume_integrator(tinyxml2::XMLElement *elem);
18 |
19 | #endif
20 |
21 |
--------------------------------------------------------------------------------
/include/loaders/load_sampler.h:
--------------------------------------------------------------------------------
1 | #ifndef LOAD_SAMPLER_H
2 | #define LOAD_SAMPLER_H
3 |
4 | #include
5 | #include
6 | #include "samplers/sampler.h"
7 |
8 | /*
9 | * Load sampler configuration from the config tag passed.
10 | * Sampler information is loaded from the tag
11 | * also takes the image width and height to set the sampler to sample
12 | */
13 | std::unique_ptr load_sampler(tinyxml2::XMLElement *elem, size_t w, size_t h);
14 |
15 | #endif
16 |
17 |
--------------------------------------------------------------------------------
/include/loaders/load_scene.h:
--------------------------------------------------------------------------------
1 | #ifndef LOAD_SCENE_H
2 | #define LOAD_SCENE_H
3 |
4 | #include
5 | #include
6 | #include "linalg/vector.h"
7 | #include "linalg/transform.h"
8 | #include "geometry/geometry.h"
9 | #include "scene.h"
10 |
11 | #ifdef _WIN32
12 | const char PATH_SEP = '\\';
13 | #else
14 | const char PATH_SEP = '/';
15 | #endif
16 |
17 | /*
18 | * Load a scene as described by the XML document and return it and
19 | * set its max ray recursion depth to the desired value
20 | * Based off of Cem's load scene utility but migrated to TinyXML-2
21 | */
22 | Scene load_scene(const std::string &file);
23 | /*
24 | * Read the x,y,z attributes of the XMLElement and return it
25 | */
26 | void read_vector(tinyxml2::XMLElement *elem, Vector &v);
27 | /*
28 | * Read the r,g,b attributes of the XMLElement and return it
29 | */
30 | void read_color(tinyxml2::XMLElement *elem, Colorf &c);
31 | /*
32 | * Read the x,y,z attributes of the XMLElement and return it
33 | */
34 | void read_point(tinyxml2::XMLElement *elem, Point &p);
35 | /*
36 | * Read a transform from the scale, rotate and translate elements
37 | * which should be children of the element passed
38 | */
39 | void read_transform(tinyxml2::XMLElement *elem, Transform &t);
40 | /*
41 | * Read the float attribute of the XMLElement and return it
42 | * optionally passing the attribute name to read from. Default is value
43 | */
44 | void read_float(tinyxml2::XMLElement *elem, float &f, const std::string &attrib = "value");
45 | /*
46 | * Read the int attribute of the XMLElement and return it
47 | * optionally passing the attribute name to read from. Default is value
48 | */
49 | void read_int(tinyxml2::XMLElement *elem, int &i, const std::string &attrib = "value");
50 |
51 | #endif
52 |
53 |
--------------------------------------------------------------------------------
/include/loaders/load_texture.h:
--------------------------------------------------------------------------------
1 | #ifndef LOAD_TEXTURE_H
2 | #define LOAD_TEXTURE_H
3 |
4 | #include
5 | #include
6 | #include "textures/texture.h"
7 |
8 | /*
9 | * Load the texture for the XML element into the texture cache
10 | * and return a non-owning pointer to it. If the item is
11 | * already in the cache the existing entry will be returned
12 | * name specifies the material name, to be used for prefixing
13 | * generated texture names
14 | * Also takes the XML file name so we can construct paths to the textures
15 | */
16 | Texture* load_texture(tinyxml2::XMLElement *elem, const std::string &mat_name,
17 | TextureCache &cache, const std::string &file);
18 | /*
19 | * Load the texture color as a constant texture by computing the RGB color
20 | * from the sampled spectrum data in the spd file. spd files are from PBRT,
21 | * the ones supported (metals only) can be found at https://github.com/mmp/pbrt-v2/tree/master/scenes/spds/metals
22 | */
23 | Texture* load_spd(tinyxml2::XMLElement *elem, const std::string &mat_name,
24 | TextureCache &cache, const std::string &file);
25 |
26 | #endif
27 |
28 |
--------------------------------------------------------------------------------
/include/loaders/load_volume.h:
--------------------------------------------------------------------------------
1 | #ifndef LOAD_VOLUMES_H
2 | #define LOAD_VOLUMES_H
3 |
4 | #include
5 | #include
6 | #include "scene.h"
7 | #include "volume/volume.h"
8 | #include "volume/volume_node.h"
9 |
10 | /*
11 | * Load the volume specified by the element passed, returns nullptr if loading failed
12 | */
13 | Volume* load_volume(tinyxml2::XMLElement *elem, VolumeCache &cache, const std::string &scene_file);
14 | /*
15 | * Load the volume node at the element
16 | * will also push the volume's transform onto the stack
17 | */
18 | void load_volume_node(tinyxml2::XMLElement *elem, Scene &scene, std::stack &transform_stack,
19 | const std::string &file);
20 |
21 | #endif
22 |
23 |
--------------------------------------------------------------------------------
/include/material/anisotropic_distribution.h:
--------------------------------------------------------------------------------
1 | #ifndef ANISOTROPIC_DISTRIBUTION_H
2 | #define ANISOTROPIC_DISTRIBUTION_H
3 |
4 | #include "microfacet_distribution.h"
5 |
6 | /*
7 | * Implementation of the anisotropic microfacet distribution described by
8 | * Ashikhmin and Shirley
9 | */
10 | class AnisotropicDistribution : public MicrofacetDistribution {
11 | //The exponents for microfacets exactly along the x/y axes
12 | float exp_x, exp_y;
13 |
14 | public:
15 | /*
16 | * Create the anisotropic distribution specifying the exponents for microfacets
17 | * oriented exactly along the x and y axes
18 | */
19 | AnisotropicDistribution(float exp_x, float exp_y);
20 | /*
21 | * Compute the probability density for microfacets to be
22 | * oriented with normal = w_h in this distribution
23 | */
24 | float operator()(const Vector &w_h) const override;
25 | /*
26 | * Sample the distribution for some outgoing direction, returning the incident
27 | * direction and the PDF for this pair of vectors
28 | */
29 | void sample(const Vector &w_o, Vector &w_i, const std::array &u, float &pdf_val) const override;
30 | /*
31 | * Sample the PDF of the distribution for some pair of directions
32 | */
33 | float pdf(const Vector &w_o, const Vector &w_i) const override;
34 |
35 | private:
36 | /*
37 | * Sample the first quadrant of the unit hemisphere for the distribution
38 | */
39 | void sample_first_quadrant(float u_0, float u_1, float &phi, float &cos_theta) const;
40 | };
41 |
42 | #endif
43 |
44 |
--------------------------------------------------------------------------------
/include/material/blinn_distribution.h:
--------------------------------------------------------------------------------
1 | #ifndef BLINN_DISTRIBUTION_H
2 | #define BLINN_DISTRIBUTION_H
3 |
4 | #include "microfacet_distribution.h"
5 |
6 | /*
7 | * Implementation of the microfacet distribution described by Blinn
8 | */
9 | class BlinnDistribution : public MicrofacetDistribution {
10 | float exponent;
11 |
12 | public:
13 | /*
14 | * Create the microfacet distribution with some exponent, the higher
15 | * the exponent the more shiny the surface appears
16 | */
17 | BlinnDistribution(float exponent);
18 | /*
19 | * Compute the probability density for microfacets to be
20 | * oriented with normal = w_h in this distribution
21 | */
22 | float operator()(const Vector &w_h) const override;
23 | /*
24 | * Sample the distribution for some outgoing direction, returning the incident
25 | * direction and the PDF for this pair of vectors
26 | */
27 | void sample(const Vector &w_o, Vector &w_i, const std::array &u, float &pdf_val) const override;
28 | /*
29 | * Sample the PDF of the distribution for some pair of directions
30 | */
31 | float pdf(const Vector &w_o, const Vector &w_i) const override;
32 | };
33 |
34 | #endif
35 |
36 |
--------------------------------------------------------------------------------
/include/material/btdf_adapter.h:
--------------------------------------------------------------------------------
1 | #ifndef BTDF_ADAPTER_H
2 | #define BTDF_ADAPTER_H
3 |
4 | #include "bxdf.h"
5 |
6 | /*
7 | * Take an existing BRDF model and use it as a transmissive model,
8 | * or the other way around. This adapter flips the reflection and transmission
9 | * type flags and flips incident vectors to the other hemisphere
10 | */
11 | class BTDFAdapter : public BxDF {
12 | BxDF *bxdf;
13 |
14 | public:
15 | /*
16 | * Create the adapter to adapt the existing BxDF model from reflection to transmission
17 | * or vice-versa
18 | */
19 | BTDFAdapter(BxDF *b);
20 | /*
21 | * Compute the value of the BxDF for some incident and outgoing directions
22 | */
23 | Colorf operator()(const Vector &w_o, const Vector &w_i) const override;
24 | /*
25 | * Sample the BxDFs value for some outgoing direction using the random values
26 | * passed and returning the incident light direction
27 | */
28 | Colorf sample(const Vector &w_o, Vector &w_i, const std::array &samples, float &pdf_val) const override;
29 | /*
30 | * Compute the hemispherical-directional reflectance function using the samples passed
31 | */
32 | Colorf rho_hd(const Vector &w_o, const std::array *samples, int n_samples) const override;
33 | /*
34 | * Compute the hemispherical-hemispherical reflectance function using the samples passed
35 | * samples_a and samples_b should contain the same number of samples
36 | */
37 | Colorf rho_hh(const std::array *samples_a, const std::array *samples_b,
38 | int n_samples) const override;
39 | /*
40 | * Compute the probability density function for sampling the directions passed
41 | */
42 | float pdf(const Vector &w_o, const Vector &w_i) const override;
43 | /*
44 | * Flip the vector to the other hemisphere in shading space
45 | */
46 | static Vector flip_hemisphere(const Vector &v);
47 | };
48 |
49 | #endif
50 |
51 |
--------------------------------------------------------------------------------
/include/material/fresnel.h:
--------------------------------------------------------------------------------
1 | #ifndef FRESNEL_H
2 | #define FRESNEL_H
3 |
4 | #include "film/color.h"
5 |
6 | /*
7 | * Implementation of BxDF motivated by PBR
8 | */
9 | /*
10 | * Compute the Fresnel reflectance term for dielectric materials
11 | */
12 | Colorf fresnel_dielectric(float cos_i, float cos_t, float eta_i, float eta_t);
13 | /*
14 | * Compute the Fresnel reflectance term for conductive materials
15 | */
16 | Colorf fresnel_conductor(float cos_i, const Colorf &eta, const Colorf &k);
17 |
18 | /*
19 | * Interface for computing Fresnel terms generically
20 | */
21 | class Fresnel {
22 | public:
23 | /*
24 | * Evaluate the Fresnel reflectance term for light incident along some angle
25 | */
26 | virtual Colorf operator()(float cos_i) const = 0;
27 | };
28 |
29 | /*
30 | * Computes the Fresnel reflectance term for dielectrics using fresnel_dielectric
31 | */
32 | class FresnelDielectric : public Fresnel {
33 | public:
34 | const float eta_i, eta_t;
35 |
36 | /*
37 | * Construct the Fresnel dielectric evaluator for the interface
38 | * between two materials
39 | */
40 | FresnelDielectric(float eta_i, float eta_t);
41 | /*
42 | * Evaluate the Fresnel reflectance term for light incident along some angle
43 | */
44 | Colorf operator()(float cos_i) const override;
45 | };
46 |
47 | /*
48 | * Computes the Fresnel reflectance term for conductors using fresnel_conductor
49 | */
50 | class FresnelConductor : public Fresnel {
51 | public:
52 | const Colorf eta, k;
53 |
54 | /*
55 | * Construct the Fresnel conductor evaluator for the conductor being hit
56 | */
57 | FresnelConductor(const Colorf &eta, const Colorf &k);
58 | /*
59 | * Evaluate the Fresnel reflectance term for light incident along some angle
60 | */
61 | Colorf operator()(float cos_i) const override;
62 | };
63 |
64 | /*
65 | * No-op Fresnel, simply returns 1
66 | */
67 | class FresnelNoOp : public Fresnel {
68 | public:
69 | /*
70 | * Evaluate the Fresnel reflectance term for light incident along some angle
71 | */
72 | Colorf operator()(float cos_i) const override;
73 | };
74 |
75 | /*
76 | * FresnelFlip takes the Fresnel value computed by the one passed and returns
77 | * 1 - fresnel_value, eg. given a reflective Fresnel value this will return the
78 | * transmission Fresnel value
79 | */
80 | class FresnelFlip : public Fresnel {
81 | const Fresnel *fresnel;
82 |
83 | public:
84 | FresnelFlip(const Fresnel *fresnel);
85 | /*
86 | * Evaluate the Fresnel reflectance term for light incident along some angle
87 | */
88 | Colorf operator()(float cos_i) const override;
89 | };
90 |
91 | #endif
92 |
93 |
--------------------------------------------------------------------------------
/include/material/glass_material.h:
--------------------------------------------------------------------------------
1 | #ifndef GLASS_MATERIAL_H
2 | #define GLASS_MATERIAL_H
3 |
4 | #include "textures/texture.h"
5 | #include "material.h"
6 |
7 | /*
8 | * A material describing perfectly smooth glass with specular reflection
9 | * and transmission
10 | */
11 | class GlassMaterial : public Material {
12 | const Texture *reflect, *transmit;
13 | const float refr_index;
14 |
15 | public:
16 | /*
17 | * Create the glass material, specifying the colors and refractive index
18 | */
19 | GlassMaterial(const Texture *reflect, const Texture *transmit, float refr_index);
20 | /*
21 | * Get the BSDF to compute the shading for the material at this
22 | * piece of geometry
23 | */
24 | BSDF* get_bsdf(const DifferentialGeometry &dg, MemoryPool &pool) const override;
25 | };
26 |
27 | #endif
28 |
29 |
--------------------------------------------------------------------------------
/include/material/lambertian.h:
--------------------------------------------------------------------------------
1 | #ifndef LAMBERTIAN_H
2 | #define LAMBERTIAN_H
3 |
4 | #include "bxdf.h"
5 |
6 | /*
7 | * BRDF describing the Lambertian diffuse reflectance model
8 | */
9 | class Lambertian : public BxDF {
10 | Colorf reflectance;
11 |
12 | public:
13 | /*
14 | * Create a Lambertian BRDF with some color
15 | */
16 | Lambertian(const Colorf &reflectance);
17 | /*
18 | * Compute the value of the BxDF for some incident and outgoing directions
19 | */
20 | Colorf operator()(const Vector &w_o, const Vector &w_i) const override;
21 | /*
22 | * Compute the hemispherical-directional reflectance function using the samples passed
23 | */
24 | Colorf rho_hd(const Vector &w_o, const std::array *samples, int n_samples) const override;
25 | /*
26 | * Compute the hemispherical-hemispherical reflectance function using the samples passed
27 | */
28 | Colorf rho_hh(const std::array *samples_a, const std::array *samples_b,
29 | int n_samples) const override;
30 | };
31 |
32 | #endif
33 |
34 |
--------------------------------------------------------------------------------
/include/material/material.h:
--------------------------------------------------------------------------------
1 | #ifndef MATERIAL_H
2 | #define MATERIAL_H
3 |
4 | #include
5 | #include "geometry/differential_geometry.h"
6 | #include "cache.h"
7 | #include "memory_pool.h"
8 | #include "bsdf.h"
9 |
10 | /*
11 | * Base interface for materials to implement, should return the BSDF that
12 | * describes the material properties at the point
13 | */
14 | class Material {
15 | public:
16 | /*
17 | * Get the BSDF to compute the shading for the material at this
18 | * piece of geometry. Allocation of the BxDFs and BSDF will be done in the
19 | * memory pool passed
20 | */
21 | virtual BSDF* get_bsdf(const DifferentialGeometry &dg, MemoryPool &pool) const = 0;
22 | };
23 |
24 | typedef Cache MaterialCache;
25 |
26 | #endif
27 |
28 |
--------------------------------------------------------------------------------
/include/material/matte_material.h:
--------------------------------------------------------------------------------
1 | #ifndef MATTE_MATERIAL_H
2 | #define MATTE_MATERIAL_H
3 |
4 | #include "textures/texture.h"
5 | #include "material.h"
6 |
7 | /*
8 | * A material describing a purely diffuse surface, parameterized by
9 | * its diffuse color and a roughness value
10 | */
11 | class MatteMaterial : public Material {
12 | const Texture *diffuse;
13 | const float roughness;
14 |
15 | public:
16 | /*
17 | * Create the matte material specifying the texture to use for diffuse color
18 | * and a scalar roughness value (TODO roughness can be a texture)
19 | */
20 | MatteMaterial(const Texture *diffuse, float roughness);
21 | /*
22 | * Get the BSDF to compute the shading for the material at this
23 | * piece of geometry
24 | */
25 | BSDF* get_bsdf(const DifferentialGeometry &dg, MemoryPool &pool) const override;
26 | };
27 |
28 | #endif
29 |
30 |
--------------------------------------------------------------------------------
/include/material/merl_brdf.h:
--------------------------------------------------------------------------------
1 | #ifndef MERL_BRDF_H
2 | #define MERL_BRDF_H
3 |
4 | #include
5 | #include "linalg/util.h"
6 | #include "bxdf.h"
7 |
8 | /*
9 | * BRDF that uses measured data from "A Data-Driven Reflectance Model",
10 | * by Wojciech Matusik, Hanspeter Pfister, Matt Brand and Leonard McMillan,
11 | * in ACM Transactions on Graphics 22, 3(2003), 759-769.
12 | *
13 | * to model the surface reflection properties. Implemnentation based on
14 | * the method introduced in PBR
15 | */
16 | class MerlBRDF : public BxDF {
17 | //The measured brdf data in regular halfangle format
18 | const std::vector &brdf;
19 | const int n_theta_h, n_theta_d, n_phi_d;
20 |
21 | public:
22 | /*
23 | * Create the Merl BRDF using some loaded data for modeling the BRDF
24 | */
25 | MerlBRDF(const std::vector &brdf, int n_theta_h, int n_theta_d, int n_phi_d);
26 | /*
27 | * Compute the value of the BxDF for some incident and outgoing directions
28 | */
29 | Colorf operator()(const Vector &w_o, const Vector &w_i) const override;
30 |
31 | private:
32 | /*
33 | * Remap values from the angular value to the index in the MERL BRDF data table
34 | */
35 | static constexpr inline int map_index(float val, float max, int n_vals){
36 | return clamp(static_cast(val / max * n_vals), 0, n_vals - 1);
37 | }
38 | };
39 |
40 | #endif
41 |
42 |
--------------------------------------------------------------------------------
/include/material/merl_material.h:
--------------------------------------------------------------------------------
1 | #ifndef MERL_MATERIAL_H
2 | #define MERL_MATERIAL_H
3 |
4 | #include
5 | #include
6 | #include "material.h"
7 |
8 | /*
9 | * Material that uses measured data from "A Data-Driven Reflectance Model",
10 | * by Wojciech Matusik, Hanspeter Pfister, Matt Brand and Leonard McMillan,
11 | * in ACM Transactions on Graphics 22, 3(2003), 759-769.
12 | *
13 | * to model the surface reflection properties. Implemnentation based on
14 | * the method introduced in PBR
15 | */
16 | class MerlMaterial : public Material {
17 | int n_theta_h, n_theta_d, n_phi_d;
18 | //The measured brdf data in regular halfangle format
19 | std::vector brdf;
20 |
21 | public:
22 | /*
23 | * Create the measured material, loading the BRDF data from some MERL
24 | * binary BRDF file
25 | */
26 | MerlMaterial(const std::string &file);
27 | /*
28 | * Get the BSDF to compute the shading for the material at this
29 | * piece of geometry. Allocation of the BxDFs and BSDF will be done in the
30 | * memory pool passed
31 | */
32 | BSDF* get_bsdf(const DifferentialGeometry &dg, MemoryPool &pool) const override;
33 |
34 | private:
35 | /*
36 | * Load the MERL BRDF data from a file, returns true if successful
37 | * Implemented as described in PBR
38 | */
39 | bool load_brdf(const std::string &file);
40 | };
41 |
42 | #endif
43 |
44 |
--------------------------------------------------------------------------------
/include/material/metal_material.h:
--------------------------------------------------------------------------------
1 | #ifndef METAL_MATERIAL_H
2 | #define METAL_MATERIAL_H
3 |
4 | #include "textures/texture.h"
5 | #include "material.h"
6 |
7 | /*
8 | * A material that models a metal surface, described by its
9 | * refractive index and absorption coefficient
10 | */
11 | class MetalMaterial : public Material {
12 | const Texture *refr_index, *absoption_coef;
13 | const float rough_x, rough_y;
14 |
15 | public:
16 | /*
17 | * Create the metal, specifying the textures to be used for its attributes
18 | * To create an anisotropic material specify both rough_x and rough_y, for a
19 | * non-aniostropic material only specify rough_x
20 | */
21 | MetalMaterial(const Texture *refr_index, const Texture *absoption_coef, float rough_x, float rough_y = -1);
22 | /*
23 | * Get the BSDF to compute the shading for the material at this
24 | * piece of geometry
25 | */
26 | BSDF* get_bsdf(const DifferentialGeometry &dg, MemoryPool &pool) const override;
27 | };
28 |
29 | #endif
30 |
31 |
--------------------------------------------------------------------------------
/include/material/microfacet_distribution.h:
--------------------------------------------------------------------------------
1 | #ifndef MICROFACET_DISTRIBUTION_H
2 | #define MICROFACET_DISTRIBUTION_H
3 |
4 | #include "linalg/vector.h"
5 |
6 | /*
7 | * Describes the distribution of microfacets on a surface for microfacet
8 | * based reflectance models
9 | */
10 | class MicrofacetDistribution {
11 | public:
12 | /*
13 | * Compute the probability density for microfacets to be
14 | * oriented with normal = w_h in this distribution
15 | */
16 | virtual float operator()(const Vector &w_h) const = 0;
17 | /*
18 | * Sample the distribution for some outgoing direction, returning the incident
19 | * direction and the PDF for this pair of vectors
20 | */
21 | virtual void sample(const Vector &w_o, Vector &w_i, const std::array &u, float &pdf_val) const = 0;
22 | /*
23 | * Sample the PDF of the distribution for some pair of directions
24 | */
25 | virtual float pdf(const Vector &w_o, const Vector &w_i) const = 0;
26 | /*
27 | * Compute the geometric attenuation term for the distribution
28 | * for the outgoing and incident directions for the microfacets
29 | * with normal = w_h
30 | */
31 | static float geom_atten(const Vector &w_o, const Vector &w_i, const Vector &w_h);
32 | };
33 |
34 | #endif
35 |
36 |
--------------------------------------------------------------------------------
/include/material/mix_material.h:
--------------------------------------------------------------------------------
1 | #ifndef MIX_MATERIAL_H
2 | #define MIX_MATERIAL_H
3 |
4 | #include "textures/texture.h"
5 | #include "material/material.h"
6 |
7 | /*
8 | * Material that mixes the output of two other materials by some color
9 | * scaling factor
10 | */
11 | class MixMaterial : public Material {
12 | const Material *mat_a, *mat_b;
13 | const Texture *scale;
14 |
15 | public:
16 | /*
17 | * Specify the two materials to be mixed and the scale level for material a,
18 | * material b is scaled by (1 - scale)
19 | */
20 | MixMaterial(const Material *mat_a, const Material *mat_b, const Texture *scale);
21 | /*
22 | * Get the BSDF to compute the shading for the material at this
23 | * piece of geometry. Allocation of the BxDFs and BSDF will be done in the
24 | * memory pool passed
25 | */
26 | BSDF* get_bsdf(const DifferentialGeometry &dg, MemoryPool &pool) const override;
27 | };
28 |
29 | #endif
30 |
31 |
--------------------------------------------------------------------------------
/include/material/oren_nayer.h:
--------------------------------------------------------------------------------
1 | #ifndef OREN_NAYER_H
2 | #define OREN_NAYER_H
3 |
4 | #include "bxdf.h"
5 |
6 | /*
7 | * BRDF describing the OrenNayer microfacet diffuse reflection model
8 | */
9 | class OrenNayer : public BxDF {
10 | Colorf reflectance;
11 | float a, b;
12 |
13 | public:
14 | /*
15 | * Create an OrenNayer BRDF with some color and specify the sigma
16 | * for the Gaussian microfacet distribution, sigma should be in degrees
17 | */
18 | OrenNayer(const Colorf &reflectance, float sigma);
19 | /*
20 | * Compute the value of the BxDF for some incident and outgoing directions
21 | */
22 | Colorf operator()(const Vector &w_o, const Vector &w_i) const override;
23 | };
24 |
25 | #endif
26 |
27 |
--------------------------------------------------------------------------------
/include/material/plastic_material.h:
--------------------------------------------------------------------------------
1 | #ifndef PLASTIC_MATERIAL_H
2 | #define PLASTIC_MATERIAL_H
3 |
4 | #include "textures/texture.h"
5 | #include "material.h"
6 |
7 | /*
8 | * A material that models plastic, parameterized by diffuse and specular
9 | * colors along with a roughness value
10 | */
11 | class PlasticMaterial : public Material {
12 | const Texture *diffuse, *specular;
13 | const float rough_x, rough_y;
14 |
15 | public:
16 | /*
17 | * Create the plastic material specifying the textures to be used for
18 | * the diffuse and specular colors
19 | */
20 | PlasticMaterial(const Texture *diffuse, const Texture *specular, float rough_x, float rough_y);
21 | /*
22 | * Get the BSDF to compute the shading for the material at this
23 | * piece of geometry
24 | */
25 | BSDF* get_bsdf(const DifferentialGeometry &dg, MemoryPool &pool) const override;
26 | };
27 |
28 | #endif
29 |
30 |
--------------------------------------------------------------------------------
/include/material/scaled_bxdf.h:
--------------------------------------------------------------------------------
1 | #ifndef SCALED_BXDF_H
2 | #define SCALED_BXDF_H
3 |
4 | #include "bxdf.h"
5 |
6 | /*
7 | * A BxDF that scales the output of the given BxDF by some color
8 | */
9 | class ScaledBxDF : public BxDF {
10 | BxDF *bxdf;
11 | Colorf scale;
12 |
13 | public:
14 | /*
15 | * Create the scaled BxDF to apply the desired scaling to the output
16 | * of the BxDF passed
17 | */
18 | ScaledBxDF(BxDF *bxdf, const Colorf &scale);
19 | /*
20 | * Compute the value of the BxDF for some incident and outgoing directions
21 | */
22 | Colorf operator()(const Vector &w_o, const Vector &w_i) const override;
23 | /*
24 | * Sample the BxDFs value for some outgoing direction using the random values
25 | * passed and returning the incident light direction
26 | */
27 | Colorf sample(const Vector &w_o, Vector &w_i, const std::array &samples, float &pdf_val) const override;
28 | /*
29 | * Compute the hemispherical-directional reflectance function using the samples passed
30 | */
31 | Colorf rho_hd(const Vector &w_o, const std::array *samples, int n_samples) const override;
32 | /*
33 | * Compute the hemispherical-hemispherical reflectance function using the samples passed
34 | * samples_a and samples_b should contain the same number of samples
35 | */
36 | Colorf rho_hh(const std::array *samples_a, const std::array *samples_b,
37 | int n_samples) const override;
38 | /*
39 | * Compute the probability density function for sampling the directions passed
40 | */
41 | float pdf(const Vector &w_o, const Vector &w_i) const override;
42 | };
43 |
44 | #endif
45 |
46 |
--------------------------------------------------------------------------------
/include/material/specular_metal_material.h:
--------------------------------------------------------------------------------
1 | #ifndef SPECULAR_METAL_MATERIAL
2 | #define SPECULAR_METAL_MATERIAL
3 |
4 | #include "textures/texture.h"
5 | #include "material.h"
6 |
7 | /*
8 | * A material that models a perfectly specular metal surface, described by its
9 | * refractive index and absorption coefficient
10 | */
11 | class SpecularMetalMaterial : public Material {
12 | const Texture *refr_index, *absoption_coef;
13 |
14 | public:
15 | /*
16 | * Create the metal, specifying the textures to be used for its attributes
17 | * To create an anisotropic material specify both rough_x and rough_y, for a
18 | * non-aniostropic material only specify rough_x
19 | */
20 | SpecularMetalMaterial(const Texture *refr_index, const Texture *absoption_coef);
21 | /*
22 | * Get the BSDF to compute the shading for the material at this
23 | * piece of geometry
24 | */
25 | BSDF* get_bsdf(const DifferentialGeometry &dg, MemoryPool &pool) const override;
26 | };
27 |
28 | #endif
29 |
30 |
--------------------------------------------------------------------------------
/include/material/specular_reflection.h:
--------------------------------------------------------------------------------
1 | #ifndef SPECULAR_REFLECTION_H
2 | #define SPECULAR_REFLECTION_H
3 |
4 | #include "fresnel.h"
5 | #include "bxdf.h"
6 |
7 | /*
8 | * BRDF describing perfectly specular reflection, eg. a delta distribution
9 | */
10 | class SpecularReflection : public BxDF {
11 | Colorf reflection;
12 | Fresnel *fresnel;
13 |
14 | public:
15 | /*
16 | * Create a specularly reflective BRDF with some reflective color
17 | * and desired Fresnel component
18 | */
19 | SpecularReflection(const Colorf &reflection, Fresnel *fresnel);
20 | /*
21 | * Compute the value of the BxDF for some incident and outgoing directions
22 | * Note that because this is a delta distribution this will always return 0,
23 | * sample must be used for delta distributions instead
24 | */
25 | Colorf operator()(const Vector &w_o, const Vector &w_i) const override;
26 | /*
27 | * Sample the BRDFs value for some outgoing direction using the random values
28 | * passed and returning the incident light direction
29 | */
30 | Colorf sample(const Vector &w_o, Vector &w_i, const std::array &samples, float &pdf_val) const override;
31 | /*
32 | * Compute the probability density function for sampling the directions passed
33 | * Note again that this returns 0 because this is a delta distribution
34 | */
35 | float pdf(const Vector &w_o, const Vector &w_i) const override;
36 | };
37 |
38 | #endif
39 |
40 |
--------------------------------------------------------------------------------
/include/material/specular_transmission.h:
--------------------------------------------------------------------------------
1 | #ifndef SPECULAR_TRANSMISSION_H
2 | #define SPECULAR_TRANSMISSION_H
3 |
4 | #include "fresnel.h"
5 | #include "bxdf.h"
6 |
7 | /*
8 | * BTDF describing perfectly specular transmission eg. a delta distribution
9 | */
10 | class SpecularTransmission : public BxDF {
11 | Colorf transmission;
12 | FresnelDielectric *fresnel;
13 |
14 | public:
15 | /*
16 | * Create a specularly transmissive BTDF with some transmissive color
17 | * and desired Fresnel component
18 | */
19 | SpecularTransmission(const Colorf &transmission, FresnelDielectric *fresnel);
20 | /*
21 | * Compute the value of the BxDF for some incident and outgoing directions
22 | * Note that because this is a delta distribution this will always return 0,
23 | * sample must be used for delta distributions instead
24 | */
25 | Colorf operator()(const Vector &w_o, const Vector &w_i) const override;
26 | /*
27 | * Sample the BTDFs value for some outgoing direction using the random values
28 | * passed and returning the incident light direction
29 | */
30 | Colorf sample(const Vector &w_o, Vector &w_i, const std::array &samples, float &pdf_val) const override;
31 | /*
32 | * Compute the probability density function for sampling the directions passed
33 | * Note again that this returns 0 because this is a delta distribution
34 | */
35 | float pdf(const Vector &w_o, const Vector &w_i) const override;
36 | };
37 |
38 | #endif
39 |
40 |
--------------------------------------------------------------------------------
/include/material/torrance_sparrow.h:
--------------------------------------------------------------------------------
1 | #ifndef TORRANCE_SPARROW_H
2 | #define TORRANCE_SPARROW_H
3 |
4 | #include
5 | #include "bxdf.h"
6 | #include "microfacet_distribution.h"
7 | #include "fresnel.h"
8 |
9 | /*
10 | * Implementation of the Torrance-Sparrow microfacet BRDF model
11 | */
12 | class TorranceSparrow : public BxDF {
13 | Colorf reflectance;
14 | Fresnel *fresnel;
15 | MicrofacetDistribution *distribution;
16 |
17 | public:
18 | /*
19 | * Create the microface BRDF with some color, fresnel term
20 | * and desired microfacet distribution
21 | */
22 | TorranceSparrow(const Colorf &reflectance, Fresnel *fresnel, MicrofacetDistribution *distribution);
23 | /*
24 | * Compute the value of the BxDF for some incident and outgoing directions
25 | */
26 | Colorf operator()(const Vector &w_o, const Vector &w_i) const override;
27 | /*
28 | * Sample the BxDFs value for some outgoing direction using the random values
29 | * passed and returning the incident light direction
30 | */
31 | Colorf sample(const Vector &w_o, Vector &w_i, const std::array &samples, float &pdf_val) const override;
32 | /*
33 | * Compute the probability density function for sampling the directions passed
34 | */
35 | float pdf(const Vector &w_o, const Vector &w_i) const override;
36 | };
37 |
38 | #endif
39 |
40 |
--------------------------------------------------------------------------------
/include/material/translucent_material.h:
--------------------------------------------------------------------------------
1 | #ifndef TRANSLUCENT_MATERIAL_H
2 | #define TRANSLUCENT_MATERIAL_H
3 |
4 | #include "textures/texture.h"
5 | #include "material.h"
6 |
7 | /*
8 | * A material modeling a translucent dielectric, eg. transparent
9 | * plastics or similar
10 | */
11 | class TranslucentMaterial : public Material {
12 | const Texture *diffuse, *specular, *reflect, *transmit;
13 | const float roughness, refr_index;
14 |
15 | public:
16 | /*
17 | * Create the translucent material specifying the textures to be used
18 | * for the colors of each property and the roughness
19 | */
20 | TranslucentMaterial(const Texture *diffuse, const Texture *specular, const Texture *reflect,
21 | const Texture *transmit, float roughness, float refr_index);
22 | /*
23 | * Get the BSDF to compute the shading for the material at this
24 | * piece of geometry
25 | */
26 | BSDF* get_bsdf(const DifferentialGeometry &dg, MemoryPool &pool) const override;
27 | };
28 |
29 | #endif
30 |
31 |
--------------------------------------------------------------------------------
/include/memory_pool.h:
--------------------------------------------------------------------------------
1 | #ifndef MEMORY_POOL_H
2 | #define MEMORY_POOL_H
3 |
4 | #include
5 |
6 | /*
7 | * A memory pool that allocates memory in chunks of the specified block size.
8 | * Only supports freeing of the entire pool, not individual allocations and can not
9 | * be moved or copied (it's possible to implement but I don't think I need it in the project)
10 | */
11 | class MemoryPool {
12 | struct Block {
13 | uint64_t size;
14 | char *block;
15 |
16 | Block(uint64_t size, char *block);
17 | };
18 |
19 | uint64_t cur_block_pos, block_size;
20 | Block cur_block;
21 | std::vector used, available;
22 |
23 | public:
24 | /*
25 | * Create the memory pool specifying the block size to do allocations in (default is 32k)
26 | */
27 | MemoryPool(uint32_t block_size = 32768);
28 | MemoryPool(const MemoryPool&) = delete;
29 | MemoryPool& operator=(const MemoryPool&) = delete;
30 | MemoryPool(const MemoryPool&&) = delete;
31 | MemoryPool& operator=(const MemoryPool&&) = delete;
32 | ~MemoryPool();
33 | /*
34 | * Construct a type in the memory pool
35 | */
36 | template
37 | T* alloc(Args&&... args){
38 | return new(alloc(sizeof(T))) T{std::forward(args)...};
39 | }
40 | /*
41 | * Allocate an array of types in the memory pool, elements in the array
42 | * will be uninitialized
43 | */
44 | template
45 | T* alloc_array(int size){
46 | return static_cast(alloc(sizeof(T) * size));
47 | }
48 | /*
49 | * Clear all used blocks, making them available again
50 | */
51 | void free_blocks();
52 |
53 | private:
54 | /*
55 | * Allocate some number of bytes to be used for constructing some object
56 | */
57 | void* alloc(uint64_t size);
58 | };
59 |
60 | #endif
61 |
62 |
--------------------------------------------------------------------------------
/include/mesh_preprocess.h:
--------------------------------------------------------------------------------
1 | #ifndef MESH_PREPROCESS_H
2 | #define MESH_PREPROCESS_H
3 |
4 | #include
5 |
6 | /*
7 | * Batch process all the mesh names passed in
8 | */
9 | void batch_process(char **argv, int argc);
10 |
11 | /*
12 | * Preprocess a mesh by loading the obj file into a model and
13 | * serializing in a binary format that can be read in much faster later
14 | * The binary model data will be written to the same file name passed in
15 | * but with the extension bobj.
16 | *
17 | * The binary model format produced will contain:
18 | * uint32: number of vertices
19 | * uint32: number of triangles
20 | * [float]: 3 * num verts positions
21 | * [float]: 3 * num verts texcoords
22 | * [float]: 3 * num verts normals
23 | * [int]: 3 * num tris indices
24 | */
25 | bool process_wobj(const std::string &file);
26 |
27 | #endif
28 |
29 |
--------------------------------------------------------------------------------
/include/monte_carlo/distribution1d.h:
--------------------------------------------------------------------------------
1 | #ifndef DISTRIBUTION1D_H
2 | #define DISTRIBUTION1D_H
3 |
4 | #include
5 |
6 | /*
7 | * Represents a piecewise-constant 1D function's PDF and CDF, as described in PBR
8 | */
9 | class Distribution1D {
10 | std::vector function, cdf;
11 | float integral;
12 |
13 | public:
14 | /*
15 | * Create the sampling distribution for the passed function values
16 | */
17 | Distribution1D(const std::vector &function);
18 | Distribution1D();
19 | /*
20 | * Sample one of the buckets of the function and return the bucket
21 | * number sampled, u should be in the range [0, 1)
22 | * optionally returns the pdf of sampling the bucket that was sampled
23 | */
24 | int sample_discrete(float u, float *pdf_val = nullptr) const;
25 | };
26 |
27 | #endif
28 |
29 |
--------------------------------------------------------------------------------
/include/renderer/renderer.h:
--------------------------------------------------------------------------------
1 | #ifndef RENDERER_H
2 | #define RENDERER_H
3 |
4 | #include
5 | #include "samplers/sampler.h"
6 | #include "linalg/ray.h"
7 | #include "memory_pool.h"
8 |
9 | class SurfaceIntegrator;
10 | class VolumeIntegrator;
11 | class Scene;
12 |
13 | /*
14 | * Interface for renderers, given a ray to trace and a scene
15 | * to trace it in returns the illumination along the ray
16 | */
17 | class Renderer {
18 | std::unique_ptr surface_integrator;
19 | std::unique_ptr volume_integrator;
20 |
21 | public:
22 | Renderer(std::unique_ptr surface_integrator, std::unique_ptr volume_integrator);
23 | /*
24 | * Have the renderer and its integrators perform any needed pre-processing of the scene
25 | */
26 | void preprocess(const Scene &scene);
27 | /*
28 | * Compute the incident radiance along the ray in the scene
29 | * The default implementation simply calls the surface integrator on
30 | * the hit geometry to compute the illumination
31 | */
32 | virtual Colorf illumination(RayDifferential &ray, const Scene &scene, Sampler &sampler, MemoryPool &pool) const;
33 | /*
34 | * Compute the beam transmittance for line segment along the ray from min_t to max_t using the
35 | * volume integrator, if any. If no volume integrator is being used, simply returns 1 (eg. air)
36 | */
37 | virtual Colorf transmittance(const Scene &scene, const RayDifferential &ray, Sampler &sampler, MemoryPool &pool) const;
38 |
39 | };
40 |
41 | #endif
42 |
43 |
--------------------------------------------------------------------------------
/include/samplers/stratified_sampler.h:
--------------------------------------------------------------------------------
1 | #ifndef STRATIFIED_SAMPLER_H
2 | #define STRATIFIED_SAMPLER_H
3 |
4 | #include
5 | #include
6 | #include
7 | #include
8 | #include "sampler.h"
9 |
10 | /*
11 | * A stratified sampler, generates multipled jittered
12 | * samples per pixel in its sample region
13 | */
14 | class StratifiedSampler : public Sampler {
15 | const int spp;
16 |
17 | public:
18 | StratifiedSampler(int x_start, int x_end, int y_start, int y_end, int spp, int seed);
19 | StratifiedSampler(int x_start, int x_end, int y_start, int y_end, int spp);
20 | /*
21 | * Get some {x, y} positions to sample in the space being sampled
22 | * If the sampler has finished sampling samples will be empty
23 | */
24 | void get_samples(std::vector &samples) override;
25 | /*
26 | * Get a set of 2D samples in range [0, 1)
27 | * Note that the stratified sampler ignores the offset value
28 | */
29 | void get_samples(std::array *samples, int n_samples, int offset = 0) override;
30 | /*
31 | * Get a set of 1D samples in range [0, 1)
32 | * Note that the stratified sampler ignores the offset value
33 | */
34 | void get_samples(float *samples, int n_samples, int offset = 0) override;
35 | /*
36 | * Get the max number of samples this sampler will take per pixel
37 | */
38 | int get_max_spp() const override;
39 | /*
40 | * Get subsamplers that divide the space to be sampled
41 | * into count disjoint subsections where each samples a w x h
42 | * section of the original sampler
43 | */
44 | std::vector> get_subsamplers(int w, int h) const override;
45 | /*
46 | * Generate a 1d pattern of stratified samples and return them
47 | * samples will be normalized between [0, 1)
48 | */
49 | static void sample1d(float *samples, int n_samples, std::minstd_rand &rng);
50 | /*
51 | * Generate a 2d pattern of stratified samples and return them
52 | * sample positions will be normalized between [0, 1)
53 | */
54 | static void sample2d(std::array *samples, int n_samples, std::minstd_rand &rng);
55 | };
56 |
57 | #endif
58 |
59 |
--------------------------------------------------------------------------------
/include/scene.h:
--------------------------------------------------------------------------------
1 | #ifndef SCENE_H
2 | #define SCENE_H
3 |
4 | #include
5 | #include
6 | #include "geometry/geometry.h"
7 | #include "volume/volume_node.h"
8 | #include "material/material.h"
9 | #include "textures/texture.h"
10 | #include "lights/light.h"
11 | #include "film/render_target.h"
12 | #include "film/camera.h"
13 | #include "samplers/sampler.h"
14 | #include "textures/texture.h"
15 | #include "renderer/renderer.h"
16 | #include "integrator/surface_integrator.h"
17 |
18 | /*
19 | * Describes a scene that we're rendering
20 | */
21 | class Scene {
22 | GeometryCache geom_cache;
23 | MaterialCache mat_cache;
24 | TextureCache tex_cache;
25 | LightCache light_cache;
26 | VolumeCache volume_cache;
27 | Camera camera;
28 | RenderTarget render_target;
29 | std::unique_ptr sampler;
30 | std::unique_ptr renderer;
31 | Node root;
32 | std::unique_ptr volume_root;
33 | Texture *background, *environment;
34 |
35 | public:
36 | /*
37 | * Setup the camera and render target for the scene
38 | * Geometry can be added by adding nodes to the root
39 | * and selecting from or adding to the geometry cache
40 | */
41 | Scene(Camera camera, RenderTarget target, std::unique_ptr sampler,
42 | std::unique_ptr);
43 | GeometryCache& get_geom_cache();
44 | MaterialCache& get_mat_cache();
45 | TextureCache& get_tex_cache();
46 | LightCache& get_light_cache();
47 | const LightCache& get_light_cache() const;
48 | VolumeCache& get_volume_cache();
49 | Camera& get_camera();
50 | RenderTarget& get_render_target();
51 | const RenderTarget& get_render_target() const;
52 | const Sampler& get_sampler() const;
53 | const Renderer& get_renderer() const;
54 | Renderer& get_renderer();
55 | /*
56 | * Get the root node of the geometry scene graph
57 | */
58 | Node& get_root();
59 | const Node& get_root() const;
60 | /*
61 | * Get the root node of the volume scene graph
62 | * Note: this may be null as volumes are optional while
63 | * a root geometry node isn't
64 | */
65 | VolumeNode* get_volume_root();
66 | const VolumeNode* get_volume_root() const;
67 | void set_volume_root(std::unique_ptr vol);
68 | void set_background(Texture *t);
69 | void set_environment(Texture *t);
70 | const Texture* get_background() const;
71 | const Texture* get_environment() const;
72 | };
73 |
74 | #endif
75 |
76 |
--------------------------------------------------------------------------------
/include/textures/checkerboard_texture.h:
--------------------------------------------------------------------------------
1 | #ifndef CHECKERBOARD_TEXTURE_H
2 | #define CHECKERBOARD_TEXTURE_H
3 |
4 | #include
5 | #include "texture.h"
6 | #include "texture_mapping.h"
7 |
8 | /*
9 | * Displays a checkerboard of the two color values
10 | */
11 | class CheckerboardTexture : public Texture {
12 | std::unique_ptr mapping;
13 | Colorf a, b;
14 |
15 | public:
16 | CheckerboardTexture(const Colorf &a, const Colorf &b,
17 | std::unique_ptr mapping);
18 | /*
19 | * Sample the texture color for the piece of geometry being textured
20 | */
21 | Colorf sample(const DifferentialGeometry &dg) const override;
22 | /*
23 | * Sometimes we want to re-use a texture but through a different mapping
24 | * This method samples the texture directly using the texture sample passed in
25 | */
26 | Colorf sample(const TextureSample &sample) const override;
27 |
28 | private:
29 | /*
30 | * Compute the integral of the checkerboard step function so
31 | * we can interpolate the textures when blending between them
32 | */
33 | float step_integral(float x) const;
34 | };
35 |
36 | #endif
37 |
38 |
--------------------------------------------------------------------------------
/include/textures/constant_texture.h:
--------------------------------------------------------------------------------
1 | #ifndef CONSTANT_TEXTURE_H
2 | #define CONSTANT_TEXTURE_H
3 |
4 | #include "texture.h"
5 |
6 | /*
7 | * A texture with the same value everywhere
8 | */
9 | class ConstantTexture : public Texture {
10 | Colorf color;
11 |
12 | public:
13 | /*
14 | * Create the constant valued texture and assign the value to show
15 | */
16 | ConstantTexture(const Colorf &color);
17 | /*
18 | * Sample the texture color for the piece of geometry being textured
19 | */
20 | Colorf sample(const DifferentialGeometry &dg) const override;
21 | /*
22 | * Sometimes we want to re-use a texture but through a different mapping
23 | * This method samples the texture directly using the texture sample passed in
24 | */
25 | Colorf sample(const TextureSample &sample) const override;
26 | };
27 |
28 | #endif
29 |
30 |
--------------------------------------------------------------------------------
/include/textures/image_texture.h:
--------------------------------------------------------------------------------
1 | #ifndef IMAGE_TEXTURE_H
2 | #define IMAGE_TEXTURE_H
3 |
4 | #include
5 | #include
6 | #include
7 | #include "texture.h"
8 | #include "texture_mapping.h"
9 | #include "mipmap.h"
10 |
11 | /*
12 | * A 2D image to be used as a texture
13 | * If the texture has one color component the color returned when sampling
14 | * will be RRR.
15 | * If two components RG0 is returned, for three RGB is returned
16 | */
17 | class ImageTexture : public Texture {
18 | std::unique_ptr mapping;
19 | //Image width, height and number of components per pixel
20 | int width, height, ncomp;
21 | MipMap mipmap;
22 |
23 | public:
24 | /*
25 | * Load the image texture from an image file, mapping controls
26 | * how u,v coordinates are mapped to texture s,t coordinates
27 | */
28 | ImageTexture(const std::string &file, std::unique_ptr mapping,
29 | WRAP_MODE wrap_mode = WRAP_MODE::REPEAT);
30 | /*
31 | * Sample the texture color for the piece of geometry being textured
32 | */
33 | Colorf sample(const DifferentialGeometry &dg) const override;
34 | /*
35 | * Sometimes we want to re-use a texture but through a different mapping
36 | * This method samples the texture directly using the texture sample passed in
37 | */
38 | Colorf sample(const TextureSample &sample) const override;
39 |
40 | private:
41 | /*
42 | * Load a texture from an image file and return the status of the load
43 | */
44 | bool load_image(const std::string &file, std::vector &texels);
45 | /*
46 | * Load a texture from a ppm file and return the status of the load
47 | */
48 | bool load_ppm(const std::string &file, std::vector &texels);
49 | /*
50 | * Load a texture using stb_image and return the status of the load
51 | */
52 | bool load_stb(const std::string &file, std::vector &texels);
53 | };
54 |
55 | #endif
56 |
57 |
--------------------------------------------------------------------------------
/include/textures/remapped_texture.h:
--------------------------------------------------------------------------------
1 | #ifndef REMAPPED_TEXTURE_H
2 | #define REMAPPED_TEXTURE_H
3 |
4 | #include "texture.h"
5 | #include "texture_mapping.h"
6 |
7 | /*
8 | * A bit of a hack, allows for re-using an existing texture but
9 | * with a different texture mapping
10 | */
11 | class RemappedTexture : public Texture {
12 | const Texture &texture;
13 | std::unique_ptr mapping;
14 |
15 | public:
16 | /*
17 | * Create the remapped texture to apply sample an existing texture
18 | * with a new mapping scheme
19 | */
20 | RemappedTexture(const Texture &texture, std::unique_ptr mapping);
21 | /*
22 | * Sample the texture color for the piece of geometry being textured
23 | */
24 | Colorf sample(const DifferentialGeometry &dg) const override;
25 | /*
26 | * Sometimes we want to re-use a texture but through a different mapping
27 | * This method samples the texture directly using the texture sample passed in
28 | */
29 | Colorf sample(const TextureSample &sample) const override;
30 | };
31 |
32 | #endif
33 |
34 |
--------------------------------------------------------------------------------
/include/textures/scale_texture.h:
--------------------------------------------------------------------------------
1 | #ifndef SCALE_TEXTURE_H
2 | #define SCALE_TEXTURE_H
3 |
4 | #include "texture.h"
5 |
6 | /*
7 | * Texture that multiplies two textures together to form the final color
8 | */
9 | class ScaleTexture : public Texture {
10 | const Texture &a, &b;
11 |
12 | public:
13 | /*
14 | * Create the scale texture and set the two textures to be multiplied together
15 | * These textures aren't owned by the scale texture and should live in the cache
16 | */
17 | ScaleTexture(const Texture &a, const Texture &b);
18 | /*
19 | * Sample the texture color for the piece of geometry being textured
20 | */
21 | Colorf sample(const DifferentialGeometry &dg) const override;
22 | /*
23 | * Sometimes we want to re-use a texture but through a different mapping
24 | * This method samples the texture directly using the texture sample passed in
25 | */
26 | Colorf sample(const TextureSample &sample) const override;
27 | };
28 |
29 | #endif
30 |
31 |
--------------------------------------------------------------------------------
/include/textures/spherical_mapping.h:
--------------------------------------------------------------------------------
1 | #ifndef SPHERICAL_MAPPING_H
2 | #define SPHERICAL_MAPPING_H
3 |
4 | #include "linalg/point.h"
5 | #include "linalg/transform.h"
6 | #include "texture_mapping.h"
7 |
8 | /*
9 | * Mapping that projects the hit point to a point on a sphere around the object
10 | * and uses the sphere parameterization as the texture coordinates
11 | */
12 | class SphericalMapping : public TextureMapping {
13 | Transform transform;
14 |
15 | public:
16 | SphericalMapping(const Transform &transform);
17 | /*
18 | * Compute the texture sample position and derivatives for the
19 | * differential geometry being rendered
20 | */
21 | TextureSample map(const DifferentialGeometry &dg) const override;
22 |
23 | private:
24 | /*
25 | * Project a point onto the sphere placed around the object and return the s,t
26 | * coordinates of the sphere at that point
27 | */
28 | void sphere_project(const Point &p, float &s, float &t) const;
29 | };
30 |
31 | #endif
32 |
33 |
--------------------------------------------------------------------------------
/include/textures/texture.h:
--------------------------------------------------------------------------------
1 | #ifndef TEXTURE_H
2 | #define TEXTURE_H
3 |
4 | #include "cache.h"
5 | #include "geometry/differential_geometry.h"
6 | #include "film/color.h"
7 | #include "texture_mapping.h"
8 |
9 | /*
10 | * Interface for texture, provides method to sample the color of the
11 | * for some differential geometry being rendered
12 | */
13 | class Texture {
14 | public:
15 | /*
16 | * Sample the texture color for the piece of geometry being textured
17 | */
18 | virtual Colorf sample(const DifferentialGeometry &dg) const = 0;
19 | /*
20 | * Sometimes we want to re-use a texture but through a different mapping
21 | * This method samples the texture directly using the texture sample passed in
22 | */
23 | virtual Colorf sample(const TextureSample &sample) const = 0;
24 | };
25 |
26 | typedef Cache TextureCache;
27 |
28 | #endif
29 |
30 |
--------------------------------------------------------------------------------
/include/textures/texture_mapping.h:
--------------------------------------------------------------------------------
1 | #ifndef TEXTURE_MAPPING_H
2 | #define TEXTURE_MAPPING_H
3 |
4 | #include "geometry/differential_geometry.h"
5 |
6 | /*
7 | * Stores information about the s,t coords and derivatives
8 | * to evaluat a texture's value
9 | */
10 | struct TextureSample {
11 | float s, t, ds_dx, ds_dy, dt_dx, dt_dy;
12 |
13 | TextureSample(float s = 0, float t = 0, float ds_dx = 0,
14 | float ds_dy = 0, float dt_dx = 0, float dt_dy = 0);
15 | };
16 |
17 | /*
18 | * Interface for mapping u,v coords to s,t texture coords
19 | */
20 | class TextureMapping {
21 | public:
22 | /*
23 | * Compute the texture sample position and derivatives for the
24 | * differential geometry being rendered
25 | */
26 | virtual TextureSample map(const DifferentialGeometry &dg) const = 0;
27 | };
28 |
29 | #endif
30 |
31 |
--------------------------------------------------------------------------------
/include/textures/uv_mapping.h:
--------------------------------------------------------------------------------
1 | #ifndef UV_MAPPING_H
2 | #define UV_MAPPING_H
3 |
4 | #include "linalg/transform.h"
5 | #include "texture_mapping.h"
6 |
7 | /*
8 | * A mapping that maps the object's uv coordinates to s,t in texture space
9 | */
10 | class UVMapping : public TextureMapping {
11 | Transform transform;
12 |
13 | public:
14 | /*
15 | * Create a uv mapping that will scale and translate the texture
16 | * coordinates by some amount
17 | */
18 | UVMapping(const Transform &transform);
19 | /*
20 | * Compute the texture sample position and derivatives for the
21 | * differential geometry being rendered
22 | */
23 | TextureSample map(const DifferentialGeometry &dg) const override;
24 | };
25 |
26 | #endif
27 |
28 |
--------------------------------------------------------------------------------
/include/textures/uv_texture.h:
--------------------------------------------------------------------------------
1 | #ifndef UV_TEXTURE_H
2 | #define UV_TEXTURE_H
3 |
4 | #include
5 | #include "texture.h"
6 | #include "texture_mapping.h"
7 |
8 | /*
9 | * Procedural texture that displays the st texture coordinates
10 | * as RG color values
11 | */
12 | class UVTexture : public Texture {
13 | std::unique_ptr mapping;
14 |
15 | public:
16 | UVTexture(std::unique_ptr mapping);
17 | /*
18 | * Sample the texture color for the piece of geometry being textured
19 | */
20 | Colorf sample(const DifferentialGeometry &dg) const override;
21 | /*
22 | * Sometimes we want to re-use a texture but through a different mapping
23 | * This method samples the texture directly using the texture sample passed in
24 | */
25 | Colorf sample(const TextureSample &sample) const override;
26 | };
27 |
28 | #endif
29 |
30 |
--------------------------------------------------------------------------------
/include/volume/exponential_volume.h:
--------------------------------------------------------------------------------
1 | #ifndef EXPONENTIAL_VOLUME_H
2 | #define EXPONENTIAL_VOLUME_H
3 |
4 | #include "varying_density_volume.h"
5 |
6 | /*
7 | * Defines a volume with density that falls of as an exponential, a*e^{-b*h}
8 | * where h is the 'height' along the up vector specified
9 | */
10 | class ExponentialVolume : public VaryingDensityVolume {
11 | //Parameters for the exponential falloff
12 | float a, b;
13 | Vector up;
14 | BBox region;
15 |
16 | public:
17 | /*
18 | * Create the exponential falloff volume specifying both the volume properties
19 | * and parameters for the exponential falloff density function
20 | */
21 | ExponentialVolume(const Colorf &sig_a, const Colorf &sig_s, const Colorf &emit, float phase_asymmetry,
22 | const BBox ®ion, float a, float b, const Vector &up);
23 | /*
24 | * Get the object-space bounds for the volume region
25 | */
26 | BBox bound() const override;
27 | /*
28 | * Test if the ray intersects the volume region, returning the min/max t
29 | * values that the ray overlapped the region in
30 | */
31 | bool intersect(const Ray &ray, std::array &t) const override;
32 | /*
33 | * Return the density of the volume at some point
34 | */
35 | float density(const Point &p) const override;
36 | };
37 |
38 | #endif
39 |
40 |
--------------------------------------------------------------------------------
/include/volume/grid_volume.h:
--------------------------------------------------------------------------------
1 | #ifndef GRID_VOLUME_H
2 | #define GRID_VOLUME_H
3 |
4 | #include
5 | #include
6 | #include "varying_density_volume.h"
7 |
8 | /*
9 | * Defines a volume where the density values are stored in some
10 | * 3D grid. Currently only gridvolume format encoding 1 from Mitsuba is supported
11 | * See the grid-based volume data source section of the Mitsuba doc for the file format:
12 | * http://www.mitsuba-renderer.org/releases/current/documentation.pdf
13 | *
14 | * Wenzel Jakob's fluid simulator is quick to get running and outputs
15 | * files in the supported format:
16 | * http://www.mitsuba-renderer.org/misc.html
17 | */
18 | class GridVolume : public VaryingDensityVolume {
19 | float density_scale;
20 | //Number of cells along x/y/z
21 | uint32_t n_x, n_y, n_z;
22 | BBox region;
23 | std::vector grid;
24 |
25 | public:
26 | /*
27 | * Create the grid volume specifying the properties of the volume and
28 | * the Mitsuba vol file to load the density grid from and an optional
29 | * extra scaling to apply to the density values
30 | */
31 | GridVolume(const Colorf &sig_a, const Colorf &sig_s, const Colorf &emit,
32 | float phase_asymmetry, const std::string &vol_file, float density_scale = 1);
33 | /*
34 | * Get the object-space bounds for the volume region
35 | */
36 | BBox bound() const override;
37 | /*
38 | * Test if the ray intersects the volume region, returning the min/max t
39 | * values that the ray overlapped the region in
40 | */
41 | bool intersect(const Ray &ray, std::array &t) const override;
42 | /*
43 | * Return the density of the volume at some point
44 | */
45 | float density(const Point &p) const override;
46 |
47 | private:
48 | /*
49 | * Utility to easily lookup the density at some location in the grid
50 | */
51 | float grid_density(uint32_t x, uint32_t y, uint32_t z) const;
52 | /*
53 | * Load the volume data from the vol file
54 | * returns true if successful
55 | */
56 | bool load_vol_file(const std::string &vol_file);
57 | };
58 |
59 | #endif
60 |
61 |
--------------------------------------------------------------------------------
/include/volume/homogeneous_volume.h:
--------------------------------------------------------------------------------
1 | #ifndef HOMOGENEOUS_VOLUME_H
2 | #define HOMOGENEOUS_VOLUME_H
3 |
4 | #include "volume.h"
5 |
6 | /*
7 | * A very simple volume, describes a box in space with
8 | * homogeneous scattering properties throughout
9 | */
10 | class HomogeneousVolume : public Volume {
11 | //Scattering, absoprtion, emission coefficients describing the volume
12 | Colorf sig_a, sig_s, emit;
13 | float phase_asymmetry;
14 | //The region that the volume occupies
15 | BBox region;
16 |
17 | public:
18 | /*
19 | * Construct the homgeneous volume specifying its properties and bounds
20 | */
21 | HomogeneousVolume(const Colorf &sig_a, const Colorf &sig_s, const Colorf &emit,
22 | float phase_asymmetry, const BBox ®ion);
23 | /*
24 | * Get the object-space bounds for the volume region
25 | */
26 | BBox bound() const override;
27 | /*
28 | * Test if the ray intersects the volume region, returning the min/max t
29 | * values that the ray overlapped the region in
30 | */
31 | bool intersect(const Ray &ray, std::array &t) const override;
32 | /*
33 | * Get the absorption coefficient of the volume at some point
34 | * along the direction passed
35 | */
36 | Colorf absorption(const Point &p, const Vector &v) const override;
37 | /*
38 | * Get the scattering coefficient of the volume at some point
39 | * along the direction passed
40 | */
41 | Colorf scattering(const Point &p, const Vector &v) const override;
42 | /*
43 | * Get the attenuation coefficient of the volume at some poiint
44 | * along the direciton passed
45 | */
46 | Colorf attenuation(const Point &p, const Vector &v) const;
47 | /*
48 | * Get the emission property of the volume region at some point
49 | * along the direction passed
50 | */
51 | Colorf emission(const Point &p, const Vector &v) const override;
52 | /*
53 | * Get the optical thickness of the region along the ray on the line
54 | * segment from min_t to max_t
55 | * For volumes that use Monte Carlo integration to compute this value step
56 | * and offset are used to control how to step along the segment to estimate
57 | * the integral
58 | */
59 | Colorf optical_thickness(const Ray &ray, float step = 1, float offset = 0.5) const override;
60 | /*
61 | * Get the phase function value for the volume at the some point
62 | * for a ray incident along w_i and exiting along w_o
63 | */
64 | float phase(const Point &p, const Vector &w_i, const Vector &w_o) const override;
65 | };
66 |
67 | #endif
68 |
69 |
--------------------------------------------------------------------------------
/include/volume/varying_density_volume.h:
--------------------------------------------------------------------------------
1 | #ifndef VARYING_DENSITY_VOLUME_H
2 | #define VARYING_DENSITY_VOLUME_H
3 |
4 | #include "volume.h"
5 |
6 | /*
7 | * Base for volumes that have a varying density but have constant scattering,
8 | * absorption, etc. Provides overloads of absoprtion, scattering, etc. that
9 | * multiply the value with the volume density at the point
10 | */
11 | class VaryingDensityVolume : public Volume {
12 | //Scattering, absoprtion, emission coefficients describing the volume
13 | Colorf sig_a, sig_s, emit;
14 | float phase_asymmetry;
15 |
16 | public:
17 | /*
18 | * Construct the volume specifying its properties
19 | */
20 | VaryingDensityVolume(const Colorf &sig_a, const Colorf &sig_s, const Colorf &emit, float phase_asymmetry);
21 | /*
22 | * Return the density of the volume at some point
23 | */
24 | virtual float density(const Point &p) const = 0;
25 | /*
26 | * Get the absorption coefficient of the volume at some point
27 | * along the direction passed
28 | */
29 | Colorf absorption(const Point &p, const Vector &v) const override;
30 | /*
31 | * Get the scattering coefficient of the volume at some point
32 | * along the direction passed
33 | */
34 | Colorf scattering(const Point &p, const Vector &v) const override;
35 | /*
36 | * Get the attenuation coefficient of the volume at some poiint
37 | * along the direciton passed
38 | */
39 | Colorf attenuation(const Point &p, const Vector &v) const;
40 | /*
41 | * Get the emission property of the volume region at some point
42 | * along the direction passed
43 | */
44 | Colorf emission(const Point &p, const Vector &v) const override;
45 | /*
46 | * Get the optical thickness of the region along the ray on the line
47 | * segment from min_t to max_t
48 | * For volumes that use Monte Carlo integration to compute this value step
49 | * and offset are used to control how to step along the segment to estimate
50 | * the integral
51 | */
52 | Colorf optical_thickness(const Ray &ray, float step = 1, float offset = 0.5) const override;
53 | /*
54 | * Get the phase function value for the volume at the some point
55 | * for a ray incident along w_i and exiting along w_o
56 | */
57 | float phase(const Point &p, const Vector &w_i, const Vector &w_o) const override;
58 | };
59 |
60 | #endif
61 |
62 |
--------------------------------------------------------------------------------
/include/volume/volume.h:
--------------------------------------------------------------------------------
1 | #ifndef VOLUME_H
2 | #define VOLUME_H
3 |
4 | #include "cache.h"
5 | #include "geometry/bbox.h"
6 | #include "film/color.h"
7 | #include "linalg/util.h"
8 | #include "linalg/ray.h"
9 |
10 | /*
11 | * Various common phase functions provided for the volumes to use
12 | * phase functions take the incident direction as w_i and the
13 | * outgoing direction as w_o
14 | * See: PBR
15 | */
16 | constexpr float phase_isotropic(){
17 | return 1.f / (4 * PI);
18 | }
19 | float phase_rayleigh(const Vector &w_i, const Vector &w_o);
20 | float phase_mie_hazy(const Vector &w_i, const Vector &w_o);
21 | float phase_mie_murky(const Vector &w_i, const Vector &w_o);
22 | float phase_henyey_greenstein(const Vector &w_i, const Vector &w_o, float g);
23 | float phase_schlick(const Vector &w_i, const Vector &w_o, float g);
24 |
25 | /*
26 | * Interface that describes a volume occupying some region in the scene
27 | * based on PBRT's VolumeRegion
28 | */
29 | class Volume {
30 | public:
31 | /*
32 | * Get the object-space bounds for the volume region
33 | */
34 | virtual BBox bound() const = 0;
35 | /*
36 | * Test if the ray intersects the volume region, returning the min/max t
37 | * values that the ray overlapped the region in
38 | */
39 | virtual bool intersect(const Ray &ray, std::array &t) const = 0;
40 | /*
41 | * Get the absorption coefficient of the volume at some point
42 | * along the direction passed
43 | */
44 | virtual Colorf absorption(const Point &p, const Vector &v) const = 0;
45 | /*
46 | * Get the scattering coefficient of the volume at some point
47 | * along the direction passed
48 | */
49 | virtual Colorf scattering(const Point &p, const Vector &v) const = 0;
50 | /*
51 | * Get the attenuation coefficient of the volume at some poiint
52 | * along the direciton passed
53 | */
54 | virtual Colorf attenuation(const Point &p, const Vector &v) const;
55 | /*
56 | * Get the emission property of the volume region at some point
57 | * along the direction passed
58 | */
59 | virtual Colorf emission(const Point &p, const Vector &v) const = 0;
60 | /*
61 | * Get the optical thickness of the region along the ray on the line
62 | * segment from min_t to max_t
63 | * For volumes that use Monte Carlo integration to compute this value step
64 | * and offset are used to control how to step along the segment to estimate
65 | * the integral
66 | */
67 | virtual Colorf optical_thickness(const Ray &ray, float step = 1, float offset = 0.5) const = 0;
68 | /*
69 | * Get the phase function value for the volume at the some point
70 | * for a ray incident along w_i and exiting along w_o
71 | */
72 | virtual float phase(const Point &p, const Vector &w_i, const Vector &w_o) const = 0;
73 | };
74 |
75 | typedef Cache VolumeCache;
76 |
77 | #endif
78 |
79 |
--------------------------------------------------------------------------------
/previewer/CMakeLists.txt:
--------------------------------------------------------------------------------
1 | add_library(previewer previewer.cpp util.cpp gl_core_3_3.c)
2 |
3 |
--------------------------------------------------------------------------------
/previewer/previewer.h:
--------------------------------------------------------------------------------
1 | #ifndef PREVIEWER_H
2 | #define PREVIEWER_H
3 |
4 | #include "driver.h"
5 |
6 | /*
7 | * Run the rendering and periodically update a live preview
8 | * of the rendered image
9 | * returns false if any errors were encountered or rendering was
10 | * aborted for some reason
11 | */
12 | bool render_with_preview(Driver &driver);
13 |
14 | #endif
15 |
16 |
--------------------------------------------------------------------------------
/previewer/util.h:
--------------------------------------------------------------------------------
1 | #ifndef UTIL_H
2 | #define UTIL_H
3 |
4 | #include
5 | #include
6 | #include
7 | #include "gl_core_3_3.h"
8 |
9 | namespace util {
10 | /*
11 | * Load a GLSL shader from some shader program text
12 | */
13 | GLint load_shader(GLenum type, const std::string &text);
14 | /*
15 | * Build a shader program from the vertex and fragment shaders passed
16 | */
17 | GLint load_program(const std::string &vertex_src, const std::string &fragment_src);
18 | /*
19 | * Check for an OpenGL error and log it along with the message passed
20 | * if an error occured. Will return true if an error occured & was logged
21 | */
22 | bool log_glerror(const std::string &msg);
23 | /*
24 | * A debug callback for the GL_ARB_debug_out extension
25 | */
26 | #ifdef _WIN32
27 | void APIENTRY gldebug_callback(GLenum src, GLenum type, GLuint id, GLenum severity,
28 | GLsizei len, const GLchar *msg, const GLvoid *user);
29 | #else
30 | void gldebug_callback(GLenum src, GLenum type, GLuint id, GLenum severity,
31 | GLsizei len, const GLchar *msg, const GLvoid *user);
32 | #endif
33 | }
34 |
35 | #endif
36 |
37 |
--------------------------------------------------------------------------------
/scenes/cornell.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
--------------------------------------------------------------------------------
/src/CMakeLists.txt:
--------------------------------------------------------------------------------
1 | add_subdirectory(linalg)
2 | add_subdirectory(film)
3 | add_subdirectory(geometry)
4 | add_subdirectory(samplers)
5 | add_subdirectory(material)
6 | add_subdirectory(loaders)
7 | add_subdirectory(lights)
8 | add_subdirectory(accelerators)
9 | add_subdirectory(filters)
10 | add_subdirectory(textures)
11 | add_subdirectory(integrator)
12 | add_subdirectory(renderer)
13 | add_subdirectory(monte_carlo)
14 | add_subdirectory(volume)
15 | set(TRAY_LIBS loaders lights renderer integrator linalg film geometry volume
16 | samplers material accelerators filters textures monte_carlo)
17 |
18 | add_executable(tray main.cpp mesh_preprocess.cpp driver.cpp block_queue.cpp args.cpp scene.cpp
19 | memory_pool.cpp)
20 |
21 | # Need to link libm on Unix
22 | if (NOT WIN32)
23 | find_library(M_LIB m)
24 | set(LIBS ${TRAY_LIBS} ${M_LIB})
25 | else ()
26 | set(LIBS ${TRAY_LIBS})
27 | endif()
28 |
29 | # If we're buidling the previewer we need SDL2 and OpenGL
30 | if (BUILD_PREVIEWER)
31 | set(LIBS ${LIBS} previewer ${SDL2_LIBRARY} ${OPENGL_LIBRARIES})
32 | endif()
33 |
34 | target_link_libraries(tray ${LIBS} ${CMAKE_THREAD_LIBS_INIT})
35 | install(TARGETS tray DESTINATION ${TRAY_INSTALL_DIR})
36 |
37 |
--------------------------------------------------------------------------------
/src/accelerators/CMakeLists.txt:
--------------------------------------------------------------------------------
1 | add_library(accelerators bvh.cpp)
2 |
3 |
--------------------------------------------------------------------------------
/src/args.cpp:
--------------------------------------------------------------------------------
1 | #include
2 | #include
3 | #include "args.h"
4 |
5 | bool flag(char **begin, char **end, const std::string &f){
6 | return std::find(begin, end, f) != end;
7 | }
8 | template<>
9 | std::string get_param(char **beg, char **end, const std::string &f){
10 | char **it = std::find(beg, end, f);
11 | if (it != end && ++it != end){
12 | return std::string{*it};
13 | }
14 | return std::string{""};
15 | }
16 | template<>
17 | int get_param(char **beg, char **end, const std::string &f){
18 | char **it = std::find(beg, end, f);
19 | if (it != end && ++it != end){
20 | return std::stoi(*it);
21 | }
22 | return 0;
23 | }
24 | template<>
25 | float get_param(char **beg, char **end, const std::string &f){
26 | char **it = std::find(beg, end, f);
27 | if (it != end && ++it != end){
28 | return std::stof(*it);
29 | }
30 | return 0;
31 | }
32 |
33 |
--------------------------------------------------------------------------------
/src/block_queue.cpp:
--------------------------------------------------------------------------------
1 | #include
2 | #include
3 | #include
4 | #include
5 | #include
6 | #include "samplers/sampler.h"
7 | #include "block_queue.h"
8 |
9 | //Fabian Giesen's Morton code generation
10 | //See: http://fgiesen.wordpress.com/2009/12/13/decoding-morton-codes/
11 | static uint32_t part1_by1(uint32_t x){
12 | // x = ---- ---- ---- ---- fedc ba98 7654 3210
13 | x &= 0x0000ffff;
14 | // x = ---- ---- fedc ba98 ---- ---- 7654 3210
15 | x = (x ^ (x << 8)) & 0x00ff00ff;
16 | // x = ---- fedc ---- ba98 ---- 7654 ---- 3210
17 | x = (x ^ (x << 4)) & 0x0f0f0f0f;
18 | // x = --fe --dc --ba --98 --76 --54 --32 --10
19 | x = (x ^ (x << 2)) & 0x33333333;
20 | // x = -f-e -d-c -b-a -9-8 -7-6 -5-4 -3-2 -1-0
21 | x = (x ^ (x << 1)) & 0x55555555;
22 | return x;
23 | }
24 | static uint32_t morton2(uint32_t x, uint32_t y){
25 | return (part1_by1(y) << 1) + part1_by1(x);
26 | }
27 |
28 | BlockQueue::BlockQueue(const Sampler &sampler, int bwidth, int bheight)
29 | : samplers(sampler.get_subsamplers(bwidth, bheight)), sampler_idx(0), total_time(0)
30 | {
31 | //Sort the samplers in Morton order
32 | std::sort(samplers.begin(), samplers.end(),
33 | [](const std::unique_ptr &a, const std::unique_ptr &b){
34 | return morton2(a->x_start, a->y_start) < morton2(b->x_start, b->y_start);
35 | });
36 | }
37 | Sampler* BlockQueue::get_block(){
38 | //I doubt I'll ever run this on an image big enough to make overflowing back to the 1st sampler
39 | //a concern here, especially since threads exit after getting a null sampler
40 | unsigned int s = sampler_idx.fetch_add(1, std::memory_order_acq_rel);
41 | if (s >= samplers.size()){
42 | return nullptr;
43 | }
44 | //Only one thread will get the sampler with this number unless we're running
45 | //really fast, so should be ok here
46 | if (s % (samplers.size() / 10) == 0){
47 | std::cout << "Starting work on block " << s << " of " << samplers.size()
48 | << " : ~" << 100.f * static_cast(s) / samplers.size() << "% of pixels completed"
49 | << std::endl;
50 | if (s == 0){
51 | prev = std::chrono::high_resolution_clock::now();
52 | }
53 | else {
54 | auto now = std::chrono::high_resolution_clock::now();
55 | auto elapsed = std::chrono::duration_cast(now - prev);
56 | auto avg_block = (total_time.count() + elapsed.count()) / s;
57 | std::cout << "Average block time: " << avg_block << "ms"
58 | << "\nRender time so far: " << total_time.count() << "ms"
59 | << "\nEstimated remaining time: " << avg_block * (samplers.size() - s) << "ms\n";
60 | total_time += elapsed;
61 | prev = now;
62 | }
63 | }
64 | return samplers[s].get();
65 | }
66 |
67 |
--------------------------------------------------------------------------------
/src/film/CMakeLists.txt:
--------------------------------------------------------------------------------
1 | add_library(film render_target.cpp camera.cpp color.cpp cie_vals.cpp)
2 |
3 |
--------------------------------------------------------------------------------
/src/filters/CMakeLists.txt:
--------------------------------------------------------------------------------
1 | add_library(filters box_filter.cpp triangle_filter.cpp gaussian_filter.cpp mitchell_filter.cpp
2 | lanczos_sinc_filter.cpp)
3 |
4 |
--------------------------------------------------------------------------------
/src/filters/box_filter.cpp:
--------------------------------------------------------------------------------
1 | #include "filters/box_filter.h"
2 |
3 | BoxFilter::BoxFilter(float w, float h) : Filter(w, h){}
4 | float BoxFilter::weight(float, float) const {
5 | return 1;
6 | }
7 |
8 |
--------------------------------------------------------------------------------
/src/filters/gaussian_filter.cpp:
--------------------------------------------------------------------------------
1 | #include
2 | #include
3 | #include "filters/gaussian_filter.h"
4 |
5 | GaussianFilter::GaussianFilter(float w, float h, float alpha)
6 | : Filter(w, h), alpha(alpha), exp_x(std::exp(-alpha * w * w)),
7 | exp_y(std::exp(-alpha * h * h))
8 | {}
9 | float GaussianFilter::weight(float x, float y) const {
10 | return gaussian_1d(x, exp_x) * gaussian_1d(y, exp_y);
11 | }
12 | float GaussianFilter::gaussian_1d(float x, float expv) const {
13 | return std::max(0.f, std::exp(-alpha * x * x) - expv);
14 | }
15 |
16 |
--------------------------------------------------------------------------------
/src/filters/lanczos_sinc_filter.cpp:
--------------------------------------------------------------------------------
1 | #include
2 | #include "linalg/util.h"
3 | #include "filters/lanczos_sinc_filter.h"
4 |
5 | LanczosSincFilter::LanczosSincFilter(float w, float h, float a)
6 | : Filter(w, h), a(a)
7 | {}
8 | float LanczosSincFilter::weight(float x, float y) const {
9 | return lanczos_sinc1d(x * inv_w) * lanczos_sinc1d(y * inv_h);
10 | }
11 | float LanczosSincFilter::lanczos_sinc1d(float x) const {
12 | float abs_x = std::abs(x);
13 | if (abs_x <= 1e-5){
14 | return 1;
15 | }
16 | if (abs_x >= a){
17 | return 0;
18 | }
19 | abs_x *= PI;
20 | return a * std::sin(abs_x) * std::sin(abs_x / a) / std::pow(abs_x, 2.f);
21 | }
22 |
23 |
--------------------------------------------------------------------------------
/src/filters/mitchell_filter.cpp:
--------------------------------------------------------------------------------
1 | #include
2 | #include "filters/mitchell_filter.h"
3 |
4 | MitchellFilter::MitchellFilter(float w, float h, float b, float c)
5 | : Filter(w, h), b(b), c(c)
6 | {}
7 | float MitchellFilter::weight(float x, float y) const {
8 | return mitchell_1d(x * inv_w) * mitchell_1d(y * inv_h);
9 | }
10 | float MitchellFilter::mitchell_1d(float x) const {
11 | float abs_x = std::abs(2 * x);
12 | //Filter for |x| >= 2 -> 0
13 | if (abs_x >= 2){
14 | return 0;
15 | }
16 | float filter = 0;
17 | //Filter for 1 <= |x| < 2
18 | if (abs_x >= 1){
19 | filter = (-b - 6.f * c) * std::pow(abs_x, 3.f)
20 | + (6.f * b + 30.f * c) * std::pow(abs_x, 2.f)
21 | + (-12.f * b - 48.f * c) * abs_x + 8.f * b + 24.f * c;
22 | }
23 | //Filter for |x| < 1
24 | else {
25 | filter = (12.f - 9.f * b - 6.f * c) * std::pow(abs_x, 3.f)
26 | + (-18.f + 12.f * b + 6.f * c) * std::pow(abs_x, 2.f)
27 | + 6.f - 2.f * b;
28 | }
29 | return filter / 6.f;
30 | }
31 |
32 |
33 |
--------------------------------------------------------------------------------
/src/filters/triangle_filter.cpp:
--------------------------------------------------------------------------------
1 | #include
2 | #include "filters/triangle_filter.h"
3 |
4 | TriangleFilter::TriangleFilter(float w, float h) : Filter(w, h){}
5 | float TriangleFilter::weight(float x, float y) const {
6 | return std::max(0.f, w - std::abs(x)) * std::max(0.f, h - std::abs(y));
7 | }
8 |
9 |
--------------------------------------------------------------------------------
/src/geometry/CMakeLists.txt:
--------------------------------------------------------------------------------
1 | add_library(geometry geometry.cpp sphere.cpp plane.cpp tri_mesh.cpp differential_geometry.cpp
2 | cylinder.cpp disk.cpp cone.cpp)
3 |
4 |
--------------------------------------------------------------------------------
/src/geometry/differential_geometry.cpp:
--------------------------------------------------------------------------------
1 | #include "linalg/point.h"
2 | #include "linalg/vector.h"
3 | #include "linalg/ray.h"
4 | #include "linalg/util.h"
5 | #include "geometry/geometry.h"
6 | #include "geometry/differential_geometry.h"
7 |
8 | DifferentialGeometry::DifferentialGeometry() : node(nullptr), hit_side(NONE), u(0), v(0), du_dx(0), dv_dx(0),
9 | du_dy(0), dv_dy(0)
10 | {}
11 | DifferentialGeometry::DifferentialGeometry(const Point &point, const Vector &dpdu, const Vector &dpdv,
12 | const Normal &dn_du, const Normal &dn_dv, const Normal &geom_normal, float u, float v, const Node *node, HITSIDE hit_side)
13 | : point(point), normal(dpdu.cross(dpdv).normalized()), geom_normal(geom_normal), node(node), hit_side(hit_side),
14 | dp_du(dpdu), dp_dv(dpdv), dn_du(dn_du), dn_dv(dn_dv), u(u), v(v), du_dx(0), dv_dx(0), du_dy(0), dv_dy(0)
15 | {}
16 | void DifferentialGeometry::compute_differentials(const RayDifferential &r){
17 | if (r.has_differentials()){
18 | //Compute the intersection points of the ray differentials with the plane of the
19 | //hit geometry (eg the plane at point with normal)
20 | float d = -normal.dot(Vector{point});
21 | float t = -(normal.dot(Vector{r.rx.o}) + d) / normal.dot(r.rx.d);
22 | Point px = r.rx(t);
23 | t = -(normal.dot(Vector{r.ry.o}) + d) / normal.dot(r.ry.d);
24 | Point py = r.ry(t);
25 | dp_dx = px - point;
26 | dp_dy = py - point;
27 |
28 | //Solve linear system with px and py to compute the various parameterization differentials
29 | //be careful to choose the system to not be degenerate
30 | std::array axes;
31 | if (std::abs(normal.x) > std::abs(normal.y) && std::abs(normal.x) > std::abs(normal.z)){
32 | axes[0] = AXIS::Y;
33 | axes[1] = AXIS::Z;
34 | }
35 | else if (std::abs(normal.y) > std::abs(normal.z)){
36 | axes[0] = AXIS::X;
37 | axes[1] = AXIS::Z;
38 | }
39 | else {
40 | axes[0] = AXIS::X;
41 | axes[1] = AXIS::Y;
42 | }
43 | std::array mat{
44 | dp_du[axes[0]], dp_dv[axes[0]],
45 | dp_du[axes[1]], dp_dv[axes[1]]
46 | };
47 | std::array bx{
48 | px[axes[0]] - point[axes[0]],
49 | px[axes[1]] - point[axes[1]]
50 | };
51 | std::array by{
52 | py[axes[0]] - point[axes[0]],
53 | py[axes[1]] - point[axes[1]]
54 | };
55 | if (!solve_linear2x2(mat, bx, du_dx, dv_dx)){
56 | du_dx = 0;
57 | dv_dx = 0;
58 | }
59 | if (!solve_linear2x2(mat, by, du_dy, dv_dy)){
60 | du_dy = 0;
61 | dv_dy = 0;
62 | }
63 | }
64 | else {
65 | du_dx = 0;
66 | dv_dx = 0;
67 | du_dy = 0;
68 | dv_dy = 0;
69 | dp_dx = Vector{0};
70 | dp_dy = Vector{0};
71 | }
72 | }
73 |
74 |
--------------------------------------------------------------------------------
/src/geometry/disk.cpp:
--------------------------------------------------------------------------------
1 | #include "monte_carlo/util.h"
2 | #include "linalg/util.h"
3 | #include "linalg/ray.h"
4 | #include "linalg/vector.h"
5 | #include "geometry/disk.h"
6 |
7 | Disk::Disk(float radius, float inner_radius) : radius(radius), inner_radius(inner_radius){}
8 | bool Disk::intersect(Ray &ray, DifferentialGeometry &dg) const {
9 | //We just intersect with the plane the disk lies in and then see if that point is on the disk
10 | //If the ray is perpindicular to the normal there's no
11 | //way for it to hit the plane
12 | if (std::abs(ray.d.z) < 1e-7){
13 | return false;
14 | }
15 | //We're still treating the plane as infinite here so if it's not
16 | //perpindicular it definitely hits somewhere
17 | float t = -ray.o.z / ray.d.z;
18 | if (t < ray.min_t || t > ray.max_t){
19 | return false;
20 | }
21 | //It's in the range for the ray so now check if it's in range
22 | //for the finite plane
23 | Point hit = ray(t);
24 | float dist_sqr = hit.x * hit.x + hit.y * hit.y;
25 | if (dist_sqr > radius * radius || dist_sqr < inner_radius * inner_radius){
26 | return false;
27 | }
28 | float phi = std::atan2(hit.y, hit.x);
29 | if (phi < 0){
30 | phi += TAU;
31 | }
32 | dg.point = hit;
33 | ray.max_t = t;
34 |
35 | dg.u = phi / TAU;
36 | dg.v = 1 - (std::sqrt(dist_sqr) - inner_radius) / (radius - inner_radius);
37 | float inv_z = 1 - dg.v > 0 ? 1.f / (1 - dg.v) : 0;
38 | dg.dp_du = Vector{-TAU * hit.y, TAU * hit.x, 0};
39 | dg.dp_dv = Vector{-hit.x * inv_z, -hit.y * inv_z, 0};
40 | dg.dp_dv *= (radius - inner_radius) / radius;
41 | //Normal doesn't change over the plane so these are trivial
42 | dg.normal = Normal{dg.dp_du.cross(dg.dp_dv).normalized()};
43 | dg.dn_du = Normal{0, 0, 0};
44 | dg.dn_dv = Normal{0, 0, 0};
45 | dg.geom = this;
46 | if (ray.d.dot(dg.normal) < 0){
47 | dg.hit_side = HITSIDE::FRONT;
48 | }
49 | else {
50 | dg.hit_side = HITSIDE::BACK;
51 | }
52 | return true;
53 | }
54 | BBox Disk::bound() const {
55 | return BBox{Point{-radius, -radius, 0}, Point{radius, radius, 0}};
56 | }
57 | void Disk::refine(std::vector &prims){
58 | prims.push_back(this);
59 | }
60 | float Disk::surface_area() const {
61 | return PI * (radius * radius - inner_radius * inner_radius);
62 | }
63 | Point Disk::sample(const GeomSample &gs, Normal &normal) const {
64 | Point p;
65 | auto disk_pos = concentric_sample_disk(gs.u);
66 | p.x = disk_pos[0] * radius;
67 | p.y = disk_pos[1] * radius;
68 | clamp(p.x, inner_radius, radius);
69 | clamp(p.y, inner_radius, radius);
70 | normal = Normal{0, 0, 1};
71 | return p;
72 | }
73 | bool Disk::attach_light(const Transform&){
74 | return true;
75 | }
76 |
77 |
--------------------------------------------------------------------------------
/src/geometry/plane.cpp:
--------------------------------------------------------------------------------
1 | #include
2 | #include "linalg/vector.h"
3 | #include "linalg/ray.h"
4 | #include "geometry/plane.h"
5 |
6 | bool Plane::intersect(Ray &ray, DifferentialGeometry &diff_geom) const {
7 | //If the ray is perpindicular to the normal there's no
8 | //way for it to hit the plane
9 | if (std::abs(ray.d.z) < 1e-8){
10 | return false;
11 | }
12 | //We're still treating the plane as infinite here so if it's not
13 | //perpindicular it definitely hits somewhere
14 | float t = -ray.o.z / ray.d.z;
15 | if (t < ray.min_t || t > ray.max_t){
16 | return false;
17 | }
18 | //It's in the range for the ray so now check if it's in range
19 | //for the finite plane
20 | Point hit = ray(t);
21 | if (hit.x >= -1 && hit.x <= 1 && hit.y >= -1 && hit.y <= 1){
22 | ray.max_t = t;
23 | diff_geom.point = hit;
24 | diff_geom.normal = Normal{0, 0, 1};
25 | diff_geom.geom_normal = diff_geom.normal;
26 | if (ray.d.dot(diff_geom.normal) < 0){
27 | diff_geom.hit_side = HITSIDE::FRONT;
28 | }
29 | else {
30 | diff_geom.hit_side = HITSIDE::BACK;
31 | }
32 | //Compute parameterization of surface and various derivatives for texturing
33 | //Plane is parameterized by x and y coords
34 | diff_geom.u = (hit.x + 1) / 2;
35 | //We flip the y parameterization to put image top-left at the top-left of the plane
36 | diff_geom.v = -(hit.y + 1) / 2 + 1;
37 | //The change in x/y vs. u/v. Is this correct?
38 | diff_geom.dp_du = Vector{2, 0, 0};
39 | diff_geom.dp_dv = Vector{0, 2, 0};
40 | //Normal doesn't change over the plane so these are trivial
41 | diff_geom.dn_du = Normal{0, 0, 0};
42 | diff_geom.dn_dv = Normal{0, 0, 0};
43 | diff_geom.geom = this;
44 | return true;
45 | }
46 | return false;
47 | }
48 | BBox Plane::bound() const {
49 | return BBox{Point{-1, -1, 0}, Point{1, 1, 0}};
50 | }
51 | void Plane::refine(std::vector &prims){
52 | prims.push_back(this);
53 | }
54 |
55 |
--------------------------------------------------------------------------------
/src/integrator/CMakeLists.txt:
--------------------------------------------------------------------------------
1 | add_library(integrator surface_integrator.cpp path_integrator.cpp bidir_path_integrator.cpp
2 | whitted_integrator.cpp photon_map_integrator.cpp emission_integrator.cpp
3 | single_scattering_integrator.cpp)
4 |
5 |
--------------------------------------------------------------------------------
/src/integrator/emission_integrator.cpp:
--------------------------------------------------------------------------------
1 | #include "scene.h"
2 | #include "integrator/emission_integrator.h"
3 |
4 | EmissionIntegrator::EmissionIntegrator(float step_size) : step_size(step_size){}
5 | Colorf EmissionIntegrator::radiance(const Scene &scene, const Renderer&, const RayDifferential &ray,
6 | Sampler &sampler, MemoryPool&, Colorf &transmit) const
7 | {
8 | const VolumeNode *vol = scene.get_volume_root();
9 | std::array t_range;
10 | if (vol == nullptr || !vol->intersect(ray, t_range) || t_range[0] == t_range[1]){
11 | transmit = Colorf{1};
12 | return Colorf{0};
13 | }
14 | Colorf rad;
15 | //We integrate through the volume via ray marching with steps of size step_size
16 | int n_samples = std::ceil((t_range[1] - t_range[0]) / step_size);
17 | float step = (t_range[1] - t_range[0]) / n_samples;
18 | transmit = Colorf{1};
19 | Point p = ray(t_range[0]), p_prev;
20 | Vector w_o = -ray.d;
21 | //Our first point inside the volume is offset by some random value
22 | float t = t_range[0] + sampler.random_float() * step;
23 | //Step through the volume
24 | for (int i = 0; i < n_samples; ++i, t += step, p_prev = p){
25 | p = ray(t);
26 | //Step forward and update the transmittance to include contrubition from this segment
27 | Ray step_ray{p_prev, p - p_prev, ray, 0, 1};
28 | Colorf step_tau = -vol->optical_thickness(step_ray, 0.5 * step_size, sampler.random_float());
29 | transmit *= step_tau.exp();
30 |
31 | //We consider terminating the ray if the transmittance is very low
32 | if (transmit.luminance() < 1e-3){
33 | const float continue_prob = 0.5;
34 | if (sampler.random_float() > continue_prob){
35 | transmit = 0;
36 | break;
37 | }
38 | transmit /= continue_prob;
39 | }
40 | //Add in emission contribution from emissive volumes
41 | rad += transmit * vol->emission(p, w_o);
42 | }
43 | return rad * step;
44 | }
45 | Colorf EmissionIntegrator::transmittance(const Scene &scene, const Renderer&, const RayDifferential &ray,
46 | Sampler &sampler, MemoryPool&) const
47 | {
48 | const VolumeNode *vol = scene.get_volume_root();
49 | if (vol == nullptr){
50 | return Colorf{1};
51 | }
52 | Colorf tau = -vol->optical_thickness(ray, step_size, sampler.random_float());
53 | return tau.exp();
54 | }
55 |
56 |
--------------------------------------------------------------------------------
/src/integrator/whitted_integrator.cpp:
--------------------------------------------------------------------------------
1 | #include
2 | #include "scene.h"
3 | #include "material/material.h"
4 | #include "lights/light.h"
5 | #include "lights/area_light.h"
6 | #include "lights/occlusion_tester.h"
7 | #include "material/bsdf.h"
8 | #include "renderer/renderer.h"
9 | #include "integrator/whitted_integrator.h"
10 |
11 | WhittedIntegrator::WhittedIntegrator(int max_depth) : max_depth(max_depth){}
12 | Colorf WhittedIntegrator::illumination(const Scene &scene, const Renderer &renderer, const RayDifferential &ray,
13 | DifferentialGeometry &dg, Sampler &sampler, MemoryPool &pool) const
14 | {
15 | Vector w_o = -ray.d;
16 | Colorf illum;
17 | const Material *mat = dg.node->get_material();
18 | const AreaLight *area_light = dg.node->get_area_light();
19 | if (area_light){
20 | illum += area_light->radiance(dg.point, dg.normal, w_o);
21 | }
22 | if (!mat){
23 | return illum;
24 | }
25 | dg.compute_differentials(ray);
26 | BSDF *bsdf = mat->get_bsdf(dg, pool);
27 |
28 | LightSample lsample;
29 | //Compute the incident light from all lights in the scene
30 | for (const auto &l : scene.get_light_cache()){
31 | sampler.get_samples(&lsample.u, 1);
32 | sampler.get_samples(&lsample.light, 1);
33 | Vector w_i;
34 | float pdf_val = 0;
35 | OcclusionTester occlusion;
36 | Colorf li = l.second->sample(bsdf->dg.point, lsample, w_i, pdf_val, occlusion);
37 | //If there's no light or no probability for this sample there's no illumination
38 | if (li.luminance() == 0 || pdf_val == 0){
39 | continue;
40 | }
41 | Colorf c = (*bsdf)(w_o, w_i);
42 | if (!c.is_black() && !occlusion.occluded(scene)){
43 | illum += c * li * std::abs(w_i.dot(bsdf->dg.normal)) / pdf_val;
44 | }
45 | }
46 | if (ray.depth < max_depth){
47 | illum += spec_reflect(ray, *bsdf, renderer, scene, sampler, pool);
48 | illum += spec_transmit(ray, *bsdf, renderer, scene, sampler, pool);
49 | }
50 | return illum;
51 | }
52 |
53 |
--------------------------------------------------------------------------------
/src/lights/CMakeLists.txt:
--------------------------------------------------------------------------------
1 | add_library(lights light.cpp point_light.cpp occlusion_tester.cpp area_light.cpp)
2 |
3 |
--------------------------------------------------------------------------------
/src/lights/area_light.cpp:
--------------------------------------------------------------------------------
1 | #include "scene.h"
2 | #include "geometry/geometry.h"
3 | #include "monte_carlo/util.h"
4 | #include "lights/area_light.h"
5 |
6 | AreaLight::AreaLight(const Transform &to_world, const Colorf &emit, Geometry *geometry, int n_samples)
7 | : Light(to_world, n_samples), emit(emit), geometry(geometry), surface_area(geometry->surface_area())
8 | {}
9 | Colorf AreaLight::radiance(const Point&, const Normal &n, const Vector &w) const {
10 | return w.dot(n) > 0 ? emit : Colorf{0};
11 | }
12 | Colorf AreaLight::sample(const Point &p, const LightSample &lsample, Vector &w_i, float &pdf_val,
13 | OcclusionTester &occlusion) const
14 | {
15 | Normal n_l;
16 | Point p_l = to_light(p);
17 | Point ps_l = geometry->sample(p_l, GeomSample{lsample.u, lsample.light}, n_l);
18 | Point ps_w = to_world(ps_l);
19 | Vector w_il = (ps_l - p_l).normalized();
20 | to_world(w_il, w_i);
21 | pdf_val = geometry->pdf(p_l, w_il);
22 | occlusion.set_points(p, ps_w);
23 | return radiance(ps_w, n_l, -w_il);
24 | }
25 | Colorf AreaLight::sample(const Scene&, const LightSample &lsample, const std::array &b,
26 | Ray &ray, Normal &normal, float &pdf_val) const
27 | {
28 | Point o = geometry->sample(GeomSample{lsample.u, lsample.light}, normal);
29 | Vector d = uniform_sample_sphere(b);
30 | //Make sure the ray is heading out of the surface
31 | if (d.dot(normal) < 0){
32 | d *= -1;
33 | }
34 | Point o_w = to_world(o);
35 | Vector d_w = to_world(d);
36 | ray = Ray{o_w, d_w, 0.001};
37 | pdf_val = geometry->pdf(o) * INV_TAU;
38 | to_world(normal, normal);
39 | return radiance(o_w, normal, d_w);
40 | }
41 | Colorf AreaLight::power(const Scene&) const {
42 | return emit * surface_area * PI;
43 | }
44 | bool AreaLight::delta_light() const {
45 | return false;
46 | }
47 | float AreaLight::pdf(const Point &p, const Vector &w_i) const {
48 | return geometry->pdf(to_light(p), to_light(w_i));
49 | }
50 |
51 |
--------------------------------------------------------------------------------
/src/lights/light.cpp:
--------------------------------------------------------------------------------
1 | #include
2 | #include "lights/light.h"
3 |
4 | Light::Light(const Transform &to_world, int n_samples)
5 | : to_world(to_world), to_light(to_world.inverse()), n_samples(n_samples)
6 | {
7 | if (to_light.has_scale()){
8 | std::cout << "WARNING: The renderer assumes that light transformations"
9 | << " don't have any scaling factors in them, this could be a problem!\n";
10 | }
11 | }
12 |
13 |
--------------------------------------------------------------------------------
/src/lights/occlusion_tester.cpp:
--------------------------------------------------------------------------------
1 | #include "scene.h"
2 | #include "geometry/differential_geometry.h"
3 | #include "lights/occlusion_tester.h"
4 |
5 | void OcclusionTester::set_points(const Point &a, const Point &b){
6 | ray = Ray{a, b - a, 0.001, 0.999};
7 | }
8 | void OcclusionTester::set_ray(const Point &p, const Vector &d){
9 | ray = Ray{p, d.normalized(), 0.001};
10 | }
11 | bool OcclusionTester::occluded(const Scene &scene){
12 | DifferentialGeometry dg;
13 | return scene.get_root().intersect(ray, dg);
14 | }
15 | Colorf OcclusionTester::transmittance(const Scene &scene, const Renderer &renderer, Sampler &sampler,
16 | MemoryPool &pool)
17 | {
18 | return renderer.transmittance(scene, RayDifferential{ray}, sampler, pool);
19 | }
20 |
21 |
--------------------------------------------------------------------------------
/src/lights/point_light.cpp:
--------------------------------------------------------------------------------
1 | #include "linalg/util.h"
2 | #include "monte_carlo/util.h"
3 | #include "lights/point_light.h"
4 |
5 | PointLight::PointLight(const Transform &to_world, const Colorf &intensity)
6 | : Light(to_world), position(to_world(Point{0, 0, 0})), intensity(intensity)
7 | {}
8 | Colorf PointLight::sample(const Point &p, const LightSample&, Vector &w_i, float &pdf_val, OcclusionTester &occlusion) const {
9 | w_i = (position - p).normalized();
10 | pdf_val = 1;
11 | occlusion.set_points(p, position);
12 | return intensity / position.distance_sqr(p);
13 | }
14 | Colorf PointLight::sample(const Scene&, const LightSample &lsample, const std::array&,
15 | Ray &ray, Normal &normal, float &pdf_val) const
16 | {
17 | ray = Ray{position, uniform_sample_sphere(lsample.u)};
18 | normal = Normal{ray.d};
19 | pdf_val = uniform_sphere_pdf();
20 | return intensity;
21 | }
22 | Colorf PointLight::power(const Scene&) const {
23 | return 4 * PI * intensity;
24 | }
25 | bool PointLight::delta_light() const {
26 | return true;
27 | }
28 | float PointLight::pdf(const Point&, const Vector&) const {
29 | return 0;
30 | }
31 |
32 |
--------------------------------------------------------------------------------
/src/linalg/CMakeLists.txt:
--------------------------------------------------------------------------------
1 | add_library(linalg matrix4.cpp transform.cpp quaternion.cpp animated_transform.cpp)
2 |
3 |
--------------------------------------------------------------------------------
/src/linalg/quaternion.cpp:
--------------------------------------------------------------------------------
1 | #include
2 | #include
3 | #include "linalg/quaternion.h"
4 |
5 | Quaternion::Quaternion(const Transform &t){
6 | //See Ken Shoemake - Quaternions (1991)
7 | float trace = t.mat[0][0] + t.mat[1][1] + t.mat[2][2];
8 | if (trace > 0){
9 | float s = std::sqrt(trace + 1.f);
10 | w = s / 2;
11 | s = 0.5 / s;
12 | v.x = (t.mat[2][1] - t.mat[1][2]) * s;
13 | v.y = (t.mat[0][2] - t.mat[2][0]) * s;
14 | v.z = (t.mat[1][0] - t.mat[0][1]) * s;
15 | }
16 | else {
17 | const std::array next{1, 2, 0};
18 | std::array q;
19 | int i = 0;
20 | if (t.mat[1][1] > t.mat[0][0]){
21 | i = 1;
22 | }
23 | if (t.mat[2][2] > t.mat[i][i]){
24 | i = 2;
25 | }
26 | int j = next[i];
27 | int k = next[j];
28 | float s = std::sqrt((t.mat[i][i] - (t.mat[j][j] + t.mat[k][k])) + 1);
29 | q[i] = s * 0.5;
30 | if (s != 0){
31 | s = 0.5 / s;
32 | }
33 | w = (t.mat[k][j] - t.mat[j][k]) * s;
34 | q[j] = (t.mat[j][i] + t.mat[i][j]) * s;
35 | q[k] = (t.mat[k][i] + t.mat[i][k]) * s;
36 | v = Vector{q[0], q[1], q[2]};
37 | }
38 | }
39 | Quaternion Quaternion::slerp(const Quaternion &q, float t) const {
40 | float cos_theta = dot(q);
41 | if (cos_theta > 0.9995){
42 | return ((1 - t) * *this + t * q).normalized();
43 | }
44 | float theta = std::acos(clamp(cos_theta, -1.f, 1.f));
45 | Quaternion q_perp = (q - *this * cos_theta).normalized();
46 | return *this * std::cos(theta * t) + q_perp * std::sin(theta * t);
47 | }
48 | Transform Quaternion::to_transform() const {
49 | float xx = v.x * v.x, yy = v.y * v.y, zz = v.z * v.z,
50 | xy = v.x * v.y, xz = v.x * v.z, yz = v.y * v.z,
51 | wx = v.x * w, wy = v.y * w, wz = v.z * w;
52 | Matrix4 m{{
53 | 1 - 2 * (yy + zz), 2 * (xy + wz), 2 * (xz - wy), 0,
54 | 2 * (xy - wz), 1 - 2 * (xx + zz), 2 * (yz + wx), 0,
55 | 2 * (xz + wy), 2 * (yz - wx), 1 - 2 * (xx + yy), 0,
56 | 0, 0, 0, 1
57 | }};
58 | //Transpose since we're left-handed. TODO are we?
59 | return Transform{m.transpose(), m};
60 | }
61 |
62 |
--------------------------------------------------------------------------------
/src/loaders/CMakeLists.txt:
--------------------------------------------------------------------------------
1 | add_library(loaders load_scene.cpp load_material.cpp load_light.cpp load_filter.cpp load_sampler.cpp
2 | load_texture.cpp load_renderer.cpp load_volume.cpp ${tinyxml2_DIR}/tinyxml2.cpp)
3 |
4 |
--------------------------------------------------------------------------------
/src/loaders/load_filter.cpp:
--------------------------------------------------------------------------------
1 | #include
2 | #include
3 | #include
4 | #include "filters/filter.h"
5 | #include "filters/box_filter.h"
6 | #include "filters/triangle_filter.h"
7 | #include "filters/gaussian_filter.h"
8 | #include "filters/mitchell_filter.h"
9 | #include "filters/lanczos_sinc_filter.h"
10 |
11 | std::unique_ptr load_filter(tinyxml2::XMLElement *elem){
12 | tinyxml2::XMLElement *f = elem->FirstChildElement("filter");
13 | if (!f){
14 | return std::make_unique(0.5, 0.5);
15 | }
16 | std::string type = f->Attribute("type");
17 | float w = f->FloatAttribute("w");
18 | float h = f->FloatAttribute("h");
19 | if (type == "box"){
20 | std::cout << "Using BoxFilter\n";
21 | return std::make_unique(w, h);
22 | }
23 | if (type == "triangle"){
24 | std::cout << "Using TriangleFilter\n";
25 | return std::make_unique(w, h);
26 | }
27 | if (type == "gaussian"){
28 | std::cout << "Using GaussianFilter\n";
29 | float alpha = f->FloatAttribute("alpha");
30 | return std::make_unique(w, h, alpha);
31 | }
32 | if (type == "mitchell"){
33 | std::cout << "Using MitchellFilter\n";
34 | float b = f->FloatAttribute("b");
35 | float c = f->FloatAttribute("c");
36 | return std::make_unique(w, h, b, c);
37 | }
38 | if (type == "lanczos"){
39 | std::cout << "Using LanczosSincFilter\n";
40 | float a = f->FloatAttribute("a");
41 | return std::make_unique(w, h, a);
42 | }
43 | std::cout << "Error: unrecognized filter type, defaulting to BoxFilter{0.5, 0.5}\n";
44 | return std::make_unique(0.5, 0.5);
45 | }
46 |
47 |
--------------------------------------------------------------------------------
/src/loaders/load_light.cpp:
--------------------------------------------------------------------------------
1 | #include
2 | #include
3 | #include
4 | #include "linalg/transform.h"
5 | #include "lights/point_light.h"
6 | #include "lights/area_light.h"
7 | #include "loaders/load_scene.h"
8 | #include "loaders/load_light.h"
9 |
10 | /*
11 | * Load the DirectLight properties and return the light
12 | * elem should be the root of the direct light being loaded
13 | */
14 | //static std::unique_ptr load_directl(tinyxml2::XMLElement *elem);
15 | /*
16 | * Load the PointLight properties and return the light
17 | * elem should be the root of the direct light being loaded
18 | */
19 | static std::unique_ptr load_point_light(tinyxml2::XMLElement *elem);
20 |
21 | void load_lights(tinyxml2::XMLElement *elem, LightCache &cache){
22 | using namespace tinyxml2;
23 | using namespace std::literals;
24 | for (XMLNode *n = elem; n; n = n->NextSibling()){
25 | if (n->Value() == "light"s){
26 | XMLElement *l = n->ToElement();
27 | std::string name = l->Attribute("name");
28 | std::cout << "Loading light: " << name << std::endl;
29 | std::unique_ptr light = nullptr;
30 | std::string type = l->Attribute("type");
31 | if (type == "point"){
32 | light = load_point_light(l);
33 | }
34 | if (light != nullptr){
35 | cache.add(name, std::move(light));
36 | }
37 | }
38 | else {
39 | //The lights are all passed in a block, so once
40 | //we hit something not a light we're done loading
41 | return;
42 | }
43 | }
44 | }
45 | std::unique_ptr load_point_light(tinyxml2::XMLElement *elem){
46 | Colorf color{1, 1, 1};
47 | Vector pos{0, 0, 0};
48 | read_color(elem->FirstChildElement("intensity"), color);
49 | read_vector(elem->FirstChildElement("position"), pos);
50 | return std::make_unique(Transform::translate(pos), color);
51 | }
52 |
53 |
--------------------------------------------------------------------------------
/src/loaders/load_renderer.cpp:
--------------------------------------------------------------------------------
1 | #include
2 | #include
3 | #include "renderer/renderer.h"
4 | #include "integrator/path_integrator.h"
5 | #include "integrator/whitted_integrator.h"
6 | #include "integrator/bidir_path_integrator.h"
7 | #include "integrator/photon_map_integrator.h"
8 | #include "integrator/emission_integrator.h"
9 | #include "integrator/single_scattering_integrator.h"
10 | #include "loaders/load_renderer.h"
11 |
12 | std::unique_ptr load_surface_integrator(tinyxml2::XMLElement *elem){
13 | tinyxml2::XMLElement *r = elem->FirstChildElement("renderer");
14 | if (!r){
15 | return std::make_unique(3, 8);
16 | }
17 | std::string type = r->Attribute("type");
18 | int max_depth = r->IntAttribute("max_depth");
19 | if (type == "whitted"){
20 | return std::make_unique(max_depth);
21 | }
22 | if (type == "photon"){
23 | int num_caustic = r->IntAttribute("num_caustic");
24 | int num_indirect = r->IntAttribute("num_indirect");
25 | int max_phot_depth = 6, query_size = 50, final_gather_samples = 32;
26 | float max_dist_sqr = 0.1, gather_angle = 10, max_radiance_dist = -1;
27 | r->QueryIntAttribute("max_phot_depth", &max_phot_depth);
28 | r->QueryIntAttribute("query_size", &query_size);
29 | r->QueryIntAttribute("final_gather_samples", &final_gather_samples);
30 | r->QueryFloatAttribute("max_dist_sqr", &max_dist_sqr);
31 | r->QueryFloatAttribute("gather_angle", &gather_angle);
32 | r->QueryFloatAttribute("max_radiance_dist", &max_radiance_dist);
33 | return std::make_unique(num_caustic, num_indirect, max_depth,
34 | max_phot_depth, query_size, final_gather_samples, max_dist_sqr, gather_angle,
35 | max_radiance_dist);
36 | }
37 |
38 | int min_depth = r->IntAttribute("min_depth");
39 | if (type == "path"){
40 | return std::make_unique(min_depth, max_depth);
41 | }
42 | if (type == "bidir"){
43 | return std::make_unique(min_depth, max_depth);
44 | }
45 | std::cout << "Load renderer error: Unrecognized surface integrator " << type << std::endl;
46 | return std::make_unique(3, 8);
47 | }
48 | std::unique_ptr load_volume_integrator(tinyxml2::XMLElement *elem){
49 | tinyxml2::XMLElement *integrator = elem->FirstChildElement("vol_integrator");
50 | if (!integrator){
51 | return nullptr;
52 | }
53 | std::string type = integrator->Attribute("type");
54 | float step = integrator->FloatAttribute("step");
55 |
56 | if (type == "emission"){
57 | return std::make_unique(step);
58 | }
59 | if (type == "single_scatter"){
60 | return std::make_unique(step);
61 | }
62 | std::cout << "Scene error: Unrecognized volume integrator " << type << std::endl;
63 | return nullptr;
64 | }
65 |
66 |
--------------------------------------------------------------------------------
/src/loaders/load_sampler.cpp:
--------------------------------------------------------------------------------
1 | #include
2 | #include
3 | #include
4 | #include
5 | #include "loaders/load_sampler.h"
6 | #include "samplers/sampler.h"
7 | #include "samplers/stratified_sampler.h"
8 | #include "samplers/ld_sampler.h"
9 | #include "samplers/adaptive_sampler.h"
10 |
11 | std::unique_ptr load_sampler(tinyxml2::XMLElement *elem, size_t w, size_t h){
12 | tinyxml2::XMLElement *s = elem->FirstChildElement("sampler");
13 | if (!s){
14 | return std::make_unique(0, w, 0, h, 1);
15 | }
16 | std::string type = s->Attribute("type");
17 | if (type == "stratified"){
18 | int spp = s->IntAttribute("spp");
19 | std::cout << "Using StratifiedSampler with " << spp * spp << " samples per pixel\n";
20 | return std::make_unique(0, w, 0, h, spp);
21 | }
22 | if (type == "lowdiscrepancy"){
23 | int spp = s->IntAttribute("spp");
24 | std::cout << "Using LDSampler with " << spp << " samples per pixel\n";
25 | return std::make_unique(0, w, 0, h, spp);
26 | }
27 | if (type == "adaptive"){
28 | int min_spp = s->IntAttribute("min");
29 | int max_spp = s->IntAttribute("max");
30 | std::cout << "Using AdaptiveSampler with min: " << min_spp
31 | << " and max: " << max_spp << " samples per pixel\n";
32 | return std::make_unique(0, w, 0, h, min_spp, max_spp);
33 | }
34 | std::cout << "Error: unrecognized sampler type, defaulting to StratifiedSampler"
35 | << " with 1 sampler per pixel\n";
36 | return std::make_unique(0, w, 0, h, 1);
37 | }
38 |
39 |
--------------------------------------------------------------------------------
/src/material/CMakeLists.txt:
--------------------------------------------------------------------------------
1 | add_library(material bxdf.cpp fresnel.cpp specular_reflection.cpp specular_transmission.cpp
2 | lambertian.cpp bsdf.cpp matte_material.cpp oren_nayer.cpp scaled_bxdf.cpp plastic_material.cpp
3 | torrance_sparrow.cpp microfacet_distribution.cpp blinn_distribution.cpp anisotropic_distribution.cpp
4 | btdf_adapter.cpp translucent_material.cpp metal_material.cpp merl_brdf.cpp merl_material.cpp glass_material.cpp
5 | mix_material.cpp specular_metal_material.cpp)
6 |
7 |
--------------------------------------------------------------------------------
/src/material/blinn_distribution.cpp:
--------------------------------------------------------------------------------
1 | #include
2 | #include "material/bxdf.h"
3 | #include "material/blinn_distribution.h"
4 |
5 | BlinnDistribution::BlinnDistribution(float exponent) : exponent(exponent){}
6 | float BlinnDistribution::operator()(const Vector &w_h) const {
7 | return (exponent + 2) * INV_TAU * std::pow(std::abs(BxDF::cos_theta(w_h)), exponent);
8 | }
9 | void BlinnDistribution::sample(const Vector &w_o, Vector &w_i, const std::array