├── .gitignore ├── .gitmodules ├── CMakeLists.txt ├── README.md ├── cuda ├── host │ ├── NEE.cuh │ ├── knn.cuh │ └── util.cuh ├── kernel │ ├── NEE.cu │ ├── knn.cu │ └── util.cu ├── psdr_jit.cpp ├── psdr_jit.cu └── psdr_jit.h ├── dev_notes.md ├── include ├── misc │ └── Exception.h ├── psdr │ ├── bsdf │ │ ├── bsdf.h │ │ ├── diffuse.h │ │ ├── ggx.h │ │ ├── microfacet.h │ │ ├── microfacet_pv.h │ │ ├── normalmap.h │ │ ├── roughconductor.h │ │ └── roughdielectric.h │ ├── constants.h │ ├── core │ │ ├── AQ_distrb.h │ │ ├── bitmap.h │ │ ├── bitmap_loader.h │ │ ├── cube_distrb.h │ │ ├── frame.h │ │ ├── intersection.h │ │ ├── miniz.h │ │ ├── pmf.h │ │ ├── ray.h │ │ ├── records.h │ │ ├── sampler.h │ │ ├── tinyexr.h │ │ ├── transform.h │ │ └── warp.h │ ├── edge │ │ └── edge.h │ ├── emitter │ │ ├── area.h │ │ ├── emitter.h │ │ └── envmap.h │ ├── fwd.h │ ├── integrator │ │ ├── collocated.h │ │ ├── direct.h │ │ ├── field.h │ │ ├── integrator.h │ │ └── path.h │ ├── jit_optix_test.h │ ├── macros.h │ ├── object.h │ ├── optix │ │ └── ptx.h │ ├── optix_stubs.h │ ├── psdr.h │ ├── scene │ │ ├── scene.h │ │ ├── scene_loader.h │ │ └── scene_optix.h │ ├── sensor │ │ ├── orthographic.h │ │ ├── perspective.h │ │ └── sensor.h │ ├── shape │ │ └── mesh.h │ ├── types.h │ └── utils.h ├── pugixml │ ├── pugiconfig.hpp │ ├── pugixml.cpp │ └── pugixml.hpp └── tiny_obj_loader │ └── tiny_obj_loader.h ├── pyproject.toml ├── src ├── bsdf │ ├── diffuse.cpp │ ├── ggx.cpp │ ├── microfacet.cpp │ ├── microfacet_pv.cpp │ ├── normalmap.cpp │ ├── roughconductor.cpp │ └── roughdielectric.cpp ├── core │ ├── AQ_distrb.cpp │ ├── bitmap.cpp │ ├── bitmap_loader.cpp │ ├── cube_distrb.cpp │ ├── miniz.cpp │ ├── pmf.cpp │ └── sampler.cpp ├── emitter │ ├── area.cpp │ └── envmap.cpp ├── integrator │ ├── collocated.cpp │ ├── direct.cpp │ ├── field.cpp │ ├── integrator.cpp │ └── path.cpp ├── jit_optix_test.cpp ├── optix │ └── ptx.cpp ├── optix_stubs.cpp ├── psdr.cpp ├── psdr_jit │ └── __init__.py ├── scene │ ├── scene.cpp │ ├── scene_loader.cpp │ └── scene_optix.cpp ├── sensor │ ├── orthographic.cpp │ ├── perspective.cpp │ └── sensor.cpp └── shape │ └── mesh.cpp └── tutorials ├── Forward_AD.ipynb ├── Forward_AD_envmap.ipynb ├── batch_render.ipynb ├── data ├── cbox │ ├── cbox_back.obj │ ├── cbox_ceiling.obj │ ├── cbox_floor.obj │ ├── cbox_greenwall.obj │ ├── cbox_largeball.obj │ ├── cbox_largebox.obj │ ├── cbox_luminaire.obj │ ├── cbox_redwall.obj │ ├── cbox_smallball.obj │ └── cbox_smallbox.obj ├── cube.obj ├── envmap │ └── ballroom_1k.exr ├── mesh │ └── bunny_low.obj ├── texture │ ├── illya.exr │ └── wood.exr └── uv_plane.obj ├── different_integrator.ipynb ├── image_util.py └── secondary_edge_guiding.ipynb /.gitignore: -------------------------------------------------------------------------------- 1 | *.o 2 | *.so 3 | *.log 4 | *.exe 5 | __pycache__ 6 | build/ 7 | ext_win64/ 8 | local/ 9 | examples/ 10 | dist/ 11 | tmp/ 12 | **/*.log 13 | unit_test/*.exr 14 | unit_test/inv_test/ 15 | unit_test/result/ 16 | .ipynb_checkpoints 17 | .vscode/ 18 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "ext/drjit"] 2 | path = ext/drjit 3 | url = https://github.com/mitsuba-renderer/drjit 4 | ignore = untracked -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.15...3.22) 2 | 3 | project(${SKBUILD_PROJECT_NAME} VERSION ${SKBUILD_PROJECT_VERSION}) 4 | 5 | set(CMAKE_CXX_STANDARD 17) 6 | set(CMAKE_CXX_STANDARD_REQUIRED ON) 7 | 8 | if( WIN32 ) 9 | add_definitions(-D_USE_MATH_DEFINES -D_CRT_SECURE_NO_WARNINGS -DNDEBUG) 10 | else() 11 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -O3 -DNDEBUG -Wall -Wno-reorder -Wno-sign-compare -fPIC") 12 | endif() 13 | 14 | option(DRJIT_ENABLE_JIT "" ON) 15 | option(DRJIT_ENABLE_AUTODIFF "" ON) 16 | option(DRJIT_ENABLE_PYTHON "" ON) 17 | 18 | set(DRJIT_DIR ${CMAKE_SOURCE_DIR}/ext/drjit) 19 | set(PYBIND11_ROOT ${DRJIT_DIR}/ext/pybind11) 20 | 21 | add_subdirectory(${PYBIND11_ROOT}) 22 | 23 | option(BUILD_SHARED_LIBS "Build shared libraries" ON) 24 | 25 | option(CUDA_REMOVE_GLOBAL_MEMORY_SPACE_WARNING 26 | "Suppress the \"Advisory: Cannot tell what pointer points to, assuming global memory space\" warning nvcc makes." ON) 27 | option(CUDA_GENERATE_DEPENDENCIES_DURING_CONFIGURE 28 | "Generate dependencies during configure time instead of only during build time." OFF) 29 | 30 | find_package(CUDA 11.0 REQUIRED) 31 | mark_as_advanced(CLEAR CUDA_64_BIT_DEVICE_CODE) 32 | 33 | set(CMAKE_RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/bin") 34 | set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/lib") 35 | set(CMAKE_LIBRARY_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/lib") 36 | 37 | set(CUDA_GENERATED_OUTPUT_DIR "${CMAKE_BINARY_DIR}/lib/ptx") 38 | 39 | include_directories( 40 | include/ 41 | ${DRJIT_DIR}/include 42 | ${PYBIND11_ROOT}/include 43 | ${PYTHON_INCLUDE_DIRS} 44 | ${PYTHON_ROOT}/include 45 | ${CMAKE_CURRENT_SOURCE_DIR}/cuda 46 | ${CMAKE_CURRENT_SOURCE_DIR} 47 | ${CMAKE_BINARY_DIR}/include 48 | ${CMAKE_CURRENT_BINARY_DIR} 49 | ${CUDA_INCLUDE_DIRS} 50 | ) 51 | 52 | set(PSDR_INCLUDE_DIR include/psdr) 53 | set(PSDR_SOURCE_DIR src) 54 | set(PSDR_SOURCE_FILES 55 | ${PSDR_INCLUDE_DIR}/psdr.h 56 | ${PSDR_INCLUDE_DIR}/constants.h 57 | ${PSDR_INCLUDE_DIR}/fwd.h 58 | ${PSDR_INCLUDE_DIR}/macros.h 59 | ${PSDR_INCLUDE_DIR}/object.h 60 | ${PSDR_INCLUDE_DIR}/types.h 61 | ${PSDR_INCLUDE_DIR}/utils.h 62 | ${PSDR_INCLUDE_DIR}/core/ray.h 63 | ${PSDR_INCLUDE_DIR}/core/frame.h 64 | ${PSDR_INCLUDE_DIR}/core/intersection.h 65 | ${PSDR_INCLUDE_DIR}/core/bitmap.h 66 | ${PSDR_SOURCE_DIR}/core/bitmap.cpp 67 | ${PSDR_INCLUDE_DIR}/core/bitmap_loader.h 68 | ${PSDR_SOURCE_DIR}/core/bitmap_loader.cpp 69 | ${PSDR_INCLUDE_DIR}/core/sampler.h 70 | ${PSDR_SOURCE_DIR}/core/sampler.cpp 71 | ${PSDR_INCLUDE_DIR}/core/records.h 72 | ${PSDR_INCLUDE_DIR}/core/warp.h 73 | ${PSDR_INCLUDE_DIR}/core/transform.h 74 | 75 | ${PSDR_INCLUDE_DIR}/core/cube_distrb.h 76 | ${PSDR_SOURCE_DIR}/core/cube_distrb.cpp 77 | 78 | ${PSDR_INCLUDE_DIR}/core/pmf.h 79 | ${PSDR_SOURCE_DIR}/core/pmf.cpp 80 | 81 | ${PSDR_INCLUDE_DIR}/optix/ptx.h 82 | ${PSDR_SOURCE_DIR}/optix/ptx.cpp 83 | 84 | ${PSDR_INCLUDE_DIR}/bsdf/bsdf.h 85 | ${PSDR_INCLUDE_DIR}/bsdf/diffuse.h 86 | ${PSDR_SOURCE_DIR}/bsdf/diffuse.cpp 87 | 88 | ${PSDR_INCLUDE_DIR}/bsdf/microfacet.h 89 | ${PSDR_SOURCE_DIR}/bsdf/microfacet.cpp 90 | 91 | ${PSDR_INCLUDE_DIR}/bsdf/microfacet_pv.h 92 | ${PSDR_SOURCE_DIR}/bsdf/microfacet_pv.cpp 93 | 94 | ${PSDR_INCLUDE_DIR}/bsdf/roughconductor.h 95 | ${PSDR_SOURCE_DIR}/bsdf/roughconductor.cpp 96 | ${PSDR_INCLUDE_DIR}/bsdf/roughdielectric.h 97 | ${PSDR_SOURCE_DIR}/bsdf/roughdielectric.cpp 98 | 99 | ${PSDR_INCLUDE_DIR}/bsdf/normalmap.h 100 | ${PSDR_SOURCE_DIR}/bsdf/normalmap.cpp 101 | 102 | ${PSDR_INCLUDE_DIR}/bsdf/ggx.h 103 | ${PSDR_SOURCE_DIR}/bsdf/ggx.cpp 104 | 105 | ${PSDR_INCLUDE_DIR}/edge/edge.h 106 | 107 | ${PSDR_INCLUDE_DIR}/shape/mesh.h 108 | ${PSDR_SOURCE_DIR}/shape/mesh.cpp 109 | 110 | ${PSDR_INCLUDE_DIR}/emitter/emitter.h 111 | ${PSDR_INCLUDE_DIR}/emitter/area.h 112 | ${PSDR_SOURCE_DIR}/emitter/area.cpp 113 | ${PSDR_INCLUDE_DIR}/emitter/envmap.h 114 | ${PSDR_SOURCE_DIR}/emitter/envmap.cpp 115 | 116 | ${PSDR_INCLUDE_DIR}/sensor/sensor.h 117 | ${PSDR_SOURCE_DIR}/sensor/sensor.cpp 118 | 119 | ${PSDR_INCLUDE_DIR}/sensor/perspective.h 120 | ${PSDR_SOURCE_DIR}/sensor/perspective.cpp 121 | ${PSDR_INCLUDE_DIR}/sensor/orthographic.h 122 | ${PSDR_SOURCE_DIR}/sensor/orthographic.cpp 123 | 124 | ${PSDR_INCLUDE_DIR}/core/tinyexr.h 125 | ${PSDR_INCLUDE_DIR}/core/miniz.h 126 | ${PSDR_SOURCE_DIR}/core/miniz.cpp 127 | 128 | ${PSDR_INCLUDE_DIR}/scene/scene_optix.h 129 | ${PSDR_SOURCE_DIR}/scene/scene_optix.cpp 130 | 131 | ${PSDR_INCLUDE_DIR}/scene/scene.h 132 | ${PSDR_SOURCE_DIR}/scene/scene.cpp 133 | 134 | ${PSDR_INCLUDE_DIR}/scene/scene_loader.h 135 | ${PSDR_SOURCE_DIR}/scene/scene_loader.cpp 136 | 137 | ${PSDR_INCLUDE_DIR}/integrator/integrator.h 138 | ${PSDR_SOURCE_DIR}/integrator/integrator.cpp 139 | ${PSDR_INCLUDE_DIR}/integrator/field.h 140 | ${PSDR_SOURCE_DIR}/integrator/field.cpp 141 | 142 | ${PSDR_INCLUDE_DIR}/integrator/collocated.h 143 | ${PSDR_SOURCE_DIR}/integrator/collocated.cpp 144 | 145 | ${PSDR_INCLUDE_DIR}/integrator/direct.h 146 | ${PSDR_SOURCE_DIR}/integrator/direct.cpp 147 | 148 | 149 | ${PSDR_INCLUDE_DIR}/integrator/path.h 150 | ${PSDR_SOURCE_DIR}/integrator/path.cpp 151 | 152 | ${PSDR_SOURCE_DIR}/jit_optix_test.cpp 153 | 154 | ${PSDR_SOURCE_DIR}/optix_stubs.cpp 155 | 156 | ${PSDR_SOURCE_DIR}/psdr.cpp 157 | ) 158 | 159 | set(CUDA_SOURCE_FILES 160 | # ./cuda/kernel/knn.cu 161 | ./cuda/kernel/util.cu 162 | # ./cuda/kernel/NEE.cu 163 | ) 164 | 165 | if( WIN32 ) 166 | cuda_add_library(cu_library STATIC ${CUDA_SOURCE_FILES}) 167 | cuda_add_cublas_to_target(cu_library) 168 | pybind11_add_module(psdr_jit ${PSDR_SOURCE_FILES}) 169 | target_compile_options(psdr_jit PRIVATE /O2 /wd4251 /MP) 170 | add_subdirectory(${DRJIT_DIR}) 171 | set(PYPSDR_LIBRARIES drjit-core drjit-autodiff nanothread) 172 | target_link_libraries(psdr_jit PRIVATE cu_library ${PYPSDR_LIBRARIES} ${CUDA_LIBRARIES}) 173 | else() 174 | cuda_add_library(cu_library STATIC ${CUDA_SOURCE_FILES}) 175 | cuda_add_cublas_to_target(cu_library) 176 | add_subdirectory(${DRJIT_DIR}) 177 | set(PYPSDR_LIBRARIES drjit-core drjit-autodiff nanothread) 178 | cuda_add_library(psdr_jit MODULE ${PSDR_SOURCE_FILES}) 179 | target_link_libraries(psdr_jit cu_library ${PYPSDR_LIBRARIES} ${CUDA_LIBRARIES}) 180 | endif() 181 | 182 | install(TARGETS psdr_jit drjit-core drjit-autodiff nanothread DESTINATION psdr_jit) 183 | install(TARGETS drjit-python drjit-core drjit-autodiff nanothread DESTINATION drjit) 184 | install(DIRECTORY ${DRJIT_DIR}/drjit DESTINATION ${SKBUILD_PLATLIB_DIR}) 185 | 186 | target_compile_definitions(psdr_jit PRIVATE PTX_OUTPUT_DIR="${CUDA_GENERATED_OUTPUT_DIR}") 187 | set_target_properties(psdr_jit drjit-python drjit-core drjit-autodiff nanothread PROPERTIES 188 | SKIP_BUILD_RPATH FALSE 189 | BUILD_WITH_INSTALL_RPATH FALSE 190 | INSTALL_RPATH "$ORIGIN" 191 | INSTALL_RPATH_USE_LINK_PATH TRUE) 192 | set_property(TARGET psdr_jit PROPERTY CXX_STANDARD 17) 193 | set_target_properties(psdr_jit PROPERTIES PREFIX "") -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![PyPI version](https://badge.fury.io/py/psdr-jit.svg?kill_cache=1)](https://badge.fury.io/py/psdr-jit) 2 | [![DOI](https://zenodo.org/badge/530067467.svg)](https://zenodo.org/doi/10.5281/zenodo.13777324) 3 | # psdr-jit 4 | Path-space differentiable renderer with [`drjit`](https://drjit.readthedocs.io/en/latest/) as the numerical backend. 5 | Old repo: https://github.com/uci-rendering/psdr-cuda 6 | 7 | 8 | ## Installation 9 | We use [`OptiX 7`](https://developer.nvidia.com/rtx/ray-tracing/optix) to accelerate ray tracing. Make sure [`cudatoolkit`](https://developer.nvidia.com/cuda-toolkit) (you may install it using `conda`) and an appropriate NVIDIA display driver that support `OptiX 7` are installed. The latest version we tested was `cudatoolkit==11.7`. Then run 10 | ```bash 11 | pip install psdr-jit 12 | ``` 13 | 14 | ## Local Intallation 15 | To install `psdr-jit` locally, first clone the repository recursivly (to include the git submodules): 16 | ```bash 17 | git clone --recursive https://github.com/andyyankai/psdr-jit.git 18 | ``` 19 | or 20 | ```bash 21 | git clone https://github.com/andyyankai/psdr-jit.git 22 | cd psdr-jit 23 | git submodule update --init --recursive 24 | ``` 25 | Then compile and install `psdr-jit` via 26 | ```bash 27 | pip install . 28 | ``` 29 | or 30 | ```bash 31 | pip install --no-build-isolation -ve . 32 | ``` 33 | 34 | Notice that it would also install `drjit` as `psdr-jit` depends on it. `cudatoolkit` is also required. 35 | 36 | ## Getting Started 37 | ```python 38 | import drjit 39 | import psdr_jit 40 | ``` 41 | 42 | ### Example of Diff rendering 43 | 44 | ```python 45 | import cv2 46 | import sys 47 | import torch 48 | 49 | import psdr_jit as psdr 50 | import drjit 51 | from drjit.cuda.ad import Float as FloatD, Matrix4f as Matrix4fD 52 | from drjit.cuda import Float as FloatC, Matrix4f as Matrix4fC 53 | 54 | sc = psdr.Scene() 55 | sc.opts.spp = 32 # Interior Term 56 | sc.opts.sppe = 32 # Primary Edge 57 | sc.opts.sppse = 32 # Secondary Edge 58 | sc.opts.height = 512 59 | sc.opts.width = 512 60 | 61 | integrator = psdr.PathTracer(3) 62 | 63 | 64 | sensor = psdr.PerspectiveCamera(60, 0.000001, 10000000.) 65 | to_world = Matrix4fD([[1.,0.,0.,208.], 66 | [0.,1.,0.,273.], 67 | [0.,0.,1.,-800.], 68 | [0.,0.,0.,1.],]) 69 | sensor.to_world = to_world 70 | sc.add_Sensor(sensor) 71 | 72 | sc.add_BSDF(psdr.DiffuseBSDF([0.0, 0.0, 0.0]), "light") 73 | sc.add_BSDF(psdr.DiffuseBSDF(), "cat") 74 | sc.add_BSDF(psdr.DiffuseBSDF([0.95, 0.95, 0.95]), "white") 75 | sc.add_BSDF(psdr.DiffuseBSDF([0.20, 0.90, 0.20]), "green") 76 | sc.add_BSDF(psdr.DiffuseBSDF([0.90, 0.20, 0.20]), "red") 77 | 78 | sc.add_Mesh("./data/objects/cbox/cbox_luminaire.obj", Matrix4fC([[1.,0.,0.,0.],[0.,1.,0.,-0.5],[0.,0.,1.,0.],[0.,0.,0.,1.]]), "light", psdr.AreaLight([20.0, 20.0, 8.0])) 79 | sc.add_Mesh("./data/objects/cbox/cbox_smallbox.obj", Matrix4fC([[1.,0.,0.,0.],[0.,1.,0.,0.],[0.,0.,1.,0.],[0.,0.,0.,1.]]), "cat", None) 80 | sc.add_Mesh("./data/objects/cbox/cbox_largebox.obj", Matrix4fC([[1.,0.,0.,0.],[0.,1.,0.,0.],[0.,0.,1.,0.],[0.,0.,0.,1.]]), "cat", None) 81 | sc.add_Mesh("./data/objects/cbox/cbox_floor.obj", Matrix4fC([[1.,0.,0.,0.],[0.,1.,0.,0.],[0.,0.,1.,0.],[0.,0.,0.,1.]]), "white", None) 82 | sc.add_Mesh("./data/objects/cbox/cbox_ceiling.obj", Matrix4fC([[1.,0.,0.,0.],[0.,1.,0.,0.],[0.,0.,1.,0.],[0.,0.,0.,1.]]), "white", None) 83 | sc.add_Mesh("./data/objects/cbox/cbox_back.obj", Matrix4fC([[1.,0.,0.,0.],[0.,1.,0.,0.],[0.,0.,1.,0.],[0.,0.,0.,1.]]), "white", None) 84 | sc.add_Mesh("./data/objects/cbox/cbox_greenwall.obj", Matrix4fC([[1.,0.,0.,0.],[0.,1.,0.,0.],[0.,0.,1.,0.],[0.,0.,0.,1.]]), "green", None) 85 | sc.add_Mesh("./data/objects/cbox/cbox_redwall.obj", Matrix4fC([[1.,0.,0.,0.],[0.,1.,0.,0.],[0.,0.,1.,0.],[0.,0.,0.,1.]]), "red", None) 86 | 87 | P = FloatD(0.) 88 | drjit.enable_grad(P) 89 | 90 | sc.param_map["Mesh[0]"].set_transform(Matrix4fD([[1.,0.,0.,P*100.],[0.,1.,0.,0.],[0.,0.,1.,0.],[0.,0.,0.,1.],])) 91 | 92 | 93 | sc.configure() 94 | sc.configure([0]) 95 | 96 | img = integrator.renderD(sc, 0) 97 | org_img = img.numpy().reshape((sc.opts.width, sc.opts.height, 3)) 98 | output = cv2.cvtColor(org_img, cv2.COLOR_RGB2BGR) 99 | cv2.imwrite("psdr_jit_forward.exr", output) 100 | 101 | 102 | drjit.set_grad(P, 1.0) 103 | drjit.forward_to(img) 104 | diff_img = drjit.grad(img) 105 | diff_img = diff_img.numpy().reshape((sc.opts.width, sc.opts.height, 3)) 106 | output = cv2.cvtColor(diff_img, cv2.COLOR_RGB2BGR) 107 | cv2.imwrite("psdr_jit_diff_debug.exr", output) 108 | ``` 109 | 110 | ### More Tutorial 111 | gradient evaluation tutorial in [`tutorial`](https://github.com/andyyankai/psdr-jit/tree/main/tutorials) folder 112 | 113 | Object level optimization example: [`Neural-PBIR`](https://neural-pbir.github.io/) (ICCV 2023) 114 | 115 | Scene level optimization example: [`psdr-room`](https://github.com/andyyankai/psdr-room) (SIGGRAPH ASIA 2023) 116 | 117 | SDF+Nerf optimization example: to be released 118 | 119 | ### Project based on psdr-jit 120 | TBA 121 | -------------------------------------------------------------------------------- /cuda/host/NEE.cuh: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | namespace psdr_cuda { 5 | 6 | void init_tree(const float* cdf_x, const float* cdf_y, const float* cdf_z, int dimx, int dimy, int dimz, float* p0_x, float* p0_y, float* p0_z, float* p1_x, float* p1_y, float* p1_z); 7 | 8 | void generate_eval_point(int leaf_size, const float* p0_x, const float* p0_y, const float* p0_z, const float* p1_x, const float* p1_y, const float* p1_z, float* outx, float* outy, float* outz, int npass); 9 | 10 | int cut_grid(const float* eval_value, float* p0_x, float* p0_y, float* p0_z, 11 | float* p1_x, float* p1_y, float* p1_z, 12 | float* eval_x, float* eval_y, float* eval_z, 13 | int fix_size, float thold, float wt1); 14 | 15 | } 16 | -------------------------------------------------------------------------------- /cuda/host/knn.cuh: -------------------------------------------------------------------------------- 1 | namespace psdr_cuda { 2 | 3 | bool knn_cuda_global(const float * ref, 4 | int ref_nb, 5 | const float * query, 6 | int query_nb, 7 | int dim, 8 | int k, 9 | float * knn_dist, 10 | int * knn_index); 11 | 12 | bool knn_cuda_texture(const float * ref, 13 | int ref_nb, 14 | const float * query, 15 | int query_nb, 16 | int dim, 17 | int k, 18 | float * knn_dist, 19 | int * knn_index); 20 | 21 | bool knn_cublas(const float * ref, 22 | int ref_nb, 23 | const float * query, 24 | int query_nb, 25 | int dim, 26 | int k, 27 | float * knn_dist, 28 | int * knn_index); 29 | 30 | } -------------------------------------------------------------------------------- /cuda/host/util.cuh: -------------------------------------------------------------------------------- 1 | namespace psdr_cuda { 2 | 3 | struct cuda_edge 4 | { 5 | float* p0_x; 6 | float* p0_y; 7 | float* p0_z; 8 | 9 | float* p1_x; 10 | float* p1_y; 11 | float* p1_z; 12 | }; 13 | 14 | void meow(int cat); 15 | void float_add(float* in1, float* in2, float* out, int size); 16 | 17 | void edge_sort(cuda_edge ce, int size); 18 | } -------------------------------------------------------------------------------- /cuda/kernel/util.cu: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | #include 7 | 8 | __global__ void cat_meow(){ 9 | printf("Meow!!!\n"); 10 | } 11 | 12 | void psdr_cuda::meow(int cat) { 13 | printf("Cats are meowing on GPU...\n"); 14 | cat_meow<<<1,cat>>>(); 15 | cudaDeviceSynchronize(); 16 | printf("Cats are sleeping!\n"); 17 | } 18 | 19 | __global__ void floatAdd_kernel(float* in1, float* in2, float* out) 20 | { 21 | int i = threadIdx.x; 22 | out[i] = in1[i] + in2[i]; 23 | } 24 | 25 | void psdr_cuda::float_add(float* in1, float* in2, float* out, int size) { 26 | floatAdd_kernel<<<1, size>>>(in1, in2, out); 27 | } 28 | 29 | __global__ void change_val(float* in1) 30 | { 31 | int i = threadIdx.x; 32 | // in1[i] = 1.0f; 33 | } 34 | 35 | __global__ void test_val(float* in1, float* in2, float* in3, float* in4, float* in5, float* in6, int size) 36 | { 37 | int i, j; 38 | float selected1, selected2, selected3, selected4, selected5, selected6; 39 | for (i = 1; i < size; i++){ 40 | selected1 = in1[i]; 41 | selected2 = in2[i]; 42 | selected3 = in3[i]; 43 | selected4 = in4[i]; 44 | selected5 = in5[i]; 45 | selected6 = in6[i]; 46 | j = i - 1; 47 | while ((j >= 0) && (selected1+selected2+selected3 < in1[j]+in2[j]+in3[j])) { 48 | in1[j+1] = in1[j]; 49 | in2[j+1] = in2[j]; 50 | in3[j+1] = in3[j]; 51 | in4[j+1] = in4[j]; 52 | in5[j+1] = in5[j]; 53 | in6[j+1] = in6[j]; 54 | j--; 55 | } 56 | in1[j+1] = selected1; 57 | in2[j+1] = selected2; 58 | in3[j+1] = selected3; 59 | in4[j+1] = selected4; 60 | in5[j+1] = selected5; 61 | in6[j+1] = selected6; 62 | } 63 | } 64 | 65 | void psdr_cuda::edge_sort(cuda_edge ce, int size) { 66 | printf("test edge sort\n"); 67 | printf("size=%d\n", size); 68 | test_val<<<1, 1>>>(ce.p0_x, ce.p0_y, ce.p0_z, ce.p1_x, ce.p1_y, ce.p1_z, size); 69 | } 70 | 71 | -------------------------------------------------------------------------------- /cuda/psdr_jit.cpp: -------------------------------------------------------------------------------- 1 | int main() 2 | { 3 | return 0; 4 | } -------------------------------------------------------------------------------- /cuda/psdr_jit.cu: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include "psdr_jit.h" 4 | 5 | extern "C" { 6 | __constant__ Params params; 7 | } 8 | 9 | extern "C" __global__ void __raygen__psdr_rg() 10 | { 11 | const int tid = optixGetLaunchIndex().x; 12 | 13 | optixTrace( 14 | params.handle, 15 | make_float3(params.ray_o_x[tid], params.ray_o_y[tid], params.ray_o_z[tid]), 16 | make_float3(params.ray_d_x[tid], params.ray_d_y[tid], params.ray_d_z[tid]), 17 | psdr_jit::RayEpsilon, 18 | params.ray_tmax[tid], 19 | 0.0f, 20 | OptixVisibilityMask( 1 ), 21 | OPTIX_RAY_FLAG_NONE, 22 | 0, 23 | 1, 24 | 0 25 | ); 26 | } 27 | 28 | extern "C" __global__ void __miss__psdr_ms() 29 | { 30 | const uint32_t image_index = optixGetLaunchIndex().x; 31 | params.tri_index[ image_index ] = -1; 32 | params.shape_index[ image_index ] = -1; 33 | params.barycentric_u[ image_index ] = -1.0f; 34 | params.barycentric_v[ image_index ] = -1.0f; 35 | } 36 | 37 | extern "C" __global__ void __closesthit__psdr_ch() 38 | { 39 | HitGroupData* rt_data = (HitGroupData*)optixGetSbtDataPointer(); 40 | const uint32_t image_index = optixGetLaunchIndex().x; 41 | params.tri_index[ image_index ] = optixGetPrimitiveIndex() + rt_data->shape_offset; 42 | params.shape_index[ image_index ] = rt_data->shape_id; 43 | float2 uv = optixGetTriangleBarycentrics(); 44 | params.barycentric_u[ image_index ] = uv.x; 45 | params.barycentric_v[ image_index ] = uv.y; 46 | } 47 | -------------------------------------------------------------------------------- /cuda/psdr_jit.h: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | struct Params 4 | { 5 | const float *ray_o_x, *ray_o_y, *ray_o_z; 6 | const float *ray_d_x, *ray_d_y, *ray_d_z; 7 | const float *ray_tmax; 8 | 9 | int *tri_index; 10 | int *shape_index; 11 | float *barycentric_u, *barycentric_v; 12 | 13 | unsigned long long handle; 14 | }; 15 | 16 | struct RayGenData 17 | { 18 | 19 | }; 20 | 21 | struct MissData 22 | { 23 | 24 | }; 25 | 26 | struct HitGroupData 27 | { 28 | int shape_offset; 29 | int shape_id; 30 | }; 31 | -------------------------------------------------------------------------------- /dev_notes.md: -------------------------------------------------------------------------------- 1 | # Dev Notes 2 | 3 | ## Publish to PyPI 4 | The following was taken from [this guide](https://realpython.com/pypi-publish-python-package/#publish-your-package-to-pypi). 5 | 6 | Intalll the necessary packages: 7 | ```bash 8 | pip install build twine 9 | ``` 10 | Create a source archive and a wheel for your package: 11 | ```bash 12 | python -m build 13 | ``` 14 | Check with `twine`: 15 | ```bash 16 | twine check dist/* 17 | ``` 18 | 19 | If you would like to do a test upload: 20 | ```bash 21 | twine upload -r testpypi dist/* 22 | ``` 23 | 24 | Final upload: 25 | ```bash 26 | twine upload dist/* 27 | ``` 28 | -------------------------------------------------------------------------------- /include/misc/Exception.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | #define PSDR_ASSERT( cond ) \ 8 | do \ 9 | { \ 10 | if( !(cond) ) \ 11 | { \ 12 | std::stringstream ss; \ 13 | ss << __FILE__ << " (" << __LINE__ << "): " << #cond; \ 14 | throw psdr_jit::Exception( ss.str().c_str() ); \ 15 | } \ 16 | } while( 0 ) 17 | 18 | 19 | #define PSDR_ASSERT_MSG( cond, msg ) \ 20 | do \ 21 | { \ 22 | if( !(cond) ) \ 23 | { \ 24 | std::stringstream ss; \ 25 | ss << "\n File \"" << __FILE__ << "\", line " << __LINE__; \ 26 | throw psdr_jit::Exception( ( std::string(msg) + ss.str() ).c_str() ); \ 27 | } \ 28 | } while( 0 ) 29 | 30 | 31 | 32 | NAMESPACE_BEGIN(psdr_jit) 33 | 34 | class Exception : public std::runtime_error 35 | { 36 | public: 37 | Exception( const char* msg ) 38 | : std::runtime_error( msg ) 39 | { } 40 | }; 41 | 42 | NAMESPACE_END(psdr_jit) 43 | -------------------------------------------------------------------------------- /include/psdr/bsdf/bsdf.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | NAMESPACE_BEGIN(psdr_jit) 8 | 9 | 10 | 11 | template 12 | struct BSDFSample_ : public SampleRecord_ { 13 | PSDR_IMPORT_BASE(SampleRecord_, ad, pdf, is_valid) 14 | 15 | Vector3f wo; 16 | Float eta; 17 | 18 | DRJIT_STRUCT(BSDFSample_, pdf, is_valid, wo, eta) 19 | }; 20 | 21 | 22 | PSDR_CLASS_DECL_BEGIN(BSDF,, Object) 23 | public: 24 | BSDF() {} 25 | virtual ~BSDF() override {} 26 | virtual SpectrumC eval(const IntersectionC &its, const Vector3fC &wo, MaskC active = true) const = 0; 27 | virtual SpectrumD eval(const IntersectionD &its, const Vector3fD &wo, MaskD active = true) const = 0; 28 | 29 | SpectrumC evalC(const IntersectionC &its, const Vector3fC &wo, MaskC active = true) const { 30 | return eval(its, wo, active); 31 | }; 32 | SpectrumD evalD(const IntersectionD &its, const Vector3fD &wo, MaskD active = true) const { 33 | return eval(its, wo, active); 34 | }; 35 | 36 | virtual BSDFSampleC sample(const IntersectionC &its, const Vector3fC &sample, MaskC active = true) const = 0; 37 | virtual BSDFSampleD sample(const IntersectionD &its, const Vector3fD &sample, MaskD active = true) const = 0; 38 | 39 | BSDFSampleC sampleC(const IntersectionC &its, const Vector3fC &sample_, MaskC active = true) const { 40 | return sample(its, sample_, active); 41 | }; 42 | BSDFSampleD sampleD(const IntersectionD &its, const Vector3fD &sample_, MaskD active = true) const { 43 | return sample(its, sample_, active); 44 | }; 45 | 46 | virtual FloatC pdf(const IntersectionC &its, const Vector3fC &wo, MaskC active) const = 0; 47 | virtual FloatD pdf(const IntersectionD &its, const Vector3fD &wo, MaskD active) const = 0; 48 | 49 | FloatC pdfC(const IntersectionC &its, const Vector3fC &wo, MaskC active = true) const { 50 | return pdf(its, wo, active); 51 | }; 52 | FloatD pdfD(const IntersectionD &its, const Vector3fD &wo, MaskD active = true) const { 53 | return pdf(its, wo, active); 54 | }; 55 | 56 | virtual bool anisotropic() const = 0; 57 | 58 | bool m_twoSide = false; 59 | 60 | DRJIT_VCALL_REGISTER(FloatD, BSDF); 61 | 62 | PSDR_CLASS_DECL_END(BSDF) 63 | 64 | NAMESPACE_END(psdr_jit) 65 | 66 | DRJIT_VCALL_BEGIN(psdr_jit::BSDF) 67 | DRJIT_VCALL_METHOD(eval) 68 | DRJIT_VCALL_METHOD(evalC) 69 | DRJIT_VCALL_METHOD(evalD) 70 | DRJIT_VCALL_METHOD(sample) 71 | DRJIT_VCALL_METHOD(sampleC) 72 | DRJIT_VCALL_METHOD(sampleD) 73 | DRJIT_VCALL_METHOD(pdf) 74 | DRJIT_VCALL_METHOD(pdfC) 75 | DRJIT_VCALL_METHOD(pdfD) 76 | DRJIT_VCALL_METHOD(anisotropic) 77 | DRJIT_VCALL_END(psdr_jit::BSDF) 78 | -------------------------------------------------------------------------------- /include/psdr/bsdf/diffuse.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include "bsdf.h" 5 | 6 | NAMESPACE_BEGIN(psdr_jit) 7 | 8 | 9 | PSDR_CLASS_DECL_BEGIN(Diffuse, final, BSDF) 10 | public: 11 | Diffuse() : m_reflectance(0.5f) {} 12 | Diffuse(const ScalarVector3f &ref) : m_reflectance(ref) { drjit::make_opaque(m_reflectance); } 13 | Diffuse(const char *refl_file); 14 | Diffuse(const Bitmap3fD &reflectance); 15 | 16 | SpectrumC eval(const IntersectionC &its, const Vector3fC &wo, MaskC active = true) const override; 17 | SpectrumD eval(const IntersectionD &its, const Vector3fD &wo, MaskD active = true) const override; 18 | 19 | BSDFSampleC sample(const IntersectionC &its, const Vector3fC &sample, MaskC active = true) const override; 20 | BSDFSampleD sample(const IntersectionD &its, const Vector3fD &sample, MaskD active = true) const override; 21 | 22 | FloatC pdf(const IntersectionC &its, const Vector3fC &wo, MaskC active) const override; 23 | FloatD pdf(const IntersectionD &its, const Vector3fD &wo, MaskD active) const override; 24 | 25 | bool anisotropic() const override { return false; } 26 | 27 | std::string to_string() const override { return std::string("Diffuse[id=") + m_id + "]"; } 28 | 29 | Bitmap3fD m_reflectance; 30 | 31 | protected: 32 | template 33 | Spectrum __eval(const Intersection&, const Vector3f&, Mask) const; 34 | 35 | template 36 | BSDFSample __sample(const Intersection&, const Vector3f&, Mask) const; 37 | 38 | template 39 | Float __pdf(const Intersection &, const Vector3f &, Mask) const; 40 | 41 | 42 | PSDR_CLASS_DECL_END(Diffuse) 43 | 44 | 45 | NAMESPACE_END(psdr_jit) 46 | -------------------------------------------------------------------------------- /include/psdr/bsdf/ggx.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include "bsdf.h" 5 | 6 | NAMESPACE_BEGIN(psdr_jit) 7 | 8 | struct GGXDistribution { 9 | public: 10 | GGXDistribution() : m_alpha_u(0.1f), m_alpha_v(0.1f) {} 11 | GGXDistribution(const FloatD &alpha) : m_alpha_u(alpha), m_alpha_v(alpha) {} 12 | GGXDistribution(const FloatD &alpha_u, const FloatD &alpha_v) : m_alpha_u(alpha_u), m_alpha_v(alpha_v) {} 13 | 14 | template 15 | Float G(const Vector3f& wi, const Vector3f& wo, const Vector3f& m) const; 16 | 17 | template 18 | Float eval(const Vector3f& m) const; 19 | 20 | template 21 | std::pair, Float> sample(const Vector3f& wi, const Vector3f& sample) const; 22 | 23 | template 24 | Float smith_g1(const Vector3f& v, const Vector3f& m) const; 25 | 26 | template 27 | Vector2f sample_visible_11(const Float& cos_theta_i, const Vector2f& sample) const; 28 | 29 | FloatD m_alpha_u, m_alpha_v; 30 | }; 31 | 32 | NAMESPACE_END(psdr_jit) 33 | -------------------------------------------------------------------------------- /include/psdr/bsdf/microfacet.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include "bsdf.h" 5 | 6 | NAMESPACE_BEGIN(psdr_jit) 7 | 8 | PSDR_CLASS_DECL_BEGIN(Microfacet, final, BSDF) 9 | public: 10 | Microfacet() : m_specularReflectance(0.04f), m_diffuseReflectance(0.5f), m_roughness(0.8f) {} 11 | 12 | Microfacet(const ScalarVector3f &specularRef, const ScalarVector3f &diffuseRef, float roughnessRef) : 13 | m_specularReflectance(specularRef), m_diffuseReflectance(diffuseRef), m_roughness(roughnessRef) {} 14 | 15 | Microfacet(const char *spec_refl_file, const char *diff_refl_file, const char *roughness_file); 16 | 17 | Microfacet(const Bitmap3fD &spec_refl, const Bitmap3fD &diff_refl, const Bitmap1fD &roughness); 18 | 19 | SpectrumC eval(const IntersectionC &its, const Vector3fC &wo, MaskC active = true) const override; 20 | SpectrumD eval(const IntersectionD &its, const Vector3fD &wo, MaskD active = true) const override; 21 | 22 | BSDFSampleC sample(const IntersectionC &its, const Vector3fC &sample, MaskC active = true) const override; 23 | BSDFSampleD sample(const IntersectionD &its, const Vector3fD &sample, MaskD active = true) const override; 24 | 25 | 26 | FloatC pdf(const IntersectionC &its, const Vector3fC &wo, MaskC active) const override; 27 | FloatD pdf(const IntersectionD &its, const Vector3fD &wo, MaskD active) const override; 28 | 29 | bool anisotropic() const override { return false; } 30 | 31 | std::string to_string() const override { return std::string("Microfacet[id=") + m_id + "]"; } 32 | 33 | Bitmap3fD m_specularReflectance, 34 | m_diffuseReflectance; 35 | Bitmap1fD m_roughness; 36 | 37 | protected: 38 | template 39 | Spectrum __eval(const Intersection &its, const Vector3f &wo, Mask active = true) const; 40 | 41 | template 42 | BSDFSample __sample(const Intersection &its, const Vector3f &wo, Mask active = true) const; 43 | 44 | template 45 | Float __pdf(const Intersection &its, const Vector3f &wo, Mask active = true) const; 46 | 47 | PSDR_CLASS_DECL_END(Microfacet) 48 | 49 | NAMESPACE_END(psdr_jit) 50 | -------------------------------------------------------------------------------- /include/psdr/bsdf/microfacet_pv.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "bsdf.h" 4 | 5 | NAMESPACE_BEGIN(psdr_jit) 6 | 7 | PSDR_CLASS_DECL_BEGIN(MicrofacetPerVertex, final, BSDF) 8 | public: 9 | MicrofacetPerVertex(const Vector3fD &spec_refl, const Vector3fD &diff_refl, const Vector1fD &roughness); 10 | 11 | SpectrumC eval(const IntersectionC &its, const Vector3fC &wo, MaskC active = true) const override; 12 | SpectrumD eval(const IntersectionD &its, const Vector3fD &wo, MaskD active = true) const override; 13 | 14 | BSDFSampleC sample(const IntersectionC &its, const Vector3fC &sample, MaskC active = true) const override; 15 | BSDFSampleD sample(const IntersectionD &its, const Vector3fD &sample, MaskD active = true) const override; 16 | 17 | 18 | FloatC pdf(const IntersectionC &its, const Vector3fC &wo, MaskC active) const override; 19 | FloatD pdf(const IntersectionD &its, const Vector3fD &wo, MaskD active) const override; 20 | 21 | bool anisotropic() const override { return false; } 22 | 23 | std::string to_string() const override { return std::string("MicrofacetPerVertex[id=") + m_id + "]"; } 24 | 25 | Vector3fD m_specularReflectance, 26 | m_diffuseReflectance; 27 | Vector1fD m_roughness; 28 | 29 | protected: 30 | template 31 | Spectrum __eval(const Intersection &its, const Vector3f &wo, Mask active = true) const; 32 | 33 | template 34 | BSDFSample __sample(const Intersection &its, const Vector3f &wo, Mask active = true) const; 35 | 36 | template 37 | Float __pdf(const Intersection &its, const Vector3f &wo, Mask active = true) const; 38 | 39 | template 40 | Vectorf __interpolate(const Intersection &its, const Vectorf &v, Mask active = true) const; 41 | 42 | PSDR_CLASS_DECL_END(MicrofacetPerVertex) 43 | 44 | NAMESPACE_END(psdr_jit) 45 | -------------------------------------------------------------------------------- /include/psdr/bsdf/normalmap.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include "bsdf.h" 5 | 6 | NAMESPACE_BEGIN(psdr_jit) 7 | 8 | PSDR_CLASS_DECL_BEGIN(NormalMap, final, BSDF) 9 | public: 10 | NormalMap() {} 11 | NormalMap(const Bitmap3fD &n_map); 12 | 13 | SpectrumC eval(const IntersectionC &its, const Vector3fC &wo, MaskC active = true) const override; 14 | SpectrumD eval(const IntersectionD &its, const Vector3fD &wo, MaskD active = true) const override; 15 | 16 | BSDFSampleC sample(const IntersectionC &its, const Vector3fC &sample, MaskC active = true) const override; 17 | BSDFSampleD sample(const IntersectionD &its, const Vector3fD &sample, MaskD active = true) const override; 18 | 19 | FloatC pdf(const IntersectionC &its, const Vector3fC &wo, MaskC active) const override; 20 | FloatD pdf(const IntersectionD &its, const Vector3fD &wo, MaskD active) const override; 21 | 22 | bool anisotropic() const override { return false; } 23 | 24 | std::string to_string() const override { return std::string("NormalMap[id=") + m_id + "]"; } 25 | 26 | Bitmap3fD m_nmap; 27 | BSDF* m_bsdf; 28 | 29 | protected: 30 | template 31 | Spectrum __eval(const Intersection&, const Vector3f&, Mask) const; 32 | 33 | template 34 | BSDFSample __sample(const Intersection&, const Vector3f&, Mask) const; 35 | 36 | template 37 | Float __pdf(const Intersection &, const Vector3f &, Mask) const; 38 | PSDR_CLASS_DECL_END(NormalMap) 39 | 40 | NAMESPACE_END(psdr_jit) 41 | -------------------------------------------------------------------------------- /include/psdr/bsdf/roughconductor.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include "bsdf.h" 5 | 6 | NAMESPACE_BEGIN(psdr_jit) 7 | 8 | PSDR_CLASS_DECL_BEGIN(RoughConductor, final, BSDF) 9 | public: 10 | RoughConductor() 11 | : m_alpha_u(0.1f), m_alpha_v(0.1f), m_eta(0.0f), m_k(1.0f), m_specular_reflectance(1.0f) { m_anisotropic = false; } 12 | 13 | RoughConductor(const Bitmap1fD &alpha, const Bitmap3fD &eta, const Bitmap3fD &k) 14 | : m_alpha_u(alpha), m_alpha_v(alpha), m_eta(eta), m_k(k), m_specular_reflectance(1.0f) { m_anisotropic = false; } 15 | 16 | RoughConductor(const Bitmap1fD &alpha, const Bitmap3fD &eta, const Bitmap3fD &k, const Bitmap3fD &sr) 17 | : m_alpha_u(alpha), m_alpha_v(alpha), m_eta(eta), m_k(k), m_specular_reflectance(sr) { m_anisotropic = false; } 18 | 19 | RoughConductor(const Bitmap1fD &alpha_u, const Bitmap1fD &alpha_v, const Bitmap3fD &eta, const Bitmap3fD &k) 20 | : m_alpha_u(alpha_u), m_alpha_v(alpha_v), m_eta(eta), m_k(k), m_specular_reflectance(1.0f) { m_anisotropic = true; } 21 | 22 | RoughConductor(const Bitmap1fD &alpha_u, const Bitmap1fD &alpha_v, const Bitmap3fD &eta, const Bitmap3fD &k, const Bitmap3fD &sr) 23 | : m_alpha_u(alpha_u), m_alpha_v(alpha_v), m_eta(eta), m_k(k), m_specular_reflectance(sr) { m_anisotropic = true; } 24 | 25 | SpectrumC eval(const IntersectionC &its, const Vector3fC &wo, MaskC active = true) const override; 26 | SpectrumD eval(const IntersectionD &its, const Vector3fD &wo, MaskD active = true) const override; 27 | 28 | BSDFSampleC sample(const IntersectionC &its, const Vector3fC &sample, MaskC active = true) const override; 29 | BSDFSampleD sample(const IntersectionD &its, const Vector3fD &sample, MaskD active = true) const override; 30 | 31 | FloatC pdf(const IntersectionC &its, const Vector3fC &wo, MaskC active = true) const override; 32 | FloatD pdf(const IntersectionD &its, const Vector3fD &wo, MaskD active = true) const override; 33 | 34 | bool anisotropic() const override { return m_anisotropic; } 35 | 36 | std::string to_string() const override { return std::string("RoughConductor[id=") + m_id + "]"; } 37 | 38 | Bitmap1fD m_alpha_u, m_alpha_v; 39 | Bitmap3fD m_eta, m_k; 40 | Bitmap3fD m_specular_reflectance; 41 | bool m_anisotropic; 42 | 43 | protected: 44 | template 45 | Spectrum __eval(const Intersection&, const Vector3f&, Mask) const; 46 | 47 | template 48 | BSDFSample __sample(const Intersection&, const Vector3f&, Mask) const; 49 | 50 | template 51 | Float __pdf(const Intersection &, const Vector3f &, Mask) const; 52 | PSDR_CLASS_DECL_END(RoughConductor) 53 | 54 | NAMESPACE_END(psdr_jit) 55 | -------------------------------------------------------------------------------- /include/psdr/bsdf/roughdielectric.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "bsdf.h" 4 | #include 5 | 6 | NAMESPACE_BEGIN(psdr_jit) 7 | 8 | PSDR_CLASS_DECL_BEGIN(RoughDielectric, final, BSDF) 9 | public: 10 | RoughDielectric() : m_specular_reflectance(1.0f), m_alpha_u(0.1f), m_alpha_v(0.1f) { 11 | float intIOR = 1.5f; 12 | float extIOR = 1.0f; 13 | 14 | m_eta = intIOR / extIOR; 15 | m_inv_eta = 1.f / m_eta; 16 | 17 | m_anisotropic = false; 18 | } 19 | 20 | RoughDielectric(float intIOR, float extIOR) 21 | : m_alpha_u(0.1f), m_alpha_v(0.1f), m_eta(intIOR / extIOR), m_inv_eta(extIOR/intIOR), m_specular_reflectance(1.0f) { m_anisotropic = false; } 22 | 23 | 24 | RoughDielectric(const Bitmap1fD &alpha, float intIOR, float extIOR) 25 | : m_alpha_u(alpha), m_alpha_v(alpha), m_eta(intIOR / extIOR), m_inv_eta(extIOR/intIOR), m_specular_reflectance(1.0f) { m_anisotropic = false; } 26 | 27 | 28 | SpectrumC eval(const IntersectionC &its, const Vector3fC &wo, MaskC active = true) const override; 29 | SpectrumD eval(const IntersectionD &its, const Vector3fD &wo, MaskD active = true) const override; 30 | 31 | BSDFSampleC sample(const IntersectionC &its, const Vector3fC &sample, MaskC active = true) const override; 32 | BSDFSampleD sample(const IntersectionD &its, const Vector3fD &sample, MaskD active = true) const override; 33 | 34 | FloatC pdf(const IntersectionC &its, const Vector3fC &wo, MaskC active = true) const override; 35 | FloatD pdf(const IntersectionD &its, const Vector3fD &wo, MaskD active = true) const override; 36 | 37 | bool anisotropic() const override { return m_anisotropic; } 38 | 39 | std::string to_string() const override { return std::string("RoughDielectric[id=") + m_id + "]"; } 40 | 41 | Bitmap1fD m_alpha_u, m_alpha_v; 42 | Bitmap3fD m_specular_reflectance; 43 | Bitmap3fD m_specular_transmittance; 44 | FloatD m_eta, m_inv_eta; 45 | bool m_anisotropic; 46 | 47 | protected: 48 | template 49 | Spectrum __eval(const Intersection&, const Vector3f&, Mask) const; 50 | 51 | template 52 | BSDFSample __sample(const Intersection&, const Vector3f&, Mask) const; 53 | 54 | template 55 | Float __pdf(const Intersection &, const Vector3f &, Mask) const; 56 | PSDR_CLASS_DECL_END(RoughDielectric) 57 | 58 | NAMESPACE_END(psdr_jit) 59 | -------------------------------------------------------------------------------- /include/psdr/constants.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | #ifndef M_PI 7 | #define M_PI 3.14159265358979323846 8 | #endif 9 | 10 | NAMESPACE_BEGIN(psdr_jit) 11 | 12 | constexpr float Epsilon = 1e-5f; 13 | constexpr float RayEpsilon = 1e-3f; 14 | constexpr float ShadowEpsilon = 1e-3f; 15 | constexpr float EdgeEpsilon = 1e-5f; 16 | constexpr float AQEpsilon = 1e-5f; 17 | constexpr float DeepEpsilon = 1e-8f; 18 | 19 | constexpr float E = 2.71828182845904523536f; 20 | constexpr float Pi = 3.14159265358979323846f; 21 | constexpr float TwoPi = 6.28318530717958647692f; 22 | constexpr float InvPi = 0.31830988618379067154f; 23 | constexpr float InvTwoPi = 0.15915494309189533577f; 24 | constexpr float InvFourPi = 0.07957747154594766788f; 25 | constexpr float SqrtPi = 1.77245385090551602793f; 26 | constexpr float InvSqrtPi = 0.56418958354775628695f; 27 | constexpr float SqrtTwo = 1.41421356237309504880f; 28 | constexpr float InvSqrtTwo = 0.70710678118654752440f; 29 | constexpr float SqrtTwoPi = 2.50662827463100050242f; 30 | constexpr float InvSqrtTwoPi = 0.39894228040143267794f; 31 | 32 | constexpr float Infinity = std::numeric_limits::infinity(); 33 | 34 | NAMESPACE_END(psdr_jit) 35 | -------------------------------------------------------------------------------- /include/psdr/core/AQ_distrb.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include "pmf.h" 5 | 6 | NAMESPACE_BEGIN(psdr_jit) 7 | 8 | template 9 | struct Histogram3D 10 | { 11 | FloatC data; 12 | 13 | Histogram3D() { 14 | data = zero(xdim*ydim*zdim); 15 | } 16 | 17 | void update(const Vector3fC &value) { 18 | IntC idx = IntC(value[0]*FloatC(xdim)); 19 | IntC idy = IntC(value[1]*FloatC(ydim)); 20 | IntC idz = IntC(value[2]*FloatC(zdim)); 21 | IntC idt = idx*xdim*ydim+idy*ydim+idz; 22 | scatter_add(data, FloatC(1.0f), idt); 23 | } 24 | 25 | void normalize() { 26 | data = data * xdim*ydim*zdim / hsum(data) / 1000.f; 27 | } 28 | 29 | FloatC get_data() { 30 | return data; 31 | } 32 | 33 | }; 34 | 35 | template 36 | struct AdaptiveQuadratureDistribution { 37 | 38 | void setup(const Scene &scene, const std::vector &sensor_id, const FloatC &cdfx, const FloatC &cdfy, const FloatC &cdfz, const AQ_Option &option); 39 | Vector3fC sample(const Vector3fC &rnd, FloatC &pdf); 40 | FloatC pdf_mis(const Scene &scene, int sensor_id, const Vector3fC &rnd); 41 | 42 | DiscreteDistribution aq_distrb; 43 | AQLeaf aq_leaf; 44 | bool aq_edge_direct; 45 | FloatC aq_sum; 46 | 47 | private: 48 | void __sample_grid(const Scene &scene, const std::vector &sensor_id, int npass, float RMSE_wt); 49 | FloatC __eval(const AQLeaf &sample_leaf, const Vector3fC &sample); 50 | 51 | FloatC __FZ(const AQLeaf &sample_leaf, const FloatC &rndz); 52 | FloatC __FY(const AQLeaf &sample_leaf, const FloatC fix_z, const FloatC &rndy); 53 | FloatC __FX(const AQLeaf &sample_leaf, const FloatC fix_y, const FloatC fix_z, const FloatC &rndx); 54 | 55 | }; 56 | 57 | NAMESPACE_END(psdr_jit) 58 | -------------------------------------------------------------------------------- /include/psdr/core/bitmap.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | 7 | NAMESPACE_BEGIN(psdr_jit) 8 | 9 | template 10 | struct Bitmap { 11 | static_assert(channels == 1 || channels == 3); 12 | using ScalarValue = typename std::conditional::type; 13 | 14 | template 15 | using Value = typename std::conditional, Vector3f>::type; 16 | 17 | using ValueC = Value; 18 | using ValueD = Value; 19 | 20 | Bitmap(); 21 | Bitmap(const char *file_name); 22 | Bitmap(ScalarValue value); 23 | Bitmap(int width, int height, const ValueD &data); 24 | 25 | void load_openexr(const char *file_name); 26 | 27 | inline void fill(ScalarValue value) { 28 | m_resolution = ScalarVector2i(1, 1); 29 | m_data = ValueD(value); 30 | } 31 | 32 | template Value eval(Vector2f uv, bool flip_v = true, bool envmap_mode = false) const; 33 | 34 | ScalarVector2i m_resolution; 35 | ValueD m_data; 36 | 37 | FloatD m_scale = 1.0f; 38 | Vector2fD m_trans = Vector2fD(0.f,0.f); 39 | FloatD m_rot = 0.f; 40 | }; 41 | 42 | using Bitmap1fD = Bitmap<1>; 43 | using Bitmap3fD = Bitmap<3>; 44 | 45 | NAMESPACE_END(psdr_jit) 46 | -------------------------------------------------------------------------------- /include/psdr/core/bitmap_loader.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | NAMESPACE_BEGIN(psdr_jit) 6 | 7 | 8 | struct BitmapLoader { 9 | static std::pair load_openexr_rgba(const char *file_name); 10 | }; 11 | 12 | 13 | NAMESPACE_END(psdr_jit) 14 | -------------------------------------------------------------------------------- /include/psdr/core/cube_distrb.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include "pmf.h" 5 | #include 6 | 7 | NAMESPACE_BEGIN(psdr_jit) 8 | 9 | template 10 | struct HyperCubeDistribution { 11 | static_assert(ndim > 1); 12 | 13 | void set_resolution(const Array &reso); 14 | void set_mass(const FloatC &pmf); 15 | 16 | FloatC sample_reuse(Vectorf &samples) const; 17 | FloatC pdf(const Vectorf &p) const; 18 | 19 | bool m_ready = false; 20 | Array m_resolution = 0; 21 | 22 | DiscreteDistribution m_distrb; 23 | 24 | int m_num_cells; 25 | Vectori m_cells; 26 | Array m_unit; 27 | }; 28 | 29 | 30 | struct CubeDistribution { 31 | void set_resolution(const Array &reso); 32 | void set_mass(const Bitmap3fD &m_radiance); 33 | 34 | FloatC sample_reuse(Vectorf<2, false> &samples) const; 35 | FloatC pdf(const Vectorf<2, false> &p) const; 36 | 37 | bool m_ready = false; 38 | Array m_resolution = 0; 39 | 40 | DiscreteDistribution m_distrb; 41 | DiscreteDistribution m_theta_distrb; 42 | DiscreteDistributionArrayC m_phi_distrbs; 43 | 44 | int m_num_cells; 45 | Vectori<2, false> m_cells; 46 | Array m_unit; 47 | }; 48 | 49 | 50 | NAMESPACE_END(psdr_jit) 51 | -------------------------------------------------------------------------------- /include/psdr/core/frame.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | NAMESPACE_BEGIN(psdr_jit) 7 | 8 | 9 | template std::pair coordinate_system(const Vector3f &n) { 10 | static_assert(Vector3f::Size == 3, "coordinate_system() expects a 3D vector as input!"); 11 | using Float = value_t; 12 | 13 | /* Based on "Building an Orthonormal Basis, Revisited" by 14 | Tom Duff, James Burgess, Per Christensen, 15 | Christophe Hery, Andrew Kensler, Max Liani, 16 | and Ryusuke Villemin (JCGT Vol 6, No 1, 2017) */ 17 | 18 | Float sign = drjit::sign(n.z()), 19 | a = -rcp(sign + n.z()), 20 | b = n.x() * n.y() * a; 21 | 22 | return { 23 | Vector3f(mulsign(sqr(n.x()) * a, n.z()) + 1.f, 24 | mulsign(b, n.z()), 25 | mulsign_neg(n.x(), n.z())), 26 | Vector3f(b, sign + sqr(n.y()) * a, -n.y()) 27 | }; 28 | } 29 | 30 | 31 | template 32 | struct Frame_ 33 | { 34 | static constexpr bool ad = std::is_same_v; 35 | 36 | inline Frame_(const Vector3f &v) : n(v) { 37 | std::tie(s, t) = coordinate_system(v); 38 | } 39 | 40 | inline Frame_(const Vector3f &s, const Vector3f &t, const Vector3f &n) : s(s), t(t), n(n) {} 41 | 42 | inline Frame_(const Vector3f &n_, const Vector3f &s_) : n(n_), s(s_) { 43 | t = normalize(cross(n, s)); 44 | s = normalize(cross(t, n)); 45 | } 46 | 47 | // inline Frame_(const Frame_ &frame) : s(frame.s), t(frame.t), n(frame.n) {} 48 | 49 | /// Convert from world coordinates to local coordinates 50 | Vector3f to_local(const Vector3f &v) const { 51 | return Vector3f(dot(v, s), dot(v, t), dot(v, n)); 52 | } 53 | 54 | /// Convert from local coordinates to world coordinates 55 | Vector3f to_world(const Vector3f &v) const { 56 | return s*v.x() + t*v.y() + n*v.z(); 57 | } 58 | 59 | /** \brief Give a unit direction, this function returns the cosine of the 60 | * elevation angle in a reference spherical coordinate system (see the \ref 61 | * Frame description) 62 | */ 63 | static Float cos_theta(const Vector3f &v) { return v.z(); } 64 | 65 | /** \brief Give a unit direction, this function returns the square cosine 66 | * of the elevation angle in a reference spherical coordinate system (see 67 | * the \ref Frame description) 68 | */ 69 | static Float cos_theta_2(const Vector3f &v) { return sqr(v.z()); } 70 | 71 | /** \brief Give a unit direction, this function returns the sine 72 | * of the elevation angle in a reference spherical coordinate system (see 73 | * the \ref Frame description) 74 | */ 75 | static Float sin_theta(const Vector3f &v) { return safe_sqrt(sin_theta_2(v)); } 76 | 77 | /** \brief Give a unit direction, this function returns the square sine 78 | * of the elevation angle in a reference spherical coordinate system (see 79 | * the \ref Frame description) 80 | */ 81 | static Float sin_theta_2(const Vector3f &v) { return fmadd(v.x(), v.x(), sqr(v.y())); } 82 | 83 | /** \brief Give a unit direction, this function returns the tangent 84 | * of the elevation angle in a reference spherical coordinate system (see 85 | * the \ref Frame description) 86 | */ 87 | static Float tan_theta(const Vector3f &v) { 88 | Float temp = fnmadd(v.z(), v.z(), 1.f); 89 | return safe_sqrt(temp) / v.z(); 90 | } 91 | 92 | /** \brief Give a unit direction, this function returns the square tangent 93 | * of the elevation angle in a reference spherical coordinate system (see 94 | * the \ref Frame description) 95 | */ 96 | static Float tan_theta_2(const Vector3f &v) { 97 | Float temp = fnmadd(v.z(), v.z(), 1.f); 98 | return max(temp, 0.f) / sqr(v.z()); 99 | } 100 | 101 | /** \brief Give a unit direction, this function returns the sine of the 102 | * azimuth in a reference spherical coordinate system (see the \ref Frame 103 | * description) 104 | */ 105 | static Float sin_phi(const Vector3f &v) { 106 | Float sin_theta_2 = Frame_::sin_theta_2(v), 107 | inv_sin_theta = rsqrt(Frame_::sin_theta_2(v)); 108 | return select(abs(sin_theta_2) <= 4.f * Epsilon, 0.f, 109 | clamp(v.y() * inv_sin_theta, -1.f, 1.f)); 110 | } 111 | 112 | /** \brief Give a unit direction, this function returns the cosine of the 113 | * azimuth in a reference spherical coordinate system (see the \ref Frame 114 | * description) 115 | */ 116 | static Float cos_phi(const Vector3f &v) { 117 | Float sin_theta_2 = Frame_::sin_theta_2(v), 118 | inv_sin_theta = rsqrt(Frame_::sin_theta_2(v)); 119 | return select(abs(sin_theta_2) <= 4.f * Epsilon, 1.f, 120 | clamp(v.x() * inv_sin_theta, -1.f, 1.f)); 121 | } 122 | 123 | /** \brief Give a unit direction, this function returns the sine and cosine 124 | * of the azimuth in a reference spherical coordinate system (see the \ref 125 | * Frame description) 126 | */ 127 | static std::pair, Float> sincos_phi(const Vector3f &v) { 128 | Float sin_theta_2 = Frame_::sin_theta_2(v), 129 | inv_sin_theta = rsqrt(Frame_::sin_theta_2(v)); 130 | 131 | Vector2fD result = head<2>(v) * inv_sin_theta; 132 | 133 | result = select(abs(sin_theta_2) <= 4.f * Epsilon, 134 | Vector2fD(1.f, 0.f), 135 | clamp(result, -1.f, 1.f)); 136 | 137 | return { result.y(), result.x() }; 138 | } 139 | 140 | /** \brief Give a unit direction, this function returns the squared sine of 141 | * the azimuth in a reference spherical coordinate system (see the \ref 142 | * Frame description) 143 | */ 144 | static Float sin_phi_2(const Vector3f &v) { 145 | Float sin_theta_2 = Frame_::sin_theta_2(v); 146 | return select(abs(sin_theta_2) <= 4.f * Epsilon, 0.f, 147 | clamp(sqr(v.y()) / sin_theta_2, -1.f, 1.f)); 148 | } 149 | 150 | /** \brief Give a unit direction, this function returns the squared cosine of 151 | * the azimuth in a reference spherical coordinate system (see the \ref 152 | * Frame description) 153 | */ 154 | static Float cos_phi_2(const Vector3f &v) { 155 | Float sin_theta_2 = Frame_::sin_theta_2(v); 156 | return select(abs(sin_theta_2) <= 4.f * Epsilon, 1.f, 157 | clamp(sqr(v.x()) / sin_theta_2, -1.f, 1.f)); 158 | } 159 | 160 | /** \brief Give a unit direction, this function returns the squared sine 161 | * and cosine of the azimuth in a reference spherical coordinate system 162 | * (see the \ref Frame description) 163 | */ 164 | static std::pair, Float> sincos_phi_2(const Vector3f &v) { 165 | Float sin_theta_2 = Frame_::sin_theta_2(v), 166 | inv_sin_theta_2 = rcp(sin_theta_2); 167 | 168 | Vector2fD result = sqr(head<2>(v)) * inv_sin_theta_2; 169 | 170 | result = select(abs(sin_theta_2) <= 4.f * Epsilon, 171 | Vector2fD(1.f, 0.f), clamp(result, -1.f, 1.f)); 172 | 173 | return { result.y(), result.x() }; 174 | } 175 | 176 | /// Equality test 177 | Mask operator==(const Frame_ &frame) const { 178 | return all(eq(frame.s, s) && eq(frame.t, t) && eq(frame.n, n)); 179 | } 180 | 181 | /// Inequality test 182 | Mask operator!=(const Frame_ &frame) const { 183 | return any(neq(frame.s, s) || neq(frame.t, t) || neq(frame.n, n)); 184 | } 185 | 186 | Vector3f s, t, n; 187 | 188 | DRJIT_STRUCT(Frame_, s, t, n) 189 | }; 190 | 191 | //inline FrameC detach(const FrameD &frame) { 192 | // return FrameC(detach(frame.s), detach(frame.t), detach(frame.n)); 193 | //} 194 | 195 | NAMESPACE_END(psdr_jit) 196 | 197 | // DRJIT_STRUCT_SUPPORT(psdr_jit::Frame_, s, t, n) 198 | -------------------------------------------------------------------------------- /include/psdr/core/intersection.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "frame.h" 4 | 5 | NAMESPACE_BEGIN(psdr_jit) 6 | 7 | template 8 | struct Interaction_ { 9 | static constexpr bool ad = std::is_same_v; 10 | 11 | template 12 | inline Interaction_(const Interaction_ &in) : wi(in.wi), p(in.p), t(in.t) {} 13 | 14 | virtual Mask is_valid() const = 0; 15 | 16 | Vector3f wi, p; 17 | Float t = Infinity; 18 | 19 | DRJIT_STRUCT(Interaction_, wi, p, t) 20 | }; 21 | 22 | 23 | template 24 | struct Intersection_ : public Interaction_ { 25 | PSDR_IMPORT_BASE(Interaction_, ad, wi, p, t) 26 | 27 | Mask is_valid() const override { 28 | return neq(shape, nullptr); 29 | } 30 | 31 | inline Mask is_emitter(Mask active) const { 32 | return neq(shape->emitter(), nullptr); 33 | } 34 | 35 | inline Spectrum Le(Mask active) const { 36 | if constexpr (ad) { 37 | return shape->emitter()->evalD(*this, active); 38 | } else { 39 | return shape->emitter()->evalC(*this, active); 40 | } 41 | 42 | } 43 | 44 | MeshArray shape; 45 | 46 | Vector3f n; // geometric normal 47 | 48 | /// Position partials wrt. the UV parameterization 49 | Vector3f dp_du, dp_dv; 50 | 51 | Frame sh_frame; // shading frame 52 | 53 | Vector2f uv; 54 | Float J; // Jacobian determinant for material-form reparam 55 | 56 | Vector2f bc; // Barycentric coordinates (local uv) 57 | Vector3i face_indices; // Face indices 58 | 59 | DRJIT_STRUCT(Intersection_, wi, p, t, shape, n, dp_du, dp_dv, sh_frame, uv, J, bc, face_indices) 60 | }; 61 | 62 | NAMESPACE_END(psdr_jit) 63 | -------------------------------------------------------------------------------- /include/psdr/core/pmf.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | NAMESPACE_BEGIN(psdr_jit) 11 | 12 | inline FloatC compute_cdf(const FloatC &pmf) { 13 | size_t size = pmf.size(); 14 | const ScalarFloat *ptr_pmf = pmf.data(); 15 | 16 | if (size == 0) 17 | PSDR_ASSERT_MSG(0, "DiscreteDistribution: empty distribution!"); 18 | 19 | std::vector cdf(size); 20 | ScalarVector2u m_valid = (uint32_t) -1; 21 | double sum = 0.0; 22 | for (uint32_t i = 0; i < size; ++i) { 23 | double value = (double) *ptr_pmf++; 24 | sum += value; 25 | cdf[i] = (ScalarFloat) sum; 26 | 27 | if (value < 0.0) { 28 | PSDR_ASSERT_MSG(0, "DiscreteDistribution: entries must be non-negative!"); 29 | } else if (value > 0.0) { 30 | // Determine the first and last wavelength bin with nonzero density 31 | if (m_valid.x() == (uint32_t) -1) 32 | m_valid.x() = i; 33 | m_valid.y() = i; 34 | } 35 | } 36 | FloatC m_cdf = load(cdf.data(), size); 37 | return m_cdf; 38 | } 39 | 40 | struct DiscreteDistribution { 41 | DiscreteDistribution() = default; 42 | 43 | void init(const FloatC &pmf); 44 | 45 | std::pair sample(const FloatC &samples) const; 46 | 47 | template 48 | std::pair sample_reuse(Float &samples) const; 49 | 50 | const FloatC &pmf() const { return m_pmf_normalized; } 51 | const FloatC &cmf() const { return m_cmf_normalized; } 52 | 53 | int m_size; 54 | FloatC m_sum; 55 | 56 | // protected: 57 | FloatC m_pmf, m_pmf_normalized, m_cmf, m_cmf_normalized; 58 | }; 59 | 60 | NAMESPACE_END(psdr_jit) 61 | -------------------------------------------------------------------------------- /include/psdr/core/ray.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | NAMESPACE_BEGIN(psdr_jit) 7 | 8 | template 9 | struct Ray_ { 10 | static constexpr bool ad = std::is_same_v; 11 | 12 | inline Ray_(const Vector3f &o, const Vector3f &d, const Float &tmax) : o(o), d(d), tmax(tmax) { 13 | } 14 | inline Ray_(const Vector3f &o, const Vector3f &d) : o(o), d(d) { 15 | tmax = full>(Infinity, slices>(d)); 16 | } 17 | 18 | inline Ray_ reversed() const { return Ray_(o, -d, tmax); } 19 | 20 | inline int size() const { 21 | return tmax.size(); 22 | } 23 | 24 | Vector3f operator() (const Float &t) const { return fmadd(d, t, o); } 25 | 26 | Vector3f o, d; 27 | Float tmax; 28 | 29 | DRJIT_STRUCT(Ray_, o, d, tmax); 30 | }; 31 | 32 | NAMESPACE_END(psdr_jit) 33 | -------------------------------------------------------------------------------- /include/psdr/core/records.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include "ray.h" 5 | #include "intersection.h" 6 | 7 | NAMESPACE_BEGIN(psdr_jit) 8 | 9 | template 10 | struct DiscreteRecord_ { 11 | static constexpr bool ad = std::is_same_v; 12 | Float rnd; 13 | Float pdf; 14 | Int idx; 15 | 16 | DRJIT_STRUCT(DiscreteRecord_, rnd, pdf, idx) 17 | }; 18 | 19 | template 20 | struct SampleRecord_ { 21 | static constexpr bool ad = std::is_same_v; 22 | 23 | Float pdf; 24 | Mask is_valid; 25 | 26 | DRJIT_STRUCT(SampleRecord_, pdf, is_valid) 27 | }; 28 | 29 | template 30 | struct SampleRecordDual_ { 31 | static constexpr bool ad = std::is_same_v; 32 | 33 | Float pdf1, pdf2; 34 | Mask is_valid1, is_valid2; 35 | 36 | DRJIT_STRUCT(SampleRecordDual_, pdf1, pdf2, is_valid1, is_valid2) 37 | }; 38 | 39 | template 40 | struct PositionSample_ : public SampleRecord_ { 41 | PSDR_IMPORT_BASE(SampleRecord_, ad, pdf, is_valid) 42 | 43 | Vector3f p, n; 44 | Float J; 45 | 46 | DRJIT_STRUCT(PositionSample_, pdf, is_valid, p, n, J) 47 | }; 48 | 49 | 50 | struct BoundarySegSampleDirect : public SampleRecord_ { 51 | PSDR_IMPORT_BASE(SampleRecord_, pdf, is_valid) 52 | 53 | // Sample point on a face edge 54 | Vector3fD p0; 55 | Vector3fC edge, edge2; 56 | 57 | // Sample point on an emitter 58 | Vector3fC p2, n; // for indirect, p2 is a direction 59 | }; 60 | 61 | template 62 | struct BoundaryMISRecord_ { 63 | static constexpr bool ad = std::is_same_v; 64 | Vector3fC p0, dir; 65 | BoundarySegSampleDirect bss; 66 | IntC idx; 67 | Spectrum value; 68 | FloatC pdf; 69 | Mask is_valid; 70 | 71 | DRJIT_STRUCT(BoundaryMISRecord_, p0, dir, bss, idx, value, pdf, is_valid) 72 | }; 73 | 74 | NAMESPACE_END(psdr_jit) 75 | -------------------------------------------------------------------------------- /include/psdr/core/sampler.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | NAMESPACE_BEGIN(psdr_jit) 7 | 8 | struct Sampler { 9 | using PCG32 = drjit::PCG32; 10 | 11 | // std::shared_ptr clone(); 12 | 13 | void seed(UInt64C seed_value); 14 | 15 | inline bool is_ready() const { return m_rng != nullptr; } 16 | 17 | template Float next_1d(); 18 | 19 | template inline Vector2f next_2d() { 20 | return Vector2f(next_1d(), next_1d()); 21 | } 22 | 23 | template inline Vector3f next_3d() { 24 | return Vector3f(next_1d(), next_1d(), next_1d()); 25 | } 26 | 27 | template inline Vectorf next_nd() { 28 | static_assert(n > 0); 29 | if constexpr ( n == 1 ) { 30 | return next_1d(); 31 | } else { 32 | constexpr int m = n / 2; 33 | return concat(next_nd(), next_nd()); 34 | } 35 | } 36 | 37 | int64_t m_sample_count = 0; 38 | uint64_t m_base_seed = PCG32_DEFAULT_STATE; 39 | std::unique_ptr m_rng = nullptr; 40 | }; 41 | 42 | NAMESPACE_END(psdr_jit) 43 | -------------------------------------------------------------------------------- /include/psdr/core/transform.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | 10 | NAMESPACE_BEGIN(psdr_jit) 11 | 12 | namespace transform 13 | { 14 | 15 | /// Create a translation transformation 16 | template 17 | static Matrix translate(const Array &v) { 18 | return drjit::translate>(v); 19 | } 20 | 21 | /// Create a scale transformation 22 | template 23 | static Matrix scale(const Array &v) { 24 | return drjit::scale>(v); 25 | } 26 | 27 | /// Create a rotation transformation around an arbitrary axis in 3D. The angle is specified in degrees 28 | template 29 | static Matrix rotate(const Array &axis, float angle) { 30 | return drjit::rotate>(axis, drjit::deg_to_rad(angle)); 31 | } 32 | 33 | /** \brief Create a perspective transformation. 34 | * (Maps [near, far] to [0, 1]) 35 | * 36 | * Projects vectors in camera space onto a plane at z=1: 37 | * 38 | * x_proj = x / z 39 | * y_proj = y / z 40 | * z_proj = (far * (z - near)) / (z * (far-near)) 41 | * 42 | * Camera-space depths are not mapped linearly! 43 | * 44 | * \param fov Field of view in degrees 45 | * \param near Near clipping plane 46 | * \param far Far clipping plane 47 | */ 48 | static inline ScalarMatrix4f perspective(float fov, float near_, float far_) { 49 | float recip = 1.f / (far_ - near_); 50 | 51 | /* Perform a scale so that the field of view is mapped 52 | to the interval [-1, 1] */ 53 | float tan = drjit::tan(drjit::deg_to_rad(fov * .5f)), 54 | cot = 1.f / tan; 55 | 56 | ScalarMatrix4f trafo = diag(ScalarVector4f(cot, cot, far_ * recip, 0.f)); 57 | trafo(2, 3) = -near_ * far_ * recip; 58 | trafo(3, 2) = 1.f; 59 | 60 | return trafo; 61 | } 62 | 63 | static inline ScalarMatrix4f perspective_intrinsic(float fx, float fy, float cx, float cy, float near_, float far_) { 64 | float recip = 1.f / (far_ - near_); 65 | 66 | ScalarMatrix4f trafo = diag(ScalarVector4f(1.f, 1.f, far_ * recip, 0.f)); 67 | trafo(2, 3) = -near_ * far_ * recip; 68 | trafo(3, 2) = 1.f; 69 | 70 | return translate(ScalarVector3f(1.f - 2.f * cx, 1.f - 2.f * cy, 0.f)) * scale(ScalarVector3f(2.f * fx, 2.f * fy, 1.f)) * trafo; 71 | } 72 | 73 | static inline ScalarMatrix4f orthographic(float near_, float far_) { 74 | return scale(Array(1.f, 1.f, 1.f / (far_ - near_))) * 75 | translate(Array( 0.f, 0.f, -near_ )); 76 | } 77 | 78 | /** \brief Create a look-at camera transformation 79 | * 80 | * \param origin Camera position 81 | * \param target Target vector 82 | * \param up Up vector 83 | */ 84 | template 85 | static Matrix look_at(const Array &origin, const Array &target, const Array &up) { 86 | Array dir = normalize(target - origin); 87 | Array left = normalize(cross(up, dir)); 88 | Array new_up = cross(dir, left); 89 | 90 | Array z(0); 91 | 92 | return Matrix( 93 | concat(left, z), 94 | concat(new_up, z), 95 | concat(dir, z), 96 | Array( 97 | origin[0], 98 | origin[1], 99 | origin[2], 100 | 1.f 101 | ) 102 | ); 103 | } 104 | 105 | 106 | } // namespace transform 107 | 108 | 109 | template 110 | static Array transform_pos(const Matrix &mat, const Array &vec) { 111 | Array tmp = mat*concat(vec, 1.f); 112 | return head<3>(tmp)/tmp.w(); 113 | } 114 | 115 | 116 | template 117 | static Array transform_dir(const Matrix &mat, const Array &vec) { 118 | return head<3>(mat*concat(vec, 0.f)); 119 | } 120 | 121 | // 2d transform 122 | template 123 | static Array transform2d_pos(const Matrix &mat, const Array &vec) { 124 | Array tmp = mat * Array(vec[0], vec[1], 1.f); 125 | return head<2>(tmp)/tmp.z(); 126 | } 127 | 128 | NAMESPACE_END(psdr_jit) 129 | -------------------------------------------------------------------------------- /include/psdr/core/warp.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include "frame.h" 5 | #include 6 | #include 7 | #include 8 | 9 | NAMESPACE_BEGIN(psdr_jit) 10 | 11 | namespace warp 12 | { 13 | 14 | /// Low-distortion concentric square to disk mapping by Peter Shirley 15 | template 16 | inline Vector2f square_to_uniform_disk_concentric(const Vector2f &sample) { 17 | Float x = fmsub(2.f, sample.x(), 1.f), 18 | y = fmsub(2.f, sample.y(), 1.f); 19 | 20 | /* Modified concentric map code with less branching (by Dave Cline), see 21 | http://psgraphics.blogspot.ch/2011/01/improved-code-for-concentric-map.html 22 | 23 | Original non-vectorized version: 24 | 25 | Value phi, r; 26 | if (x == 0 && y == 0) { 27 | r = phi = 0; 28 | } else if (x * x > y * y) { 29 | r = x; 30 | phi = (math::Pi / 4.f) * (y / x); 31 | } else { 32 | r = y; 33 | phi = (math::Pi / 2.f) - (x / y) * (math::Pi / 4.f); 34 | } 35 | */ 36 | 37 | Mask is_zero = eq(x, 0.f) && 38 | eq(y, 0.f), 39 | quadrant_1_or_3 = abs(x) < abs(y); 40 | 41 | Float r = select(quadrant_1_or_3, y, x), 42 | rp = select(quadrant_1_or_3, x, y); 43 | 44 | Float phi = .25f * Pi * rp / r; 45 | masked(phi, quadrant_1_or_3) = .5f * Pi - phi; 46 | masked(phi, is_zero) = 0.f; 47 | 48 | auto [s, c] = sincos(phi); 49 | return { r * c, r * s }; 50 | } 51 | 52 | 53 | /// Sample a cosine-weighted vector on the unit hemisphere with respect to solid angles 54 | template 55 | inline Vector3f square_to_cosine_hemisphere(const Vector2f &sample) { 56 | // Low-distortion warping technique based on concentric disk mapping 57 | Vector2f p = square_to_uniform_disk_concentric(sample); 58 | 59 | // Guard against numerical imprecisions 60 | Float z = safe_sqrt(1.f - squared_norm(p)); 61 | 62 | return { p.x(), p.y(), z }; 63 | } 64 | 65 | 66 | /// Density of \ref square_to_cosine_hemisphere() with respect to solid angles 67 | template 68 | inline Float square_to_cosine_hemisphere_pdf(const Vector3f &v) { 69 | if constexpr (TestDomain) 70 | return select(abs(squared_norm(v) - 1.f) > RayEpsilon || 71 | v.z() < 0.f, zeros>(), InvPi * v.z()); 72 | else 73 | return InvPi * v.z(); 74 | } 75 | 76 | 77 | /// Convert an uniformly distributed square sample into barycentric coordinates 78 | template 79 | inline Vector2f square_to_uniform_triangle(const Vector2f &sample) { 80 | Float t = safe_sqrt(1.f - sample.x()); 81 | return { 1.f - t, t * sample.y() }; 82 | } 83 | 84 | /// Density of \ref square_to_uniform_triangle per unit area. 85 | template 86 | inline Float square_to_uniform_triangle_pdf(const Vector2f &p) { 87 | if constexpr (TestDomain) { 88 | return select( 89 | p.x() < zeros>() || p.y() < zeros>() 90 | || (p.x() + p.y() > 1.f), 91 | zeros>(), 92 | 2.f 93 | ); 94 | } else { 95 | return 2.f; 96 | } 97 | } 98 | 99 | 100 | } // namespace warp 101 | 102 | NAMESPACE_END(psdr_jit) 103 | -------------------------------------------------------------------------------- /include/psdr/edge/edge.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | NAMESPACE_BEGIN(psdr_jit) 8 | 9 | /******************************************** 10 | * Primary edge info types 11 | ********************************************/ 12 | 13 | struct PrimaryEdgeSample { 14 | FloatD x_dot_n; 15 | IntC idx; 16 | RayC ray_n, ray_p; 17 | 18 | #ifdef PSDR_PRIMARY_EDGE_VIS_CHECK 19 | RayC ray_c; 20 | #endif 21 | 22 | FloatC pdf; 23 | }; 24 | 25 | 26 | template 27 | struct PrimaryEdgeInfo_ { 28 | static constexpr bool ad = std::is_same_v; 29 | 30 | #ifdef PSDR_PRIMARY_EDGE_VIS_CHECK 31 | Vector3f p0, p1; 32 | #else 33 | Vector2f p0, p1; 34 | #endif 35 | Vector2f edge_normal; 36 | Float edge_length; 37 | 38 | DRJIT_STRUCT(PrimaryEdgeInfo_, p0, p1, edge_normal, edge_length) 39 | }; 40 | 41 | using PrimaryEdgeInfo = PrimaryEdgeInfo_; 42 | 43 | 44 | /******************************************** 45 | * Secondary edge info 46 | ********************************************/ 47 | 48 | template 49 | struct SecondaryEdgeInfo_ { 50 | static constexpr bool ad = std::is_same_v; 51 | 52 | // p0 and (p0 + e1) are the two endpoints of the edge 53 | Vector3f p0, e1; 54 | 55 | // n0 and n1 are the normals of the two faces sharing the edge 56 | Vector3f n0, n1; 57 | 58 | // p2 is the third vertex of the face with normal n0 59 | Vector3f p2; 60 | 61 | Mask is_boundary; 62 | 63 | int size() { 64 | return is_boundary.size(); 65 | } 66 | 67 | DRJIT_STRUCT(SecondaryEdgeInfo_, p0, e1, n0, n1, p2, is_boundary) 68 | }; 69 | 70 | using SecondaryEdgeInfo = SecondaryEdgeInfo_; 71 | 72 | 73 | 74 | NAMESPACE_END(psdr_jit) 75 | 76 | -------------------------------------------------------------------------------- /include/psdr/emitter/area.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include "emitter.h" 5 | 6 | NAMESPACE_BEGIN(psdr_jit) 7 | 8 | PSDR_CLASS_DECL_BEGIN(AreaLight, final, Emitter) 9 | public: 10 | AreaLight(const ScalarVector3f &radiance) : m_radiance(radiance) {} 11 | AreaLight(const ScalarVector3f &radiance, const Mesh *mesh) : m_radiance(radiance), m_mesh(mesh) {} 12 | AreaLight(const Mesh *mesh) : m_mesh(mesh) {} 13 | 14 | void configure() override; 15 | 16 | SpectrumC eval(const IntersectionC &its, MaskC active = true) const override; 17 | SpectrumD eval(const IntersectionD &its, MaskD active = true) const override; 18 | 19 | PositionSampleC sample_position(const Vector3fC &ref_p, const Vector2fC &sample2, MaskC active = true) const override; 20 | PositionSampleD sample_position(const Vector3fD &ref_p, const Vector2fD &sample2, MaskD active = true) const override; 21 | 22 | FloatC sample_position_pdf(const Vector3fC &ref_p, const IntersectionC &its, MaskC active = true) const override; 23 | FloatD sample_position_pdf(const Vector3fD &ref_p, const IntersectionD &its, MaskD active = true) const override; 24 | 25 | std::string to_string() const override; 26 | 27 | SpectrumD m_radiance; 28 | const Mesh *m_mesh; 29 | 30 | protected: 31 | template 32 | PositionSample __sample_position(const Vector2f&, Mask) const; 33 | 34 | template 35 | Float __sample_position_pdf(const Vector3f&, const Intersection&, Mask) const; 36 | PSDR_CLASS_DECL_END(AreaLight) 37 | 38 | NAMESPACE_END(psdr_jit) 39 | -------------------------------------------------------------------------------- /include/psdr/emitter/emitter.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | NAMESPACE_BEGIN(psdr_jit) 7 | 8 | PSDR_CLASS_DECL_BEGIN(Emitter,, Object) 9 | public: 10 | virtual ~Emitter() override {} 11 | 12 | virtual void configure() = 0; 13 | 14 | // Returns the emitted radiance at its.p in direction its.wi 15 | 16 | virtual SpectrumC eval(const IntersectionC &its, MaskC active = true) const = 0; 17 | virtual SpectrumD eval(const IntersectionD &its, MaskD active = true) const = 0; 18 | 19 | SpectrumC evalC(const IntersectionC &its, MaskC active = true) const { 20 | return eval(its, active); 21 | }; 22 | SpectrumD evalD(const IntersectionD &its, MaskD active = true) const { 23 | return eval(its, active); 24 | }; 25 | 26 | virtual PositionSampleC sample_position(const Vector3fC &ref_p, const Vector2fC &sample2, MaskC active = true) const = 0; 27 | virtual PositionSampleD sample_position(const Vector3fD &ref_p, const Vector2fD &sample2, MaskD active = true) const = 0; 28 | 29 | PositionSampleC sample_positionC(const Vector3fC &ref_p, const Vector2fC &sample2, MaskC active = true) const { 30 | return sample_position(ref_p, sample2, active); 31 | }; 32 | PositionSampleD sample_positionD(const Vector3fD &ref_p, const Vector2fD &sample2, MaskD active = true) const { 33 | return sample_position(ref_p, sample2, active); 34 | }; 35 | 36 | 37 | virtual FloatC sample_position_pdf(const Vector3fC &ref_p, const IntersectionC &its, MaskC active = true) const = 0; 38 | virtual FloatD sample_position_pdf(const Vector3fD &ref_p, const IntersectionD &its, MaskD active = true) const = 0; 39 | 40 | FloatC sample_position_pdfC(const Vector3fC &ref_p, const IntersectionC &its, MaskC active = true) const { 41 | return sample_position_pdf(ref_p, its, active); 42 | }; 43 | FloatD sample_position_pdfD(const Vector3fD &ref_p, const IntersectionD &its, MaskD active = true) const { 44 | return sample_position_pdf(ref_p, its, active); 45 | }; 46 | 47 | bool m_ready = false; 48 | float m_sampling_weight = 1.f; 49 | 50 | DRJIT_VCALL_REGISTER(FloatD, Emitter); 51 | 52 | PSDR_CLASS_DECL_END(Emitter) 53 | 54 | NAMESPACE_END(psdr_jit) 55 | 56 | DRJIT_VCALL_BEGIN(psdr_jit::Emitter) 57 | DRJIT_VCALL_METHOD(eval) 58 | DRJIT_VCALL_METHOD(evalC) 59 | DRJIT_VCALL_METHOD(evalD) 60 | DRJIT_VCALL_METHOD(sample_position) 61 | DRJIT_VCALL_METHOD(sample_positionC) 62 | DRJIT_VCALL_METHOD(sample_positionD) 63 | DRJIT_VCALL_METHOD(sample_position_pdf) 64 | DRJIT_VCALL_METHOD(sample_position_pdfC) 65 | DRJIT_VCALL_METHOD(sample_position_pdfD) 66 | DRJIT_VCALL_END(psdr_jit::Emitter) 67 | -------------------------------------------------------------------------------- /include/psdr/emitter/envmap.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include "emitter.h" 7 | 8 | NAMESPACE_BEGIN(psdr_jit) 9 | 10 | PSDR_CLASS_DECL_BEGIN(EnvironmentMap, final, Emitter) 11 | public: 12 | inline EnvironmentMap() {} 13 | 14 | inline EnvironmentMap(const char *file_name) { 15 | m_radiance.load_openexr(file_name); 16 | } 17 | 18 | void configure() override; 19 | 20 | inline void set_transform(const Matrix4fD &mat) { 21 | m_to_world_left = mat; 22 | m_ready = false; 23 | } 24 | 25 | SpectrumC eval(const IntersectionC &its, MaskC active = true) const override; 26 | SpectrumD eval(const IntersectionD &its, MaskD active = true) const override; 27 | 28 | template 29 | Spectrum eval_direction(const Vector3f &wi, Mask active = true) const; 30 | 31 | PositionSampleC sample_position(const Vector3fC &ref_p, const Vector2fC &sample2, MaskC active = true) const override; 32 | PositionSampleD sample_position(const Vector3fD &ref_p, const Vector2fD &sample2, MaskD active = true) const override; 33 | 34 | FloatC sample_position_pdf(const Vector3fC &ref_p, const IntersectionC &its, MaskC active = true) const override; 35 | FloatD sample_position_pdf(const Vector3fD &ref_p, const IntersectionD &its, MaskD active = true) const override; 36 | 37 | std::string to_string() const override; 38 | 39 | Bitmap3fD m_radiance; // stored in latitude-longitude format 40 | FloatD m_scale = 1.f; 41 | 42 | Matrix4fD m_to_world_raw = identity(), 43 | m_to_world_left = identity(), 44 | m_to_world, m_from_world; 45 | 46 | Vector3fC m_lower, m_upper; 47 | HyperCubeDistribution2f m_cell_distrb; 48 | 49 | // CubeDistribution m_cube_distrb; 50 | 51 | 52 | template 53 | PositionSample __sample_position(const Vector3f &ref_p, const Vector2f&, Mask) const; 54 | 55 | std::pair sample_direction(Vector2fC&) const; 56 | 57 | template 58 | Float __sample_position_pdf(const Vector3f &, const Intersection &, Mask) const; 59 | PSDR_CLASS_DECL_END(EnvironmentMap) 60 | 61 | NAMESPACE_END(psdr_jit) 62 | -------------------------------------------------------------------------------- /include/psdr/fwd.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | NAMESPACE_BEGIN(psdr_jit) 4 | // Core classes 5 | 6 | template struct Frame_; 7 | template 8 | using Frame = Frame_>; 9 | using FrameC = Frame; 10 | using FrameD = Frame; 11 | 12 | // template struct Ray; 13 | // using RayC = Ray; 14 | // using RayD = Ray; 15 | 16 | template struct Ray_; 17 | template 18 | using Ray = Ray_>; 19 | using RayC = Ray; 20 | using RayD = Ray; 21 | 22 | 23 | 24 | template struct Interaction_; 25 | 26 | template 27 | using Interaction = Interaction_>; 28 | 29 | using InteractionC = Interaction; 30 | using InteractionD = Interaction; 31 | 32 | struct Intersection_OptiX; 33 | 34 | // template struct Intersection; 35 | template struct Intersection_; 36 | 37 | template 38 | using Intersection = Intersection_>; 39 | 40 | using IntersectionC = Intersection; 41 | using IntersectionD = Intersection; 42 | 43 | struct Sampler; 44 | 45 | struct DiscreteDistribution; 46 | 47 | template struct HyperCubeDistribution; 48 | using HyperCubeDistribution2f = HyperCubeDistribution<2>; 49 | using HyperCubeDistribution3f = HyperCubeDistribution<3>; 50 | 51 | template struct AdaptiveQuadratureDistribution; 52 | using AdaptiveQuadratureDistribution3f = AdaptiveQuadratureDistribution<3>; 53 | struct MicrofacetDistribution; 54 | 55 | // Sampling records 56 | 57 | template struct BoundaryMISRecord_; 58 | template 59 | using BoundaryMISRecord = BoundaryMISRecord_>; 60 | using BoundaryMISRecordC = BoundaryMISRecord; 61 | using BoundaryMISRecordD = BoundaryMISRecord; 62 | 63 | 64 | 65 | template struct SampleRecord_; 66 | template 67 | using SampleRecord = SampleRecord_>; 68 | using SampleRecordC = SampleRecord; 69 | using SampleRecordD = SampleRecord; 70 | 71 | template struct SampleRecordDual_; 72 | template 73 | using SampleRecordDual = SampleRecordDual_>; 74 | using SampleRecordDualC = SampleRecordDual; 75 | using SampleRecordDualD = SampleRecordDual; 76 | 77 | template struct DirectionSample_; 78 | template 79 | using DirectionSample = DirectionSample_>; 80 | using DirectionSampleC = DirectionSample; 81 | using DirectionSampleD = DirectionSample; 82 | 83 | template struct PositionSample_; 84 | template 85 | using PositionSample = PositionSample_>; 86 | using PositionSampleC = PositionSample; 87 | using PositionSampleD = PositionSample; 88 | 89 | template struct BSDFSample_; 90 | template 91 | using BSDFSample = BSDFSample_>; 92 | using BSDFSampleC = BSDFSample; 93 | using BSDFSampleD = BSDFSample; 94 | 95 | template struct BSDFSampleDual_; 96 | template 97 | using BSDFSampleDual = BSDFSampleDual_>; 98 | using BSDFSampleDualC = BSDFSampleDual; 99 | using BSDFSampleDualD = BSDFSampleDual; 100 | 101 | 102 | template struct SensorDirectSample_; 103 | template 104 | using SensorDirectSample = SensorDirectSample_>; 105 | using SensorDirectSampleC = SensorDirectSample; 106 | using SensorDirectSampleD = SensorDirectSample; 107 | 108 | struct BoundarySegSampleDirect; 109 | struct BoundarySegSampleIndirect; 110 | 111 | // Main classes 112 | template struct DiscreteRecord_; 113 | template 114 | using DiscreteRecord = DiscreteRecord_>; 115 | using DiscreteRecordC = DiscreteRecord; 116 | using DiscreteRecordD = DiscreteRecord; 117 | 118 | 119 | 120 | class DDistribution; 121 | template 122 | using DiscreteDistributionArray = Type; 123 | using DiscreteDistributionArrayC = DiscreteDistributionArray; 124 | using DiscreteDistributionArrayD = DiscreteDistributionArray; 125 | 126 | 127 | class _DiscreteDistribution; 128 | // template 129 | // using DiscreteDistributionArray = Type<_DiscreteDistribution*, ad>; 130 | // using DiscreteDistributionArrayC = DiscreteDistributionArray; 131 | // using DiscreteDistributionArrayD = DiscreteDistributionArray; 132 | 133 | 134 | class BSDF; 135 | template 136 | using BSDFArray = Type; 137 | using BSDFArrayC = BSDFArray; 138 | using BSDFArrayD = BSDFArray; 139 | 140 | class Diffuse; 141 | class RoughConductor; 142 | class NormalMap; 143 | 144 | class Emitter; 145 | template 146 | using EmitterArray = Type; 147 | using EmitterArrayC = EmitterArray; 148 | using EmitterArrayD = EmitterArray; 149 | 150 | class AreaLight; 151 | class EnvironmentMap; 152 | 153 | class Sensor; 154 | class PerspectiveCamera; 155 | 156 | class Mesh; 157 | template 158 | using MeshArray = Type; 159 | using MeshArrayC = MeshArray; 160 | using MeshArrayD = MeshArray; 161 | 162 | class Integrator; 163 | class FieldExtractionIntegrator; 164 | class DirectIntegrator; 165 | 166 | class Scene_OptiX; 167 | class Scene; 168 | class SceneLoader; 169 | 170 | 171 | NAMESPACE_END(psdr_jit) 172 | 173 | -------------------------------------------------------------------------------- /include/psdr/integrator/collocated.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "integrator.h" 4 | 5 | NAMESPACE_BEGIN(psdr_jit) 6 | 7 | PSDR_CLASS_DECL_BEGIN(CollocatedIntegrator, final, Integrator) 8 | public: 9 | CollocatedIntegrator(const FloatD &intensity) : m_intensity(intensity) {} 10 | 11 | FloatD m_intensity; 12 | 13 | protected: 14 | SpectrumC Li(const Scene &scene, Sampler &sampler, const RayC &ray, MaskC active = true) const override; 15 | SpectrumD Li(const Scene &scene, Sampler &sampler, const RayD &ray, MaskD active = true) const override; 16 | 17 | template 18 | Spectrum __Li(const Scene &scene, const Ray &ray, Mask active) const; 19 | PSDR_CLASS_DECL_END(CollocatedIntegrator) 20 | 21 | NAMESPACE_END(psdr_jit) 22 | -------------------------------------------------------------------------------- /include/psdr/integrator/direct.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "integrator.h" 4 | 5 | NAMESPACE_BEGIN(psdr_jit) 6 | 7 | PSDR_CLASS_DECL_BEGIN(DirectIntegrator, final, Integrator) 8 | public: 9 | DirectIntegrator(int mis = 1); 10 | virtual ~DirectIntegrator(); 11 | 12 | bool m_hide_emitters = false; 13 | void preprocess_secondary_edges(const Scene &scene, int sensor_id, const ScalarVector4i &reso, int nrounds = 1, int seed = 0); 14 | 15 | protected: 16 | SpectrumC Li(const Scene &scene, Sampler &sampler, const RayC &ray, MaskC active = true) const override; 17 | SpectrumD Li(const Scene &scene, Sampler &sampler, const RayD &ray, MaskD active = true) const override; 18 | 19 | template 20 | Spectrum __Li(const Scene &scene, Sampler &sampler, const Ray &ray, Mask active) const; 21 | 22 | void render_secondary_edges(const Scene &scene, int sensor_id, SpectrumD &result) const override; 23 | 24 | 25 | template 26 | std::pair> eval_secondary_edge(const Scene &scene, const Sensor &sensor, const Vector3fC &sample3) const; 27 | 28 | int m_mis; 29 | 30 | std::vector m_warpper; 31 | 32 | PSDR_CLASS_DECL_END(PathTracer) 33 | 34 | NAMESPACE_END(psdr_jit) 35 | -------------------------------------------------------------------------------- /include/psdr/integrator/field.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "integrator.h" 4 | 5 | NAMESPACE_BEGIN(psdr_jit) 6 | 7 | PSDR_CLASS_DECL_BEGIN(FieldExtractionIntegrator, final, Integrator) 8 | public: 9 | FieldExtractionIntegrator(char *field); 10 | 11 | std::string m_field; 12 | std::string m_object; 13 | 14 | protected: 15 | SpectrumC Li(const Scene &scene, Sampler &sampler, const RayC &ray, MaskC active = true) const override; 16 | SpectrumD Li(const Scene &scene, Sampler &sampler, const RayD &ray, MaskD active = true) const override; 17 | 18 | template 19 | Spectrum __Li(const Scene &scene, const Ray &ray, Mask active) const; 20 | PSDR_CLASS_DECL_END(FieldExtractionIntegrator) 21 | 22 | NAMESPACE_END(psdr_jit) 23 | -------------------------------------------------------------------------------- /include/psdr/integrator/integrator.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | NAMESPACE_BEGIN(psdr_jit) 7 | 8 | PSDR_CLASS_DECL_BEGIN(Integrator,, Object) 9 | public: 10 | virtual ~Integrator() {} 11 | 12 | SpectrumC renderC(const Scene &scene, int sensor_id = 0, int seed=-1, IntC batch_pix=-1) const; 13 | SpectrumD renderD(const Scene &scene, int sensor_id = 0, int seed=-1, IntD batch_pix=-1) const; 14 | 15 | protected: 16 | virtual SpectrumC Li(const Scene &scene, Sampler &sampler, const RayC &ray, MaskC active = true) const = 0; 17 | virtual SpectrumD Li(const Scene &scene, Sampler &sampler, const RayD &ray, MaskD active = true) const = 0; 18 | 19 | 20 | virtual void render_primary_edges(const Scene &scene, int sensor_id, SpectrumD &result) const; 21 | 22 | virtual void render_secondary_edges(const Scene &scene, int sensor_id, SpectrumD &result) const {} 23 | 24 | template 25 | Spectrum __render(const Scene &scene, int sensor_id) const; 26 | 27 | template 28 | Spectrum __render_batch(const Scene &scene, int sensor_id, Int batch_pix) const; 29 | 30 | PSDR_CLASS_DECL_END(SamplingIntegrator) 31 | 32 | NAMESPACE_END(psdr_jit) 33 | -------------------------------------------------------------------------------- /include/psdr/integrator/path.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "integrator.h" 4 | 5 | NAMESPACE_BEGIN(psdr_jit) 6 | 7 | PSDR_CLASS_DECL_BEGIN(PathTracer, final, Integrator) 8 | public: 9 | PathTracer(int max_depth = 1); 10 | virtual ~PathTracer(); 11 | 12 | bool m_hide_emitters = false; 13 | void preprocess_secondary_edges(const Scene &scene, int sensor_id, const ScalarVector4i &reso, int nrounds = 1, int seed = 0); 14 | 15 | protected: 16 | SpectrumC Li(const Scene &scene, Sampler &sampler, const RayC &ray, MaskC active = true) const override; 17 | SpectrumD Li(const Scene &scene, Sampler &sampler, const RayD &ray, MaskD active = true) const override; 18 | 19 | template 20 | Spectrum __Li(const Scene &scene, Sampler &sampler, const Ray &ray, Mask active) const; 21 | 22 | void render_secondary_edges(const Scene &scene, int sensor_id, SpectrumD &result) const override; 23 | 24 | 25 | template 26 | std::pair> eval_secondary_edge(const Scene &scene, const Sensor &sensor, const Vector3fC &sample3) const; 27 | 28 | int m_max_depth; 29 | 30 | std::vector m_warpper; 31 | 32 | PSDR_CLASS_DECL_END(PathTracer) 33 | 34 | NAMESPACE_END(psdr_jit) 35 | -------------------------------------------------------------------------------- /include/psdr/jit_optix_test.h: -------------------------------------------------------------------------------- 1 | struct jit_test { 2 | void trace_ray(); 3 | }; 4 | -------------------------------------------------------------------------------- /include/psdr/macros.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #ifdef _WIN32 4 | # define likely(x) (x) 5 | # define unlikely(x) (x) 6 | #else 7 | # define likely(x) __builtin_expect((x),1) 8 | # define unlikely(x) __builtin_expect((x),0) 9 | #endif 10 | 11 | // #define PSDR_OPTIX_DEBUG 12 | // #define PSDR_MESH_ENABLE_1D_VERTEX_OFFSET 13 | // #define PSDR_PRIMARY_EDGE_VIS_CHECK 14 | 15 | 16 | #define PSDR_CLASS_DECL_BEGIN(_class_, _mode_, _parent_) \ 17 | class _class_ _mode_ : public _parent_ { 18 | 19 | 20 | #define PSDR_CLASS_DECL_END(_class_) \ 21 | public: \ 22 | virtual std::string type_name() const override { \ 23 | return #_class_; \ 24 | } \ 25 | }; 26 | 27 | // #define NAMESPACE_BEGIN(name) namespace name { 28 | 29 | 30 | #define __PSDR_USING_MEMBERS_MACRO__(x) using Base::x; 31 | #define PSDR_USING_MEMBERS(...) DRJIT_MAP(__PSDR_USING_MEMBERS_MACRO__, __VA_ARGS__) 32 | 33 | 34 | #define PSDR_IMPORT_BASE(Name, ...) \ 35 | using Base = Name; \ 36 | PSDR_USING_MEMBERS(__VA_ARGS__) 37 | 38 | // #define NAMESPACE_BEGIN(psdr_jit) namespace psdr_jit { 39 | 40 | // #define PSDR_NAMESPACE_END } 41 | 42 | 43 | #if !defined(NAMESPACE_BEGIN) 44 | # define NAMESPACE_BEGIN(name) namespace name { 45 | #endif 46 | #if !defined(NAMESPACE_END) 47 | # define NAMESPACE_END(name) } 48 | #endif 49 | -------------------------------------------------------------------------------- /include/psdr/object.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | // Object Base Class 4 | NAMESPACE_BEGIN(psdr_jit) 5 | 6 | class Object { 7 | public: 8 | virtual ~Object() {} 9 | 10 | virtual std::string type_name() const = 0; 11 | 12 | void log(const char *msg) const { 13 | std::cout << "[" << type_name() << "] " << msg << std::endl; 14 | } 15 | 16 | virtual std::string to_string() const { 17 | std::stringstream oss; 18 | oss << type_name(); 19 | if ( m_id != "" ) oss << "[id=" << m_id << "]"; 20 | return oss.str(); 21 | } 22 | 23 | std::string m_id = ""; 24 | }; 25 | 26 | } -------------------------------------------------------------------------------- /include/psdr/optix/ptx.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #if defined(_WIN32) || defined(_WIN64) 4 | # define PSDRAPI __declspec(dllimport) 5 | # define PSDRCLASSAPI 6 | #else 7 | # define PSDRAPI __attribute__ ((visibility ("default"))) 8 | # define PSDRCLASSAPI PSDRAPI 9 | #endif 10 | 11 | #define OPTIX_SAMPLE_NAME_STRINGIFY2(name) #name 12 | #define OPTIX_SAMPLE_NAME_STRINGIFY(name) OPTIX_SAMPLE_NAME_STRINGIFY2(name) 13 | #define OPTIX_SAMPLE_NAME OPTIX_SAMPLE_NAME_STRINGIFY(OPTIX_SAMPLE_NAME_DEFINE) 14 | 15 | namespace psdr_jit { 16 | 17 | PSDRAPI const char* getPtxString(const char* sample, const char* filename, const char** log = NULL ); 18 | 19 | } // end namespace PSDR 20 | -------------------------------------------------------------------------------- /include/psdr/optix_stubs.h: -------------------------------------------------------------------------------- 1 | #include 2 | // ===================================================== 3 | // Various opaque handles and enumerations 4 | // ===================================================== 5 | 6 | using CUdeviceptr = void*; 7 | using CUstream = void*; 8 | using OptixPipeline = void *; 9 | using OptixModule = void *; 10 | using OptixProgramGroup = void *; 11 | using OptixResult = int; 12 | using OptixTraversableHandle = unsigned long long; 13 | using OptixBuildOperation = int; 14 | using OptixBuildInputType = int; 15 | using OptixVertexFormat = int; 16 | using OptixIndicesFormat = int; 17 | using OptixTransformFormat = int; 18 | using OptixAccelPropertyType = int; 19 | using OptixProgramGroupKind = int; 20 | 21 | // ===================================================== 22 | // Commonly used OptiX constants 23 | // ===================================================== 24 | 25 | #define OPTIX_BUILD_INPUT_TYPE_TRIANGLES 0x2141 26 | #define OPTIX_BUILD_OPERATION_BUILD 0x2161 27 | #define OPTIX_GEOMETRY_FLAG_DISABLE_ANYHIT 1 28 | #define OPTIX_VERTEX_FORMAT_FLOAT3 0x2121 29 | #define OPTIX_SBT_RECORD_HEADER_SIZE 32 30 | #define OPTIX_INDICES_FORMAT_UNSIGNED_INT3 0x2103 31 | #define OPTIX_COMPILE_OPTIMIZATION_LEVEL_0 0x2340 32 | #define OPTIX_COMPILE_OPTIMIZATION_DEFAULT 0 33 | #define OPTIX_COMPILE_DEBUG_LEVEL_MINIMAL 0x2351 34 | 35 | #define OPTIX_COMPILE_DEBUG_LEVEL_NONE 0x2350 36 | 37 | #define OPTIX_BUILD_FLAG_ALLOW_COMPACTION 2 38 | #define OPTIX_BUILD_FLAG_PREFER_FAST_TRACE 4 39 | #define OPTIX_PROPERTY_TYPE_COMPACTED_SIZE 0x2181 40 | 41 | #define OPTIX_EXCEPTION_FLAG_NONE 0 42 | #define OPTIX_EXCEPTION_FLAG_STACK_OVERFLOW 1 43 | #define OPTIX_EXCEPTION_FLAG_TRACE_DEPTH 2 44 | #define OPTIX_EXCEPTION_FLAG_DEBUG 8 45 | 46 | #define OPTIX_PROGRAM_GROUP_KIND_MISS 0x2422 47 | #define OPTIX_PROGRAM_GROUP_KIND_EXCEPTION 0x2423 48 | #define OPTIX_PROGRAM_GROUP_KIND_HITGROUP 0x2424 49 | 50 | #define OPTIX_TRAVERSABLE_GRAPH_FLAG_ALLOW_SINGLE_GAS 1 51 | #define OPTIX_PRIMITIVE_TYPE_FLAGS_TRIANGLE (1 << 31) 52 | #define OPTIX_SBT_RECORD_ALIGNMENT 16ull 53 | #define OPTIX_SBT_RECORD_HEADER_SIZE 32 54 | 55 | 56 | // ===================================================== 57 | // Commonly used OptiX data structures 58 | // ===================================================== 59 | 60 | struct OptixMotionOptions { 61 | unsigned short numKeys; 62 | unsigned short flags; 63 | float timeBegin; 64 | float timeEnd; 65 | }; 66 | 67 | struct OptixAccelBuildOptions { 68 | unsigned int buildFlags; 69 | OptixBuildOperation operation; 70 | OptixMotionOptions motionOptions; 71 | }; 72 | 73 | struct OptixAccelBufferSizes { 74 | size_t outputSizeInBytes; 75 | size_t tempSizeInBytes; 76 | size_t tempUpdateSizeInBytes; 77 | }; 78 | 79 | struct OptixBuildInputTriangleArray { 80 | const CUdeviceptr* vertexBuffers; 81 | unsigned int numVertices; 82 | OptixVertexFormat vertexFormat; 83 | unsigned int vertexStrideInBytes; 84 | CUdeviceptr indexBuffer; 85 | unsigned int numIndexTriplets; 86 | OptixIndicesFormat indexFormat; 87 | unsigned int indexStrideInBytes; 88 | CUdeviceptr preTransform; 89 | const unsigned int* flags; 90 | unsigned int numSbtRecords; 91 | CUdeviceptr sbtIndexOffsetBuffer; 92 | unsigned int sbtIndexOffsetSizeInBytes; 93 | unsigned int sbtIndexOffsetStrideInBytes; 94 | unsigned int primitiveIndexOffset; 95 | OptixTransformFormat transformFormat; 96 | }; 97 | 98 | struct OptixBuildInput { 99 | OptixBuildInputType type; 100 | union { 101 | OptixBuildInputTriangleArray triangleArray; 102 | char pad[1024]; 103 | }; 104 | }; 105 | 106 | struct OptixPayloadType { 107 | unsigned int numPayloadValues; 108 | const unsigned int *payloadSemantics; 109 | }; 110 | 111 | struct OptixModuleCompileOptions { 112 | int maxRegisterCount; 113 | int optLevel; 114 | int debugLevel; 115 | const void *boundValues; 116 | unsigned int numBoundValues; 117 | unsigned int numPayloadTypes; 118 | OptixPayloadType *payloadTypes; 119 | }; 120 | 121 | struct OptixPipelineCompileOptions { 122 | int usesMotionBlur; 123 | unsigned int traversableGraphFlags; 124 | int numPayloadValues; 125 | int numAttributeValues; 126 | unsigned int exceptionFlags; 127 | const char* pipelineLaunchParamsVariableName; 128 | unsigned int usesPrimitiveTypeFlags; 129 | }; 130 | 131 | struct OptixAccelEmitDesc { 132 | CUdeviceptr result; 133 | OptixAccelPropertyType type; 134 | }; 135 | 136 | struct OptixProgramGroupSingleModule { 137 | OptixModule module; 138 | const char* entryFunctionName; 139 | }; 140 | 141 | struct OptixProgramGroupHitgroup { 142 | OptixModule moduleCH; 143 | const char* entryFunctionNameCH; 144 | OptixModule moduleAH; 145 | const char* entryFunctionNameAH; 146 | OptixModule moduleIS; 147 | const char* entryFunctionNameIS; 148 | }; 149 | 150 | struct OptixProgramGroupDesc { 151 | OptixProgramGroupKind kind; 152 | unsigned int flags; 153 | 154 | union { 155 | OptixProgramGroupSingleModule raygen; 156 | OptixProgramGroupSingleModule miss; 157 | OptixProgramGroupSingleModule exception; 158 | OptixProgramGroupHitgroup hitgroup; 159 | }; 160 | }; 161 | 162 | struct OptixProgramGroupOptions { 163 | OptixPayloadType *payloadType; 164 | }; 165 | 166 | struct OptixShaderBindingTable { 167 | CUdeviceptr raygenRecord; 168 | CUdeviceptr exceptionRecord; 169 | CUdeviceptr missRecordBase; 170 | unsigned int missRecordStrideInBytes; 171 | unsigned int missRecordCount; 172 | CUdeviceptr hitgroupRecordBase; 173 | unsigned int hitgroupRecordStrideInBytes; 174 | unsigned int hitgroupRecordCount; 175 | CUdeviceptr callablesRecordBase; 176 | unsigned int callablesRecordStrideInBytes; 177 | unsigned int callablesRecordCount; 178 | }; 179 | 180 | /// Stores information about a Shape on the Optix side 181 | struct OptixHitGroupData { 182 | int shape_offset; 183 | int shape_id; 184 | }; 185 | 186 | template 187 | struct alignas(OPTIX_SBT_RECORD_ALIGNMENT) SbtRecord { 188 | char header[OPTIX_SBT_RECORD_HEADER_SIZE]; 189 | T data; 190 | }; 191 | 192 | struct alignas(OPTIX_SBT_RECORD_ALIGNMENT) EmptySbtRecord { 193 | char header[OPTIX_SBT_RECORD_HEADER_SIZE]; 194 | }; 195 | 196 | using MissSbtRecord = EmptySbtRecord; 197 | using HitGroupSbtRecord = SbtRecord; 198 | 199 | 200 | 201 | // ===================================================== 202 | // Commonly used OptiX functions 203 | // ===================================================== 204 | 205 | #if defined(OPTIX_STUBS_IMPL) 206 | # define D(name, ...) OptixResult (*name)(__VA_ARGS__) = nullptr; 207 | #else 208 | # define D(name, ...) extern OptixResult (*name)(__VA_ARGS__) 209 | #endif 210 | 211 | D(optixAccelComputeMemoryUsage, OptixDeviceContext, 212 | const OptixAccelBuildOptions *, const OptixBuildInput *, unsigned int, 213 | OptixAccelBufferSizes *); 214 | D(optixAccelBuild, OptixDeviceContext, CUstream, const OptixAccelBuildOptions *, 215 | const OptixBuildInput *, unsigned int, CUdeviceptr, size_t, CUdeviceptr, 216 | size_t, OptixTraversableHandle *, const OptixAccelEmitDesc *, unsigned int); 217 | D(optixModuleCreateFromPTX, OptixDeviceContext, 218 | const OptixModuleCompileOptions *, const OptixPipelineCompileOptions *, 219 | const char *, size_t, char *, size_t *, OptixModule *); 220 | D(optixModuleDestroy, OptixModule); 221 | D(optixProgramGroupCreate, OptixDeviceContext, const OptixProgramGroupDesc *, 222 | unsigned int, const OptixProgramGroupOptions *, char *, size_t *, 223 | OptixProgramGroup *); 224 | D(optixProgramGroupDestroy, OptixProgramGroup); 225 | D(optixSbtRecordPackHeader, OptixProgramGroup, void*); 226 | D(optixAccelCompact, OptixDeviceContext, CUstream, OptixTraversableHandle, 227 | CUdeviceptr, size_t, OptixTraversableHandle *); 228 | 229 | #undef D 230 | 231 | extern void init_optix_api(); 232 | 233 | -------------------------------------------------------------------------------- /include/psdr/psdr.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | constexpr int PSDR_NUM_CHANNELS = 3; 10 | 11 | #include "macros.h" 12 | #include "types.h" 13 | #include "constants.h" 14 | #include "fwd.h" 15 | #include "macros.h" 16 | #include "object.h" 17 | #include "utils.h" 18 | -------------------------------------------------------------------------------- /include/psdr/scene/scene.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | NAMESPACE_BEGIN(psdr_jit) 13 | 14 | PSDR_CLASS_DECL_BEGIN(Scene, final, Object) 15 | friend class SceneLoader; 16 | 17 | public: 18 | using ParamMap = std::unordered_map; 19 | 20 | Scene(); 21 | ~Scene() override; 22 | 23 | void load_file(const char *file_name, bool auto_configure = true); 24 | void load_string(const char *scene_xml, bool auto_configure = true); 25 | 26 | void configure(std::vector active_sensor=std::vector()); 27 | 28 | void add_Sensor(Sensor *sensor); 29 | void add_EnvironmentMap(const char *fname, ScalarMatrix4f to_world, float scale); 30 | void add_EnvironmentMap(EnvironmentMap *emitter); 31 | void add_BSDF(BSDF *bsdf, const char *bsdf_id, bool twoSide = false); 32 | void add_Mesh(const char *fname, Matrix4fC transform, const char *bsdf_id, Emitter *emitter); 33 | void add_Mesh(Mesh *mesh, const char *bsdf_id, Emitter *emitter); 34 | void add_normalmap_BSDF(NormalMap* bsdf1, Microfacet* bsdf2, const char *bsdf_id, bool twoSide = false); 35 | 36 | bool is_ready() const; 37 | 38 | template 39 | Intersection ray_intersect(const Ray &ray, Mask active = true, TriangleInfoD *out_info = nullptr) const; 40 | 41 | template 42 | Intersection unit_ray_intersect(const Ray &ray, Mask active = true) const; 43 | 44 | template 45 | PositionSample sample_emitter_position(const Vector3f &ref_p, const Vector2f &sample, Mask active = true) const; 46 | 47 | template 48 | Float emitter_position_pdf(const Vector3f &ref_p, const Intersection &its, Mask active = true) const; 49 | 50 | BoundarySegSampleDirect sample_boundary_segment_direct(const Vector3fC &sample3, MaskC active = true) const; 51 | 52 | int seed = 0; 53 | 54 | // std::string to_string() const override; 55 | 56 | int m_num_sensors; 57 | std::vector m_sensors; 58 | 59 | std::vector m_emitters; 60 | EnvironmentMap *m_emitter_env; 61 | EmitterArrayD m_emitters_cuda; 62 | DiscreteDistribution *m_emitters_distrb; 63 | 64 | std::vector m_bsdfs; 65 | 66 | int m_num_meshes; 67 | std::vector m_meshes; 68 | MeshArrayD m_meshes_cuda; 69 | 70 | // Scene bounding box 71 | Vector3fC m_lower, m_upper; 72 | 73 | ParamMap m_param_map; 74 | 75 | RenderOption m_opts; 76 | mutable Sampler *m_samplers; 77 | 78 | // protected: 79 | TriangleInfoD m_triangle_info; 80 | TriangleUVD m_triangle_uv; 81 | MaskD m_triangle_face_normals; 82 | bool m_has_bound_mesh; 83 | 84 | 85 | SecondaryEdgeInfo m_sec_edge_info; 86 | DiscreteDistribution *m_sec_edge_distrb; 87 | 88 | 89 | bool m_loaded; 90 | Scene_OptiX *m_optix; 91 | 92 | PSDR_CLASS_DECL_END(Scene) 93 | 94 | NAMESPACE_END(psdr_jit) 95 | -------------------------------------------------------------------------------- /include/psdr/scene/scene_loader.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | namespace pugi 6 | { 7 | class xml_document; 8 | class xml_node; 9 | } // namespace pugi 10 | 11 | 12 | NAMESPACE_BEGIN(psdr_jit) 13 | 14 | class SceneLoader { 15 | public: 16 | static void load_from_file(const char *file_name, Scene &scene); 17 | static void load_from_string(const char *scene_xml, Scene &scene); 18 | 19 | protected: 20 | static void load_scene(const pugi::xml_document &doc, Scene &scene); 21 | static void load_sensor(const pugi::xml_node &node, Scene &scene); 22 | static void load_emitter(const pugi::xml_node &node, Scene &scene); 23 | static void load_bsdf(const pugi::xml_node &node, Scene &scene); 24 | static void load_shape(const pugi::xml_node &node, Scene &scene, int shape_id = -1); 25 | }; 26 | 27 | NAMESPACE_END(psdr_jit) 28 | -------------------------------------------------------------------------------- /include/psdr/scene/scene_optix.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | 8 | NAMESPACE_BEGIN(psdr_jit) 9 | 10 | struct PathTracerState; 11 | 12 | 13 | struct Intersection_OptiX { 14 | void reserve(int64_t size); 15 | 16 | int64_t m_size = 0; 17 | IntC triangle_id; 18 | IntC shape_id; 19 | Vector2fC uv; 20 | }; 21 | 22 | 23 | class Scene_OptiX { 24 | friend class Scene; 25 | //friend std::unique_ptr std::make_unique(); 26 | 27 | public: 28 | ~Scene_OptiX(); 29 | 30 | protected: 31 | Scene_OptiX(); 32 | void configure(const std::vector &meshes); 33 | bool is_ready() const; 34 | 35 | template 36 | Intersection_OptiX ray_intersect(const Ray &ray, Mask &active) const; 37 | 38 | PathTracerState *m_accel = nullptr; 39 | 40 | // mutable Intersection_OptiX m_its; 41 | 42 | 43 | 44 | }; 45 | 46 | NAMESPACE_END(psdr_jit) 47 | -------------------------------------------------------------------------------- /include/psdr/sensor/orthographic.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include "sensor.h" 5 | 6 | NAMESPACE_BEGIN(psdr_jit) 7 | 8 | PSDR_CLASS_DECL_BEGIN(OrthographicCamera, final, Sensor) 9 | public: 10 | OrthographicCamera(float near, float far) : m_near_clip(near), m_far_clip(far) {} 11 | 12 | void configure(bool cache) override; 13 | 14 | RayC sample_primary_ray(const Vector2fC &samples) const override; 15 | RayD sample_primary_ray(const Vector2fD &samples) const override; 16 | 17 | SensorDirectSampleC sample_direct(const Vector3fC &p) const override; 18 | 19 | PrimaryEdgeSample sample_primary_edge(const FloatC &sample1) const override; 20 | 21 | std::string to_string() const override; 22 | 23 | float m_near_clip, 24 | m_far_clip; 25 | 26 | Matrix4fD m_sample_to_camera, m_camera_to_sample, 27 | m_world_to_sample, m_sample_to_world; 28 | 29 | Vector3fD m_camera_pos, m_camera_dir; 30 | FloatD m_inv_area; 31 | PSDR_CLASS_DECL_END(OrthographicCamera) 32 | 33 | NAMESPACE_END(psdr_jit) 34 | -------------------------------------------------------------------------------- /include/psdr/sensor/perspective.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include "sensor.h" 5 | 6 | NAMESPACE_BEGIN(psdr_jit) 7 | 8 | PSDR_CLASS_DECL_BEGIN(PerspectiveCamera, final, Sensor) 9 | public: 10 | PerspectiveCamera(float fov_x, float near, float far) : m_fov_x(fov_x), m_near_clip(near), m_far_clip(far), m_use_intrinsic(false) {} 11 | PerspectiveCamera(float fx, float fy, float cx, float cy, float near, float far) 12 | : m_fx(fx), m_fy(fy), m_cx(cx), m_cy(cy), m_near_clip(near), m_far_clip(far), m_use_intrinsic(true) {}; 13 | 14 | void configure(bool cache) override; 15 | 16 | RayC sample_primary_ray(const Vector2fC &samples) const override; 17 | RayD sample_primary_ray(const Vector2fD &samples) const override; 18 | 19 | SensorDirectSampleC sample_direct(const Vector3fC &p) const override; 20 | 21 | PrimaryEdgeSample sample_primary_edge(const FloatC &sample1) const override; 22 | 23 | std::string to_string() const override; 24 | 25 | float m_fov_x, 26 | m_fx, 27 | m_fy, 28 | m_cx, 29 | m_cy, 30 | m_near_clip, 31 | m_far_clip; 32 | 33 | Matrix4fD m_sample_to_camera, m_camera_to_sample, 34 | m_world_to_sample, m_sample_to_world; 35 | 36 | Vector3fD m_camera_pos, m_camera_dir; 37 | FloatD m_inv_area; 38 | 39 | bool m_use_intrinsic; 40 | PSDR_CLASS_DECL_END(PerspectiveCamera) 41 | 42 | NAMESPACE_END(psdr_jit) 43 | -------------------------------------------------------------------------------- /include/psdr/sensor/sensor.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | NAMESPACE_BEGIN(psdr_jit) 9 | 10 | template 11 | struct SensorDirectSample_ : public SampleRecord_ { 12 | PSDR_IMPORT_BASE(SampleRecord_, ad, pdf, is_valid) 13 | 14 | Vector2f q; 15 | Int pixel_idx; 16 | Float sensor_val; 17 | 18 | DRJIT_STRUCT(SensorDirectSample_, pdf, is_valid, q, pixel_idx, sensor_val) 19 | }; 20 | 21 | PSDR_CLASS_DECL_BEGIN(Sensor,, Object) 22 | public: 23 | virtual ~Sensor() override {} 24 | 25 | virtual void configure(bool cache); 26 | 27 | virtual RayC sample_primary_ray(const Vector2fC &samples) const = 0; 28 | virtual RayD sample_primary_ray(const Vector2fD &samples) const = 0; 29 | 30 | virtual SensorDirectSampleC sample_direct(const Vector3fC &p) const = 0; 31 | 32 | virtual PrimaryEdgeSample sample_primary_edge(const FloatC &sample1) const = 0; 33 | 34 | inline void set_transform(const Matrix4fD &mat, bool set_left = true) { 35 | if ( set_left ) { 36 | m_to_world_left = mat; 37 | } else { 38 | m_to_world_right = mat; 39 | } 40 | } 41 | 42 | inline void append_transform(const Matrix4fD &mat, bool append_left = true) { 43 | if ( append_left ) { 44 | m_to_world_left = mat*m_to_world_left; 45 | } else { 46 | m_to_world_right *= mat; 47 | } 48 | } 49 | 50 | ScalarVector2i m_resolution; 51 | float m_aspect; 52 | 53 | // Matrix4fD m_to_world = identity(); 54 | Matrix4fD m_to_world_raw = identity(), 55 | m_to_world_left = identity(), 56 | m_to_world_right = identity(); 57 | 58 | const Scene *m_scene = nullptr; 59 | 60 | // Properties for primary edge sampling 61 | 62 | bool m_enable_edges = false; 63 | PrimaryEdgeInfo m_edge_info; 64 | DiscreteDistribution m_edge_distrb; 65 | PSDR_CLASS_DECL_END(Sensor) 66 | 67 | NAMESPACE_END(psdr_jit) 68 | 69 | DRJIT_VCALL_BEGIN(psdr_jit::Sensor) 70 | DRJIT_VCALL_METHOD(sample_primary_ray) 71 | DRJIT_VCALL_METHOD(sample_primary_edge) 72 | DRJIT_VCALL_END(psdr_jit::Sensor) 73 | -------------------------------------------------------------------------------- /include/psdr/shape/mesh.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | NAMESPACE_BEGIN(psdr_jit) 12 | 13 | PSDR_CLASS_DECL_BEGIN(Mesh, final, Object) 14 | public: 15 | Mesh() = default; 16 | ~Mesh() override; 17 | 18 | void load_raw(const Vector3fC &new_vertex_positions, const Vector3iC &new_face_indices, 19 | const Vector2fC &new_vertex_uv = Vector2fC(), const Vector3iC &new_face_uv_indices = Vector3iC(), 20 | bool verbose = false); 21 | void load(const char *fname, bool verbose = false); 22 | void configure(); 23 | void prepare_optix_buffers(); 24 | 25 | inline void set_transform(const Matrix4fD &mat, bool set_left = true) { 26 | if ( set_left ) { 27 | m_to_world_left = mat; 28 | } else { 29 | m_to_world_right = mat; 30 | } 31 | m_ready = false; 32 | } 33 | 34 | inline void append_transform(const Matrix4fD &mat, bool append_left = true) { 35 | if ( append_left ) { 36 | m_to_world_left = mat*m_to_world_left; 37 | } else { 38 | m_to_world_right *= mat; 39 | } 40 | m_ready = false; 41 | } 42 | 43 | PositionSampleC sample_position(const Vector2fC &sample2, MaskC active = true) const; 44 | PositionSampleD sample_position(const Vector2fD &sample2, MaskD active = true) const; 45 | 46 | FloatC sample_position_pdf(const IntersectionC &its, MaskC active = true) const; 47 | FloatD sample_position_pdfD(const IntersectionD &its, MaskD active = true) const; 48 | 49 | MaskC get_obj_mask(std::string obj_name) const{ 50 | if (m_id != "") { 51 | if (m_id == obj_name) { 52 | return MaskC(1); 53 | } else { 54 | return MaskC(0); 55 | } 56 | } else { 57 | int obj_id = stoi(obj_name); 58 | if (m_mesh_id == obj_id) { 59 | return MaskC(1); 60 | } else { 61 | return MaskC(0); 62 | } 63 | } 64 | }; 65 | 66 | IntC get_obj_id() const{ 67 | return IntC(m_mesh_id+1); 68 | }; 69 | 70 | const BSDF* bsdf() const { 71 | return m_bsdf; 72 | } 73 | 74 | const Emitter* emitter() const { 75 | return m_emitter; 76 | } 77 | 78 | void dump(const char *fname, bool raw) const; 79 | 80 | 81 | std::string to_string() const override; 82 | 83 | int m_mesh_id = -1; 84 | 85 | bool m_ready = false; 86 | 87 | bool m_use_face_normals = false, 88 | m_has_uv = false; 89 | 90 | bool m_enable_edges = true; 91 | 92 | EdgeSortOption m_edge_sort; 93 | 94 | // Indicates if the mesh creates primiary and secondary edges 95 | 96 | Matrix4fD m_to_world_raw = identity(), 97 | m_to_world_left = identity(), 98 | m_to_world_right = identity(); 99 | 100 | const BSDF* m_bsdf = nullptr; 101 | const Emitter* m_emitter = nullptr; 102 | 103 | // ref mm_bsdr; 104 | 105 | int m_num_vertices = 0, 106 | m_num_faces = 0; 107 | 108 | IntC m_cut_position; 109 | 110 | Vector2iD m_valid_edge_indices; 111 | 112 | Vector3fD m_vertex_positions_raw, 113 | m_vertex_normals_raw; 114 | 115 | Vector2fD m_vertex_uv; 116 | 117 | Vector3fD m_vertex_positions; 118 | 119 | Vector3iD m_face_indices, 120 | m_face_uv_indices; 121 | 122 | // 0, 1: storing vertex indices of the end points 123 | // 2, 3: storing face indices sharing each edge 124 | // 4 : storing the third vertex of face with index stored in [2] 125 | Vectori<5, true> m_edge_indices; 126 | 127 | float m_total_area, m_inv_total_area; 128 | 129 | // For position sampling 130 | DiscreteDistribution *m_face_distrb = nullptr; 131 | 132 | // Temporary triangle info for Scene::configure() 133 | TriangleInfoD *m_triangle_info = nullptr; 134 | TriangleUVD *m_triangle_uv = nullptr; 135 | SecondaryEdgeInfo *m_sec_edge_info = nullptr; 136 | 137 | // For OptiX ray tracing 138 | FloatC m_vertex_buffer; 139 | IntC m_face_buffer; 140 | 141 | DRJIT_VCALL_REGISTER(FloatD, Mesh); 142 | 143 | protected: 144 | template 145 | PositionSample __sample_position(const Vector2f&, Mask) const; 146 | PSDR_CLASS_DECL_END(Mesh) 147 | 148 | NAMESPACE_END(psdr_jit) 149 | 150 | DRJIT_VCALL_BEGIN(psdr_jit::Mesh) 151 | // DRJIT_VCALL_GETTER(m_bsdf, const Emitter *) 152 | // DRJIT_VCALL_GETTER(m_emitter, const typename Class::Emitter *) 153 | 154 | // DRJIT_VCALL_GETTER(flags, uint32_t) 155 | DRJIT_VCALL_METHOD(bsdf) 156 | DRJIT_VCALL_METHOD(emitter) 157 | DRJIT_VCALL_METHOD(get_obj_mask) 158 | DRJIT_VCALL_METHOD(get_obj_id) 159 | DRJIT_VCALL_METHOD(sample_position) 160 | DRJIT_VCALL_METHOD(sample_position_pdf) 161 | DRJIT_VCALL_METHOD(sample_position_pdfD) 162 | DRJIT_VCALL_END(psdr_jit::Mesh) 163 | -------------------------------------------------------------------------------- /include/psdr/types.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | using namespace drjit; 12 | 13 | NAMESPACE_BEGIN(psdr_jit) 14 | 15 | /******************************************** 16 | * GPU array types 17 | ********************************************/ 18 | 19 | using UInt32 = CUDAArray; 20 | using UInt64 = CUDAArray; 21 | 22 | 23 | template 24 | using Type = typename std::conditional>, 26 | CUDAArray>::type; 27 | 28 | 29 | // Scalar arrays (GPU) 30 | 31 | template 32 | using Float = Type; 33 | 34 | 35 | 36 | template 37 | using Int = Type; 38 | 39 | using FloatC = Float; 40 | using FloatD = Float; 41 | 42 | using IntC = Int; 43 | using IntD = Int; 44 | 45 | using ScalarFloat = scalar_t; 46 | using ScalarVector2f = Array; 47 | using ScalarVector2u = Array; 48 | // Vector arrays (GPU) 49 | 50 | template 51 | using Vectorf = Array, n>; 52 | 53 | template 54 | using Vectori = Array, n>; 55 | 56 | template 57 | using Matrixf = Matrix, n>; 58 | 59 | template 60 | using Vector1f = Vectorf<1, ad>; 61 | 62 | template 63 | using Vector1i = Vectori<1, ad>; 64 | 65 | 66 | template 67 | using Vector2f = Vectorf<2, ad>; 68 | 69 | template 70 | using Vector2i = Vectori<2, ad>; 71 | 72 | template 73 | using Vector3f = Vectorf<3, ad>; 74 | 75 | template 76 | using Vector3i = Vectori<3, ad>; 77 | 78 | template 79 | using Vector4f = Vectorf<4, ad>; 80 | 81 | template 82 | using Vector4i = Vectori<4, ad>; 83 | 84 | using Vector1fC = Vector1f; 85 | using Vector1fD = Vector1f; 86 | 87 | using Vector2fC = Vector2f; 88 | using Vector2fD = Vector2f; 89 | 90 | using Vector2iC = Vector2i; 91 | using Vector2iD = Vector2i; 92 | 93 | using Vector3fC = Vector3f; 94 | using Vector3fD = Vector3f; 95 | 96 | using Vector3iC = Vector3i; 97 | using Vector3iD = Vector3i; 98 | 99 | using Vector4fC = Vector4f; 100 | using Vector4fD = Vector4f; 101 | 102 | using Vector4iC = Vector4i; 103 | using Vector4iD = Vector4i; 104 | 105 | // Matrix arrays (GPU) 106 | 107 | template 108 | using Matrix3f = Matrixf<3, ad>; 109 | 110 | template 111 | using Matrix4f = Matrixf<4, ad>; 112 | 113 | using Matrix3fC = Matrix3f; 114 | using Matrix3fD = Matrix3f; 115 | 116 | using Matrix4fC = Matrix4f; 117 | using Matrix4fD = Matrix4f; 118 | 119 | using Matrix3x3fC = Matrix; 120 | using Matrix1x3fC = Matrix; 121 | 122 | // Mask arrays (GPU) 123 | 124 | template 125 | using Mask = mask_t>; 126 | 127 | using MaskC = Mask; 128 | using MaskD = Mask; 129 | 130 | // Spectrum types (GPU) 131 | 132 | template 133 | using Spectrum = Vectorf; 134 | 135 | using SpectrumC = Spectrum; 136 | using SpectrumD = Spectrum; 137 | 138 | 139 | // 140 | /******************************************** 141 | * CPU types 142 | ********************************************/ 143 | 144 | // Scalar types (CPU) 145 | 146 | using ScalarVector2f = Array; 147 | using ScalarVector3f = Array; 148 | using ScalarVector4f = Array; 149 | 150 | using ScalarVector2i = Array; 151 | using ScalarVector3i = Array; 152 | using ScalarVector4i = Array; 153 | 154 | using ScalarMatrix2f = Matrix; 155 | using ScalarMatrix3f = Matrix; 156 | using ScalarMatrix4f = Matrix; 157 | 158 | /******************************************** 159 | * Triangle info types 160 | ********************************************/ 161 | 162 | template 163 | struct TriangleInfo_ { 164 | static constexpr bool ad = std::is_same_v; 165 | 166 | Vector3f p0, e1, e2, n0, n1, n2, face_normal; 167 | Vector3i face_indices; 168 | Float face_area; 169 | 170 | DRJIT_STRUCT(TriangleInfo_, p0, e1, e2, 171 | n0, n1, n2, 172 | face_normal, 173 | face_indices, 174 | face_area) 175 | }; 176 | 177 | template 178 | using TriangleInfo = TriangleInfo_>; 179 | 180 | using TriangleInfoC = TriangleInfo; 181 | using TriangleInfoD = TriangleInfo; 182 | 183 | template 184 | using TriangleUV = Array, 3>; 185 | 186 | using TriangleUVC = TriangleUV; 187 | using TriangleUVD = TriangleUV; 188 | 189 | /******************************************** 190 | * Others 191 | ********************************************/ 192 | 193 | template 194 | struct AQLeaf_ { 195 | Vectorf<8, false> poly; 196 | Vector3f p0, p1; 197 | DRJIT_STRUCT(AQLeaf_, p0, p1, poly) 198 | }; 199 | 200 | template 201 | struct Tree_ { 202 | Float_ p0, p1; 203 | DRJIT_STRUCT(Tree_, p0, p1) 204 | }; 205 | 206 | 207 | using AQLeaf = AQLeaf_; 208 | using tree3D = Tree_; 209 | 210 | // For samplers 211 | 212 | using UIntC = Type; 213 | using UInt64C = Type; 214 | 215 | // Render options 216 | 217 | struct RenderOption { 218 | RenderOption() : width(128), height(128), spp(1), sppe(0), log_level(1) {} 219 | RenderOption(int w, int h, int s) : width(w), height(h), spp(s), sppe(s), sppse(s), log_level(1) {} 220 | RenderOption(int w, int h, int s1, int s2) : width(w), height(h), spp(s1), sppe(s2), sppse(s2), log_level(1) {} 221 | RenderOption(int w, int h, int s1, int s2, int s3) : width(w), height(h), spp(s1), sppe(s2), sppse(s3), log_level(1) {} 222 | 223 | int width, height; // Image resolution 224 | int spp; // Spp for the main image/interior integral 225 | int sppe; // Spp for primary edge integral 226 | int sppse; // Spp for secondary edge integral 227 | int log_level; 228 | }; 229 | 230 | struct EdgeSortOption { 231 | EdgeSortOption() : enable_sort(false), local_angle(180), global_angle(180), min_global_step(1), max_depth(1) {} 232 | bool enable_sort; 233 | float local_angle; 234 | float global_angle; 235 | int min_global_step; 236 | int max_depth; 237 | }; 238 | 239 | 240 | struct AQ_Option { 241 | AQ_Option() {}; 242 | AQ_Option(const std::vector &config, int option) { 243 | num_x = config[0]; 244 | num_y = config[1]; 245 | num_z = config[2]; 246 | thold = config[3]; 247 | wt1 = config[4]; 248 | max_memory = static_cast(config[5]); 249 | max_depth = static_cast(config[6]); 250 | final_spp = static_cast(config[7]); 251 | RMSE_wt = config[8]; 252 | eps = config[9]; 253 | 254 | guiding_option = option; 255 | } 256 | float num_x; 257 | float num_y; 258 | float num_z; 259 | float thold; 260 | float wt1; 261 | int max_memory; 262 | int max_depth; 263 | int final_spp; 264 | 265 | float RMSE_wt; 266 | float eps; 267 | int guiding_option; 268 | }; 269 | 270 | 271 | 272 | 273 | NAMESPACE_END(psdr_jit) 274 | -------------------------------------------------------------------------------- /include/pugixml/pugiconfig.hpp: -------------------------------------------------------------------------------- 1 | /** 2 | * pugixml parser - version 1.10 3 | * -------------------------------------------------------- 4 | * Copyright (C) 2006-2019, by Arseny Kapoulkine (arseny.kapoulkine@gmail.com) 5 | * Report bugs and download new versions at https://pugixml.org/ 6 | * 7 | * This library is distributed under the MIT License. See notice at the end 8 | * of this file. 9 | * 10 | * This work is based on the pugxml parser, which is: 11 | * Copyright (C) 2003, by Kristen Wegner (kristen@tima.net) 12 | */ 13 | 14 | #ifndef HEADER_PUGICONFIG_HPP 15 | #define HEADER_PUGICONFIG_HPP 16 | 17 | // Uncomment this to enable wchar_t mode 18 | // #define PUGIXML_WCHAR_MODE 19 | 20 | // Uncomment this to enable compact mode 21 | // #define PUGIXML_COMPACT 22 | 23 | // Uncomment this to disable XPath 24 | // #define PUGIXML_NO_XPATH 25 | 26 | // Uncomment this to disable STL 27 | // #define PUGIXML_NO_STL 28 | 29 | // Uncomment this to disable exceptions 30 | // #define PUGIXML_NO_EXCEPTIONS 31 | 32 | // Set this to control attributes for public classes/functions, i.e.: 33 | // #define PUGIXML_API __declspec(dllexport) // to export all public symbols from DLL 34 | // #define PUGIXML_CLASS __declspec(dllimport) // to import all classes from DLL 35 | // #define PUGIXML_FUNCTION __fastcall // to set calling conventions to all public functions to fastcall 36 | // In absence of PUGIXML_CLASS/PUGIXML_FUNCTION definitions PUGIXML_API is used instead 37 | 38 | // Tune these constants to adjust memory-related behavior 39 | // #define PUGIXML_MEMORY_PAGE_SIZE 32768 40 | // #define PUGIXML_MEMORY_OUTPUT_STACK 10240 41 | // #define PUGIXML_MEMORY_XPATH_PAGE_SIZE 4096 42 | 43 | // Uncomment this to switch to header-only version 44 | #define PUGIXML_HEADER_ONLY 45 | 46 | // Uncomment this to enable long long support 47 | // #define PUGIXML_HAS_LONG_LONG 48 | 49 | #endif 50 | 51 | /** 52 | * Copyright (c) 2006-2019 Arseny Kapoulkine 53 | * 54 | * Permission is hereby granted, free of charge, to any person 55 | * obtaining a copy of this software and associated documentation 56 | * files (the "Software"), to deal in the Software without 57 | * restriction, including without limitation the rights to use, 58 | * copy, modify, merge, publish, distribute, sublicense, and/or sell 59 | * copies of the Software, and to permit persons to whom the 60 | * Software is furnished to do so, subject to the following 61 | * conditions: 62 | * 63 | * The above copyright notice and this permission notice shall be 64 | * included in all copies or substantial portions of the Software. 65 | * 66 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 67 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES 68 | * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 69 | * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT 70 | * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 71 | * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 72 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 73 | * OTHER DEALINGS IN THE SOFTWARE. 74 | */ 75 | -------------------------------------------------------------------------------- /pyproject.toml: -------------------------------------------------------------------------------- 1 | [build-system] 2 | requires = ["scikit-build-core>=0.3.3"] 3 | build-backend = "scikit_build_core.build" 4 | 5 | 6 | [project] 7 | name = "psdr_jit" 8 | version = "0.2.1" 9 | description = "Path-space differentiable renderer with drjit as the numerical backend." 10 | readme = "README.md" 11 | 12 | requires-python = ">=3.8" 13 | 14 | [tool.scikit-build] 15 | cmake.verbose = true 16 | wheel.expand-macos-universal-tags = true 17 | build-dir = "build/{wheel_tag}" -------------------------------------------------------------------------------- /src/bsdf/diffuse.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | NAMESPACE_BEGIN(psdr_jit) 6 | 7 | Diffuse::Diffuse(const char *refl_file) : m_reflectance(refl_file) {} 8 | 9 | 10 | Diffuse::Diffuse(const Bitmap3fD &reflectance) : m_reflectance(reflectance) {} 11 | 12 | 13 | SpectrumC Diffuse::eval(const IntersectionC &its, const Vector3fC &wo, MaskC active) const { 14 | return __eval(its, wo, active); 15 | } 16 | 17 | 18 | SpectrumD Diffuse::eval(const IntersectionD &its, const Vector3fD &wo, MaskD active) const { 19 | return __eval(its, wo, active); 20 | } 21 | 22 | 23 | template 24 | Spectrum Diffuse::__eval(const Intersection &_its, const Vector3f &_wo, Mask active) const { 25 | Intersection its(_its); 26 | Vector3f wo(_wo); 27 | 28 | if (m_twoSide) { 29 | wo.z() = drjit::mulsign(wo.z(), its.wi.z()); 30 | its.wi.z() = drjit::abs(its.wi.z()); 31 | } 32 | 33 | Float cos_theta_i = Frame::cos_theta(its.wi), 34 | cos_theta_o = Frame::cos_theta(wo); 35 | 36 | active &= (cos_theta_i > 0.f && cos_theta_o > 0.f); 37 | 38 | Spectrum value = m_reflectance.eval(its.uv) * InvPi * cos_theta_o; 39 | return value & active; 40 | } 41 | 42 | 43 | BSDFSampleC Diffuse::sample(const IntersectionC &its, const Vector3fC &sample, MaskC active) const { 44 | return __sample(its, sample, active); 45 | } 46 | 47 | 48 | BSDFSampleD Diffuse::sample(const IntersectionD &its, const Vector3fD &sample, MaskD active) const { 49 | return __sample(its, sample, active); 50 | } 51 | 52 | template 53 | BSDFSample Diffuse::__sample(const Intersection &_its, const Vector3f &sample, Mask active) const { 54 | 55 | Intersection its(_its); 56 | 57 | if (m_twoSide) { 58 | its.wi.z() = drjit::abs(its.wi.z()); 59 | } 60 | 61 | Float cos_theta_i = Frame::cos_theta(its.wi); 62 | BSDFSample bs; 63 | Vector2f sample2 = tail<2>(sample); 64 | bs.wo = warp::square_to_cosine_hemisphere(sample2); 65 | 66 | bs.eta = 1.0f; 67 | bs.pdf = warp::square_to_cosine_hemisphere_pdf(bs.wo); 68 | bs.is_valid = active && (cos_theta_i > 0.f); 69 | return detach(bs); 70 | } 71 | 72 | 73 | FloatC Diffuse::pdf(const IntersectionC &its, const Vector3fC &wo, MaskC active) const { 74 | return __pdf(its, wo, active); 75 | } 76 | 77 | 78 | FloatD Diffuse::pdf(const IntersectionD &its, const Vector3fD &wo, MaskD active) const { 79 | return __pdf(its, wo, active); 80 | } 81 | 82 | 83 | template 84 | Float Diffuse::__pdf(const Intersection &_its, const Vector3f &_wo, Mask active) const { 85 | 86 | 87 | Intersection its(_its); 88 | Vector3f wo(_wo); 89 | 90 | if (m_twoSide) { 91 | wo.z() = drjit::mulsign(wo.z(), its.wi.z()); 92 | its.wi.z() = drjit::abs(its.wi.z()); 93 | } 94 | 95 | 96 | FloatC cos_theta_i, cos_theta_o; 97 | if constexpr ( ad ) { 98 | cos_theta_i = FrameC::cos_theta(detach(its.wi)); 99 | cos_theta_o = FrameC::cos_theta(detach(wo)); 100 | } else { 101 | cos_theta_i = FrameC::cos_theta(its.wi); 102 | cos_theta_o = FrameC::cos_theta(wo); 103 | } 104 | active &= (cos_theta_i > 0.f && cos_theta_o > 0.f); 105 | 106 | Float value = InvPi * cos_theta_o; 107 | return value & active; 108 | } 109 | 110 | NAMESPACE_END(psdr_jit) 111 | -------------------------------------------------------------------------------- /src/bsdf/ggx.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | NAMESPACE_BEGIN(psdr_jit) 6 | 7 | template 8 | Float GGXDistribution::G(const Vector3f& wi, const Vector3f& wo, const Vector3f& m) const { 9 | return smith_g1(wi, m) * smith_g1(wo, m); 10 | } 11 | 12 | 13 | template 14 | Float GGXDistribution::eval(const Vector3f& m) const { 15 | Float alpha_uv; 16 | if constexpr (ad) { 17 | alpha_uv = m_alpha_u * m_alpha_v; 18 | } 19 | else { 20 | alpha_uv = detach(m_alpha_u) * detach(m_alpha_v); 21 | } 22 | Float cos_theta = Frame::cos_theta(m), 23 | cos_theta_2 = sqr(cos_theta), 24 | result; 25 | if constexpr (ad) { 26 | result = rcp(Pi * alpha_uv * sqr(sqr(m.x() / m_alpha_u) + sqr(m.y() / m_alpha_v) + sqr(m.z()))); 27 | } 28 | else { 29 | result = rcp(Pi * alpha_uv * sqr(sqr(m.x() / detach(m_alpha_u)) + sqr(m.y() / detach(m_alpha_v)) + sqr(m.z()))); 30 | } 31 | return select(result * cos_theta > 1e-20f, result, 0.f); 32 | } 33 | 34 | 35 | template 36 | std::pair, Float> GGXDistribution::sample(const Vector3f& wi, const Vector3f& sample) const { 37 | Float sin_phi, cos_phi, cos_theta; 38 | Vector3f wi_p; 39 | 40 | if constexpr (ad) { 41 | wi_p = normalize(Vector3f( 42 | m_alpha_u * wi.x(), 43 | m_alpha_v * wi.y(), 44 | wi.z() 45 | )); 46 | } 47 | else { 48 | wi_p = normalize(Vector3f( 49 | detach(m_alpha_u) * wi.x(), 50 | detach(m_alpha_v) * wi.y(), 51 | wi.z() 52 | )); 53 | } 54 | 55 | sin_phi = Frame::sin_phi(wi_p); 56 | cos_phi = Frame::cos_phi(wi_p); 57 | cos_theta = Frame::cos_theta(wi_p); 58 | Vector2f sample2(sample.x(), sample.y()); 59 | Vector2f slope = sample_visible_11(cos_theta, sample2); 60 | 61 | if constexpr (ad) { 62 | slope = Vector2f( 63 | fmsub(cos_phi, slope.x(), sin_phi * slope.y()) * m_alpha_u, 64 | fmadd(sin_phi, slope.x(), cos_phi * slope.y()) * m_alpha_v 65 | ); 66 | } 67 | else { 68 | slope = Vector2f( 69 | fmsub(cos_phi, slope.x(), sin_phi * slope.y()) * detach(m_alpha_u), 70 | fmadd(sin_phi, slope.x(), cos_phi * slope.y()) * detach(m_alpha_v) 71 | ); 72 | } 73 | Vector3f m = normalize(Vector3f(-slope.x(), -slope.y(), 1)); 74 | 75 | // Compute probability density of the sampled position 76 | Float pdf = smith_g1(wi, m) * abs(dot(wi, m)) * eval(m) / abs(Frame::cos_theta(wi)); 77 | pdf = detach(pdf); 78 | return {m, pdf}; 79 | } 80 | 81 | 82 | template 83 | Float GGXDistribution::smith_g1(const Vector3f& v, const Vector3f& m) const { 84 | Float xy_alpha_2; 85 | if constexpr (ad) { 86 | xy_alpha_2 = sqr(m_alpha_u * v.x()) + sqr(m_alpha_v * v.y()); 87 | } 88 | else { 89 | xy_alpha_2 = sqr(detach(m_alpha_u) * v.x()) + sqr(detach(m_alpha_v) * v.y()); 90 | } 91 | Float tan_theta_alpha_2 = xy_alpha_2 / sqr(v.z()), result; 92 | result = 2.f / (1.f + sqrt(1.f + tan_theta_alpha_2)); 93 | masked(result, eq(xy_alpha_2, 0.f)) = 1.f; 94 | masked(result, dot(v, m) * Frame::cos_theta(v) <= 0.f) = 0.f; 95 | return result; 96 | } 97 | 98 | 99 | template 100 | Vector2f GGXDistribution::sample_visible_11(const Float& cos_theta_i, const Vector2f& sample) const { 101 | Vector2f p = warp::square_to_uniform_disk_concentric(sample); 102 | Float s = .5f * (1.f + cos_theta_i); 103 | p.y() = lerp(safe_sqrt(1.f - sqr(p.x())), p.y(), s); 104 | Float x = p.x(), y = p.y(), 105 | z = safe_sqrt(1.f - squared_norm(p)); 106 | Float sin_theta_i = safe_sqrt(1.f - sqr(cos_theta_i)); 107 | Float norm = rcp(fmadd(sin_theta_i, y, cos_theta_i * z)); 108 | return Vector2f(fmsub(cos_theta_i, y, sin_theta_i * z), x) * norm; 109 | } 110 | 111 | // Explicit instancitations 112 | 113 | template FloatC GGXDistribution::eval(const Vector3fC& m) const; 114 | template FloatD GGXDistribution::eval(const Vector3fD& m) const; 115 | template FloatC GGXDistribution::G(const Vector3fC& wi, const Vector3fC& wo, const Vector3fC& m) const; 116 | template FloatD GGXDistribution::G(const Vector3fD& wi, const Vector3fD& wo, const Vector3fD& m) const; 117 | template std::pair GGXDistribution::sample(const Vector3fC& wi, const Vector3fC& sample) const; 118 | template std::pair GGXDistribution::sample(const Vector3fD& wi, const Vector3fD& sample) const; 119 | template FloatC GGXDistribution::smith_g1(const Vector3fC& v, const Vector3fC& m) const; 120 | template FloatD GGXDistribution::smith_g1(const Vector3fD& v, const Vector3fD& m) const; 121 | 122 | NAMESPACE_END(psdr_jit) 123 | -------------------------------------------------------------------------------- /src/bsdf/microfacet.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | NAMESPACE_BEGIN(psdr_jit) 8 | Microfacet::Microfacet(const char *spec_refl_file, const char *diff_refl_file, const char *roughness_file) : 9 | m_specularReflectance(spec_refl_file), m_diffuseReflectance(diff_refl_file), m_roughness(roughness_file) {} 10 | 11 | Microfacet::Microfacet(const Bitmap3fD &spec_refl, const Bitmap3fD &diff_refl, const Bitmap1fD &roughness) : 12 | m_specularReflectance(spec_refl), m_diffuseReflectance(diff_refl), m_roughness(roughness) {} 13 | 14 | SpectrumC Microfacet::eval(const IntersectionC &its, const Vector3fC &wo, MaskC active) const { 15 | return __eval(its, wo, active); 16 | } 17 | 18 | SpectrumD Microfacet::eval(const IntersectionD &its, const Vector3fD &wo, MaskD active) const { 19 | return __eval(its, wo, active); 20 | } 21 | 22 | template 23 | Spectrum Microfacet::__eval(const Intersection &_its, const Vector3f &_wo, Mask active) const { 24 | Intersection its(_its); 25 | Vector3f wo(_wo); 26 | 27 | if (m_twoSide) { 28 | wo.z() = drjit::mulsign(wo.z(), its.wi.z()); 29 | its.wi.z() = drjit::abs(its.wi.z()); 30 | } 31 | 32 | 33 | 34 | Float cos_theta_nv = Frame::cos_theta(its.wi),// view dir 35 | cos_theta_nl = Frame::cos_theta(wo); // light dir 36 | 37 | active &= (cos_theta_nv > 0.f && cos_theta_nl > 0.f); 38 | 39 | Spectrum diffuse = m_diffuseReflectance.eval(its.uv) * InvPi; 40 | 41 | Vector3f H = normalize(its.wi + wo); 42 | Float cos_theta_vh = dot(H, its.wi); 43 | 44 | Spectrum F0 = m_specularReflectance.eval(its.uv); 45 | Float roughness = m_roughness.eval(its.uv); 46 | Float alpha = sqr(roughness); 47 | GGXDistribution distr(alpha); 48 | 49 | // GGX NDF term 50 | Float ggx = distr.eval(H); 51 | 52 | // Fresnel term 53 | Float coeff = cos_theta_vh * (-5.55473f * cos_theta_vh - 6.8316f); 54 | Spectrum fresnel = F0 + (1.f - F0) * pow(2.f, coeff); 55 | 56 | // Geometry term 57 | Float smithG = distr.smith_g1(its.wi, H) * distr.smith_g1(wo, H); 58 | 59 | Spectrum numerator = ggx * smithG * fresnel; 60 | Float denominator = 4.f * cos_theta_nl * cos_theta_nv; 61 | Spectrum specular = numerator / (denominator + 1e-6f); 62 | 63 | Spectrum value = (diffuse + specular) * cos_theta_nl; 64 | 65 | return select(active, value, 0.f); 66 | } 67 | 68 | 69 | BSDFSampleC Microfacet::sample(const IntersectionC &its, const Vector3fC &sample, MaskC active) const { 70 | return __sample(its, sample, active); 71 | } 72 | 73 | 74 | BSDFSampleD Microfacet::sample(const IntersectionD &its, const Vector3fD &sample, MaskD active) const { 75 | return __sample(its, sample, active); 76 | } 77 | 78 | template 79 | BSDFSample Microfacet::__sample(const Intersection& _its, const Vector3f& sample, Mask active) const { 80 | Intersection its(_its); 81 | 82 | if (m_twoSide) { 83 | its.wi.z() = drjit::abs(its.wi.z()); 84 | } 85 | 86 | 87 | BSDFSample bs; 88 | Float cos_theta_i = Frame::cos_theta(its.wi); 89 | Float alpha = sqr(m_roughness.eval(its.uv)); 90 | GGXDistribution distr(alpha); 91 | 92 | auto [m, m_pdf] = distr.sample(its.wi, sample); 93 | bs.wo = fmsub(Vector3f(m), 2.f * dot(its.wi, m), its.wi); 94 | bs.eta = 1.0f; // TODO: Fix later 95 | bs.pdf = (m_pdf / (4.f * dot(bs.wo, m))); 96 | bs.is_valid = (cos_theta_i > 0.f && neq(bs.pdf, 0.f) && Frame::cos_theta(bs.wo) > 0.f) & active; 97 | return detach(bs); 98 | } 99 | 100 | 101 | FloatC Microfacet::pdf(const IntersectionC &its, const Vector3fC &wo, MaskC active) const { 102 | return __pdf(its, wo, active); 103 | } 104 | 105 | 106 | FloatD Microfacet::pdf(const IntersectionD &its, const Vector3fD &wo, MaskD active) const { 107 | return __pdf(its, wo, active); 108 | } 109 | 110 | template 111 | Float Microfacet::__pdf(const Intersection &_its, const Vector3f &_wo, Mask active) const { 112 | 113 | Intersection its(_its); 114 | Vector3f wo(_wo); 115 | 116 | if (m_twoSide) { 117 | wo.z() = drjit::mulsign(wo.z(), its.wi.z()); 118 | its.wi.z() = drjit::abs(its.wi.z()); 119 | } 120 | 121 | Float cos_theta_i = Frame::cos_theta(its.wi), 122 | cos_theta_o = Frame::cos_theta(wo); 123 | 124 | Spectrum m = normalize(wo + its.wi); 125 | 126 | active &= cos_theta_i > 0.f && cos_theta_o > 0.f && 127 | dot(its.wi, m) > 0.f && dot(wo, m) > 0.f; 128 | 129 | Float alpha = sqr(m_roughness.eval(its.uv)); 130 | GGXDistribution distr(alpha); 131 | 132 | Float result = (distr.eval(m) * distr.smith_g1(its.wi, m) / 133 | (4.f * cos_theta_i)); 134 | return detach(result); 135 | } 136 | 137 | NAMESPACE_END(psdr_jit) 138 | -------------------------------------------------------------------------------- /src/bsdf/microfacet_pv.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | NAMESPACE_BEGIN(psdr_jit) 7 | 8 | MicrofacetPerVertex::MicrofacetPerVertex(const Vector3fD &spec_refl, const Vector3fD &diff_refl, const Vector1fD &roughness) : 9 | m_specularReflectance(spec_refl), m_diffuseReflectance(diff_refl), m_roughness(roughness) {} 10 | 11 | SpectrumC MicrofacetPerVertex::eval(const IntersectionC &its, const Vector3fC &wo, MaskC active) const { 12 | return __eval(its, wo, active); 13 | } 14 | 15 | SpectrumD MicrofacetPerVertex::eval(const IntersectionD &its, const Vector3fD &wo, MaskD active) const { 16 | return __eval(its, wo, active); 17 | } 18 | 19 | template 20 | Spectrum MicrofacetPerVertex::__eval(const Intersection &_its, const Vector3f &_wo, Mask active) const { 21 | Intersection its(_its); 22 | Vector3f wo(_wo); 23 | 24 | if (m_twoSide) { 25 | wo.z() = drjit::mulsign(wo.z(), its.wi.z()); 26 | its.wi.z() = drjit::abs(its.wi.z()); 27 | } 28 | 29 | Vector3f specularReflectance = __interpolate<3, ad>(its, m_specularReflectance, active); 30 | Vector3f diffuseReflectance = __interpolate<3, ad>(its, m_diffuseReflectance, active); 31 | Float roughness = __interpolate<1, ad>(its, m_roughness, active)[0]; 32 | 33 | Float cos_theta_nv = Frame::cos_theta(its.wi),// view dir 34 | cos_theta_nl = Frame::cos_theta(wo); // light dir 35 | 36 | active &= (cos_theta_nv > 0.f && cos_theta_nl > 0.f); 37 | 38 | Spectrum diffuse = diffuseReflectance * InvPi; 39 | 40 | Vector3f H = normalize(its.wi + wo); 41 | Float cos_theta_nh = Frame::cos_theta(H), 42 | cos_theta_vh = dot(H, its.wi); 43 | 44 | Spectrum F0 = specularReflectance; 45 | Float alpha = sqr(roughness); 46 | Float k = sqr(roughness + 1.f) / 8.f; 47 | 48 | // GGX NDF term 49 | Float tmp = alpha / (cos_theta_nh * cos_theta_nh * (sqr(alpha) - 1.f) + 1.f); 50 | Float ggx = tmp * tmp * InvPi; 51 | 52 | // Fresnel term 53 | Float coeff = cos_theta_vh * (-5.55473f * cos_theta_vh - 6.8316f); 54 | Spectrum fresnel = F0 + (1.f - F0) * pow(2.f, coeff); 55 | 56 | // Geometry term 57 | Float smithG1 = cos_theta_nv / (cos_theta_nv * (1.f - k) + k); 58 | Float smithG2 = cos_theta_nl / (cos_theta_nl * (1.f - k) + k); 59 | Float smithG = smithG1 * smithG2; 60 | 61 | Spectrum numerator = ggx * smithG * fresnel; 62 | Float denominator = 4.f * cos_theta_nl * cos_theta_nv; 63 | Spectrum specular = numerator / (denominator + 1e-6f); 64 | 65 | Spectrum value = (diffuse + specular) * cos_theta_nl; 66 | 67 | return select(active, value, 0.f); 68 | } 69 | 70 | 71 | BSDFSampleC MicrofacetPerVertex::sample(const IntersectionC &its, const Vector3fC &sample, MaskC active) const { 72 | return __sample(its, sample, active); 73 | } 74 | 75 | 76 | BSDFSampleD MicrofacetPerVertex::sample(const IntersectionD &its, const Vector3fD &sample, MaskD active) const { 77 | return __sample(its, sample, active); 78 | } 79 | 80 | template 81 | BSDFSample MicrofacetPerVertex::__sample(const Intersection& _its, const Vector3f& sample, Mask active) const { 82 | Intersection its(_its); 83 | 84 | if (m_twoSide) { 85 | its.wi.z() = drjit::abs(its.wi.z()); 86 | } 87 | 88 | Float roughness = __interpolate<1, ad>(its, m_roughness, active)[0]; 89 | 90 | BSDFSample bs; 91 | Float cos_theta_i = Frame::cos_theta(its.wi); 92 | Float alpha = sqr(roughness); 93 | GGXDistribution distr(alpha); 94 | 95 | auto m_pair = distr.sample(its.wi, sample); 96 | Vector3f m = std::get<0>(m_pair); 97 | Float m_pdf = std::get<1>(m_pair); 98 | bs.wo = fmsub(Vector3f(m), 2.f * dot(its.wi, m), its.wi); 99 | bs.eta = 1.0f; // TODO: Fix later 100 | bs.pdf = (m_pdf / (4.f * dot(bs.wo, m))); 101 | bs.is_valid = (cos_theta_i > 0.f && neq(bs.pdf, 0.f) && Frame::cos_theta(bs.wo) > 0.f) & active; 102 | return detach(bs); 103 | } 104 | 105 | 106 | FloatC MicrofacetPerVertex::pdf(const IntersectionC &its, const Vector3fC &wo, MaskC active) const { 107 | return __pdf(its, wo, active); 108 | } 109 | 110 | 111 | FloatD MicrofacetPerVertex::pdf(const IntersectionD &its, const Vector3fD &wo, MaskD active) const { 112 | return __pdf(its, wo, active); 113 | } 114 | 115 | template 116 | Float MicrofacetPerVertex::__pdf(const Intersection &_its, const Vector3f &_wo, Mask active) const { 117 | 118 | Intersection its(_its); 119 | Vector3f wo(_wo); 120 | 121 | if (m_twoSide) { 122 | wo.z() = drjit::mulsign(wo.z(), its.wi.z()); 123 | its.wi.z() = drjit::abs(its.wi.z()); 124 | } 125 | 126 | Float roughness = __interpolate<1, ad>(its, m_roughness, active)[0]; 127 | 128 | Float cos_theta_i = Frame::cos_theta(its.wi), 129 | cos_theta_o = Frame::cos_theta(wo); 130 | 131 | Spectrum m = normalize(wo + its.wi); 132 | 133 | active &= cos_theta_i > 0.f && cos_theta_o > 0.f && 134 | dot(its.wi, m) > 0.f && dot(wo, m) > 0.f; 135 | 136 | Float alpha = sqr(roughness); 137 | GGXDistribution distr(alpha); 138 | 139 | Float result = (distr.eval(m) * distr.smith_g1(its.wi, m) / 140 | (4.f * cos_theta_i)); 141 | return detach(result); 142 | } 143 | 144 | template 145 | Vectorf MicrofacetPerVertex::__interpolate(const Intersection &its, const Vectorf &v, Mask active) const { 146 | Vectorf v_; 147 | if constexpr ( ad ) { 148 | v_ = v; 149 | } 150 | else { 151 | v_ = detach(v); 152 | } 153 | auto v0 = gather>(v_, its.face_indices[0], active); 154 | auto v1 = gather>(v_, its.face_indices[1], active); 155 | auto v2 = gather>(v_, its.face_indices[2], active); 156 | 157 | // Bilinear interpolation. 158 | auto result = fmadd(v1 - v0, its.bc.x(), fmadd(v2 - v0, its.bc.y(), v0)); 159 | return result; 160 | } 161 | 162 | NAMESPACE_END(psdr_jit) 163 | -------------------------------------------------------------------------------- /src/bsdf/normalmap.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include 6 | 7 | NAMESPACE_BEGIN(psdr_jit) 8 | 9 | NormalMap::NormalMap(const Bitmap3fD &n_map) : m_nmap(n_map) {} 10 | 11 | 12 | SpectrumC NormalMap::eval(const IntersectionC &its, const Vector3fC &wo, MaskC active) const { 13 | return __eval(its, wo, active); 14 | } 15 | 16 | 17 | SpectrumD NormalMap::eval(const IntersectionD &its, const Vector3fD &wo, MaskD active) const { 18 | return __eval(its, wo, active); 19 | } 20 | template 21 | Vector3f wt(Vector3f wp) { 22 | return normalize(Vector3f(-wp.x(), -wp.y(), 0.f)); 23 | } 24 | 25 | template 26 | Float pdot(Vector3f a, Vector3f b) { 27 | return maximum(Float(0.f), dot(a, b)); 28 | } 29 | 30 | template 31 | Float G1(Vector3f wp, Vector3f w) { 32 | return minimum(1.f, 33 | maximum(0.f, Frame::cos_theta(w)) * maximum(0.f, Frame::cos_theta(wp)) 34 | / (pdot(w, wp) + pdot(w, wt(wp)) * Frame::sin_theta(wp)) 35 | ); 36 | } 37 | 38 | template 39 | Float lambda_p(Vector3f wp, Vector3f wi) { 40 | Float i_dot_p = pdot(wp, wi); 41 | return i_dot_p / (i_dot_p + pdot(wt(wp), wi) * Frame::sin_theta(wp)); 42 | } 43 | 44 | 45 | template 46 | Spectrum NormalMap::__eval(const Intersection &_its, const Vector3f &_wo, Mask active) const { 47 | Intersection its(_its); 48 | Vector3f wo(_wo); 49 | if (m_twoSide) { 50 | wo.z() = drjit::mulsign(wo.z(), its.wi.z()); 51 | its.wi.z() = drjit::abs(its.wi.z()); 52 | } 53 | 54 | Float cos_theta_i = Frame::cos_theta(its.wi), 55 | cos_theta_o = Frame::cos_theta(wo); 56 | active &= cos_theta_i > 0.f && cos_theta_o > 0.f; 57 | 58 | 59 | 60 | Vector3f wp = normalize(fmadd(m_nmap.eval(its.uv), 2, -1.f)); 61 | Frame p_frame(wp, normalize(fnmadd(wp, dot(wp, its.dp_du), its.dp_du))); 62 | Intersection perturbed_its = its; 63 | perturbed_its.wi = p_frame.to_local(its.wi); 64 | 65 | Vector3f perturbed_wo = p_frame.to_local(wo); 66 | 67 | Float shadowing = G1(wp, wo); 68 | Float lambda_p_ = lambda_p(wp, its.wi); 69 | Vector3f wt_ = wt(wp); 70 | 71 | // i -> p -> o 72 | Spectrum value = m_bsdf->eval(perturbed_its, perturbed_wo, active) * lambda_p_ * shadowing; 73 | 74 | // i -> p -> t -> o 75 | // Vector3f wo_reflected = normalize(wo - 2.0f * dot(wo, wt_) * wt_); 76 | // Vector3f reflected_perturbed_wo = p_frame.to_local(wo_reflected); 77 | // Float notShadowedWpMirror = 1.f - G1(wp, wo_reflected); 78 | // value[dot(wo, wt_) > 0] += m_bsdf->eval(perturbed_its, reflected_perturbed_wo) * (lambda_p_ * notShadowedWpMirror * shadowing); 79 | 80 | // i -> t -> p -> o 81 | Vector3f wi_reflected = normalize(its.wi - 2.0f * dot(its.wi, wt_) * wt_); 82 | Intersection reflected_perturbed_its = perturbed_its; 83 | reflected_perturbed_its.wi = p_frame.to_local(wi_reflected); 84 | value[dot(its.wi, wt_) > 0] += m_bsdf->eval(reflected_perturbed_its, perturbed_wo, active) * (1.f - lambda_p_) * shadowing; 85 | 86 | return value & active; 87 | } 88 | 89 | 90 | BSDFSampleC NormalMap::sample(const IntersectionC &its, const Vector3fC &sample, MaskC active) const { 91 | return __sample(its, sample, active); 92 | } 93 | 94 | 95 | BSDFSampleD NormalMap::sample(const IntersectionD &its, const Vector3fD &sample, MaskD active) const { 96 | return __sample(its, sample, active); 97 | } 98 | 99 | FloatC NormalMap::pdf(const IntersectionC &its, const Vector3fC &wo, MaskC active) const { 100 | return __pdf(its, wo, active); 101 | } 102 | 103 | 104 | FloatD NormalMap::pdf(const IntersectionD &its, const Vector3fD &wo, MaskD active) const { 105 | return __pdf(its, wo, active); 106 | } 107 | 108 | 109 | template 110 | Float NormalMap::__pdf(const Intersection &_its, const Vector3f &_wo, Mask active) const { 111 | Intersection its(_its); 112 | Vector3f wo(_wo); 113 | 114 | if (m_twoSide) { 115 | wo.z() = drjit::mulsign(wo.z(), its.wi.z()); 116 | its.wi.z() = drjit::abs(its.wi.z()); 117 | } 118 | 119 | 120 | 121 | 122 | Float cos_theta_i = Frame::cos_theta(its.wi), 123 | cos_theta_o = Frame::cos_theta(wo); 124 | active &= cos_theta_i > 0.f && cos_theta_o > 0.f; 125 | 126 | Vector3f wp = normalize(fmadd(m_nmap.eval(its.uv), 2, -1.f)); 127 | Frame p_frame(wp, normalize(fnmadd(wp, dot(wp, its.dp_du), its.dp_du))); 128 | 129 | Vector3f perturbed_wo = p_frame.to_local(wo); 130 | Float probability_wp = lambda_p(wp, its.wi); 131 | Vector3f wt_ = wt(wp); 132 | 133 | // i -> p -> o 134 | Intersection perturbed_its = its; 135 | perturbed_its.wi = p_frame.to_local(its.wi); 136 | // Float value = probability_wp * m_bsdf->pdf(perturbed_its, perturbed_wo, active); 137 | 138 | // i -> t -> p -> o 139 | Vector3f wi_reflected = normalize(its.wi - 2.0f * dot(its.wi, wt_) * wt_); 140 | Intersection reflected_perturbed_its = perturbed_its; 141 | reflected_perturbed_its.wi = p_frame.to_local(wi_reflected); 142 | Float value = probability_wp * m_bsdf->pdf(perturbed_its, perturbed_wo, active) + (1.f-probability_wp) * m_bsdf->pdf(reflected_perturbed_its, perturbed_wo, active); 143 | 144 | return detach(value) & active; 145 | } 146 | 147 | template 148 | BSDFSample NormalMap::__sample(const Intersection &_its, const Vector3f &sample, Mask active) const { 149 | 150 | Intersection its(_its); 151 | 152 | if (m_twoSide) { 153 | its.wi.z() = drjit::abs(its.wi.z()); 154 | } 155 | 156 | 157 | Vector3f wp = normalize(fmadd(m_nmap.eval(its.uv), 2, -1.f)); 158 | Frame p_frame(wp, normalize(fnmadd(wp, dot(wp, its.dp_du), its.dp_du))); 159 | 160 | Intersection perturbed_its = its; 161 | perturbed_its.wi = p_frame.to_local(its.wi); 162 | 163 | Float probability_wp = lambda_p(wp, its.wi); 164 | Vector3f wt_ = wt(wp); 165 | Mask itpo_mask = sample.z() >= probability_wp; 166 | 167 | // i -> p -> o 168 | BSDFSample bs = m_bsdf->sample(perturbed_its, sample, active && !itpo_mask); 169 | Vector3f perturbed_wo = p_frame.to_world(bs.wo); 170 | 171 | // i -> t -> p -> o 172 | Vector3f wi_reflected = normalize(its.wi - 2.0f * dot(its.wi, wt_) * wt_); 173 | Intersection reflected_perturbed_its = its; 174 | reflected_perturbed_its.wi = p_frame.to_local(wi_reflected); 175 | BSDFSample bs_itpo = m_bsdf->sample(reflected_perturbed_its, sample, active && itpo_mask); 176 | 177 | bs.wo[itpo_mask] = bs_itpo.wo; 178 | 179 | Float pdf1 = m_bsdf->pdf(perturbed_its, bs.wo, active); 180 | Float pdf2 = m_bsdf->pdf(reflected_perturbed_its, bs.wo, active); 181 | 182 | bs.pdf = probability_wp * pdf1 + (1.f-probability_wp)*pdf2; 183 | bs.wo = p_frame.to_world(bs.wo); 184 | bs.is_valid = active && (bs.is_valid || bs_itpo.is_valid); 185 | 186 | return detach(bs); 187 | } 188 | 189 | NAMESPACE_END(psdr_jit) 190 | -------------------------------------------------------------------------------- /src/bsdf/roughconductor.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | NAMESPACE_BEGIN(psdr_jit) 7 | 8 | SpectrumC RoughConductor::eval(const IntersectionC& its, const Vector3fC& wo, MaskC active) const { 9 | return __eval(its, wo, active); 10 | } 11 | 12 | 13 | SpectrumD RoughConductor::eval(const IntersectionD& its, const Vector3fD& wo, MaskD active) const { 14 | return __eval(its, wo, active); 15 | } 16 | 17 | 18 | BSDFSampleC RoughConductor::sample(const IntersectionC& its, const Vector3fC& sample, MaskC active) const { 19 | return __sample(its, sample, active); 20 | } 21 | 22 | 23 | BSDFSampleD RoughConductor::sample(const IntersectionD& its, const Vector3fD& sample, MaskD active) const { 24 | return __sample(its, sample, active); 25 | } 26 | 27 | FloatC RoughConductor::pdf(const IntersectionC& its, const Vector3fC& wo, MaskC active) const { 28 | return __pdf(its, wo, active); 29 | } 30 | 31 | 32 | FloatD RoughConductor::pdf(const IntersectionD& its, const Vector3fD& wo, MaskD active) const { 33 | return __pdf(its, wo, active); 34 | } 35 | 36 | 37 | template 38 | Spectrum RoughConductor::__eval(const Intersection& _its, const Vector3f& _wo, Mask active) const { 39 | Intersection its(_its); 40 | Vector3f wo(_wo); 41 | 42 | if (m_twoSide) { 43 | wo.z() = drjit::mulsign(wo.z(), its.wi.z()); 44 | its.wi.z() = drjit::abs(its.wi.z()); 45 | } 46 | 47 | Float cos_theta_i = Frame::cos_theta(its.wi), 48 | cos_theta_o = Frame::cos_theta(wo); 49 | active &= cos_theta_i > 0.f && cos_theta_o > 0.f; 50 | Float alpha_u = m_alpha_u.eval(its.uv); 51 | Float alpha_v = m_alpha_v.eval(its.uv); 52 | GGXDistribution m_distr(alpha_u, alpha_v); 53 | Vector3f H = normalize(wo + its.wi); 54 | Float D = m_distr.eval(H); 55 | active &= neq(D, 0.f); 56 | Float G = m_distr.G(its.wi, wo, H); 57 | Spectrum result = D * G / (4.f * Frame::cos_theta(its.wi)); 58 | Spectrum F = fresnel(m_eta.eval(its.uv), m_k.eval(its.uv), dot(its.wi, H)); 59 | Spectrum specular_reflectance = m_specular_reflectance.eval(its.uv); 60 | 61 | return (F * result * specular_reflectance) & active; 62 | } 63 | 64 | 65 | template 66 | Float RoughConductor::__pdf(const Intersection& _its, const Vector3f& _wo, Mask active) const { 67 | 68 | Intersection its(_its); 69 | Vector3f wo(_wo); 70 | 71 | if (m_twoSide) { 72 | wo.z() = drjit::mulsign(wo.z(), its.wi.z()); 73 | its.wi.z() = drjit::abs(its.wi.z()); 74 | } 75 | 76 | Float cos_theta_i = Frame::cos_theta(its.wi), 77 | cos_theta_o = Frame::cos_theta(wo); 78 | 79 | Spectrum m = normalize(wo + its.wi); 80 | 81 | active &= cos_theta_i > 0.f && cos_theta_o > 0.f && 82 | dot(its.wi, m) > 0.f && dot(wo, m) > 0.f; 83 | 84 | Float alpha_u = m_alpha_u.eval(its.uv); 85 | Float alpha_v = m_alpha_v.eval(its.uv); 86 | GGXDistribution distr(alpha_u, alpha_v); 87 | Float result = distr.eval(m) * distr.smith_g1(its.wi, m) / 88 | (4.f * cos_theta_i); 89 | result = detach(result); 90 | return result; 91 | } 92 | 93 | 94 | template 95 | BSDFSample RoughConductor::__sample(const Intersection& _its, const Vector3f& sample, Mask active) const { 96 | 97 | Intersection its(_its); 98 | 99 | if (m_twoSide) { 100 | its.wi.z() = drjit::abs(its.wi.z()); 101 | } 102 | 103 | BSDFSample bs; 104 | Float cos_theta_i = Frame::cos_theta(its.wi); 105 | Float alpha_u = m_alpha_u.eval(its.uv); 106 | Float alpha_v = m_alpha_v.eval(its.uv); 107 | GGXDistribution distr(alpha_u, alpha_v); 108 | 109 | auto m_pair = distr.sample(its.wi, sample); 110 | Vector3f m = std::get<0>(m_pair); 111 | Float m_pdf = std::get<1>(m_pair); 112 | bs.wo = fmsub(Vector3f(m), 2.f * dot(its.wi, m), its.wi); 113 | bs.eta = 1.0f; // TODO: Fix later 114 | bs.pdf = m_pdf / (4.f * dot(bs.wo, m)); 115 | bs.is_valid = (cos_theta_i > 0.f && neq(bs.pdf, 0.f) && Frame::cos_theta(bs.wo) > 0.f) & active; 116 | return detach(bs); 117 | } 118 | 119 | NAMESPACE_END(psdr_jit) 120 | -------------------------------------------------------------------------------- /src/bsdf/roughdielectric.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | NAMESPACE_BEGIN(psdr_jit) 6 | 7 | SpectrumC RoughDielectric::eval(const IntersectionC& its, const Vector3fC& wo, MaskC active) const { 8 | return __eval(its, wo, active); 9 | } 10 | 11 | 12 | SpectrumD RoughDielectric::eval(const IntersectionD& its, const Vector3fD& wo, MaskD active) const { 13 | return __eval(its, wo, active); 14 | } 15 | 16 | 17 | BSDFSampleC RoughDielectric::sample(const IntersectionC& its, const Vector3fC& sample, MaskC active) const { 18 | return __sample(its, sample, active); 19 | } 20 | 21 | 22 | BSDFSampleD RoughDielectric::sample(const IntersectionD& its, const Vector3fD& sample, MaskD active) const { 23 | return __sample(its, sample, active); 24 | } 25 | 26 | FloatC RoughDielectric::pdf(const IntersectionC& its, const Vector3fC& wo, MaskC active) const { 27 | return __pdf(its, wo, active); 28 | } 29 | 30 | 31 | FloatD RoughDielectric::pdf(const IntersectionD& its, const Vector3fD& wo, MaskD active) const { 32 | return __pdf(its, wo, active); 33 | } 34 | 35 | 36 | template 37 | Spectrum RoughDielectric::__eval(const Intersection& _its, const Vector3f& _wo, Mask active) const { 38 | Intersection its(_its); 39 | Vector3f wo(_wo); 40 | 41 | if (m_twoSide) { 42 | wo.z() = drjit::mulsign(wo.z(), its.wi.z()); 43 | its.wi.z() = drjit::abs(its.wi.z()); 44 | } 45 | Float cos_theta_i = Frame::cos_theta(its.wi), 46 | cos_theta_o = Frame::cos_theta(wo); 47 | 48 | // Ignore perfectly grazing configurations 49 | active &= neq(cos_theta_i, 0.f); 50 | 51 | // Determine the type of interaction 52 | bool has_reflection = true, 53 | has_transmission = true; 54 | 55 | Mask reflect = cos_theta_i * cos_theta_o > 0.f; 56 | 57 | // Determine the relative index of refraction 58 | Float eta, inv_eta; 59 | if constexpr (ad) { 60 | eta = select(cos_theta_i > 0.f, m_eta, m_inv_eta); 61 | inv_eta = select(cos_theta_i > 0.f, m_inv_eta, m_eta); 62 | } else { 63 | eta = select(cos_theta_i > 0.f, detach(m_eta), detach(m_inv_eta)); 64 | inv_eta = select(cos_theta_i > 0.f, detach(m_inv_eta), detach(m_eta)); 65 | } 66 | 67 | // Compute the half-vector 68 | Vector3f m = normalize(its.wi + wo * select(reflect, 1.f, eta)); 69 | 70 | // Ensure that the half-vector points into the same hemisphere as the macrosurface normal 71 | m = mulsign(m, Frame::cos_theta(m)); 72 | 73 | 74 | /* Construct the microfacet distribution matching the 75 | roughness values at the current surface position. */ 76 | Float alpha_u = m_alpha_u.eval(its.uv); 77 | Float alpha_v = m_alpha_v.eval(its.uv); 78 | GGXDistribution distr(alpha_u, alpha_v); 79 | 80 | // Evaluate the microfacet normal distribution 81 | Float D = distr.eval(m); 82 | 83 | // Fresnel factor 84 | Float F; 85 | if constexpr (ad) { 86 | F = std::get<0>(fresnel_dielectric(m_eta, dot(its.wi, m))); 87 | } else { 88 | F = std::get<0>(fresnel_dielectric(detach(m_eta), dot(its.wi, m))); 89 | } 90 | 91 | // Smith's shadow-masking function 92 | Float G = distr.G(its.wi, wo, m); 93 | 94 | 95 | Spectrum result = 0.0f; 96 | 97 | Mask eval_r = Mask(has_reflection) && reflect && active, 98 | eval_t = Mask(has_transmission) && !reflect && active; 99 | 100 | if (any_or(eval_r)) { 101 | Spectrum value = F * D * G / (4.f * abs(cos_theta_i)); 102 | // TODO: add m_specular_reflectance eval 103 | result[eval_r] = value; 104 | } 105 | 106 | if (any_or(eval_t)) { 107 | /* Missing term in the original paper: account for the solid angle 108 | compression when tracing radiance -- this is necessary for 109 | bidirectional methods. */ 110 | Float scale = sqr(inv_eta); 111 | // Float scale(1.f); 112 | 113 | // Compute the total amount of transmission 114 | Spectrum value = abs( 115 | (scale * (1.f - F) * D * G * eta * eta * dot(its.wi, m) * dot(wo, m)) / 116 | (cos_theta_i * sqr(dot(its.wi, m) + eta * dot(wo, m)))); 117 | 118 | result[eval_t] = value; 119 | } 120 | return result; 121 | } 122 | 123 | 124 | template 125 | Float RoughDielectric::__pdf(const Intersection& _its, const Vector3f& _wo, Mask active) const { 126 | 127 | Intersection its(_its); 128 | Vector3f wo(_wo); 129 | 130 | if (m_twoSide) { 131 | wo.z() = drjit::mulsign(wo.z(), its.wi.z()); 132 | its.wi.z() = drjit::abs(its.wi.z()); 133 | } 134 | 135 | Float cos_theta_i = Frame::cos_theta(its.wi), 136 | cos_theta_o = Frame::cos_theta(wo); 137 | 138 | active &= neq(cos_theta_i, 0.f); 139 | Mask reflect = cos_theta_i * cos_theta_o > 0.f; 140 | 141 | 142 | 143 | Float eta; 144 | if constexpr (ad) { 145 | eta = select(cos_theta_i > 0.f, m_eta, m_inv_eta); 146 | } else { 147 | eta = select(cos_theta_i > 0.f, detach(m_eta), detach(m_inv_eta)); 148 | } 149 | 150 | Vector3f m = normalize(its.wi + wo * select(reflect, 1.f, eta)); 151 | m = mulsign(m, Frame::cos_theta(m)); 152 | active &= dot(its.wi, m) * Frame::cos_theta(its.wi) > 0.f && 153 | dot(wo, m) * Frame::cos_theta(wo) > 0.f; 154 | 155 | Float dwh_dwo = select(reflect, rcp(4.f * dot(wo, m)), 156 | (eta * eta * dot(wo, m)) / 157 | sqr(dot(its.wi, m) + eta * dot(wo, m))); 158 | Float alpha_u = m_alpha_u.eval(its.uv); 159 | Float alpha_v = m_alpha_v.eval(its.uv); 160 | GGXDistribution distr(alpha_u, alpha_v); 161 | Vector3f pwi = mulsign(its.wi, Frame::cos_theta(its.wi)); 162 | Float prob = distr.eval(m) * distr.smith_g1(pwi, m) / Frame::cos_theta(pwi); 163 | 164 | Float mmeta; 165 | if constexpr (ad) { 166 | mmeta = m_eta; 167 | } else { 168 | mmeta = detach(m_eta); 169 | } 170 | 171 | Float F = std::get<0>(fresnel_dielectric(mmeta, dot(its.wi, m))); 172 | prob *= select(reflect, F, 1.f - F); 173 | 174 | return select(active, prob * abs(dwh_dwo), 0.f); 175 | } 176 | 177 | 178 | template 179 | BSDFSample RoughDielectric::__sample(const Intersection& _its, const Vector3f& sample, Mask active) const { 180 | 181 | Intersection its(_its); 182 | 183 | if (m_twoSide) { 184 | its.wi.z() = drjit::abs(its.wi.z()); 185 | } 186 | 187 | BSDFSample bs = zeros>(); 188 | Float cos_theta_i = Frame::cos_theta(its.wi); 189 | Float alpha_u = m_alpha_u.eval(its.uv); 190 | Float alpha_v = m_alpha_v.eval(its.uv); 191 | GGXDistribution distr(alpha_u, alpha_v); 192 | 193 | 194 | active &= neq(cos_theta_i, 0.f); 195 | 196 | 197 | // Sample the microfacet normal 198 | Vector3f m; 199 | std::tie(m, bs.pdf) = distr.sample(mulsign(its.wi, cos_theta_i), sample); 200 | active &= neq(bs.pdf, 0.f); 201 | 202 | Float eta; 203 | if constexpr (ad) { 204 | eta = m_eta; 205 | } else { 206 | eta = detach(m_eta); 207 | } 208 | 209 | auto [F, cos_theta_t, eta_it, eta_ti] = 210 | fresnel_dielectric(eta, dot(its.wi, m)); 211 | 212 | Mask selected_r, selected_t; 213 | selected_r = sample.z() <= F && active; 214 | selected_t = !selected_r && active; 215 | // Select the lobe to be sampled 216 | bs.pdf *= select(selected_r, F, 1.f - F); 217 | bs.eta = select(selected_r, 1.f, eta_it); 218 | 219 | Float dwh_dwo = 0.f; 220 | // Reflection sampling 221 | if (any_or(selected_r)) { 222 | bs.wo[selected_r] = fmsub(Vector3f(m), 2.f * dot(its.wi, m), its.wi); 223 | dwh_dwo = rcp(4.f * dot(bs.wo, m)); 224 | } 225 | // Transmission sampling 226 | if (any_or(selected_t)) { 227 | bs.wo[selected_t] = fmsub(m, fmadd(dot(its.wi, m), eta_ti, cos_theta_t), its.wi * eta_ti); 228 | dwh_dwo[selected_t] = 229 | (sqr(bs.eta) * dot(bs.wo, m)) / 230 | sqr(dot(its.wi, m) + bs.eta * dot(bs.wo, m)); 231 | } 232 | 233 | 234 | bs.pdf *= abs(dwh_dwo) * distr.smith_g1(bs.wo, m); 235 | bs.is_valid = active && (selected_t || selected_r); 236 | return detach(bs); 237 | } 238 | 239 | NAMESPACE_END(psdr_jit) 240 | -------------------------------------------------------------------------------- /src/core/bitmap.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | NAMESPACE_BEGIN(psdr_jit) 9 | 10 | template 11 | Bitmap::Bitmap() 12 | : m_resolution(1, 1), m_data(zeros()) {drjit::make_opaque(m_scale, m_rot, m_trans, m_data);} 13 | 14 | 15 | template 16 | Bitmap::Bitmap(const char *file_name) { 17 | load_openexr(file_name); 18 | drjit::make_opaque(m_scale, m_rot, m_trans, m_data); 19 | } 20 | 21 | 22 | template 23 | Bitmap::Bitmap(ScalarValue value) : m_resolution(1, 1), m_data(value) {drjit::make_opaque(m_scale, m_rot, m_trans, m_data);} 24 | 25 | 26 | template 27 | Bitmap::Bitmap(int width, int height, const ValueD &data) : m_resolution(width, height), m_data(data) { 28 | // TODO: fix this 29 | // PSDR_ASSERT(width*height*channels == static_cast(data.size())); 30 | drjit::make_opaque(m_scale, m_rot, m_trans, m_data); 31 | } 32 | 33 | 34 | template 35 | void Bitmap::load_openexr(const char *file_name) { 36 | Vector4fC data; 37 | std::tie(data, m_resolution) = BitmapLoader::load_openexr_rgba(file_name); 38 | if constexpr ( channels == 1 ) { 39 | m_data = data[0]; 40 | } else { 41 | m_data = Vector3fD(data[0], data[1], data[2]); 42 | } 43 | } 44 | 45 | 46 | template 47 | template 48 | typename Bitmap::template Value Bitmap::eval(Vector2f uv, bool flip_v, bool envmap_mode) const { 49 | const int width = m_resolution.x(), height = m_resolution.y(); 50 | 51 | if ( static_cast(slices(m_data)) != width*height ) 52 | throw Exception("Bitmap: invalid data size!"); 53 | 54 | if ( width == 1 && height == 1 ) { 55 | if constexpr ( ad ) 56 | return m_data; 57 | else 58 | return detach(m_data); 59 | } else { 60 | if ( width < 2 || height < 2 ) 61 | throw Exception("Bitmap: invalid resolution!"); 62 | 63 | if constexpr ( ad ) { 64 | uv = Vector2fD((uv.x()-0.5f)*cos((m_rot)) + (uv.y()-0.5f)*sin((m_rot)), -(uv.x()-0.5f)*sin((m_rot)) + (uv.y()-0.5f)*cos((m_rot))); 65 | uv += .5f; 66 | if ( flip_v ) { 67 | // flip the v coordinates to match common practices 68 | uv.y() = -uv.y(); 69 | } 70 | uv *= (m_scale); 71 | uv.x() -= -.5f+(m_scale)/2; 72 | uv.y() += -.5f+(m_scale)/2; 73 | uv += (m_trans); 74 | } else { 75 | uv = Vector2fC((uv.x()-0.5f)*cos(detach(m_rot)) + (uv.y()-0.5f)*sin(detach(m_rot)), -(uv.x()-0.5f)*sin(detach(m_rot)) + (uv.y()-0.5f)*cos(detach(m_rot))); 76 | uv += .5f; 77 | if ( flip_v ) { 78 | // flip the v coordinates to match common practices 79 | uv.y() = -uv.y(); 80 | } 81 | uv *= detach(m_scale); 82 | uv.x() -= -.5f+detach(m_scale)/2; 83 | uv.y() += -.5f+detach(m_scale)/2; 84 | uv += detach(m_trans); 85 | } 86 | 87 | if (envmap_mode) { 88 | uv.x() -= 0.5 / width; 89 | uv -= floor(uv); 90 | uv.x() *= m_resolution.x(); 91 | uv.y() *= m_resolution.y() - 1; 92 | } 93 | else { 94 | uv -= floor(uv); 95 | uv *= m_resolution - 1; 96 | } 97 | 98 | Vector2i pos = floor2int, Vector2f>(uv); 99 | Vector2f w1 = uv - Vector2f(pos), w0 = 1.0f - w1; 100 | 101 | Int yw, xp1; 102 | if (envmap_mode) { 103 | yw = minimum(pos.y(), m_resolution.y() - 2) * width; 104 | xp1 = imod(pos.x() + 1, m_resolution.x()); 105 | } 106 | else { 107 | pos = minimum(pos, m_resolution - 2); 108 | yw = pos.y() * width; 109 | xp1 = pos.x() + 1; 110 | } 111 | 112 | Value v00, v10, v01, v11; 113 | if constexpr ( ad ) { 114 | v00 = gather(m_data, yw + pos.x()); 115 | v10 = gather(m_data, yw + xp1); 116 | v01 = gather(m_data, yw + pos.x() + width); 117 | v11 = gather(m_data, yw + xp1 + width); 118 | } else { 119 | const ValueC &data = detach(m_data); 120 | v00 = gather(data, yw + pos.x()); 121 | v10 = gather(data, yw + xp1); 122 | v01 = gather(data, yw + pos.x() + width); 123 | v11 = gather(data, yw + xp1 + width); 124 | } 125 | 126 | // Bilinear interpolation 127 | Value v0 = fmadd(w0.x(), v00, w1.x()*v10), 128 | v1 = fmadd(w0.x(), v01, w1.x()*v11); 129 | return fmadd(w0.y(), v0, w1.y()*v1); 130 | } 131 | } 132 | 133 | 134 | // Explicit instantiations 135 | template struct Bitmap<1>; 136 | template struct Bitmap<3>; 137 | 138 | template FloatC Bitmap<1>::eval(Vector2fC, bool, bool) const; 139 | template FloatD Bitmap<1>::eval(Vector2fD, bool, bool) const; 140 | 141 | template Vector3fC Bitmap<3>::eval(Vector2fC, bool, bool) const; 142 | template Vector3fD Bitmap<3>::eval(Vector2fD, bool, bool) const; 143 | 144 | NAMESPACE_END(psdr_jit) 145 | -------------------------------------------------------------------------------- /src/core/bitmap_loader.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #define TINYEXR_USE_MINIZ 0 6 | #include 7 | #define TINYEXR_IMPLEMENTATION 8 | #include 9 | 10 | NAMESPACE_BEGIN(psdr_jit) 11 | 12 | std::pair BitmapLoader::load_openexr_rgba(const char *file_name) { 13 | { 14 | float* out; // width * height * RGBA 15 | int width; 16 | int height; 17 | const char* err = NULL; // or nullptr in C++11 18 | 19 | int ret = LoadEXR(&out, &width, &height, file_name, &err); 20 | int size = width*height; 21 | 22 | if (ret != TINYEXR_SUCCESS) { 23 | if (err) { 24 | fprintf(stderr, "ERR : %s\n", err); 25 | FreeEXRErrorMessage(err); // release memory of error message. 26 | PSDR_ASSERT(0); 27 | } 28 | } 29 | std::vector buf[4]; 30 | for ( int i = 0; i < 4; ++i ) buf[i].resize(size); 31 | 32 | int offset = 0; 33 | for ( int i = 0; i < height; ++i ) 34 | for ( int j = 0; j < width; ++j ) { 35 | buf[0][offset] = out[offset*4]; 36 | buf[1][offset] = out[offset*4+1]; 37 | buf[2][offset] = out[offset*4+2]; 38 | buf[3][offset] = out[offset*4+3]; 39 | ++offset; 40 | } 41 | free(out); // release memory of image data 42 | 43 | return { 44 | Vector4fC( 45 | drjit::load(buf[0].data(), size), 46 | drjit::load(buf[1].data(), size), 47 | drjit::load(buf[2].data(), size), 48 | drjit::load(buf[3].data(), size) 49 | ), 50 | ScalarVector2i(width, height) 51 | }; 52 | } 53 | } 54 | 55 | NAMESPACE_END(psdr_jit) 56 | -------------------------------------------------------------------------------- /src/core/cube_distrb.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | // #define USE_2D_DIS 6 | 7 | NAMESPACE_BEGIN(psdr_jit) 8 | 9 | template 10 | void HyperCubeDistribution::set_resolution(const Array &reso) { 11 | if ( m_resolution != reso ) { 12 | Array prod_reso; 13 | prod_reso[ndim - 1] = reso[ndim - 1]; 14 | for ( int i = ndim - 2; i >= 0; --i ) 15 | prod_reso[i] = reso[i]*prod_reso[i + 1]; 16 | PSDR_ASSERT(prod_reso[0] < std::numeric_limits::max()); 17 | 18 | m_num_cells = static_cast(prod_reso[0]); 19 | m_resolution = reso; 20 | m_unit = rcp(Array(reso)); 21 | 22 | IntC &cur = m_cells[ndim - 1]; 23 | cur = arange(m_num_cells); 24 | for ( int i = 0; i < ndim - 1; ++i ) { 25 | int denorm = static_cast(prod_reso[i + 1]); 26 | m_cells[i] = divisor(denorm)(cur); 27 | cur -= m_cells[i]*denorm; 28 | } 29 | m_ready = false; 30 | } 31 | } 32 | 33 | 34 | template 35 | void HyperCubeDistribution::set_mass(const FloatC &pmf) { 36 | PSDR_ASSERT(static_cast(slices(pmf)) == m_num_cells); 37 | m_distrb.init(pmf); 38 | m_ready = true; 39 | } 40 | 41 | 42 | template 43 | FloatC HyperCubeDistribution::sample_reuse(Vectorf &samples) const { 44 | PSDR_ASSERT(m_ready); 45 | auto [idx, pdf] = m_distrb.sample_reuse(samples[ndim - 1]); 46 | samples += gather>(m_cells, idx); 47 | samples *= m_unit; 48 | return pdf*static_cast(m_num_cells); 49 | } 50 | 51 | 52 | template 53 | FloatC HyperCubeDistribution::pdf(const Vectorf &p) const { 54 | PSDR_ASSERT(m_ready); 55 | 56 | auto ip = floor2int, Vectorf>(p*m_resolution); 57 | MaskC valid = (ip[0] >= 0 && ip[0] < m_resolution[0]); 58 | IntC idx = ip[0]; 59 | for ( int i = 1; i < ndim; ++i ) { 60 | valid &= (ip[i] >= 0 && ip[i] < m_resolution[i]); 61 | idx = fmadd(idx, m_resolution[i], ip[i]); 62 | } 63 | return (gather(m_distrb.pmf(), idx)*static_cast(m_num_cells)) & valid; 64 | } 65 | 66 | void CubeDistribution::set_resolution(const Array &reso) { 67 | 68 | // std::cout << "set reolution for CubeDistribution" << std::endl; 69 | // std::cout << reso << std::endl; 70 | if ( m_resolution != reso ) { 71 | Array prod_reso; 72 | prod_reso[1] = reso[1]; 73 | for ( int i = 0; i >= 0; --i ) 74 | prod_reso[i] = reso[i]*prod_reso[i + 1]; 75 | PSDR_ASSERT(prod_reso[0] < std::numeric_limits::max()); 76 | 77 | m_num_cells = static_cast(prod_reso[0]); 78 | m_resolution = reso; 79 | m_unit = rcp(Array(reso)); 80 | 81 | IntC &cur = m_cells[1]; 82 | cur = arange(m_num_cells); 83 | for ( int i = 0; i < 1; ++i ) { 84 | int denorm = static_cast(prod_reso[i + 1]); 85 | m_cells[i] = divisor(denorm)(cur); 86 | cur -= m_cells[i]*denorm; 87 | } 88 | m_ready = false; 89 | } 90 | } 91 | 92 | 93 | void CubeDistribution::set_mass(const Bitmap3fD &m_radiance) { 94 | int width = m_resolution[0]; 95 | int height = m_resolution[1]; 96 | 97 | Vector2fC uv = (m_cells + Vector2fC(.5f, .5f))*m_unit; 98 | SpectrumC val = m_radiance.eval(uv, false); 99 | FloatC theta = ((arange(width*height) % (height)) + .5f)*(Pi/static_cast(height)); 100 | FloatC radiance_val = rgb2luminance(val)*sin(theta); 101 | 102 | m_distrb.init(radiance_val); 103 | 104 | 105 | #ifdef USE_2D_DIS 106 | FloatC theta_pmf = zero(width); 107 | scatter_add(theta_pmf, radiance_val, m_cells[0]); 108 | m_theta_distrb.init(theta_pmf); 109 | 110 | std::vector m_DiscreteDistributions; 111 | for (int i=0; i(height); 114 | IntC phi_id = m_cells[1]; 115 | IntC buf_mask = arange(width*height); 116 | MaskC other_phi = ( i*height <= buf_mask && buf_mask < (i+1)*height); 117 | scatter_add(temp_phi_pmf, radiance_val, phi_id, other_phi); 118 | _DiscreteDistribution *distrb = new _DiscreteDistribution(); 119 | distrb->init(temp_phi_pmf); 120 | distrb->m_id = std::to_string(i); 121 | m_DiscreteDistributions.push_back(distrb); 122 | } 123 | m_phi_distrbs = DiscreteDistributionArrayC::copy(m_DiscreteDistributions.data(), m_DiscreteDistributions.size()); 124 | #endif 125 | 126 | 127 | m_ready = true; 128 | } 129 | 130 | 131 | FloatC CubeDistribution::sample_reuse(Vectorf<2, false> &_samples) const { 132 | PSDR_ASSERT(m_ready); 133 | #ifdef USE_2D_DIS 134 | Vector2fC samples(_samples); 135 | auto [idx, pdf_theta] = m_theta_distrb.sample_reuse(samples[1]); 136 | DiscreteDistributionArrayC arr_phi_distrb = gather(m_phi_distrbs, idx); 137 | DiscreteRecordC phi_record = arr_phi_distrb->sample_reuse(samples[0]); 138 | samples[0] = phi_record.rnd; 139 | _samples[1] = (samples[0] + phi_record.idx)/m_resolution[1]; 140 | _samples[0] = (samples[1] + idx)/m_resolution[0]; 141 | return pdf_theta*phi_record.pdf*static_cast(m_num_cells); 142 | #else 143 | auto [idx, pdf] = m_distrb.sample_reuse(_samples[1]); 144 | _samples += gather>(m_cells, idx); 145 | _samples *= m_unit; 146 | return pdf*static_cast(m_num_cells); 147 | #endif 148 | } 149 | 150 | 151 | FloatC CubeDistribution::pdf(const Vectorf<2, false> &p) const { 152 | PSDR_ASSERT(m_ready); 153 | auto ip = floor2int, Vectorf<2, false>>(p*m_resolution); 154 | MaskC valid = (ip[0] >= 0 && ip[0] < m_resolution[0]); 155 | IntC idx = ip[0]; 156 | for ( int i = 1; i < 2; ++i ) { 157 | valid &= (ip[i] >= 0 && ip[i] < m_resolution[i]); 158 | idx = fmadd(idx, m_resolution[i], ip[i]); 159 | } 160 | return (gather(m_distrb.pmf(), idx)*static_cast(m_num_cells)) & valid; 161 | } 162 | 163 | 164 | 165 | // Explicit instantiations 166 | template void HyperCubeDistribution<2>::set_resolution(const ScalarVector2i&); 167 | template void HyperCubeDistribution<2>::set_mass(const FloatC&); 168 | template FloatC HyperCubeDistribution<2>::sample_reuse(Vector2fC&) const; 169 | template FloatC HyperCubeDistribution<2>::pdf(const Vector2fC&) const; 170 | 171 | template void HyperCubeDistribution<3>::set_resolution(const ScalarVector3i &reso); 172 | template void HyperCubeDistribution<3>::set_mass(const FloatC &pmf); 173 | template FloatC HyperCubeDistribution<3>::sample_reuse(Vector3fC &samples) const; 174 | template FloatC HyperCubeDistribution<3>::pdf(const Vector3fC&) const; 175 | 176 | NAMESPACE_END(psdr_jit) 177 | -------------------------------------------------------------------------------- /src/core/pmf.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | NAMESPACE_BEGIN(psdr_jit) 5 | 6 | void DiscreteDistribution::init(const FloatC &pmf) { 7 | m_size = static_cast((pmf.size())); 8 | m_sum = sum(pmf); 9 | m_pmf = pmf; 10 | FloatC temp = drjit::migrate(m_pmf, AllocType::Host); 11 | drjit::sync_thread(); 12 | m_cmf = compute_cdf(temp); 13 | m_pmf_normalized = pmf/m_sum; 14 | m_cmf_normalized = m_cmf/m_sum; 15 | } 16 | 17 | 18 | std::pair DiscreteDistribution::sample(const FloatC &_samples) const { 19 | if ( unlikely(m_size == 1) ) { 20 | return { zeros(), full(1.f) }; 21 | } 22 | FloatC samples = _samples*m_sum; 23 | IntC idx = binary_search( 24 | 0, m_size - 1, [&](IntC i)DRJIT_INLINE_LAMBDA { return gather(m_cmf, i) < samples; } 25 | ); 26 | return { idx, gather(m_pmf, idx)/m_sum }; 27 | } 28 | 29 | 30 | template 31 | std::pair DiscreteDistribution::sample_reuse(Float &samples) const { 32 | if ( unlikely(m_size == 1) ) { 33 | return { zeros(), full(1.f) }; 34 | } 35 | samples *= m_sum; 36 | IntC idx; 37 | if constexpr ( ad ) { 38 | idx = binary_search( 39 | 0, m_size - 1, [&](IntC i)DRJIT_INLINE_LAMBDA { return gather(m_cmf, i) < detach(samples); } 40 | ); 41 | } else { 42 | idx = binary_search( 43 | 0, m_size - 1, [&](IntC i)DRJIT_INLINE_LAMBDA { return gather(m_cmf, i) < samples; } 44 | ); 45 | } 46 | samples -= gather(m_cmf, idx - 1, idx > 0); 47 | FloatC pmf = gather(m_pmf, idx); 48 | masked(samples, pmf > 0.f) /= pmf; 49 | samples = clamp(samples, 0.f, 1.f); 50 | return { idx, pmf/m_sum }; 51 | } 52 | 53 | // Explicit instantiations 54 | template std::pair DiscreteDistribution::sample_reuse(FloatC&) const; 55 | template std::pair DiscreteDistribution::sample_reuse(FloatD&) const; 56 | 57 | NAMESPACE_END(psdr_jit) 58 | -------------------------------------------------------------------------------- /src/core/sampler.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | NAMESPACE_BEGIN(psdr_jit) 5 | 6 | template 7 | uint64_array_t sample_tea_64(UInt32 v0, UInt32 v1, int rounds = 4) { 8 | UIntC sum = 0; 9 | 10 | for ( int i = 0; i < rounds; ++i ) { 11 | sum += 0x9e3779b9; 12 | v0 += (sl<4>(v1) + 0xa341316c) ^ (v1 + sum) ^ (sr<5>(v1) + 0xc8013ea4); 13 | v1 += (sl<4>(v0) + 0xad90777d) ^ (v0 + sum) ^ (sr<5>(v0) + 0x7e95761e); 14 | } 15 | 16 | return uint64_array_t(v0) + sl<32>(uint64_array_t(v1)); 17 | } 18 | 19 | void Sampler::seed(UInt64C seed_value) { 20 | if ( !m_rng ) 21 | m_rng = std::make_unique(); 22 | 23 | seed_value += m_base_seed; 24 | 25 | UInt64C idx = arange(seed_value.size()); 26 | 27 | m_rng->seed(1, sample_tea_64(seed_value, idx), sample_tea_64(idx, seed_value)); 28 | 29 | m_sample_count = static_cast(seed_value.size()); 30 | } 31 | 32 | 33 | template 34 | Float Sampler::next_1d() { 35 | if ( m_rng == nullptr ) 36 | throw Exception("Sampler::seed() must be invoked before using this sampler!"); 37 | else { 38 | Float rs = m_rng->template next_float>(); 39 | schedule(m_rng->inc, m_rng->state); 40 | return rs; 41 | } 42 | } 43 | 44 | 45 | // Explicit instanciations 46 | template FloatC Sampler::next_1d(); 47 | template FloatD Sampler::next_1d(); 48 | 49 | NAMESPACE_END(psdr_jit) 50 | -------------------------------------------------------------------------------- /src/emitter/area.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | NAMESPACE_BEGIN(psdr_jit) 8 | 9 | void AreaLight::configure() { 10 | PSDR_ASSERT((m_mesh != nullptr) && m_mesh->m_ready); 11 | m_sampling_weight = m_mesh->m_total_area* 12 | rgb2luminance(detach(m_radiance))[0]; 13 | m_ready = true; 14 | } 15 | 16 | 17 | SpectrumC AreaLight::eval(const IntersectionC &its, MaskC active) const { 18 | PSDR_ASSERT(m_ready); 19 | return select(active && FrameC::cos_theta(its.wi) > 0.f, detach(m_radiance), 0.f); 20 | } 21 | 22 | 23 | SpectrumD AreaLight::eval(const IntersectionD &its, MaskD active) const { 24 | PSDR_ASSERT(m_ready); 25 | return select(active && FrameD::cos_theta(its.wi) > 0.f, m_radiance, 0.f); 26 | } 27 | 28 | 29 | PositionSampleC AreaLight::sample_position(const Vector3fC &ref_p, const Vector2fC &sample2, MaskC active) const { 30 | return __sample_position(sample2, active); 31 | } 32 | 33 | 34 | PositionSampleD AreaLight::sample_position(const Vector3fD &ref_p, const Vector2fD &sample2, MaskD active) const { 35 | return __sample_position(sample2, active); 36 | } 37 | 38 | 39 | template 40 | PositionSample AreaLight::__sample_position(const Vector2f &sample2, Mask active) const { 41 | PSDR_ASSERT(m_ready); 42 | // PositionSample rs = m_mesh->sample_position(sample2, active); 43 | // return rs; 44 | return m_mesh->sample_position(sample2, active); 45 | } 46 | 47 | 48 | FloatC AreaLight::sample_position_pdf(const Vector3fC &ref_p, const IntersectionC &its, MaskC active) const { 49 | return m_sampling_weight*its.shape->sample_position_pdf(its, active); 50 | } 51 | 52 | 53 | FloatD AreaLight::sample_position_pdf(const Vector3fD &ref_p, const IntersectionD &its, MaskD active) const { 54 | 55 | // FloatD temp = its.shape->sample_position_pdf(its, active); 56 | return m_sampling_weight*its.shape->sample_position_pdfD(its, active); 57 | // return m_sampling_weight*its.shape->sample_position_pdf(its, active); 58 | // return __sample_position_pdf(ref_p, its, active); 59 | } 60 | 61 | 62 | template 63 | Float AreaLight::__sample_position_pdf(const Vector3f &ref_p, const Intersection &its, Mask active) const { 64 | // return 1.f; 65 | 66 | // auto temp = its.shape->sample_position_pdf(its, active); 67 | return 1.f; 68 | // Float rs; 69 | // if constexpr (!ad) { 70 | // rs = m_sampling_weight*its.shape->sample_position_pdf(detach(its), detach(active)); 71 | // } else { 72 | // rs = m_sampling_weight*its.shape->sample_position_pdf(its, active); 73 | // } 74 | // // Float rs = m_sampling_weight*its.shape->sample_position_pdf(its, active); 75 | // return rs; 76 | } 77 | 78 | 79 | std::string AreaLight::to_string() const { 80 | std::ostringstream oss; 81 | oss << "AreaLight[radiance = " << m_radiance << ", sampling_weight = " << m_sampling_weight << "]"; 82 | return oss.str(); 83 | } 84 | 85 | NAMESPACE_END(psdr_jit) 86 | -------------------------------------------------------------------------------- /src/emitter/envmap.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | 8 | // #include 9 | // #include 10 | // #include 11 | // #include 12 | 13 | 14 | 15 | NAMESPACE_BEGIN(psdr_jit) 16 | 17 | void EnvironmentMap::configure() { 18 | m_sampling_weight = 0.0f; 19 | int width = m_radiance.m_resolution.x(), height = m_radiance.m_resolution.y(); 20 | 21 | PSDR_ASSERT(width > 1 && height > 1); 22 | 23 | width = (width - 1) << 1; height = (height - 1) << 1; 24 | 25 | m_cell_distrb.set_resolution(ScalarVector2i(width, height)); 26 | 27 | // m_cube_distrb.set_resolution(ScalarVector2i(width, height)); 28 | 29 | Vector2fC uv = (m_cell_distrb.m_cells + Vector2fC(.5f, .5f))*m_cell_distrb.m_unit; 30 | SpectrumC val = m_radiance.eval(uv, false, true); 31 | FloatC theta = ((arange(width*height) % (height)) + .5f)*(Pi/static_cast(height)); 32 | m_cell_distrb.set_mass(rgb2luminance(val)*sin(theta)); 33 | 34 | // m_cube_distrb.set_mass(m_radiance); 35 | 36 | 37 | 38 | m_to_world = m_to_world_left*m_to_world_raw; 39 | m_from_world = inverse(m_to_world); 40 | m_ready = true; 41 | } 42 | 43 | 44 | SpectrumC EnvironmentMap::eval(const IntersectionC &its, MaskC active) const { 45 | Vector3fC wi_world = its.sh_frame.to_world(its.wi); 46 | return eval_direction(-wi_world, active); 47 | } 48 | 49 | 50 | SpectrumD EnvironmentMap::eval(const IntersectionD &its, MaskD active) const { 51 | Vector3fD wi_world = its.sh_frame.to_world(its.wi); 52 | return eval_direction(-wi_world, active); 53 | } 54 | 55 | 56 | template 57 | Spectrum EnvironmentMap::eval_direction(const Vector3f &wi, Mask active) const { 58 | PSDR_ASSERT(m_ready); 59 | Vector3f v; 60 | if constexpr ( ad ) { 61 | v = transform_dir(m_from_world, wi); 62 | } else { 63 | v = transform_dir(detach(m_from_world), wi); 64 | } 65 | 66 | Vector2f uv(atan2(v.x(), -v.z())*InvTwoPi, safe_acos(v.y())*InvPi); 67 | uv -= floor(uv); 68 | if constexpr ( ad ) { 69 | return m_radiance.eval(uv, false, true)*m_scale; 70 | } else { 71 | return m_radiance.eval(uv, false, true)*detach(m_scale); 72 | } 73 | } 74 | 75 | 76 | PositionSampleC EnvironmentMap::sample_position(const Vector3fC &ref_p, const Vector2fC &sample2, MaskC active) const { 77 | return __sample_position(ref_p, sample2, active); 78 | } 79 | 80 | 81 | PositionSampleD EnvironmentMap::sample_position(const Vector3fD &ref_p, const Vector2fD &sample2, MaskD active) const { 82 | return __sample_position(ref_p, sample2, active); 83 | } 84 | 85 | 86 | template 87 | PositionSample EnvironmentMap::__sample_position(const Vector3f &ref_p, const Vector2f &_sample2, Mask active) const { 88 | PSDR_ASSERT(m_ready); 89 | 90 | PositionSample result; 91 | 92 | RayC ray; 93 | Vector2fC sample2; 94 | FloatC pdf; 95 | if constexpr ( ad ) { 96 | ray.o = detach(ref_p); 97 | sample2 = detach(_sample2); 98 | } else { 99 | ray.o = ref_p; 100 | sample2 = _sample2; 101 | } 102 | std::tie(ray.d, pdf) = sample_direction(sample2); 103 | 104 | auto [t, n, G] = ray_intersect_scene_aabb(ray, m_lower, m_upper); 105 | 106 | result.is_valid = active; 107 | result.p = ray(t); 108 | result.n = n; 109 | result.pdf = pdf*G; 110 | result.J = 1.f; 111 | 112 | 113 | return result; 114 | } 115 | 116 | std::pair EnvironmentMap::sample_direction(Vector2fC &uv) const { 117 | PSDR_ASSERT(m_ready); 118 | FloatC pdf = m_cell_distrb.sample_reuse(uv); 119 | // FloatC pdf = m_cube_distrb.sample_reuse(uv); 120 | FloatC theta = uv.y()*Pi, phi = uv.x()*TwoPi; 121 | Vector3fC d = sphdir(theta, phi); 122 | d = Vector3fC(d.y(), d.z(), -d.x()); 123 | 124 | FloatC inv_sin_theta = safe_rsqrt(maximum(sqr(d.x()) + sqr(d.z()), sqr(Epsilon))); 125 | masked(pdf, pdf > Epsilon) *= inv_sin_theta*(.5f/sqr(Pi)); 126 | 127 | d = transform_dir(detach(m_to_world), d); 128 | return { d, pdf }; 129 | } 130 | 131 | 132 | FloatC EnvironmentMap::sample_position_pdf(const Vector3fC &ref_p, const IntersectionC &its, MaskC active) const { 133 | return __sample_position_pdf(ref_p, its, active); 134 | } 135 | 136 | 137 | FloatD EnvironmentMap::sample_position_pdf(const Vector3fD &ref_p, const IntersectionD &its, MaskD active) const { 138 | return __sample_position_pdf(ref_p, its, active); 139 | } 140 | 141 | 142 | template 143 | Float EnvironmentMap::__sample_position_pdf(const Vector3f &ref_p, const Intersection &its, Mask active) const { 144 | Vector3fC d; 145 | FloatC G, dist2; 146 | if constexpr ( ad ) { 147 | d = detach(its.p) - detach(ref_p); 148 | dist2 = squared_norm(d); d /= safe_sqrt(dist2); 149 | G = abs(dot(d, detach(its.n)))/dist2; 150 | } else { 151 | d = its.p - ref_p; 152 | dist2 = squared_norm(d); d /= safe_sqrt(dist2); 153 | G = abs(dot(d, its.n))/dist2; 154 | } 155 | 156 | d = transform_dir(detach(m_from_world), d); 157 | FloatC factor = G*safe_rsqrt(maximum(sqr(d.x()) + sqr(d.z()), sqr(Epsilon)))*(.5f/sqr(Pi)); 158 | Vector2fC uv(atan2(d.x(), -d.z())*InvTwoPi, safe_acos(d.y())*InvPi); 159 | uv -= floor(uv); 160 | return m_cell_distrb.pdf(uv)*factor; 161 | // return m_cube_distrb.pdf(uv)*factor; 162 | } 163 | 164 | 165 | std::string EnvironmentMap::to_string() const { 166 | std::ostringstream oss; 167 | oss << "EnvironmentMap[sampling_weight = " << m_sampling_weight << "]"; 168 | return oss.str(); 169 | } 170 | 171 | // Explicit instantiations 172 | template SpectrumC EnvironmentMap::eval_direction(const Vector3fC&, MaskC) const; 173 | template SpectrumD EnvironmentMap::eval_direction(const Vector3fD&, MaskD) const; 174 | 175 | NAMESPACE_END(psdr_jit) 176 | -------------------------------------------------------------------------------- /src/integrator/collocated.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | NAMESPACE_BEGIN(psdr_jit) 10 | 11 | SpectrumC CollocatedIntegrator::Li(const Scene &scene, Sampler &sampler, const RayC &ray, MaskC active) const { 12 | return __Li(scene, ray, active); 13 | } 14 | 15 | 16 | SpectrumD CollocatedIntegrator::Li(const Scene &scene, Sampler &sampler, const RayD &ray, MaskD active) const { 17 | return __Li(scene, ray, active); 18 | } 19 | 20 | 21 | template 22 | Spectrum CollocatedIntegrator::__Li(const Scene &scene, const Ray &ray, Mask active) const { 23 | Intersection its = scene.ray_intersect(ray, active); 24 | Spectrum result; 25 | // std::cout << "here" << std::endl; 26 | // return its.p; 27 | 28 | if constexpr (ad) { 29 | active &= its.is_valid(); 30 | if ( scene.m_bsdfs.size() == 1U || scene.m_meshes.size() == 1U ) { 31 | const BSDF *bsdf = scene.m_meshes[0]->m_bsdf; 32 | result = bsdf->evalD(its, its.wi, active)/sqr(its.t); 33 | } else { 34 | BSDFArray bsdf_array = its.shape->bsdf(); 35 | 36 | result = bsdf_array->evalD(its, its.wi, active)/sqr(its.t); 37 | } 38 | result *= m_intensity; 39 | } else { 40 | active &= its.is_valid(); 41 | if ( scene.m_bsdfs.size() == 1U || scene.m_meshes.size() == 1U ) { 42 | const BSDF *bsdf = scene.m_meshes[0]->m_bsdf; 43 | result = bsdf->evalC(its, its.wi, active)/sqr(its.t); 44 | } else { 45 | BSDFArray bsdf_array = its.shape->bsdf(); 46 | 47 | result = bsdf_array->evalC(its, its.wi, active)/sqr(its.t); 48 | } 49 | result *= detach(m_intensity); 50 | } 51 | 52 | // result[!active] = Spectrum(1.0); 53 | 54 | return result; 55 | } 56 | 57 | NAMESPACE_END(psdr_jit) 58 | -------------------------------------------------------------------------------- /src/integrator/field.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | NAMESPACE_BEGIN(psdr_jit) 10 | 11 | FieldExtractionIntegrator::FieldExtractionIntegrator(char *field) { 12 | std::string field_name = strtok(field, " "); 13 | PSDR_ASSERT_MSG( 14 | field_name == "bsdf" || 15 | field_name == "segmentation" || 16 | field_name == "silhouette" || 17 | field_name == "position" || 18 | field_name == "depth" || 19 | field_name == "geoNormal" || 20 | field_name == "shNormal" || 21 | field_name == "uv", 22 | "Unsupported field: " + field_name 23 | ); 24 | m_field = field_name; 25 | 26 | char *obj_name; 27 | obj_name = strtok(NULL, " "); 28 | if( obj_name != NULL ) { 29 | m_object = obj_name; 30 | } else { 31 | m_object = ""; 32 | } 33 | 34 | } 35 | 36 | 37 | SpectrumC FieldExtractionIntegrator::Li(const Scene &scene, Sampler &sampler, const RayC &ray, MaskC active) const { 38 | return __Li(scene, ray, active); 39 | } 40 | 41 | 42 | SpectrumD FieldExtractionIntegrator::Li(const Scene &scene, Sampler &sampler, const RayD &ray, MaskD active) const { 43 | return __Li(scene, ray, active); 44 | } 45 | 46 | 47 | template 48 | Spectrum FieldExtractionIntegrator::__Li(const Scene &scene, const Ray &ray, Mask active) const { 49 | Vector3f result; 50 | Intersection its = scene.ray_intersect(ray); 51 | 52 | BSDFArray bsdf_array = its.shape->bsdf(); 53 | if ( scene.m_emitter_env != nullptr ) { 54 | // Skip reflectance computations for intersections on the bounding mesh 55 | active &= neq(bsdf_array, nullptr); 56 | } 57 | Mask valid_obj(1); 58 | if (m_object != "") { 59 | if constexpr ( !ad ) { 60 | valid_obj = its.shape->get_obj_mask(m_object); 61 | } else { 62 | valid_obj = detach(its.shape)->get_obj_mask(m_object); 63 | } 64 | } 65 | 66 | 67 | if ( m_field == "segmentation" ) { 68 | if constexpr ( !ad ) { 69 | IntC rresult = (its.shape)->get_obj_id(); 70 | result = Vector3f(rresult,rresult,rresult); 71 | } else { 72 | IntC rresult = detach(its.shape)->get_obj_id(); 73 | result = Vector3f(rresult,rresult,rresult); 74 | } 75 | } else if ( m_field == "bsdf" ) { 76 | if constexpr (ad) { 77 | active &= its.is_valid(); 78 | if ( scene.m_bsdfs.size() == 1U || scene.m_meshes.size() == 1U ) { 79 | const BSDF *bsdf = scene.m_meshes[0]->m_bsdf; 80 | result = bsdf->evalD(its, its.wi, active); 81 | } else { 82 | BSDFArray bsdf_array = its.shape->bsdf(); 83 | 84 | result = bsdf_array->evalD(its, its.wi, active); 85 | } 86 | } else { 87 | active &= its.is_valid(); 88 | if ( scene.m_bsdfs.size() == 1U || scene.m_meshes.size() == 1U ) { 89 | const BSDF *bsdf = scene.m_meshes[0]->m_bsdf; 90 | result = bsdf->evalC(its, its.wi, active); 91 | } else { 92 | BSDFArray bsdf_array = its.shape->bsdf(); 93 | 94 | result = bsdf_array->evalC(its, its.wi, active); 95 | } 96 | } 97 | 98 | } else if ( m_field == "silhouette" ) { 99 | result = full>(1.f); 100 | } else if ( m_field == "position" ) { 101 | result = its.p; 102 | } else if ( m_field == "depth" ) { 103 | result = its.t; 104 | } else if ( m_field == "geoNormal" ) { 105 | result = its.n; 106 | } else if ( m_field == "shNormal" ) { 107 | result = its.sh_frame.n; 108 | } else if ( m_field == "uv" ) { 109 | result = Vector3f(its.uv[0], its.uv[1], 0.f); 110 | } else { 111 | PSDR_ASSERT(0); 112 | } 113 | 114 | return result & (active && (its.is_valid()) && valid_obj); 115 | 116 | } 117 | 118 | 119 | 120 | 121 | 122 | NAMESPACE_END(psdr_jit) 123 | 124 | -------------------------------------------------------------------------------- /src/integrator/integrator.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | NAMESPACE_BEGIN(psdr_jit) 11 | 12 | SpectrumC Integrator::renderC(const Scene &scene, int sensor_id, int seed, IntC pix_id) const { 13 | using namespace std::chrono; 14 | auto start_time = high_resolution_clock::now(); 15 | 16 | const RenderOption &opts = scene.m_opts; 17 | int64_t sample_count = static_cast(opts.height)*opts.width*opts.spp; 18 | 19 | if (pix_id != -1 && seed == -1) { 20 | PSDR_ASSERT_MSG(false, "While using batch rendering, seed must be set!"); 21 | } 22 | 23 | if (seed != -1) { 24 | auto seeds = arange(sample_count) + seed; 25 | if (pix_id == -1) { 26 | scene.m_samplers[0].seed(seeds); 27 | } else { 28 | scene.m_samplers[0].seed(gather(seeds, repeat(pix_id, opts.spp))); 29 | } 30 | } 31 | 32 | 33 | const int num_pixels = opts.width*opts.height; 34 | SpectrumC result; 35 | if (pix_id == -1) { 36 | result = __render(scene, sensor_id); 37 | } else { 38 | result = __render_batch(scene, sensor_id, pix_id); 39 | } 40 | auto end_time = high_resolution_clock::now(); 41 | if ( opts.log_level ) { 42 | std::stringstream oss; 43 | oss << "Rendered in " << duration_cast>(end_time - start_time).count() << " seconds."; 44 | log(oss.str().c_str()); 45 | } 46 | drjit::eval(result); 47 | return result; 48 | } 49 | 50 | 51 | SpectrumD Integrator::renderD(const Scene &scene, int sensor_id, int seed, IntD pix_id) const { 52 | if (pix_id != -1 && seed == -1) { 53 | PSDR_ASSERT_MSG(false, "While using batch rendering, seed must be set!"); 54 | } 55 | using namespace std::chrono; 56 | auto start_time = high_resolution_clock::now(); 57 | const RenderOption &opts = scene.m_opts; 58 | 59 | int64_t num_pixels = static_cast(opts.height)*opts.width; 60 | if (opts.spp > 0 && seed != -1) { 61 | auto seeds = arange(num_pixels * opts.spp) + seed; 62 | if (pix_id == -1) { 63 | scene.m_samplers[0].seed(seeds); 64 | } else { 65 | scene.m_samplers[0].seed(gather(seeds, repeat(pix_id, opts.spp))); 66 | } 67 | } 68 | if (opts.sppe > 0 && seed != -1) { 69 | scene.m_samplers[1].seed(arange(num_pixels * opts.sppe) + seed); 70 | } 71 | if (opts.sppse > 0 && seed != -1) { 72 | scene.m_samplers[2].seed(arange(num_pixels * opts.sppse) + seed); 73 | } 74 | 75 | // Interior integral 76 | SpectrumD result; 77 | if (pix_id == -1) { 78 | result = __render(scene, sensor_id); 79 | } else { 80 | result = __render_batch(scene, sensor_id, pix_id); 81 | } 82 | 83 | // Boundary integral 84 | if ( likely(scene.m_opts.sppe > 0) ) { 85 | render_primary_edges(scene, sensor_id, result); 86 | } 87 | 88 | if ( likely(scene.m_opts.sppse > 0) ) { 89 | render_secondary_edges(scene, sensor_id, result); 90 | } 91 | 92 | auto end_time = high_resolution_clock::now(); 93 | if ( scene.m_opts.log_level ) { 94 | std::stringstream oss; 95 | oss << "Rendered in " << duration_cast>(end_time - start_time).count() << " seconds."; 96 | log(oss.str().c_str()); 97 | } 98 | drjit::eval(result); 99 | return result; 100 | } 101 | 102 | 103 | template 104 | Spectrum Integrator::__render(const Scene &scene, int sensor_id) const { 105 | PSDR_ASSERT_MSG(scene.is_ready(), "Input scene must be configured!"); 106 | PSDR_ASSERT_MSG(sensor_id >= 0 && sensor_id < scene.m_num_sensors, "Invalid sensor id!"); 107 | const RenderOption &opts = scene.m_opts; 108 | const int num_pixels = opts.width*opts.height; 109 | 110 | Spectrum result = zeros>(num_pixels); 111 | if ( likely(opts.spp > 0) ) { 112 | int64_t num_samples = static_cast(num_pixels)*opts.spp; 113 | PSDR_ASSERT(num_samples <= std::numeric_limits::max()); 114 | 115 | Int idx = arange>(num_samples); 116 | if ( likely(opts.spp > 1) ) idx /= opts.spp; 117 | 118 | auto [dx, dy] = meshgrid(arange>(opts.width),arange>(opts.height)); 119 | 120 | Vector2f samples_base = gather>(Vector2f(dx, dy), idx); 121 | 122 | Vector2f samples = (samples_base + scene.m_samplers[0].next_2d()) 123 | /ScalarVector2f(opts.width, opts.height); 124 | Ray camera_ray = scene.m_sensors[sensor_id]->sample_primary_ray(samples); 125 | Spectrum value = Li(scene, scene.m_samplers[0], camera_ray); 126 | masked(value, ~drjit::isfinite>(value) || drjit::isnan>(value)) = 0.f; 127 | for (int j=0; j<3; ++j) { 128 | scatter_reduce(ReduceOp::Add, result[j], value[j], idx); 129 | } 130 | if ( likely(opts.spp > 1) ) { 131 | result /= static_cast(opts.spp); 132 | } 133 | } 134 | 135 | return result; 136 | } 137 | 138 | 139 | template 140 | Spectrum Integrator::__render_batch(const Scene &scene, int sensor_id, Int pix_id) const { 141 | // std::cout << "using batch renderer" << std::endl; 142 | PSDR_ASSERT_MSG(scene.is_ready(), "Input scene must be configured!"); 143 | PSDR_ASSERT_MSG(sensor_id >= 0 && sensor_id < scene.m_num_sensors, "Invalid sensor id!"); 144 | const RenderOption &opts = scene.m_opts; 145 | const int num_pixels = pix_id.size(); 146 | 147 | 148 | Spectrum result = zeros>(num_pixels); 149 | if ( likely(opts.spp > 0) ) { 150 | int64_t num_samples = static_cast(num_pixels)*opts.spp; 151 | PSDR_ASSERT(num_samples <= std::numeric_limits::max()); 152 | 153 | Int repeat_idx = arange>(num_samples) / opts.spp; 154 | Vector2f sampled_pixels(pix_id % opts.width, pix_id / opts.width); 155 | 156 | Vector2f samples_base = gather>(sampled_pixels, repeat_idx); 157 | // std::cout << samples_base << std::endl; 158 | // std::cout << scene.m_samplers[0].next_2d() << std::endl; 159 | Vector2f samples = (samples_base + scene.m_samplers[0].next_2d()) 160 | /ScalarVector2f(opts.width, opts.height); 161 | 162 | Ray camera_ray = scene.m_sensors[sensor_id]->sample_primary_ray(samples); 163 | 164 | 165 | Spectrum value = Li(scene, scene.m_samplers[0], camera_ray); 166 | masked(value, ~drjit::isfinite>(value) || drjit::isnan>(value)) = 0.f; 167 | for (int j=0; j<3; ++j) { 168 | scatter_reduce(ReduceOp::Add, result[j], value[j], repeat_idx); 169 | } 170 | if ( likely(opts.spp > 1) ) { 171 | result /= static_cast(opts.spp); 172 | } 173 | } 174 | 175 | return result; 176 | } 177 | 178 | 179 | void Integrator::render_primary_edges(const Scene &scene, int sensor_id, SpectrumD &result) const { 180 | const RenderOption &opts = scene.m_opts; 181 | const Sensor *sensor = scene.m_sensors[sensor_id]; 182 | if ( sensor->m_enable_edges ) { 183 | PrimaryEdgeSample edge_samples = sensor->sample_primary_edge(scene.m_samplers[1].next_1d()); 184 | MaskC valid = (edge_samples.idx >= 0); 185 | SpectrumC delta_L = Li(scene, scene.m_samplers[1], edge_samples.ray_n, valid) - 186 | Li(scene, scene.m_samplers[1], edge_samples.ray_p, valid); 187 | SpectrumD value = edge_samples.x_dot_n*SpectrumD(delta_L/edge_samples.pdf); 188 | masked(value, ~drjit::isfinite(value)) = 0.f; 189 | if ( likely(opts.sppe > 1) ) { 190 | value /= static_cast(opts.sppe); 191 | } 192 | value -= detach(value); 193 | 194 | for (int j=0; j<3; ++j) { 195 | scatter_reduce(ReduceOp::Add, result[j], value[j], IntD(edge_samples.idx), valid); 196 | } 197 | } 198 | } 199 | 200 | NAMESPACE_END(psdr_jit) 201 | -------------------------------------------------------------------------------- /src/optix/ptx.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #ifdef _WIN32 5 | # include 6 | #else 7 | # include 8 | #endif 9 | #include 10 | #include 11 | 12 | #include 13 | 14 | namespace psdr_jit { 15 | 16 | static bool fileExists( const char* path ) 17 | { 18 | std::ifstream str( path ); 19 | return static_cast( str ); 20 | } 21 | 22 | static bool fileExists( const std::string& path ) 23 | { 24 | return fileExists( path.c_str() ); 25 | } 26 | 27 | static bool readSourceFile( std::string& str, const std::string& filename ) 28 | { 29 | // Try to open file 30 | std::ifstream file( filename.c_str() ); 31 | if( file.good() ) 32 | { 33 | // Found usable source file 34 | std::stringstream source_buffer; 35 | source_buffer << file.rdbuf(); 36 | str = source_buffer.str(); 37 | return true; 38 | } 39 | return false; 40 | } 41 | 42 | static std::string samplePTXFilePath( const char* sampleName, const char* fileName ) 43 | { 44 | // Allow for overrides. 45 | static const char* directories[] = 46 | { 47 | PTX_OUTPUT_DIR 48 | }; 49 | for( const char* directory : directories ) 50 | { 51 | if( directory ) 52 | { 53 | std::string path = directory; 54 | path += '/'; 55 | path += "ptx"; 56 | path += "_generated_"; 57 | path += fileName; 58 | path += ".ptx"; 59 | if( fileExists( path ) ) 60 | return path; 61 | } 62 | } 63 | 64 | std::string error = "samplePTXFilePath couldn't locate "; 65 | error += fileName; 66 | error += " for sample "; 67 | error += sampleName; 68 | throw Exception( error.c_str() ); 69 | } 70 | 71 | static void getPtxStringFromFile( std::string& ptx, const char* sample_name, const char* filename ) 72 | { 73 | const std::string sourceFilePath = samplePTXFilePath( sample_name, filename ); 74 | 75 | // Try to open source PTX file 76 | if( !readSourceFile( ptx, sourceFilePath ) ) 77 | { 78 | std::string err = "Couldn't open source file " + sourceFilePath; 79 | throw std::runtime_error( err.c_str() ); 80 | } 81 | } 82 | 83 | struct PtxSourceCache 84 | { 85 | std::map map; 86 | ~PtxSourceCache() 87 | { 88 | for( std::map::const_iterator it = map.begin(); it != map.end(); ++it ) 89 | delete it->second; 90 | } 91 | }; 92 | static PtxSourceCache g_ptxSourceCache; 93 | 94 | const char* getPtxString( const char* sample, const char* filename, const char** log ) 95 | { 96 | if( log ) 97 | *log = NULL; 98 | 99 | std::string * ptx, cu; 100 | std::string key = std::string( filename ) + ";" + ( sample ? sample : "" ); 101 | std::map::iterator elem = g_ptxSourceCache.map.find( key ); 102 | 103 | if( elem == g_ptxSourceCache.map.end() ) 104 | { 105 | ptx = new std::string(); 106 | getPtxStringFromFile( *ptx, sample, filename ); 107 | g_ptxSourceCache.map[key] = ptx; 108 | } 109 | else 110 | { 111 | ptx = elem->second; 112 | } 113 | 114 | return ptx->c_str(); 115 | } 116 | 117 | } // namespace sutil 118 | -------------------------------------------------------------------------------- /src/optix_stubs.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #define OPTIX_STUBS_IMPL 3 | #include 4 | 5 | void init_optix_api() { 6 | jit_optix_context(); // Ensure OptiX is initialized 7 | 8 | #define L(name) name = (decltype(name)) jit_optix_lookup(#name); 9 | 10 | L(optixAccelComputeMemoryUsage); 11 | L(optixAccelBuild); 12 | L(optixAccelCompact); 13 | L(optixModuleCreateFromPTX); 14 | L(optixModuleDestroy) 15 | L(optixProgramGroupCreate); 16 | L(optixProgramGroupDestroy) 17 | L(optixSbtRecordPackHeader); 18 | 19 | #undef L 20 | } 21 | -------------------------------------------------------------------------------- /src/psdr_jit/__init__.py: -------------------------------------------------------------------------------- 1 | from .psdr_jit import * 2 | -------------------------------------------------------------------------------- /src/sensor/sensor.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | NAMESPACE_BEGIN(psdr_jit) 6 | 7 | void Sensor::configure(bool cache=true) { 8 | if (cache) drjit::make_opaque(m_to_world_left, m_to_world_raw, m_to_world_right); 9 | 10 | m_aspect = static_cast(m_resolution.x())/m_resolution.y(); 11 | Matrix4fD m_to_world = m_to_world_left * m_to_world_raw * m_to_world_right; 12 | PSDR_ASSERT_MSG(std::abs(det(Matrix3fD(m_to_world))[0] - 1.f) < Epsilon, 13 | "Sensor transformation should not involve scaling!"); 14 | } 15 | 16 | NAMESPACE_END(psdr_jit) 17 | -------------------------------------------------------------------------------- /tutorials/data/cbox/cbox_back.obj: -------------------------------------------------------------------------------- 1 | # Blender v3.4.1 OBJ File: '' 2 | # www.blender.org 3 | o cbox_back 4 | v 549.599976 -0.000091 559.200012 5 | v 0.000000 -0.000091 559.200012 6 | v 0.000000 548.799927 559.200073 7 | v 556.000000 548.799927 559.200073 8 | vt 0.987061 0.011536 9 | vt 0.987061 1.000000 10 | vt 0.000000 1.000000 11 | vt 0.000000 0.000000 12 | s off 13 | f 1/1 2/2 3/3 4/4 14 | -------------------------------------------------------------------------------- /tutorials/data/cbox/cbox_ceiling.obj: -------------------------------------------------------------------------------- 1 | v 556 548.79999 0 2 | v 556 548.79999 559.20001 3 | v 0 548.79999 559.20001 4 | v 0 548.79999 0 5 | 6 | f 1 2 3 4 7 | -------------------------------------------------------------------------------- /tutorials/data/cbox/cbox_floor.obj: -------------------------------------------------------------------------------- 1 | v 552.79999 0 0 2 | v 0 0 0 3 | v 0 0 559.20001 4 | v 549.59998 0 559.20001 5 | v 130 0 65 6 | v 82 0 225 7 | v 240 0 272 8 | v 290 0 114 9 | v 423 0 247 10 | v 265 0 296 11 | v 314 0 456 12 | v 472 0 406 13 | f 1 2 3 4 14 | -------------------------------------------------------------------------------- /tutorials/data/cbox/cbox_greenwall.obj: -------------------------------------------------------------------------------- 1 | v 0 0 559.20001 2 | v 0 0 0 3 | v 0 548.79999 0 4 | v 0 548.79999 559.20001 5 | f 1 2 3 4 6 | -------------------------------------------------------------------------------- /tutorials/data/cbox/cbox_largebox.obj: -------------------------------------------------------------------------------- 1 | # Blender v3.4.1 OBJ File: '' 2 | # www.blender.org 3 | o cbox_largebox 4 | v 423.000000 329.999969 247.000061 5 | v 265.000000 329.999939 296.000061 6 | v 314.000000 329.999939 456.000061 7 | v 472.000000 329.999939 406.000061 8 | v 423.000000 -0.000040 247.000000 9 | v 472.000000 -0.000066 406.000000 10 | v 314.000000 -0.000074 456.000000 11 | v 265.000000 -0.000048 296.000000 12 | vt 0.994838 0.753967 13 | vt 0.663615 0.753540 14 | vt 0.663030 0.500000 15 | vt 0.994838 0.501892 16 | vt 0.335054 0.500000 17 | vt 0.335054 0.000000 18 | vt 0.668172 0.000000 19 | vt 0.668172 0.500000 20 | vt 1.000000 0.000000 21 | vt 1.000000 0.500000 22 | vt 0.335054 0.000000 23 | vt 0.335054 0.500000 24 | vt 0.000000 0.500000 25 | vt 0.000000 0.000000 26 | vt 0.000000 1.000000 27 | vt 0.331223 0.500000 28 | vt 0.331223 1.000000 29 | vt 0.663030 0.752075 30 | vt 0.331223 0.753998 31 | vt 0.331808 0.500458 32 | vt 0.663030 0.500000 33 | s off 34 | f 1/1 2/2 3/3 35 | f 1/1 3/3 4/4 36 | f 5/5 1/6 4/7 37 | f 5/5 4/7 6/8 38 | f 6/8 4/7 3/9 39 | f 6/8 3/9 7/10 40 | f 7/11 3/12 2/13 41 | f 7/11 2/13 8/14 42 | f 8/15 2/13 1/16 43 | f 8/15 1/16 5/17 44 | f 6/18 7/19 8/20 45 | f 6/18 8/20 5/21 46 | -------------------------------------------------------------------------------- /tutorials/data/cbox/cbox_luminaire.obj: -------------------------------------------------------------------------------- 1 | v 343 540.79999 227 2 | v 343 540.79999 332 3 | v 213 540.79999 332 4 | v 213 540.79999 227 5 | f 1 2 3 4 -------------------------------------------------------------------------------- /tutorials/data/cbox/cbox_redwall.obj: -------------------------------------------------------------------------------- 1 | v 552.79999 0 0 2 | v 549.59998 0 559.20001 3 | v 556 548.79999 559.20001 4 | v 556 548.79999 0 5 | f 1 2 3 4 6 | -------------------------------------------------------------------------------- /tutorials/data/cbox/cbox_smallbox.obj: -------------------------------------------------------------------------------- 1 | #### 2 | # 3 | # OBJ File Generated by Meshlab 4 | # 5 | #### 6 | # Object cbox_smallbox.obj 7 | # 8 | # Vertices: 8 9 | # Faces: 12 10 | # 11 | #### 12 | v 130.000000 165.000000 65.000000 13 | v 82.000000 165.000000 225.000000 14 | v 240.000000 165.000000 272.000000 15 | v 290.000000 165.000000 114.000000 16 | v 290.000000 0.000000 114.000000 17 | v 240.000000 0.000000 272.000000 18 | v 130.000000 0.000000 65.000000 19 | v 82.000000 0.000000 225.000000 20 | # 8 vertices, 0 vertices normals 21 | 22 | f 1 2 3 23 | f 1 3 4 24 | f 5 4 3 25 | f 5 3 6 26 | f 7 1 4 27 | f 7 4 5 28 | f 8 2 1 29 | f 8 1 7 30 | f 6 3 2 31 | f 6 2 8 32 | f 5 6 8 33 | f 5 8 7 34 | # 12 faces, 0 coords texture 35 | 36 | # End of File 37 | -------------------------------------------------------------------------------- /tutorials/data/cube.obj: -------------------------------------------------------------------------------- 1 | mtllib cube.mtl 2 | o Cube 3 | v 1.000000 -1.000000 -1.000000 4 | v 1.000000 -1.000000 1.000000 5 | v -1.000000 -1.000000 1.000000 6 | v -1.000000 -1.000000 -1.000000 7 | v 1.000000 1.000000 -0.999999 8 | v 0.999999 1.000000 1.000001 9 | v -1.000000 1.000000 1.000000 10 | v -1.000000 1.000000 -1.000000 11 | vt 1.000000 0.333333 12 | vt 1.000000 0.666667 13 | vt 0.666667 0.666667 14 | vt 0.666667 0.333333 15 | vt 0.666667 0.000000 16 | vt 0.000000 0.333333 17 | vt 0.000000 0.000000 18 | vt 0.333333 0.000000 19 | vt 0.333333 1.000000 20 | vt 0.000000 1.000000 21 | vt 0.000000 0.666667 22 | vt 0.333333 0.333333 23 | vt 0.333333 0.666667 24 | vt 1.000000 0.000000 25 | vn 0.000000 -1.000000 0.000000 26 | vn 0.000000 1.000000 0.000000 27 | vn 1.000000 0.000000 0.000000 28 | vn -0.000000 0.000000 1.000000 29 | vn -1.000000 -0.000000 -0.000000 30 | vn 0.000000 0.000000 -1.000000 31 | usemtl Material 32 | s off 33 | f 2/1/1 3/2/1 4/3/1 34 | f 8/1/2 7/4/2 6/5/2 35 | f 5/6/3 6/7/3 2/8/3 36 | f 6/8/4 7/5/4 3/4/4 37 | f 3/9/5 7/10/5 8/11/5 38 | f 1/12/6 4/13/6 8/11/6 39 | f 1/4/1 2/1/1 4/3/1 40 | f 5/14/2 8/1/2 6/5/2 41 | f 1/12/3 5/6/3 2/8/3 42 | f 2/12/4 6/8/4 3/4/4 43 | f 4/13/5 3/9/5 8/11/5 44 | f 5/6/6 1/12/6 8/11/6 45 | -------------------------------------------------------------------------------- /tutorials/data/envmap/ballroom_1k.exr: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/andyyankai/psdr-jit/476ba6ef39e24474e85953ba3743bb0b967c797e/tutorials/data/envmap/ballroom_1k.exr -------------------------------------------------------------------------------- /tutorials/data/texture/illya.exr: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/andyyankai/psdr-jit/476ba6ef39e24474e85953ba3743bb0b967c797e/tutorials/data/texture/illya.exr -------------------------------------------------------------------------------- /tutorials/data/texture/wood.exr: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/andyyankai/psdr-jit/476ba6ef39e24474e85953ba3743bb0b967c797e/tutorials/data/texture/wood.exr -------------------------------------------------------------------------------- /tutorials/data/uv_plane.obj: -------------------------------------------------------------------------------- 1 | v 1.000000 0.000000 -1.000000 2 | v 1.000000 0.000000 1.000000 3 | v -1.000000 0.000000 1.000000 4 | v -1.000000 0.000000 -1.000000 5 | 6 | vt 0.0 0.0 7 | vt 0.0 1.0 8 | vt 1.0 1.0 9 | vt 1.0 0.0 10 | 11 | f 4/4 3/3 2/2 12 | f 4/4 2/2 1/1 13 | -------------------------------------------------------------------------------- /tutorials/image_util.py: -------------------------------------------------------------------------------- 1 | import torch 2 | 3 | from pathlib import Path 4 | import numpy as np 5 | from PIL import Image as im 6 | import imageio.v3 as iio 7 | import torch 8 | 9 | def write_exr(exr_path, image): 10 | image = to_numpy(image).astype(np.float32) 11 | if len(image.shape) == 3: 12 | image = np.expand_dims(image, axis=0) 13 | try: 14 | iio.imwrite(exr_path, image) 15 | except OSError: 16 | imageio.plugins.freeimage.download() 17 | iio.imwrite(exr_path, image, extension='.exr') 18 | 19 | 20 | def to_numpy(data): 21 | if torch.is_tensor(data): 22 | return data.detach().cpu().numpy() 23 | else: 24 | return np.array(data) 25 | 26 | def linear_to_srgb(l): 27 | s = np.zeros_like(l) 28 | m = l <= 0.00313066844250063 29 | s[m] = l[m] * 12.92 30 | s[~m] = 1.055*(l[~m]**(1.0/2.4))-0.055 31 | return s 32 | 33 | 34 | def srgb_to_linear(s): 35 | l = np.zeros_like(s) 36 | m = s <= 0.0404482362771082 37 | l[m] = s[m] / 12.92 38 | l[~m] = ((s[~m]+0.055)/1.055) ** 2.4 39 | return l 40 | 41 | def to_srgb(image): 42 | image = to_numpy(image) 43 | if image.shape[2] == 4: 44 | image_alpha = image[:, :, 3:4] 45 | image = linear_to_srgb(image[:, :, 0:3]) 46 | image = np.concatenate([image, image_alpha], axis=2) 47 | else: 48 | image = linear_to_srgb(image) 49 | return np.clip(image, 0, 1) 50 | 51 | def write_jpg(jpg_path, image): 52 | image = to_srgb(to_numpy(image)) 53 | image = (image * 255).astype(np.uint8) 54 | if image.shape[2] == 1: 55 | image = np.repeat(image, 3, axis=2) 56 | rgb_im = im.fromarray(image).convert('RGB') 57 | rgb_im.save(jpg_path, format='JPEG', quality=95) 58 | 59 | def read_image(image_path, is_srgb=None, remove_alpha=True): 60 | image_path = Path(image_path) 61 | image = iio.imread(image_path) 62 | image = np.atleast_3d(image) 63 | if remove_alpha and image.shape[2] == 4: 64 | image = image[:, :, 0:3] 65 | 66 | if image.dtype == np.uint8 or image.dtype == np.int16: 67 | image = image.astype("float32") / 255.0 68 | elif image.dtype == np.uint16 or image.dtype == np.int32: 69 | image = image.astype("float32") / 65535.0 70 | 71 | if is_srgb is None: 72 | if image_path.suffix in ['.exr', '.hdr', '.rgbe']: 73 | is_srgb = False 74 | else: 75 | is_srgb = True 76 | 77 | if is_srgb: 78 | image = to_linear(image) 79 | 80 | return image 81 | 82 | def write_image(image_path, image, is_srgb=None): 83 | image_path = Path(image_path) 84 | image = to_numpy(image) 85 | image = np.atleast_3d(image) 86 | if image.shape[2] == 1: 87 | image = np.repeat(image, 3, axis=2) 88 | 89 | if is_srgb is None: 90 | if image_path.suffix in ['.exr', '.hdr', '.rgbe']: 91 | is_srgb = False 92 | else: 93 | is_srgb = True 94 | 95 | if is_srgb: 96 | image = to_srgb(image) 97 | 98 | if image_path.suffix == '.exr': 99 | image = image.astype(np.float32) 100 | else: 101 | image = (image * 255).astype(np.uint8) 102 | 103 | iio.imwrite(image_path, image) 104 | --------------------------------------------------------------------------------