├── .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