├── .github └── workflows │ └── cmake.yml ├── .gitignore ├── .gitmodules ├── CMakeLists.txt ├── README.md ├── SHARING_SOLUTIONS_ONLINE_IS_FORBIDDEN.txt ├── ext ├── CMakeLists.txt ├── README.md └── plugin │ ├── io_nori.py │ └── readme.md ├── include └── nori │ ├── accel.h │ ├── bbox.h │ ├── bitmap.h │ ├── block.h │ ├── bsdf.h │ ├── camera.h │ ├── color.h │ ├── common.h │ ├── dpdf.h │ ├── emitter.h │ ├── frame.h │ ├── gui.h │ ├── integrator.h │ ├── mesh.h │ ├── object.h │ ├── parser.h │ ├── proplist.h │ ├── ray.h │ ├── rfilter.h │ ├── sampler.h │ ├── scene.h │ ├── timer.h │ ├── transform.h │ ├── vector.h │ └── warp.h ├── scenes ├── pa1 │ ├── bunny.obj │ └── bunny.xml ├── pa2 │ └── ajax-normals.xml ├── pa3 │ ├── ajax-ao.xml │ └── ajax-simple.xml ├── pa4 │ ├── cbox │ │ ├── cbox-distributed.xml │ │ ├── cbox-whitted.xml │ │ └── meshes │ │ │ ├── leftwall.obj │ │ │ ├── light.obj │ │ │ ├── rightwall.obj │ │ │ ├── sphere1.obj │ │ │ ├── sphere1_.obj │ │ │ ├── sphere2.obj │ │ │ ├── sphere2_.obj │ │ │ └── walls.obj │ ├── motto │ │ ├── meshes │ │ │ ├── floor.obj │ │ │ ├── light1.obj │ │ │ ├── light2.obj │ │ │ └── motto_2018.obj │ │ ├── motto-dielectric.xml │ │ └── motto-diffuse.xml │ └── tests │ │ ├── meshes │ │ ├── floor.obj │ │ ├── furnace.obj │ │ ├── polylum.py │ │ ├── polylum1.obj │ │ ├── polylum2.obj │ │ ├── polylum3.obj │ │ ├── polylum4.obj │ │ └── polylum5.obj │ │ ├── test-mesh-furnace.xml │ │ └── test-mesh.xml └── pa5 │ ├── ajax │ ├── ajax-rough.xml │ ├── ajax-smooth.xml │ └── light.obj │ ├── cbox │ ├── cbox_ems.xml │ ├── cbox_mats.xml │ ├── cbox_mis.xml │ └── meshes │ │ ├── leftwall.obj │ │ ├── light.obj │ │ ├── rightwall.obj │ │ ├── sphere1.obj │ │ ├── sphere2.obj │ │ └── walls.obj │ ├── table │ ├── meshes │ │ ├── mesh_0.obj │ │ ├── mesh_1.obj │ │ ├── mesh_2.obj │ │ ├── mesh_3.obj │ │ └── mesh_4.obj │ ├── table_ems.xml │ ├── table_mats.xml │ └── table_mis.xml │ ├── tests │ ├── chi2test-microfacet.xml │ ├── floor.obj │ ├── furnace.obj │ ├── polylum.py │ ├── polylum1.obj │ ├── polylum2.obj │ ├── polylum3.obj │ ├── polylum4.obj │ ├── polylum5.obj │ ├── test-direct.xml │ ├── test-furnace.xml │ └── ttest-microfacet.xml │ └── veach_mi │ ├── meshes │ ├── floor.obj │ ├── plate1.obj │ ├── plate2.obj │ ├── plate3.obj │ ├── plate4.obj │ └── sphere.obj │ ├── veach_ems.xml │ ├── veach_mats.xml │ └── veach_mis.xml └── src ├── accel.cpp ├── bitmap.cpp ├── block.cpp ├── chi2test.cpp ├── common.cpp ├── dielectric.cpp ├── diffuse.cpp ├── gui.cpp ├── independent.cpp ├── main.cpp ├── mesh.cpp ├── microfacet.cpp ├── mirror.cpp ├── obj.cpp ├── object.cpp ├── parser.cpp ├── perspective.cpp ├── proplist.cpp ├── rfilter.cpp ├── scene.cpp ├── ttest.cpp ├── warp.cpp └── warptest.cpp /.github/workflows/cmake.yml: -------------------------------------------------------------------------------- 1 | name: Build 2 | 3 | on: [push] 4 | 5 | env: 6 | BUILD_TYPE: Release 7 | 8 | jobs: 9 | build: 10 | name: ${{ matrix.config.name }} 11 | runs-on: ${{ matrix.config.os }} 12 | strategy: 13 | fail-fast: false 14 | matrix: 15 | config: 16 | - { 17 | name: "Windows Latest - MSVC", 18 | os: windows-latest, 19 | cc: "cl", 20 | cxx: "cl", 21 | } 22 | - { 23 | name: "Ubuntu Latest - GCC", 24 | os: ubuntu-latest, 25 | cc: "gcc", 26 | cxx: "g++", 27 | } 28 | - { 29 | name: "MacOS Latest - Clang", 30 | os: macos-latest, 31 | cc: "clang", 32 | cxx: "clang++", 33 | } 34 | 35 | steps: 36 | - uses: actions/checkout@v2 37 | with: 38 | submodules: recursive 39 | 40 | # Setup caching on of build artifacts to reduce total build time (only Linux and MacOS) 41 | - name: ccache 42 | if: runner.os != 'Windows' 43 | uses: hendrikmuhs/ccache-action@v1 44 | 45 | - name: Create Build Environment 46 | run: cmake -E make_directory ${{github.workspace}}/build 47 | 48 | - name: Install dependencies (Linux) 49 | if: runner.os == 'Linux' 50 | run: sudo apt-get update && sudo apt-get install -yq libglu1-mesa-dev libxxf86vm-dev libxrandr-dev libxinerama-dev libxcursor-dev libxi-dev libx11-dev 51 | 52 | - name: Configure CMake 53 | shell: bash 54 | working-directory: ${{github.workspace}}/build 55 | if: runner.os != 'Windows' 56 | run: cmake $GITHUB_WORKSPACE -DCMAKE_BUILD_TYPE=$BUILD_TYPE -DCMAKE_C_COMPILER_LAUNCHER=ccache -DCMAKE_CXX_COMPILER_LAUNCHER=ccache 57 | 58 | - name: Configure CMake (Windows) 59 | shell: bash 60 | working-directory: ${{github.workspace}}/build 61 | if: runner.os == 'Windows' 62 | run: cmake $GITHUB_WORKSPACE -DCMAKE_BUILD_TYPE=$BUILD_TYPE 63 | 64 | - name: Build 65 | working-directory: ${{github.workspace}}/build 66 | shell: bash 67 | run: cmake --build . --config $BUILD_TYPE --parallel 68 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | .DS_Store 3 | /nori 4 | /warptest 5 | /ext_build 6 | CMakeCache.txt 7 | CMakeSettings.json 8 | CMakeFiles 9 | Makefile 10 | *.cmake 11 | .ninja_deps 12 | .ninja_log 13 | build.ninja 14 | rules.ninja 15 | .vs 16 | .vscode 17 | /out 18 | *.sublime-workspace 19 | *.sublime-project 20 | *.vcxproj 21 | *.filters 22 | *.sdf 23 | *.sln 24 | *.dir 25 | *.opendb 26 | Debug 27 | x64 -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "ext/filesystem"] 2 | path = ext/filesystem 3 | url = https://github.com/wjakob/filesystem 4 | [submodule "ext/tbb"] 5 | path = ext/tbb 6 | url = https://github.com/wjakob/tbb 7 | [submodule "ext/pugixml"] 8 | path = ext/pugixml 9 | url = http://github.com/zeux/pugixml 10 | [submodule "ext/tinyformat"] 11 | path = ext/tinyformat 12 | url = https://github.com/wjakob/tinyformat 13 | [submodule "ext/hypothesis"] 14 | path = ext/hypothesis 15 | url = https://github.com/wjakob/hypothesis 16 | [submodule "ext/pcg32"] 17 | path = ext/pcg32 18 | url = https://github.com/wjakob/pcg32 19 | [submodule "ext/nanogui"] 20 | path = ext/nanogui 21 | url = https://github.com/mitsuba-renderer/nanogui 22 | [submodule "ext/zlib"] 23 | path = ext/zlib 24 | url = https://github.com/mitsuba-renderer/zlib 25 | [submodule "ext/openexr"] 26 | path = ext/openexr 27 | url = https://github.com/mitsuba-renderer/openexr/ 28 | [submodule "ext/eigen"] 29 | path = ext/eigen 30 | url = https://gitlab.com/libeigen/eigen.git 31 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required (VERSION 3.8) 2 | project(nori) 3 | 4 | add_subdirectory(ext ext_build) 5 | 6 | include_directories( 7 | # Nori include files 8 | ${CMAKE_CURRENT_SOURCE_DIR}/include 9 | # tinyformat string formatting library 10 | ${TFM_INCLUDE_DIR} 11 | # Eigen linear algebra library 12 | SYSTEM ${EIGEN_INCLUDE_DIR} 13 | # OpenEXR high dynamic range bitmap library 14 | SYSTEM ${OPENEXR_INCLUDE_DIRS} 15 | # Intel Thread Building Blocks 16 | SYSTEM ${TBB_INCLUDE_DIR} 17 | # Pseudorandom number generator 18 | ${PCG32_INCLUDE_DIR} 19 | # PugiXML parser 20 | ${PUGIXML_INCLUDE_DIR} 21 | # Helper functions for statistical hypothesis tests 22 | ${HYPOTHESIS_INCLUDE_DIR} 23 | # GLFW library for OpenGL context creation 24 | SYSTEM ${GLFW_INCLUDE_DIR} 25 | # GLEW library for accessing OpenGL functions 26 | SYSTEM ${GLEW_INCLUDE_DIR} 27 | # NanoVG drawing library 28 | SYSTEM ${NANOVG_INCLUDE_DIR} 29 | # NanoGUI user interface library 30 | SYSTEM ${NANOGUI_INCLUDE_DIR} 31 | SYSTEM ${NANOGUI_EXTRA_INCS} 32 | # Portable filesystem API 33 | SYSTEM ${FILESYSTEM_INCLUDE_DIR} 34 | # STB Image Write 35 | SYSTEM ${STB_IMAGE_WRITE_INCLUDE_DIR} 36 | ) 37 | 38 | # The following lines build the main executable. If you add a source 39 | # code file to Nori, be sure to include it in this list. 40 | add_executable(nori 41 | 42 | # Header files 43 | include/nori/bbox.h 44 | include/nori/bitmap.h 45 | include/nori/block.h 46 | include/nori/bsdf.h 47 | include/nori/accel.h 48 | include/nori/camera.h 49 | include/nori/color.h 50 | include/nori/common.h 51 | include/nori/dpdf.h 52 | include/nori/frame.h 53 | include/nori/integrator.h 54 | include/nori/emitter.h 55 | include/nori/mesh.h 56 | include/nori/object.h 57 | include/nori/parser.h 58 | include/nori/proplist.h 59 | include/nori/ray.h 60 | include/nori/rfilter.h 61 | include/nori/sampler.h 62 | include/nori/scene.h 63 | include/nori/timer.h 64 | include/nori/transform.h 65 | include/nori/vector.h 66 | include/nori/warp.h 67 | 68 | # Source code files 69 | src/bitmap.cpp 70 | src/block.cpp 71 | src/accel.cpp 72 | src/chi2test.cpp 73 | src/common.cpp 74 | src/diffuse.cpp 75 | src/gui.cpp 76 | src/independent.cpp 77 | src/main.cpp 78 | src/mesh.cpp 79 | src/obj.cpp 80 | src/object.cpp 81 | src/parser.cpp 82 | src/perspective.cpp 83 | src/proplist.cpp 84 | src/rfilter.cpp 85 | src/scene.cpp 86 | src/ttest.cpp 87 | src/warp.cpp 88 | src/microfacet.cpp 89 | src/mirror.cpp 90 | src/dielectric.cpp 91 | ) 92 | 93 | add_definitions(${NANOGUI_EXTRA_DEFS}) 94 | 95 | # The following lines build the warping test application 96 | add_executable(warptest 97 | include/nori/warp.h 98 | src/warp.cpp 99 | src/warptest.cpp 100 | src/microfacet.cpp 101 | src/object.cpp 102 | src/proplist.cpp 103 | src/common.cpp 104 | ) 105 | 106 | if (WIN32) 107 | target_link_libraries(nori tbb_static pugixml IlmImf nanogui ${NANOGUI_EXTRA_LIBS} zlibstatic) 108 | else() 109 | target_link_libraries(nori tbb_static pugixml IlmImf nanogui ${NANOGUI_EXTRA_LIBS}) 110 | endif() 111 | 112 | target_link_libraries(warptest tbb_static nanogui ${NANOGUI_EXTRA_LIBS}) 113 | 114 | # Force colored output for the ninja generator 115 | if (CMAKE_GENERATOR STREQUAL "Ninja") 116 | if (CMAKE_CXX_COMPILER_ID MATCHES "Clang") 117 | set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fcolor-diagnostics") 118 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fcolor-diagnostics") 119 | elseif (CMAKE_CXX_COMPILER_ID MATCHES "GNU") 120 | set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fdiagnostics-color=always") 121 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fdiagnostics-color=always") 122 | endif() 123 | endif() 124 | 125 | target_compile_features(warptest PRIVATE cxx_std_17) 126 | target_compile_features(nori PRIVATE cxx_std_17) 127 | 128 | # vim: set et ts=2 sw=2 ft=cmake nospell: 129 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![CS440 Banner](https://rgl.s3.eu-central-1.amazonaws.com/media/uploads/wjakob/2017/02/16/cs440-logo_web.jpg)](https://rgl.s3.eu-central-1.amazonaws.com/media/uploads/wjakob/2017/02/20/cs440-rgl.jpg) 2 | 3 | ## Nori Version 2 4 | ![Build status](https://github.com/wjakob/nori/workflows/Build/badge.svg) 5 | 6 | Nori is a simple ray tracer written in C++. It runs on Windows, Linux, and 7 | Mac OS and provides basic functionality that is required to complete the 8 | assignments in the course Advanced Computer Graphics taught at EPFL. 9 | 10 | ### Course information and framework documentation 11 | 12 | For access to course information including slides and reading material, visit the main [Advanced Computer Graphics website](https://rgl.epfl.ch/courses/ACG17). The Nori 2 framework and coding assignments will be described on the [Nori website](https://wjakob.github.io/nori). 13 | 14 | ### Note to researchers and students from other institutions 15 | 16 | Last year's version of Nori including a full set of assignment descriptions can 17 | be found in the following [archive](https://github.com/wjakob/nori-old). 18 | 19 | 20 | ### Known Issues 21 | There is a known issue with the NanoGUI version that Nori uses: on Linux systems with an integrated Intel GPU, a bug in the Mesa graphics drivers causes the GUI to freeze on startup. A workaround is to temporarily switch to an older Mesa driver to run Nori. This can be done by running 22 | ``` 23 | export MESA_LOADER_DRIVER_OVERRIDE=i965 24 | ``` 25 | -------------------------------------------------------------------------------- /SHARING_SOLUTIONS_ONLINE_IS_FORBIDDEN.txt: -------------------------------------------------------------------------------- 1 | This base code is exlusively provided for classroom use. 2 | Sharing modifications of this repository online, especially including solutions 3 | code, is expressively forbidden. For example, you may not post your final 4 | implementation in a publicly accessible GitHub/GitLab/.. repository. Since it 5 | can lead to problematic cases of cheating, we are strict about this rule and 6 | regularly search for leaked solutions online. 7 | -------------------------------------------------------------------------------- /ext/README.md: -------------------------------------------------------------------------------- 1 | ### Overview of dependency libraries used by Nori 2 | 3 | Nori requires several utility libraries to function correctly; a full list with 4 | explanations is given below. You should feel free to use any of their 5 | functionality in your own submissions—however, you are not required to do so. 6 | 7 | * `filesystem`: tiny self-contained library for manipulating file paths 8 | * `hypothesis`: utility functions for statistical hypothesis tests 9 | * `nanogui`: minimalistic GUI library for OpenGL 10 | * `openexr`: High dynamic range image format library 11 | * `pcg32`: tiny self-contained pseudorandom number generator 12 | * `pugixml`: light-weight XML processing library 13 | * `tbb`: Intel's Boost Thread Building Blocks for multithreading 14 | * `tinyformat`: type-safe C++11 version of `sprintf` and friends 15 | * `zlib`: data compression library, used by `openexr` 16 | -------------------------------------------------------------------------------- /ext/plugin/readme.md: -------------------------------------------------------------------------------- 1 | # Nori Exporter for Blender 2 | by Delio Vicini and Tizian Zeltner, based on Adrien Gruson's original exporter. 3 | 4 | ## Installation 5 | 6 | First, you must download a fairly recent version of Blender (the plugin is tested for versions >= 2.8). You can download blender from https://www.blender.org or using your system's package manager. 7 | 8 | To install the plugin, open Blender and go to "Edit -> Preferences... -> Add-ons" and click on "Install...". 9 | This should open a file browser in which you can navigate to the `io_nori.py` file and select it. 10 | This will copy the exporter script to Blender's plugin directory. 11 | After the plugin is installed, it has to be activated by clicking the checkbox next to it in the Add-ons menu. 12 | 13 | ## Usage 14 | 15 | Once the plugin is installed, scenes can be expored by clicking "File -> Export -> Export Nori Scene..." 16 | 17 | The plugin exports all objects in the scene as separate OBJ FIles. It then generates a Nori XML file with the scene's camera, a basic integrator and XML entries to reference all the exported meshes. 18 | 19 | ## Limitations 20 | 21 | The plugin does not support exporting BSDFs and emitters. It will just assign a default BSDF to all shapes. It further exports each mesh as is. 22 | This means that if you have a mesh with multiple materials in Blender, you will have to split it manually into separate submeshes before exporting (one for each material). This can be done by selecting the mesh with multiple materials, going to edit mode (tab) then selecting all vertices (A) and clicking "separate" (P) and then "by material". This will separate the mesh into meshes with one material each. After exporting, each of these meshes will have a separate entry in the scene's XML file and can therefore be assigned a different BSDF. -------------------------------------------------------------------------------- /include/nori/accel.h: -------------------------------------------------------------------------------- 1 | /* 2 | This file is part of Nori, a simple educational ray tracer 3 | 4 | Copyright (c) 2015 by Wenzel Jakob 5 | */ 6 | 7 | #pragma once 8 | 9 | #include 10 | 11 | NORI_NAMESPACE_BEGIN 12 | 13 | /** 14 | * \brief Acceleration data structure for ray intersection queries 15 | * 16 | * The current implementation falls back to a brute force loop 17 | * through the geometry. 18 | */ 19 | class Accel { 20 | public: 21 | /** 22 | * \brief Register a triangle mesh for inclusion in the acceleration 23 | * data structure 24 | * 25 | * This function can only be used before \ref build() is called 26 | */ 27 | void addMesh(Mesh *mesh); 28 | 29 | /// Build the acceleration data structure (currently a no-op) 30 | void build(); 31 | 32 | /// Return an axis-aligned box that bounds the scene 33 | const BoundingBox3f &getBoundingBox() const { return m_bbox; } 34 | 35 | /** 36 | * \brief Intersect a ray against all triangles stored in the scene and 37 | * return detailed intersection information 38 | * 39 | * \param ray 40 | * A 3-dimensional ray data structure with minimum/maximum extent 41 | * information 42 | * 43 | * \param its 44 | * A detailed intersection record, which will be filled by the 45 | * intersection query 46 | * 47 | * \param shadowRay 48 | * \c true if this is a shadow ray query, i.e. a query that only aims to 49 | * find out whether the ray is blocked or not without returning detailed 50 | * intersection information. 51 | * 52 | * \return \c true if an intersection was found 53 | */ 54 | bool rayIntersect(const Ray3f &ray, Intersection &its, bool shadowRay) const; 55 | 56 | private: 57 | Mesh *m_mesh = nullptr; ///< Mesh (only a single one for now) 58 | BoundingBox3f m_bbox; ///< Bounding box of the entire scene 59 | }; 60 | 61 | NORI_NAMESPACE_END 62 | -------------------------------------------------------------------------------- /include/nori/bitmap.h: -------------------------------------------------------------------------------- 1 | /* 2 | This file is part of Nori, a simple educational ray tracer 3 | 4 | Copyright (c) 2015 by Wenzel Jakob 5 | */ 6 | 7 | #pragma once 8 | 9 | #include 10 | #include 11 | 12 | NORI_NAMESPACE_BEGIN 13 | 14 | /** 15 | * \brief Stores a RGB high dynamic-range bitmap 16 | * 17 | * The bitmap class provides I/O support using the OpenEXR file format 18 | */ 19 | class Bitmap : public Eigen::Array { 20 | public: 21 | typedef Eigen::Array Base; 22 | 23 | /** 24 | * \brief Allocate a new bitmap of the specified size 25 | * 26 | * The contents will initially be undefined, so make sure 27 | * to call \ref clear() if necessary 28 | */ 29 | Bitmap(const Vector2i &size = Vector2i(0, 0)) 30 | : Base(size.y(), size.x()) { } 31 | 32 | /// Load an OpenEXR file with the specified filename 33 | Bitmap(const std::string &filename); 34 | 35 | /// Save the bitmap as an EXR file with the specified filename 36 | void saveEXR(const std::string &filename); 37 | 38 | /// Save the bitmap as a PNG file (with sRGB tonemapping) with the specified filename 39 | void savePNG(const std::string &filename); 40 | }; 41 | 42 | NORI_NAMESPACE_END 43 | -------------------------------------------------------------------------------- /include/nori/block.h: -------------------------------------------------------------------------------- 1 | /* 2 | This file is part of Nori, a simple educational ray tracer 3 | 4 | Copyright (c) 2015 by Wenzel Jakob 5 | */ 6 | 7 | /* ======================================================================= 8 | This file contains classes for parallel rendering of "image blocks". 9 | * ======================================================================= */ 10 | 11 | #pragma once 12 | 13 | #include 14 | #include 15 | #include 16 | 17 | #define NORI_BLOCK_SIZE 32 /* Block size used for parallelization */ 18 | 19 | NORI_NAMESPACE_BEGIN 20 | 21 | /** 22 | * \brief Weighted pixel storage for a rectangular subregion of an image 23 | * 24 | * This class implements storage for a rectangular subregion of a 25 | * larger image that is being rendered. For each pixel, it records color 26 | * values along with a weight that specifies the accumulated influence of 27 | * nearby samples on the pixel (according to the used reconstruction filter). 28 | * 29 | * When rendering with filters, the samples in a rectangular 30 | * region will generally also contribute to pixels just outside of 31 | * this region. For that reason, this class also stores information about 32 | * a small border region around the rectangle, whose size depends on the 33 | * properties of the reconstruction filter. 34 | */ 35 | class ImageBlock : public Eigen::Array { 36 | public: 37 | /** 38 | * Create a new image block of the specified maximum size 39 | * \param size 40 | * Desired maximum size of the block 41 | * \param filter 42 | * Samples will be convolved with the image reconstruction 43 | * filter provided here. 44 | */ 45 | ImageBlock(const Vector2i &size, const ReconstructionFilter *filter); 46 | 47 | /// Release all memory 48 | ~ImageBlock(); 49 | 50 | /// Configure the offset of the block within the main image 51 | void setOffset(const Point2i &offset) { m_offset = offset; } 52 | 53 | /// Return the offset of the block within the main image 54 | inline const Point2i &getOffset() const { return m_offset; } 55 | 56 | /// Configure the size of the block within the main image 57 | void setSize(const Point2i &size) { m_size = size; } 58 | 59 | /// Return the size of the block within the main image 60 | inline const Vector2i &getSize() const { return m_size; } 61 | 62 | /// Return the border size in pixels 63 | inline int getBorderSize() const { return m_borderSize; } 64 | 65 | /** 66 | * \brief Turn the block into a proper bitmap 67 | * 68 | * This entails normalizing all pixels and discarding 69 | * the border region. 70 | */ 71 | Bitmap *toBitmap() const; 72 | 73 | /// Convert a bitmap into an image block 74 | void fromBitmap(const Bitmap &bitmap); 75 | 76 | /// Clear all contents 77 | void clear() { setConstant(Color4f()); } 78 | 79 | /// Record a sample with the given position and radiance value 80 | void put(const Point2f &pos, const Color3f &value); 81 | 82 | /** 83 | * \brief Merge another image block into this one 84 | * 85 | * During the merge operation, this function locks 86 | * the destination block using a mutex. 87 | */ 88 | void put(ImageBlock &b); 89 | 90 | /// Lock the image block (using an internal mutex) 91 | inline void lock() const { m_mutex.lock(); } 92 | 93 | /// Unlock the image block 94 | inline void unlock() const { m_mutex.unlock(); } 95 | 96 | /// Return a human-readable string summary 97 | std::string toString() const; 98 | protected: 99 | Point2i m_offset; 100 | Vector2i m_size; 101 | int m_borderSize = 0; 102 | float *m_filter = nullptr; 103 | float m_filterRadius = 0; 104 | float *m_weightsX = nullptr; 105 | float *m_weightsY = nullptr; 106 | float m_lookupFactor = 0; 107 | mutable tbb::mutex m_mutex; 108 | }; 109 | 110 | /** 111 | * \brief Spiraling block generator 112 | * 113 | * This class can be used to chop up an image into many small 114 | * rectangular blocks suitable for parallel rendering. The blocks 115 | * are ordered in spiraling pattern so that the center is 116 | * rendered first. 117 | */ 118 | class BlockGenerator { 119 | public: 120 | /** 121 | * \brief Create a block generator with 122 | * \param size 123 | * Size of the image that should be split into blocks 124 | * \param blockSize 125 | * Maximum size of the individual blocks 126 | */ 127 | BlockGenerator(const Vector2i &size, int blockSize); 128 | 129 | /** 130 | * \brief Return the next block to be rendered 131 | * 132 | * This function is thread-safe 133 | * 134 | * \return \c false if there were no more blocks 135 | */ 136 | bool next(ImageBlock &block); 137 | 138 | /// Return the total number of blocks 139 | int getBlockCount() const { return m_blocksLeft; } 140 | protected: 141 | enum EDirection { ERight = 0, EDown, ELeft, EUp }; 142 | 143 | Point2i m_block; 144 | Vector2i m_numBlocks; 145 | Vector2i m_size; 146 | int m_blockSize; 147 | int m_numSteps; 148 | int m_blocksLeft; 149 | int m_stepsLeft; 150 | int m_direction; 151 | tbb::mutex m_mutex; 152 | }; 153 | 154 | NORI_NAMESPACE_END 155 | -------------------------------------------------------------------------------- /include/nori/bsdf.h: -------------------------------------------------------------------------------- 1 | /* 2 | This file is part of Nori, a simple educational ray tracer 3 | 4 | Copyright (c) 2015 by Wenzel Jakob 5 | */ 6 | 7 | #pragma once 8 | 9 | #include 10 | 11 | NORI_NAMESPACE_BEGIN 12 | 13 | /** 14 | * \brief Convenience data structure used to pass multiple 15 | * parameters to the evaluation and sampling routines in \ref BSDF 16 | */ 17 | struct BSDFQueryRecord { 18 | /// Incident direction (in the local frame) 19 | Vector3f wi; 20 | 21 | /// Outgoing direction (in the local frame) 22 | Vector3f wo; 23 | 24 | /// Relative refractive index in the sampled direction 25 | float eta; 26 | 27 | /// Measure associated with the sample 28 | EMeasure measure; 29 | 30 | /// Create a new record for sampling the BSDF 31 | BSDFQueryRecord(const Vector3f &wi) 32 | : wi(wi), eta(1.f), measure(EUnknownMeasure) { } 33 | 34 | /// Create a new record for querying the BSDF 35 | BSDFQueryRecord(const Vector3f &wi, 36 | const Vector3f &wo, EMeasure measure) 37 | : wi(wi), wo(wo), eta(1.f), measure(measure) { } 38 | }; 39 | 40 | /** 41 | * \brief Superclass of all bidirectional scattering distribution functions 42 | */ 43 | class BSDF : public NoriObject { 44 | public: 45 | /** 46 | * \brief Sample the BSDF and return the importance weight (i.e. the 47 | * value of the BSDF * cos(theta_o) divided by the probability density 48 | * of the sample with respect to solid angles). 49 | * 50 | * \param bRec A BSDF query record 51 | * \param sample A uniformly distributed sample on \f$[0,1]^2\f$ 52 | * 53 | * \return The BSDF value divided by the probability density of the sample 54 | * sample. The returned value also includes the cosine 55 | * foreshortening factor associated with the outgoing direction, 56 | * when this is appropriate. A zero value means that sampling 57 | * failed. 58 | */ 59 | virtual Color3f sample(BSDFQueryRecord &bRec, const Point2f &sample) const = 0; 60 | 61 | /** 62 | * \brief Evaluate the BSDF for a pair of directions and measure 63 | * specified in \code bRec 64 | * 65 | * \param bRec 66 | * A record with detailed information on the BSDF query 67 | * \return 68 | * The BSDF value, evaluated for each color channel 69 | */ 70 | virtual Color3f eval(const BSDFQueryRecord &bRec) const = 0; 71 | 72 | /** 73 | * \brief Compute the probability of sampling \c bRec.wo 74 | * (conditioned on \c bRec.wi). 75 | * 76 | * This method provides access to the probability density that 77 | * is realized by the \ref sample() method. 78 | * 79 | * \param bRec 80 | * A record with detailed information on the BSDF query 81 | * 82 | * \return 83 | * A probability/density value expressed with respect 84 | * to the specified measure 85 | */ 86 | 87 | virtual float pdf(const BSDFQueryRecord &bRec) const = 0; 88 | 89 | /** 90 | * \brief Return the type of object (i.e. Mesh/BSDF/etc.) 91 | * provided by this instance 92 | * */ 93 | EClassType getClassType() const { return EBSDF; } 94 | 95 | /** 96 | * \brief Return whether or not this BRDF is diffuse. This 97 | * is primarily used by photon mapping to decide whether 98 | * or not to store photons on a surface 99 | */ 100 | virtual bool isDiffuse() const { return false; } 101 | }; 102 | 103 | NORI_NAMESPACE_END 104 | -------------------------------------------------------------------------------- /include/nori/camera.h: -------------------------------------------------------------------------------- 1 | /* 2 | This file is part of Nori, a simple educational ray tracer 3 | 4 | Copyright (c) 2015 by Wenzel Jakob 5 | */ 6 | 7 | #pragma once 8 | 9 | #include 10 | 11 | NORI_NAMESPACE_BEGIN 12 | 13 | /** 14 | * \brief Generic camera interface 15 | * 16 | * This class provides an abstract interface to cameras in Nori and 17 | * exposes the ability to sample their response function. By default, only 18 | * a perspective camera implementation exists, but you may choose to 19 | * implement other types (e.g. an environment camera, or a physically-based 20 | * camera model that simulates the behavior actual lenses) 21 | */ 22 | class Camera : public NoriObject { 23 | public: 24 | /** 25 | * \brief Importance sample a ray according to the camera's response function 26 | * 27 | * \param ray 28 | * A ray data structure to be filled with a position 29 | * and direction value 30 | * 31 | * \param samplePosition 32 | * Denotes the desired sample position on the film 33 | * expressed in fractional pixel coordinates 34 | * 35 | * \param apertureSample 36 | * A uniformly distributed 2D vector that is used to sample 37 | * a position on the aperture of the sensor if necessary. 38 | * 39 | * \return 40 | * An importance weight associated with the sampled ray. 41 | * This accounts for the difference in the camera response 42 | * function and the sampling density. 43 | */ 44 | virtual Color3f sampleRay(Ray3f &ray, 45 | const Point2f &samplePosition, 46 | const Point2f &apertureSample) const = 0; 47 | 48 | /// Return the size of the output image in pixels 49 | const Vector2i &getOutputSize() const { return m_outputSize; } 50 | 51 | /// Return the camera's reconstruction filter in image space 52 | const ReconstructionFilter *getReconstructionFilter() const { return m_rfilter; } 53 | 54 | /** 55 | * \brief Return the type of object (i.e. Mesh/Camera/etc.) 56 | * provided by this instance 57 | * */ 58 | EClassType getClassType() const { return ECamera; } 59 | protected: 60 | Vector2i m_outputSize; 61 | ReconstructionFilter *m_rfilter; 62 | }; 63 | 64 | NORI_NAMESPACE_END 65 | -------------------------------------------------------------------------------- /include/nori/color.h: -------------------------------------------------------------------------------- 1 | /* 2 | This file is part of Nori, a simple educational ray tracer 3 | 4 | Copyright (c) 2015 by Wenzel Jakob 5 | */ 6 | 7 | #pragma once 8 | 9 | #include 10 | 11 | NORI_NAMESPACE_BEGIN 12 | 13 | /** 14 | * \brief Represents a linear RGB color value 15 | */ 16 | struct Color3f : public Eigen::Array3f { 17 | public: 18 | typedef Eigen::Array3f Base; 19 | 20 | /// Initialize the color vector with a uniform value 21 | Color3f(float value = 0.f) : Base(value, value, value) { } 22 | 23 | /// Initialize the color vector with specific per-channel values 24 | Color3f(float r, float g, float b) : Base(r, g, b) { } 25 | 26 | /// Construct a color vector from ArrayBase (needed to play nice with Eigen) 27 | template Color3f(const Eigen::ArrayBase& p) 28 | : Base(p) { } 29 | 30 | /// Assign a color vector from ArrayBase (needed to play nice with Eigen) 31 | template Color3f &operator=(const Eigen::ArrayBase& p) { 32 | this->Base::operator=(p); 33 | return *this; 34 | } 35 | 36 | /// Return a reference to the red channel 37 | float &r() { return x(); } 38 | /// Return a reference to the red channel (const version) 39 | const float &r() const { return x(); } 40 | /// Return a reference to the green channel 41 | float &g() { return y(); } 42 | /// Return a reference to the green channel (const version) 43 | const float &g() const { return y(); } 44 | /// Return a reference to the blue channel 45 | float &b() { return z(); } 46 | /// Return a reference to the blue channel (const version) 47 | const float &b() const { return z(); } 48 | 49 | /// Clamp to the positive range 50 | Color3f clamp() const { return Color3f(std::max(r(), 0.0f), 51 | std::max(g(), 0.0f), std::max(b(), 0.0f)); } 52 | 53 | /// Check if the color vector contains a NaN/Inf/negative value 54 | bool isValid() const; 55 | 56 | /// Convert from sRGB to linear RGB 57 | Color3f toLinearRGB() const; 58 | 59 | /// Convert from linear RGB to sRGB 60 | Color3f toSRGB() const; 61 | 62 | /// Return the associated luminance 63 | float getLuminance() const; 64 | 65 | /// Return a human-readable string summary 66 | std::string toString() const { 67 | return tfm::format("[%f, %f, %f]", coeff(0), coeff(1), coeff(2)); 68 | } 69 | }; 70 | 71 | /** 72 | * \brief Represents a linear RGB color and a weight 73 | * 74 | * This is used by Nori's image reconstruction filter code 75 | */ 76 | struct Color4f : public Eigen::Array4f { 77 | public: 78 | typedef Eigen::Array4f Base; 79 | 80 | /// Create an zero value 81 | Color4f() : Base(0.0f, 0.0f, 0.0f, 0.0f) { } 82 | 83 | /// Create from a 3-channel color 84 | Color4f(const Color3f &c) : Base(c.r(), c.g(), c.b(), 1.0f) { } 85 | 86 | /// Initialize the color vector with specific per-channel values 87 | Color4f(float r, float g, float b, float w) : Base(r, g, b, w) { } 88 | 89 | /// Construct a color vector from ArrayBase (needed to play nice with Eigen) 90 | template Color4f(const Eigen::ArrayBase& p) 91 | : Base(p) { } 92 | 93 | /// Assign a color vector from ArrayBase (needed to play nice with Eigen) 94 | template Color4f &operator=(const Eigen::ArrayBase& p) { 95 | this->Base::operator=(p); 96 | return *this; 97 | } 98 | 99 | /// Divide by the filter weight and convert into a \ref Color3f value 100 | Color3f divideByFilterWeight() const { 101 | if (w() != 0) 102 | return head<3>() / w(); 103 | else 104 | return Color3f(0.0f); 105 | } 106 | 107 | /// Return a human-readable string summary 108 | std::string toString() const { 109 | return tfm::format("[%f, %f, %f, %f]", coeff(0), coeff(1), coeff(2), coeff(3)); 110 | } 111 | }; 112 | 113 | NORI_NAMESPACE_END 114 | -------------------------------------------------------------------------------- /include/nori/dpdf.h: -------------------------------------------------------------------------------- 1 | /* 2 | This file is part of Nori, a simple educational ray tracer 3 | 4 | Copyright (c) 2015 by Wenzel Jakob 5 | */ 6 | 7 | #pragma once 8 | 9 | #include 10 | 11 | NORI_NAMESPACE_BEGIN 12 | 13 | /** 14 | * \brief Discrete probability distribution 15 | * 16 | * This data structure can be used to transform uniformly distributed 17 | * samples to a stored discrete probability distribution. 18 | * 19 | * \ingroup libcore 20 | */ 21 | struct DiscretePDF { 22 | public: 23 | /// Allocate memory for a distribution with the given number of entries 24 | explicit DiscretePDF(size_t nEntries = 0) { 25 | reserve(nEntries); 26 | clear(); 27 | } 28 | 29 | /// Clear all entries 30 | void clear() { 31 | m_cdf.clear(); 32 | m_cdf.push_back(0.0f); 33 | m_normalized = false; 34 | } 35 | 36 | /// Reserve memory for a certain number of entries 37 | void reserve(size_t nEntries) { 38 | m_cdf.reserve(nEntries+1); 39 | } 40 | 41 | /// Append an entry with the specified discrete probability 42 | void append(float pdfValue) { 43 | m_cdf.push_back(m_cdf[m_cdf.size()-1] + pdfValue); 44 | } 45 | 46 | /// Return the number of entries so far 47 | size_t size() const { 48 | return m_cdf.size()-1; 49 | } 50 | 51 | /// Access an entry by its index 52 | float operator[](size_t entry) const { 53 | return m_cdf[entry+1] - m_cdf[entry]; 54 | } 55 | 56 | /// Have the probability densities been normalized? 57 | bool isNormalized() const { 58 | return m_normalized; 59 | } 60 | 61 | /** 62 | * \brief Return the original (unnormalized) sum of all PDF entries 63 | * 64 | * This assumes that \ref normalize() has previously been called 65 | */ 66 | float getSum() const { 67 | return m_sum; 68 | } 69 | 70 | /** 71 | * \brief Return the normalization factor (i.e. the inverse of \ref getSum()) 72 | * 73 | * This assumes that \ref normalize() has previously been called 74 | */ 75 | float getNormalization() const { 76 | return m_normalization; 77 | } 78 | 79 | /** 80 | * \brief Normalize the distribution 81 | * 82 | * \return Sum of the (previously unnormalized) entries 83 | */ 84 | float normalize() { 85 | m_sum = m_cdf[m_cdf.size()-1]; 86 | if (m_sum > 0) { 87 | m_normalization = 1.0f / m_sum; 88 | for (size_t i=1; i::const_iterator entry = 108 | std::lower_bound(m_cdf.begin(), m_cdf.end(), sampleValue); 109 | size_t index = (size_t) std::max((ptrdiff_t) 0, entry - m_cdf.begin() - 1); 110 | return std::min(index, m_cdf.size()-2); 111 | } 112 | 113 | /** 114 | * \brief %Transform a uniformly distributed sample to the stored distribution 115 | * 116 | * \param[in] sampleValue 117 | * An uniformly distributed sample on [0,1] 118 | * \param[out] pdf 119 | * Probability value of the sample 120 | * \return 121 | * The discrete index associated with the sample 122 | */ 123 | size_t sample(float sampleValue, float &pdf) const { 124 | size_t index = sample(sampleValue); 125 | pdf = operator[](index); 126 | return index; 127 | } 128 | 129 | /** 130 | * \brief %Transform a uniformly distributed sample to the stored distribution 131 | * 132 | * The original sample is value adjusted so that it can be "reused". 133 | * 134 | * \param[in, out] sampleValue 135 | * An uniformly distributed sample on [0,1] 136 | * \return 137 | * The discrete index associated with the sample 138 | */ 139 | size_t sampleReuse(float &sampleValue) const { 140 | size_t index = sample(sampleValue); 141 | sampleValue = (sampleValue - m_cdf[index]) 142 | / (m_cdf[index + 1] - m_cdf[index]); 143 | return index; 144 | } 145 | 146 | /** 147 | * \brief %Transform a uniformly distributed sample. 148 | * 149 | * The original sample is value adjusted so that it can be "reused". 150 | * 151 | * \param[in,out] 152 | * An uniformly distributed sample on [0,1] 153 | * \param[out] pdf 154 | * Probability value of the sample 155 | * \return 156 | * The discrete index associated with the sample 157 | */ 158 | size_t sampleReuse(float &sampleValue, float &pdf) const { 159 | size_t index = sample(sampleValue, pdf); 160 | sampleValue = (sampleValue - m_cdf[index]) 161 | / (m_cdf[index + 1] - m_cdf[index]); 162 | return index; 163 | } 164 | 165 | /** 166 | * \brief Turn the underlying distribution into a 167 | * human-readable string format 168 | */ 169 | std::string toString() const { 170 | std::string result = tfm::format("DiscretePDF[sum=%f, " 171 | "normalized=%f, pdf = {", m_sum, m_normalized); 172 | 173 | for (size_t i=0; i m_cdf; 182 | float m_sum, m_normalization; 183 | bool m_normalized; 184 | }; 185 | 186 | NORI_NAMESPACE_END 187 | -------------------------------------------------------------------------------- /include/nori/emitter.h: -------------------------------------------------------------------------------- 1 | /* 2 | This file is part of Nori, a simple educational ray tracer 3 | 4 | Copyright (c) 2015 by Wenzel Jakob 5 | */ 6 | 7 | #pragma once 8 | 9 | #include 10 | 11 | NORI_NAMESPACE_BEGIN 12 | 13 | /** 14 | * \brief Superclass of all emitters 15 | */ 16 | class Emitter : public NoriObject { 17 | public: 18 | 19 | /** 20 | * \brief Return the type of object (i.e. Mesh/Emitter/etc.) 21 | * provided by this instance 22 | * */ 23 | EClassType getClassType() const { return EEmitter; } 24 | }; 25 | 26 | NORI_NAMESPACE_END 27 | -------------------------------------------------------------------------------- /include/nori/frame.h: -------------------------------------------------------------------------------- 1 | /* 2 | This file is part of Nori, a simple educational ray tracer 3 | 4 | Copyright (c) 2015 by Wenzel Jakob 5 | */ 6 | 7 | #pragma once 8 | 9 | #include 10 | 11 | NORI_NAMESPACE_BEGIN 12 | 13 | /** 14 | * \brief Stores a three-dimensional orthonormal coordinate frame 15 | * 16 | * This class is mostly used to quickly convert between different 17 | * cartesian coordinate systems and to efficiently compute certain 18 | * quantities (e.g. \ref cosTheta(), \ref tanTheta, ..). 19 | */ 20 | struct Frame { 21 | Vector3f s, t; 22 | Normal3f n; 23 | 24 | /// Default constructor -- performs no initialization! 25 | Frame() { } 26 | 27 | /// Given a normal and tangent vectors, construct a new coordinate frame 28 | Frame(const Vector3f &s, const Vector3f &t, const Normal3f &n) 29 | : s(s), t(t), n(n) { } 30 | 31 | /// Construct a frame from the given orthonormal vectors 32 | Frame(const Vector3f &x, const Vector3f &y, const Vector3f &z) 33 | : s(x), t(y), n(z) { } 34 | 35 | /// Construct a new coordinate frame from a single vector 36 | Frame(const Vector3f &n) : n(n) { 37 | coordinateSystem(n, s, t); 38 | } 39 | 40 | /// Convert from world coordinates to local coordinates 41 | Vector3f toLocal(const Vector3f &v) const { 42 | return Vector3f( 43 | v.dot(s), v.dot(t), v.dot(n) 44 | ); 45 | } 46 | 47 | /// Convert from local coordinates to world coordinates 48 | Vector3f toWorld(const Vector3f &v) const { 49 | return s * v.x() + t * v.y() + n * v.z(); 50 | } 51 | 52 | /** \brief Assuming that the given direction is in the local coordinate 53 | * system, return the cosine of the angle between the normal and v */ 54 | static float cosTheta(const Vector3f &v) { 55 | return v.z(); 56 | } 57 | 58 | /** \brief Assuming that the given direction is in the local coordinate 59 | * system, return the sine of the angle between the normal and v */ 60 | static float sinTheta(const Vector3f &v) { 61 | float temp = sinTheta2(v); 62 | if (temp <= 0.0f) 63 | return 0.0f; 64 | return std::sqrt(temp); 65 | } 66 | 67 | /** \brief Assuming that the given direction is in the local coordinate 68 | * system, return the tangent of the angle between the normal and v */ 69 | static float tanTheta(const Vector3f &v) { 70 | float temp = 1 - v.z()*v.z(); 71 | if (temp <= 0.0f) 72 | return 0.0f; 73 | return std::sqrt(temp) / v.z(); 74 | } 75 | 76 | /** \brief Assuming that the given direction is in the local coordinate 77 | * system, return the squared sine of the angle between the normal and v */ 78 | static float sinTheta2(const Vector3f &v) { 79 | return 1.0f - v.z() * v.z(); 80 | } 81 | 82 | /** \brief Assuming that the given direction is in the local coordinate 83 | * system, return the sine of the phi parameter in spherical coordinates */ 84 | static float sinPhi(const Vector3f &v) { 85 | float sinTheta = Frame::sinTheta(v); 86 | if (sinTheta == 0.0f) 87 | return 1.0f; 88 | return clamp(v.y() / sinTheta, -1.0f, 1.0f); 89 | } 90 | 91 | /** \brief Assuming that the given direction is in the local coordinate 92 | * system, return the cosine of the phi parameter in spherical coordinates */ 93 | static float cosPhi(const Vector3f &v) { 94 | float sinTheta = Frame::sinTheta(v); 95 | if (sinTheta == 0.0f) 96 | return 1.0f; 97 | return clamp(v.x() / sinTheta, -1.0f, 1.0f); 98 | } 99 | 100 | /** \brief Assuming that the given direction is in the local coordinate 101 | * system, return the squared sine of the phi parameter in spherical 102 | * coordinates */ 103 | static float sinPhi2(const Vector3f &v) { 104 | return clamp(v.y() * v.y() / sinTheta2(v), 0.0f, 1.0f); 105 | } 106 | 107 | /** \brief Assuming that the given direction is in the local coordinate 108 | * system, return the squared cosine of the phi parameter in spherical 109 | * coordinates */ 110 | static float cosPhi2(const Vector3f &v) { 111 | return clamp(v.x() * v.x() / sinTheta2(v), 0.0f, 1.0f); 112 | } 113 | 114 | /// Equality test 115 | bool operator==(const Frame &frame) const { 116 | return frame.s == s && frame.t == t && frame.n == n; 117 | } 118 | 119 | /// Inequality test 120 | bool operator!=(const Frame &frame) const { 121 | return !operator==(frame); 122 | } 123 | 124 | /// Return a human-readable string summary of this frame 125 | std::string toString() const { 126 | return tfm::format( 127 | "Frame[\n" 128 | " s = %s,\n" 129 | " t = %s,\n" 130 | " n = %s\n" 131 | "]", s.toString(), t.toString(), n.toString()); 132 | } 133 | }; 134 | 135 | NORI_NAMESPACE_END 136 | -------------------------------------------------------------------------------- /include/nori/gui.h: -------------------------------------------------------------------------------- 1 | /* 2 | This file is part of Nori, a simple educational ray tracer 3 | 4 | Copyright (c) 2015 by Wenzel Jakob 5 | */ 6 | 7 | #pragma once 8 | 9 | #include 10 | #include 11 | 12 | NORI_NAMESPACE_BEGIN 13 | 14 | class NoriScreen : public nanogui::Screen { 15 | public: 16 | NoriScreen(const ImageBlock &block); 17 | void draw_contents() override; 18 | private: 19 | const ImageBlock &m_block; 20 | nanogui::ref m_shader; 21 | nanogui::ref m_texture; 22 | nanogui::ref m_renderPass; 23 | float m_scale = 1.f; 24 | }; 25 | 26 | NORI_NAMESPACE_END 27 | -------------------------------------------------------------------------------- /include/nori/integrator.h: -------------------------------------------------------------------------------- 1 | /* 2 | This file is part of Nori, a simple educational ray tracer 3 | 4 | Copyright (c) 2015 by Wenzel Jakob 5 | */ 6 | 7 | #pragma once 8 | 9 | #include 10 | 11 | NORI_NAMESPACE_BEGIN 12 | 13 | /** 14 | * \brief Abstract integrator (i.e. a rendering technique) 15 | * 16 | * In Nori, the different rendering techniques are collectively referred to as 17 | * integrators, since they perform integration over a high-dimensional 18 | * space. Each integrator represents a specific approach for solving 19 | * the light transport equation---usually favored in certain scenarios, but 20 | * at the same time affected by its own set of intrinsic limitations. 21 | */ 22 | class Integrator : public NoriObject { 23 | public: 24 | /// Release all memory 25 | virtual ~Integrator() { } 26 | 27 | /// Perform an (optional) preprocess step 28 | virtual void preprocess(const Scene *scene) { } 29 | 30 | /** 31 | * \brief Sample the incident radiance along a ray 32 | * 33 | * \param scene 34 | * A pointer to the underlying scene 35 | * \param sampler 36 | * A pointer to a sample generator 37 | * \param ray 38 | * The ray in question 39 | * \return 40 | * A (usually) unbiased estimate of the radiance in this direction 41 | */ 42 | virtual Color3f Li(const Scene *scene, Sampler *sampler, const Ray3f &ray) const = 0; 43 | 44 | /** 45 | * \brief Return the type of object (i.e. Mesh/BSDF/etc.) 46 | * provided by this instance 47 | * */ 48 | EClassType getClassType() const { return EIntegrator; } 49 | }; 50 | 51 | NORI_NAMESPACE_END 52 | -------------------------------------------------------------------------------- /include/nori/object.h: -------------------------------------------------------------------------------- 1 | /* 2 | This file is part of Nori, a simple educational ray tracer 3 | 4 | Copyright (c) 2015 by Wenzel Jakob 5 | */ 6 | 7 | #pragma once 8 | 9 | #include 10 | 11 | NORI_NAMESPACE_BEGIN 12 | 13 | /** 14 | * \brief Base class of all objects 15 | * 16 | * A Nori object represents an instance that is part of 17 | * a scene description, e.g. a scattering model or emitter. 18 | */ 19 | class NoriObject { 20 | public: 21 | enum EClassType { 22 | EScene = 0, 23 | EMesh, 24 | EBSDF, 25 | EPhaseFunction, 26 | EEmitter, 27 | EMedium, 28 | ECamera, 29 | EIntegrator, 30 | ESampler, 31 | ETest, 32 | EReconstructionFilter, 33 | EClassTypeCount 34 | }; 35 | 36 | /// Virtual destructor 37 | virtual ~NoriObject() { } 38 | 39 | /** 40 | * \brief Return the type of object (i.e. Mesh/BSDF/etc.) 41 | * provided by this instance 42 | * */ 43 | virtual EClassType getClassType() const = 0; 44 | 45 | /** 46 | * \brief Add a child object to the current instance 47 | * 48 | * The default implementation does not support children and 49 | * simply throws an exception 50 | */ 51 | virtual void addChild(NoriObject *child); 52 | 53 | /** 54 | * \brief Set the parent object 55 | * 56 | * Subclasses may choose to override this method to be 57 | * notified when they are added to a parent object. The 58 | * default implementation does nothing. 59 | */ 60 | virtual void setParent(NoriObject *parent); 61 | 62 | /** 63 | * \brief Perform some action associated with the object 64 | * 65 | * The default implementation throws an exception. Certain objects 66 | * may choose to override it, e.g. to implement initialization, 67 | * testing, or rendering functionality. 68 | * 69 | * This function is called by the XML parser once it has 70 | * constructed an object and added all of its children 71 | * using \ref addChild(). 72 | */ 73 | virtual void activate(); 74 | 75 | /// Return a brief string summary of the instance (for debugging purposes) 76 | virtual std::string toString() const = 0; 77 | 78 | /// Turn a class type into a human-readable string 79 | static std::string classTypeName(EClassType type) { 80 | switch (type) { 81 | case EScene: return "scene"; 82 | case EMesh: return "mesh"; 83 | case EBSDF: return "bsdf"; 84 | case EEmitter: return "emitter"; 85 | case ECamera: return "camera"; 86 | case EIntegrator: return "integrator"; 87 | case ESampler: return "sampler"; 88 | case ETest: return "test"; 89 | default: return ""; 90 | } 91 | } 92 | }; 93 | 94 | /** 95 | * \brief Factory for Nori objects 96 | * 97 | * This utility class is part of a mini-RTTI framework and can 98 | * instantiate arbitrary Nori objects by their name. 99 | */ 100 | class NoriObjectFactory { 101 | public: 102 | typedef std::function Constructor; 103 | 104 | /** 105 | * \brief Register an object constructor with the object factory 106 | * 107 | * This function is called by the macro \ref NORI_REGISTER_CLASS 108 | * 109 | * \param name 110 | * An internal name that is associated with this class. This is the 111 | * 'type' field found in the scene description XML files 112 | * 113 | * \param constr 114 | * A function pointer to an anonymous function that is 115 | * able to call the constructor of the class. 116 | */ 117 | static void registerClass(const std::string &name, const Constructor &constr); 118 | 119 | /** 120 | * \brief Construct an instance from the class of the given name 121 | * 122 | * \param name 123 | * An internal name that is associated with this class. This is the 124 | * 'type' field found in the scene description XML files 125 | * 126 | * \param propList 127 | * A list of properties that will be passed to the constructor 128 | * of the class. 129 | */ 130 | static NoriObject *createInstance(const std::string &name, 131 | const PropertyList &propList) { 132 | if (!m_constructors || m_constructors->find(name) == m_constructors->end()) 133 | throw NoriException("A constructor for class \"%s\" could not be found!", name); 134 | return (*m_constructors)[name](propList); 135 | } 136 | private: 137 | static std::map *m_constructors; 138 | }; 139 | 140 | /// Macro for registering an object constructor with the \ref NoriObjectFactory 141 | #define NORI_REGISTER_CLASS(cls, name) \ 142 | cls *cls ##_create(const PropertyList &list) { \ 143 | return new cls(list); \ 144 | } \ 145 | static struct cls ##_{ \ 146 | cls ##_() { \ 147 | NoriObjectFactory::registerClass(name, cls ##_create); \ 148 | } \ 149 | } cls ##__NORI_; 150 | 151 | NORI_NAMESPACE_END 152 | -------------------------------------------------------------------------------- /include/nori/parser.h: -------------------------------------------------------------------------------- 1 | /* 2 | This file is part of Nori, a simple educational ray tracer 3 | 4 | Copyright (c) 2015 by Wenzel Jakob 5 | */ 6 | 7 | #pragma once 8 | 9 | #include 10 | 11 | NORI_NAMESPACE_BEGIN 12 | 13 | /** 14 | * \brief Load a scene from the specified filename and 15 | * return its root object 16 | */ 17 | extern NoriObject *loadFromXML(const std::string &filename); 18 | 19 | NORI_NAMESPACE_END 20 | -------------------------------------------------------------------------------- /include/nori/proplist.h: -------------------------------------------------------------------------------- 1 | /* 2 | This file is part of Nori, a simple educational ray tracer 3 | 4 | Copyright (c) 2015 by Wenzel Jakob 5 | */ 6 | 7 | #pragma once 8 | 9 | #include 10 | #include 11 | #include 12 | 13 | NORI_NAMESPACE_BEGIN 14 | 15 | /** 16 | * \brief This is an associative container used to supply the constructors 17 | * of \ref NoriObject subclasses with parameter information. 18 | */ 19 | class PropertyList { 20 | public: 21 | PropertyList() { } 22 | 23 | /// Set a boolean property 24 | void setBoolean(const std::string &name, const bool &value); 25 | 26 | /// Get a boolean property, and throw an exception if it does not exist 27 | bool getBoolean(const std::string &name) const; 28 | 29 | /// Get a boolean property, and use a default value if it does not exist 30 | bool getBoolean(const std::string &name, const bool &defaultValue) const; 31 | 32 | /// Set an integer property 33 | void setInteger(const std::string &name, const int &value); 34 | 35 | /// Get an integer property, and throw an exception if it does not exist 36 | int getInteger(const std::string &name) const; 37 | 38 | /// Get am integer property, and use a default value if it does not exist 39 | int getInteger(const std::string &name, const int &defaultValue) const; 40 | 41 | /// Set a float property 42 | void setFloat(const std::string &name, const float &value); 43 | 44 | /// Get a float property, and throw an exception if it does not exist 45 | float getFloat(const std::string &name) const; 46 | 47 | /// Get a float property, and use a default value if it does not exist 48 | float getFloat(const std::string &name, const float &defaultValue) const; 49 | 50 | /// Set a string property 51 | void setString(const std::string &name, const std::string &value); 52 | 53 | /// Get a string property, and throw an exception if it does not exist 54 | std::string getString(const std::string &name) const; 55 | 56 | /// Get a string property, and use a default value if it does not exist 57 | std::string getString(const std::string &name, const std::string &defaultValue) const; 58 | 59 | /// Set a color property 60 | void setColor(const std::string &name, const Color3f &value); 61 | 62 | /// Get a color property, and throw an exception if it does not exist 63 | Color3f getColor(const std::string &name) const; 64 | 65 | /// Get a color property, and use a default value if it does not exist 66 | Color3f getColor(const std::string &name, const Color3f &defaultValue) const; 67 | 68 | /// Set a point property 69 | void setPoint(const std::string &name, const Point3f &value); 70 | 71 | /// Get a point property, and throw an exception if it does not exist 72 | Point3f getPoint(const std::string &name) const; 73 | 74 | /// Get a point property, and use a default value if it does not exist 75 | Point3f getPoint(const std::string &name, const Point3f &defaultValue) const; 76 | 77 | /// Set a vector property 78 | void setVector(const std::string &name, const Vector3f &value); 79 | 80 | /// Get a vector property, and throw an exception if it does not exist 81 | Vector3f getVector(const std::string &name) const; 82 | 83 | /// Get a vector property, and use a default value if it does not exist 84 | Vector3f getVector(const std::string &name, const Vector3f &defaultValue) const; 85 | 86 | /// Set a transform property 87 | void setTransform(const std::string &name, const Transform &value); 88 | 89 | /// Get a transform property, and throw an exception if it does not exist 90 | Transform getTransform(const std::string &name) const; 91 | 92 | /// Get a transform property, and use a default value if it does not exist 93 | Transform getTransform(const std::string &name, const Transform &defaultValue) const; 94 | private: 95 | /* Custom variant data type (stores one of boolean/integer/float/...) */ 96 | struct Property { 97 | enum { 98 | boolean_type, integer_type, float_type, 99 | string_type, color_type, point_type, 100 | vector_type, transform_type 101 | } type; 102 | 103 | /* Visual studio lacks support for unrestricted unions (as of ver. 2013) */ 104 | struct Value 105 | { 106 | Value() : boolean_value(false) { } 107 | ~Value() { } 108 | 109 | bool boolean_value; 110 | int integer_value; 111 | float float_value; 112 | std::string string_value; 113 | Color3f color_value; 114 | Point3f point_value; 115 | Vector3f vector_value; 116 | Transform transform_value; 117 | } value; 118 | 119 | Property() : type(boolean_type) { } 120 | }; 121 | 122 | std::map m_properties; 123 | }; 124 | 125 | NORI_NAMESPACE_END 126 | -------------------------------------------------------------------------------- /include/nori/ray.h: -------------------------------------------------------------------------------- 1 | /* 2 | This file is part of Nori, a simple educational ray tracer 3 | 4 | Copyright (c) 2015 by Wenzel Jakob 5 | */ 6 | 7 | #pragma once 8 | 9 | #include 10 | 11 | NORI_NAMESPACE_BEGIN 12 | 13 | /** 14 | * \brief Simple n-dimensional ray segment data structure 15 | * 16 | * Along with the ray origin and direction, this data structure additionally 17 | * stores a ray segment [mint, maxt] (whose entries may include positive/negative 18 | * infinity), as well as the componentwise reciprocals of the ray direction. 19 | * That is just done for convenience, as these values are frequently required. 20 | * 21 | * \remark Important: be careful when changing the ray direction. You must 22 | * call \ref update() to compute the componentwise reciprocals as well, or Nori's 23 | * ray-triangle intersection code will go haywire. 24 | */ 25 | template struct TRay { 26 | typedef _PointType PointType; 27 | typedef _VectorType VectorType; 28 | typedef typename PointType::Scalar Scalar; 29 | 30 | PointType o; ///< Ray origin 31 | VectorType d; ///< Ray direction 32 | VectorType dRcp; ///< Componentwise reciprocals of the ray direction 33 | Scalar mint; ///< Minimum position on the ray segment 34 | Scalar maxt; ///< Maximum position on the ray segment 35 | 36 | /// Construct a new ray 37 | TRay() : mint(Epsilon), 38 | maxt(std::numeric_limits::infinity()) { } 39 | 40 | /// Construct a new ray 41 | TRay(const PointType &o, const VectorType &d) : o(o), d(d), 42 | mint(Epsilon), maxt(std::numeric_limits::infinity()) { 43 | update(); 44 | } 45 | 46 | /// Construct a new ray 47 | TRay(const PointType &o, const VectorType &d, 48 | Scalar mint, Scalar maxt) : o(o), d(d), mint(mint), maxt(maxt) { 49 | update(); 50 | } 51 | 52 | /// Copy constructor 53 | TRay(const TRay &ray) 54 | : o(ray.o), d(ray.d), dRcp(ray.dRcp), 55 | mint(ray.mint), maxt(ray.maxt) { } 56 | 57 | /// Copy a ray, but change the covered segment of the copy 58 | TRay(const TRay &ray, Scalar mint, Scalar maxt) 59 | : o(ray.o), d(ray.d), dRcp(ray.dRcp), mint(mint), maxt(maxt) { } 60 | 61 | /// Update the reciprocal ray directions after changing 'd' 62 | void update() { 63 | dRcp = d.cwiseInverse(); 64 | } 65 | 66 | /// Return the position of a point along the ray 67 | PointType operator() (Scalar t) const { return o + t * d; } 68 | 69 | /// Return a ray that points into the opposite direction 70 | TRay reverse() const { 71 | TRay result; 72 | result.o = o; result.d = -d; result.dRcp = -dRcp; 73 | result.mint = mint; result.maxt = maxt; 74 | return result; 75 | } 76 | 77 | /// Return a human-readable string summary of this ray 78 | std::string toString() const { 79 | return tfm::format( 80 | "Ray[\n" 81 | " o = %s,\n" 82 | " d = %s,\n" 83 | " mint = %f,\n" 84 | " maxt = %f\n" 85 | "]", o.toString(), d.toString(), mint, maxt); 86 | } 87 | }; 88 | 89 | NORI_NAMESPACE_END 90 | -------------------------------------------------------------------------------- /include/nori/rfilter.h: -------------------------------------------------------------------------------- 1 | /* 2 | This file is part of Nori, a simple educational ray tracer 3 | 4 | Copyright (c) 2015 by Wenzel Jakob 5 | */ 6 | 7 | #pragma once 8 | 9 | #include 10 | 11 | /// Reconstruction filters will be tabulated at this resolution 12 | #define NORI_FILTER_RESOLUTION 32 13 | 14 | NORI_NAMESPACE_BEGIN 15 | 16 | /** 17 | * \brief Generic radially symmetric image reconstruction filter 18 | * 19 | * When adding radiance-valued samples to the rendered image, Nori 20 | * first convolves them with a so-called image reconstruction filter. 21 | * 22 | * To learn more about reconstruction filters and sampling theory 23 | * in general, take a look at the excellenent chapter 7 of PBRT, 24 | * which is freely available at: 25 | * 26 | * http://graphics.stanford.edu/~mmp/chapters/pbrt_chapter7.pdf 27 | */ 28 | class ReconstructionFilter : public NoriObject { 29 | public: 30 | /// Return the filter radius in fractional pixels 31 | float getRadius() const { return m_radius; } 32 | 33 | /// Evaluate the filter function 34 | virtual float eval(float x) const = 0; 35 | 36 | /** 37 | * \brief Return the type of object (i.e. Mesh/Camera/etc.) 38 | * provided by this instance 39 | * */ 40 | EClassType getClassType() const { return EReconstructionFilter; } 41 | protected: 42 | float m_radius; 43 | }; 44 | 45 | NORI_NAMESPACE_END 46 | -------------------------------------------------------------------------------- /include/nori/sampler.h: -------------------------------------------------------------------------------- 1 | /* 2 | This file is part of Nori, a simple educational ray tracer 3 | 4 | Copyright (c) 2015 by Wenzel Jakob 5 | */ 6 | 7 | #pragma once 8 | 9 | #include 10 | #include 11 | 12 | NORI_NAMESPACE_BEGIN 13 | 14 | class ImageBlock; 15 | 16 | /** 17 | * \brief Abstract sample generator 18 | * 19 | * A sample generator is responsible for generating the random number stream 20 | * that will be passed an \ref Integrator implementation as it computes the 21 | * radiance incident along a specified ray. 22 | * 23 | * The most simple conceivable sample generator is just a wrapper around the 24 | * Mersenne-Twister random number generator and is implemented in 25 | * independent.cpp (it is named this way because it generates 26 | * statistically independent random numbers). 27 | * 28 | * Fancier samplers might use stratification or low-discrepancy sequences 29 | * (e.g. Halton, Hammersley, or Sobol point sets) for improved convergence. 30 | * Another use of this class is in producing intentionally correlated 31 | * random numbers, e.g. as part of a Metropolis-Hastings integration scheme. 32 | * 33 | * The general interface between a sampler and a rendering algorithm is as 34 | * follows: Before beginning to render a pixel, the rendering algorithm calls 35 | * \ref generate(). The first pixel sample can now be computed, after which 36 | * \ref advance() needs to be invoked. This repeats until all pixel samples have 37 | * been exhausted. While computing a pixel sample, the rendering 38 | * algorithm requests (pseudo-) random numbers using the \ref next1D() and 39 | * \ref next2D() functions. 40 | * 41 | * Conceptually, the right way of thinking of this goes as follows: 42 | * For each sample in a pixel, a sample generator produces a (hypothetical) 43 | * point in an infinite dimensional random number hypercube. A rendering 44 | * algorithm can then request subsequent 1D or 2D components of this point 45 | * using the \ref next1D() and \ref next2D() functions. Fancy implementations 46 | * of this class make certain guarantees about the stratification of the 47 | * first n components with respect to the other points that are sampled 48 | * within a pixel. 49 | */ 50 | class Sampler : public NoriObject { 51 | public: 52 | /// Release all memory 53 | virtual ~Sampler() { } 54 | 55 | /// Create an exact clone of the current instance 56 | virtual std::unique_ptr clone() const = 0; 57 | 58 | /** 59 | * \brief Prepare to render a new image block 60 | * 61 | * This function is called when the sampler begins rendering 62 | * a new image block. This can be used to deterministically 63 | * initialize the sampler so that repeated program runs 64 | * always create the same image. 65 | */ 66 | virtual void prepare(const ImageBlock &block) = 0; 67 | 68 | /** 69 | * \brief Prepare to generate new samples 70 | * 71 | * This function is called initially and every time the 72 | * integrator starts rendering a new pixel. 73 | */ 74 | virtual void generate() = 0; 75 | 76 | /// Advance to the next sample 77 | virtual void advance() = 0; 78 | 79 | /// Retrieve the next component value from the current sample 80 | virtual float next1D() = 0; 81 | 82 | /// Retrieve the next two component values from the current sample 83 | virtual Point2f next2D() = 0; 84 | 85 | /// Return the number of configured pixel samples 86 | virtual size_t getSampleCount() const { return m_sampleCount; } 87 | 88 | /** 89 | * \brief Return the type of object (i.e. Mesh/Sampler/etc.) 90 | * provided by this instance 91 | * */ 92 | EClassType getClassType() const { return ESampler; } 93 | protected: 94 | size_t m_sampleCount; 95 | }; 96 | 97 | NORI_NAMESPACE_END 98 | -------------------------------------------------------------------------------- /include/nori/scene.h: -------------------------------------------------------------------------------- 1 | /* 2 | This file is part of Nori, a simple educational ray tracer 3 | 4 | Copyright (c) 2015 by Wenzel Jakob 5 | */ 6 | 7 | #pragma once 8 | 9 | #include 10 | 11 | NORI_NAMESPACE_BEGIN 12 | 13 | /** 14 | * \brief Main scene data structure 15 | * 16 | * This class holds information on scene objects and is responsible for 17 | * coordinating rendering jobs. It also provides useful query routines that 18 | * are mostly used by the \ref Integrator implementations. 19 | */ 20 | class Scene : public NoriObject { 21 | public: 22 | /// Construct a new scene object 23 | Scene(const PropertyList &); 24 | 25 | /// Release all memory 26 | virtual ~Scene(); 27 | 28 | /// Return a pointer to the scene's kd-tree 29 | const Accel *getAccel() const { return m_accel; } 30 | 31 | /// Return a pointer to the scene's integrator 32 | const Integrator *getIntegrator() const { return m_integrator; } 33 | 34 | /// Return a pointer to the scene's integrator 35 | Integrator *getIntegrator() { return m_integrator; } 36 | 37 | /// Return a pointer to the scene's camera 38 | const Camera *getCamera() const { return m_camera; } 39 | 40 | /// Return a pointer to the scene's sample generator (const version) 41 | const Sampler *getSampler() const { return m_sampler; } 42 | 43 | /// Return a pointer to the scene's sample generator 44 | Sampler *getSampler() { return m_sampler; } 45 | 46 | /// Return a reference to an array containing all meshes 47 | const std::vector &getMeshes() const { return m_meshes; } 48 | 49 | /** 50 | * \brief Intersect a ray against all triangles stored in the scene 51 | * and return detailed intersection information 52 | * 53 | * \param ray 54 | * A 3-dimensional ray data structure with minimum/maximum 55 | * extent information 56 | * 57 | * \param its 58 | * A detailed intersection record, which will be filled by the 59 | * intersection query 60 | * 61 | * \return \c true if an intersection was found 62 | */ 63 | bool rayIntersect(const Ray3f &ray, Intersection &its) const { 64 | return m_accel->rayIntersect(ray, its, false); 65 | } 66 | 67 | /** 68 | * \brief Intersect a ray against all triangles stored in the scene 69 | * and \a only determine whether or not there is an intersection. 70 | * 71 | * This method much faster than the other ray tracing function, 72 | * but the performance comes at the cost of not providing any 73 | * additional information about the detected intersection 74 | * (not even its position). 75 | * 76 | * \param ray 77 | * A 3-dimensional ray data structure with minimum/maximum 78 | * extent information 79 | * 80 | * \return \c true if an intersection was found 81 | */ 82 | bool rayIntersect(const Ray3f &ray) const { 83 | Intersection its; /* Unused */ 84 | return m_accel->rayIntersect(ray, its, true); 85 | } 86 | 87 | /// \brief Return an axis-aligned box that bounds the scene 88 | const BoundingBox3f &getBoundingBox() const { 89 | return m_accel->getBoundingBox(); 90 | } 91 | 92 | /** 93 | * \brief Inherited from \ref NoriObject::activate() 94 | * 95 | * Initializes the internal data structures (kd-tree, 96 | * emitter sampling data structures, etc.) 97 | */ 98 | void activate(); 99 | 100 | /// Add a child object to the scene (meshes, integrators etc.) 101 | void addChild(NoriObject *obj); 102 | 103 | /// Return a string summary of the scene (for debugging purposes) 104 | std::string toString() const; 105 | 106 | EClassType getClassType() const { return EScene; } 107 | private: 108 | std::vector m_meshes; 109 | Integrator *m_integrator = nullptr; 110 | Sampler *m_sampler = nullptr; 111 | Camera *m_camera = nullptr; 112 | Accel *m_accel = nullptr; 113 | }; 114 | 115 | NORI_NAMESPACE_END 116 | -------------------------------------------------------------------------------- /include/nori/timer.h: -------------------------------------------------------------------------------- 1 | /* 2 | This file is part of Nori, a simple educational ray tracer 3 | 4 | Copyright (c) 2015 by Wenzel Jakob 5 | */ 6 | 7 | #pragma once 8 | 9 | #include 10 | #include 11 | 12 | NORI_NAMESPACE_BEGIN 13 | 14 | /** 15 | * \brief Simple timer with millisecond precision 16 | * 17 | * This class is convenient for collecting performance data 18 | */ 19 | class Timer { 20 | public: 21 | /// Create a new timer and reset it 22 | Timer() { reset(); } 23 | 24 | /// Reset the timer to the current time 25 | void reset() { start = std::chrono::system_clock::now(); } 26 | 27 | /// Return the number of milliseconds elapsed since the timer was last reset 28 | double elapsed() const { 29 | auto now = std::chrono::system_clock::now(); 30 | auto duration = std::chrono::duration_cast(now - start); 31 | return (double) duration.count(); 32 | } 33 | 34 | /// Like \ref elapsed(), but return a human-readable string 35 | std::string elapsedString(bool precise = false) const { 36 | return timeString(elapsed(), precise); 37 | } 38 | 39 | /// Return the number of milliseconds elapsed since the timer was last reset and then reset it 40 | double lap() { 41 | auto now = std::chrono::system_clock::now(); 42 | auto duration = std::chrono::duration_cast(now - start); 43 | start = now; 44 | return (double) duration.count(); 45 | } 46 | 47 | /// Like \ref lap(), but return a human-readable string 48 | std::string lapString(bool precise = false) { 49 | return timeString(lap(), precise); 50 | } 51 | private: 52 | std::chrono::system_clock::time_point start; 53 | }; 54 | 55 | NORI_NAMESPACE_END 56 | -------------------------------------------------------------------------------- /include/nori/transform.h: -------------------------------------------------------------------------------- 1 | /* 2 | This file is part of Nori, a simple educational ray tracer 3 | 4 | Copyright (c) 2015 by Wenzel Jakob 5 | */ 6 | 7 | #pragma once 8 | 9 | #include 10 | #include 11 | 12 | NORI_NAMESPACE_BEGIN 13 | 14 | /** 15 | * \brief Homogeneous coordinate transformation 16 | * 17 | * This class stores a general homogeneous coordinate tranformation, such as 18 | * rotation, translation, uniform or non-uniform scaling, and perspective 19 | * transformations. The inverse of this transformation is also recorded 20 | * here, since it is required when transforming normal vectors. 21 | */ 22 | struct Transform { 23 | public: 24 | /// Create the identity transform 25 | Transform() : 26 | m_transform(Eigen::Matrix4f::Identity()), 27 | m_inverse(Eigen::Matrix4f::Identity()) { } 28 | 29 | /// Create a new transform instance for the given matrix 30 | Transform(const Eigen::Matrix4f &trafo); 31 | 32 | /// Create a new transform instance for the given matrix and its inverse 33 | Transform(const Eigen::Matrix4f &trafo, const Eigen::Matrix4f &inv) 34 | : m_transform(trafo), m_inverse(inv) { } 35 | 36 | /// Return the underlying matrix 37 | const Eigen::Matrix4f &getMatrix() const { 38 | return m_transform; 39 | } 40 | 41 | /// Return the inverse of the underlying matrix 42 | const Eigen::Matrix4f &getInverseMatrix() const { 43 | return m_inverse; 44 | } 45 | 46 | /// Return the inverse transformation 47 | Transform inverse() const { 48 | return Transform(m_inverse, m_transform); 49 | } 50 | 51 | /// Concatenate with another transform 52 | Transform operator*(const Transform &t) const; 53 | 54 | /// Apply the homogeneous transformation to a 3D vector 55 | Vector3f operator*(const Vector3f &v) const { 56 | return m_transform.topLeftCorner<3,3>() * v; 57 | } 58 | 59 | /// Apply the homogeneous transformation to a 3D normal 60 | Normal3f operator*(const Normal3f &n) const { 61 | return m_inverse.topLeftCorner<3, 3>().transpose() * n; 62 | } 63 | 64 | /// Transform a point by an arbitrary matrix in homogeneous coordinates 65 | Point3f operator*(const Point3f &p) const { 66 | Vector4f result = m_transform * Vector4f(p[0], p[1], p[2], 1.0f); 67 | return result.head<3>() / result.w(); 68 | } 69 | 70 | /// Apply the homogeneous transformation to a ray 71 | Ray3f operator*(const Ray3f &r) const { 72 | return Ray3f( 73 | operator*(r.o), 74 | operator*(r.d), 75 | r.mint, r.maxt 76 | ); 77 | } 78 | 79 | /// Return a string representation 80 | std::string toString() const; 81 | private: 82 | Eigen::Matrix4f m_transform; 83 | Eigen::Matrix4f m_inverse; 84 | }; 85 | 86 | NORI_NAMESPACE_END 87 | -------------------------------------------------------------------------------- /include/nori/vector.h: -------------------------------------------------------------------------------- 1 | /* 2 | This file is part of Nori, a simple educational ray tracer 3 | 4 | Copyright (c) 2015 by Wenzel Jakob 5 | */ 6 | 7 | #pragma once 8 | 9 | #include 10 | 11 | NORI_NAMESPACE_BEGIN 12 | 13 | /* =================================================================== 14 | This file contains a few templates and specializations, which 15 | provide 2/3D points, vectors, and normals over different 16 | underlying data types. Points, vectors, and normals are distinct 17 | in Nori, because they transform differently under homogeneous 18 | coordinate transformations. 19 | * =================================================================== */ 20 | 21 | /** 22 | * \brief Generic N-dimensional vector data structure based on Eigen::Matrix 23 | */ 24 | template struct TVector : public Eigen::Matrix<_Scalar, _Dimension, 1> { 25 | public: 26 | enum { 27 | Dimension = _Dimension 28 | }; 29 | 30 | typedef _Scalar Scalar; 31 | typedef Eigen::Matrix Base; 32 | typedef TVector VectorType; 33 | typedef TPoint PointType; 34 | 35 | /// Create a new vector with constant component vlaues 36 | TVector(Scalar value = (Scalar) 0) { Base::setConstant(value); } 37 | 38 | /// Create a new 2D vector (type error if \c Dimension != 2) 39 | TVector(Scalar x, Scalar y) : Base(x, y) { } 40 | 41 | /// Create a new 3D vector (type error if \c Dimension != 3) 42 | TVector(Scalar x, Scalar y, Scalar z) : Base(x, y, z) { } 43 | 44 | /// Create a new 4D vector (type error if \c Dimension != 4) 45 | TVector(Scalar x, Scalar y, Scalar z, Scalar w) : Base(x, y, z, w) { } 46 | 47 | /// Construct a vector from MatrixBase (needed to play nice with Eigen) 48 | template TVector(const Eigen::MatrixBase& p) 49 | : Base(p) { } 50 | 51 | /// Assign a vector from MatrixBase (needed to play nice with Eigen) 52 | template TVector &operator=(const Eigen::MatrixBase& p) { 53 | this->Base::operator=(p); 54 | return *this; 55 | } 56 | 57 | /// Return a human-readable string summary 58 | std::string toString() const { 59 | std::string result; 60 | for (size_t i=0; icoeff(i)); 62 | if (i+1 < Dimension) 63 | result += ", "; 64 | } 65 | return "[" + result + "]"; 66 | } 67 | }; 68 | 69 | /** 70 | * \brief Generic N-dimensional point data structure based on Eigen::Matrix 71 | */ 72 | template struct TPoint : public Eigen::Matrix<_Scalar, _Dimension, 1> { 73 | public: 74 | enum { 75 | Dimension = _Dimension 76 | }; 77 | 78 | typedef _Scalar Scalar; 79 | typedef Eigen::Matrix Base; 80 | typedef TVector VectorType; 81 | typedef TPoint PointType; 82 | 83 | /// Create a new point with constant component vlaues 84 | TPoint(Scalar value = (Scalar) 0) { Base::setConstant(value); } 85 | 86 | /// Create a new 2D point (type error if \c Dimension != 2) 87 | TPoint(Scalar x, Scalar y) : Base(x, y) { } 88 | 89 | /// Create a new 3D point (type error if \c Dimension != 3) 90 | TPoint(Scalar x, Scalar y, Scalar z) : Base(x, y, z) { } 91 | 92 | /// Create a new 4D point (type error if \c Dimension != 4) 93 | TPoint(Scalar x, Scalar y, Scalar z, Scalar w) : Base(x, y, z, w) { } 94 | 95 | /// Construct a point from MatrixBase (needed to play nice with Eigen) 96 | template TPoint(const Eigen::MatrixBase& p) 97 | : Base(p) { } 98 | 99 | /// Assign a point from MatrixBase (needed to play nice with Eigen) 100 | template TPoint &operator=(const Eigen::MatrixBase& p) { 101 | this->Base::operator=(p); 102 | return *this; 103 | } 104 | 105 | /// Return a human-readable string summary 106 | std::string toString() const { 107 | std::string result; 108 | for (size_t i=0; icoeff(i)); 110 | if (i+1 < Dimension) 111 | result += ", "; 112 | } 113 | return "[" + result + "]"; 114 | } 115 | }; 116 | 117 | /** 118 | * \brief 3-dimensional surface normal representation 119 | */ 120 | struct Normal3f : public Eigen::Matrix { 121 | public: 122 | enum { 123 | Dimension = 3 124 | }; 125 | 126 | typedef float Scalar; 127 | typedef Eigen::Matrix Base; 128 | typedef TVector VectorType; 129 | typedef TPoint PointType; 130 | 131 | 132 | /// Create a new normal with constant component vlaues 133 | Normal3f(Scalar value = 0.0f) { Base::setConstant(value); } 134 | 135 | /// Create a new 3D normal 136 | Normal3f(Scalar x, Scalar y, Scalar z) : Base(x, y, z) { } 137 | 138 | /// Construct a normal from MatrixBase (needed to play nice with Eigen) 139 | template Normal3f(const Eigen::MatrixBase& p) 140 | : Base(p) { } 141 | 142 | /// Assign a normal from MatrixBase (needed to play nice with Eigen) 143 | template Normal3f &operator=(const Eigen::MatrixBase& p) { 144 | this->Base::operator=(p); 145 | return *this; 146 | } 147 | 148 | /// Return a human-readable string summary 149 | std::string toString() const { 150 | return tfm::format("[%f, %f, %f]", coeff(0), coeff(1), coeff(2)); 151 | } 152 | }; 153 | 154 | /// Complete the set {a} to an orthonormal base 155 | extern void coordinateSystem(const Vector3f &a, Vector3f &b, Vector3f &c); 156 | 157 | NORI_NAMESPACE_END 158 | -------------------------------------------------------------------------------- /include/nori/warp.h: -------------------------------------------------------------------------------- 1 | /* 2 | This file is part of Nori, a simple educational ray tracer 3 | 4 | Copyright (c) 2015 by Wenzel Jakob 5 | */ 6 | 7 | #pragma once 8 | 9 | #include 10 | #include 11 | 12 | NORI_NAMESPACE_BEGIN 13 | 14 | /// A collection of useful warping functions for importance sampling 15 | class Warp { 16 | public: 17 | /// Dummy warping function: takes uniformly distributed points in a square and just returns them 18 | static Point2f squareToUniformSquare(const Point2f &sample); 19 | 20 | /// Probability density of \ref squareToUniformSquare() 21 | static float squareToUniformSquarePdf(const Point2f &p); 22 | 23 | /// Sample a 2D tent distribution 24 | static Point2f squareToTent(const Point2f &sample); 25 | 26 | /// Probability density of \ref squareToTent() 27 | static float squareToTentPdf(const Point2f &p); 28 | 29 | /// Uniformly sample a vector on a 2D disk with radius 1, centered around the origin 30 | static Point2f squareToUniformDisk(const Point2f &sample); 31 | 32 | /// Probability density of \ref squareToUniformDisk() 33 | static float squareToUniformDiskPdf(const Point2f &p); 34 | 35 | /// Uniformly sample a vector on the unit sphere with respect to solid angles 36 | static Vector3f squareToUniformSphere(const Point2f &sample); 37 | 38 | /// Probability density of \ref squareToUniformSphere() 39 | static float squareToUniformSpherePdf(const Vector3f &v); 40 | 41 | /// Uniformly sample a vector on the unit hemisphere around the pole (0,0,1) with respect to solid angles 42 | static Vector3f squareToUniformHemisphere(const Point2f &sample); 43 | 44 | /// Probability density of \ref squareToUniformHemisphere() 45 | static float squareToUniformHemispherePdf(const Vector3f &v); 46 | 47 | /// Uniformly sample a vector on the unit hemisphere around the pole (0,0,1) with respect to projected solid angles 48 | static Vector3f squareToCosineHemisphere(const Point2f &sample); 49 | 50 | /// Probability density of \ref squareToCosineHemisphere() 51 | static float squareToCosineHemispherePdf(const Vector3f &v); 52 | 53 | /// Warp a uniformly distributed square sample to a Beckmann distribution * cosine for the given 'alpha' parameter 54 | static Vector3f squareToBeckmann(const Point2f &sample, float alpha); 55 | 56 | /// Probability density of \ref squareToBeckmann() 57 | static float squareToBeckmannPdf(const Vector3f &m, float alpha); 58 | }; 59 | 60 | NORI_NAMESPACE_END 61 | -------------------------------------------------------------------------------- /scenes/pa1/bunny.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | -------------------------------------------------------------------------------- /scenes/pa2/ajax-normals.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | -------------------------------------------------------------------------------- /scenes/pa3/ajax-ao.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | -------------------------------------------------------------------------------- /scenes/pa3/ajax-simple.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | -------------------------------------------------------------------------------- /scenes/pa4/cbox/cbox-distributed.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 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 | -------------------------------------------------------------------------------- /scenes/pa4/cbox/cbox-whitted.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 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 | -------------------------------------------------------------------------------- /scenes/pa4/cbox/meshes/leftwall.obj: -------------------------------------------------------------------------------- 1 | # Blender v2.72 (sub 0) OBJ File: '' 2 | # www.blender.org 3 | mtllib leftwall.mtl 4 | o leftWall 5 | v -1.020000 1.590000 -1.040000 6 | v -1.020000 1.590000 0.990000 7 | v -1.010000 -0.000000 0.990000 8 | v -0.990000 0.000000 -1.040000 9 | vt 0.000000 1.000000 10 | usemtl leftWall 11 | s 1 12 | f 1/1 2/1 3/1 13 | f 4/1 1/1 3/1 14 | -------------------------------------------------------------------------------- /scenes/pa4/cbox/meshes/light.obj: -------------------------------------------------------------------------------- 1 | # Blender v2.72 (sub 0) OBJ File: '' 2 | # www.blender.org 3 | mtllib light.mtl 4 | o light 5 | v 0.230000 1.580000 -0.220000 6 | v 0.230000 1.580000 0.160000 7 | v -0.240000 1.580000 0.160000 8 | v -0.240000 1.580000 -0.220000 9 | vt 0.000000 1.000000 10 | usemtl light 11 | s 1 12 | f 1/1 2/1 3/1 13 | f 4/1 1/1 3/1 14 | -------------------------------------------------------------------------------- /scenes/pa4/cbox/meshes/rightwall.obj: -------------------------------------------------------------------------------- 1 | # Blender v2.72 (sub 0) OBJ File: '' 2 | # www.blender.org 3 | mtllib rightwall.mtl 4 | o rightWall 5 | v 1.000000 1.590000 0.990000 6 | v 1.000000 1.590000 -1.040000 7 | v 1.000000 0.000000 -1.040000 8 | v 1.000000 -0.000000 0.990000 9 | vt 0.000000 1.000000 10 | usemtl rightWall 11 | s 1 12 | f 1/1 2/1 3/1 13 | f 4/1 1/1 3/1 14 | -------------------------------------------------------------------------------- /scenes/pa4/cbox/meshes/walls.obj: -------------------------------------------------------------------------------- 1 | # Blender v2.72 (sub 0) OBJ File: '' 2 | # www.blender.org 3 | mtllib walls.mtl 4 | o ceiling 5 | v 1.000000 1.590000 -1.040000 6 | v 1.000000 1.590000 0.990000 7 | v -1.020000 1.590000 0.990000 8 | v -1.020000 1.590000 -1.040000 9 | vt 0.000000 1.000000 10 | usemtl ceiling 11 | s 1 12 | f 1/1 2/1 3/1 13 | f 4/1 1/1 3/1 14 | o floor 15 | v 1.000000 0.000000 -1.040000 16 | v -0.990000 0.000000 -1.040000 17 | v -1.010000 -0.000000 0.990000 18 | v 1.000000 -0.000000 0.990000 19 | vt 0.000000 1.000000 20 | usemtl floor 21 | s 1 22 | f 5/2 6/2 7/2 23 | f 8/2 5/2 7/2 24 | o backWall 25 | v 1.000000 1.590000 -1.040000 26 | v -1.020000 1.590000 -1.040000 27 | v -0.990000 0.000000 -1.040000 28 | v 1.000000 0.000000 -1.040000 29 | vt 0.000000 1.000000 30 | usemtl backWall 31 | s 1 32 | f 9/3 10/3 11/3 33 | f 12/3 9/3 11/3 34 | -------------------------------------------------------------------------------- /scenes/pa4/motto/meshes/floor.obj: -------------------------------------------------------------------------------- 1 | v -9.2 -4.5 3.2 2 | v -9.2 -4.48421 2.68512 3 | v -9.2 -4.43692 2.17227 4 | v -9.2 -4.3583 1.66347 5 | v -9.2 -4.24867 1.16074 6 | v -9.2 -4.10845 0.666061 7 | v -9.2 -3.93821 0.181379 8 | v -9.2 -3.73862 -0.29139 9 | v -9.2 -3.51045 -0.75038 10 | v -9.2 -3.25462 -1.19378 11 | v -9.2 -2.97214 -1.61984 12 | v -9.2 -2.66411 -2.02688 13 | v -9.2 -2.33175 -2.41329 14 | v -9.2 -1.97638 -2.77754 15 | v -9.2 -1.59939 -3.11821 16 | v -9.2 -1.20228 -3.43394 17 | v -9.2 -0.786614 -3.72349 18 | v -9.2 -0.354029 -3.98571 19 | v -9.2 0.0937657 -4.21958 20 | v -9.2 0.555004 -4.42417 21 | v -9.2 1.02786 -4.59866 22 | v -9.2 1.51048 -4.74238 23 | v -9.2 2.00095 -4.85476 24 | v -9.2 2.49733 -4.93534 25 | v -9.2 2.99768 -4.98382 26 | v -9.2 3.5 -5 27 | v -9.2 4.00232 -4.98382 28 | v -9.2 4.50267 -4.93534 29 | v -9.2 4.99905 -4.85476 30 | v -9.2 5.48952 -4.74238 31 | v -9.2 5.97214 -4.59866 32 | v -9.2 6.445 -4.42417 33 | v -9.2 6.90623 -4.21958 34 | v -9.2 7.35403 -3.98571 35 | v -9.2 7.78661 -3.72349 36 | v -9.2 -4.48421 3.71488 37 | v 10.8 -4.5 3.2 38 | v 10.8 -4.48421 2.68512 39 | v 10.8 -4.43692 2.17227 40 | v 10.8 -4.3583 1.66347 41 | v 10.8 -4.24867 1.16074 42 | v 10.8 -4.10845 0.666061 43 | v 10.8 -3.93821 0.181379 44 | v 10.8 -3.73862 -0.29139 45 | v 10.8 -3.51045 -0.75038 46 | v 10.8 -3.25462 -1.19378 47 | v 10.8 -2.97214 -1.61984 48 | v 10.8 -2.66411 -2.02688 49 | v 10.8 -2.33175 -2.41329 50 | v 10.8 -1.97638 -2.77754 51 | v 10.8 -1.59939 -3.11821 52 | v 10.8 -1.20228 -3.43394 53 | v 10.8 -0.786614 -3.72349 54 | v 10.8 -0.354029 -3.98571 55 | v 10.8 0.0937657 -4.21958 56 | v 10.8 0.555004 -4.42417 57 | v 10.8 1.02786 -4.59866 58 | v 10.8 1.51048 -4.74238 59 | v 10.8 2.00095 -4.85476 60 | v 10.8 2.49733 -4.93534 61 | v 10.8 2.99768 -4.98382 62 | v 10.8 3.5 -5 63 | v 10.8 4.00232 -4.98382 64 | v 10.8 4.50267 -4.93534 65 | v 10.8 4.99905 -4.85476 66 | v 10.8 5.48952 -4.74238 67 | v 10.8 5.97214 -4.59866 68 | v 10.8 6.445 -4.42417 69 | v 10.8 6.90623 -4.21958 70 | v 10.8 7.35403 -3.98571 71 | v 10.8 7.78661 -3.72349 72 | v 10.8 -4.48421 3.71488 73 | vn 1 0 0 74 | vn 0 1 0 75 | vn 0 0.998121 0.0612671 76 | vn 0 0.99249 0.122328 77 | vn 0 0.983118 0.182975 78 | vn 0 0.970027 0.242998 79 | vn 0 0.953248 0.302189 80 | vn 0 0.932821 0.360339 81 | vn 0 0.908798 0.417237 82 | vn 0 0.881239 0.472672 83 | vn 0 0.850218 0.52643 84 | vn 0 0.815823 0.578301 85 | vn 0 0.778154 0.628074 86 | vn 0 0.737325 0.675539 87 | vn 0 0.693466 0.72049 88 | vn 0 0.646723 0.762725 89 | vn 0 0.59726 0.802047 90 | vn 0 0.545255 0.83827 91 | vn 0 0.490904 0.871214 92 | vn 0 0.434418 0.900712 93 | vn 0 0.376024 0.92661 94 | vn 0 0.315965 0.948771 95 | vn 0 0.254497 0.967074 96 | vn 0 0.191886 0.981417 97 | vn 0 0.128409 0.991721 98 | vn 0 0.0643503 0.997927 99 | vn 0 -7.45445e-09 1 100 | vn 0 -0.0643503 0.997927 101 | vn 0 -0.128409 0.991721 102 | vn 0 -0.191886 0.981417 103 | vn 0 -0.254497 0.967074 104 | vn 0 -0.315965 0.948771 105 | vn 0 -0.376024 0.92661 106 | vn 0 -0.434418 0.900712 107 | vn 0 -0.490903 0.871214 108 | vn 0 -0.518378 0.855152 109 | vn 0 0.99953 -0.0306454 110 | vn -1 0 0 111 | f 1//1 2//1 3//1 4//1 5//1 6//1 7//1 8//1 9//1 10//1 11//1 12//1 13//1 14//1 15//1 16//1 17//1 18//1 19//1 20//1 21//1 22//1 23//1 24//1 25//1 26//1 27//1 28//1 29//1 30//1 31//1 32//1 33//1 34//1 35//1 36//1 112 | f 37//2 38//3 2//3 1//2 113 | f 38//3 39//4 3//4 2//3 114 | f 39//4 40//5 4//5 3//4 115 | f 40//5 41//6 5//6 4//5 116 | f 41//6 42//7 6//7 5//6 117 | f 42//7 43//8 7//8 6//7 118 | f 43//8 44//9 8//9 7//8 119 | f 44//9 45//10 9//10 8//9 120 | f 45//10 46//11 10//11 9//10 121 | f 46//11 47//12 11//12 10//11 122 | f 47//12 48//13 12//13 11//12 123 | f 48//13 49//14 13//14 12//13 124 | f 49//14 50//15 14//15 13//14 125 | f 50//15 51//16 15//16 14//15 126 | f 51//16 52//17 16//17 15//16 127 | f 52//17 53//18 17//18 16//17 128 | f 53//18 54//19 18//19 17//18 129 | f 54//19 55//20 19//20 18//19 130 | f 55//20 56//21 20//21 19//20 131 | f 56//21 57//22 21//22 20//21 132 | f 57//22 58//23 22//23 21//22 133 | f 58//23 59//24 23//24 22//23 134 | f 59//24 60//25 24//25 23//24 135 | f 60//25 61//26 25//26 24//25 136 | f 61//26 62//27 26//27 25//26 137 | f 62//27 63//28 27//28 26//27 138 | f 63//28 64//29 28//29 27//28 139 | f 64//29 65//30 29//30 28//29 140 | f 65//30 66//31 30//31 29//30 141 | f 66//31 67//32 31//32 30//31 142 | f 67//32 68//33 32//33 31//32 143 | f 68//33 69//34 33//34 32//33 144 | f 69//34 70//35 34//35 33//34 145 | f 70//35 71//36 35//36 34//35 146 | f 72//37 37//2 1//2 36//37 147 | f 37//38 72//38 71//38 70//38 69//38 68//38 67//38 66//38 65//38 64//38 63//38 62//38 61//38 60//38 59//38 58//38 57//38 56//38 55//38 54//38 53//38 52//38 51//38 50//38 49//38 48//38 47//38 46//38 45//38 44//38 43//38 42//38 41//38 40//38 39//38 38//38 148 | -------------------------------------------------------------------------------- /scenes/pa4/motto/motto-dielectric.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | -------------------------------------------------------------------------------- /scenes/pa4/motto/motto-diffuse.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | -------------------------------------------------------------------------------- /scenes/pa4/tests/meshes/floor.obj: -------------------------------------------------------------------------------- 1 | v -10 0 -10 2 | v -10 0 10 3 | v 10 0 10 4 | v 10 0 -10 5 | f 1 2 3 6 | f 1 3 4 7 | -------------------------------------------------------------------------------- /scenes/pa4/tests/meshes/furnace.obj: -------------------------------------------------------------------------------- 1 | v -0.500000 -0.500000 0.500000 2 | v 0.500000 -0.500000 0.500000 3 | v -0.500000 0.500000 0.500000 4 | v 0.500000 0.500000 0.500000 5 | v -0.500000 0.500000 -0.500000 6 | v 0.500000 0.500000 -0.500000 7 | v -0.500000 -0.500000 -0.500000 8 | v 0.500000 -0.500000 -0.500000 9 | vt 0.375000 0.000000 10 | vt 0.625000 0.000000 11 | vt 0.375000 0.250000 12 | vt 0.625000 0.250000 13 | vt 0.375000 0.500000 14 | vt 0.625000 0.500000 15 | vt 0.375000 0.750000 16 | vt 0.625000 0.750000 17 | vt 0.375000 1.000000 18 | vt 0.625000 1.000000 19 | vt 0.875000 0.000000 20 | vt 0.875000 0.250000 21 | vt 0.125000 0.000000 22 | vt 0.125000 0.250000 23 | vn 0.000000 0.000000 -1.000000 24 | vn 0.000000 0.000000 -1.000000 25 | vn 0.000000 0.000000 -1.000000 26 | vn 0.000000 0.000000 -1.000000 27 | vn 0.000000 -1.000000 0.000000 28 | vn 0.000000 -1.000000 0.000000 29 | vn 0.000000 -1.000000 0.000000 30 | vn 0.000000 -1.000000 0.000000 31 | vn 0.000000 0.000000 1.000000 32 | vn 0.000000 0.000000 1.000000 33 | vn 0.000000 0.000000 1.000000 34 | vn 0.000000 0.000000 1.000000 35 | vn 0.000000 1.000000 0.000000 36 | vn 0.000000 1.000000 0.000000 37 | vn 0.000000 1.000000 0.000000 38 | vn 0.000000 1.000000 0.000000 39 | vn -1.000000 0.000000 0.000000 40 | vn -1.000000 0.000000 0.000000 41 | vn -1.000000 0.000000 0.000000 42 | vn -1.000000 0.000000 0.000000 43 | vn 1.000000 0.000000 0.000000 44 | vn 1.000000 0.000000 0.000000 45 | vn 1.000000 0.000000 0.000000 46 | vn 1.000000 0.000000 0.000000 47 | f 1/1/1 2/2/2 3/3/3 48 | f 3/3/3 2/2/2 4/4/4 49 | f 3/3/5 4/4/6 5/5/7 50 | f 5/5/7 4/4/6 6/6/8 51 | f 5/5/9 6/6/10 7/7/11 52 | f 7/7/11 6/6/10 8/8/12 53 | f 7/7/13 8/8/14 1/9/15 54 | f 1/9/15 8/8/14 2/10/16 55 | f 2/2/17 8/11/18 4/4/19 56 | f 4/4/19 8/11/18 6/12/20 57 | f 7/13/21 1/1/22 5/14/23 58 | f 5/14/23 1/1/22 3/3/24 59 | -------------------------------------------------------------------------------- /scenes/pa4/tests/meshes/polylum.py: -------------------------------------------------------------------------------- 1 | #!python 2 | 3 | import numpy as np 4 | import sys 5 | 6 | # Make up test cases with polygonal luminaires. 7 | 8 | # Step 1: generate a random triangle that lies in the +y half-space. 9 | # Make sure it faces the origin. 10 | v = np.transpose(np.random.rand(3,3) - [[0.5], [0], [0.5]]) 11 | normal = np.cross(v[1] - v[0], v[2] - v[0]) 12 | if (np.dot(normal, v[0]) > 0): 13 | v = np.flipud(v) 14 | 15 | # Step 2: compute the irradiance using Lambert's formula. 16 | # See Arvo's thesis, equations 3.1 to 3.3. 17 | 18 | def norm(x): 19 | return np.sqrt(np.dot(x,x)) 20 | 21 | Phi = 0 # vector irradiance 22 | for k0 in range(3): 23 | k1 = (k0 + 1) % 3 24 | Theta = np.arccos(np.dot(v[k0], v[k1]) / (norm(v[k0]) * norm(v[k1]))) 25 | Gamma1 = np.cross(v[k0], v[k1]) 26 | Gamma = Gamma1 / norm(Gamma1) 27 | Phi += 1 / 4.0 * Theta * Gamma 28 | 29 | irradiance = -np.dot(Phi, [0,1,0]) 30 | 31 | # Step 3: write out a nori test scene, wrapped in a t-test 32 | 33 | xml_text = """ 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 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 | obj_text = """v %g %g %g 73 | v %g %g %g 74 | v %g %g %g 75 | f 1 2 3 76 | """ 77 | 78 | if len(sys.argv) < 3: 79 | print "Usage: python polylum.py " 80 | sys.exit(-1) 81 | 82 | fname_obj = sys.argv[2] 83 | 84 | f_xml = open(sys.argv[1], 'w') 85 | f_xml.write(xml_text % (.5 / np.pi * irradiance, fname_obj)) 86 | f_xml.close() 87 | 88 | f_obj = open(fname_obj, 'w') 89 | f_obj.write(obj_text % tuple(v.flat)) 90 | f_obj.close() 91 | -------------------------------------------------------------------------------- /scenes/pa4/tests/meshes/polylum1.obj: -------------------------------------------------------------------------------- 1 | v -0.443432 0.596366 0.495985 2 | v -0.320397 0.0677699 -0.229359 3 | v 0.0341197 0.11415 -0.343049 4 | f 1 2 3 5 | -------------------------------------------------------------------------------- /scenes/pa4/tests/meshes/polylum2.obj: -------------------------------------------------------------------------------- 1 | v 0.461963 0.397843 0.412747 2 | v -0.0381582 0.851323 -0.156872 3 | v 0.195443 0.891957 -0.260672 4 | f 1 2 3 5 | -------------------------------------------------------------------------------- /scenes/pa4/tests/meshes/polylum3.obj: -------------------------------------------------------------------------------- 1 | v 0.100871 0.289075 -0.422609 2 | v -0.308386 0.629587 0.423966 3 | v -0.373132 0.237287 0.351592 4 | f 1 2 3 5 | -------------------------------------------------------------------------------- /scenes/pa4/tests/meshes/polylum4.obj: -------------------------------------------------------------------------------- 1 | v -0.17575 0.639397 0.151268 2 | v 0.318604 0.732996 0.459771 3 | v 0.139095 0.294498 0.478556 4 | f 1 2 3 5 | -------------------------------------------------------------------------------- /scenes/pa4/tests/meshes/polylum5.obj: -------------------------------------------------------------------------------- 1 | v -0.17487 0.447916 0.367201 2 | v -0.405259 0.249607 -0.354079 3 | v 0.356029 0.0918984 -0.132271 4 | f 1 2 3 5 | -------------------------------------------------------------------------------- /scenes/pa4/tests/test-mesh-furnace.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 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 | -------------------------------------------------------------------------------- /scenes/pa4/tests/test-mesh.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 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 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | -------------------------------------------------------------------------------- /scenes/pa5/ajax/ajax-rough.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | -------------------------------------------------------------------------------- /scenes/pa5/ajax/ajax-smooth.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | -------------------------------------------------------------------------------- /scenes/pa5/ajax/light.obj: -------------------------------------------------------------------------------- 1 | o Mesh 2 | v -50.357 33.7 33.6193 3 | v -69.643 33.7 6.3807 4 | v -69.643 6.3 6.3807 5 | v -50.357 6.3 33.6193 6 | vn 0.816138 0 -0.577857 7 | mtllib Untitled.mtl 8 | usemtl Default 9 | g Default 10 | f 2//1 1//1 4//1 3//1 11 | -------------------------------------------------------------------------------- /scenes/pa5/cbox/cbox_ems.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 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 | -------------------------------------------------------------------------------- /scenes/pa5/cbox/cbox_mats.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 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 | -------------------------------------------------------------------------------- /scenes/pa5/cbox/cbox_mis.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 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 | -------------------------------------------------------------------------------- /scenes/pa5/cbox/meshes/leftwall.obj: -------------------------------------------------------------------------------- 1 | # Blender v2.72 (sub 0) OBJ File: '' 2 | # www.blender.org 3 | mtllib leftwall.mtl 4 | o leftWall 5 | v -1.020000 1.590000 -1.040000 6 | v -1.020000 1.590000 0.990000 7 | v -1.010000 -0.000000 0.990000 8 | v -0.990000 0.000000 -1.040000 9 | vt 0.000000 1.000000 10 | usemtl leftWall 11 | s 1 12 | f 1/1 2/1 3/1 13 | f 4/1 1/1 3/1 14 | -------------------------------------------------------------------------------- /scenes/pa5/cbox/meshes/light.obj: -------------------------------------------------------------------------------- 1 | # Blender v2.72 (sub 0) OBJ File: '' 2 | # www.blender.org 3 | mtllib light.mtl 4 | o light 5 | v 0.230000 1.580000 -0.220000 6 | v 0.230000 1.580000 0.160000 7 | v -0.240000 1.580000 0.160000 8 | v -0.240000 1.580000 -0.220000 9 | vt 0.000000 1.000000 10 | usemtl light 11 | s 1 12 | f 1/1 2/1 3/1 13 | f 4/1 1/1 3/1 14 | -------------------------------------------------------------------------------- /scenes/pa5/cbox/meshes/rightwall.obj: -------------------------------------------------------------------------------- 1 | # Blender v2.72 (sub 0) OBJ File: '' 2 | # www.blender.org 3 | mtllib rightwall.mtl 4 | o rightWall 5 | v 1.000000 1.590000 0.990000 6 | v 1.000000 1.590000 -1.040000 7 | v 1.000000 0.000000 -1.040000 8 | v 1.000000 -0.000000 0.990000 9 | vt 0.000000 1.000000 10 | usemtl rightWall 11 | s 1 12 | f 1/1 2/1 3/1 13 | f 4/1 1/1 3/1 14 | -------------------------------------------------------------------------------- /scenes/pa5/cbox/meshes/walls.obj: -------------------------------------------------------------------------------- 1 | # Blender v2.72 (sub 0) OBJ File: '' 2 | # www.blender.org 3 | mtllib walls.mtl 4 | o ceiling 5 | v 1.000000 1.590000 -1.040000 6 | v 1.000000 1.590000 0.990000 7 | v -1.020000 1.590000 0.990000 8 | v -1.020000 1.590000 -1.040000 9 | vt 0.000000 1.000000 10 | usemtl ceiling 11 | s 1 12 | f 1/1 2/1 3/1 13 | f 4/1 1/1 3/1 14 | o floor 15 | v 1.000000 0.000000 -1.040000 16 | v -0.990000 0.000000 -1.040000 17 | v -1.010000 -0.000000 0.990000 18 | v 1.000000 -0.000000 0.990000 19 | vt 0.000000 1.000000 20 | usemtl floor 21 | s 1 22 | f 5/2 6/2 7/2 23 | f 8/2 5/2 7/2 24 | o backWall 25 | v 1.000000 1.590000 -1.040000 26 | v -1.020000 1.590000 -1.040000 27 | v -0.990000 0.000000 -1.040000 28 | v 1.000000 0.000000 -1.040000 29 | vt 0.000000 1.000000 30 | usemtl backWall 31 | s 1 32 | f 9/3 10/3 11/3 33 | f 12/3 9/3 11/3 34 | -------------------------------------------------------------------------------- /scenes/pa5/table/meshes/mesh_1.obj: -------------------------------------------------------------------------------- 1 | o meshes@1 2 | v -231.652 -199.179 0 3 | v 392.021 -199.179 0 4 | v 392.021 182.76 0 5 | v -231.652 182.76 0 6 | vt 0.00980392 0.00980392 7 | vt 0.990196 0.00980392 8 | vt 0.990196 0.990196 9 | vt 0.00980392 0.990196 10 | vn 0 0 1 11 | vn 0 0 1 12 | vn 0 0 1 13 | vn 0 0 1 14 | f 2/2/2 4/4/4 1/1/1 15 | f 4/4/4 2/2/2 3/3/3 16 | -------------------------------------------------------------------------------- /scenes/pa5/table/table_ems.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 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 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | -------------------------------------------------------------------------------- /scenes/pa5/table/table_mats.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 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 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | -------------------------------------------------------------------------------- /scenes/pa5/table/table_mis.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 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 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | -------------------------------------------------------------------------------- /scenes/pa5/tests/chi2test-microfacet.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /scenes/pa5/tests/floor.obj: -------------------------------------------------------------------------------- 1 | v -10 0 -10 2 | v -10 0 10 3 | v 10 0 10 4 | v 10 0 -10 5 | f 1 2 3 6 | f 1 3 4 7 | -------------------------------------------------------------------------------- /scenes/pa5/tests/furnace.obj: -------------------------------------------------------------------------------- 1 | v -0.500000 -0.500000 0.500000 2 | v 0.500000 -0.500000 0.500000 3 | v -0.500000 0.500000 0.500000 4 | v 0.500000 0.500000 0.500000 5 | v -0.500000 0.500000 -0.500000 6 | v 0.500000 0.500000 -0.500000 7 | v -0.500000 -0.500000 -0.500000 8 | v 0.500000 -0.500000 -0.500000 9 | vt 0.375000 0.000000 10 | vt 0.625000 0.000000 11 | vt 0.375000 0.250000 12 | vt 0.625000 0.250000 13 | vt 0.375000 0.500000 14 | vt 0.625000 0.500000 15 | vt 0.375000 0.750000 16 | vt 0.625000 0.750000 17 | vt 0.375000 1.000000 18 | vt 0.625000 1.000000 19 | vt 0.875000 0.000000 20 | vt 0.875000 0.250000 21 | vt 0.125000 0.000000 22 | vt 0.125000 0.250000 23 | vn 0.000000 0.000000 -1.000000 24 | vn 0.000000 0.000000 -1.000000 25 | vn 0.000000 0.000000 -1.000000 26 | vn 0.000000 0.000000 -1.000000 27 | vn 0.000000 -1.000000 0.000000 28 | vn 0.000000 -1.000000 0.000000 29 | vn 0.000000 -1.000000 0.000000 30 | vn 0.000000 -1.000000 0.000000 31 | vn 0.000000 0.000000 1.000000 32 | vn 0.000000 0.000000 1.000000 33 | vn 0.000000 0.000000 1.000000 34 | vn 0.000000 0.000000 1.000000 35 | vn 0.000000 1.000000 0.000000 36 | vn 0.000000 1.000000 0.000000 37 | vn 0.000000 1.000000 0.000000 38 | vn 0.000000 1.000000 0.000000 39 | vn -1.000000 0.000000 0.000000 40 | vn -1.000000 0.000000 0.000000 41 | vn -1.000000 0.000000 0.000000 42 | vn -1.000000 0.000000 0.000000 43 | vn 1.000000 0.000000 0.000000 44 | vn 1.000000 0.000000 0.000000 45 | vn 1.000000 0.000000 0.000000 46 | vn 1.000000 0.000000 0.000000 47 | f 1/1/1 2/2/2 3/3/3 48 | f 3/3/3 2/2/2 4/4/4 49 | f 3/3/5 4/4/6 5/5/7 50 | f 5/5/7 4/4/6 6/6/8 51 | f 5/5/9 6/6/10 7/7/11 52 | f 7/7/11 6/6/10 8/8/12 53 | f 7/7/13 8/8/14 1/9/15 54 | f 1/9/15 8/8/14 2/10/16 55 | f 2/2/17 8/11/18 4/4/19 56 | f 4/4/19 8/11/18 6/12/20 57 | f 7/13/21 1/1/22 5/14/23 58 | f 5/14/23 1/1/22 3/3/24 59 | -------------------------------------------------------------------------------- /scenes/pa5/tests/polylum.py: -------------------------------------------------------------------------------- 1 | #!python 2 | 3 | import numpy as np 4 | import sys 5 | 6 | # Make up test cases with polygonal luminaires. 7 | 8 | # Step 1: generate a random triangle that lies in the +y half-space. 9 | # Make sure it faces the origin. 10 | v = np.transpose(np.random.rand(3,3) - [[0.5], [0], [0.5]]) 11 | normal = np.cross(v[1] - v[0], v[2] - v[0]) 12 | if (np.dot(normal, v[0]) > 0): 13 | v = np.flipud(v) 14 | 15 | # Step 2: compute the irradiance using Lambert's formula. 16 | # See Arvo's thesis, equations 3.1 to 3.3. 17 | 18 | def norm(x): 19 | return np.sqrt(np.dot(x,x)) 20 | 21 | Phi = 0 # vector irradiance 22 | for k0 in range(3): 23 | k1 = (k0 + 1) % 3 24 | Theta = np.arccos(np.dot(v[k0], v[k1]) / (norm(v[k0]) * norm(v[k1]))) 25 | Gamma1 = np.cross(v[k0], v[k1]) 26 | Gamma = Gamma1 / norm(Gamma1) 27 | Phi += 1 / 4.0 * Theta * Gamma 28 | 29 | irradiance = -np.dot(Phi, [0,1,0]) 30 | 31 | # Step 3: write out a nori test scene, wrapped in a t-test 32 | 33 | xml_text = """ 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 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 | obj_text = """v %g %g %g 73 | v %g %g %g 74 | v %g %g %g 75 | f 1 2 3 76 | """ 77 | 78 | if len(sys.argv) < 3: 79 | print "Usage: python polylum.py " 80 | sys.exit(-1) 81 | 82 | fname_obj = sys.argv[2] 83 | 84 | f_xml = open(sys.argv[1], 'w') 85 | f_xml.write(xml_text % (.5 / np.pi * irradiance, fname_obj)) 86 | f_xml.close() 87 | 88 | f_obj = open(fname_obj, 'w') 89 | f_obj.write(obj_text % tuple(v.flat)) 90 | f_obj.close() 91 | -------------------------------------------------------------------------------- /scenes/pa5/tests/polylum1.obj: -------------------------------------------------------------------------------- 1 | v -0.443432 0.596366 0.495985 2 | v -0.320397 0.0677699 -0.229359 3 | v 0.0341197 0.11415 -0.343049 4 | f 1 2 3 5 | -------------------------------------------------------------------------------- /scenes/pa5/tests/polylum2.obj: -------------------------------------------------------------------------------- 1 | v 0.461963 0.397843 0.412747 2 | v -0.0381582 0.851323 -0.156872 3 | v 0.195443 0.891957 -0.260672 4 | f 1 2 3 5 | -------------------------------------------------------------------------------- /scenes/pa5/tests/polylum3.obj: -------------------------------------------------------------------------------- 1 | v 0.100871 0.289075 -0.422609 2 | v -0.308386 0.629587 0.423966 3 | v -0.373132 0.237287 0.351592 4 | f 1 2 3 5 | -------------------------------------------------------------------------------- /scenes/pa5/tests/polylum4.obj: -------------------------------------------------------------------------------- 1 | v -0.17575 0.639397 0.151268 2 | v 0.318604 0.732996 0.459771 3 | v 0.139095 0.294498 0.478556 4 | f 1 2 3 5 | -------------------------------------------------------------------------------- /scenes/pa5/tests/polylum5.obj: -------------------------------------------------------------------------------- 1 | v -0.17487 0.447916 0.367201 2 | v -0.405259 0.249607 -0.354079 3 | v 0.356029 0.0918984 -0.132271 4 | f 1 2 3 5 | -------------------------------------------------------------------------------- /scenes/pa5/tests/test-furnace.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 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 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | -------------------------------------------------------------------------------- /scenes/pa5/tests/ttest-microfacet.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /scenes/pa5/veach_mi/meshes/floor.obj: -------------------------------------------------------------------------------- 1 | v -10 -4.14615 -10 2 | v -10 -4.14615 20 3 | v 10 -4.14615 20 4 | v 10 -4.14615 20 5 | v 10 -4.14615 -10 6 | v -10 -4.14615 -10 7 | v -10 -10 -2 8 | v 10 -10 -2 9 | v 10 10 -2 10 | v 10 10 -2 11 | v -10 10 -2 12 | v -10 -10 -2 13 | v -10 5 -10 14 | v -10 5 20 15 | v 10 5 20 16 | v 10 5 20 17 | v 10 5 -10 18 | v -10 5 -10 19 | f 1 2 3 20 | f 4 5 6 21 | f 7 8 9 22 | f 10 11 12 23 | f 13 14 15 24 | f 16 17 18 25 | -------------------------------------------------------------------------------- /scenes/pa5/veach_mi/meshes/plate1.obj: -------------------------------------------------------------------------------- 1 | v 4 -2.70651 0.25609 2 | v 4 -2.08375 -0.526323 3 | v -4 -2.08375 -0.526323 4 | v -4 -2.08375 -0.526323 5 | v -4 -2.70651 0.25609 6 | v 4 -2.70651 0.25609 7 | f 1 2 3 8 | f 4 5 6 9 | -------------------------------------------------------------------------------- /scenes/pa5/veach_mi/meshes/plate2.obj: -------------------------------------------------------------------------------- 1 | v 4 -3.28825 1.36972 2 | v 4 -2.83856 0.476536 3 | v -4 -2.83856 0.476536 4 | v -4 -2.83856 0.476536 5 | v -4 -3.28825 1.36972 6 | v 4 -3.28825 1.36972 7 | f 1 2 3 8 | f 4 5 6 9 | -------------------------------------------------------------------------------- /scenes/pa5/veach_mi/meshes/plate3.obj: -------------------------------------------------------------------------------- 1 | v 4 -3.73096 2.70046 2 | v 4 -3.43378 1.74564 3 | v -4 -3.43378 1.74564 4 | v -4 -3.43378 1.74564 5 | v -4 -3.73096 2.70046 6 | v 4 -3.73096 2.70046 7 | f 1 2 3 8 | f 4 5 6 9 | -------------------------------------------------------------------------------- /scenes/pa5/veach_mi/meshes/plate4.obj: -------------------------------------------------------------------------------- 1 | v 4 -3.99615 4.0667 2 | v 4 -3.82069 3.08221 3 | v -4 -3.82069 3.08221 4 | v -4 -3.82069 3.08221 5 | v -4 -3.99615 4.0667 6 | v 4 -3.99615 4.0667 7 | f 1 2 3 8 | f 4 5 6 9 | -------------------------------------------------------------------------------- /scenes/pa5/veach_mi/veach_ems.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 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 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | -------------------------------------------------------------------------------- /scenes/pa5/veach_mi/veach_mats.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 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 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | -------------------------------------------------------------------------------- /scenes/pa5/veach_mi/veach_mis.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 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 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | -------------------------------------------------------------------------------- /src/accel.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | This file is part of Nori, a simple educational ray tracer 3 | 4 | Copyright (c) 2015 by Wenzel Jakob 5 | */ 6 | 7 | #include 8 | #include 9 | 10 | NORI_NAMESPACE_BEGIN 11 | 12 | void Accel::addMesh(Mesh *mesh) { 13 | if (m_mesh) 14 | throw NoriException("Accel: only a single mesh is supported!"); 15 | m_mesh = mesh; 16 | m_bbox = m_mesh->getBoundingBox(); 17 | } 18 | 19 | void Accel::build() { 20 | /* Nothing to do here for now */ 21 | } 22 | 23 | bool Accel::rayIntersect(const Ray3f &ray_, Intersection &its, bool shadowRay) const { 24 | bool foundIntersection = false; // Was an intersection found so far? 25 | uint32_t f = (uint32_t) -1; // Triangle index of the closest intersection 26 | 27 | Ray3f ray(ray_); /// Make a copy of the ray (we will need to update its '.maxt' value) 28 | 29 | /* Brute force search through all triangles */ 30 | for (uint32_t idx = 0; idx < m_mesh->getTriangleCount(); ++idx) { 31 | float u, v, t; 32 | if (m_mesh->rayIntersect(idx, ray, u, v, t)) { 33 | /* An intersection was found! Can terminate 34 | immediately if this is a shadow ray query */ 35 | if (shadowRay) 36 | return true; 37 | ray.maxt = its.t = t; 38 | its.uv = Point2f(u, v); 39 | its.mesh = m_mesh; 40 | f = idx; 41 | foundIntersection = true; 42 | } 43 | } 44 | 45 | if (foundIntersection) { 46 | /* At this point, we now know that there is an intersection, 47 | and we know the triangle index of the closest such intersection. 48 | 49 | The following computes a number of additional properties which 50 | characterize the intersection (normals, texture coordinates, etc..) 51 | */ 52 | 53 | /* Find the barycentric coordinates */ 54 | Vector3f bary; 55 | bary << 1-its.uv.sum(), its.uv; 56 | 57 | /* References to all relevant mesh buffers */ 58 | const Mesh *mesh = its.mesh; 59 | const MatrixXf &V = mesh->getVertexPositions(); 60 | const MatrixXf &N = mesh->getVertexNormals(); 61 | const MatrixXf &UV = mesh->getVertexTexCoords(); 62 | const MatrixXu &F = mesh->getIndices(); 63 | 64 | /* Vertex indices of the triangle */ 65 | uint32_t idx0 = F(0, f), idx1 = F(1, f), idx2 = F(2, f); 66 | 67 | Point3f p0 = V.col(idx0), p1 = V.col(idx1), p2 = V.col(idx2); 68 | 69 | /* Compute the intersection positon accurately 70 | using barycentric coordinates */ 71 | its.p = bary.x() * p0 + bary.y() * p1 + bary.z() * p2; 72 | 73 | /* Compute proper texture coordinates if provided by the mesh */ 74 | if (UV.size() > 0) 75 | its.uv = bary.x() * UV.col(idx0) + 76 | bary.y() * UV.col(idx1) + 77 | bary.z() * UV.col(idx2); 78 | 79 | /* Compute the geometry frame */ 80 | its.geoFrame = Frame((p1-p0).cross(p2-p0).normalized()); 81 | 82 | if (N.size() > 0) { 83 | /* Compute the shading frame. Note that for simplicity, 84 | the current implementation doesn't attempt to provide 85 | tangents that are continuous across the surface. That 86 | means that this code will need to be modified to be able 87 | use anisotropic BRDFs, which need tangent continuity */ 88 | 89 | its.shFrame = Frame( 90 | (bary.x() * N.col(idx0) + 91 | bary.y() * N.col(idx1) + 92 | bary.z() * N.col(idx2)).normalized()); 93 | } else { 94 | its.shFrame = its.geoFrame; 95 | } 96 | } 97 | 98 | return foundIntersection; 99 | } 100 | 101 | NORI_NAMESPACE_END 102 | 103 | -------------------------------------------------------------------------------- /src/bitmap.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | This file is part of Nori, a simple educational ray tracer 3 | 4 | Copyright (c) 2015 by Wenzel Jakob 5 | */ 6 | 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | 15 | #define STB_IMAGE_WRITE_IMPLEMENTATION 16 | #include 17 | 18 | NORI_NAMESPACE_BEGIN 19 | 20 | Bitmap::Bitmap(const std::string &filename) { 21 | Imf::InputFile file(filename.c_str()); 22 | const Imf::Header &header = file.header(); 23 | const Imf::ChannelList &channels = header.channels(); 24 | 25 | Imath::Box2i dw = file.header().dataWindow(); 26 | resize(dw.max.y - dw.min.y + 1, dw.max.x - dw.min.x + 1); 27 | 28 | cout << "Reading a " << cols() << "x" << rows() << " OpenEXR file from \"" 29 | << filename << "\"" << endl; 30 | 31 | const char *ch_r = nullptr, *ch_g = nullptr, *ch_b = nullptr; 32 | for (Imf::ChannelList::ConstIterator it = channels.begin(); it != channels.end(); ++it) { 33 | std::string name = toLower(it.name()); 34 | 35 | if (it.channel().xSampling != 1 || it.channel().ySampling != 1) { 36 | /* Sub-sampled layers are not supported */ 37 | continue; 38 | } 39 | 40 | if (!ch_r && (name == "r" || name == "red" || 41 | endsWith(name, ".r") || endsWith(name, ".red"))) { 42 | ch_r = it.name(); 43 | } else if (!ch_g && (name == "g" || name == "green" || 44 | endsWith(name, ".g") || endsWith(name, ".green"))) { 45 | ch_g = it.name(); 46 | } else if (!ch_b && (name == "b" || name == "blue" || 47 | endsWith(name, ".b") || endsWith(name, ".blue"))) { 48 | ch_b = it.name(); 49 | } 50 | } 51 | 52 | if (!ch_r || !ch_g || !ch_b) 53 | throw NoriException("This is not a standard RGB OpenEXR file!"); 54 | 55 | size_t compStride = sizeof(float), 56 | pixelStride = 3 * compStride, 57 | rowStride = pixelStride * cols(); 58 | 59 | char *ptr = reinterpret_cast(data()); 60 | 61 | Imf::FrameBuffer frameBuffer; 62 | frameBuffer.insert(ch_r, Imf::Slice(Imf::FLOAT, ptr, pixelStride, rowStride)); ptr += compStride; 63 | frameBuffer.insert(ch_g, Imf::Slice(Imf::FLOAT, ptr, pixelStride, rowStride)); ptr += compStride; 64 | frameBuffer.insert(ch_b, Imf::Slice(Imf::FLOAT, ptr, pixelStride, rowStride)); 65 | file.setFrameBuffer(frameBuffer); 66 | file.readPixels(dw.min.y, dw.max.y); 67 | } 68 | 69 | void Bitmap::saveEXR(const std::string &filename) { 70 | cout << "Writing a " << cols() << "x" << rows() 71 | << " OpenEXR file to \"" << filename << "\"" << endl; 72 | 73 | std::string path = filename + ".exr"; 74 | 75 | Imf::Header header((int) cols(), (int) rows()); 76 | header.insert("comments", Imf::StringAttribute("Generated by Nori")); 77 | 78 | Imf::ChannelList &channels = header.channels(); 79 | channels.insert("R", Imf::Channel(Imf::FLOAT)); 80 | channels.insert("G", Imf::Channel(Imf::FLOAT)); 81 | channels.insert("B", Imf::Channel(Imf::FLOAT)); 82 | 83 | Imf::FrameBuffer frameBuffer; 84 | size_t compStride = sizeof(float), 85 | pixelStride = 3 * compStride, 86 | rowStride = pixelStride * cols(); 87 | 88 | char *ptr = reinterpret_cast(data()); 89 | frameBuffer.insert("R", Imf::Slice(Imf::FLOAT, ptr, pixelStride, rowStride)); ptr += compStride; 90 | frameBuffer.insert("G", Imf::Slice(Imf::FLOAT, ptr, pixelStride, rowStride)); ptr += compStride; 91 | frameBuffer.insert("B", Imf::Slice(Imf::FLOAT, ptr, pixelStride, rowStride)); 92 | 93 | Imf::OutputFile file(path.c_str(), header); 94 | file.setFrameBuffer(frameBuffer); 95 | file.writePixels((int) rows()); 96 | } 97 | 98 | void Bitmap::savePNG(const std::string &filename) { 99 | cout << "Writing a " << cols() << "x" << rows() 100 | << " PNG file to \"" << filename << "\"" << endl; 101 | 102 | std::string path = filename + ".png"; 103 | 104 | uint8_t *rgb8 = new uint8_t[3 * cols() * rows()]; 105 | uint8_t *dst = rgb8; 106 | for (int i = 0; i < rows(); ++i) { 107 | for (int j = 0; j < cols(); ++j) { 108 | Color3f tonemapped = coeffRef(i, j).toSRGB(); 109 | dst[0] = (uint8_t) clamp(255.f * tonemapped[0], 0.f, 255.f); 110 | dst[1] = (uint8_t) clamp(255.f * tonemapped[1], 0.f, 255.f); 111 | dst[2] = (uint8_t) clamp(255.f * tonemapped[2], 0.f, 255.f); 112 | dst += 3; 113 | } 114 | } 115 | 116 | int ret = stbi_write_png(path.c_str(), (int) cols(), (int) rows(), 3, rgb8, 3 * (int) cols()); 117 | if (ret == 0) { 118 | cout << "Bitmap::savePNG(): Could not save PNG file \"" << path << "%s\"" << endl; 119 | } 120 | 121 | delete[] rgb8; 122 | } 123 | 124 | NORI_NAMESPACE_END 125 | -------------------------------------------------------------------------------- /src/block.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | This file is part of Nori, a simple educational ray tracer 3 | 4 | Copyright (c) 2015 by Wenzel Jakob 5 | */ 6 | 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | 13 | NORI_NAMESPACE_BEGIN 14 | 15 | ImageBlock::ImageBlock(const Vector2i &size, const ReconstructionFilter *filter) 16 | : m_offset(0, 0), m_size(size) { 17 | if (filter) { 18 | /* Tabulate the image reconstruction filter for performance reasons */ 19 | m_filterRadius = filter->getRadius(); 20 | m_borderSize = (int) std::ceil(m_filterRadius - 0.5f); 21 | m_filter = new float[NORI_FILTER_RESOLUTION + 1]; 22 | for (int i=0; ieval(pos); 25 | } 26 | m_filter[NORI_FILTER_RESOLUTION] = 0.0f; 27 | m_lookupFactor = NORI_FILTER_RESOLUTION / m_filterRadius; 28 | int weightSize = (int) std::ceil(2*m_filterRadius) + 1; 29 | m_weightsX = new float[weightSize]; 30 | m_weightsY = new float[weightSize]; 31 | memset(m_weightsX, 0, sizeof(float) * weightSize); 32 | memset(m_weightsY, 0, sizeof(float) * weightSize); 33 | } 34 | 35 | /* Allocate space for pixels and border regions */ 36 | resize(size.y() + 2*m_borderSize, size.x() + 2*m_borderSize); 37 | } 38 | 39 | ImageBlock::~ImageBlock() { 40 | delete[] m_filter; 41 | delete[] m_weightsX; 42 | delete[] m_weightsY; 43 | } 44 | 45 | Bitmap *ImageBlock::toBitmap() const { 46 | Bitmap *result = new Bitmap(m_size); 47 | for (int y=0; ycoeffRef(y, x) = coeff(y + m_borderSize, x + m_borderSize).divideByFilterWeight(); 50 | return result; 51 | } 52 | 53 | void ImageBlock::fromBitmap(const Bitmap &bitmap) { 54 | if (bitmap.cols() != cols() || bitmap.rows() != rows()) 55 | throw NoriException("Invalid bitmap dimensions!"); 56 | 57 | for (int y=0; y= m_numBlocks.array()).any()); 150 | 151 | return true; 152 | } 153 | 154 | NORI_NAMESPACE_END 155 | -------------------------------------------------------------------------------- /src/dielectric.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | This file is part of Nori, a simple educational ray tracer 3 | 4 | Copyright (c) 2015 by Wenzel Jakob 5 | */ 6 | 7 | #include 8 | #include 9 | 10 | NORI_NAMESPACE_BEGIN 11 | 12 | /// Ideal dielectric BSDF 13 | class Dielectric : public BSDF { 14 | public: 15 | Dielectric(const PropertyList &propList) { 16 | /* Interior IOR (default: BK7 borosilicate optical glass) */ 17 | m_intIOR = propList.getFloat("intIOR", 1.5046f); 18 | 19 | /* Exterior IOR (default: air) */ 20 | m_extIOR = propList.getFloat("extIOR", 1.000277f); 21 | } 22 | 23 | Color3f eval(const BSDFQueryRecord &) const { 24 | /* Discrete BRDFs always evaluate to zero in Nori */ 25 | return Color3f(0.0f); 26 | } 27 | 28 | float pdf(const BSDFQueryRecord &) const { 29 | /* Discrete BRDFs always evaluate to zero in Nori */ 30 | return 0.0f; 31 | } 32 | 33 | Color3f sample(BSDFQueryRecord &bRec, const Point2f &sample) const { 34 | throw NoriException("Unimplemented!"); 35 | } 36 | 37 | std::string toString() const { 38 | return tfm::format( 39 | "Dielectric[\n" 40 | " intIOR = %f,\n" 41 | " extIOR = %f\n" 42 | "]", 43 | m_intIOR, m_extIOR); 44 | } 45 | private: 46 | float m_intIOR, m_extIOR; 47 | }; 48 | 49 | NORI_REGISTER_CLASS(Dielectric, "dielectric"); 50 | NORI_NAMESPACE_END 51 | -------------------------------------------------------------------------------- /src/diffuse.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | This file is part of Nori, a simple educational ray tracer 3 | 4 | Copyright (c) 2015 by Wenzel Jakob 5 | */ 6 | 7 | #include 8 | #include 9 | #include 10 | 11 | NORI_NAMESPACE_BEGIN 12 | 13 | /** 14 | * \brief Diffuse / Lambertian BRDF model 15 | */ 16 | class Diffuse : public BSDF { 17 | public: 18 | Diffuse(const PropertyList &propList) { 19 | m_albedo = propList.getColor("albedo", Color3f(0.5f)); 20 | } 21 | 22 | /// Evaluate the BRDF model 23 | Color3f eval(const BSDFQueryRecord &bRec) const { 24 | /* This is a smooth BRDF -- return zero if the measure 25 | is wrong, or when queried for illumination on the backside */ 26 | if (bRec.measure != ESolidAngle 27 | || Frame::cosTheta(bRec.wi) <= 0 28 | || Frame::cosTheta(bRec.wo) <= 0) 29 | return Color3f(0.0f); 30 | 31 | /* The BRDF is simply the albedo / pi */ 32 | return m_albedo * INV_PI; 33 | } 34 | 35 | /// Compute the density of \ref sample() wrt. solid angles 36 | float pdf(const BSDFQueryRecord &bRec) const { 37 | /* This is a smooth BRDF -- return zero if the measure 38 | is wrong, or when queried for illumination on the backside */ 39 | if (bRec.measure != ESolidAngle 40 | || Frame::cosTheta(bRec.wi) <= 0 41 | || Frame::cosTheta(bRec.wo) <= 0) 42 | return 0.0f; 43 | 44 | 45 | /* Importance sampling density wrt. solid angles: 46 | cos(theta) / pi. 47 | 48 | Note that the directions in 'bRec' are in local coordinates, 49 | so Frame::cosTheta() actually just returns the 'z' component. 50 | */ 51 | return INV_PI * Frame::cosTheta(bRec.wo); 52 | } 53 | 54 | /// Draw a a sample from the BRDF model 55 | Color3f sample(BSDFQueryRecord &bRec, const Point2f &sample) const { 56 | if (Frame::cosTheta(bRec.wi) <= 0) 57 | return Color3f(0.0f); 58 | 59 | bRec.measure = ESolidAngle; 60 | 61 | /* Warp a uniformly distributed sample on [0,1]^2 62 | to a direction on a cosine-weighted hemisphere */ 63 | bRec.wo = Warp::squareToCosineHemisphere(sample); 64 | 65 | /* Relative index of refraction: no change */ 66 | bRec.eta = 1.0f; 67 | 68 | /* eval() / pdf() * cos(theta) = albedo. There 69 | is no need to call these functions. */ 70 | return m_albedo; 71 | } 72 | 73 | bool isDiffuse() const { 74 | return true; 75 | } 76 | 77 | /// Return a human-readable summary 78 | std::string toString() const { 79 | return tfm::format( 80 | "Diffuse[\n" 81 | " albedo = %s\n" 82 | "]", m_albedo.toString()); 83 | } 84 | 85 | EClassType getClassType() const { return EBSDF; } 86 | private: 87 | Color3f m_albedo; 88 | }; 89 | 90 | NORI_REGISTER_CLASS(Diffuse, "diffuse"); 91 | NORI_NAMESPACE_END 92 | -------------------------------------------------------------------------------- /src/gui.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | This file is part of Nori, a simple educational ray tracer 3 | 4 | Copyright (c) 2015 by Wenzel Jakob 5 | */ 6 | 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | 16 | NORI_NAMESPACE_BEGIN 17 | 18 | NoriScreen::NoriScreen(const ImageBlock &block) 19 | : nanogui::Screen(nanogui::Vector2i(block.getSize().x(), block.getSize().y() + 36), 20 | "Nori", false), 21 | m_block(block) { 22 | using namespace nanogui; 23 | inc_ref(); 24 | 25 | /* Add some UI elements to adjust the exposure value */ 26 | Widget *panel = new Widget(this); 27 | panel->set_layout(new BoxLayout(Orientation::Horizontal, Alignment::Middle, 10, 10)); 28 | new Label(panel, "Exposure value: ", "sans-bold"); 29 | Slider *slider = new Slider(panel); 30 | slider->set_value(0.5f); 31 | slider->set_fixed_width(150); 32 | slider->set_callback( 33 | [&](float value) { 34 | m_scale = std::pow(2.f, (value - 0.5f) * 20); 35 | } 36 | ); 37 | 38 | panel->set_size(nanogui::Vector2i(block.getSize().x(), block.getSize().y())); 39 | perform_layout(); 40 | 41 | panel->set_position( 42 | nanogui::Vector2i((m_size.x() - panel->size().x()) / 2, block.getSize().y())); 43 | 44 | /* Simple gamma tonemapper as a GLSL shader */ 45 | 46 | m_renderPass = new RenderPass({ this }); 47 | m_renderPass->set_clear_color(0, Color(0.3f, 0.3f, 0.3f, 1.f)); 48 | 49 | m_shader = new Shader( 50 | m_renderPass, 51 | /* An identifying name */ 52 | "Tonemapper", 53 | /* Vertex shader */ 54 | R"(#version 330 55 | uniform ivec2 size; 56 | uniform int borderSize; 57 | 58 | in vec2 position; 59 | out vec2 uv; 60 | void main() { 61 | gl_Position = vec4(position.x * 2 - 1, position.y * 2 - 1, 0.0, 1.0); 62 | 63 | // Crop away image border (due to pixel filter) 64 | vec2 total_size = size + 2 * borderSize; 65 | vec2 scale = size / total_size; 66 | uv = vec2(position.x * scale.x + borderSize / total_size.x, 67 | 1 - (position.y * scale.y + borderSize / total_size.y)); 68 | })", 69 | /* Fragment shader */ 70 | R"(#version 330 71 | uniform sampler2D source; 72 | uniform float scale; 73 | in vec2 uv; 74 | out vec4 out_color; 75 | float toSRGB(float value) { 76 | if (value < 0.0031308) 77 | return 12.92 * value; 78 | return 1.055 * pow(value, 0.41666) - 0.055; 79 | } 80 | void main() { 81 | vec4 color = texture(source, uv); 82 | color *= scale / color.w; 83 | out_color = vec4(toSRGB(color.r), toSRGB(color.g), toSRGB(color.b), 1); 84 | })" 85 | ); 86 | 87 | // Draw 2 triangles 88 | uint32_t indices[3 * 2] = { 89 | 0, 1, 2, 90 | 2, 3, 0 91 | }; 92 | float positions[2 * 4] = { 93 | 0.f, 0.f, 94 | 1.f, 0.f, 95 | 1.f, 1.f, 96 | 0.f, 1.f 97 | }; 98 | 99 | m_shader->set_buffer("indices", VariableType::UInt32, {3*2}, indices); 100 | m_shader->set_buffer("position", VariableType::Float32, {4, 2}, positions); 101 | 102 | const Vector2i &size = m_block.getSize(); 103 | m_shader->set_uniform("size", nanogui::Vector2i(size.x(), size.y())); 104 | m_shader->set_uniform("borderSize", m_block.getBorderSize()); 105 | 106 | // Allocate texture memory for the rendered image 107 | m_texture = new Texture( 108 | Texture::PixelFormat::RGBA, 109 | Texture::ComponentFormat::Float32, 110 | nanogui::Vector2i(size.x() + 2 * m_block.getBorderSize(), 111 | size.y() + 2 * m_block.getBorderSize()), 112 | Texture::InterpolationMode::Nearest, 113 | Texture::InterpolationMode::Nearest); 114 | 115 | draw_all(); 116 | set_visible(true); 117 | } 118 | 119 | 120 | void NoriScreen::draw_contents() { 121 | // Reload the partially rendered image onto the GPU 122 | m_block.lock(); 123 | const Vector2i &size = m_block.getSize(); 124 | m_shader->set_uniform("scale", m_scale); 125 | m_renderPass->resize(framebuffer_size()); 126 | m_renderPass->begin(); 127 | m_renderPass->set_viewport(nanogui::Vector2i(0, 0), 128 | nanogui::Vector2i(m_pixel_ratio * size[0], 129 | m_pixel_ratio * size[1])); 130 | m_texture->upload((uint8_t *) m_block.data()); 131 | m_shader->set_texture("source", m_texture); 132 | m_shader->begin(); 133 | m_shader->draw_array(nanogui::Shader::PrimitiveType::Triangle, 0, 6, true); 134 | m_shader->end(); 135 | m_renderPass->set_viewport(nanogui::Vector2i(0, 0), framebuffer_size()); 136 | m_renderPass->end(); 137 | m_block.unlock(); 138 | } 139 | 140 | NORI_NAMESPACE_END 141 | -------------------------------------------------------------------------------- /src/independent.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | This file is part of Nori, a simple educational ray tracer 3 | 4 | Copyright (c) 2015 by Wenzel Jakob 5 | */ 6 | 7 | #include 8 | #include 9 | #include 10 | 11 | NORI_NAMESPACE_BEGIN 12 | 13 | /** 14 | * Independent sampling - returns independent uniformly distributed 15 | * random numbers on [0, 1)x[0, 1). 16 | * 17 | * This class is essentially just a wrapper around the pcg32 pseudorandom 18 | * number generator. For more details on what sample generators do in 19 | * general, refer to the \ref Sampler class. 20 | */ 21 | class Independent : public Sampler { 22 | public: 23 | Independent(const PropertyList &propList) { 24 | m_sampleCount = (size_t) propList.getInteger("sampleCount", 1); 25 | } 26 | 27 | virtual ~Independent() { } 28 | 29 | std::unique_ptr clone() const { 30 | std::unique_ptr cloned(new Independent()); 31 | cloned->m_sampleCount = m_sampleCount; 32 | cloned->m_random = m_random; 33 | return std::move(cloned); 34 | } 35 | 36 | void prepare(const ImageBlock &block) { 37 | m_random.seed( 38 | block.getOffset().x(), 39 | block.getOffset().y() 40 | ); 41 | } 42 | 43 | void generate() { /* No-op for this sampler */ } 44 | void advance() { /* No-op for this sampler */ } 45 | 46 | float next1D() { 47 | return m_random.nextFloat(); 48 | } 49 | 50 | Point2f next2D() { 51 | return Point2f( 52 | m_random.nextFloat(), 53 | m_random.nextFloat() 54 | ); 55 | } 56 | 57 | std::string toString() const { 58 | return tfm::format("Independent[sampleCount=%i]", m_sampleCount); 59 | } 60 | protected: 61 | Independent() { } 62 | 63 | private: 64 | pcg32 m_random; 65 | }; 66 | 67 | NORI_REGISTER_CLASS(Independent, "independent"); 68 | NORI_NAMESPACE_END 69 | -------------------------------------------------------------------------------- /src/mesh.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | This file is part of Nori, a simple educational ray tracer 3 | 4 | Copyright (c) 2015 by Wenzel Jakob 5 | */ 6 | 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | 14 | NORI_NAMESPACE_BEGIN 15 | 16 | Mesh::Mesh() { } 17 | 18 | Mesh::~Mesh() { 19 | delete m_bsdf; 20 | delete m_emitter; 21 | } 22 | 23 | void Mesh::activate() { 24 | if (!m_bsdf) { 25 | /* If no material was assigned, instantiate a diffuse BRDF */ 26 | m_bsdf = static_cast( 27 | NoriObjectFactory::createInstance("diffuse", PropertyList())); 28 | } 29 | } 30 | 31 | float Mesh::surfaceArea(uint32_t index) const { 32 | uint32_t i0 = m_F(0, index), i1 = m_F(1, index), i2 = m_F(2, index); 33 | 34 | const Point3f p0 = m_V.col(i0), p1 = m_V.col(i1), p2 = m_V.col(i2); 35 | 36 | return 0.5f * Vector3f((p1 - p0).cross(p2 - p0)).norm(); 37 | } 38 | 39 | bool Mesh::rayIntersect(uint32_t index, const Ray3f &ray, float &u, float &v, float &t) const { 40 | uint32_t i0 = m_F(0, index), i1 = m_F(1, index), i2 = m_F(2, index); 41 | const Point3f p0 = m_V.col(i0), p1 = m_V.col(i1), p2 = m_V.col(i2); 42 | 43 | /* Find vectors for two edges sharing v[0] */ 44 | Vector3f edge1 = p1 - p0, edge2 = p2 - p0; 45 | 46 | /* Begin calculating determinant - also used to calculate U parameter */ 47 | Vector3f pvec = ray.d.cross(edge2); 48 | 49 | /* If determinant is near zero, ray lies in plane of triangle */ 50 | float det = edge1.dot(pvec); 51 | 52 | if (det > -1e-8f && det < 1e-8f) 53 | return false; 54 | float inv_det = 1.0f / det; 55 | 56 | /* Calculate distance from v[0] to ray origin */ 57 | Vector3f tvec = ray.o - p0; 58 | 59 | /* Calculate U parameter and test bounds */ 60 | u = tvec.dot(pvec) * inv_det; 61 | if (u < 0.0 || u > 1.0) 62 | return false; 63 | 64 | /* Prepare to test V parameter */ 65 | Vector3f qvec = tvec.cross(edge1); 66 | 67 | /* Calculate V parameter and test bounds */ 68 | v = ray.d.dot(qvec) * inv_det; 69 | if (v < 0.0 || u + v > 1.0) 70 | return false; 71 | 72 | /* Ray intersects triangle -> compute t */ 73 | t = edge2.dot(qvec) * inv_det; 74 | 75 | return t >= ray.mint && t <= ray.maxt; 76 | } 77 | 78 | BoundingBox3f Mesh::getBoundingBox(uint32_t index) const { 79 | BoundingBox3f result(m_V.col(m_F(0, index))); 80 | result.expandBy(m_V.col(m_F(1, index))); 81 | result.expandBy(m_V.col(m_F(2, index))); 82 | return result; 83 | } 84 | 85 | Point3f Mesh::getCentroid(uint32_t index) const { 86 | return (1.0f / 3.0f) * 87 | (m_V.col(m_F(0, index)) + 88 | m_V.col(m_F(1, index)) + 89 | m_V.col(m_F(2, index))); 90 | } 91 | 92 | void Mesh::addChild(NoriObject *obj) { 93 | switch (obj->getClassType()) { 94 | case EBSDF: 95 | if (m_bsdf) 96 | throw NoriException( 97 | "Mesh: tried to register multiple BSDF instances!"); 98 | m_bsdf = static_cast(obj); 99 | break; 100 | 101 | case EEmitter: { 102 | Emitter *emitter = static_cast(obj); 103 | if (m_emitter) 104 | throw NoriException( 105 | "Mesh: tried to register multiple Emitter instances!"); 106 | m_emitter = emitter; 107 | } 108 | break; 109 | 110 | default: 111 | throw NoriException("Mesh::addChild(<%s>) is not supported!", 112 | classTypeName(obj->getClassType())); 113 | } 114 | } 115 | 116 | std::string Mesh::toString() const { 117 | return tfm::format( 118 | "Mesh[\n" 119 | " name = \"%s\",\n" 120 | " vertexCount = %i,\n" 121 | " triangleCount = %i,\n" 122 | " bsdf = %s,\n" 123 | " emitter = %s\n" 124 | "]", 125 | m_name, 126 | m_V.cols(), 127 | m_F.cols(), 128 | m_bsdf ? indent(m_bsdf->toString()) : std::string("null"), 129 | m_emitter ? indent(m_emitter->toString()) : std::string("null") 130 | ); 131 | } 132 | 133 | std::string Intersection::toString() const { 134 | if (!mesh) 135 | return "Intersection[invalid]"; 136 | 137 | return tfm::format( 138 | "Intersection[\n" 139 | " p = %s,\n" 140 | " t = %f,\n" 141 | " uv = %s,\n" 142 | " shFrame = %s,\n" 143 | " geoFrame = %s,\n" 144 | " mesh = %s\n" 145 | "]", 146 | p.toString(), 147 | t, 148 | uv.toString(), 149 | indent(shFrame.toString()), 150 | indent(geoFrame.toString()), 151 | mesh ? mesh->toString() : std::string("null") 152 | ); 153 | } 154 | 155 | NORI_NAMESPACE_END 156 | -------------------------------------------------------------------------------- /src/microfacet.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | This file is part of Nori, a simple educational ray tracer 3 | 4 | Copyright (c) 2015 by Wenzel Jakob 5 | */ 6 | 7 | #include 8 | #include 9 | #include 10 | 11 | NORI_NAMESPACE_BEGIN 12 | 13 | class Microfacet : public BSDF { 14 | public: 15 | Microfacet(const PropertyList &propList) { 16 | /* RMS surface roughness */ 17 | m_alpha = propList.getFloat("alpha", 0.1f); 18 | 19 | /* Interior IOR (default: BK7 borosilicate optical glass) */ 20 | m_intIOR = propList.getFloat("intIOR", 1.5046f); 21 | 22 | /* Exterior IOR (default: air) */ 23 | m_extIOR = propList.getFloat("extIOR", 1.000277f); 24 | 25 | /* Albedo of the diffuse base material (a.k.a "kd") */ 26 | m_kd = propList.getColor("kd", Color3f(0.5f)); 27 | 28 | /* To ensure energy conservation, we must scale the 29 | specular component by 1-kd. 30 | 31 | While that is not a particularly realistic model of what 32 | happens in reality, this will greatly simplify the 33 | implementation. Please see the course staff if you're 34 | interested in implementing a more realistic version 35 | of this BRDF. */ 36 | m_ks = 1 - m_kd.maxCoeff(); 37 | } 38 | 39 | /// Evaluate the BRDF for the given pair of directions 40 | Color3f eval(const BSDFQueryRecord &bRec) const { 41 | throw NoriException("MicrofacetBRDF::eval(): not implemented!"); 42 | } 43 | 44 | /// Evaluate the sampling density of \ref sample() wrt. solid angles 45 | float pdf(const BSDFQueryRecord &bRec) const { 46 | throw NoriException("MicrofacetBRDF::pdf(): not implemented!"); 47 | } 48 | 49 | /// Sample the BRDF 50 | Color3f sample(BSDFQueryRecord &bRec, const Point2f &_sample) const { 51 | throw NoriException("MicrofacetBRDF::sample(): not implemented!"); 52 | 53 | // Note: Once you have implemented the part that computes the scattered 54 | // direction, the last part of this function should simply return the 55 | // BRDF value divided by the solid angle density and multiplied by the 56 | // cosine factor from the reflection equation, i.e. 57 | // return eval(bRec) * Frame::cosTheta(bRec.wo) / pdf(bRec); 58 | } 59 | 60 | bool isDiffuse() const { 61 | /* While microfacet BRDFs are not perfectly diffuse, they can be 62 | handled by sampling techniques for diffuse/non-specular materials, 63 | hence we return true here */ 64 | return true; 65 | } 66 | 67 | std::string toString() const { 68 | return tfm::format( 69 | "Microfacet[\n" 70 | " alpha = %f,\n" 71 | " intIOR = %f,\n" 72 | " extIOR = %f,\n" 73 | " kd = %s,\n" 74 | " ks = %f\n" 75 | "]", 76 | m_alpha, 77 | m_intIOR, 78 | m_extIOR, 79 | m_kd.toString(), 80 | m_ks 81 | ); 82 | } 83 | private: 84 | float m_alpha; 85 | float m_intIOR, m_extIOR; 86 | float m_ks; 87 | Color3f m_kd; 88 | }; 89 | 90 | NORI_REGISTER_CLASS(Microfacet, "microfacet"); 91 | NORI_NAMESPACE_END 92 | -------------------------------------------------------------------------------- /src/mirror.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | This file is part of Nori, a simple educational ray tracer 3 | 4 | Copyright (c) 2015 by Wenzel Jakob 5 | */ 6 | 7 | #include 8 | #include 9 | 10 | NORI_NAMESPACE_BEGIN 11 | 12 | /// Ideal mirror BRDF 13 | class Mirror : public BSDF { 14 | public: 15 | Mirror(const PropertyList &) { } 16 | 17 | Color3f eval(const BSDFQueryRecord &) const { 18 | /* Discrete BRDFs always evaluate to zero in Nori */ 19 | return Color3f(0.0f); 20 | } 21 | 22 | float pdf(const BSDFQueryRecord &) const { 23 | /* Discrete BRDFs always evaluate to zero in Nori */ 24 | return 0.0f; 25 | } 26 | 27 | Color3f sample(BSDFQueryRecord &bRec, const Point2f &) const { 28 | if (Frame::cosTheta(bRec.wi) <= 0) 29 | return Color3f(0.0f); 30 | 31 | // Reflection in local coordinates 32 | bRec.wo = Vector3f( 33 | -bRec.wi.x(), 34 | -bRec.wi.y(), 35 | bRec.wi.z() 36 | ); 37 | bRec.measure = EDiscrete; 38 | 39 | /* Relative index of refraction: no change */ 40 | bRec.eta = 1.0f; 41 | 42 | return Color3f(1.0f); 43 | } 44 | 45 | std::string toString() const { 46 | return "Mirror[]"; 47 | } 48 | }; 49 | 50 | NORI_REGISTER_CLASS(Mirror, "mirror"); 51 | NORI_NAMESPACE_END 52 | -------------------------------------------------------------------------------- /src/obj.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | This file is part of Nori, a simple educational ray tracer 3 | 4 | Copyright (c) 2015 by Wenzel Jakob 5 | */ 6 | 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | 13 | NORI_NAMESPACE_BEGIN 14 | 15 | /** 16 | * \brief Loader for Wavefront OBJ triangle meshes 17 | */ 18 | class WavefrontOBJ : public Mesh { 19 | public: 20 | WavefrontOBJ(const PropertyList &propList) { 21 | typedef std::unordered_map VertexMap; 22 | 23 | filesystem::path filename = 24 | getFileResolver()->resolve(propList.getString("filename")); 25 | 26 | std::ifstream is(filename.str()); 27 | if (is.fail()) 28 | throw NoriException("Unable to open OBJ file \"%s\"!", filename); 29 | Transform trafo = propList.getTransform("toWorld", Transform()); 30 | 31 | cout << "Loading \"" << filename << "\" .. "; 32 | cout.flush(); 33 | Timer timer; 34 | 35 | std::vector positions; 36 | std::vector texcoords; 37 | std::vector normals; 38 | std::vector indices; 39 | std::vector vertices; 40 | VertexMap vertexMap; 41 | 42 | std::string line_str; 43 | while (std::getline(is, line_str)) { 44 | std::istringstream line(line_str); 45 | 46 | std::string prefix; 47 | line >> prefix; 48 | 49 | if (prefix == "v") { 50 | Point3f p; 51 | line >> p.x() >> p.y() >> p.z(); 52 | p = trafo * p; 53 | m_bbox.expandBy(p); 54 | positions.push_back(p); 55 | } else if (prefix == "vt") { 56 | Point2f tc; 57 | line >> tc.x() >> tc.y(); 58 | texcoords.push_back(tc); 59 | } else if (prefix == "vn") { 60 | Normal3f n; 61 | line >> n.x() >> n.y() >> n.z(); 62 | normals.push_back((trafo * n).normalized()); 63 | } else if (prefix == "f") { 64 | std::string v1, v2, v3, v4; 65 | line >> v1 >> v2 >> v3 >> v4; 66 | OBJVertex verts[6]; 67 | int nVertices = 3; 68 | 69 | verts[0] = OBJVertex(v1); 70 | verts[1] = OBJVertex(v2); 71 | verts[2] = OBJVertex(v3); 72 | 73 | if (!v4.empty()) { 74 | /* This is a quad, split into two triangles */ 75 | verts[3] = OBJVertex(v4); 76 | verts[4] = verts[0]; 77 | verts[5] = verts[2]; 78 | nVertices = 6; 79 | } 80 | /* Convert to an indexed vertex list */ 81 | for (int i=0; isecond); 90 | } 91 | } 92 | } 93 | } 94 | 95 | m_F.resize(3, indices.size()/3); 96 | memcpy(m_F.data(), indices.data(), sizeof(uint32_t)*indices.size()); 97 | 98 | m_V.resize(3, vertices.size()); 99 | for (uint32_t i=0; i tokens = tokenize(string, "/", true); 133 | 134 | if (tokens.size() < 1 || tokens.size() > 3) 135 | throw NoriException("Invalid vertex data: \"%s\"", string); 136 | 137 | p = toUInt(tokens[0]); 138 | 139 | if (tokens.size() >= 2 && !tokens[1].empty()) 140 | uv = toUInt(tokens[1]); 141 | 142 | if (tokens.size() >= 3 && !tokens[2].empty()) 143 | n = toUInt(tokens[2]); 144 | } 145 | 146 | inline bool operator==(const OBJVertex &v) const { 147 | return v.p == p && v.n == n && v.uv == uv; 148 | } 149 | }; 150 | 151 | /// Hash function for OBJVertex 152 | struct OBJVertexHash { 153 | std::size_t operator()(const OBJVertex &v) const { 154 | size_t hash = std::hash()(v.p); 155 | hash = hash * 37 + std::hash()(v.uv); 156 | hash = hash * 37 + std::hash()(v.n); 157 | return hash; 158 | } 159 | }; 160 | }; 161 | 162 | NORI_REGISTER_CLASS(WavefrontOBJ, "obj"); 163 | NORI_NAMESPACE_END 164 | -------------------------------------------------------------------------------- /src/object.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | This file is part of Nori, a simple educational ray tracer 3 | 4 | Copyright (c) 2015 by Wenzel Jakob 5 | */ 6 | 7 | #include 8 | 9 | NORI_NAMESPACE_BEGIN 10 | 11 | void NoriObject::addChild(NoriObject *) { 12 | throw NoriException( 13 | "NoriObject::addChild() is not implemented for objects of type '%s'!", 14 | classTypeName(getClassType())); 15 | } 16 | 17 | void NoriObject::activate() { /* Do nothing */ } 18 | void NoriObject::setParent(NoriObject *) { /* Do nothing */ } 19 | 20 | std::map *NoriObjectFactory::m_constructors = nullptr; 21 | 22 | void NoriObjectFactory::registerClass(const std::string &name, const Constructor &constr) { 23 | if (!m_constructors) 24 | m_constructors = new std::map(); 25 | (*m_constructors)[name] = constr; 26 | } 27 | 28 | NORI_NAMESPACE_END 29 | -------------------------------------------------------------------------------- /src/perspective.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | This file is part of Nori, a simple educational ray tracer 3 | 4 | Copyright (c) 2015 by Wenzel Jakob 5 | */ 6 | 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | NORI_NAMESPACE_BEGIN 13 | 14 | /** 15 | * \brief Perspective camera with depth of field 16 | * 17 | * This class implements a simple perspective camera model. It uses an 18 | * infinitesimally small aperture, creating an infinite depth of field. 19 | */ 20 | class PerspectiveCamera : public Camera { 21 | public: 22 | PerspectiveCamera(const PropertyList &propList) { 23 | /* Width and height in pixels. Default: 720p */ 24 | m_outputSize.x() = propList.getInteger("width", 1280); 25 | m_outputSize.y() = propList.getInteger("height", 720); 26 | m_invOutputSize = m_outputSize.cast().cwiseInverse(); 27 | 28 | /* Specifies an optional camera-to-world transformation. Default: none */ 29 | m_cameraToWorld = propList.getTransform("toWorld", Transform()); 30 | 31 | /* Horizontal field of view in degrees */ 32 | m_fov = propList.getFloat("fov", 30.0f); 33 | 34 | /* Near and far clipping planes in world-space units */ 35 | m_nearClip = propList.getFloat("nearClip", 1e-4f); 36 | m_farClip = propList.getFloat("farClip", 1e4f); 37 | 38 | m_rfilter = NULL; 39 | } 40 | 41 | void activate() { 42 | float aspect = m_outputSize.x() / (float) m_outputSize.y(); 43 | 44 | /* Project vectors in camera space onto a plane at z=1: 45 | * 46 | * xProj = cot * x / z 47 | * yProj = cot * y / z 48 | * zProj = (far * (z - near)) / (z * (far-near)) 49 | * The cotangent factor ensures that the field of view is 50 | * mapped to the interval [-1, 1]. 51 | */ 52 | float recip = 1.0f / (m_farClip - m_nearClip), 53 | cot = 1.0f / std::tan(degToRad(m_fov / 2.0f)); 54 | 55 | Eigen::Matrix4f perspective; 56 | perspective << 57 | cot, 0, 0, 0, 58 | 0, cot, 0, 0, 59 | 0, 0, m_farClip * recip, -m_nearClip * m_farClip * recip, 60 | 0, 0, 1, 0; 61 | 62 | /** 63 | * Translation and scaling to shift the clip coordinates into the 64 | * range from zero to one. Also takes the aspect ratio into account. 65 | */ 66 | m_sampleToCamera = Transform( 67 | Eigen::DiagonalMatrix(Vector3f(-0.5f, -0.5f * aspect, 1.0f)) * 68 | Eigen::Translation(-1.0f, -1.0f/aspect, 0.0f) * perspective).inverse(); 69 | 70 | /* If no reconstruction filter was assigned, instantiate a Gaussian filter */ 71 | if (!m_rfilter) 72 | m_rfilter = static_cast( 73 | NoriObjectFactory::createInstance("gaussian", PropertyList())); 74 | } 75 | 76 | Color3f sampleRay(Ray3f &ray, 77 | const Point2f &samplePosition, 78 | const Point2f &apertureSample) const { 79 | /* Compute the corresponding position on the 80 | near plane (in local camera space) */ 81 | Point3f nearP = m_sampleToCamera * Point3f( 82 | samplePosition.x() * m_invOutputSize.x(), 83 | samplePosition.y() * m_invOutputSize.y(), 0.0f); 84 | 85 | /* Turn into a normalized ray direction, and 86 | adjust the ray interval accordingly */ 87 | Vector3f d = nearP.normalized(); 88 | float invZ = 1.0f / d.z(); 89 | 90 | ray.o = m_cameraToWorld * Point3f(0, 0, 0); 91 | ray.d = m_cameraToWorld * d; 92 | ray.mint = m_nearClip * invZ; 93 | ray.maxt = m_farClip * invZ; 94 | ray.update(); 95 | 96 | return Color3f(1.0f); 97 | } 98 | 99 | void addChild(NoriObject *obj) { 100 | switch (obj->getClassType()) { 101 | case EReconstructionFilter: 102 | if (m_rfilter) 103 | throw NoriException("Camera: tried to register multiple reconstruction filters!"); 104 | m_rfilter = static_cast(obj); 105 | break; 106 | 107 | default: 108 | throw NoriException("Camera::addChild(<%s>) is not supported!", 109 | classTypeName(obj->getClassType())); 110 | } 111 | } 112 | 113 | /// Return a human-readable summary 114 | std::string toString() const { 115 | return tfm::format( 116 | "PerspectiveCamera[\n" 117 | " cameraToWorld = %s,\n" 118 | " outputSize = %s,\n" 119 | " fov = %f,\n" 120 | " clip = [%f, %f],\n" 121 | " rfilter = %s\n" 122 | "]", 123 | indent(m_cameraToWorld.toString(), 18), 124 | m_outputSize.toString(), 125 | m_fov, 126 | m_nearClip, 127 | m_farClip, 128 | indent(m_rfilter->toString()) 129 | ); 130 | } 131 | private: 132 | Vector2f m_invOutputSize; 133 | Transform m_sampleToCamera; 134 | Transform m_cameraToWorld; 135 | float m_fov; 136 | float m_nearClip; 137 | float m_farClip; 138 | }; 139 | 140 | NORI_REGISTER_CLASS(PerspectiveCamera, "perspective"); 141 | NORI_NAMESPACE_END 142 | -------------------------------------------------------------------------------- /src/proplist.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | This file is part of Nori, a simple educational ray tracer 3 | 4 | Copyright (c) 2015 by Wenzel Jakob 5 | */ 6 | 7 | #include 8 | 9 | NORI_NAMESPACE_BEGIN 10 | 11 | #define DEFINE_PROPERTY_ACCESSOR(Type, TypeName, XmlName) \ 12 | void PropertyList::set##TypeName(const std::string &name, const Type &value) { \ 13 | if (m_properties.find(name) != m_properties.end()) \ 14 | cerr << "Property \"" << name << "\" was specified multiple times!" << endl; \ 15 | auto &prop = m_properties[name]; \ 16 | prop.value.XmlName##_value = value; \ 17 | prop.type = Property::XmlName##_type; \ 18 | } \ 19 | \ 20 | Type PropertyList::get##TypeName(const std::string &name) const { \ 21 | auto it = m_properties.find(name); \ 22 | if (it == m_properties.end()) \ 23 | throw NoriException("Property '%s' is missing!", name); \ 24 | if (it->second.type != Property::XmlName##_type) \ 25 | throw NoriException("Property '%s' has the wrong type! " \ 26 | "(expected <" #XmlName ">)!", name); \ 27 | return it->second.value.XmlName##_value; \ 28 | } \ 29 | \ 30 | Type PropertyList::get##TypeName(const std::string &name, const Type &defVal) const { \ 31 | auto it = m_properties.find(name); \ 32 | if (it == m_properties.end()) \ 33 | return defVal; \ 34 | if (it->second.type != Property::XmlName##_type) \ 35 | throw NoriException("Property '%s' has the wrong type! " \ 36 | "(expected <" #XmlName ">)!", name); \ 37 | return it->second.value.XmlName##_value; \ 38 | } 39 | 40 | DEFINE_PROPERTY_ACCESSOR(bool, Boolean, boolean) 41 | DEFINE_PROPERTY_ACCESSOR(int, Integer, integer) 42 | DEFINE_PROPERTY_ACCESSOR(float, Float, float) 43 | DEFINE_PROPERTY_ACCESSOR(Color3f, Color, color) 44 | DEFINE_PROPERTY_ACCESSOR(Point3f, Point, point) 45 | DEFINE_PROPERTY_ACCESSOR(Vector3f, Vector, vector) 46 | DEFINE_PROPERTY_ACCESSOR(std::string, String, string) 47 | DEFINE_PROPERTY_ACCESSOR(Transform, Transform, transform) 48 | 49 | NORI_NAMESPACE_END 50 | 51 | -------------------------------------------------------------------------------- /src/rfilter.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | This file is part of Nori, a simple educational ray tracer 3 | 4 | Copyright (c) 2015 by Wenzel Jakob 5 | */ 6 | 7 | #include 8 | 9 | NORI_NAMESPACE_BEGIN 10 | 11 | /** 12 | * Windowed Gaussian filter with configurable extent 13 | * and standard deviation. Often produces pleasing 14 | * results, but may introduce too much blurring. 15 | */ 16 | class GaussianFilter : public ReconstructionFilter { 17 | public: 18 | GaussianFilter(const PropertyList &propList) { 19 | /* Half filter size */ 20 | m_radius = propList.getFloat("radius", 2.0f); 21 | /* Standard deviation of the Gaussian */ 22 | m_stddev = propList.getFloat("stddev", 0.5f); 23 | } 24 | 25 | float eval(float x) const { 26 | float alpha = -1.0f / (2.0f * m_stddev*m_stddev); 27 | return std::max(0.0f, 28 | std::exp(alpha * x * x) - 29 | std::exp(alpha * m_radius * m_radius)); 30 | } 31 | 32 | std::string toString() const { 33 | return tfm::format("GaussianFilter[radius=%f, stddev=%f]", m_radius, m_stddev); 34 | } 35 | protected: 36 | float m_stddev; 37 | }; 38 | 39 | /** 40 | * Separable reconstruction filter by Mitchell and Netravali 41 | * 42 | * D. Mitchell, A. Netravali, Reconstruction filters for computer graphics, 43 | * Proceedings of SIGGRAPH 88, Computer Graphics 22(4), pp. 221-228, 1988. 44 | */ 45 | class MitchellNetravaliFilter : public ReconstructionFilter { 46 | public: 47 | MitchellNetravaliFilter(const PropertyList &propList) { 48 | /* Filter size in pixels */ 49 | m_radius = propList.getFloat("radius", 2.0f); 50 | /* B parameter from the paper */ 51 | m_B = propList.getFloat("B", 1.0f / 3.0f); 52 | /* C parameter from the paper */ 53 | m_C = propList.getFloat("C", 1.0f / 3.0f); 54 | } 55 | 56 | float eval(float x) const { 57 | x = std::abs(2.0f * x / m_radius); 58 | float x2 = x*x, x3 = x2*x; 59 | 60 | if (x < 1) { 61 | return 1.0f/6.0f * ((12-9*m_B-6*m_C)*x3 62 | + (-18+12*m_B+6*m_C) * x2 + (6-2*m_B)); 63 | } else if (x < 2) { 64 | return 1.0f/6.0f * ((-m_B-6*m_C)*x3 + (6*m_B+30*m_C) * x2 65 | + (-12*m_B-48*m_C)*x + (8*m_B + 24*m_C)); 66 | } else { 67 | return 0.0f; 68 | } 69 | } 70 | 71 | std::string toString() const { 72 | return tfm::format("MitchellNetravaliFilter[radius=%f, B=%f, C=%f]", m_radius, m_B, m_C); 73 | } 74 | protected: 75 | float m_B, m_C; 76 | }; 77 | 78 | /// Tent filter 79 | class TentFilter : public ReconstructionFilter { 80 | public: 81 | TentFilter(const PropertyList &) { 82 | m_radius = 1.0f; 83 | } 84 | 85 | float eval(float x) const { 86 | return std::max(0.0f, 1.0f - std::abs(x)); 87 | } 88 | 89 | std::string toString() const { 90 | return "TentFilter[]"; 91 | } 92 | }; 93 | 94 | /// Box filter -- fastest, but prone to aliasing 95 | class BoxFilter : public ReconstructionFilter { 96 | public: 97 | BoxFilter(const PropertyList &) { 98 | m_radius = 0.5f; 99 | } 100 | 101 | float eval(float) const { 102 | return 1.0f; 103 | } 104 | 105 | std::string toString() const { 106 | return "BoxFilter[]"; 107 | } 108 | }; 109 | 110 | NORI_REGISTER_CLASS(GaussianFilter, "gaussian"); 111 | NORI_REGISTER_CLASS(MitchellNetravaliFilter, "mitchell"); 112 | NORI_REGISTER_CLASS(TentFilter, "tent"); 113 | NORI_REGISTER_CLASS(BoxFilter, "box"); 114 | 115 | NORI_NAMESPACE_END 116 | -------------------------------------------------------------------------------- /src/scene.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | This file is part of Nori, a simple educational ray tracer 3 | 4 | Copyright (c) 2015 by Wenzel Jakob 5 | */ 6 | 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | 14 | NORI_NAMESPACE_BEGIN 15 | 16 | Scene::Scene(const PropertyList &) { 17 | m_accel = new Accel(); 18 | } 19 | 20 | Scene::~Scene() { 21 | delete m_accel; 22 | delete m_sampler; 23 | delete m_camera; 24 | delete m_integrator; 25 | } 26 | 27 | void Scene::activate() { 28 | m_accel->build(); 29 | 30 | if (!m_integrator) 31 | throw NoriException("No integrator was specified!"); 32 | if (!m_camera) 33 | throw NoriException("No camera was specified!"); 34 | 35 | if (!m_sampler) { 36 | /* Create a default (independent) sampler */ 37 | m_sampler = static_cast( 38 | NoriObjectFactory::createInstance("independent", PropertyList())); 39 | } 40 | 41 | cout << endl; 42 | cout << "Configuration: " << toString() << endl; 43 | cout << endl; 44 | } 45 | 46 | void Scene::addChild(NoriObject *obj) { 47 | switch (obj->getClassType()) { 48 | case EMesh: { 49 | Mesh *mesh = static_cast(obj); 50 | m_accel->addMesh(mesh); 51 | m_meshes.push_back(mesh); 52 | } 53 | break; 54 | 55 | case EEmitter: { 56 | //Emitter *emitter = static_cast(obj); 57 | /* TBD */ 58 | throw NoriException("Scene::addChild(): You need to implement this for emitters"); 59 | } 60 | break; 61 | 62 | case ESampler: 63 | if (m_sampler) 64 | throw NoriException("There can only be one sampler per scene!"); 65 | m_sampler = static_cast(obj); 66 | break; 67 | 68 | case ECamera: 69 | if (m_camera) 70 | throw NoriException("There can only be one camera per scene!"); 71 | m_camera = static_cast(obj); 72 | break; 73 | 74 | case EIntegrator: 75 | if (m_integrator) 76 | throw NoriException("There can only be one integrator per scene!"); 77 | m_integrator = static_cast(obj); 78 | break; 79 | 80 | default: 81 | throw NoriException("Scene::addChild(<%s>) is not supported!", 82 | classTypeName(obj->getClassType())); 83 | } 84 | } 85 | 86 | std::string Scene::toString() const { 87 | std::string meshes; 88 | for (size_t i=0; itoString(), 2); 90 | if (i + 1 < m_meshes.size()) 91 | meshes += ","; 92 | meshes += "\n"; 93 | } 94 | 95 | return tfm::format( 96 | "Scene[\n" 97 | " integrator = %s,\n" 98 | " sampler = %s\n" 99 | " camera = %s,\n" 100 | " meshes = {\n" 101 | " %s }\n" 102 | "]", 103 | indent(m_integrator->toString()), 104 | indent(m_sampler->toString()), 105 | indent(m_camera->toString()), 106 | indent(meshes, 2) 107 | ); 108 | } 109 | 110 | NORI_REGISTER_CLASS(Scene, "scene"); 111 | NORI_NAMESPACE_END 112 | -------------------------------------------------------------------------------- /src/warp.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | This file is part of Nori, a simple educational ray tracer 3 | 4 | Copyright (c) 2015 by Wenzel Jakob 5 | */ 6 | 7 | #include 8 | #include 9 | #include 10 | 11 | NORI_NAMESPACE_BEGIN 12 | 13 | Point2f Warp::squareToUniformSquare(const Point2f &sample) { 14 | return sample; 15 | } 16 | 17 | float Warp::squareToUniformSquarePdf(const Point2f &sample) { 18 | return ((sample.array() >= 0).all() && (sample.array() <= 1).all()) ? 1.0f : 0.0f; 19 | } 20 | 21 | Point2f Warp::squareToTent(const Point2f &sample) { 22 | throw NoriException("Warp::squareToTent() is not yet implemented!"); 23 | } 24 | 25 | float Warp::squareToTentPdf(const Point2f &p) { 26 | throw NoriException("Warp::squareToTentPdf() is not yet implemented!"); 27 | } 28 | 29 | Point2f Warp::squareToUniformDisk(const Point2f &sample) { 30 | throw NoriException("Warp::squareToUniformDisk() is not yet implemented!"); 31 | } 32 | 33 | float Warp::squareToUniformDiskPdf(const Point2f &p) { 34 | throw NoriException("Warp::squareToUniformDiskPdf() is not yet implemented!"); 35 | } 36 | 37 | Vector3f Warp::squareToUniformSphere(const Point2f &sample) { 38 | throw NoriException("Warp::squareToUniformSphere() is not yet implemented!"); 39 | } 40 | 41 | float Warp::squareToUniformSpherePdf(const Vector3f &v) { 42 | throw NoriException("Warp::squareToUniformSpherePdf() is not yet implemented!"); 43 | } 44 | 45 | Vector3f Warp::squareToUniformHemisphere(const Point2f &sample) { 46 | throw NoriException("Warp::squareToUniformHemisphere() is not yet implemented!"); 47 | } 48 | 49 | float Warp::squareToUniformHemispherePdf(const Vector3f &v) { 50 | throw NoriException("Warp::squareToUniformHemispherePdf() is not yet implemented!"); 51 | } 52 | 53 | Vector3f Warp::squareToCosineHemisphere(const Point2f &sample) { 54 | throw NoriException("Warp::squareToCosineHemisphere() is not yet implemented!"); 55 | } 56 | 57 | float Warp::squareToCosineHemispherePdf(const Vector3f &v) { 58 | throw NoriException("Warp::squareToCosineHemispherePdf() is not yet implemented!"); 59 | } 60 | 61 | Vector3f Warp::squareToBeckmann(const Point2f &sample, float alpha) { 62 | throw NoriException("Warp::squareToBeckmann() is not yet implemented!"); 63 | } 64 | 65 | float Warp::squareToBeckmannPdf(const Vector3f &m, float alpha) { 66 | throw NoriException("Warp::squareToBeckmannPdf() is not yet implemented!"); 67 | } 68 | 69 | NORI_NAMESPACE_END 70 | --------------------------------------------------------------------------------