├── .gitignore ├── LICENSE ├── README.md ├── scenes └── torus │ ├── checker.png │ ├── meshes.serialized │ ├── sunsky.exr │ └── torus.xml └── src ├── .clang-format ├── Tupfile ├── alignedallocator.cpp ├── alignedallocator.h ├── animatedtransform.cpp ├── animatedtransform.h ├── arealight.cpp ├── arealight.h ├── bitmaptexture.h ├── bounds.h ├── bsdf.cpp ├── bsdf.h ├── camera.cpp ├── camera.h ├── chad.cpp ├── chad.h ├── commondef.h ├── constanttexture.h ├── distribution.h ├── dptoptions.h ├── envlight.cpp ├── envlight.h ├── flexception.h ├── gaussian.cpp ├── gaussian.h ├── h2mc.cpp ├── h2mc.h ├── image.cpp ├── image.h ├── lambertian.cpp ├── lambertian.h ├── light.cpp ├── light.h ├── loadserialized.cpp ├── loadserialized.h ├── main.cpp ├── microfacet.h ├── mlt.cpp ├── mlt.h ├── parallel.cpp ├── parallel.h ├── parseobj.cpp ├── parseobj.h ├── parsescene.cpp ├── parsescene.h ├── path.cpp ├── path.h ├── pathtrace.cpp ├── pathtrace.h ├── pcg_extras.hpp ├── pcg_random.hpp ├── phong.cpp ├── phong.h ├── pointlight.cpp ├── pointlight.h ├── progressreporter.h ├── pugiconfig.hpp ├── pugixml.cpp ├── pugixml.hpp ├── quaternion.cpp ├── quaternion.h ├── ray.h ├── roughdielectric.cpp ├── roughdielectric.h ├── sampling.h ├── scene.cpp ├── scene.h ├── shape.cpp ├── shape.h ├── texture.h ├── texturesystem.cpp ├── texturesystem.h ├── timer.h ├── transform.cpp ├── transform.h ├── trianglemesh.cpp ├── trianglemesh.h └── utils.h /.gitignore: -------------------------------------------------------------------------------- 1 | # Compiled Object files 2 | *.slo 3 | *.lo 4 | *.o 5 | *.obj 6 | 7 | # Precompiled Headers 8 | *.gch 9 | *.pch 10 | 11 | # Compiled Dynamic libraries 12 | *.so 13 | *.dylib 14 | *.dll 15 | 16 | # Fortran module files 17 | *.mod 18 | 19 | # Compiled Static libraries 20 | *.lai 21 | *.la 22 | *.a 23 | *.lib 24 | 25 | # Executables 26 | *.exe 27 | *.out 28 | *.app 29 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2016 Tzu-Mao Li 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # dpt 2 | 3 | dpt is a prototypical renderer that implements the algorithm presented in the paper "[Anisotropic Gaussian Mutations for Metropolis Light Transport through Hessian-Hamiltonian Dynamics] (https://people.csail.mit.edu/tzumao/h2mc/)" by Tzu-Mao Li, Jaakko Lehtinen, Ravi Ramamoorthi, Wenzel Jakob, and Frédo Durand. The algorithm utilizes the derivatives of the contribution function of a (bidirectional) path tracer to guide local sampling, hence the name dpt. 4 | 5 | dpt supports a limited form of [mitsuba](http://www.mitsuba-renderer.org/)'s scene format. It supports pinhole camera, three kinds of BSDF (diffuse, phong, roughdielectric), three kinds of emitters (point, area, envmap), trianglemesh shape, and linear motion blur. See scenes/torus for an example scene. 6 | 7 | If you want to understand the algorithm by looking at the source code, a good starting point is to look at mlt.cpp, h2mc.cpp, and path.cpp first. 8 | 9 | dpt uses [tup](http://gittup.org/tup/index.html) as its build system. It depends on several libraries: [Eigen](http://eigen.tuxfamily.org/index.php?title=Main_Page), [OpenImageIO](https://github.com/OpenImageIO/oiio), [embree](https://embree.github.io/), and [zlib](http://www.zlib.net/). It uses [ispc](https://ispc.github.io/ispc.html) to compile the derivative code it generates. It also uses [pugixml](http://pugixml.org/) to parse the scene file and [PCG](http://www.pcg-random.org/) for fast and high-quality random number generation. A large portion of dpt is inspired by [mitsuba](http://www.mitsuba-renderer.org/), [pbrt-v3](https://github.com/mmp/pbrt-v3), and [SmallVCM](https://github.com/SmallVCM/SmallVCM/) 10 | 11 | dpt needs to generate a dynamic library that contains functions for computing the derivatives of the path contribution function. Before you execute dpt, you will need to specify the directory used for reading/writing the dynamic library by setting the environment variable DPT\_LIBPATH (e.g. export DPT\_LIBPATH=/path/to/dpt/src/bin). dpt will search that directory for the dynamic library, and if it does not find it, it will create one. 12 | 13 | The program is only tested on OSX 10.10.5 with clang and Ubuntu 14.04 with gcc. Currently it does not support windows system. 14 | 15 | Please contact Tzu-Mao Li (tzumao at mit.edu) if there are any issues/comments/questions. 16 | 17 | -------------------------------------------------------------------------------- /scenes/torus/checker.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BachiLi/dpt/383531f50f9b4f03b71ccf3ac1118a816c2a79d2/scenes/torus/checker.png -------------------------------------------------------------------------------- /scenes/torus/meshes.serialized: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BachiLi/dpt/383531f50f9b4f03b71ccf3ac1118a816c2a79d2/scenes/torus/meshes.serialized -------------------------------------------------------------------------------- /scenes/torus/sunsky.exr: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BachiLi/dpt/383531f50f9b4f03b71ccf3ac1118a816c2a79d2/scenes/torus/sunsky.exr -------------------------------------------------------------------------------- /scenes/torus/torus.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 | 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 | 142 | -------------------------------------------------------------------------------- /src/.clang-format: -------------------------------------------------------------------------------- 1 | --- 2 | Language: Cpp 3 | # BasedOnStyle: Chromium 4 | AccessModifierOffset: 0 5 | AlignAfterOpenBracket: true 6 | AlignEscapedNewlinesLeft: true 7 | AlignOperands: true 8 | AlignTrailingComments: true 9 | AllowAllParametersOfDeclarationOnNextLine: true 10 | AllowShortBlocksOnASingleLine: false 11 | AllowShortCaseLabelsOnASingleLine: false 12 | AllowShortIfStatementsOnASingleLine: false 13 | AllowShortLoopsOnASingleLine: false 14 | AllowShortFunctionsOnASingleLine: None 15 | AlwaysBreakAfterDefinitionReturnType: false 16 | AlwaysBreakTemplateDeclarations: true 17 | AlwaysBreakBeforeMultilineStrings: true 18 | BreakBeforeBraces: Attach 19 | BreakBeforeBinaryOperators: None 20 | BreakBeforeTernaryOperators: true 21 | BreakConstructorInitializersBeforeComma: false 22 | BinPackParameters: false 23 | BinPackArguments: false 24 | ColumnLimit: 100 25 | ConstructorInitializerAllOnOneLineOrOnePerLine: true 26 | ConstructorInitializerIndentWidth: 4 27 | DerivePointerAlignment: true 28 | ExperimentalAutoDetectBinPacking: false 29 | IndentCaseLabels: true 30 | IndentWrappedFunctionNames: true 31 | IndentFunctionDeclarationAfterType: false 32 | MaxEmptyLinesToKeep: 2 33 | KeepEmptyLinesAtTheStartOfBlocks: false 34 | NamespaceIndentation: Inner 35 | ObjCBlockIndentWidth: 2 36 | ObjCSpaceAfterProperty: false 37 | ObjCSpaceBeforeProtocolList: false 38 | PenaltyBreakBeforeFirstCallParameter: 1 39 | PenaltyBreakComment: 300 40 | PenaltyBreakString: 1000 41 | PenaltyBreakFirstLessLess: 120 42 | PenaltyExcessCharacter: 1000000 43 | PenaltyReturnTypeOnItsOwnLine: 200 44 | PointerAlignment: Left 45 | SpacesBeforeTrailingComments: 2 46 | Cpp11BracedListStyle: true 47 | Standard: Cpp11 48 | IndentWidth: 4 49 | TabWidth: 4 50 | UseTab: Never 51 | BreakBeforeBraces: Attach 52 | SpacesInParentheses: false 53 | SpacesInSquareBrackets: false 54 | SpacesInAngles: false 55 | SpaceInEmptyParentheses: false 56 | SpacesInCStyleCastParentheses: false 57 | SpaceAfterCStyleCast: false 58 | SpacesInContainerLiterals: true 59 | SpaceBeforeAssignmentOperators: true 60 | ContinuationIndentWidth: 4 61 | CommentPragmas: '^ IWYU pragma:' 62 | ForEachMacros: [ foreach, Q_FOREACH, BOOST_FOREACH ] 63 | SpaceBeforeParens: ControlStatements 64 | DisableFormat: false 65 | ... 66 | 67 | -------------------------------------------------------------------------------- /src/Tupfile: -------------------------------------------------------------------------------- 1 | # Change the following to your own path 2 | # Eigen 3.3-beta1 3 | EIGEN_INCLUDE = -I../eigen 4 | # OIIO 1.7 5 | OIIO_INCLUDE = -I../oiio/include 6 | OIIO_LIB = -L../oiio/lib 7 | # EMBREE 2.8 8 | EMBREE_INCLUDE = -I/usr/local/include 9 | EMBREE_LIB = -L/usr/local/lib 10 | TBB_LIB = -L/usr/lib 11 | 12 | CC = gcc 13 | CXX = g++ 14 | INCLUDES = $(EIGEN_INCLUDE) $(OIIO_INCLUDE) $(EMBREE_INCLUDE) 15 | CXXFLAGS = -march=native -std=c++11 -Ofast -Wall -DSINGLE_PRECISION -DNDEBUG 16 | #CXXFLAGS = -march=native -std=c++11 -Ofast -Wall -DSINGLE_PRECISION -g 17 | LIBDIRS = $(OIIO_LIB) $(EMBREE_LIB) $(TBB_LIB) 18 | LIBS = $(LIBDIRS) -lOpenImageIO -lembree -lz -lpthread -lboost_system -ldl -ltbb 19 | 20 | : foreach *.cpp |> $(CXX) $(CXXFLAGS) $(INCLUDES) -c %f -o %o |> objs/%B.o 21 | : objs/*.o |> $(CXX) $(CXXFLAGS) %f $(LIBS) -o %o |> bin/dpt 22 | # use below to chroot (for debug symbols) 23 | #: foreach *.cpp |> ^c^ $(CXX) $(CXXFLAGS) $(INCLUDES) -c %f -o %o |> objs/%B.o 24 | #: objs/*.o |> ^c^ $(CXX) $(CXXFLAGS) %f $(LIBS) -o %o |> bin/dpt 25 | -------------------------------------------------------------------------------- /src/alignedallocator.cpp: -------------------------------------------------------------------------------- 1 | #include "alignedallocator.h" 2 | 3 | #include 4 | 5 | void *AllocAligned(size_t size, size_t alignment) { 6 | void *ptr; 7 | if (posix_memalign(&ptr, alignment, size) != 0) 8 | ptr = nullptr; 9 | return ptr; 10 | } 11 | 12 | void FreeAligned(void *ptr) { 13 | if (!ptr) 14 | return; 15 | free(ptr); 16 | } 17 | -------------------------------------------------------------------------------- /src/alignedallocator.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | void *AllocAligned(std::size_t size, std::size_t alignment); 9 | template 10 | T *AllocAligned(int count) { 11 | return (T *)AllocAligned(count * sizeof(T), Alignment); 12 | } 13 | void FreeAligned(void *); 14 | 15 | // http://blogs.msdn.com/b/vcblog/archive/2008/08/28/the-mallocator.aspx 16 | template 17 | class aligned_allocator { 18 | public: 19 | // The following will be the same for virtually all allocators. 20 | typedef T *pointer; 21 | typedef const T *const_pointer; 22 | typedef T &reference; 23 | typedef const T &const_reference; 24 | typedef T value_type; 25 | typedef std::size_t size_type; 26 | typedef std::ptrdiff_t difference_type; 27 | 28 | T *address(T &r) const { 29 | return &r; 30 | } 31 | 32 | const T *address(const T &s) const { 33 | return &s; 34 | } 35 | 36 | std::size_t max_size() const { 37 | // The following has been carefully written to be independent of 38 | // the definition of size_t and to avoid signed/unsigned warnings. 39 | return (static_cast(0) - static_cast(1)) / sizeof(T); 40 | } 41 | 42 | 43 | // The following must be the same for all allocators. 44 | template 45 | struct rebind { 46 | typedef aligned_allocator other; 47 | }; 48 | 49 | bool operator!=(const aligned_allocator &other) const { 50 | return !(*this == other); 51 | } 52 | 53 | void construct(T *const p, const T &t) const { 54 | void *const pv = static_cast(p); 55 | 56 | new (pv) T(t); 57 | } 58 | 59 | void destroy(T *const p) const { 60 | p->~T(); 61 | } 62 | 63 | // Returns true if and only if storage allocated from *this 64 | // can be deallocated from other, and vice versa. 65 | // Always returns true for stateless allocators. 66 | bool operator==(const aligned_allocator &other) const { 67 | return true; 68 | } 69 | 70 | 71 | // Default constructor, copy constructor, rebinding constructor, and destructor. 72 | // Empty for stateless allocators. 73 | aligned_allocator() { 74 | } 75 | 76 | aligned_allocator(const aligned_allocator &) { 77 | } 78 | 79 | template 80 | aligned_allocator(const aligned_allocator &) { 81 | } 82 | 83 | ~aligned_allocator() { 84 | } 85 | 86 | 87 | // The following will be different for each allocator. 88 | T *allocate(const std::size_t n) const { 89 | // The return value of allocate(0) is unspecified. 90 | // Mallocator returns NULL in order to avoid depending 91 | // on malloc(0)'s implementation-defined behavior 92 | // (the implementation can define malloc(0) to return NULL, 93 | // in which case the bad_alloc check below would fire). 94 | // All allocators can return NULL in this case. 95 | if (n == 0) { 96 | return NULL; 97 | } 98 | 99 | // All allocators should contain an integer overflow check. 100 | // The Standardization Committee recommends that std::length_error 101 | // be thrown in the case of integer overflow. 102 | if (n > max_size()) { 103 | throw std::length_error("aligned_allocator::allocate() - Integer overflow."); 104 | } 105 | 106 | // Mallocator wraps malloc(). 107 | void *const pv = AllocAligned(n); 108 | 109 | // Allocators should throw std::bad_alloc in the case of memory allocation failure. 110 | if (pv == NULL) { 111 | throw std::bad_alloc(); 112 | } 113 | 114 | return static_cast(pv); 115 | } 116 | 117 | void deallocate(T *const p, const std::size_t n) const { 118 | FreeAligned(p); 119 | } 120 | 121 | 122 | // The following will be the same for all allocators that ignore hints. 123 | template 124 | T *allocate(const std::size_t n, const U * /* const hint */) const { 125 | return allocate(n); 126 | } 127 | 128 | 129 | // Allocators are not required to be assignable, so 130 | // all allocators should have a private unimplemented 131 | // assignment operator. Note that this will trigger the 132 | // off-by-default (enabled under /Wall) warning C4626 133 | // "assignment operator could not be generated because a 134 | // base class assignment operator is inaccessible" within 135 | // the STL headers, but that warning is useless. 136 | private: 137 | aligned_allocator &operator=(const aligned_allocator &); 138 | }; 139 | -------------------------------------------------------------------------------- /src/animatedtransform.cpp: -------------------------------------------------------------------------------- 1 | #include "animatedtransform.h" 2 | #include "transform.h" 3 | 4 | int GetAnimatedTransformSerializedSize() { 5 | return 1 + // isMoving 6 | 3 * 2 + // translate[2] 7 | 4 * 2; // rotate[2] 8 | } 9 | 10 | void Decompose(const Matrix4x4 &m, Vector3 *t, Quaternion *r) { 11 | Matrix3x3 A = m.topLeftCorner<3, 3>(); 12 | Eigen::JacobiSVD svd(A, Eigen::ComputeFullU | Eigen::ComputeFullV); 13 | Matrix3x3 U = svd.matrixU(); 14 | Matrix3x3 V = svd.matrixV(); 15 | Matrix3x3 S = svd.singularValues().asDiagonal(); 16 | if (svd.singularValues().prod() < 0) { 17 | S = -S; 18 | U = -U; 19 | } 20 | Matrix3x3 Q = U * V.transpose(); 21 | Matrix3x3 P = V * S * V.transpose(); 22 | if (fabs(P(0, 0) - Float(1.0)) > Float(1e-5) || fabs(P(1, 1) - Float(1.0)) > Float(1e-5) || 23 | fabs(P(2, 2) - Float(1.0)) > Float(1e-5)) { 24 | Error("Scaling in animation"); 25 | } 26 | 27 | Matrix4x4 Q4 = Matrix4x4::Identity(); 28 | Q4.topLeftCorner<3, 3>() = Q; 29 | *r = MakeQuaternion(Q4); 30 | *t = Vector3(m(0, 3), m(1, 3), m(2, 3)); 31 | } 32 | 33 | Matrix4x4 Interpolate(const AnimatedTransform &transform, const Float time) { 34 | if (transform.isMoving == FFALSE) { 35 | Vector3 translate = transform.translate[0]; 36 | Vector4 rotate = transform.rotate[0]; 37 | return Translate(translate) * ToMatrix4x4(rotate); 38 | } else { 39 | Vector3 trans = 40 | (Float(1.0) - time) * transform.translate[0] + time * transform.translate[1]; 41 | Quaternion rot = Slerp(time, transform.rotate[0], transform.rotate[1]); 42 | return Translate(trans) * ToMatrix4x4(rot); 43 | } 44 | } 45 | 46 | ADMatrix4x4 Interpolate(const ADAnimatedTransform &transform, const ADFloat time) { 47 | std::vector ret = CreateCondExprVec(16); 48 | BeginIf(Eq(transform.isMoving, FFALSE), ret); 49 | { 50 | ADMatrix4x4 ret = Translate(transform.translate[0]) * ToMatrix4x4(transform.rotate[0]); 51 | SetCondOutput(ToADFloatVec(ret)); 52 | } 53 | BeginElse(); 54 | { 55 | ADVector3 trans = 56 | (Float(1.0) - time) * transform.translate[0] + time * transform.translate[1]; 57 | ADQuaternion rot = Slerp(time, transform.rotate[0], transform.rotate[1]); 58 | ADMatrix4x4 ret = Translate(trans) * ToMatrix4x4(rot); 59 | SetCondOutput(ToADFloatVec(ret)); 60 | } 61 | EndIf(); 62 | return ToADMatrix4x4(ret); 63 | } 64 | 65 | ADMatrix4x4 ToMatrix4x4(const ADAnimatedTransform &transform) { 66 | return Translate(transform.translate[0]) * ToMatrix4x4(transform.rotate[0]); 67 | } 68 | -------------------------------------------------------------------------------- /src/animatedtransform.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "commondef.h" 4 | #include "quaternion.h" 5 | #include "transform.h" 6 | 7 | int GetAnimatedTransformSerializedSize(); 8 | 9 | void Decompose(const Matrix4x4 &m, Vector3 *t, Quaternion *r); 10 | 11 | template 12 | struct TAnimatedTransform { 13 | FloatType isMoving; 14 | TVector3 translate[2]; 15 | TVector4 rotate[2]; 16 | }; 17 | 18 | template <> 19 | struct TAnimatedTransform { 20 | ADFloat isMoving; 21 | ADVector3 translate[2]; 22 | ADVector4 rotate[2]; 23 | }; 24 | 25 | typedef TAnimatedTransform AnimatedTransform; 26 | typedef TAnimatedTransform ADAnimatedTransform; 27 | 28 | inline Float *Serialize(const AnimatedTransform &transform, Float *buffer) { 29 | buffer = Serialize(transform.isMoving, buffer); 30 | buffer = Serialize(transform.translate[0], buffer); 31 | buffer = Serialize(transform.translate[1], buffer); 32 | buffer = Serialize(transform.rotate[0], buffer); 33 | buffer = Serialize(transform.rotate[1], buffer); 34 | return buffer; 35 | } 36 | 37 | template 38 | const FloatType *Deserialize(const FloatType *buffer, TAnimatedTransform &transform) { 39 | buffer = Deserialize(buffer, transform.isMoving); 40 | buffer = Deserialize(buffer, transform.translate[0]); 41 | buffer = Deserialize(buffer, transform.translate[1]); 42 | buffer = Deserialize(buffer, transform.rotate[0]); 43 | buffer = Deserialize(buffer, transform.rotate[1]); 44 | return buffer; 45 | } 46 | 47 | inline AnimatedTransform MakeAnimatedTransform(const Matrix4x4 &m0, const Matrix4x4 &m1) { 48 | AnimatedTransform ret; 49 | ret.isMoving = BoolToFloat(m0 != m1); 50 | Vector3 translate; 51 | Vector4 rotate; 52 | Decompose(m0, &translate, &rotate); 53 | ret.translate[0] = translate; 54 | ret.rotate[0] = rotate; 55 | if (ret.isMoving == FTRUE) { 56 | Decompose(m1, &translate, &rotate); 57 | ret.translate[1] = translate; 58 | ret.rotate[1] = rotate; 59 | } else { 60 | ret.translate[1] = ret.translate[0]; 61 | ret.rotate[1] = ret.rotate[0]; 62 | } 63 | return ret; 64 | } 65 | 66 | Float *Serialize(const AnimatedTransform &transform, Float *buffer); 67 | 68 | Matrix4x4 Interpolate(const AnimatedTransform &transform, const Float time); 69 | 70 | ADMatrix4x4 Interpolate(const ADAnimatedTransform &transform, const ADFloat time); 71 | ADMatrix4x4 ToMatrix4x4(const ADAnimatedTransform &transform); 72 | 73 | template 74 | TAnimatedTransform Invert(const TAnimatedTransform &transform) { 75 | TAnimatedTransform ret; 76 | ret.isMoving = transform.isMoving; 77 | ret.rotate[0] = TVector4(-transform.rotate[0][0], 78 | -transform.rotate[0][1], 79 | -transform.rotate[0][2], 80 | transform.rotate[0][3]); 81 | ret.rotate[1] = TVector4(-transform.rotate[1][0], 82 | -transform.rotate[1][1], 83 | -transform.rotate[1][2], 84 | transform.rotate[1][3]); 85 | TMatrix4x4 rot0 = ToMatrix4x4(ret.rotate[0]); 86 | TMatrix4x4 rot1 = ToMatrix4x4(ret.rotate[1]); 87 | ret.translate[0] = -XformVector(rot0, transform.translate[0]); 88 | ret.translate[1] = -XformVector(rot1, transform.translate[1]); 89 | return ret; 90 | } 91 | -------------------------------------------------------------------------------- /src/arealight.cpp: -------------------------------------------------------------------------------- 1 | #include "arealight.h" 2 | #include "shape.h" 3 | #include "utils.h" 4 | #include "sampling.h" 5 | 6 | int GetAreaLightSerializedSize() { 7 | return 1 + // type 8 | GetMaxShapeSerializedSize() + // shape 9 | 3; // emission 10 | } 11 | 12 | AreaLight::AreaLight(const Float &samplingWeight, Shape *_shape, const Vector3 &emission) 13 | : Light(samplingWeight), shape(_shape), emission(emission) { 14 | _shape->SetAreaLight(this); 15 | } 16 | 17 | void AreaLight::Serialize(const LightPrimID &lPrimID, Float *buffer) const { 18 | buffer = ::Serialize((Float)LightType::AreaLight, buffer); 19 | shape->Serialize(lPrimID, buffer); 20 | buffer += GetMaxShapeSerializedSize(); 21 | ::Serialize(emission, buffer); 22 | } 23 | 24 | LightPrimID AreaLight::SampleDiscrete(const Float uDiscrete) const { 25 | return shape->Sample(uDiscrete); 26 | } 27 | 28 | bool AreaLight::SampleDirect(const BSphere & /*sceneSphere*/, 29 | const Vector3 &pos, 30 | const Vector3 &normal, 31 | const Vector2 rndParam, 32 | const Float time, 33 | LightPrimID &lPrimID, 34 | Vector3 &dirToLight, 35 | Float &dist, 36 | Vector3 &contrib, 37 | Float &cosAtLight, 38 | Float &directPdf, 39 | Float &emissionPdf) const { 40 | Vector3 posOnLight; 41 | Vector3 normalOnLight; 42 | Float shapePdf; 43 | shape->Sample(rndParam, time, lPrimID, posOnLight, normalOnLight, &shapePdf); 44 | dirToLight = posOnLight - pos; 45 | Float distSq = LengthSquared(dirToLight); 46 | dist = sqrt(distSq); 47 | assert(dist > Float(0.0)); 48 | dirToLight = dirToLight / dist; 49 | cosAtLight = -Dot(dirToLight, normalOnLight); 50 | if (cosAtLight > c_CosEpsilon) { // ignore grazing angle 51 | contrib = (cosAtLight / (distSq * shapePdf)) * emission; 52 | directPdf = shapePdf * distSq / cosAtLight; 53 | emissionPdf = shapePdf * cosAtLight * c_INVPI; 54 | return true; 55 | } else { 56 | return false; 57 | } 58 | } 59 | 60 | void AreaLight::Emission(const BSphere & /*sceneSphere*/, 61 | const Vector3 &dirToLight, 62 | const Vector3 &normalOnLight, 63 | const Float time, 64 | LightPrimID &lPrimID, 65 | Vector3 &emission, 66 | Float &directPdf, 67 | Float &emissionPdf) const { 68 | Float cosAtLight = -Dot(normalOnLight, dirToLight); 69 | if (cosAtLight > Float(0.0)) { 70 | emission = this->emission; 71 | directPdf = shape->SamplePdf(); 72 | emissionPdf = cosAtLight * directPdf * c_INVPI; 73 | } else { 74 | emission = Vector3::Zero(); 75 | directPdf = Float(0.0); 76 | emissionPdf = Float(0.0); 77 | } 78 | } 79 | 80 | void AreaLight::Emit(const BSphere & /*sceneSphere*/, 81 | const Vector2 rndParamPos, 82 | const Vector2 rndParamDir, 83 | const Float time, 84 | LightPrimID &lPrimID, 85 | Ray &ray, 86 | Vector3 &emission, 87 | Float &cosAtLight, 88 | Float &emissionPdf, 89 | Float &directPdf) const { 90 | Vector3 normal; 91 | Float shapePdf; 92 | shape->Sample(rndParamPos, time, lPrimID, ray.org, normal, &shapePdf); 93 | assert(shapePdf > Float(0.0)); 94 | 95 | Vector3 d = SampleCosHemisphere(rndParamDir); 96 | Vector3 b0; 97 | Vector3 b1; 98 | CoordinateSystem(normal, b0, b1); 99 | ray.dir = d[0] * b0 + d[1] * b1 + d[2] * normal; 100 | emission = this->emission * (Float(M_PI) / shapePdf); 101 | cosAtLight = d[2]; 102 | emissionPdf = d[2] * c_INVPI * shapePdf; 103 | directPdf = shapePdf; 104 | } 105 | 106 | template 107 | void _SampleDirectAreaLight(const FloatType *buffer, 108 | const TVector3 &pos, 109 | const TVector3 &normal, 110 | const TVector2 rndParam, 111 | const FloatType time, 112 | const bool isStatic, 113 | TVector3 &dirToLight, 114 | TVector3 &lightContrib, 115 | FloatType &cosAtLight, 116 | FloatType &directPdf, 117 | FloatType &emissionPdf) { 118 | TVector3 posOnLight; 119 | TVector3 normalOnLight; 120 | FloatType shapePdf; 121 | SampleShape(buffer, rndParam, time, isStatic, posOnLight, normalOnLight, shapePdf); 122 | buffer += GetMaxShapeSerializedSize(); 123 | TVector3 emission; 124 | Deserialize(buffer, emission); 125 | 126 | dirToLight = posOnLight - pos; 127 | FloatType distSq = LengthSquared(dirToLight); 128 | FloatType dist = sqrt(distSq); 129 | dirToLight = dirToLight / dist; 130 | cosAtLight = -Dot(dirToLight, normalOnLight); 131 | directPdf = shapePdf * distSq / cosAtLight; 132 | lightContrib = emission / directPdf; 133 | emissionPdf = shapePdf * cosAtLight * c_INVPI; 134 | } 135 | 136 | void SampleDirectAreaLight(const ADFloat *buffer, 137 | const ADBSphere & /*sceneSphere*/, 138 | const ADVector3 &pos, 139 | const ADVector3 &normal, 140 | const ADVector2 rndParam, 141 | const ADFloat time, 142 | const bool isStatic, 143 | ADVector3 &dirToLight, 144 | ADVector3 &lightContrib, 145 | ADFloat &cosAtLight, 146 | ADFloat &directPdf, 147 | ADFloat &emissionPdf) { 148 | _SampleDirectAreaLight(buffer, 149 | pos, 150 | normal, 151 | rndParam, 152 | time, 153 | isStatic, 154 | dirToLight, 155 | lightContrib, 156 | cosAtLight, 157 | directPdf, 158 | emissionPdf); 159 | } 160 | 161 | void EmissionAreaLight(const ADFloat *buffer, 162 | const ADBSphere &sceneSphere, 163 | const ADVector3 &dirToLight, 164 | const ADVector3 &normalOnLight, 165 | const ADFloat time, 166 | ADVector3 &emission, 167 | ADFloat &directPdf, 168 | ADFloat &emissionPdf) { 169 | ADFloat shapePdf; 170 | buffer = SampleShapePdf(buffer, shapePdf); 171 | ADVector3 emission_; 172 | Deserialize(buffer, emission_); 173 | ADFloat cosAtLight = -Dot(normalOnLight, dirToLight); 174 | // Assume cosAtLight > 0 175 | emission = emission_; 176 | directPdf = shapePdf; 177 | emissionPdf = cosAtLight * directPdf * c_INVPI; 178 | } 179 | 180 | void EmitAreaLight(const ADFloat *buffer, 181 | const ADBSphere &sceneSphere, 182 | const ADVector2 rndParamPos, 183 | const ADVector2 rndParamDir, 184 | const ADFloat time, 185 | const bool isStatic, 186 | ADRay &ray, 187 | ADVector3 &emission, 188 | ADFloat &cosAtLight, 189 | ADFloat &emissionPdf, 190 | ADFloat &directPdf) { 191 | ADVector3 normalOnLight; 192 | ADFloat shapePdf; 193 | SampleShape(buffer, rndParamPos, time, isStatic, ray.org, normalOnLight, shapePdf); 194 | buffer += GetMaxShapeSerializedSize(); 195 | ADVector3 emission_; 196 | Deserialize(buffer, emission_); 197 | 198 | ADVector3 d = SampleCosHemisphere(rndParamDir); 199 | ADVector3 b0; 200 | ADVector3 b1; 201 | CoordinateSystem(normalOnLight, b0, b1); 202 | 203 | ray.dir = d[0] * b0 + d[1] * b1 + d[2] * normalOnLight; 204 | emission = emission_ * (Float(M_PI) / shapePdf); 205 | cosAtLight = d[2]; 206 | emissionPdf = d[2] * c_INVPI * shapePdf; 207 | directPdf = shapePdf; 208 | } 209 | -------------------------------------------------------------------------------- /src/arealight.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "light.h" 4 | 5 | struct Shape; 6 | 7 | int GetAreaLightSerializedSize(); 8 | 9 | struct AreaLight : public Light { 10 | AreaLight(const Float &samplingWeight, Shape *shape, const Vector3 &emission); 11 | 12 | LightType GetType() const override { 13 | return LightType::AreaLight; 14 | } 15 | void Serialize(const LightPrimID &lPrimID, Float *buffer) const override; 16 | LightPrimID SampleDiscrete(const Float uDiscrete) const override; 17 | bool SampleDirect(const BSphere &sceneSphere, 18 | const Vector3 &pos, 19 | const Vector3 &normal, 20 | const Vector2 rndParam, 21 | const Float time, 22 | LightPrimID &lPrimID, 23 | Vector3 &dirToLight, 24 | Float &dist, 25 | Vector3 &contrib, 26 | Float &cosAtLight, 27 | Float &directPdf, 28 | Float &emissionPdf) const override; 29 | void Emission(const BSphere &sceneSphere, 30 | const Vector3 &dirToLight, 31 | const Vector3 &normalAtLight, 32 | const Float time, 33 | LightPrimID &lPrimID, 34 | Vector3 &emission, 35 | Float &directPdf, 36 | Float &emissionPdf) const override; 37 | void Emit(const BSphere &sceneSphere, 38 | const Vector2 rndParamPos, 39 | const Vector2 rndParamDir, 40 | const Float time, 41 | LightPrimID &lPrimID, 42 | Ray &ray, 43 | Vector3 &emission, 44 | Float &cosAtLight, 45 | Float &emissionPdf, 46 | Float &directPdf) const override; 47 | bool IsFinite() const override { 48 | return true; 49 | } 50 | bool IsDelta() const override { 51 | return false; 52 | } 53 | 54 | const Shape *shape; 55 | const Vector3 emission; 56 | }; 57 | 58 | void SampleDirectAreaLight(const ADFloat *buffer, 59 | const ADBSphere &sceneSphere, 60 | const ADVector3 &pos, 61 | const ADVector3 &normal, 62 | const ADVector2 rndParam, 63 | const ADFloat time, 64 | const bool isStatic, 65 | ADVector3 &dirToLight, 66 | ADVector3 &lightContrib, 67 | ADFloat &cosAtLight, 68 | ADFloat &directPdf, 69 | ADFloat &emissionPdf); 70 | 71 | void EmissionAreaLight(const ADFloat *buffer, 72 | const ADBSphere &sceneSphere, 73 | const ADVector3 &dirToLight, 74 | const ADVector3 &normalOnLight, 75 | const ADFloat time, 76 | ADVector3 &emission, 77 | ADFloat &directPdf, 78 | ADFloat &emissionPdf); 79 | 80 | void EmitAreaLight(const ADFloat *buffer, 81 | const ADBSphere &sceneSphere, 82 | const ADVector2 rndParamPos, 83 | const ADVector2 rndParamDir, 84 | const ADFloat time, 85 | const bool isStatic, 86 | ADRay &ray, 87 | ADVector3 &emission, 88 | ADFloat &cosAtLight, 89 | ADFloat &emissionPdf, 90 | ADFloat &directPdf); 91 | -------------------------------------------------------------------------------- /src/bitmaptexture.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "texture.h" 4 | #include "texturesystem.h" 5 | #include 6 | #include 7 | #include 8 | 9 | template 10 | class BitmapTexture : public Texture { 11 | public: 12 | BitmapTexture(const std::string &filename, 13 | const Vector2 stScaler = Vector2(Float(1.0), Float(1.0))); 14 | BitmapTexture(const OpenImageIO::ustring &filename, 15 | const Vector2 stScaler = Vector2(Float(1.0), Float(1.0))); 16 | TVector Eval(const Vector2 st) const override; 17 | TVector Avg() const override { 18 | return avg; 19 | } 20 | std::string Name() const override { 21 | return filename.string(); 22 | } 23 | std::shared_ptr> ToTexture1D() const override { 24 | if (nChannels == 1) { 25 | return std::dynamic_pointer_cast>(this->shared_from_this()); 26 | } else { 27 | return std::make_shared>(filename, stScaler); 28 | } 29 | } 30 | std::shared_ptr> ToTexture3D() const override { 31 | if (nChannels == 1) { 32 | return std::make_shared>(filename, stScaler); 33 | } else { 34 | return std::dynamic_pointer_cast>(this->shared_from_this()); 35 | } 36 | } 37 | 38 | private: 39 | TVector ComputeAvg() const; 40 | Float GetGamma() const; 41 | 42 | const OpenImageIO::ustring filename; 43 | const Vector2 stScaler; 44 | const TVector avg; 45 | const Float gamma; 46 | // weird non-constant declaration in oiio... 47 | mutable OpenImageIO::TextureOpt options; 48 | }; 49 | 50 | template 51 | BitmapTexture::BitmapTexture(const std::string &filename, const Vector2 stScaler) 52 | : filename(OpenImageIO::ustring(filename)), 53 | stScaler(stScaler), 54 | avg(ComputeAvg()), 55 | gamma(GetGamma()) { 56 | options.swrap = OpenImageIO::TextureOpt::Wrap::WrapPeriodic; 57 | options.twrap = OpenImageIO::TextureOpt::Wrap::WrapPeriodic; 58 | } 59 | 60 | template 61 | BitmapTexture::BitmapTexture(const OpenImageIO::ustring &filename, 62 | const Vector2 stScaler) 63 | : filename(filename), stScaler(stScaler), avg(ComputeAvg()), gamma(GetGamma()) { 64 | options.swrap = OpenImageIO::TextureOpt::Wrap::WrapPeriodic; 65 | options.twrap = OpenImageIO::TextureOpt::Wrap::WrapPeriodic; 66 | } 67 | 68 | template 69 | TVector BitmapTexture::Eval(const Vector2 st) const { 70 | float scaledS = stScaler[0] * st[0]; 71 | float scaledT = stScaler[1] * st[1]; 72 | std::array result; 73 | if (!TextureSystem::s_TextureSystem->texture(filename, 74 | options, 75 | scaledS, 76 | scaledT, 77 | 0.0f, 78 | 0.0f, 79 | 0.0f, 80 | 0.0f, 81 | nChannels, 82 | result.data())) { 83 | std::cerr << "Filename:" << filename << std::endl; 84 | std::cerr << "Error:" << OpenImageIO::geterror() << std::endl; 85 | Error("Texture lookup error"); 86 | } 87 | TVector fresult; 88 | for (int i = 0; i < nChannels; i++) { 89 | fresult[i] = std::pow(std::max(Float(result[i]), Float(0.0)), gamma); 90 | } 91 | return fresult; 92 | } 93 | 94 | template 95 | TVector BitmapTexture::ComputeAvg() const { 96 | OpenImageIO::ImageBuf img(filename); 97 | OpenImageIO::ImageSpec spec = img.nativespec(); 98 | Float gamma = Float(1.0); 99 | if (spec.format == OpenImageIO::TypeDesc::UINT8) { 100 | gamma = Float(2.2); 101 | } 102 | OpenImageIO::ImageBufAlgo::pow(img, img, gamma); 103 | OpenImageIO::ImageBufAlgo::PixelStats stats; 104 | OpenImageIO::ImageBufAlgo::computePixelStats(stats, img); 105 | TVector avg; 106 | if (img.nchannels() != nChannels) { 107 | if (nChannels == 1) { 108 | avg[0] = Float(0.0); 109 | for (int c = 0; c < img.nchannels(); c++) { 110 | avg[0] += stats.avg[c]; 111 | } 112 | } else if (img.nchannels() == 1) { 113 | for (int c = 0; c < nChannels; c++) { 114 | avg[c] = stats.avg[0]; 115 | } 116 | } else { 117 | Error("Texture dimension mismatch"); 118 | } 119 | } else { 120 | for (int c = 0; c < img.nchannels(); c++) { 121 | avg[c] = stats.avg[c]; 122 | } 123 | } 124 | return avg; 125 | } 126 | 127 | template 128 | Float BitmapTexture::GetGamma() const { 129 | OpenImageIO::ImageBuf img(filename); 130 | OpenImageIO::ImageSpec spec = img.nativespec(); 131 | Float gamma = Float(1.0); 132 | if (spec.format == OpenImageIO::TypeDesc::UINT8) { 133 | gamma = Float(2.2); 134 | } 135 | return gamma; 136 | } 137 | 138 | typedef BitmapTexture<3> BitmapTextureRGB; 139 | -------------------------------------------------------------------------------- /src/bounds.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "commondef.h" 4 | #include "utils.h" 5 | 6 | struct BBox { 7 | BBox() = default; 8 | BBox(const Vector3 &pMin, const Vector3 &pMax) : pMin(pMin), pMax(pMax) { 9 | } 10 | 11 | Vector3 pMin = Vector3(std::numeric_limits::infinity(), 12 | std::numeric_limits::infinity(), 13 | std::numeric_limits::infinity()); 14 | Vector3 pMax = Vector3(-std::numeric_limits::infinity(), 15 | -std::numeric_limits::infinity(), 16 | -std::numeric_limits::infinity()); 17 | }; 18 | 19 | inline BBox Grow(const BBox &bbox, const Vector3 &p) { 20 | return BBox{Vector3(bbox.pMin.cwiseMin(p)), Vector3(bbox.pMax.cwiseMax(p))}; 21 | } 22 | 23 | inline BBox Merge(const BBox &bbox0, const BBox &bbox1) { 24 | return BBox{Vector3(bbox0.pMin.cwiseMin(bbox1.pMin)), Vector3(bbox0.pMax.cwiseMax(bbox1.pMax))}; 25 | } 26 | 27 | struct BSphere { 28 | BSphere() { 29 | } 30 | BSphere(const BBox &bbox) { 31 | center = Float(0.5) * (bbox.pMin + bbox.pMax); 32 | radius = Float(0.5) * Distance(bbox.pMin, bbox.pMax); 33 | } 34 | 35 | Vector3 center; 36 | Float radius; 37 | }; 38 | 39 | struct ADBSphere { 40 | ADVector3 center; 41 | ADFloat radius; 42 | }; 43 | 44 | inline int GetBSphereSerializedSize() { 45 | return 4; // center & radius 46 | } 47 | 48 | inline Float *Serialize(const BSphere &bSphere, Float *buffer) { 49 | buffer = Serialize(bSphere.center, buffer); 50 | buffer = Serialize(bSphere.radius, buffer); 51 | return buffer; 52 | } 53 | 54 | template 55 | const FloatType *Deserialize(const FloatType *buffer, ADBSphere &bSphere) { 56 | buffer = Deserialize(buffer, bSphere.center); 57 | buffer = Deserialize(buffer, bSphere.radius); 58 | return buffer; 59 | } 60 | -------------------------------------------------------------------------------- /src/bsdf.cpp: -------------------------------------------------------------------------------- 1 | #include "bsdf.h" 2 | #include "lambertian.h" 3 | #include "phong.h" 4 | #include "roughdielectric.h" 5 | #include "utils.h" 6 | 7 | int GetMaxBSDFSerializedSize() { 8 | return std::max({GetLambertianSerializedSize(), 9 | GetPhongSerializedSize(), 10 | GetRoughDielectricSerializedSize()}); 11 | } 12 | 13 | const ADFloat *EvaluateBSDF(const bool adjoint, 14 | const ADFloat *buffer, 15 | const ADVector3 &wi, 16 | const ADVector3 &normal, 17 | const ADVector3 &wo, 18 | const ADVector2 st, 19 | ADVector3 &contrib, 20 | ADFloat &cosWo, 21 | ADFloat &pdf, 22 | ADFloat &revPdf) { 23 | ADFloat type; 24 | buffer = Deserialize(buffer, type); 25 | std::vector ret = CreateCondExprVec(6); 26 | BeginIf(Eq(type, (Float)BSDFType::Phong), ret); 27 | { 28 | ADVector3 contrib; 29 | ADFloat cosWo, pdf, revPdf; 30 | EvaluatePhong(adjoint, buffer, wi, normal, wo, st, contrib, cosWo, pdf, revPdf); 31 | SetCondOutput({contrib[0], contrib[1], contrib[2], cosWo, pdf, revPdf}); 32 | } 33 | BeginElseIf(Eq(type, (Float)BSDFType::RoughDielectric)); 34 | { 35 | ADVector3 contrib; 36 | ADFloat cosWo, pdf, revPdf; 37 | EvaluateRoughDielectric(adjoint, buffer, wi, normal, wo, st, contrib, cosWo, pdf, revPdf); 38 | SetCondOutput({contrib[0], contrib[1], contrib[2], cosWo, pdf, revPdf}); 39 | } 40 | BeginElseIf(Eq(type, (Float)BSDFType::Lambertian)); 41 | { 42 | ADVector3 contrib; 43 | ADFloat cosWo, pdf, revPdf; 44 | EvaluateLambertian(adjoint, buffer, wi, normal, wo, st, contrib, cosWo, pdf, revPdf); 45 | SetCondOutput({contrib[0], contrib[1], contrib[2], cosWo, pdf, revPdf}); 46 | } 47 | BeginElse(); 48 | { 49 | SetCondOutput({Const(0.0), 50 | Const(0.0), 51 | Const(0.0), 52 | Const(0.0), 53 | Const(0.0), 54 | Const(0.0)}); 55 | } 56 | EndIf(); 57 | contrib[0] = ret[0]; 58 | contrib[1] = ret[1]; 59 | contrib[2] = ret[2]; 60 | cosWo = ret[3]; 61 | pdf = ret[4]; 62 | revPdf = ret[5]; 63 | buffer += (GetMaxBSDFSerializedSize() - 1); 64 | return buffer; 65 | } 66 | 67 | const ADFloat *SampleBSDF(const bool adjoint, 68 | const ADFloat *buffer, 69 | const ADVector3 &wi, 70 | const ADVector3 &normal, 71 | const ADVector2 st, 72 | const ADVector2 rndParam, 73 | const ADFloat uDiscrete, 74 | const bool fixDiscrete, 75 | ADVector3 &wo, 76 | ADVector3 &contrib, 77 | ADFloat &cosWo, 78 | ADFloat &pdf, 79 | ADFloat &revPdf) { 80 | ADFloat type; 81 | buffer = Deserialize(buffer, type); 82 | std::vector ret = CreateCondExprVec(9); 83 | BeginIf(Eq(type, (Float)BSDFType::Phong), ret); 84 | { 85 | ADVector3 wo; 86 | ADVector3 contrib; 87 | ADFloat cosWo, pdf, revPdf; 88 | SamplePhong(adjoint, 89 | buffer, 90 | wi, 91 | normal, 92 | st, 93 | rndParam, 94 | uDiscrete, 95 | fixDiscrete, 96 | wo, 97 | contrib, 98 | cosWo, 99 | pdf, 100 | revPdf); 101 | SetCondOutput( 102 | {wo[0], wo[1], wo[2], contrib[0], contrib[1], contrib[2], cosWo, pdf, revPdf}); 103 | } 104 | BeginElseIf(Eq(type, (Float)BSDFType::RoughDielectric)); 105 | { 106 | ADVector3 wo; 107 | ADVector3 contrib; 108 | ADFloat cosWo, pdf, revPdf; 109 | SampleRoughDielectric(adjoint, 110 | buffer, 111 | wi, 112 | normal, 113 | st, 114 | rndParam, 115 | uDiscrete, 116 | fixDiscrete, 117 | wo, 118 | contrib, 119 | cosWo, 120 | pdf, 121 | revPdf); 122 | SetCondOutput( 123 | {wo[0], wo[1], wo[2], contrib[0], contrib[1], contrib[2], cosWo, pdf, revPdf}); 124 | } 125 | BeginElseIf(Eq(type, (Float)BSDFType::Lambertian)); 126 | { 127 | ADVector3 wo; 128 | ADVector3 contrib; 129 | ADFloat cosWo, pdf, revPdf; 130 | SampleLambertian(adjoint, 131 | buffer, 132 | wi, 133 | normal, 134 | st, 135 | rndParam, 136 | uDiscrete, 137 | fixDiscrete, 138 | wo, 139 | contrib, 140 | cosWo, 141 | pdf, 142 | revPdf); 143 | SetCondOutput( 144 | {wo[0], wo[1], wo[2], contrib[0], contrib[1], contrib[2], cosWo, pdf, revPdf}); 145 | } 146 | 147 | BeginElse(); 148 | { 149 | SetCondOutput({Const(0.0), 150 | Const(0.0), 151 | Const(0.0), 152 | Const(0.0), 153 | Const(0.0), 154 | Const(0.0), 155 | Const(0.0), 156 | Const(0.0), 157 | Const(0.0)}); 158 | } 159 | EndIf(); 160 | wo[0] = ret[0]; 161 | wo[1] = ret[1]; 162 | wo[2] = ret[2]; 163 | contrib[0] = ret[3]; 164 | contrib[1] = ret[4]; 165 | contrib[2] = ret[5]; 166 | cosWo = ret[6]; 167 | pdf = ret[7]; 168 | revPdf = ret[8]; 169 | buffer += (GetMaxBSDFSerializedSize() - 1); 170 | return buffer; 171 | } 172 | -------------------------------------------------------------------------------- /src/bsdf.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "commondef.h" 4 | 5 | enum class BSDFType { Lambertian, Phong, RoughDielectric }; 6 | 7 | int GetMaxBSDFSerializedSize(); 8 | 9 | struct BSDF { 10 | virtual BSDFType GetType() const = 0; 11 | virtual void Serialize(const Vector2 st, Float *buffer) const = 0; 12 | virtual void Evaluate(const Vector3 &wi, 13 | const Vector3 &normal, 14 | const Vector3 &wo, 15 | const Vector2 st, 16 | Vector3 &contrib, 17 | Float &cosWo, 18 | Float &pdf, 19 | Float &revPdf) const = 0; 20 | virtual void EvaluateAdjoint(const Vector3 &wi, 21 | const Vector3 &normal, 22 | const Vector3 &wo, 23 | const Vector2 st, 24 | Vector3 &contrib, 25 | Float &cosWo, 26 | Float &pdf, 27 | Float &revPdf) const { 28 | Evaluate(wi, normal, wo, st, contrib, cosWo, pdf, revPdf); 29 | } 30 | virtual bool Sample(const Vector3 &wi, 31 | const Vector3 &normal, 32 | const Vector2 st, 33 | const Vector2 rndParam, 34 | const Float uDiscrete, 35 | Vector3 &wo, 36 | Vector3 &contrib, 37 | Float &cosWo, 38 | Float &pdf, 39 | Float &revPdf) const = 0; 40 | virtual bool SampleAdjoint(const Vector3 &wi, 41 | const Vector3 &normal, 42 | const Vector2 st, 43 | const Vector2 rndParam, 44 | const Float uDiscrete, 45 | Vector3 &wo, 46 | Vector3 &contrib, 47 | Float &cosWo, 48 | Float &pdf, 49 | Float &revPdf) const { 50 | return Sample(wi, normal, st, rndParam, uDiscrete, wo, contrib, cosWo, pdf, revPdf); 51 | } 52 | 53 | virtual Float Roughness(const Vector2 st, const Float uDiscrete) const = 0; 54 | }; 55 | 56 | const ADFloat *EvaluateBSDF(const bool adjoint, 57 | const ADFloat *buffer, 58 | const ADVector3 &wi, 59 | const ADVector3 &normal, 60 | const ADVector3 &wo, 61 | const ADVector2 st, 62 | ADVector3 &contrib, 63 | ADFloat &cosWo, 64 | ADFloat &pdf, 65 | ADFloat &revPdf); 66 | 67 | const ADFloat *SampleBSDF(const bool adjoint, 68 | const ADFloat *buffer, 69 | const ADVector3 &wi, 70 | const ADVector3 &normal, 71 | const ADVector2 st, 72 | const ADVector2 rndParam, 73 | const ADFloat uDiscrete, 74 | const bool fixDiscrete, 75 | ADVector3 &wo, 76 | ADVector3 &contrib, 77 | ADFloat &cosWo, 78 | ADFloat &pdf, 79 | ADFloat &revPdf); 80 | -------------------------------------------------------------------------------- /src/camera.cpp: -------------------------------------------------------------------------------- 1 | #include "camera.h" 2 | #include "transform.h" 3 | 4 | int GetCameraSerializedSize() { 5 | return 16 + // sampleToCam 6 | GetAnimatedTransformSerializedSize() + // camToWorld 7 | 1 + // screenPixelCount 8 | 1; // dist 9 | } 10 | 11 | Camera::Camera(const AnimatedTransform &camToWorld, 12 | const Float fov, 13 | const std::shared_ptr film, 14 | const Float nearClip, 15 | const Float farClip) 16 | : camToWorld(camToWorld), 17 | worldToCamera(Invert(camToWorld)), 18 | film(film), 19 | nearClip(nearClip), 20 | farClip(farClip) { 21 | Float aspect = (Float)film->pixelWidth / (Float)film->pixelHeight; 22 | camToSample = Scale(Vector3(-Float(0.5), -Float(0.5) * aspect, Float(1.0))) * 23 | Translate(Vector3(-Float(1.0), -Float(1.0) / aspect, Float(0.0))) * 24 | Perspective(fov, nearClip, farClip); 25 | 26 | sampleToCam = camToSample.inverse(); 27 | dist = film->pixelWidth / (Float(2.0) * tan((fov / Float(2.0)) * (c_PI / Float(180.0)))); 28 | } 29 | 30 | Float *Serialize(const Camera *camera, Float *buffer) { 31 | buffer = Serialize(camera->sampleToCam, buffer); 32 | buffer = Serialize(camera->camToWorld, buffer); 33 | buffer = Serialize(Float(GetPixelHeight(camera) * GetPixelWidth(camera)), buffer); 34 | buffer = Serialize(camera->dist, buffer); 35 | return buffer; 36 | } 37 | 38 | void SamplePrimary(const Camera *camera, 39 | const Vector2 screenPos, 40 | const Float time, 41 | RaySegment &raySeg) { 42 | Ray &ray = raySeg.ray; 43 | ray.org = XformPoint(camera->sampleToCam, Vector3(screenPos[0], screenPos[1], Float(0.0))); 44 | ray.dir = Normalize(ray.org); 45 | Float invZ = inverse(ray.dir[2]); 46 | Matrix4x4 toWorld = Interpolate(camera->camToWorld, time); 47 | ray.org = XformPoint(toWorld, Vector3(Vector3::Zero())); 48 | ray.dir = XformVector(toWorld, ray.dir); 49 | raySeg.minT = camera->nearClip * invZ; 50 | raySeg.maxT = camera->farClip * invZ; 51 | } 52 | 53 | void SamplePrimary(const ADMatrix4x4 &sampleToCam, 54 | const ADAnimatedTransform &camToWorld, 55 | const ADVector2 screenPos, 56 | const ADFloat time, 57 | const bool isStatic, 58 | ADRay &ray) { 59 | ray.org = XformPoint(sampleToCam, ADVector3(screenPos[0], screenPos[1], Const(0.0))); 60 | ray.dir = Normalize(ray.org); 61 | ADMatrix4x4 toWorld = isStatic ? ToMatrix4x4(camToWorld) : Interpolate(camToWorld, time); 62 | ray.org = XformPoint(toWorld, 63 | ADVector3(Const(0.0), Const(0.0), Const(0.0))); 64 | ray.dir = XformVector(toWorld, ray.dir); 65 | } 66 | 67 | bool ProjectPoint(const Camera *camera, const Vector3 &p, const Float time, Vector2 &screenPos) { 68 | Matrix4x4 wtc = Interpolate(camera->worldToCamera, time); 69 | Vector3 camP = XformPoint(wtc, p); 70 | if (camP[2] < camera->nearClip || camP[2] > camera->farClip) { 71 | return false; 72 | } 73 | 74 | Vector3 rasterP = XformPoint(camera->camToSample, camP); 75 | 76 | if (rasterP[0] < Float(0.0) || rasterP[0] > Float(1.0) || rasterP[1] < Float(0.0) || 77 | rasterP[1] > Float(1.0)) { 78 | return false; 79 | } 80 | 81 | screenPos[0] = rasterP[0]; 82 | screenPos[1] = rasterP[1]; 83 | return true; 84 | } 85 | -------------------------------------------------------------------------------- /src/camera.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "commondef.h" 4 | #include "animatedtransform.h" 5 | #include "image.h" 6 | #include "ray.h" 7 | 8 | int GetCameraSerializedSize(); 9 | 10 | struct Camera { 11 | Camera(const AnimatedTransform &camToWorld, 12 | const Float fov, 13 | const std::shared_ptr film, 14 | const Float nearClip, 15 | const Float farClip); 16 | 17 | Matrix4x4 sampleToCam, camToSample; 18 | AnimatedTransform camToWorld, worldToCamera; 19 | 20 | std::shared_ptr film; 21 | Float nearClip, farClip; 22 | Float dist; 23 | }; 24 | 25 | Float *Serialize(const Camera *camera, Float *buffer); 26 | 27 | void SamplePrimary(const Camera *camera, 28 | const Vector2 screenPos, 29 | const Float time, 30 | RaySegment &raySeg); 31 | 32 | void SamplePrimary(const ADMatrix4x4 &sampleToCam, 33 | const ADAnimatedTransform &camToWorld, 34 | const ADVector2 screenPos, 35 | const ADFloat time, 36 | const bool isStatic, 37 | ADRay &ray); 38 | 39 | bool ProjectPoint(const Camera *camera, const Vector3 &p, const Float t, Vector2 &screenPos); 40 | 41 | inline std::shared_ptr GetFilm(const Camera *camera) { 42 | return camera->film; 43 | } 44 | 45 | inline int GetPixelHeight(const Camera *camera) { 46 | return camera->film->pixelHeight; 47 | } 48 | 49 | inline int GetPixelWidth(const Camera *camera) { 50 | return camera->film->pixelWidth; 51 | } 52 | -------------------------------------------------------------------------------- /src/commondef.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "chad.h" 4 | #include "flexception.h" 5 | #include "pcg_random.hpp" 6 | #include "alignedallocator.h" 7 | 8 | #include 9 | #include 10 | 11 | using namespace chad; 12 | 13 | template 14 | using TVector = Eigen::Matrix; 15 | template 16 | using TVector1 = TVector; 17 | template 18 | using TVector2 = TVector; 19 | template 20 | using TVector3 = TVector; 21 | template 22 | using TVector4 = TVector; 23 | template 24 | using TMatrix3x3 = Eigen::Matrix; 25 | template 26 | using TMatrix4x4 = Eigen::Matrix; 27 | #if defined(SINGLE_PRECISION) 28 | using Float = float; 29 | #elif defined(DOUBLE_PRECISION) 30 | using Float = double; 31 | #endif 32 | using Vector1 = TVector1; 33 | using Vector2 = TVector2; 34 | using Vector3 = TVector3; 35 | using Vector4 = TVector4; 36 | using Matrix3x3 = TMatrix3x3; 37 | using Matrix4x4 = TMatrix4x4; 38 | using ADFloat = ExpressionCPtr; 39 | using ADBool = BooleanCPtr; 40 | using ADVector2 = TVector2; 41 | using ADVector3 = TVector3; 42 | using ADVector4 = TVector4; 43 | using ADMatrix3x3 = TMatrix3x3; 44 | using ADMatrix4x4 = TMatrix4x4; 45 | using Vector2i = TVector2; 46 | 47 | using Vector = Eigen::Matrix; 48 | using Matrix = Eigen::Matrix; 49 | 50 | using AlignedStdVector = std::vector>; 51 | 52 | #if defined(SINGLE_PRECISION) 53 | const Float c_IsectEpsilon = Float(1e-3); 54 | const Float c_ShadowEpsilon = Float(1e-3); 55 | #elif defined(DOUBLE_PRECISION) 56 | const Float c_IsectEpsilon = Float(1e-7); 57 | const Float c_ShadowEpsilon = Float(1e-5); 58 | #endif 59 | // Avoid grazing angle artifacts 60 | const Float c_CosEpsilon = Float(1e-4); 61 | 62 | // using RNG = std::mt19937; 63 | using RNG = pcg32_k64_fast; 64 | 65 | const Float FTRUE = Float(1.0); 66 | const Float FFALSE = Float(0.0); 67 | 68 | inline Float BoolToFloat(const bool c) { 69 | return c ? FTRUE : FFALSE; 70 | } 71 | 72 | using PrimID = int; 73 | using ShapeID = int; 74 | using TriIndexID = uint32_t; 75 | 76 | const Float c_PI = Float(3.14159265358979323846); 77 | const Float c_INVPI = Float(1.0) / c_PI; 78 | const Float c_TWOPI = Float(2.0) * c_PI; 79 | const Float c_INVTWOPI = Float(1.0) / c_TWOPI; 80 | const Float c_FOURPI = Float(4.0) * c_PI; 81 | const Float c_INVFOURPI = Float(1.0) / c_FOURPI; 82 | const Float c_PIOVERTWO = Float(0.5) * c_PI; 83 | const Float c_PIOVERFOUR = Float(0.25) * c_PI; 84 | 85 | #if defined(__APPLE__) && defined(__MACH__) 86 | // clang on MacOS does not support thread_local keyword... 87 | #define thread_local __thread 88 | #endif 89 | -------------------------------------------------------------------------------- /src/constanttexture.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "texture.h" 4 | #include "utils.h" 5 | 6 | template 7 | class ConstantTexture : public Texture { 8 | public: 9 | ConstantTexture(const TVector &val) : val(val) { 10 | } 11 | TVector Eval(const Vector2 /*st*/) const override { 12 | return val; 13 | } 14 | TVector Avg() const override { 15 | return val; 16 | } 17 | std::string Name() const override { 18 | return "Constant"; 19 | } 20 | std::shared_ptr> ToTexture1D() const override { 21 | if (nChannels == 1) { 22 | return std::dynamic_pointer_cast>(this->shared_from_this()); 23 | } else if (nChannels == 3) { 24 | return std::make_shared>(Vector1(::Avg(val))); 25 | } else { 26 | Error("unsupported texture dimension"); 27 | } 28 | } 29 | std::shared_ptr> ToTexture3D() const override { 30 | if (nChannels == 1) { 31 | return std::make_shared>(Vector3(val[0], val[0], val[0])); 32 | } else if (nChannels == 3) { 33 | return std::dynamic_pointer_cast>(this->shared_from_this()); 34 | } else { 35 | Error("unsupported texture dimension"); 36 | } 37 | } 38 | 39 | private: 40 | TVector val; 41 | }; 42 | 43 | using ConstantTexture1D = ConstantTexture<1>; 44 | using ConstantTexture3D = ConstantTexture<3>; 45 | using ConstantTextureRGB = ConstantTexture<3>; 46 | -------------------------------------------------------------------------------- /src/distribution.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "commondef.h" 4 | #include "utils.h" 5 | #include 6 | 7 | // From https://github.com/mmp/pbrt-v3/blob/master/src/core/sampling.h 8 | struct PiecewiseConstant1D { 9 | PiecewiseConstant1D(const Float *f, int n) { 10 | count = n; 11 | func = new Float[n]; 12 | memcpy(func, f, n * sizeof(Float)); 13 | cdf = new Float[n + 1]; 14 | cdf[0] = 0.; 15 | for (int i = 1; i < count + 1; ++i) 16 | cdf[i] = cdf[i - 1] + func[i - 1] / n; 17 | 18 | funcInt = cdf[count]; 19 | if (funcInt == 0.f) { 20 | for (int i = 1; i < n + 1; ++i) 21 | cdf[i] = Float(i) / Float(n); 22 | } else { 23 | for (int i = 1; i < n + 1; ++i) 24 | cdf[i] /= funcInt; 25 | } 26 | } 27 | ~PiecewiseConstant1D() { 28 | delete[] func; 29 | delete[] cdf; 30 | } 31 | Float SampleContinuous(const Float u, Float *pdf, int *off = nullptr) const { 32 | Float *ptr = std::upper_bound(cdf, cdf + count + 1, u); 33 | int offset = Clamp(int(ptr - cdf - 1), 0, count - 1); 34 | if (off) 35 | *off = offset; 36 | 37 | Float du = (u - cdf[offset]) / (cdf[offset + 1] - cdf[offset]); 38 | 39 | if (pdf) 40 | *pdf = func[offset] / funcInt; 41 | 42 | return (offset + du) / count; 43 | } 44 | int SampleDiscrete(const Float u, Float *pdf) const { 45 | Float *ptr = std::upper_bound(cdf, cdf + count + 1, u); 46 | int offset = Clamp(int(ptr - cdf - 1), 0, count - 1); 47 | if (pdf != nullptr) 48 | *pdf = func[offset] / (funcInt * count); 49 | return offset; 50 | } 51 | 52 | Float Pmf(int offset) const { 53 | return func[offset] / (funcInt * count); 54 | } 55 | 56 | Float GetNormalization() const { 57 | return funcInt * count; 58 | } 59 | 60 | Float *func, *cdf; 61 | Float funcInt; 62 | int count; 63 | }; 64 | -------------------------------------------------------------------------------- /src/dptoptions.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "commondef.h" 4 | 5 | #include 6 | 7 | struct DptOptions { 8 | std::string integrator = "mc"; // MC or MCMC 9 | bool bidirectional = true; 10 | int spp = 16; 11 | int numInitSamples = 100000; 12 | Float largeStepProb = Float(0.3); 13 | int minDepth = -1; 14 | int maxDepth = -1; 15 | int directSpp = 16; 16 | bool h2mc = true; 17 | Float perturbStdDev = Float(0.01); 18 | Float roughnessThreshold = Float(0.03); 19 | Float lensPerturbProb = Float(0.3); 20 | Float lensPerturbStdDev = Float(0.01); 21 | int numChains = 1024; 22 | int seedOffset = 0; 23 | int reportIntervalSpp = 0; 24 | bool useLightCoordinateSampling = true; 25 | Float discreteStdDev = Float(0.01); 26 | bool largeStepMultiplexed = true; 27 | Float uniformMixingProbability = Float(0.2); 28 | }; 29 | 30 | inline std::string GetLibPath() { 31 | return std::string(getenv("DPT_LIBPATH")); 32 | } 33 | -------------------------------------------------------------------------------- /src/envlight.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "light.h" 4 | #include "animatedtransform.h" 5 | 6 | struct Image3; 7 | struct PiecewiseConstant2D; 8 | 9 | int GetEnvLightSerializedSize(); 10 | 11 | struct EnvmapSampleInfo { 12 | std::vector cdfRows; 13 | std::vector cdfCols; 14 | std::vector rowWeights; 15 | Float normalization; 16 | Vector2 pixelSize; 17 | }; 18 | 19 | struct EnvLight : public Light { 20 | EnvLight(const Float &samplingWeight, 21 | const AnimatedTransform &toWorld, 22 | const std::string &filename); 23 | 24 | LightType GetType() const override { 25 | return LightType::EnvLight; 26 | } 27 | void Serialize(const LightPrimID &lPrimID, Float *buffer) const override; 28 | bool SampleDirect(const BSphere &sceneSphere, 29 | const Vector3 &pos, 30 | const Vector3 &normal, 31 | const Vector2 rndParam, 32 | const Float time, 33 | LightPrimID &lPrimID, 34 | Vector3 &dirToLight, 35 | Float &dist, 36 | Vector3 &contrib, 37 | Float &cosAtLight, 38 | Float &directPdf, 39 | Float &emissionPdf) const override; 40 | void Emission(const BSphere &sceneSphere, 41 | const Vector3 &dirToLight, 42 | const Vector3 &normalOnLight, 43 | const Float time, 44 | LightPrimID &lPrimID, 45 | Vector3 &emission, 46 | Float &directPdf, 47 | Float &emissionPdf) const override; 48 | void Emit(const BSphere &sceneSphere, 49 | const Vector2 rndParamPos, 50 | const Vector2 rndParamDir, 51 | const Float time, 52 | LightPrimID &lPrimID, 53 | Ray &ray, 54 | Vector3 &emission, 55 | Float &cosAtLight, 56 | Float &emissionPdf, 57 | Float &directPdf) const override; 58 | bool IsFinite() const override { 59 | return false; 60 | } 61 | bool IsDelta() const override { 62 | return false; 63 | } 64 | 65 | const AnimatedTransform toWorld; 66 | const AnimatedTransform toLight; 67 | const std::unique_ptr image; 68 | const std::unique_ptr sampleInfo; 69 | }; 70 | 71 | void SampleDirectEnvLight(const ADFloat *buffer, 72 | const ADBSphere &sceneSphere, 73 | const ADVector3 &pos, 74 | const ADVector3 &normal, 75 | const ADVector2 rndParam, 76 | const ADFloat time, 77 | const bool isStatic, 78 | ADVector3 &dirToLight, 79 | ADVector3 &lightContrib, 80 | ADFloat &cosAtLight, 81 | ADFloat &directPdf, 82 | ADFloat &emissionPdf); 83 | 84 | void EmissionEnvLight(const ADFloat *buffer, 85 | const ADBSphere &sceneSphere, 86 | const ADVector3 &dirToLight, 87 | const ADVector3 &normalOnLight, 88 | const ADFloat time, 89 | ADVector3 &emission, 90 | ADFloat &directPdf, 91 | ADFloat &emissionPdf); 92 | 93 | void EmitEnvLight(const ADFloat *buffer, 94 | const ADBSphere &sceneSphere, 95 | const ADVector2 rndParamPos, 96 | const ADVector2 rndParamDir, 97 | const ADFloat time, 98 | const bool isStatic, 99 | ADRay &ray, 100 | ADVector3 &emission, 101 | ADFloat &cosAtLight, 102 | ADFloat &emissionPdf, 103 | ADFloat &directPdf); 104 | -------------------------------------------------------------------------------- /src/flexception.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | // http://stackoverflow.com/questions/348833/how-to-know-the-exact-line-of-code-where-where-an-exception-has-been-caused 7 | class fl_exception : public std::runtime_error { 8 | std::string msg; 9 | 10 | public: 11 | fl_exception(const std::string &arg, const char *file, int line) : std::runtime_error(arg) { 12 | std::ostringstream o; 13 | o << file << ":" << line << ": " << arg; 14 | msg = o.str(); 15 | } 16 | ~fl_exception() throw() { 17 | } 18 | const char *what() const throw() { 19 | return msg.c_str(); 20 | } 21 | }; 22 | 23 | #define Error(arg) throw fl_exception(arg, __FILE__, __LINE__); 24 | -------------------------------------------------------------------------------- /src/gaussian.cpp: -------------------------------------------------------------------------------- 1 | #include "gaussian.h" 2 | 3 | void IsotropicGaussian(const int dim, const Float sigma, Gaussian &gaussian) { 4 | auto vsigma = Vector::Constant(dim, sigma); 5 | auto invSigmaSq = vsigma.cwiseProduct(vsigma).cwiseInverse(); 6 | gaussian.mean = Vector::Zero(dim); 7 | gaussian.covL = vsigma.asDiagonal(); 8 | gaussian.invCov = invSigmaSq.asDiagonal(); 9 | gaussian.logDet = Float(0.0); 10 | for (int i = 0; i < dim; i++) { 11 | gaussian.logDet += log(invSigmaSq[i]); 12 | } 13 | } 14 | 15 | Float GaussianLogPdf(const Vector &offset, const Gaussian &gaussian) { 16 | assert(gaussian.mean.size() == offset.size()); 17 | auto d = offset - gaussian.mean; 18 | Float logPdf = gaussian.mean.size() * (-Float(0.5) * log(Float(2.0 * M_PI))); 19 | logPdf += Float(0.5) * gaussian.logDet; 20 | logPdf -= Float(0.5) * (d.transpose() * (gaussian.invCov * d))[0]; 21 | return logPdf; 22 | } 23 | 24 | void GenerateSample(const Gaussian &gaussian, Vector &x, RNG &rng) { 25 | if (gaussian.mean.size() != x.size()) { 26 | std::cerr << "gaussian.mean.size():" << gaussian.mean.size() << std::endl; 27 | std::cerr << "x.size():" << x.size() << std::endl; 28 | } 29 | assert(gaussian.mean.size() == x.size()); 30 | std::normal_distribution normDist(Float(0.0), Float(1.0)); 31 | for (int i = 0; i < x.size(); i++) { 32 | x[i] = normDist(rng); 33 | } 34 | x = gaussian.covL * x + gaussian.mean; 35 | } 36 | -------------------------------------------------------------------------------- /src/gaussian.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "commondef.h" 4 | #include 5 | 6 | struct Gaussian { 7 | Matrix covL; 8 | Matrix invCov; 9 | Vector mean; 10 | Float logDet; 11 | }; 12 | 13 | inline int GetDimension(const Gaussian &gaussian) { 14 | return gaussian.mean.size(); 15 | } 16 | 17 | void IsotropicGaussian(const int dim, const Float sigma, Gaussian &gaussian); 18 | Float GaussianLogPdf(const Vector &offset, const Gaussian &gaussian); 19 | void GenerateSample(const Gaussian &gaussian, Vector &x, RNG &rng); 20 | -------------------------------------------------------------------------------- /src/h2mc.cpp: -------------------------------------------------------------------------------- 1 | #include "h2mc.h" 2 | 3 | template 4 | void ComputeGaussian(const H2MCParam ¶m, 5 | const Eigen::Matrix &grad, 6 | const Eigen::Matrix &hess, 7 | const Eigen::Matrix &invSigmaSq, 8 | Gaussian &gaussian) { 9 | Eigen::SelfAdjointEigenSolver> eigenSolver; 10 | eigenSolver.compute(hess); 11 | const auto &hEigenvector = eigenSolver.eigenvectors(); 12 | const auto &hEigenvalues = eigenSolver.eigenvalues(); 13 | Eigen::Matrix eigenBuff; 14 | Eigen::Matrix offsetBuff; 15 | Eigen::Matrix postInvCovEigenvalues; 16 | int dimension = dim == -1 ? grad.size() : dim; 17 | eigenBuff.resize(dimension); 18 | offsetBuff.resize(dimension); 19 | postInvCovEigenvalues.resize(dimension); 20 | 21 | for (int i = 0; i < dimension; i++) { 22 | if (fabs(hEigenvalues(i)) > Float(1e-10)) { 23 | eigenBuff(i) = 1.0 / fabs(hEigenvalues(i)); 24 | } else { 25 | eigenBuff(i) = 0.0; 26 | } 27 | } 28 | 29 | offsetBuff.noalias() = eigenBuff.asDiagonal() * (hEigenvector.transpose() * grad); 30 | for (int i = 0; i < dimension; i++) { 31 | Float s2 = Float(1.0); 32 | Float o = Float(0.0); 33 | if (fabs(hEigenvalues(i)) > Float(1e-10)) { 34 | o = offsetBuff(i); 35 | if (hEigenvalues(i) > 0.0) { 36 | s2 = param.posScaleFactor; // Float(0.5) * (exp(c_L) - exp(-c_L)) ^ 2; 37 | o *= param.posOffsetFactor; //(Float(0.5) * (exp(c_L) + exp(-c_L)) - Float(1.0)); 38 | } else { //<= 0.0 39 | s2 = param.negScaleFactor; // sin(c_L) ^ 2; 40 | o *= param.negOffsetFactor; //-(cos(c_L) - Float(1.0)); 41 | } 42 | } else { 43 | s2 = param.L * param.L; 44 | o = Float(0.5) * offsetBuff(i) * param.L * param.L; 45 | } 46 | eigenBuff(i) *= (s2); 47 | if (eigenBuff(i) > Float(1e-10)) { 48 | eigenBuff(i) = Float(1.0) / eigenBuff(i); 49 | } else { 50 | eigenBuff(i) = Float(0.0); 51 | } 52 | offsetBuff(i) = o; 53 | } 54 | 55 | postInvCovEigenvalues = eigenBuff.array() + invSigmaSq.array(); 56 | gaussian.invCov.noalias() = 57 | hEigenvector * postInvCovEigenvalues.asDiagonal() * hEigenvector.transpose(); 58 | gaussian.mean.noalias() = 59 | hEigenvector * (eigenBuff.cwiseQuotient(postInvCovEigenvalues).asDiagonal() * offsetBuff); 60 | gaussian.covL.noalias() = 61 | hEigenvector * postInvCovEigenvalues.cwiseInverse().cwiseSqrt().asDiagonal(); 62 | gaussian.logDet = Float(0.0); 63 | for (int i = 0; i < dim; i++) { 64 | gaussian.logDet += log(postInvCovEigenvalues[i]); 65 | } 66 | } 67 | 68 | void ComputeGaussian(const H2MCParam ¶m, 69 | const Float sc, 70 | const AlignedStdVector &vGrad, 71 | const AlignedStdVector &vHess, 72 | Gaussian &gaussian) { 73 | int dim = (int)vGrad.size(); 74 | Eigen::Map grad(&vGrad[0], dim); 75 | Eigen::Map hess(&vHess[0], dim, dim); 76 | 77 | Vector sigma = Vector::Constant(dim, param.sigma); 78 | Float sigmaMax = sigma.maxCoeff(); 79 | auto sigmaSq = sigma.cwiseProduct(sigma); 80 | Vector invSigmaSq = sigmaSq.cwiseInverse(); 81 | if (sc <= Float(1e-15) || hess.norm() < Float(0.5) / (sigmaMax * sigmaMax)) { 82 | gaussian.mean = Vector::Zero(dim); 83 | gaussian.covL = sigma.asDiagonal(); 84 | gaussian.invCov = invSigmaSq.asDiagonal(); 85 | gaussian.logDet = Float(0.0); 86 | for (int i = 0; i < dim; i++) { 87 | gaussian.logDet += log(invSigmaSq[i]); 88 | } 89 | } else { 90 | switch (dim) { 91 | case 2: { 92 | ComputeGaussian<2>(param, grad, hess, invSigmaSq, gaussian); 93 | break; 94 | } 95 | case 3: { 96 | ComputeGaussian<3>(param, grad, hess, invSigmaSq, gaussian); 97 | break; 98 | } 99 | case 4: { 100 | ComputeGaussian<4>(param, grad, hess, invSigmaSq, gaussian); 101 | break; 102 | } 103 | case 5: { 104 | ComputeGaussian<5>(param, grad, hess, invSigmaSq, gaussian); 105 | break; 106 | } 107 | case 6: { 108 | ComputeGaussian<6>(param, grad, hess, invSigmaSq, gaussian); 109 | break; 110 | } 111 | case 7: { 112 | ComputeGaussian<7>(param, grad, hess, invSigmaSq, gaussian); 113 | break; 114 | } 115 | case 8: { 116 | ComputeGaussian<8>(param, grad, hess, invSigmaSq, gaussian); 117 | break; 118 | } 119 | case 9: { 120 | ComputeGaussian<9>(param, grad, hess, invSigmaSq, gaussian); 121 | break; 122 | } 123 | case 10: { 124 | ComputeGaussian<10>(param, grad, hess, invSigmaSq, gaussian); 125 | break; 126 | } 127 | case 11: { 128 | ComputeGaussian<11>(param, grad, hess, invSigmaSq, gaussian); 129 | break; 130 | } 131 | case 12: { 132 | ComputeGaussian<12>(param, grad, hess, invSigmaSq, gaussian); 133 | break; 134 | } 135 | default: { ComputeGaussian<-1>(param, grad, hess, invSigmaSq, gaussian); } 136 | } 137 | } 138 | } 139 | -------------------------------------------------------------------------------- /src/h2mc.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "commondef.h" 4 | #include "gaussian.h" 5 | #include "alignedallocator.h" 6 | 7 | #include 8 | 9 | struct H2MCParam { 10 | H2MCParam(const Float sigma = 0.01, const Float L = Float(M_PI / 2.0)) : sigma(sigma), L(L) { 11 | posScaleFactor = Float(0.5) * (exp(L) - exp(-L)) * Float(0.5) * (exp(L) - exp(-L)); 12 | posOffsetFactor = Float(0.5) * (exp(L) + exp(-L) - Float(1.0)); 13 | negScaleFactor = sin(L) * sin(L); 14 | negOffsetFactor = -(cos(L) - Float(1.0)); 15 | } 16 | 17 | Float sigma; 18 | Float posScaleFactor; 19 | Float posOffsetFactor; 20 | Float negScaleFactor; 21 | Float negOffsetFactor; 22 | Float L; 23 | }; 24 | 25 | void ComputeGaussian(const H2MCParam ¶m, 26 | const Float sc, 27 | const AlignedStdVector &vGrad, 28 | const AlignedStdVector &vHess, 29 | Gaussian &gaussian); 30 | -------------------------------------------------------------------------------- /src/image.cpp: -------------------------------------------------------------------------------- 1 | #include "image.h" 2 | 3 | #include 4 | 5 | Image3::Image3(const std::string &filename) { 6 | OpenImageIO::ImageInput *in = OpenImageIO::ImageInput::open(filename); 7 | if (in == nullptr) { 8 | Error("File not found"); 9 | } 10 | const OpenImageIO::ImageSpec &spec = in->spec(); 11 | pixelWidth = spec.width; 12 | pixelHeight = spec.height; 13 | OpenImageIO::TypeDesc typeDesc = sizeof(Float) == sizeof(double) ? OpenImageIO::TypeDesc::DOUBLE 14 | : OpenImageIO::TypeDesc::FLOAT; 15 | if (spec.nchannels == 1) { 16 | std::vector pixels(pixelWidth * pixelHeight); 17 | in->read_image(typeDesc, &pixels[0]); 18 | data.resize(pixelWidth * pixelHeight); 19 | int p = 0; 20 | for (int y = 0; y < pixelHeight; y++) { 21 | for (int x = 0; x < pixelWidth; x++) { 22 | Float val = pixels[p++]; 23 | At(x, y) = Vector3(val, val, val); 24 | } 25 | } 26 | } else if (spec.nchannels == 3) { 27 | std::vector pixels(3 * pixelWidth * pixelHeight); 28 | in->read_image(typeDesc, &pixels[0]); 29 | data.resize(pixelWidth * pixelHeight); 30 | int p = 0; 31 | for (int y = 0; y < pixelHeight; y++) { 32 | for (int x = 0; x < pixelWidth; x++) { 33 | Float val0 = pixels[p++]; 34 | Float val1 = pixels[p++]; 35 | Float val2 = pixels[p++]; 36 | At(x, y) = Vector3(val0, val1, val2); 37 | } 38 | } 39 | } else { 40 | Error("Unsupported number of channels"); 41 | } 42 | in->close(); 43 | } 44 | 45 | void WriteImage(const std::string &filename, const Image3 *image) { 46 | OpenImageIO::ImageOutput *out = OpenImageIO::ImageOutput::create(filename); 47 | if (out == nullptr) { 48 | Error("Fail to create file"); 49 | return; 50 | } 51 | OpenImageIO::ImageSpec spec( 52 | image->pixelWidth, image->pixelHeight, 3, OpenImageIO::TypeDesc::HALF); 53 | out->open(filename, spec); 54 | out->write_image(sizeof(Float) == sizeof(double) ? OpenImageIO::TypeDesc::DOUBLE 55 | : OpenImageIO::TypeDesc::FLOAT, 56 | &image->data[0]); 57 | out->close(); 58 | OpenImageIO::ImageOutput::destroy(out); 59 | } -------------------------------------------------------------------------------- /src/image.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "commondef.h" 4 | #include "utils.h" 5 | #include "parallel.h" 6 | 7 | #include 8 | #include 9 | #include 10 | 11 | struct Image3 { 12 | Image3() { 13 | } 14 | Image3(const std::string &filename); 15 | Image3(int w, int h) : pixelWidth(w), pixelHeight(h) { 16 | data.resize(w * h); 17 | fill(data.begin(), data.end(), Vector3::Zero()); 18 | } 19 | 20 | Vector3 &At(int x) { 21 | return data[x]; 22 | } 23 | const Vector3 &At(int x) const { 24 | return data[x]; 25 | } 26 | Vector3 &At(int x, int y) { 27 | return data[y * pixelWidth + x]; 28 | } 29 | const Vector3 &At(int x, int y) const { 30 | return data[y * pixelWidth + x]; 31 | } 32 | Vector3 &RepAt(int x, int y) { 33 | return data[Modulo(y, pixelHeight) * pixelWidth + Modulo(x, pixelWidth)]; 34 | } 35 | const Vector3 &RepAt(int x, int y) const { 36 | return data[Modulo(y, pixelHeight) * pixelWidth + Modulo(x, pixelWidth)]; 37 | } 38 | int NumPixels() const { 39 | return pixelWidth * pixelHeight; 40 | } 41 | void Clear() { 42 | fill(data.begin(), data.end(), Vector3::Zero()); 43 | } 44 | 45 | int pixelWidth; 46 | int pixelHeight; 47 | std::vector> data; 48 | }; 49 | 50 | void WriteImage(const std::string &filename, const Image3 *image); 51 | 52 | using Pixel = std::array; 53 | 54 | struct SampleBuffer { 55 | SampleBuffer(const int pixelWidth, const int pixelHeight) 56 | : pixelWidth(pixelWidth), pixelHeight(pixelHeight) { 57 | pixels = std::unique_ptr(new Pixel[pixelWidth * pixelHeight]); 58 | } 59 | 60 | std::unique_ptr pixels; 61 | const int pixelWidth; 62 | const int pixelHeight; 63 | }; 64 | 65 | inline void Splat(SampleBuffer &buffer, const Vector2 screenPos, const Vector3 &contrib) { 66 | int ix = Clamp(int(screenPos[0] * buffer.pixelWidth), 0, buffer.pixelWidth - 1); 67 | int iy = Clamp(int(screenPos[1] * buffer.pixelHeight), 0, buffer.pixelHeight - 1); 68 | Pixel &pixel = buffer.pixels[iy * buffer.pixelWidth + ix]; 69 | if (contrib.allFinite()) { 70 | for (int i = 0; i < int(pixel.size()); i++) { 71 | pixel[i].Add(contrib[i]); 72 | } 73 | } 74 | } 75 | 76 | inline void MergeBuffer(const SampleBuffer &buffer1, 77 | const Float b1Weight, 78 | const SampleBuffer &buffer2, 79 | const Float b2Weight, 80 | SampleBuffer &bufferOut) { 81 | assert(buffer1.pixelWidth == buffer2.pixelWidth); 82 | assert(buffer1.pixelHeight == buffer2.pixelHeight); 83 | assert(buffer2.pixelWidth == bufferOut.pixelWidth); 84 | assert(buffer2.pixelHeight == bufferOut.pixelHeight); 85 | for (int i = 0; i < bufferOut.pixelWidth * bufferOut.pixelHeight; i++) { 86 | const Pixel &pixel1 = buffer1.pixels[i]; 87 | const Pixel &pixel2 = buffer2.pixels[i]; 88 | Pixel &pixelOut = bufferOut.pixels[i]; 89 | for (int j = 0; j < int(pixelOut.size()); j++) { 90 | pixelOut[j].Add(b1Weight * pixel1[j]); 91 | pixelOut[j].Add(b2Weight * pixel2[j]); 92 | } 93 | } 94 | } 95 | 96 | inline void BufferToFilm(const SampleBuffer &buffer, 97 | Image3 *film, 98 | const Float factor = Float(1.0)) { 99 | for (int i = 0; i < buffer.pixelWidth * buffer.pixelHeight; i++) { 100 | const Pixel &pixel = buffer.pixels[i]; 101 | Vector3 color = factor * Vector3(Float(pixel[0]), Float(pixel[1]), Float(pixel[2])); 102 | film->At(i) = color; 103 | } 104 | } 105 | -------------------------------------------------------------------------------- /src/lambertian.cpp: -------------------------------------------------------------------------------- 1 | #include "lambertian.h" 2 | #include "utils.h" 3 | #include "sampling.h" 4 | 5 | int GetLambertianSerializedSize() { 6 | return 1 + // type 7 | 3; // Kd 8 | } 9 | 10 | void Lambertian::Serialize(const Vector2 st, Float *buffer) const { 11 | buffer = ::Serialize((Float)BSDFType::Lambertian, buffer); 12 | ::Serialize(Kd->Eval(st), buffer); 13 | } 14 | 15 | void Lambertian::Evaluate(const Vector3 &wi, 16 | const Vector3 &normal, 17 | const Vector3 &wo, 18 | const Vector2 st, 19 | Vector3 &contrib, 20 | Float &cosWo, 21 | Float &pdf, 22 | Float &revPdf) const { 23 | Float cosWi = Dot(normal, wi); 24 | Vector3 normal_ = normal; 25 | if (twoSided && cosWi < Float(0.0)) { 26 | cosWi = -cosWi; 27 | normal_ = -normal_; 28 | } 29 | cosWo = Dot(normal_, wo); 30 | contrib.setZero(); 31 | if (cosWi < c_CosEpsilon || cosWo < c_CosEpsilon) { 32 | return; 33 | } 34 | 35 | Float fwdScalar = cosWo * c_INVPI; 36 | Float revScalar = cosWi * c_INVPI; 37 | contrib = fwdScalar * Kd->Eval(st); 38 | pdf = fwdScalar; 39 | revPdf = revScalar; 40 | } 41 | 42 | 43 | template 44 | void Sample(const TVector3 &normal, 45 | const TVector2 rndParam, 46 | TVector3 &wo, 47 | FloatType &cosWo, 48 | FloatType &pdf) { 49 | TVector3 b0; 50 | TVector3 b1; 51 | CoordinateSystem(normal, b0, b1); 52 | TVector3 ret = SampleCosHemisphere(rndParam); 53 | wo = ret[0] * b0 + ret[1] * b1 + ret[2] * normal; 54 | cosWo = ret[2]; 55 | pdf = ret[2] * c_INVPI; 56 | } 57 | 58 | bool Lambertian::Sample(const Vector3 &wi, 59 | const Vector3 &normal, 60 | const Vector2 st, 61 | const Vector2 rndParam, 62 | const Float /*uDiscrete*/, 63 | Vector3 &wo, 64 | Vector3 &contrib, 65 | Float &cosWo, 66 | Float &pdf, 67 | Float &revPdf) const { 68 | Float cosWi = Dot(wi, normal); 69 | Vector3 normal_ = normal; 70 | if (fabs(cosWi) < c_CosEpsilon) { 71 | return false; 72 | } 73 | 74 | if (cosWi < Float(0.0)) { 75 | if (twoSided) { 76 | cosWi = -cosWi; 77 | normal_ = -normal_; 78 | } else { 79 | return false; 80 | } 81 | } 82 | 83 | ::Sample(normal_, rndParam, wo, cosWo, pdf); 84 | if (cosWo < c_CosEpsilon) { 85 | return false; 86 | } 87 | 88 | revPdf = cosWi * c_INVPI; 89 | contrib = Kd->Eval(st); 90 | 91 | return true; 92 | } 93 | 94 | void EvaluateLambertian(const bool adjoint, 95 | const ADFloat *buffer, 96 | const ADVector3 &wi, 97 | const ADVector3 &normal, 98 | const ADVector3 &wo, 99 | const ADVector2 st, 100 | ADVector3 &contrib, 101 | ADFloat &cosWo, 102 | ADFloat &pdf, 103 | ADFloat &revPdf) { 104 | ADVector3 Kd; 105 | Deserialize(buffer, Kd); 106 | ADFloat cosWi = Dot(normal, wi); 107 | std::vector ret = CreateCondExprVec(4); 108 | BeginIf(Gt(cosWi, Float(0.0)), ret); 109 | { SetCondOutput({normal[0], normal[1], normal[2], cosWi}); } 110 | BeginElse(); 111 | { SetCondOutput({-normal[0], -normal[1], -normal[2], -cosWi}); } 112 | EndIf(); 113 | ADVector3 normal_(ret[0], ret[1], ret[2]); 114 | cosWi = ret[3]; 115 | cosWo = Dot(normal_, wo); 116 | ADFloat fwdScalar = cosWo * c_INVPI; 117 | ADFloat revScalar = cosWi * c_INVPI; 118 | contrib = fwdScalar * Kd; 119 | pdf = fwdScalar; 120 | revPdf = revScalar; 121 | } 122 | 123 | void SampleLambertian(const bool adjoint, 124 | const ADFloat *buffer, 125 | const ADVector3 &wi, 126 | const ADVector3 &normal, 127 | const ADVector2 st, 128 | const ADVector2 rndParam, 129 | const ADFloat uDiscrete, 130 | const bool fixDiscrete, 131 | ADVector3 &wo, 132 | ADVector3 &contrib, 133 | ADFloat &cosWo, 134 | ADFloat &pdf, 135 | ADFloat &revPdf) { 136 | ADVector3 Kd; 137 | Deserialize(buffer, Kd); 138 | ADFloat cosWi = Dot(wi, normal); 139 | std::vector ret = CreateCondExprVec(4); 140 | BeginIf(Gt(cosWi, Float(0.0)), ret); 141 | { SetCondOutput({normal[0], normal[1], normal[2], cosWi}); } 142 | BeginElse(); 143 | { SetCondOutput({-normal[0], -normal[1], -normal[2], -cosWi}); } 144 | EndIf(); 145 | ADVector3 normal_(ret[0], ret[1], ret[2]); 146 | cosWi = ret[3]; 147 | Sample(normal_, rndParam, wo, cosWo, pdf); 148 | contrib = Kd; 149 | revPdf = cosWi * c_INVPI; 150 | } 151 | -------------------------------------------------------------------------------- /src/lambertian.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "commondef.h" 4 | #include "bsdf.h" 5 | #include "texture.h" 6 | 7 | int GetLambertianSerializedSize(); 8 | 9 | struct Lambertian : public BSDF { 10 | Lambertian(const bool twoSided, const std::shared_ptr &Kd) 11 | : twoSided(twoSided), Kd(Kd) { 12 | } 13 | 14 | BSDFType GetType() const override { 15 | return BSDFType::Lambertian; 16 | } 17 | void Serialize(const Vector2 st, Float *buffer) const override; 18 | void Evaluate(const Vector3 &wi, 19 | const Vector3 &normal, 20 | const Vector3 &wo, 21 | const Vector2 st, 22 | Vector3 &contrib, 23 | Float &cosWo, 24 | Float &pdf, 25 | Float &revPdf) const override; 26 | bool Sample(const Vector3 &wi, 27 | const Vector3 &normal, 28 | const Vector2 st, 29 | const Vector2 rndParam, 30 | const Float uDiscrete, 31 | Vector3 &wo, 32 | Vector3 &contrib, 33 | Float &cosWo, 34 | Float &pdf, 35 | Float &revPdf) const override; 36 | 37 | Float Roughness(const Vector2 st, const Float uDiscrete) const override { 38 | return Float(1.0); 39 | } 40 | 41 | const bool twoSided; 42 | const std::shared_ptr Kd; 43 | }; 44 | 45 | void EvaluateLambertian(const bool adjoint, 46 | const ADFloat *buffer, 47 | const ADVector3 &wi, 48 | const ADVector3 &normal, 49 | const ADVector3 &wo, 50 | const ADVector2 st, 51 | ADVector3 &contrib, 52 | ADFloat &cosWo, 53 | ADFloat &pdf, 54 | ADFloat &revPdf); 55 | 56 | void SampleLambertian(const bool adjoint, 57 | const ADFloat *buffer, 58 | const ADVector3 &wi, 59 | const ADVector3 &normal, 60 | const ADVector2 st, 61 | const ADVector2 rndParam, 62 | const ADFloat uDiscrete, 63 | const bool fixDiscrete, 64 | ADVector3 &wo, 65 | ADVector3 &contrib, 66 | ADFloat &cosWo, 67 | ADFloat &pdf, 68 | ADFloat &revPdf); 69 | -------------------------------------------------------------------------------- /src/light.cpp: -------------------------------------------------------------------------------- 1 | #include "light.h" 2 | #include "pointlight.h" 3 | #include "arealight.h" 4 | #include "envlight.h" 5 | #include "utils.h" 6 | 7 | int GetMaxLightSerializedSize() { 8 | return std::max( 9 | {GetPointLightSerializedSize(), GetAreaLightSerializedSize(), GetEnvLightSerializedSize()}); 10 | } 11 | 12 | const ADFloat *SampleDirect(const ADFloat *buffer, 13 | const ADBSphere &sceneSphere, 14 | const ADVector3 &pos, 15 | const ADVector3 &normal, 16 | const ADVector2 rndParam, 17 | const ADFloat time, 18 | const bool isStatic, 19 | ADVector3 &dirToLight, 20 | ADVector3 &lightContrib, 21 | ADFloat &cosAtLight, 22 | ADFloat &directPdf, 23 | ADFloat &emissionPdf) { 24 | ADFloat type; 25 | buffer = Deserialize(buffer, type); 26 | std::vector ret = CreateCondExprVec(9); 27 | BeginIf(Eq(type, (Float)LightType::PointLight), ret); 28 | { 29 | ADVector3 dirToLight; 30 | ADVector3 lightContrib; 31 | ADFloat cosAtLight; 32 | ADFloat directPdf; 33 | ADFloat emissionPdf; 34 | SampleDirectPointLight(buffer, 35 | sceneSphere, 36 | pos, 37 | normal, 38 | rndParam, 39 | time, 40 | isStatic, 41 | dirToLight, 42 | lightContrib, 43 | cosAtLight, 44 | directPdf, 45 | emissionPdf); 46 | SetCondOutput({dirToLight[0], 47 | dirToLight[1], 48 | dirToLight[2], 49 | lightContrib[0], 50 | lightContrib[1], 51 | lightContrib[2], 52 | cosAtLight, 53 | directPdf, 54 | emissionPdf}); 55 | } 56 | BeginElseIf(Eq(type, (Float)LightType::AreaLight)); 57 | { 58 | ADVector3 dirToLight; 59 | ADVector3 lightContrib; 60 | ADFloat cosAtLight; 61 | ADFloat directPdf; 62 | ADFloat emissionPdf; 63 | SampleDirectAreaLight(buffer, 64 | sceneSphere, 65 | pos, 66 | normal, 67 | rndParam, 68 | time, 69 | isStatic, 70 | dirToLight, 71 | lightContrib, 72 | cosAtLight, 73 | directPdf, 74 | emissionPdf); 75 | SetCondOutput({dirToLight[0], 76 | dirToLight[1], 77 | dirToLight[2], 78 | lightContrib[0], 79 | lightContrib[1], 80 | lightContrib[2], 81 | cosAtLight, 82 | directPdf, 83 | emissionPdf}); 84 | } 85 | BeginElseIf(Eq(type, (Float)LightType::EnvLight)); 86 | { 87 | ADVector3 dirToLight; 88 | ADVector3 lightContrib; 89 | ADFloat cosAtLight; 90 | ADFloat directPdf; 91 | ADFloat emissionPdf; 92 | SampleDirectEnvLight(buffer, 93 | sceneSphere, 94 | pos, 95 | normal, 96 | rndParam, 97 | time, 98 | isStatic, 99 | dirToLight, 100 | lightContrib, 101 | cosAtLight, 102 | directPdf, 103 | emissionPdf); 104 | SetCondOutput({dirToLight[0], 105 | dirToLight[1], 106 | dirToLight[2], 107 | lightContrib[0], 108 | lightContrib[1], 109 | lightContrib[2], 110 | cosAtLight, 111 | directPdf, 112 | emissionPdf}); 113 | } 114 | BeginElse(); 115 | { 116 | SetCondOutput({Const(0.0), 117 | Const(0.0), 118 | Const(0.0), 119 | Const(0.0), 120 | Const(0.0), 121 | Const(0.0), 122 | Const(0.0), 123 | Const(0.0), 124 | Const(0.0)}); 125 | } 126 | EndIf(); 127 | dirToLight[0] = ret[0]; 128 | dirToLight[1] = ret[1]; 129 | dirToLight[2] = ret[2]; 130 | lightContrib[0] = ret[3]; 131 | lightContrib[1] = ret[4]; 132 | lightContrib[2] = ret[5]; 133 | cosAtLight = ret[6]; 134 | directPdf = ret[7]; 135 | emissionPdf = ret[8]; 136 | buffer += (GetMaxLightSerializedSize() - 1); 137 | return buffer; 138 | } 139 | 140 | const ADFloat *Emission(const ADFloat *buffer, 141 | const ADBSphere &sceneSphere, 142 | const ADVector3 &dirToLight, 143 | const ADVector3 &normalOnLight, 144 | const ADFloat time, 145 | ADVector3 &emission, 146 | ADFloat &directPdf, 147 | ADFloat &emissionPdf) { 148 | ADFloat type; 149 | buffer = Deserialize(buffer, type); 150 | std::vector ret = CreateCondExprVec(5); 151 | BeginIf(Eq(type, (Float)LightType::AreaLight), ret); 152 | { 153 | ADVector3 emission; 154 | ADFloat directPdf; 155 | ADFloat emissionPdf; 156 | EmissionAreaLight( 157 | buffer, sceneSphere, dirToLight, normalOnLight, time, emission, directPdf, emissionPdf); 158 | SetCondOutput({emission[0], emission[1], emission[2], directPdf, emissionPdf}); 159 | } 160 | BeginElseIf(Eq(type, (Float)LightType::EnvLight)); 161 | { 162 | ADVector3 emission; 163 | ADFloat directPdf; 164 | ADFloat emissionPdf; 165 | EmissionEnvLight( 166 | buffer, sceneSphere, dirToLight, normalOnLight, time, emission, directPdf, emissionPdf); 167 | SetCondOutput({emission[0], emission[1], emission[2], directPdf, emissionPdf}); 168 | } 169 | BeginElse(); 170 | { 171 | SetCondOutput({Const(0.0), 172 | Const(0.0), 173 | Const(0.0), 174 | Const(0.0), 175 | Const(0.0)}); 176 | } 177 | EndIf(); 178 | emission[0] = ret[0]; 179 | emission[1] = ret[1]; 180 | emission[2] = ret[2]; 181 | directPdf = ret[3]; 182 | emissionPdf = ret[4]; 183 | buffer += (GetMaxLightSerializedSize() - 1); 184 | return buffer; 185 | } 186 | 187 | const ADFloat *Emit(const ADFloat *buffer, 188 | const ADBSphere &sceneSphere, 189 | const ADVector2 rndParamPos, 190 | const ADVector2 rndParamDir, 191 | const ADFloat time, 192 | const bool isStatic, 193 | ADRay &ray, 194 | ADVector3 &emission, 195 | ADFloat &cosAtLight, 196 | ADFloat &emissionPdf, 197 | ADFloat &directPdf) { 198 | ADFloat type; 199 | buffer = Deserialize(buffer, type); 200 | std::vector ret = CreateCondExprVec(12); 201 | BeginIf(Eq(type, (Float)LightType::PointLight), ret); 202 | { 203 | ADRay ray; 204 | ADVector3 emission; 205 | ADFloat cosAtLight; 206 | ADFloat emissionPdf; 207 | ADFloat directPdf; 208 | EmitPointLight(buffer, 209 | sceneSphere, 210 | rndParamPos, 211 | rndParamDir, 212 | time, 213 | isStatic, 214 | ray, 215 | emission, 216 | cosAtLight, 217 | emissionPdf, 218 | directPdf); 219 | SetCondOutput({ray.org[0], 220 | ray.org[1], 221 | ray.org[2], 222 | ray.dir[0], 223 | ray.dir[1], 224 | ray.dir[2], 225 | emission[0], 226 | emission[1], 227 | emission[2], 228 | cosAtLight, 229 | emissionPdf, 230 | directPdf}); 231 | } 232 | BeginElseIf(Eq(type, (Float)LightType::AreaLight)); 233 | { 234 | ADRay ray; 235 | ADVector3 emission; 236 | ADFloat cosAtLight; 237 | ADFloat emissionPdf; 238 | ADFloat directPdf; 239 | EmitAreaLight(buffer, 240 | sceneSphere, 241 | rndParamPos, 242 | rndParamDir, 243 | time, 244 | isStatic, 245 | ray, 246 | emission, 247 | cosAtLight, 248 | emissionPdf, 249 | directPdf); 250 | SetCondOutput({ray.org[0], 251 | ray.org[1], 252 | ray.org[2], 253 | ray.dir[0], 254 | ray.dir[1], 255 | ray.dir[2], 256 | emission[0], 257 | emission[1], 258 | emission[2], 259 | cosAtLight, 260 | emissionPdf, 261 | directPdf}); 262 | } 263 | BeginElseIf(Eq(type, (Float)LightType::EnvLight)); 264 | { 265 | ADRay ray; 266 | ADVector3 emission; 267 | ADFloat cosAtLight; 268 | ADFloat emissionPdf; 269 | ADFloat directPdf; 270 | EmitEnvLight(buffer, 271 | sceneSphere, 272 | rndParamPos, 273 | rndParamDir, 274 | time, 275 | isStatic, 276 | ray, 277 | emission, 278 | cosAtLight, 279 | emissionPdf, 280 | directPdf); 281 | SetCondOutput({ray.org[0], 282 | ray.org[1], 283 | ray.org[2], 284 | ray.dir[0], 285 | ray.dir[1], 286 | ray.dir[2], 287 | emission[0], 288 | emission[1], 289 | emission[2], 290 | cosAtLight, 291 | emissionPdf, 292 | directPdf}); 293 | } 294 | BeginElse(); 295 | { 296 | SetCondOutput({Const(0.0), 297 | Const(0.0), 298 | Const(0.0), 299 | Const(0.0), 300 | Const(0.0), 301 | Const(0.0), 302 | Const(0.0), 303 | Const(0.0), 304 | Const(0.0), 305 | Const(0.0), 306 | Const(0.0), 307 | Const(0.0)}); 308 | } 309 | EndIf(); 310 | ray.org[0] = ret[0]; 311 | ray.org[1] = ret[1]; 312 | ray.org[2] = ret[2]; 313 | ray.dir[0] = ret[3]; 314 | ray.dir[1] = ret[4]; 315 | ray.dir[2] = ret[5]; 316 | emission[0] = ret[6]; 317 | emission[1] = ret[7]; 318 | emission[2] = ret[8]; 319 | cosAtLight = ret[9]; 320 | emissionPdf = ret[10]; 321 | directPdf = ret[11]; 322 | buffer += (GetMaxLightSerializedSize() - 1); 323 | return buffer; 324 | } 325 | -------------------------------------------------------------------------------- /src/light.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "commondef.h" 4 | #include "bounds.h" 5 | #include "ray.h" 6 | 7 | enum class LightType { PointLight, AreaLight, EnvLight }; 8 | 9 | int GetMaxLightSerializedSize(); 10 | 11 | typedef PrimID LightPrimID; 12 | const LightPrimID INVALID_LPRIM_ID = LightPrimID(-1); 13 | 14 | struct Light { 15 | Light(const Float &samplingWeight) : samplingWeight(samplingWeight) { 16 | } 17 | 18 | virtual LightType GetType() const = 0; 19 | virtual void Serialize(const LightPrimID &lPrimID, Float *buffer) const = 0; 20 | virtual LightPrimID SampleDiscrete(const Float uDiscrete) const { 21 | return INVALID_LPRIM_ID; 22 | } 23 | virtual bool SampleDirect(const BSphere &sceneSphere, 24 | const Vector3 &pos, 25 | const Vector3 &normal, 26 | const Vector2 rndParam, 27 | const Float time, 28 | LightPrimID &lPrimID, 29 | Vector3 &dirToLight, 30 | Float &dist, 31 | Vector3 &contrib, 32 | Float &cosAtLight, 33 | Float &directPdf, 34 | Float &emissionPdf) const = 0; 35 | virtual void Emission(const BSphere &sceneSphere, 36 | const Vector3 &dirToLight, 37 | const Vector3 &normalOnLight, 38 | const Float time, 39 | LightPrimID &lPrimID, 40 | Vector3 &emission, 41 | Float &directPdf, 42 | Float &emissionPdf) const { 43 | Error("Unimplemented method"); 44 | } 45 | virtual void Emit(const BSphere &sceneSphere, 46 | const Vector2 rndParamPos, 47 | const Vector2 rndParamDir, 48 | const Float time, 49 | LightPrimID &lPrimID, 50 | Ray &ray, 51 | Vector3 &emission, 52 | Float &cosAtLight, 53 | Float &emissionPdf, 54 | Float &directPdf) const = 0; 55 | virtual bool IsFinite() const = 0; 56 | virtual bool IsDelta() const = 0; 57 | 58 | Float samplingWeight; 59 | }; 60 | 61 | struct LightInst { 62 | const Light *light; 63 | LightPrimID lPrimID; 64 | }; 65 | 66 | const ADFloat *SampleDirect(const ADFloat *buffer, 67 | const ADBSphere &sceneSphere, 68 | const ADVector3 &pos, 69 | const ADVector3 &normal, 70 | const ADVector2 rndParam, 71 | const ADFloat time, 72 | const bool isStatic, 73 | ADVector3 &dirToLight, 74 | ADVector3 &lightContrib, 75 | ADFloat &cosAtLight, 76 | ADFloat &directPdf, 77 | ADFloat &emissionPdf); 78 | 79 | const ADFloat *Emission(const ADFloat *buffer, 80 | const ADBSphere &sceneSphere, 81 | const ADVector3 &dirToLight, 82 | const ADVector3 &normalOnLight, 83 | const ADFloat time, 84 | ADVector3 &emission, 85 | ADFloat &directPdf, 86 | ADFloat &emissionPdf); 87 | 88 | const ADFloat *Emit(const ADFloat *buffer, 89 | const ADBSphere &sceneSphere, 90 | const ADVector2 rndParamPos, 91 | const ADVector2 rndParamDir, 92 | const ADFloat time, 93 | const bool isStatic, 94 | ADRay &ray, 95 | ADVector3 &emission, 96 | ADFloat &cosAtLight, 97 | ADFloat &emissionPdf, 98 | ADFloat &directPdf); 99 | -------------------------------------------------------------------------------- /src/loadserialized.cpp: -------------------------------------------------------------------------------- 1 | #include "loadserialized.h" 2 | 3 | #include "transform.h" 4 | 5 | #include 6 | #include 7 | #include 8 | 9 | #define MTS_FILEFORMAT_VERSION_V3 0x0003 10 | #define MTS_FILEFORMAT_VERSION_V4 0x0004 11 | 12 | /// Buffer size used to communicate with zlib. The larger, the better. 13 | #define ZSTREAM_BUFSIZE 32768 14 | 15 | enum ETriMeshFlags { 16 | EHasNormals = 0x0001, 17 | EHasTexcoords = 0x0002, 18 | EHasTangents = 0x0004, // unused 19 | EHasColors = 0x0008, 20 | EFaceNormals = 0x0010, 21 | ESinglePrecision = 0x1000, 22 | EDoublePrecision = 0x2000 23 | }; 24 | 25 | class ZStream { 26 | public: 27 | /// Create a new compression stream 28 | ZStream(std::fstream &fs); 29 | void read(void *ptr, size_t size); 30 | virtual ~ZStream(); 31 | 32 | private: 33 | std::fstream &fs; 34 | size_t fsize; 35 | z_stream m_inflateStream; 36 | uint8_t m_inflateBuffer[ZSTREAM_BUFSIZE]; 37 | }; 38 | 39 | ZStream::ZStream(std::fstream &fs) : fs(fs) { 40 | std::streampos pos = fs.tellg(); 41 | fs.seekg(0, fs.end); 42 | fsize = fs.tellg(); 43 | fs.seekg(pos, fs.beg); 44 | 45 | int windowBits = 15; 46 | m_inflateStream.zalloc = Z_NULL; 47 | m_inflateStream.zfree = Z_NULL; 48 | m_inflateStream.opaque = Z_NULL; 49 | m_inflateStream.avail_in = 0; 50 | m_inflateStream.next_in = Z_NULL; 51 | 52 | int retval = inflateInit2(&m_inflateStream, windowBits); 53 | if (retval != Z_OK) { 54 | Error("Could not initialize ZLIB"); 55 | } 56 | } 57 | 58 | void ZStream::read(void *ptr, size_t size) { 59 | uint8_t *targetPtr = (uint8_t *)ptr; 60 | while (size > 0) { 61 | if (m_inflateStream.avail_in == 0) { 62 | size_t remaining = fsize - fs.tellg(); 63 | m_inflateStream.next_in = m_inflateBuffer; 64 | m_inflateStream.avail_in = (uInt)std::min(remaining, sizeof(m_inflateBuffer)); 65 | if (m_inflateStream.avail_in == 0) { 66 | Error("Read less data than expected"); 67 | } 68 | 69 | fs.read((char *)m_inflateBuffer, m_inflateStream.avail_in); 70 | } 71 | 72 | m_inflateStream.avail_out = (uInt)size; 73 | m_inflateStream.next_out = targetPtr; 74 | 75 | int retval = inflate(&m_inflateStream, Z_NO_FLUSH); 76 | switch (retval) { 77 | case Z_STREAM_ERROR: { 78 | Error("inflate(): stream error!"); 79 | } 80 | case Z_NEED_DICT: { 81 | Error("inflate(): need dictionary!"); 82 | } 83 | case Z_DATA_ERROR: { 84 | Error("inflate(): data error!"); 85 | } 86 | case Z_MEM_ERROR: { 87 | Error("inflate(): memory error!"); 88 | } 89 | }; 90 | 91 | size_t outputSize = size - (size_t)m_inflateStream.avail_out; 92 | targetPtr += outputSize; 93 | size -= outputSize; 94 | 95 | if (size > 0 && retval == Z_STREAM_END) { 96 | Error("inflate(): attempting to read past the end of the stream!"); 97 | } 98 | } 99 | } 100 | 101 | ZStream::~ZStream() { 102 | inflateEnd(&m_inflateStream); 103 | } 104 | 105 | template 106 | inline Float UnitAngle(const VectorType &u, const VectorType &v) { 107 | if (Dot(u, v) < 0) { 108 | return (c_PI - Float(2.0)) * asin(Float(0.5) * Length(Vector3(v + u))); 109 | } else { 110 | return Float(2.0) * asin(Float(0.5) * Length(Vector3(v - u))); 111 | } 112 | } 113 | 114 | inline void ComputeNormal(const std::vector &vertices, 115 | const std::vector &triangles, 116 | std::vector &normals) { 117 | normals.resize(vertices.size(), Vector3::Zero()); 118 | 119 | // Nelson Max, "Computing Vertex Vector3ds from Facet Vector3ds", 1999 120 | for (auto &tri : triangles) { 121 | Vector3 n = Vector3::Zero(); 122 | for (int i = 0; i < 3; ++i) { 123 | const Vector3 &v0 = vertices[tri.index[i]]; 124 | const Vector3 &v1 = vertices[tri.index[(i + 1) % 3]]; 125 | const Vector3 &v2 = vertices[tri.index[(i + 2) % 3]]; 126 | Vector3 sideA(v1 - v0), sideB(v2 - v0); 127 | if (i == 0) { 128 | n = Cross(sideA, sideB); 129 | Float length = Length(n); 130 | if (length == 0) 131 | break; 132 | n = n / length; 133 | } 134 | Float angle = UnitAngle(Normalize(sideA), Normalize(sideB)); 135 | normals[tri.index[i]] = normals[tri.index[i]] + n * angle; 136 | } 137 | } 138 | 139 | for (auto &n : normals) { 140 | Float length = Length(n); 141 | if (length != 0) { 142 | n = n / length; 143 | } else { 144 | /* Choose some bogus value */ 145 | n = Vector3::Zero(); 146 | } 147 | } 148 | } 149 | 150 | void SkipToIdx(std::fstream &fs, const short version, const size_t idx) { 151 | // Go to the end of the file to see how many components are there 152 | fs.seekg(-sizeof(uint32_t), fs.end); 153 | uint32_t count = 0; 154 | fs.read((char *)&count, sizeof(uint32_t)); 155 | size_t offset = 0; 156 | if (version == MTS_FILEFORMAT_VERSION_V4) { 157 | fs.seekg(-sizeof(uint64_t) * (count - idx) - sizeof(uint32_t), fs.end); 158 | fs.read((char *)&offset, sizeof(size_t)); 159 | } else { // V3 160 | fs.seekg(-sizeof(uint32_t) * (count - idx + 1), fs.end); 161 | uint32_t upos = 0; 162 | fs.read((char *)&upos, sizeof(unsigned int)); 163 | offset = upos; 164 | } 165 | fs.seekg(offset, fs.beg); 166 | // Skip the header 167 | fs.ignore(sizeof(short) * 2); 168 | } 169 | 170 | template 171 | void LoadPosition(ZStream &zs, 172 | const std::shared_ptr &data, 173 | const Matrix4x4 &toWorld0, 174 | const Matrix4x4 &toWorld1, 175 | const bool isMoving) { 176 | for (size_t i = 0; i < data->position0.size(); i++) { 177 | Precision x, y, z; 178 | zs.read(&x, sizeof(Precision)); 179 | zs.read(&y, sizeof(Precision)); 180 | zs.read(&z, sizeof(Precision)); 181 | data->position0[i] = XformPoint(toWorld0, Vector3(x, y, z)); 182 | if (isMoving) { 183 | data->position1[i] = XformPoint(toWorld1, Vector3(x, y, z)); 184 | } else { 185 | data->position1[i] = data->position0[i]; 186 | } 187 | } 188 | } 189 | 190 | template 191 | void LoadNormal(ZStream &zs, 192 | const std::shared_ptr &data, 193 | const Matrix4x4 &invToWorld0, 194 | const Matrix4x4 &invToWorld1, 195 | const bool isMoving) { 196 | for (size_t i = 0; i < data->normal0.size(); i++) { 197 | Precision x, y, z; 198 | zs.read(&x, sizeof(Precision)); 199 | zs.read(&y, sizeof(Precision)); 200 | zs.read(&z, sizeof(Precision)); 201 | data->normal0[i] = XformNormal(invToWorld0, Vector3(x, y, z)); 202 | if (isMoving) { 203 | data->normal1[i] = XformNormal(invToWorld1, Vector3(x, y, z)); 204 | } else { 205 | data->normal1[i] = data->normal0[i]; 206 | } 207 | } 208 | } 209 | 210 | template 211 | void LoadUV(ZStream &zs, const std::shared_ptr &data) { 212 | for (size_t i = 0; i < data->st.size(); i++) { 213 | Precision u, v; 214 | zs.read(&u, sizeof(Precision)); 215 | zs.read(&v, sizeof(Precision)); 216 | data->st[i] = Vector2(Float(u), Float(v)); 217 | } 218 | } 219 | 220 | template 221 | void LoadColor(ZStream &zs, const std::shared_ptr &data) { 222 | for (size_t i = 0; i < data->colors.size(); i++) { 223 | double r, g, b; 224 | zs.read(&r, sizeof(double)); 225 | zs.read(&g, sizeof(double)); 226 | zs.read(&b, sizeof(double)); 227 | data->colors[i] = Vector3(r, g, b); 228 | } 229 | } 230 | 231 | std::shared_ptr LoadSerialized(const std::string &filename, 232 | int idx, 233 | const Matrix4x4 &toWorld0, 234 | const Matrix4x4 &toWorld1, 235 | bool isMoving) { 236 | Matrix4x4 invToWorld0 = toWorld0.inverse(); 237 | Matrix4x4 invToWorld1 = toWorld1.inverse(); 238 | 239 | std::fstream fs(filename.c_str(), std::fstream::in | std::fstream::binary); 240 | // Format magic number, ignore it 241 | fs.ignore(sizeof(short)); 242 | // Version number 243 | short version = 0; 244 | fs.read((char *)&version, sizeof(short)); 245 | if (idx > 0) { 246 | SkipToIdx(fs, version, idx); 247 | } 248 | ZStream zs(fs); 249 | 250 | uint32_t flags; 251 | zs.read((char *)&flags, sizeof(uint32_t)); 252 | std::string name; 253 | if (version == MTS_FILEFORMAT_VERSION_V4) { 254 | char c; 255 | while (true) { 256 | zs.read((char *)&c, sizeof(char)); 257 | if (c == '\0') 258 | break; 259 | name.push_back(c); 260 | } 261 | } 262 | size_t vertexCount = 0; 263 | zs.read((char *)&vertexCount, sizeof(size_t)); 264 | size_t triangleCount = 0; 265 | zs.read((char *)&triangleCount, sizeof(size_t)); 266 | 267 | bool fileDoublePrecision = flags & EDoublePrecision; 268 | // bool faceNormals = flags & EFaceNormals; 269 | 270 | std::shared_ptr data = std::make_shared(); 271 | data->isMoving = isMoving; 272 | data->position0 = std::vector(vertexCount); 273 | data->position1 = std::vector(vertexCount); 274 | if (fileDoublePrecision) { 275 | LoadPosition(zs, data, toWorld0, toWorld1, isMoving); 276 | } else { 277 | LoadPosition(zs, data, toWorld0, toWorld1, isMoving); 278 | } 279 | 280 | if (flags & EHasNormals) { 281 | data->normal0 = std::vector(vertexCount); 282 | data->normal1 = std::vector(vertexCount); 283 | if (fileDoublePrecision) { 284 | LoadNormal(zs, data, invToWorld0, invToWorld1, isMoving); 285 | } else { 286 | LoadNormal(zs, data, invToWorld0, invToWorld1, isMoving); 287 | } 288 | } 289 | 290 | if (flags & EHasTexcoords) { 291 | data->st = std::vector(vertexCount); 292 | if (fileDoublePrecision) { 293 | LoadUV(zs, data); 294 | } else { 295 | LoadUV(zs, data); 296 | } 297 | } 298 | 299 | if (flags & EHasColors) { 300 | data->colors = std::vector(vertexCount); 301 | if (fileDoublePrecision) { 302 | LoadColor(zs, data); 303 | } else { 304 | LoadColor(zs, data); 305 | } 306 | } 307 | 308 | data->indices = std::vector(triangleCount); 309 | zs.read(&data->indices[0], triangleCount * sizeof(TriIndex)); 310 | 311 | if (data->normal0.size() == 0) { 312 | ComputeNormal(data->position0, data->indices, data->normal0); 313 | ComputeNormal(data->position1, data->indices, data->normal1); 314 | } 315 | 316 | return data; 317 | } 318 | -------------------------------------------------------------------------------- /src/loadserialized.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "commondef.h" 4 | #include "trianglemesh.h" 5 | #include 6 | 7 | std::shared_ptr LoadSerialized(const std::string &filename, 8 | int shapeIndex, 9 | const Matrix4x4 &toWorld0, 10 | const Matrix4x4 &toWorld1, 11 | bool isMoving); 12 | -------------------------------------------------------------------------------- /src/main.cpp: -------------------------------------------------------------------------------- 1 | #include "parsescene.h" 2 | #include "pathtrace.h" 3 | #include "mlt.h" 4 | #include "image.h" 5 | #include "camera.h" 6 | #include "texturesystem.h" 7 | #include "parallel.h" 8 | #include "path.h" 9 | 10 | #include 11 | #include 12 | 13 | void DptInit() { 14 | TextureSystem::Init(); 15 | std::cout << "Running with " << NumSystemCores() << " threads." << std::endl; 16 | if (getenv("DPT_LIBPATH") == nullptr) { 17 | std::cout 18 | << "Environment variable DPT_LIBPATH not set." 19 | "Please set it to a directory (e.g. export DPT_LIBPATH=/dir/to/dpt/src/bin) so " 20 | "that I can write/find the dynamic libraries." << std::endl; 21 | exit(0); 22 | } 23 | } 24 | 25 | void DptCleanup() { 26 | TextureSystem::Destroy(); 27 | TerminateWorkerThreads(); 28 | } 29 | 30 | int main(int argc, char *argv[]) { 31 | if (argc <= 1) { 32 | return 0; 33 | } 34 | 35 | try { 36 | DptInit(); 37 | 38 | bool compilePathLib = false; 39 | bool compileBidirPathLib = false; 40 | int maxDervDepth = 6; 41 | std::vector filenames; 42 | for (int i = 1; i < argc; ++i) { 43 | if (std::string(argv[i]) == "--compile-pathlib") { 44 | compilePathLib = true; 45 | } else if (std::string(argv[i]) == "--compile-bidirpathlib") { 46 | compileBidirPathLib = true; 47 | } else if (std::string(argv[i]) == "--max-derivatives-depth") { 48 | maxDervDepth = std::stoi(std::string(argv[++i])); 49 | } else { 50 | filenames.push_back(std::string(argv[i])); 51 | } 52 | } 53 | 54 | if (compilePathLib) { 55 | CompilePathFuncLibrary(false, maxDervDepth); 56 | } 57 | if (compileBidirPathLib) { 58 | CompilePathFuncLibrary(true, maxDervDepth); 59 | } 60 | 61 | std::string cwd = getcwd(NULL, 0); 62 | for (std::string filename : filenames) { 63 | if (filename.rfind('/') != std::string::npos && 64 | chdir(filename.substr(0, filename.rfind('/')).c_str()) != 0) { 65 | Error("chdir failed"); 66 | } 67 | if (filename.rfind('/') != std::string::npos) { 68 | filename = filename.substr(filename.rfind('/') + 1); 69 | } 70 | std::unique_ptr scene = ParseScene(filename); 71 | std::string integrator = scene->options->integrator; 72 | if (integrator == "mc") { 73 | std::shared_ptr library = 74 | BuildPathFuncLibrary(scene->options->bidirectional, maxDervDepth); 75 | PathTrace(scene.get(), library); 76 | } else if (integrator == "mcmc") { 77 | std::shared_ptr library = 78 | BuildPathFuncLibrary(scene->options->bidirectional, maxDervDepth); 79 | MLT(scene.get(), library); 80 | } else { 81 | Error("Unknown integrator"); 82 | } 83 | WriteImage(scene->outputName, GetFilm(scene->camera.get()).get()); 84 | if (chdir(cwd.c_str()) != 0) { 85 | Error("chdir failed"); 86 | } 87 | } 88 | DptCleanup(); 89 | } catch (std::exception &ex) { 90 | std::cerr << ex.what() << std::endl; 91 | } 92 | 93 | return 0; 94 | } 95 | -------------------------------------------------------------------------------- /src/microfacet.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "commondef.h" 4 | #include "utils.h" 5 | 6 | template 7 | FloatType BeckmennDistributionTerm(const TVector3 &localH, 8 | const FloatType &alphaU, 9 | const FloatType &alphaV) { 10 | const FloatType cosTheta = localH[2]; 11 | const FloatType mu = localH[0]; 12 | const FloatType mv = localH[1]; 13 | FloatType cosTheta2 = square(cosTheta); 14 | FloatType beckmannExponent = 15 | (square(mu) / square(alphaU) + square(mv) / square(alphaV)) / cosTheta2; 16 | FloatType D = exp(-beckmannExponent) / (c_PI * alphaU * alphaV * square(cosTheta2)); 17 | return D; 18 | } 19 | 20 | inline Float BeckmennGeometryTerm(const Float alpha, const Float cosTheta) { 21 | Float tanTheta = sqrt(fabs(Float(1.0) - square(cosTheta))) / cosTheta; 22 | if (tanTheta <= 0.0) 23 | return Float(1.0); 24 | Float G = 0.0; 25 | Float a = Float(1.0) / (alpha * tanTheta); 26 | if (a >= Float(1.6)) { 27 | G = Float(1.0); 28 | } else { 29 | /* Use a fast and accurate (<0.35% rel. error) rational 30 | approximation to the shadowing-masking function */ 31 | // Note: this could be a bad approximation for derivatives, 32 | // ideally we should refit the function 33 | Float aSqr = a * a; 34 | G = (Float(3.535) * a + Float(2.181) * aSqr) / 35 | (Float(1.0) + Float(2.276) * a + Float(2.577) * aSqr); 36 | } 37 | return G; 38 | } 39 | 40 | inline ADFloat BeckmennGeometryTerm(const ADFloat alpha, const ADFloat cosTheta) { 41 | ADFloat tanTheta = sqrt(fabs((Float(1.0) + Float(1e-6)) - square(cosTheta))) / cosTheta; 42 | CondExprCPtr ret = CondExpr::Create(); 43 | BeginIf(Lte(tanTheta, Float(0.0)), {ret}); 44 | { SetCondOutput({Const(1.0)}); } 45 | BeginElse(); 46 | { 47 | CondExprCPtr G = CondExpr::Create(); 48 | ADFloat a = inverse(alpha * tanTheta); 49 | BeginIf(Gte(a, Float(1.6)), {G}); 50 | { SetCondOutput({Const(1.0)}); } 51 | BeginElse(); 52 | { 53 | ADFloat aSqr = square(a); 54 | ADFloat G_ = (Float(3.535) * a + Float(2.181) * aSqr) / 55 | (Float(1.0) + Float(2.276) * a + Float(2.577) * aSqr); 56 | SetCondOutput({G_}); 57 | } 58 | EndIf(); 59 | SetCondOutput({G}); 60 | } 61 | EndIf(); 62 | return ret; 63 | } 64 | 65 | template 66 | FloatType BeckmennGeometryTerm(const FloatType alpha, 67 | const FloatType cosWi, 68 | const FloatType cosWo) { 69 | return BeckmennGeometryTerm(alpha, cosWi) * BeckmennGeometryTerm(alpha, cosWo); 70 | } 71 | 72 | inline Float FresnelDielectricExt(const Float cosThetaI_, 73 | Float &cosThetaT_, 74 | const Float eta, 75 | const Float invEta) { 76 | Float scale = (cosThetaI_ > 0) ? invEta : eta; 77 | Float cosThetaTSqr = Float(1.0) - (Float(1.0) - square(cosThetaI_)) * square(scale); 78 | 79 | if (cosThetaTSqr <= Float(0.0)) { 80 | cosThetaT_ = Float(0.0); 81 | return Float(1.0); 82 | } 83 | 84 | Float cosThetaI = fabs(cosThetaI_); 85 | Float cosThetaT = sqrt(cosThetaTSqr); 86 | 87 | Float Rs = (cosThetaI - eta * cosThetaT) / (cosThetaI + eta * cosThetaT); 88 | Float Rp = (eta * cosThetaI - cosThetaT) / (eta * cosThetaI + cosThetaT); 89 | 90 | cosThetaT_ = (cosThetaI_ > 0) ? -cosThetaT : cosThetaT; 91 | 92 | return Float(0.5) * (square(Rs) + square(Rp)); 93 | } 94 | 95 | inline Float FresnelDielectricExt(const Float cosThetaI_, const Float eta, const Float invEta) { 96 | Float scale = (cosThetaI_ > 0) ? invEta : eta; 97 | Float cosThetaTSqr = Float(1.0) - (Float(1.0) - square(cosThetaI_)) * square(scale); 98 | 99 | if (cosThetaTSqr <= Float(0.0)) { 100 | return Float(1.0); 101 | } 102 | 103 | Float cosThetaI = fabs(cosThetaI_); 104 | Float cosThetaT = sqrt(cosThetaTSqr); 105 | 106 | Float Rs = (cosThetaI - eta * cosThetaT) / (cosThetaI + eta * cosThetaT); 107 | Float Rp = (eta * cosThetaI - cosThetaT) / (eta * cosThetaI + cosThetaT); 108 | 109 | return Float(0.5) * (square(Rs) + square(Rp)); 110 | } 111 | 112 | inline ADFloat FresnelDielectricExt(const ADFloat cosThetaI_, 113 | ADFloat &cosThetaT_, 114 | const ADFloat eta, 115 | const ADFloat invEta) { 116 | ADFloat scale = if_else(Gt(cosThetaI_, Float(0.0)), invEta, eta); 117 | ADFloat cosThetaTSqr = Float(1.0) - (Float(1.0) - square(cosThetaI_)) * square(scale); 118 | 119 | std::vector ret = CreateCondExprVec(2); 120 | BeginIf(Lte(cosThetaTSqr, Float(0.0)), ret); 121 | { SetCondOutput({Const(0.0), Const(1.0)}); } 122 | BeginElse(); 123 | { 124 | ADFloat cosThetaI = fabs(cosThetaI_); 125 | ADFloat cosThetaT = sqrt(cosThetaTSqr); 126 | ADFloat etaCosThetaT = eta * cosThetaT; 127 | ADFloat etaCosThetaI = eta * cosThetaI; 128 | ADFloat Rs = (cosThetaI - etaCosThetaT) / (cosThetaI + etaCosThetaT); 129 | ADFloat Rp = (etaCosThetaI - cosThetaT) / (etaCosThetaI + cosThetaT); 130 | 131 | ADFloat cosThetaT_ = if_else(Gt(cosThetaI_, Float(0.0)), -cosThetaT, cosThetaT); 132 | ADFloat F = Float(0.5) * (square(Rs) + square(Rp)); 133 | SetCondOutput({cosThetaT_, F}); 134 | } 135 | EndIf(); 136 | 137 | cosThetaT_ = ret[0]; 138 | return ret[1]; 139 | } 140 | 141 | inline ADFloat FresnelDielectricExt(const ADFloat cosThetaI_, 142 | const ADFloat eta, 143 | const ADFloat invEta) { 144 | ADFloat scale = if_else(Gt(cosThetaI_, Float(0.0)), invEta, eta); 145 | ADFloat cosThetaTSqr = Float(1.0) - (Float(1.0) - square(cosThetaI_)) * square(scale); 146 | 147 | std::vector ret = CreateCondExprVec(1); 148 | BeginIf(Lte(cosThetaTSqr, Float(0.0)), ret); 149 | { SetCondOutput({Const(1.0)}); } 150 | BeginElse(); 151 | { 152 | ADFloat cosThetaI = fabs(cosThetaI_); 153 | ADFloat cosThetaT = sqrt(cosThetaTSqr); 154 | ADFloat etaCosThetaT = eta * cosThetaT; 155 | ADFloat etaCosThetaI = eta * cosThetaI; 156 | ADFloat Rs = (cosThetaI - etaCosThetaT) / (cosThetaI + etaCosThetaT); 157 | ADFloat Rp = (etaCosThetaI - cosThetaT) / (etaCosThetaI + cosThetaT); 158 | 159 | ADFloat F = Float(0.5) * (square(Rs) + square(Rp)); 160 | SetCondOutput({F}); 161 | } 162 | EndIf(); 163 | return ret[0]; 164 | } 165 | 166 | template 167 | TVector3 SampleMicronormal(const TVector2 rndParam, 168 | const FloatType alpha, 169 | FloatType &pdfW) { 170 | FloatType phiM = c_TWOPI * rndParam[1]; 171 | FloatType sinPhiM = sin(phiM); 172 | FloatType cosPhiM = cos(phiM); 173 | FloatType alphaSqr = square(alpha); 174 | 175 | FloatType tanThetaMSqr = alphaSqr * (-log(fmax(Float(1.0) - rndParam[0], Float(1e-6)))); 176 | FloatType cosThetaM = Float(1.0) / sqrt(Float(1.0) + tanThetaMSqr); 177 | FloatType cosThetaMSqr = square(cosThetaM); 178 | 179 | pdfW = (Float(1.0) - rndParam[0]) / (c_PI * alphaSqr * cosThetaM * cosThetaMSqr); 180 | FloatType sinThetaMSq = fmax(Float(1.0) - cosThetaMSqr, ADEpsilon()); 181 | FloatType sinThetaM = sqrt(sinThetaMSq); 182 | 183 | TVector3 localH(sinThetaM * cosPhiM, sinThetaM * sinPhiM, cosThetaM); 184 | return localH; 185 | } 186 | -------------------------------------------------------------------------------- /src/mlt.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "scene.h" 4 | 5 | #include 6 | 7 | struct PathFuncLib; 8 | 9 | void MLT(const Scene *scene, const std::shared_ptr pathFuncLib); 10 | -------------------------------------------------------------------------------- /src/parallel.cpp: -------------------------------------------------------------------------------- 1 | #include "parallel.h" 2 | #include 3 | #include 4 | #include 5 | 6 | // From https://github.com/mmp/pbrt-v3/blob/master/src/core/parallel.cpp 7 | 8 | static std::vector threads; 9 | static bool shutdownThreads = false; 10 | struct ParallelForLoop; 11 | static ParallelForLoop *workList = nullptr; 12 | static std::mutex workListMutex; 13 | struct ParallelForLoop { 14 | ParallelForLoop(std::function func1D, int64_t maxIndex, int chunkSize) 15 | : func1D(std::move(func1D)), maxIndex(maxIndex), chunkSize(chunkSize) { 16 | } 17 | ParallelForLoop(const std::function &f, const Vector2i count) 18 | : func2D(f), maxIndex(count[0] * count[1]), chunkSize(1) { 19 | nX = count[0]; 20 | } 21 | 22 | std::function func1D; 23 | std::function func2D; 24 | const int64_t maxIndex; 25 | const int chunkSize; 26 | int64_t nextIndex = 0; 27 | int activeWorkers = 0; 28 | ParallelForLoop *next = nullptr; 29 | int nX = -1; 30 | 31 | bool Finished() const { 32 | return nextIndex >= maxIndex && activeWorkers == 0; 33 | } 34 | }; 35 | 36 | static std::condition_variable workListCondition; 37 | static void workerThreadFunc(const int tIndex) { 38 | threadIndex = tIndex; 39 | std::unique_lock lock(workListMutex); 40 | while (!shutdownThreads) { 41 | if (!workList) { 42 | // Sleep until there are more tasks to run 43 | workListCondition.wait(lock); 44 | } else { 45 | // Get work from _workList_ and run loop iterations 46 | ParallelForLoop &loop = *workList; 47 | 48 | // Run a chunk of loop iterations for _loop_ 49 | 50 | // Find the set of loop iterations to run next 51 | int64_t indexStart = loop.nextIndex; 52 | int64_t indexEnd = std::min(indexStart + loop.chunkSize, loop.maxIndex); 53 | 54 | // Update _loop_ to reflect iterations this thread will run 55 | loop.nextIndex = indexEnd; 56 | if (loop.nextIndex == loop.maxIndex) 57 | workList = loop.next; 58 | loop.activeWorkers++; 59 | 60 | // Run loop indices in _[indexStart, indexEnd)_ 61 | lock.unlock(); 62 | for (int64_t index = indexStart; index < indexEnd; ++index) { 63 | if (loop.func1D) { 64 | loop.func1D(index); 65 | } 66 | // Handle other types of loops 67 | else { 68 | assert(loop.func2D != nullptr); 69 | loop.func2D(Vector2i(index % loop.nX, index / loop.nX)); 70 | } 71 | } 72 | lock.lock(); 73 | 74 | // Update _loop_ to reflect completion of iterations 75 | loop.activeWorkers--; 76 | if (loop.Finished()) 77 | workListCondition.notify_all(); 78 | } 79 | } 80 | } 81 | 82 | void ParallelFor(const std::function &func, int64_t count, int chunkSize) { 83 | // Run iterations immediately if not using threads or if _count_ is small 84 | if (count < chunkSize) { 85 | for (int64_t i = 0; i < count; ++i) { 86 | func(i); 87 | } 88 | return; 89 | } 90 | 91 | // Launch worker threads if needed 92 | if (threads.size() == 0) { 93 | threadIndex = 0; 94 | for (int i = 0; i < NumSystemCores() - 1; ++i) { 95 | threads.push_back(std::thread(workerThreadFunc, i + 1)); 96 | } 97 | } 98 | 99 | // Create and enqueue _ParallelForLoop_ for this loop 100 | ParallelForLoop loop(func, count, chunkSize); 101 | workListMutex.lock(); 102 | loop.next = workList; 103 | workList = &loop; 104 | workListMutex.unlock(); 105 | 106 | // Notify worker threads of work to be done 107 | std::unique_lock lock(workListMutex); 108 | workListCondition.notify_all(); 109 | 110 | // Help out with parallel loop iterations in the current thread 111 | while (!loop.Finished()) { 112 | // Run a chunk of loop iterations for _loop_ 113 | 114 | // Find the set of loop iterations to run next 115 | int64_t indexStart = loop.nextIndex; 116 | int64_t indexEnd = std::min(indexStart + loop.chunkSize, loop.maxIndex); 117 | 118 | // Update _loop_ to reflect iterations this thread will run 119 | loop.nextIndex = indexEnd; 120 | if (loop.nextIndex == loop.maxIndex) { 121 | workList = loop.next; 122 | } 123 | loop.activeWorkers++; 124 | 125 | // Run loop indices in _[indexStart, indexEnd)_ 126 | lock.unlock(); 127 | for (int64_t index = indexStart; index < indexEnd; ++index) { 128 | if (loop.func1D) { 129 | loop.func1D(index); 130 | } 131 | // Handle other types of loops 132 | else { 133 | assert(loop.func2D != nullptr); 134 | loop.func2D(Vector2i(index % loop.nX, index / loop.nX)); 135 | } 136 | } 137 | lock.lock(); 138 | 139 | // Update _loop_ to reflect completion of iterations 140 | loop.activeWorkers--; 141 | } 142 | } 143 | 144 | thread_local int threadIndex; 145 | int MaxThreadIndex() { 146 | // Launch worker threads if needed 147 | if (threads.size() == 0) { 148 | threadIndex = 0; 149 | for (int i = 0; i < NumSystemCores() - 1; ++i) 150 | threads.push_back(std::thread(workerThreadFunc, i + 1)); 151 | } 152 | return 1 + threads.size(); 153 | } 154 | 155 | void ParallelFor(std::function func, const Vector2i count) { 156 | // Launch worker threads if needed 157 | if (threads.size() == 0) { 158 | threadIndex = 0; 159 | for (int i = 0; i < NumSystemCores() - 1; ++i) 160 | threads.push_back(std::thread(workerThreadFunc, i + 1)); 161 | } 162 | 163 | ParallelForLoop loop(std::move(func), count); 164 | { 165 | std::lock_guard lock(workListMutex); 166 | loop.next = workList; 167 | workList = &loop; 168 | } 169 | 170 | std::unique_lock lock(workListMutex); 171 | workListCondition.notify_all(); 172 | 173 | // Help out with parallel loop iterations in the current thread 174 | while (!loop.Finished()) { 175 | // Run a chunk of loop iterations for _loop_ 176 | 177 | // Find the set of loop iterations to run next 178 | int64_t indexStart = loop.nextIndex; 179 | int64_t indexEnd = std::min(indexStart + loop.chunkSize, loop.maxIndex); 180 | 181 | // Update _loop_ to reflect iterations this thread will run 182 | loop.nextIndex = indexEnd; 183 | if (loop.nextIndex == loop.maxIndex) { 184 | workList = loop.next; 185 | } 186 | loop.activeWorkers++; 187 | 188 | // Run loop indices in _[indexStart, indexEnd)_ 189 | lock.unlock(); 190 | for (int64_t index = indexStart; index < indexEnd; ++index) { 191 | if (loop.func1D) { 192 | loop.func1D(index); 193 | } 194 | // Handle other types of loops 195 | else { 196 | assert(loop.func2D != nullptr); 197 | loop.func2D(Vector2i(index % loop.nX, index / loop.nX)); 198 | } 199 | } 200 | lock.lock(); 201 | 202 | // Update _loop_ to reflect completion of iterations 203 | loop.activeWorkers--; 204 | } 205 | } 206 | 207 | int NumSystemCores() { 208 | // return 1; 209 | int ret = std::thread::hardware_concurrency(); 210 | if (ret == 0) { 211 | return 16; 212 | } 213 | return ret; 214 | } 215 | 216 | void TerminateWorkerThreads() { 217 | if (threads.size() == 0) { 218 | return; 219 | } 220 | 221 | { 222 | std::lock_guard lock(workListMutex); 223 | shutdownThreads = true; 224 | workListCondition.notify_all(); 225 | } 226 | 227 | for (std::thread &thread : threads) { 228 | thread.join(); 229 | } 230 | threads.erase(threads.begin(), threads.end()); 231 | shutdownThreads = false; 232 | } 233 | -------------------------------------------------------------------------------- /src/parallel.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "commondef.h" 4 | #include "utils.h" 5 | 6 | #include 7 | #include 8 | #include 9 | 10 | // From https://github.com/mmp/pbrt-v3/blob/master/src/core/parallel.h 11 | 12 | class AtomicFloat { 13 | public: 14 | explicit AtomicFloat(Float v = 0) { 15 | bits = FloatToBits(v); 16 | } 17 | operator Float() const { 18 | return BitsToFloat(bits); 19 | } 20 | Float operator=(Float v) { 21 | bits = FloatToBits(v); 22 | return v; 23 | } 24 | void Add(Float v) { 25 | #if defined(DOUBLE_PRECISION) 26 | uint64_t oldBits = bits, newBits; 27 | #elif defined(SINGLE_PRECISION) 28 | uint32_t oldBits = bits, newBits; 29 | #endif 30 | do { 31 | newBits = FloatToBits(BitsToFloat(oldBits) + v); 32 | } while (!bits.compare_exchange_weak(oldBits, newBits)); 33 | } 34 | 35 | private: 36 | #if defined(DOUBLE_PRECISION) 37 | std::atomic bits; 38 | #elif defined(SINGLE_PRECISION) 39 | std::atomic bits; 40 | #endif 41 | }; 42 | 43 | void ParallelFor(const std::function &func, int64_t count, int chunkSize = 1); 44 | extern thread_local int threadIndex; 45 | void ParallelFor(std::function func, const Vector2i count); 46 | int MaxThreadIndex(); 47 | int NumSystemCores(); 48 | void TerminateWorkerThreads(); 49 | -------------------------------------------------------------------------------- /src/parseobj.cpp: -------------------------------------------------------------------------------- 1 | #include "parseobj.h" 2 | #include "transform.h" 3 | #include "utils.h" 4 | 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | // trim from start 11 | static inline std::string <rim(std::string &s) { 12 | s.erase(s.begin(), 13 | std::find_if(s.begin(), s.end(), std::not1(std::ptr_fun(std::isspace)))); 14 | return s; 15 | } 16 | 17 | // trim from end 18 | static inline std::string &rtrim(std::string &s) { 19 | s.erase( 20 | std::find_if(s.rbegin(), s.rend(), std::not1(std::ptr_fun(std::isspace))).base(), 21 | s.end()); 22 | return s; 23 | } 24 | 25 | // trim from both ends 26 | static inline std::string &trim(std::string &s) { 27 | return ltrim(rtrim(s)); 28 | } 29 | 30 | static std::vector splitFaceStr(const std::string &s) { 31 | std::regex rgx("/"); 32 | std::sregex_token_iterator first{begin(s), end(s), rgx, -1}, last; 33 | std::vector list{first, last}; 34 | std::vector result; 35 | for (auto &i : list) { 36 | if (i != "") 37 | result.push_back(std::stoi(i)); 38 | else 39 | result.push_back(0); 40 | } 41 | while (result.size() < 3) 42 | result.push_back(0); 43 | 44 | return result; 45 | } 46 | 47 | 48 | // Numerical robust computation of angle between unit vectors 49 | template 50 | inline Float UnitAngle(const VectorType &u, const VectorType &v) { 51 | if (Dot(u, v) < 0) 52 | return (c_PI - Float(2.0)) * asin(Float(0.5) * Length(Vector3(v + u))); 53 | else 54 | return Float(2.0) * asin(Float(0.5) * Length(Vector3(v - u))); 55 | } 56 | 57 | inline void ComputeNormal(const std::vector &vertices, 58 | const std::vector &triangles, 59 | std::vector &normals) { 60 | normals.resize(vertices.size(), Vector3::Zero()); 61 | 62 | // Nelson Max, "Computing Vertex Vector3ds from Facet Vector3ds", 1999 63 | for (auto &tri : triangles) { 64 | Vector3 n = Vector3::Zero(); 65 | for (int i = 0; i < 3; ++i) { 66 | const Vector3 &v0 = vertices[tri.index[i]]; 67 | const Vector3 &v1 = vertices[tri.index[(i + 1) % 3]]; 68 | const Vector3 &v2 = vertices[tri.index[(i + 2) % 3]]; 69 | Vector3 sideA(v1 - v0), sideB(v2 - v0); 70 | if (i == 0) { 71 | n = Cross(sideA, sideB); 72 | Float length = Length(n); 73 | if (length == 0) 74 | break; 75 | n = n / length; 76 | } 77 | Float angle = UnitAngle(Normalize(sideA), Normalize(sideB)); 78 | normals[tri.index[i]] = normals[tri.index[i]] + n * angle; 79 | } 80 | } 81 | 82 | for (auto &n : normals) { 83 | Float length = Length(n); 84 | if (length != 0) { 85 | n = n / length; 86 | } else { 87 | /* Choose some bogus value */ 88 | n = Vector3::Zero(); 89 | } 90 | } 91 | } 92 | 93 | struct ObjVertex { 94 | ObjVertex(const std::vector &id) : v(id[0] - 1), vt(id[1] - 1), vn(id[2] - 1) { 95 | } 96 | 97 | bool operator<(const ObjVertex &vertex) const { 98 | if (v != vertex.v) 99 | return v < vertex.v; 100 | if (vt != vertex.vt) 101 | return vt < vertex.vt; 102 | if (vn != vertex.vn) 103 | return vn < vertex.vn; 104 | return false; 105 | } 106 | 107 | int v, vt, vn; 108 | }; 109 | 110 | size_t GetVertexId(const ObjVertex &vertex, 111 | const std::vector &posPool, 112 | const std::vector &stPool, 113 | const std::vector &norPool, 114 | const Matrix4x4 &toWorld0, 115 | const Matrix4x4 &toWorld1, 116 | bool isMoving, 117 | std::vector &pos0, 118 | std::vector &pos1, 119 | std::vector &st, 120 | std::vector &nor0, 121 | std::vector &nor1, 122 | std::map &vertexMap) { 123 | auto it = vertexMap.find(vertex); 124 | if (it != vertexMap.end()) { 125 | return it->second; 126 | } 127 | size_t id = pos0.size(); 128 | pos0.push_back(XformPoint(toWorld0, posPool[vertex.v])); 129 | if (isMoving) { 130 | pos1.push_back(XformPoint(toWorld1, posPool[vertex.v])); 131 | } else { 132 | pos1.push_back(pos0.back()); 133 | } 134 | if (vertex.vt != -1) 135 | st.push_back(stPool[vertex.vt]); 136 | if (vertex.vn != -1) { 137 | nor0.push_back(XformNormal(Matrix4x4(toWorld0.inverse()), norPool[vertex.vn])); 138 | if (isMoving) { 139 | nor1.push_back(XformNormal(Matrix4x4(toWorld1.inverse()), norPool[vertex.vn])); 140 | } else { 141 | nor1.push_back(nor0.back()); 142 | } 143 | } 144 | vertexMap[vertex] = id; 145 | return id; 146 | } 147 | 148 | std::shared_ptr ParseObj(const std::string &filename, 149 | const Matrix4x4 &toWorld0, 150 | const Matrix4x4 &toWorld1, 151 | bool isMoving) { 152 | std::vector posPool; 153 | std::vector norPool; 154 | std::vector stPool; 155 | std::map vertexMap; 156 | std::shared_ptr data = std::make_shared(); 157 | data->isMoving = isMoving; 158 | 159 | std::ifstream ifs(filename.c_str(), std::ifstream::in); 160 | if (!ifs.is_open()) 161 | throw std::runtime_error("Unable to open the obj file"); 162 | while (ifs.good()) { 163 | std::string line; 164 | std::getline(ifs, line); 165 | line = trim(line); 166 | if (line.size() == 0 || line[0] == '#') // comment 167 | continue; 168 | 169 | std::stringstream ss(line); 170 | std::string token; 171 | ss >> token; 172 | if (token == "v") { // vertices 173 | Float x, y, z, w = 1.f; 174 | ss >> x >> y >> z >> w; 175 | Float invW = 1.f / w; 176 | posPool.push_back(Vector3(x * invW, y * invW, z * invW)); 177 | } else if (token == "vt") { 178 | Float s, t, w; 179 | ss >> s >> t >> w; 180 | stPool.push_back(Vector2(s, Float(1.0) - t)); 181 | } else if (token == "vn") { 182 | Float x, y, z; 183 | ss >> x >> y >> z; 184 | norPool.push_back(Normalize(Vector3(x, y, z))); 185 | } else if (token == "f") { 186 | std::string i0, i1, i2; 187 | ss >> i0 >> i1 >> i2; 188 | std::vector i0f = splitFaceStr(i0); 189 | std::vector i1f = splitFaceStr(i1); 190 | std::vector i2f = splitFaceStr(i2); 191 | 192 | ObjVertex v0(i0f), v1(i1f), v2(i2f); 193 | size_t v0id = GetVertexId(v0, 194 | posPool, 195 | stPool, 196 | norPool, 197 | toWorld0, 198 | toWorld1, 199 | isMoving, 200 | data->position0, 201 | data->position1, 202 | data->st, 203 | data->normal0, 204 | data->normal1, 205 | vertexMap); 206 | size_t v1id = GetVertexId(v1, 207 | posPool, 208 | stPool, 209 | norPool, 210 | toWorld0, 211 | toWorld1, 212 | isMoving, 213 | data->position0, 214 | data->position1, 215 | data->st, 216 | data->normal0, 217 | data->normal1, 218 | vertexMap); 219 | size_t v2id = GetVertexId(v2, 220 | posPool, 221 | stPool, 222 | norPool, 223 | toWorld0, 224 | toWorld1, 225 | isMoving, 226 | data->position0, 227 | data->position1, 228 | data->st, 229 | data->normal0, 230 | data->normal1, 231 | vertexMap); 232 | data->indices.push_back(TriIndex(v0id, v1id, v2id)); 233 | 234 | std::string i3; 235 | if (ss >> i3) { 236 | std::vector i3f = splitFaceStr(i3); 237 | ObjVertex v3(i3f); 238 | size_t v3id = GetVertexId(v3, 239 | posPool, 240 | stPool, 241 | norPool, 242 | toWorld0, 243 | toWorld1, 244 | isMoving, 245 | data->position0, 246 | data->position1, 247 | data->st, 248 | data->normal0, 249 | data->normal1, 250 | vertexMap); 251 | data->indices.push_back(TriIndex(v0id, v2id, v3id)); 252 | } 253 | std::string i4; 254 | if (ss >> i4) { 255 | Error("The object file contains n-gon (n>4) that we do not support."); 256 | } 257 | } // Currently ignore other tokens 258 | } 259 | if (data->normal0.size() == 0) { 260 | ComputeNormal(data->position0, data->indices, data->normal0); 261 | ComputeNormal(data->position1, data->indices, data->normal1); 262 | } 263 | 264 | return data; 265 | } -------------------------------------------------------------------------------- /src/parseobj.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "commondef.h" 4 | #include "trianglemesh.h" 5 | 6 | std::shared_ptr ParseObj(const std::string &filename, 7 | const Matrix4x4 &toWorld0, 8 | const Matrix4x4 &toWorld1, 9 | bool isMoving); 10 | -------------------------------------------------------------------------------- /src/parsescene.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "scene.h" 4 | 5 | #include 6 | #include 7 | 8 | std::unique_ptr ParseScene(const std::string &filename); 9 | -------------------------------------------------------------------------------- /src/path.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "commondef.h" 4 | #include "scene.h" 5 | #include "light.h" 6 | #include "shape.h" 7 | #include 8 | #include 9 | 10 | struct EnvLight; 11 | 12 | struct SubpathContrib { 13 | int camDepth; 14 | int lightDepth; 15 | Vector2 screenPos; 16 | Vector3 contrib; 17 | Float lsScore; 18 | Float ssScore; 19 | Float lensScore; 20 | Float misWeight; 21 | }; 22 | 23 | inline int GetPathLength(const SubpathContrib &spContrib) { 24 | return spContrib.camDepth + spContrib.lightDepth - 1; 25 | } 26 | 27 | struct CameraVertex { 28 | Vector2 screenPos; 29 | }; 30 | 31 | struct SurfaceVertex { 32 | ShapeInst shapeInst; 33 | Vector2 bsdfRndParam; 34 | Float bsdfDiscrete; 35 | Float useAbsoluteParam; 36 | LightInst directLightInst; 37 | Vector2 directLightRndParam; 38 | Float rrWeight; 39 | }; 40 | 41 | struct LightVertex { 42 | Vector2 rndParamPos; 43 | Vector2 rndParamDir; 44 | LightInst lightInst; 45 | }; 46 | 47 | struct Path { 48 | Float time; 49 | CameraVertex camVertex; 50 | std::vector camSurfaceVertex; 51 | LightVertex lgtVertex; 52 | std::vector lgtSurfaceVertex; 53 | // if last camera vertex hits an environment light, envLightInst.light != nullptr 54 | LightInst envLightInst; 55 | // Only used by camera subpaths 56 | Vector3 lensVertexPos; 57 | 58 | bool isSubpath; 59 | // undefined for non subpath 60 | int camDepth; 61 | int lgtDepth; 62 | bool isMoving; 63 | }; 64 | 65 | struct SerializedSubpath { 66 | AlignedStdVector primary; 67 | AlignedStdVector vertParams; 68 | }; 69 | 70 | void Clear(Path &path); 71 | void GeneratePath(const Scene *scene, 72 | const Vector2i screenPosi, 73 | const int minDepth, 74 | const int maxDepth, 75 | Path &path, 76 | std::vector &contribs, 77 | RNG &rng); 78 | void GeneratePathBidir(const Scene *scene, 79 | const Vector2i screenPosi, 80 | const int minDepth, 81 | const int maxDepth, 82 | Path &path, 83 | std::vector &contribs, 84 | RNG &rng); 85 | void GenerateSubpath(const Scene *scene, 86 | const Vector2i screenPosi, 87 | const int camLength, 88 | const int lgtLength, 89 | const bool bidirMIS, 90 | Path &path, 91 | std::vector &contribs, 92 | RNG &rng); 93 | void ToSubpath(const int camDepth, const int lightDepth, Path &path); 94 | void SetIsMoving(Path &path); 95 | void PerturbPath(const Scene *scene, 96 | const Vector &offset, 97 | Path &path, 98 | std::vector &contribs, 99 | RNG &rng); 100 | void PerturbPathBidir(const Scene *scene, 101 | const Vector &offset, 102 | Path &path, 103 | std::vector &contribs, 104 | RNG &rng); 105 | void PerturbLens(const Scene *scene, 106 | const Vector2 screenPos, 107 | Path &path, 108 | std::vector &contribs); 109 | void PerturbLensBidir(const Scene *scene, 110 | const Vector2 screenPos, 111 | Path &path, 112 | std::vector &contribs); 113 | size_t GetVertParamSize(const int maxCamDepth, const int maxLgtDepth); 114 | size_t GetPrimaryParamSize(const int camDepth, const int lightDepth); 115 | void Serialize(const Scene *scene, const Path &path, SerializedSubpath &subPath); 116 | Vector GetPathPos(const Path &path); 117 | 118 | inline int GetDimension(const Path &path) { 119 | assert(path.isSubpath); 120 | size_t pps = GetPrimaryParamSize(path.camDepth, path.lgtDepth); 121 | if (!path.isMoving) { 122 | pps--; 123 | } 124 | return pps; 125 | } 126 | 127 | inline int GetPathLength(const int camLength, const int lgtLength) { 128 | return camLength + lgtLength - 1; 129 | } 130 | 131 | using PathFunc = void (*)(const Float *, const Float *, const Float *, const Float *, Float *); 132 | using PathFuncDerv = 133 | void (*)(const Float *, const Float *, const Float *, const Float *, Float *, Float *); 134 | using PathFuncMap = std::unordered_map, PathFunc>; 135 | using PathFuncDervMap = std::unordered_map, PathFuncDerv>; 136 | 137 | struct PathFuncLib { 138 | int maxDepth; 139 | std::shared_ptr library; 140 | PathFuncMap funcMap; 141 | PathFuncDervMap dervFuncMap; 142 | PathFuncMap staticFuncMap; 143 | PathFuncDervMap staticDervFuncMap; 144 | PathFuncMap lensFuncMap; 145 | PathFuncDervMap lensDervFuncMap; 146 | }; 147 | 148 | std::shared_ptr CompilePathFuncLibrary(const bool bidirectional, 149 | const int maxDepth, 150 | std::shared_ptr *library = nullptr); 151 | 152 | std::shared_ptr BuildPathFuncLibrary(const bool bidirectional, 153 | const int maxDepth); 154 | -------------------------------------------------------------------------------- /src/pathtrace.cpp: -------------------------------------------------------------------------------- 1 | #include "pathtrace.h" 2 | #include "camera.h" 3 | #include "image.h" 4 | #include "path.h" 5 | #include "progressreporter.h" 6 | #include "parallel.h" 7 | #include "timer.h" 8 | #include "bsdf.h" 9 | 10 | #include 11 | #include 12 | #include 13 | 14 | void PathTrace(const Scene *scene, const std::shared_ptr pathFuncLib) { 15 | SerializedSubpath ssubPath; 16 | 17 | int maxDervDepth = pathFuncLib->maxDepth; 18 | std::vector sceneParams(GetSceneSerializedSize()); 19 | Serialize(scene, &sceneParams[0]); 20 | ssubPath.primary.resize(GetPrimaryParamSize(maxDervDepth, maxDervDepth)); 21 | ssubPath.vertParams.resize(GetVertParamSize(maxDervDepth, maxDervDepth)); 22 | 23 | const int spp = scene->options->spp; 24 | std::shared_ptr camera = scene->camera; 25 | std::shared_ptr film = camera->film; 26 | film->Clear(); 27 | const int pixelHeight = GetPixelHeight(camera.get()); 28 | const int pixelWidth = GetPixelWidth(camera.get()); 29 | const int tileSize = 16; 30 | const int nXTiles = (pixelWidth + tileSize - 1) / tileSize; 31 | const int nYTiles = (pixelHeight + tileSize - 1) / tileSize; 32 | ProgressReporter reporter(nXTiles * nYTiles); 33 | SampleBuffer buffer(pixelWidth, pixelHeight); 34 | auto pathFunc = scene->options->bidirectional ? GeneratePathBidir : GeneratePath; 35 | Timer timer; 36 | Tick(timer); 37 | ParallelFor([&](const Vector2i tile) { 38 | const int seed = tile[1] * nXTiles + tile[0]; 39 | RNG rng(seed); 40 | const int x0 = tile[0] * tileSize; 41 | const int x1 = std::min(x0 + tileSize, pixelWidth); 42 | const int y0 = tile[1] * tileSize; 43 | const int y1 = std::min(y0 + tileSize, pixelHeight); 44 | Path path; 45 | for (int y = y0; y < y1; y++) { 46 | for (int x = x0; x < x1; x++) { 47 | for (int s = 0; s < spp; s++) { 48 | Clear(path); 49 | std::vector spContribs; 50 | pathFunc(scene, 51 | Vector2i(x, y), 52 | scene->options->minDepth, 53 | scene->options->maxDepth, 54 | path, 55 | spContribs, 56 | rng); 57 | for (const auto &spContrib : spContribs) { 58 | if (Luminance(spContrib.contrib) <= Float(1e-10)) { 59 | continue; 60 | } 61 | Vector3 contrib = spContrib.contrib / Float(spp); 62 | Splat(buffer, spContrib.screenPos, contrib); 63 | } 64 | } 65 | } 66 | } 67 | reporter.Update(1); 68 | }, Vector2i(nXTiles, nYTiles)); 69 | TerminateWorkerThreads(); 70 | reporter.Done(); 71 | Float elapsed = Tick(timer); 72 | std::cout << "Elapsed time:" << elapsed << std::endl; 73 | 74 | BufferToFilm(buffer, film.get()); 75 | } 76 | -------------------------------------------------------------------------------- /src/pathtrace.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "scene.h" 4 | 5 | #include 6 | 7 | struct PathFuncLib; 8 | 9 | void PathTrace(const Scene *scene, const std::shared_ptr pathFuncLib); 10 | -------------------------------------------------------------------------------- /src/phong.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "commondef.h" 4 | #include "bsdf.h" 5 | #include "texture.h" 6 | 7 | int GetPhongSerializedSize(); 8 | 9 | struct Phong : public BSDF { 10 | Phong(const bool twoSided, 11 | const std::shared_ptr &Kd, 12 | const std::shared_ptr &Ks, 13 | const std::shared_ptr &exponent) 14 | : twoSided(twoSided), Kd(Kd), Ks(Ks), exponent(exponent), KsWeight(GetKsWeight()) { 15 | } 16 | 17 | BSDFType GetType() const override { 18 | return BSDFType::Phong; 19 | } 20 | void Serialize(const Vector2 st, Float *buffer) const override; 21 | void Evaluate(const Vector3 &wi, 22 | const Vector3 &normal, 23 | const Vector3 &wo, 24 | const Vector2 st, 25 | Vector3 &contrib, 26 | Float &cosWo, 27 | Float &pdf, 28 | Float &revPdf) const override; 29 | bool Sample(const Vector3 &wi, 30 | const Vector3 &normal, 31 | const Vector2 st, 32 | const Vector2 rndParam, 33 | const Float uDiscrete, 34 | Vector3 &wo, 35 | Vector3 &contrib, 36 | Float &cosWo, 37 | Float &pdf, 38 | Float &revPdf) const override; 39 | 40 | Float GetKsWeight() const; 41 | 42 | Float Roughness(const Vector2 st, const Float uDiscrete) const override; 43 | 44 | const bool twoSided; 45 | const std::shared_ptr Kd; 46 | const std::shared_ptr Ks; 47 | const std::shared_ptr exponent; 48 | const Float KsWeight; 49 | }; 50 | 51 | void EvaluatePhong(const bool adjoint, 52 | const ADFloat *buffer, 53 | const ADVector3 &wi, 54 | const ADVector3 &normal, 55 | const ADVector3 &wo, 56 | const ADVector2 st, 57 | ADVector3 &contrib, 58 | ADFloat &cosWo, 59 | ADFloat &pdf, 60 | ADFloat &revPdf); 61 | 62 | 63 | void SamplePhong(const bool adjoint, 64 | const ADFloat *buffer, 65 | const ADVector3 &wi, 66 | const ADVector3 &normal, 67 | const ADVector2 st, 68 | const ADVector2 rndParam, 69 | const ADFloat uDiscrete, 70 | const bool fixDiscrete, 71 | ADVector3 &wo, 72 | ADVector3 &contrib, 73 | ADFloat &cosWo, 74 | ADFloat &pdf, 75 | ADFloat &revPdf); 76 | -------------------------------------------------------------------------------- /src/pointlight.cpp: -------------------------------------------------------------------------------- 1 | #include "pointlight.h" 2 | #include "utils.h" 3 | #include "sampling.h" 4 | 5 | int GetPointLightSerializedSize() { 6 | return 1 + // type 7 | 3 + // position 8 | 3; // emission 9 | } 10 | 11 | PointLight::PointLight(const Float &samplingWeight, const Vector3 &pos, const Vector3 &emission) 12 | : Light(samplingWeight), lightPos(pos), emission(emission) { 13 | } 14 | 15 | void PointLight::Serialize(const LightPrimID &lPrimID, Float *buffer) const { 16 | buffer = ::Serialize((Float)LightType::PointLight, buffer); 17 | buffer = ::Serialize(lightPos, buffer); 18 | ::Serialize(emission, buffer); 19 | } 20 | 21 | template 22 | void _SampleDirectPointLight(const TVector3 &lightPos, 23 | const TVector3 &emission, 24 | const TVector3 &pos, 25 | FloatType &dist, 26 | TVector3 &dirToLight, 27 | TVector3 &lightContrib, 28 | FloatType &pdf) { 29 | dirToLight = lightPos - pos; 30 | const FloatType distSq = LengthSquared(dirToLight); 31 | pdf = distSq; 32 | dist = sqrt(distSq); 33 | dirToLight = dirToLight / dist; 34 | lightContrib = emission * inverse(distSq); 35 | } 36 | 37 | bool PointLight::SampleDirect(const BSphere & /*sceneSphere*/, 38 | const Vector3 &pos, 39 | const Vector3 & /*normal*/, 40 | const Vector2 /*rndParam*/, 41 | const Float /*time*/, 42 | LightPrimID &lPrimID, 43 | Vector3 &dirToLight, 44 | Float &dist, 45 | Vector3 &contrib, 46 | Float &cosAtLight, 47 | Float &directPdf, 48 | Float &emissionPdf) const { 49 | _SampleDirectPointLight(lightPos, emission, pos, dist, dirToLight, contrib, directPdf); 50 | assert(dist > Float(0.0)); 51 | emissionPdf = c_INVFOURPI; 52 | cosAtLight = Float(1.0); 53 | lPrimID = 0; 54 | return true; 55 | } 56 | 57 | void PointLight::Emit(const BSphere & /*sceneSphere*/, 58 | const Vector2 /*rndParamPos*/, 59 | const Vector2 rndParamDir, 60 | const Float /*time*/, 61 | LightPrimID & /*lPrimID*/, 62 | Ray &ray, 63 | Vector3 &emission, 64 | Float &cosAtLight, 65 | Float &emissionPdf, 66 | Float &directPdf) const { 67 | ray.org = this->lightPos; 68 | ray.dir = SampleSphere(rndParamDir); 69 | emission = this->emission; 70 | emissionPdf = c_INVFOURPI; 71 | cosAtLight = directPdf = Float(1.0); 72 | } 73 | 74 | void SampleDirectPointLight(const ADFloat *buffer, 75 | const ADBSphere & /*sceneSphere*/, 76 | const ADVector3 &pos, 77 | const ADVector3 & /*normal*/, 78 | const ADVector2 /*rndParam*/, 79 | const ADFloat /*time*/, 80 | const bool /*isStatic*/, 81 | ADVector3 &dirToLight, 82 | ADVector3 &lightContrib, 83 | ADFloat &cosAtLight, 84 | ADFloat &directPdf, 85 | ADFloat &emissionPdf) { 86 | ADVector3 lightPos, emission; 87 | buffer = Deserialize(buffer, lightPos); 88 | Deserialize(buffer, emission); 89 | ADFloat dist; 90 | _SampleDirectPointLight(lightPos, emission, pos, dist, dirToLight, lightContrib, directPdf); 91 | emissionPdf = Const(c_INVFOURPI); 92 | cosAtLight = Const(1.0); 93 | } 94 | 95 | void EmitPointLight(const ADFloat *buffer, 96 | const ADBSphere &sceneSphere, 97 | const ADVector2 rndParamPos, 98 | const ADVector2 rndParamDir, 99 | const ADFloat time, 100 | const bool /*isStatic*/, 101 | ADRay &ray, 102 | ADVector3 &emission, 103 | ADFloat &cosAtLight, 104 | ADFloat &emissionPdf, 105 | ADFloat &directPdf) { 106 | ADVector3 lightPos, emission_; 107 | buffer = Deserialize(buffer, lightPos); 108 | Deserialize(buffer, emission_); 109 | 110 | ray.org = lightPos; 111 | ray.dir = SampleSphere(rndParamDir); 112 | emission = emission_; 113 | emissionPdf = Const(c_INVFOURPI); 114 | cosAtLight = Const(1.0); 115 | directPdf = Const(1.0); 116 | } 117 | -------------------------------------------------------------------------------- /src/pointlight.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "light.h" 4 | 5 | int GetPointLightSerializedSize(); 6 | 7 | struct PointLight : public Light { 8 | PointLight(const Float &samplingWeight, const Vector3 &pos, const Vector3 &emission); 9 | 10 | LightType GetType() const override { 11 | return LightType::PointLight; 12 | } 13 | void Serialize(const LightPrimID &lPrimID, Float *buffer) const override; 14 | bool SampleDirect(const BSphere &sceneSphere, 15 | const Vector3 &pos, 16 | const Vector3 &normal, 17 | const Vector2 rndParam, 18 | const Float time, 19 | LightPrimID &lPrimID, 20 | Vector3 &dirToLight, 21 | Float &dist, 22 | Vector3 &contrib, 23 | Float &cosAtLight, 24 | Float &directPdf, 25 | Float &emissionPdf) const override; 26 | void Emit(const BSphere &sceneSphere, 27 | const Vector2 rndParamPos, 28 | const Vector2 rndParamDir, 29 | const Float time, 30 | LightPrimID &lPrimID, 31 | Ray &ray, 32 | Vector3 &emission, 33 | Float &cosAtLight, 34 | Float &emissionPdf, 35 | Float &directPdf) const override; 36 | bool IsFinite() const override { 37 | return false; 38 | } 39 | bool IsDelta() const override { 40 | return true; 41 | } 42 | 43 | const Vector3 lightPos; 44 | const Vector3 emission; 45 | }; 46 | 47 | void SampleDirectPointLight(const ADFloat *buffer, 48 | const ADBSphere &sceneSphere, 49 | const ADVector3 &pos, 50 | const ADVector3 &normal, 51 | const ADVector2 rndParam, 52 | const ADFloat time, 53 | const bool isStatic, 54 | ADVector3 &dirToLight, 55 | ADVector3 &lightContrib, 56 | ADFloat &cosAtLight, 57 | ADFloat &directPdf, 58 | ADFloat &emissionPdf); 59 | 60 | void EmitPointLight(const ADFloat *buffer, 61 | const ADBSphere &sceneSphere, 62 | const ADVector2 rndParamPos, 63 | const ADVector2 rndParamDir, 64 | const ADFloat time, 65 | const bool isStatic, 66 | ADRay &ray, 67 | ADVector3 &emission, 68 | ADFloat &cosAtLight, 69 | ADFloat &emissionPdf, 70 | ADFloat &directPdf); 71 | -------------------------------------------------------------------------------- /src/progressreporter.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | class ProgressReporter { 6 | public: 7 | ProgressReporter(uint64_t totalWork) : totalWork(totalWork), workDone(0) { 8 | } 9 | void Update(uint64_t num) { 10 | std::lock_guard lock(mutex); 11 | workDone += num; 12 | Float workRatio = (Float)workDone / (Float)totalWork; 13 | fprintf(stdout, 14 | "\r %.2f Percent Done (%llu / %llu)", 15 | workRatio * Float(100.0), 16 | (unsigned long long)workDone, 17 | (unsigned long long)totalWork); 18 | } 19 | void Done() { 20 | workDone = totalWork; 21 | fprintf(stdout, 22 | "\r %.2f Percent Done (%llu / %llu)\n", 23 | Float(100.0), 24 | (unsigned long long)workDone, 25 | (unsigned long long)totalWork); 26 | } 27 | uint64_t GetWorkDone() const { 28 | return workDone; 29 | } 30 | 31 | private: 32 | const uint64_t totalWork; 33 | uint64_t workDone; 34 | std::mutex mutex; 35 | }; 36 | -------------------------------------------------------------------------------- /src/pugiconfig.hpp: -------------------------------------------------------------------------------- 1 | /** 2 | * pugixml parser - version 1.7 3 | * -------------------------------------------------------- 4 | * Copyright (C) 2006-2015, by Arseny Kapoulkine (arseny.kapoulkine@gmail.com) 5 | * Report bugs and download new versions at http://pugixml.org/ 6 | * 7 | * This library is distributed under the MIT License. See notice at the end 8 | * of this file. 9 | * 10 | * This work is based on the pugxml parser, which is: 11 | * Copyright (C) 2003, by Kristen Wegner (kristen@tima.net) 12 | */ 13 | 14 | #ifndef HEADER_PUGICONFIG_HPP 15 | #define HEADER_PUGICONFIG_HPP 16 | 17 | // Uncomment this to enable wchar_t mode 18 | // #define PUGIXML_WCHAR_MODE 19 | 20 | // Uncomment this to enable compact mode 21 | // #define PUGIXML_COMPACT 22 | 23 | // Uncomment this to disable XPath 24 | // #define PUGIXML_NO_XPATH 25 | 26 | // Uncomment this to disable STL 27 | // #define PUGIXML_NO_STL 28 | 29 | // Uncomment this to disable exceptions 30 | // #define PUGIXML_NO_EXCEPTIONS 31 | 32 | // Set this to control attributes for public classes/functions, i.e.: 33 | // #define PUGIXML_API __declspec(dllexport) // to export all public symbols from DLL 34 | // #define PUGIXML_CLASS __declspec(dllimport) // to import all classes from DLL 35 | // #define PUGIXML_FUNCTION __fastcall // to set calling conventions to all public functions to 36 | // fastcall 37 | // In absence of PUGIXML_CLASS/PUGIXML_FUNCTION definitions PUGIXML_API is used instead 38 | 39 | // Tune these constants to adjust memory-related behavior 40 | // #define PUGIXML_MEMORY_PAGE_SIZE 32768 41 | // #define PUGIXML_MEMORY_OUTPUT_STACK 10240 42 | // #define PUGIXML_MEMORY_XPATH_PAGE_SIZE 4096 43 | 44 | // Uncomment this to switch to header-only version 45 | // #define PUGIXML_HEADER_ONLY 46 | 47 | // Uncomment this to enable long long support 48 | // #define PUGIXML_HAS_LONG_LONG 49 | 50 | #endif 51 | 52 | /** 53 | * Copyright (c) 2006-2015 Arseny Kapoulkine 54 | * 55 | * Permission is hereby granted, free of charge, to any person 56 | * obtaining a copy of this software and associated documentation 57 | * files (the "Software"), to deal in the Software without 58 | * restriction, including without limitation the rights to use, 59 | * copy, modify, merge, publish, distribute, sublicense, and/or sell 60 | * copies of the Software, and to permit persons to whom the 61 | * Software is furnished to do so, subject to the following 62 | * conditions: 63 | * 64 | * The above copyright notice and this permission notice shall be 65 | * included in all copies or substantial portions of the Software. 66 | * 67 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 68 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES 69 | * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 70 | * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT 71 | * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 72 | * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 73 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 74 | * OTHER DEALINGS IN THE SOFTWARE. 75 | */ 76 | -------------------------------------------------------------------------------- /src/quaternion.cpp: -------------------------------------------------------------------------------- 1 | #include "quaternion.h" 2 | #include "utils.h" 3 | 4 | Quaternion MakeQuaternion(const Matrix4x4 &m) { 5 | Quaternion q; 6 | const Float trace = m(0, 0) + m(1, 1) + m(2, 2); 7 | if (trace > Float(1e-7)) { 8 | Float s = sqrt(trace + 1.0); 9 | q[3] = s / Float(2.0); 10 | s = Float(0.5) / s; 11 | q[0] = (m(2, 1) - m(1, 2)) * s; 12 | q[1] = (m(0, 2) - m(2, 0)) * s; 13 | q[2] = (m(1, 0) - m(0, 1)) * s; 14 | } else { 15 | const int nxt[3] = {1, 2, 0}; 16 | Float _q[3]; 17 | int i = 0; 18 | if (m(1, 1) > m(0, 0)) 19 | i = 1; 20 | if (m(2, 2) > m(i, i)) 21 | i = 2; 22 | int j = nxt[i]; 23 | int k = nxt[j]; 24 | Float s = sqrt((m(i, i) - (m(j, j) + m(k, k))) + Float(1.0)); 25 | _q[i] = s * Float(0.5); 26 | if (s != Float(0.0)) 27 | s = Float(0.5) / s; 28 | q[3] = (m(k, j) - m(j, k)) * s; 29 | _q[j] = (m(j, i) + m(i, j)) * s; 30 | _q[k] = (m(k, i) + m(i, k)) * s; 31 | q[0] = _q[0]; 32 | q[1] = _q[1]; 33 | q[2] = _q[2]; 34 | } 35 | return q; 36 | } 37 | 38 | Quaternion Slerp(const Float &t, const Quaternion &q1, const Quaternion &_q2) { 39 | Quaternion q2 = _q2; 40 | Float cosTheta = Dot(q1, q2); 41 | if (cosTheta < Float(0.0)) { 42 | q2 = -q2; 43 | cosTheta = -cosTheta; 44 | } 45 | 46 | if (cosTheta > Float(.9995)) { 47 | return Normalize(Vector4((Float(1.0) - t) * q1 + t * q2)); 48 | } else { 49 | const Float theta = acos(Clamp(cosTheta, Float(-1.0), Float(1.0))); 50 | const Float thetap = theta * t; 51 | Quaternion qperp = Normalize(Vector4(q2 - q1 * cosTheta)); 52 | return q1 * cos(thetap) + qperp * sin(thetap); 53 | } 54 | } 55 | 56 | ADQuaternion Slerp(const ADFloat &t, const ADQuaternion &q1, const ADQuaternion &_q2) { 57 | ADFloat _cosTheta = Dot(q1, _q2); 58 | std::vector ret = CreateCondExprVec(5); 59 | BeginIf(Lt(_cosTheta, Float(0.0)), ret); 60 | { 61 | ADVector4 q2 = -_q2; 62 | ADFloat cosTheta = -_cosTheta; 63 | std::vector ret = ToADFloatVec(q2); 64 | ret.push_back(cosTheta); 65 | SetCondOutput(ret); 66 | } 67 | BeginElse(); 68 | { 69 | std::vector ret = ToADFloatVec(_q2); 70 | ret.push_back(_cosTheta); 71 | SetCondOutput(ret); 72 | } 73 | EndIf(); 74 | ADVector4 q2 = ToADVector4(ret); 75 | ADFloat cosTheta = ret[4]; 76 | 77 | ret = CreateCondExprVec(4); 78 | BeginIf(Gt(cosTheta, Float(.9995)), ret); 79 | { 80 | ADVector4 ret = Normalize(ADVector4((Float(1.0) - t) * q1 + t * q2)); 81 | SetCondOutput(ToADFloatVec(ret)); 82 | } 83 | BeginElse(); 84 | { 85 | ADFloat theta = acos(cosTheta); 86 | ADFloat thetap = theta * t; 87 | ADVector4 qperp = Normalize(ADVector4(q2 - q1 * cosTheta)); 88 | ADVector4 ret = q1 * cos(thetap) + qperp * sin(thetap); 89 | SetCondOutput(ToADFloatVec(ret)); 90 | } 91 | EndIf(); 92 | return ToADVector4(ret); 93 | } 94 | -------------------------------------------------------------------------------- /src/quaternion.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "commondef.h" 4 | #include "utils.h" 5 | 6 | using Quaternion = Vector4; 7 | using ADQuaternion = ADVector4; 8 | template 9 | using TQuaternion = TVector4; 10 | 11 | Quaternion MakeQuaternion(const Matrix4x4 &m); 12 | 13 | template 14 | TMatrix4x4 ToMatrix4x4(const TQuaternion &q) { 15 | const FloatType xx = q[0] * q[0], yy = q[1] * q[1], zz = q[2] * q[2]; 16 | const FloatType xy = q[0] * q[1], xz = q[0] * q[2], yz = q[1] * q[2]; 17 | const FloatType wx = q[0] * q[3], wy = q[1] * q[3], wz = q[2] * q[3]; 18 | 19 | TMatrix4x4 m; 20 | m(0, 0) = Float(1.0) - Float(2.0) * (yy + zz); 21 | m(0, 1) = Float(2.0) * (xy + wz); 22 | m(0, 2) = Float(2.0) * (xz - wy); 23 | m(0, 3) = Const(0.0); 24 | m(1, 0) = Float(2.0) * (xy - wz); 25 | m(1, 1) = Float(1.0) - Float(2.0) * (xx + zz); 26 | m(1, 2) = Float(2.0) * (yz + wx); 27 | m(1, 3) = Const(0.0); 28 | m(2, 0) = Float(2.0) * (xz + wy); 29 | m(2, 1) = Float(2.0) * (yz - wx); 30 | m(2, 2) = Float(1.0) - Float(2.0) * (xx + yy); 31 | m(2, 3) = Const(0.0); 32 | m(3, 0) = Const(0.0); 33 | m(3, 1) = Const(0.0); 34 | m(3, 2) = Const(0.0); 35 | m(3, 3) = Const(1.0); 36 | 37 | return m.transpose(); 38 | } 39 | 40 | Quaternion Slerp(const Float &t, const Quaternion &q1, const Quaternion &q2); 41 | ADQuaternion Slerp(const ADFloat &t, const ADQuaternion &q1, const ADQuaternion &q2); 42 | -------------------------------------------------------------------------------- /src/ray.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "commondef.h" 4 | 5 | template 6 | struct TRay { 7 | TVector3 org; 8 | TVector3 dir; 9 | }; 10 | 11 | using Ray = TRay; 12 | using ADRay = TRay; 13 | 14 | struct RaySegment { 15 | Ray ray; 16 | Float minT; 17 | Float maxT; 18 | }; 19 | -------------------------------------------------------------------------------- /src/roughdielectric.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "commondef.h" 4 | #include "bsdf.h" 5 | #include "utils.h" 6 | #include "texture.h" 7 | 8 | int GetRoughDielectricSerializedSize(); 9 | 10 | struct RoughDielectric : public BSDF { 11 | RoughDielectric(const bool /*twoSided*/, 12 | const std::shared_ptr &Ks, 13 | const std::shared_ptr &Kt, 14 | const Float &intIOR, 15 | const Float &extIOR, 16 | const std::shared_ptr &alpha) 17 | : Ks(Ks), Kt(Kt), eta(intIOR / extIOR), invEta(inverse(eta)), alpha(alpha) { 18 | } 19 | 20 | BSDFType GetType() const override { 21 | return BSDFType::RoughDielectric; 22 | } 23 | void Serialize(const Vector2 st, Float *buffer) const override; 24 | void Evaluate(const Vector3 &wi, 25 | const Vector3 &normal, 26 | const Vector3 &wo, 27 | const Vector2 st, 28 | Vector3 &contrib, 29 | Float &cosWo, 30 | Float &pdf, 31 | Float &revPdf) const override; 32 | void EvaluateAdjoint(const Vector3 &wi, 33 | const Vector3 &normal, 34 | const Vector3 &wo, 35 | const Vector2 st, 36 | Vector3 &contrib, 37 | Float &cosWo, 38 | Float &pdf, 39 | Float &revPdf) const override; 40 | bool Sample(const Vector3 &wi, 41 | const Vector3 &normal, 42 | const Vector2 st, 43 | const Vector2 rndParam, 44 | const Float uDiscrete, 45 | Vector3 &wo, 46 | Vector3 &contrib, 47 | Float &cosWo, 48 | Float &pdf, 49 | Float &revPdf) const override; 50 | bool SampleAdjoint(const Vector3 &wi, 51 | const Vector3 &normal, 52 | const Vector2 st, 53 | const Vector2 rndParam, 54 | const Float uDiscrete, 55 | Vector3 &wo, 56 | Vector3 &contrib, 57 | Float &cosWo, 58 | Float &pdf, 59 | Float &revPdf) const override; 60 | 61 | Float Roughness(const Vector2 st, const Float /*uDiscrete*/) const override { 62 | return alpha->Eval(st)[0]; 63 | } 64 | 65 | std::shared_ptr Ks; 66 | std::shared_ptr Kt; 67 | Float eta; 68 | Float invEta; 69 | std::shared_ptr alpha; 70 | }; 71 | 72 | void EvaluateRoughDielectric(const bool adjoint, 73 | const ADFloat *buffer, 74 | const ADVector3 &wi, 75 | const ADVector3 &normal, 76 | const ADVector3 &wo, 77 | const ADVector2 st, 78 | ADVector3 &contrib, 79 | ADFloat &cosWo, 80 | ADFloat &pdf, 81 | ADFloat &revPdf); 82 | 83 | 84 | void SampleRoughDielectric(const bool adjoint, 85 | const ADFloat *buffer, 86 | const ADVector3 &wi, 87 | const ADVector3 &normal, 88 | const ADVector2 st, 89 | const ADVector2 rndParam, 90 | const ADFloat uDiscrete, 91 | const bool fixDiscrete, 92 | ADVector3 &wo, 93 | ADVector3 &contrib, 94 | ADFloat &cosWo, 95 | ADFloat &pdf, 96 | ADFloat &revPdf); 97 | -------------------------------------------------------------------------------- /src/sampling.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "commondef.h" 4 | #include "utils.h" 5 | 6 | template 7 | inline TVector3 SampleSphere(const TVector2 coord, FloatType &jacobian) { 8 | const FloatType scaledTheta = c_TWOPI * coord[0]; 9 | const FloatType scaledPhi = c_PI * coord[1]; 10 | const FloatType sinPhi = sin(scaledPhi); 11 | const FloatType cosPhi = cos(scaledPhi); 12 | TVector3 dir(sinPhi * cos(scaledTheta), sinPhi * sin(scaledTheta), cosPhi); 13 | jacobian = fabs(sinPhi); 14 | return dir; 15 | } 16 | 17 | template 18 | inline TVector3 SampleSphere(const TVector2 coord) { 19 | FloatType jacobian; 20 | return SampleSphere(coord, jacobian); 21 | } 22 | 23 | inline Float patan2(const Float y, const Float x) { 24 | if (y == Float(0.0) && x == Float(0.0)) { 25 | return Float(0.0); 26 | } 27 | Float result = atan2(y, x); 28 | if (result < 0.0) { 29 | result += Float(2.0 * M_PI); 30 | } 31 | 32 | return result; 33 | } 34 | 35 | inline Vector2 ToSphericalCoord(const Vector3 &dir, Float &jacobian) { 36 | Float theta = patan2(dir[1], dir[0]) * c_INVTWOPI; 37 | Float phi = acos(dir[2]); 38 | jacobian = fabs(sin(phi)); 39 | phi *= c_INVPI; 40 | return Vector2(theta, phi); 41 | } 42 | 43 | inline Vector2 ToSphericalCoord(const Vector3 &dir) { 44 | Float jacobian; 45 | return ToSphericalCoord(dir, jacobian); 46 | } 47 | 48 | inline Vector2 SampleConcentricDisc(const Vector2 rndParam) { 49 | Float r1 = Float(2.0) * rndParam[0] - Float(1.0); 50 | Float r2 = Float(2.0) * rndParam[1] - Float(1.0); 51 | 52 | // http://psgraphics.blogspot.ch/2011/01/improved-code-for-concentric-map.html 53 | Float phi, r; 54 | if (r1 == 0 || r2 == 0) { 55 | r = phi = 0; 56 | } else if (square(r1) > square(r2)) { 57 | r = r1; 58 | phi = c_PIOVERFOUR * (r2 / r1); 59 | } else { 60 | r = r2; 61 | phi = c_PIOVERTWO - (r1 / r2) * c_PIOVERFOUR; 62 | } 63 | 64 | Float sinPhi = sin(phi); 65 | Float cosPhi = cos(phi); 66 | 67 | return Vector2(r * cosPhi, r * sinPhi); 68 | } 69 | 70 | inline ADVector2 SampleConcentricDisc(const ADVector2 rndParam) { 71 | ADFloat r1 = Float(2.0) * rndParam[0] - Float(1.0); 72 | ADFloat r2 = Float(2.0) * rndParam[1] - Float(1.0); 73 | 74 | std::vector ret = CreateCondExprVec(2); 75 | BeginIf(Eq(r1, Float(0.0)), ret); 76 | { SetCondOutput({Const(0.0), Const(0.0)}); } 77 | BeginElseIf(Eq(r2, Float(0.0))); 78 | { SetCondOutput({Const(0.0), Const(0.0)}); } 79 | BeginElseIf(Gt(square(r1), square(r2))); 80 | { 81 | ADFloat r = r1; 82 | ADFloat phi = c_PIOVERFOUR * (r2 / r1); 83 | SetCondOutput({r, phi}); 84 | } 85 | BeginElse(); 86 | { 87 | ADFloat r = r2; 88 | ADFloat phi = c_PIOVERTWO - (r1 / r2) * c_PIOVERFOUR; 89 | SetCondOutput({r, phi}); 90 | } 91 | EndIf(); 92 | ADFloat r = ret[0]; 93 | ADFloat phi = ret[1]; 94 | 95 | ADFloat sinPhi = sin(phi); 96 | ADFloat cosPhi = cos(phi); 97 | 98 | return ADVector2(r * cosPhi, r * sinPhi); 99 | } 100 | 101 | template 102 | inline TVector3 SampleCosHemisphere(const TVector2 rndParam) { 103 | FloatType phi = c_TWOPI * rndParam[0]; 104 | FloatType tmp = sqrt(fmax(Float(1.0) - rndParam[1], ADEpsilon())); 105 | TVector3 ret( 106 | cos(phi) * tmp, sin(phi) * tmp, sqrt(fmax(rndParam[1], ADEpsilon()))); 107 | return ret; 108 | } 109 | -------------------------------------------------------------------------------- /src/scene.cpp: -------------------------------------------------------------------------------- 1 | #include "scene.h" 2 | #include "distribution.h" 3 | #include "shape.h" 4 | #include "light.h" 5 | #include "camera.h" 6 | #include "bounds.h" 7 | 8 | Scene::Scene(const std::shared_ptr &options, 9 | const std::shared_ptr &camera, 10 | const std::vector> &objects, 11 | const std::vector> &lights, 12 | const std::shared_ptr &envLight, 13 | const std::string &outputName) 14 | : options(options), 15 | camera(camera), 16 | objects(objects), 17 | lights(lights), 18 | envLight(envLight), 19 | outputName(outputName) { 20 | // Build light cdf 21 | std::vector weights(lights.size()); 22 | lightWeightSum = Float(0.0); 23 | for (size_t i = 0; i < weights.size(); i++) { 24 | weights[i] = lights[i]->samplingWeight; 25 | lightWeightSum += weights[i]; 26 | } 27 | lightDist = 28 | std::unique_ptr(new PiecewiseConstant1D(&weights[0], weights.size())); 29 | rtcDevice = rtcNewDevice(NULL); 30 | rtcScene = rtcDeviceNewScene( 31 | rtcDevice, 32 | RTC_SCENE_STATIC | RTC_SCENE_INCOHERENT | RTC_SCENE_HIGH_QUALITY | RTC_SCENE_ROBUST, 33 | RTC_INTERSECT1); 34 | BBox bbox; 35 | for (auto &obj : objects) { 36 | obj->RtcRegister(rtcScene); 37 | bbox = Merge(bbox, obj->GetBBox()); 38 | } 39 | bSphere = BSphere(bbox); 40 | rtcCommit(rtcScene); 41 | } 42 | 43 | Scene::~Scene() { 44 | rtcDeleteScene(rtcScene); 45 | rtcDeleteDevice(rtcDevice); 46 | } 47 | 48 | static inline RTCRay ToRTCRay(const Float time, const RaySegment &raySeg) { 49 | const Ray &ray = raySeg.ray; 50 | RTCRay rtcRay; 51 | for (int i = 0; i < 3; i++) { 52 | assert(std::isfinite(ray.org[i])); 53 | assert(std::isfinite(ray.dir[i])); 54 | rtcRay.org[i] = float(ray.org[i]); 55 | rtcRay.dir[i] = float(ray.dir[i]); 56 | } 57 | rtcRay.tnear = float(raySeg.minT); 58 | rtcRay.tfar = float(raySeg.maxT); 59 | rtcRay.geomID = RTC_INVALID_GEOMETRY_ID; 60 | rtcRay.primID = RTC_INVALID_GEOMETRY_ID; 61 | rtcRay.instID = RTC_INVALID_GEOMETRY_ID; 62 | rtcRay.mask = 0xFFFFFFFF; 63 | rtcRay.time = float(time); 64 | return rtcRay; 65 | } 66 | 67 | bool Intersect(const Scene *scene, 68 | const Float time, 69 | const RaySegment &raySeg, 70 | ShapeInst &shapeInst) { 71 | RTCRay rtcRay = ToRTCRay(time, raySeg); 72 | rtcIntersect(scene->rtcScene, rtcRay); 73 | if (rtcRay.geomID == RTC_INVALID_GEOMETRY_ID) { 74 | return false; 75 | } 76 | 77 | shapeInst.obj = scene->objects[rtcRay.geomID].get(); 78 | shapeInst.primID = rtcRay.primID; 79 | return true; 80 | } 81 | 82 | bool Occluded(const Scene *scene, const Float time, const Ray &ray, const Float dist) { 83 | Float minT, maxT; 84 | if (dist == std::numeric_limits::infinity()) { 85 | minT = c_IsectEpsilon; 86 | maxT = std::numeric_limits::infinity(); 87 | } else { 88 | minT = c_IsectEpsilon; 89 | maxT = (Float(1.0) - c_ShadowEpsilon) * dist; 90 | } 91 | return Occluded(scene, time, RaySegment{ray, minT, maxT}); 92 | } 93 | 94 | bool Occluded(const Scene *scene, const Float time, const RaySegment &raySeg) { 95 | RTCRay rtcRay = ToRTCRay(time, raySeg); 96 | rtcOccluded(scene->rtcScene, rtcRay); 97 | return rtcRay.geomID == 0; 98 | } 99 | 100 | const Light *PickLight(const Scene *scene, const Float u, Float &prob) { 101 | int lightId = scene->lightDist->SampleDiscrete(u, &prob); 102 | return scene->lights[lightId].get(); 103 | } 104 | 105 | const Float PickLightProb(const Scene *scene, const Light *light) { 106 | return light->samplingWeight / scene->lightWeightSum; 107 | } 108 | 109 | int GetSceneSerializedSize() { 110 | return 1 + GetCameraSerializedSize() + GetBSphereSerializedSize(); 111 | } 112 | 113 | Float *Serialize(const Scene *scene, Float *buffer) { 114 | buffer = Serialize(BoolToFloat(scene->options->useLightCoordinateSampling), buffer); 115 | buffer = Serialize(scene->camera.get(), buffer); 116 | buffer = Serialize(scene->bSphere, buffer); 117 | return buffer; 118 | } 119 | -------------------------------------------------------------------------------- /src/scene.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "commondef.h" 4 | #include "bounds.h" 5 | #include "ray.h" 6 | #include "dptoptions.h" 7 | 8 | #include 9 | #include 10 | 11 | #include 12 | #include 13 | 14 | struct Camera; 15 | struct Shape; 16 | struct Light; 17 | struct PiecewiseConstant1D; 18 | struct ShapeInst; 19 | struct EnvLight; 20 | 21 | struct Scene { 22 | Scene(const std::shared_ptr &options, 23 | const std::shared_ptr &camera, 24 | const std::vector> &objects, 25 | const std::vector> &lights, 26 | const std::shared_ptr &envLight, 27 | const std::string &outputName); 28 | ~Scene(); 29 | 30 | std::shared_ptr options; 31 | std::shared_ptr camera; 32 | std::vector> objects; 33 | std::vector> lights; 34 | std::unique_ptr lightDist; 35 | std::shared_ptr envLight; 36 | BSphere bSphere; 37 | 38 | std::string outputName; 39 | 40 | RTCDevice rtcDevice; 41 | RTCScene rtcScene; 42 | 43 | Float lightWeightSum; 44 | }; 45 | 46 | bool Intersect(const Scene *scene, 47 | const Float time, 48 | const RaySegment &raySeg, 49 | ShapeInst &shapeInst); 50 | 51 | bool Occluded(const Scene *scene, const Float time, const Ray &ray, const Float dist); 52 | bool Occluded(const Scene *scene, const Float time, const RaySegment &raySeg); 53 | 54 | const Light *PickLight(const Scene *scene, const Float u, Float &prob); 55 | 56 | const Float PickLightProb(const Scene *scene, const Light *light); 57 | 58 | int GetSceneSerializedSize(); 59 | 60 | Float *Serialize(const Scene *scene, Float *buffer); 61 | -------------------------------------------------------------------------------- /src/shape.cpp: -------------------------------------------------------------------------------- 1 | #include "shape.h" 2 | #include "trianglemesh.h" 3 | #include "utils.h" 4 | 5 | int GetMaxShapeSerializedSize() { 6 | return GetTriangleMeshSerializedSize(); 7 | } 8 | 9 | void Shape::SetAreaLight(const AreaLight *areaLight) { 10 | this->areaLight = areaLight; 11 | } 12 | 13 | const ADFloat *Intersect(const ADFloat *buffer, 14 | const ADRay &ray, 15 | const ADFloat time, 16 | const bool isStatic, 17 | ADIntersection &isect, 18 | ADVector2 &st) { 19 | ADFloat type; 20 | buffer = Deserialize(buffer, type); 21 | std::vector ret = CreateCondExprVec(11); 22 | BeginIf(Eq(type, (Float)ShapeType::TriangleMesh), ret); 23 | { 24 | ADIntersection isect; 25 | IntersectTriangleMesh(buffer, ray, time, isStatic, isect, st); 26 | SetCondOutput({isect.position[0], 27 | isect.position[1], 28 | isect.position[2], 29 | isect.geomNormal[0], 30 | isect.geomNormal[1], 31 | isect.geomNormal[2], 32 | isect.shadingNormal[0], 33 | isect.shadingNormal[1], 34 | isect.shadingNormal[2], 35 | st[0], 36 | st[1]}); 37 | } 38 | BeginElse(); 39 | { 40 | SetCondOutput({Const(0.0), 41 | Const(0.0), 42 | Const(0.0), 43 | Const(0.0), 44 | Const(0.0), 45 | Const(0.0), 46 | Const(0.0), 47 | Const(0.0), 48 | Const(0.0), 49 | Const(0.0), 50 | Const(0.0)}); 51 | } 52 | EndIf(); 53 | isect.position[0] = ret[0]; 54 | isect.position[1] = ret[1]; 55 | isect.position[2] = ret[2]; 56 | isect.geomNormal[0] = ret[3]; 57 | isect.geomNormal[1] = ret[4]; 58 | isect.geomNormal[2] = ret[5]; 59 | isect.shadingNormal[0] = ret[6]; 60 | isect.shadingNormal[1] = ret[7]; 61 | isect.shadingNormal[2] = ret[8]; 62 | st[0] = ret[9]; 63 | st[1] = ret[10]; 64 | buffer += (GetMaxShapeSerializedSize() - 1); 65 | return buffer; 66 | } 67 | 68 | void SampleShape(const ADFloat *buffer, 69 | const ADVector2 rndParam, 70 | const ADFloat time, 71 | const bool isStatic, 72 | ADVector3 &position, 73 | ADVector3 &normal, 74 | ADFloat &pdf) { 75 | ADFloat type; 76 | buffer = Deserialize(buffer, type); 77 | std::vector ret = CreateCondExprVec(7); 78 | BeginIf(Eq(type, (Float)ShapeType::TriangleMesh), ret); 79 | { 80 | ADVector3 position; 81 | ADVector3 normal; 82 | ADFloat pdf; 83 | SampleTriangleMesh(buffer, rndParam, time, isStatic, position, normal, pdf); 84 | SetCondOutput( 85 | {position[0], position[1], position[2], normal[0], normal[1], normal[2], pdf}); 86 | } 87 | BeginElse(); 88 | { 89 | SetCondOutput({Const(0.0), 90 | Const(0.0), 91 | Const(0.0), 92 | Const(0.0), 93 | Const(0.0), 94 | Const(0.0), 95 | Const(0.0)}); 96 | } 97 | EndIf(); 98 | position[0] = ret[0]; 99 | position[1] = ret[1]; 100 | position[2] = ret[2]; 101 | normal[0] = ret[3]; 102 | normal[1] = ret[4]; 103 | normal[2] = ret[5]; 104 | pdf = ret[6]; 105 | } 106 | 107 | const ADFloat *SampleShapePdf(const ADFloat *buffer, ADFloat &shapePdf) { 108 | ADFloat type; 109 | buffer = Deserialize(buffer, type); 110 | std::vector ret = CreateCondExprVec(1); 111 | BeginIf(Eq(type, (Float)ShapeType::TriangleMesh), ret); 112 | { 113 | ADFloat shapePdf = SampleTriangleMeshPdf(buffer); 114 | SetCondOutput({shapePdf}); 115 | } 116 | BeginElse(); 117 | { SetCondOutput({Const(0.0)}); } 118 | EndIf(); 119 | shapePdf = ret[0]; 120 | buffer += (GetMaxShapeSerializedSize() - 1); 121 | return buffer; 122 | } 123 | -------------------------------------------------------------------------------- /src/shape.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "commondef.h" 4 | #include "bounds.h" 5 | #include "ray.h" 6 | 7 | #include 8 | #include 9 | 10 | struct AreaLight; 11 | struct BSDF; 12 | 13 | enum class ShapeType { TriangleMesh }; 14 | 15 | int GetMaxShapeSerializedSize(); 16 | 17 | template 18 | struct TIntersection { 19 | TVector3 position; 20 | TVector3 shadingNormal; 21 | TVector3 geomNormal; 22 | }; 23 | 24 | using Intersection = TIntersection; 25 | using ADIntersection = TIntersection; 26 | 27 | struct Shape { 28 | Shape(const std::shared_ptr bsdf) : bsdf(bsdf), areaLight(nullptr) { 29 | } 30 | virtual ShapeType GetType() const = 0; 31 | virtual ShapeID RtcRegister(const RTCScene &scene) const = 0; 32 | virtual void Serialize(const PrimID primID, Float *buffer) const = 0; 33 | virtual bool Intersect(const PrimID &primID, 34 | const Float time, 35 | const RaySegment &raySeg, 36 | Intersection &isect, 37 | Vector2 &st) const = 0; 38 | // Find sampleParam so that Shape::Sample(sampleParam) returns the same position 39 | virtual Vector2 GetSampleParam(const PrimID &primID, 40 | const Vector3 &position, 41 | const Float time) const = 0; 42 | virtual void SetAreaLight(const AreaLight *areaLight); 43 | virtual PrimID Sample(const Float u) const = 0; 44 | virtual void Sample(const Vector2 rndParam, 45 | const Float time, 46 | const PrimID primID, 47 | Vector3 &position, 48 | Vector3 &normal, 49 | Float *pdf) const = 0; 50 | virtual Float SamplePdf() const = 0; 51 | virtual BBox GetBBox() const = 0; 52 | virtual bool IsMoving() const = 0; 53 | 54 | const std::shared_ptr bsdf; 55 | const AreaLight *areaLight; 56 | }; 57 | 58 | struct ShapeInst { 59 | const Shape *obj; 60 | PrimID primID; 61 | Vector2 st; 62 | }; 63 | 64 | const ADFloat *Intersect(const ADFloat *buffer, 65 | const ADRay &ray, 66 | const ADFloat time, 67 | const bool isStatic, 68 | ADIntersection &isect, 69 | ADVector2 &st); 70 | 71 | void SampleShape(const ADFloat *buffer, 72 | const ADVector2 rndParam, 73 | const ADFloat time, 74 | const bool isStatic, 75 | ADVector3 &position, 76 | ADVector3 &normal, 77 | ADFloat &pdf); 78 | 79 | const ADFloat *SampleShapePdf(const ADFloat *buffer, ADFloat &shapePdf); 80 | -------------------------------------------------------------------------------- /src/texture.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "commondef.h" 4 | #include 5 | #include 6 | 7 | template 8 | class Texture : public std::enable_shared_from_this> { 9 | public: 10 | virtual TVector Eval(const Vector2 st) const = 0; 11 | virtual TVector Avg() const = 0; 12 | virtual std::string Name() const = 0; 13 | virtual std::shared_ptr> ToTexture1D() const = 0; 14 | virtual std::shared_ptr> ToTexture3D() const = 0; 15 | }; 16 | 17 | using Texture1D = Texture<1>; 18 | using Texture3D = Texture<3>; 19 | using TextureRGB = Texture<3>; 20 | -------------------------------------------------------------------------------- /src/texturesystem.cpp: -------------------------------------------------------------------------------- 1 | #include "texturesystem.h" 2 | 3 | OpenImageIO::TextureSystem* TextureSystem::s_TextureSystem = nullptr; -------------------------------------------------------------------------------- /src/texturesystem.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | class TextureSystem { 6 | public: 7 | static void Init() { 8 | s_TextureSystem = OpenImageIO::TextureSystem::create(); 9 | s_TextureSystem->attribute("autotile", 64); 10 | s_TextureSystem->attribute("automip", 1); 11 | s_TextureSystem->attribute("forcefloat", 1); 12 | } 13 | static void Destroy() { 14 | OpenImageIO::TextureSystem::destroy(s_TextureSystem); 15 | } 16 | 17 | private: 18 | static OpenImageIO::TextureSystem *s_TextureSystem; 19 | template 20 | friend class BitmapTexture; 21 | }; 22 | -------------------------------------------------------------------------------- /src/timer.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "commondef.h" 4 | 5 | #include 6 | #include 7 | 8 | struct Timer { 9 | std::chrono::time_point last; 10 | }; 11 | 12 | inline Float Tick(Timer &timer) { 13 | std::chrono::time_point now = std::chrono::system_clock::now(); 14 | std::chrono::duration elapsed = now - timer.last; 15 | Float ret = elapsed.count(); 16 | timer.last = now; 17 | return ret; 18 | } 19 | -------------------------------------------------------------------------------- /src/transform.cpp: -------------------------------------------------------------------------------- 1 | #include "transform.h" 2 | #include "utils.h" 3 | 4 | Matrix4x4 Scale(const Vector3 &scale) { 5 | Matrix4x4 m; 6 | m << scale[0], 0, 0, 0, 0, scale[1], 0, 0, 0, 0, scale[2], 0, 0, 0, 0, 1; 7 | return m; 8 | } 9 | 10 | Matrix4x4 Rotate(const Float angle, const Vector3 &axis) { 11 | const Vector3 a = Normalize(axis); 12 | const Float s = sin(Radians(angle)); 13 | const Float c = cos(Radians(angle)); 14 | Float m[4][4]; 15 | 16 | m[0][0] = a[0] * a[0] + (Float(1.0) - a[0] * a[0]) * c; 17 | m[0][1] = a[0] * a[1] * (Float(1.0) - c) - a[2] * s; 18 | m[0][2] = a[0] * a[2] * (Float(1.0) - c) + a[1] * s; 19 | m[0][3] = 0; 20 | 21 | m[1][0] = a[0] * a[1] * (Float(1.0) - c) + a[2] * s; 22 | m[1][1] = a[1] * a[1] + (Float(1.0) - a[1] * a[1]) * c; 23 | m[1][2] = a[1] * a[2] * (Float(1.0) - c) - a[0] * s; 24 | m[1][3] = 0; 25 | 26 | m[2][0] = a[0] * a[2] * (Float(1.0) - c) - a[1] * s; 27 | m[2][1] = a[1] * a[2] * (Float(1.0) - c) + a[0] * s; 28 | m[2][2] = a[2] * a[2] + (Float(1.0) - a[2] * a[2]) * c; 29 | m[2][3] = 0; 30 | 31 | m[3][0] = 0; 32 | m[3][1] = 0; 33 | m[3][2] = 0; 34 | m[3][3] = 1; 35 | 36 | Matrix4x4 mat; 37 | mat << m[0][0], m[0][1], m[0][2], m[0][3], m[1][0], m[1][1], m[1][2], m[1][3], m[2][0], m[2][1], 38 | m[2][2], m[2][3], m[3][0], m[3][1], m[3][2], m[3][3]; 39 | return mat; 40 | } 41 | 42 | Matrix4x4 LookAt(const Vector3 &pos, const Vector3 &look, const Vector3 &up) { 43 | Float m[4][4]; 44 | // Initialize fourth column of viewing matrix 45 | m[0][3] = pos[0]; 46 | m[1][3] = pos[1]; 47 | m[2][3] = pos[2]; 48 | m[3][3] = 1; 49 | 50 | // Initialize first three columns of viewing matrix 51 | Vector3 dir = Normalize(Vector3(look - pos)); 52 | if (Length(Cross(Normalize(up), dir)) == 0) { 53 | throw std::runtime_error( 54 | "[Lookat] Up vector and viewing direction passed" 55 | " are point in the same direction"); 56 | return Matrix4x4::Zero(); 57 | } 58 | Vector3 left = Normalize(Cross(Normalize(up), dir)); 59 | Vector3 newUp = Cross(dir, left); 60 | m[0][0] = left[0]; 61 | m[1][0] = left[1]; 62 | m[2][0] = left[2]; 63 | m[3][0] = 0.; 64 | m[0][1] = newUp[0]; 65 | m[1][1] = newUp[1]; 66 | m[2][1] = newUp[2]; 67 | m[3][1] = 0.; 68 | m[0][2] = dir[0]; 69 | m[1][2] = dir[1]; 70 | m[2][2] = dir[2]; 71 | m[3][2] = 0.; 72 | 73 | Matrix4x4 mat; 74 | mat << m[0][0], m[0][1], m[0][2], m[0][3], m[1][0], m[1][1], m[1][2], m[1][3], m[2][0], m[2][1], 75 | m[2][2], m[2][3], m[3][0], m[3][1], m[3][2], m[3][3]; 76 | return mat; 77 | } 78 | 79 | Matrix4x4 Perspective(const Float fov, const Float clipNear, const Float clipFar) { 80 | Float recip = Float(1.0) / (clipFar - clipNear); 81 | 82 | /* Perform a scale so that the field of view is mapped 83 | * to the interval [-1, 1] */ 84 | Float cot = Float(1.0) / tan(Radians(fov / 2.0)); 85 | 86 | Matrix4x4 m; 87 | m << cot, 0, 0, 0, 0, cot, 0, 0, 0, 0, clipFar *recip, -clipNear *clipFar *recip, 0, 0, 1, 0; 88 | 89 | return m; 90 | } 91 | -------------------------------------------------------------------------------- /src/transform.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "commondef.h" 4 | #include "quaternion.h" 5 | #include "utils.h" 6 | 7 | template 8 | TMatrix4x4 Translate(const TVector3 &delta) { 9 | TMatrix4x4 m; 10 | m << Const(1.0), Const(0.0), Const(0.0), delta[0], Const(0.0), Const(1.0), 11 | Const(0.0), delta[1], Const(0.0), Const(0.0), Const(1.0), delta[2], 12 | Const(0.0), Const(0.0), Const(0.0), Const(1.0); 13 | return m; 14 | } 15 | Matrix4x4 Scale(const Vector3 &scale); 16 | Matrix4x4 Rotate(const Float angle, const Vector3 &axis); 17 | Matrix4x4 LookAt(const Vector3 &pos, const Vector3 &look, const Vector3 &up); 18 | Matrix4x4 Perspective(const Float fov, const Float clipNear, const Float clipFar); 19 | 20 | template 21 | inline TVector3 XformPoint(const TMatrix4x4 &xform, 22 | const TVector3 &pt) { 23 | TVector4 tpt( 24 | xform(0, 0) * pt[0] + xform(0, 1) * pt[1] + xform(0, 2) * pt[2] + xform(0, 3), 25 | xform(1, 0) * pt[0] + xform(1, 1) * pt[1] + xform(1, 2) * pt[2] + xform(1, 3), 26 | xform(2, 0) * pt[0] + xform(2, 1) * pt[1] + xform(2, 2) * pt[2] + xform(2, 3), 27 | xform(3, 0) * pt[0] + xform(3, 1) * pt[1] + xform(3, 2) * pt[2] + xform(3, 3)); 28 | FloatType invW = inverse(tpt[3]); 29 | return TVector3(tpt[0] * invW, tpt[1] * invW, tpt[2] * invW); 30 | } 31 | 32 | template 33 | inline TVector3 XformVector(const TMatrix4x4 &xform, 34 | const TVector3 &vec) { 35 | return TVector3(xform(0, 0) * vec[0] + xform(0, 1) * vec[1] + xform(0, 2) * vec[2], 36 | xform(1, 0) * vec[0] + xform(1, 1) * vec[1] + xform(1, 2) * vec[2], 37 | xform(2, 0) * vec[0] + xform(2, 1) * vec[1] + xform(2, 2) * vec[2]); 38 | } 39 | 40 | template 41 | inline TVector3 XformNormal(const TMatrix4x4 &invXform, 42 | const TVector3 &vec) { 43 | return TVector3( 44 | invXform(0, 0) * vec[0] + invXform(1, 0) * vec[1] + invXform(2, 0) * vec[2], 45 | invXform(0, 1) * vec[0] + invXform(1, 1) * vec[1] + invXform(2, 1) * vec[2], 46 | invXform(0, 2) * vec[0] + invXform(1, 2) * vec[1] + invXform(2, 2) * vec[2]); 47 | } 48 | -------------------------------------------------------------------------------- /src/trianglemesh.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "shape.h" 4 | #include "distribution.h" 5 | 6 | int GetTriangleMeshSerializedSize(); 7 | 8 | struct TriIndex { 9 | TriIndex() { 10 | } 11 | TriIndex(const TriIndexID id0, const TriIndexID id1, const TriIndexID id2) { 12 | index[0] = id0; 13 | index[1] = id1; 14 | index[2] = id2; 15 | } 16 | TriIndexID index[3]; 17 | }; 18 | 19 | struct TriMeshData { 20 | std::vector position0; 21 | std::vector position1; 22 | std::vector normal0; 23 | std::vector normal1; 24 | std::vector st; 25 | std::vector colors; 26 | std::vector indices; 27 | bool isMoving; 28 | }; 29 | 30 | struct TriangleMesh : public Shape { 31 | TriangleMesh(const std::shared_ptr bsdf, const std::shared_ptr data); 32 | ShapeType GetType() const override { 33 | return ShapeType::TriangleMesh; 34 | } 35 | ShapeID RtcRegister(const RTCScene &scene) const override; 36 | void Serialize(const PrimID primID, Float *buffer) const override; 37 | bool Intersect(const PrimID &primID, 38 | const Float time, 39 | const RaySegment &raySeg, 40 | Intersection &isect, 41 | Vector2 &st) const override; 42 | Vector2 GetSampleParam(const PrimID &primID, 43 | const Vector3 &position, 44 | const Float time) const override; 45 | void SetAreaLight(const AreaLight *areaLight) override; 46 | PrimID Sample(const Float u) const override; 47 | void Sample(const Vector2 rndParam, 48 | const Float time, 49 | const PrimID primID, 50 | Vector3 &position, 51 | Vector3 &normal, 52 | Float *pdf) const override; 53 | Float SamplePdf() const override { 54 | return inverse(totalArea); 55 | } 56 | BBox GetBBox() const override { 57 | return bbox; 58 | } 59 | bool IsMoving() const override { 60 | return data->isMoving; 61 | } 62 | 63 | const std::shared_ptr data; 64 | const BBox bbox; 65 | // Only used when the mesh is associated with an area light 66 | Float totalArea; 67 | std::unique_ptr areaDist; 68 | }; 69 | 70 | void IntersectTriangleMesh(const ADFloat *buffer, 71 | const ADRay &ray, 72 | const ADFloat time, 73 | const bool isStatic, 74 | ADIntersection &isect, 75 | ADVector2 &st); 76 | 77 | void SampleTriangleMesh(const ADFloat *buffer, 78 | const ADVector2 rndParam, 79 | const ADFloat time, 80 | const bool isStatic, 81 | ADVector3 &position, 82 | ADVector3 &normal, 83 | ADFloat &pdf); 84 | 85 | ADFloat SampleTriangleMeshPdf(const ADFloat *buffer); 86 | --------------------------------------------------------------------------------