├── 3rdparty ├── dummy.cpp ├── stb │ ├── CMakeLists.txt │ └── stb.cpp ├── exr │ └── CMakeLists.txt ├── imgui │ ├── CMakeLists.txt │ └── imgui_impl_dx12.h └── CMakeLists.txt ├── framework ├── cuda │ ├── cuda_util.cpp │ ├── matrix.h │ ├── shape.h │ ├── shape.cpp │ ├── stream.cpp │ ├── context.h │ ├── preprocessor.h │ ├── stream.h │ ├── random.h │ ├── kernel.h │ ├── context.cpp │ ├── texture.h │ └── data_view.h ├── util │ ├── component.h │ ├── enum.h │ ├── util.cpp │ ├── timer.h │ ├── transform.h │ ├── util.h │ ├── event.h │ ├── texture.h │ ├── log.h │ ├── thread_pool.h │ ├── camera.h │ ├── aabb.h │ ├── thread_pool.cpp │ ├── camera.cpp │ └── type.h ├── render │ ├── camera.h │ ├── emitter │ │ ├── types.h │ │ ├── sphere.h │ │ ├── area.h │ │ └── env.h │ └── material │ │ ├── predefine.h │ │ ├── bsdf │ │ ├── bsdf.cu │ │ ├── diffuse.h │ │ ├── conductor.h │ │ ├── bsdf.h │ │ ├── dielectric.h │ │ ├── rough_conductor.h │ │ ├── plastic.h │ │ ├── rough_plastic.h │ │ └── rough_dielectric.h │ │ └── fresnel.h ├── static.h.in ├── system │ ├── wsa.h │ ├── pass.cpp │ ├── gui │ │ ├── buffer_to_canvas.cuh │ │ ├── buffer_to_canvas.cu │ │ ├── output.hlsl │ │ └── gui.h │ ├── system.h │ ├── denoise_pass.h │ ├── type.h │ ├── pass.h │ ├── buffer.h │ ├── wsa.cpp │ ├── denoise_pass.cpp │ └── buffer.cpp ├── optix │ ├── context.h │ ├── module.h │ ├── check.h │ ├── context.cpp │ ├── denoiser.h │ ├── pipeline.h │ ├── pass.h │ └── sbt.h ├── resource │ ├── xml │ │ ├── parser.h │ │ ├── tag.h │ │ ├── parser.cpp │ │ ├── object.h │ │ ├── util_loader.h │ │ └── object.cpp │ ├── emitter.h │ ├── texture.h │ ├── scene.h │ ├── hair │ │ ├── cemyuksel_hair.h │ │ └── cemyuksel_hair.cpp │ ├── texture.cpp │ └── material.h ├── world │ ├── render_object.h │ ├── gas_manager.h │ ├── emitter.h │ ├── ias_manager.h │ ├── camera.h │ ├── world.h │ ├── render_object.cpp │ └── camera.cpp ├── decl │ └── material_decl.inl ├── CMakeLists.txt └── dx12 │ └── context.h ├── image └── PupilOptixLab.jpg ├── .gitignore ├── example ├── CMakeLists.txt ├── cuda_test │ ├── kernel.h │ ├── pass.h │ ├── CMakeLists.txt │ ├── main.cpp │ ├── kernel.cu │ └── pass.cpp ├── empty_gui │ ├── CMakeLists.txt │ └── main.cpp └── path_tracer │ ├── CMakeLists.txt │ ├── main.cpp │ ├── type.h │ └── pt_pass.h ├── .gitmodules ├── README.md ├── data └── static │ ├── default.xml │ ├── denoised_scene.xml │ ├── restir_test.xml │ └── cornellbox.xml ├── CMakeLists.txt ├── .clang-format └── cmake └── FindOptiX.cmake /3rdparty/dummy.cpp: -------------------------------------------------------------------------------- 1 | // dummy -------------------------------------------------------------------------------- /framework/cuda/cuda_util.cpp: -------------------------------------------------------------------------------- 1 | #include "util.h" -------------------------------------------------------------------------------- /image/PupilOptixLab.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mchenwang/PupilOptixLab/HEAD/image/PupilOptixLab.jpg -------------------------------------------------------------------------------- /framework/util/component.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | namespace Pupil { 4 | class Component { 5 | }; 6 | }// namespace Pupil -------------------------------------------------------------------------------- /3rdparty/stb/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | add_library(stb SHARED stb.cpp) 2 | set_target_properties(stb PROPERTIES WINDOWS_EXPORT_ALL_SYMBOLS ON) -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | build/ 2 | test/ 3 | target/ 4 | .vscode/ 5 | *.ptx 6 | data/* 7 | !data/static/* 8 | imgui.ini 9 | framework/static.h 10 | -------------------------------------------------------------------------------- /example/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | set(pupil_application_example example) 2 | 3 | if(${pupil_build_example}) 4 | add_subdirectory(empty_gui) 5 | add_subdirectory(path_tracer) 6 | add_subdirectory(cuda_test) 7 | endif() 8 | -------------------------------------------------------------------------------- /example/cuda_test/kernel.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "cuda/preprocessor.h" 4 | #include "cuda/stream.h" 5 | #include "cuda/data_view.h" 6 | 7 | void SetColor(uint2, unsigned int, Pupil::cuda::RWArrayView &, Pupil::cuda::Stream *); -------------------------------------------------------------------------------- /3rdparty/stb/stb.cpp: -------------------------------------------------------------------------------- 1 | #define STB_IMAGE_IMPLEMENTATION 2 | #include 3 | 4 | #define STB_IMAGE_WRITE_IMPLEMENTATION 5 | #include 6 | 7 | #define STB_IMAGE_RESIZE_IMPLEMENTATION 8 | #include -------------------------------------------------------------------------------- /framework/render/camera.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "cuda/matrix.h" 4 | #include "util/camera.h" 5 | 6 | namespace Pupil::optix { 7 | struct Camera { 8 | mat4x4 sample_to_camera; 9 | mat4x4 camera_to_world; 10 | }; 11 | }// namespace Pupil::optix -------------------------------------------------------------------------------- /example/empty_gui/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | set(target_name empty_gui) 2 | 3 | add_executable(${target_name} main.cpp) 4 | 5 | target_link_libraries(${target_name} PUBLIC ${pupil_framework_name}) 6 | set_target_properties(${target_name} PROPERTIES FOLDER ${pupil_application_example}) 7 | -------------------------------------------------------------------------------- /framework/static.h.in: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | namespace Pupil { 6 | 7 | constexpr std::string_view ROOT_DIR = "${PROJECT_SOURCE_DIR}"; 8 | constexpr std::string_view DATA_DIR = "${PROJECT_SOURCE_DIR}/data"; 9 | 10 | }// namespace Pupil 11 | -------------------------------------------------------------------------------- /framework/cuda/matrix.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "vec_math.h" 4 | 5 | struct mat4x4 { 6 | float4 r0, r1, r2, r3; 7 | }; 8 | 9 | CUDA_INLINE CUDA_HOSTDEVICE float4 operator*(const mat4x4 &m, const float4 &v) noexcept { 10 | return make_float4(dot(m.r0, v), dot(m.r1, v), dot(m.r2, v), dot(m.r3, v)); 11 | } 12 | -------------------------------------------------------------------------------- /example/empty_gui/main.cpp: -------------------------------------------------------------------------------- 1 | #include "system/system.h" 2 | #include "dx12/context.h" 3 | 4 | int main() { 5 | auto system = Pupil::util::Singleton::instance(); 6 | system->Init(true); 7 | system->Run(); 8 | system->Destroy(); 9 | atexit(&Pupil::DirectX::Context::ReportLiveObjects); 10 | 11 | return 0; 12 | } -------------------------------------------------------------------------------- /3rdparty/exr/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | set(TINYEXR_BUILD_SAMPLE OFF CACHE BOOL " " FORCE) 2 | 3 | set(TINYEXR_USE_MINIZ ON CACHE BOOL " " FORCE) 4 | add_subdirectory(tinyexr) 5 | 6 | # add_library(tinyexr SHARED tinyexr/tinyexr.h tinyexr/tinyexr.cc) 7 | target_include_directories(tinyexr INTERFACE tinyexr/) 8 | set_target_properties(tinyexr PROPERTIES WINDOWS_EXPORT_ALL_SYMBOLS ON) -------------------------------------------------------------------------------- /framework/system/wsa.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | class WindowsSecurityAttributes { 6 | protected: 7 | SECURITY_ATTRIBUTES m_win_security_attributes; 8 | PSECURITY_DESCRIPTOR m_win_p_security_descriptor; 9 | 10 | public: 11 | WindowsSecurityAttributes(); 12 | ~WindowsSecurityAttributes(); 13 | SECURITY_ATTRIBUTES *operator&(); 14 | }; -------------------------------------------------------------------------------- /example/cuda_test/pass.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "system/pass.h" 4 | #include "util/timer.h" 5 | 6 | #include "cuda/stream.h" 7 | 8 | class CudaPass : public Pupil::Pass { 9 | public: 10 | CudaPass(std::string_view name = "Cuda") noexcept; 11 | virtual void OnRun() noexcept override; 12 | 13 | private: 14 | std::unique_ptr m_stream = nullptr; 15 | }; -------------------------------------------------------------------------------- /example/path_tracer/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | set(target_name path_tracer) 2 | 3 | cuda_compile_and_embed(embedded_ptx_code main.cu) 4 | 5 | add_executable(${target_name} 6 | ${embedded_ptx_code} 7 | main.cpp 8 | pt_pass.h 9 | pt_pass.cpp 10 | type.h 11 | ) 12 | 13 | target_link_libraries(${target_name} PUBLIC ${pupil_framework_name}) 14 | set_target_properties(${target_name} PROPERTIES FOLDER ${pupil_application_example}) 15 | -------------------------------------------------------------------------------- /example/cuda_test/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | set(target_name cuda_test) 2 | 3 | add_executable(${target_name} 4 | main.cpp 5 | pass.h 6 | pass.cpp 7 | kernel.h 8 | kernel.cu 9 | ) 10 | 11 | set_target_properties(${target_name} PROPERTIES CUDA_ARCHITECTURES ${pupil_cuda_architectures}) 12 | target_link_libraries(${target_name} PUBLIC ${pupil_framework_name}) 13 | 14 | set_target_properties(${target_name} PROPERTIES FOLDER ${pupil_application_example}) 15 | -------------------------------------------------------------------------------- /framework/system/pass.cpp: -------------------------------------------------------------------------------- 1 | #include "pass.h" 2 | 3 | #include "imgui.h" 4 | 5 | namespace Pupil { 6 | void Pass::Run() noexcept { 7 | if (!m_enable) return; 8 | 9 | m_timer.Start(); 10 | OnRun(); 11 | m_timer.Stop(); 12 | m_last_exec_time = m_timer.ElapsedMilliseconds(); 13 | } 14 | 15 | void Pass::Inspector() noexcept { 16 | ImGui::Checkbox("enalbe", &m_enable); 17 | ImGui::Text("time cost: %.3lf ms", m_last_exec_time); 18 | } 19 | }// namespace Pupil -------------------------------------------------------------------------------- /framework/system/gui/buffer_to_canvas.cuh: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "cuda/stream.h" 4 | #include "cuda/data_view.h" 5 | #include 6 | 7 | namespace Pupil { 8 | void CopyFloat1BufferToCanvas(CUdeviceptr dst, CUdeviceptr src, unsigned int size, cuda::Stream *stream) noexcept; 9 | void CopyFloat2BufferToCanvas(CUdeviceptr dst, CUdeviceptr src, unsigned int size, cuda::Stream *stream) noexcept; 10 | void CopyFloat3BufferToCanvas(CUdeviceptr dst, CUdeviceptr src, unsigned int size, cuda::Stream *stream) noexcept; 11 | } -------------------------------------------------------------------------------- /framework/cuda/shape.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "preprocessor.h" 4 | #include "cuda/vec_math.h" 5 | #include 6 | #include 7 | #include "util/util.h" 8 | 9 | namespace Pupil::cuda { 10 | class CudaShapeDataManager : public util::Singleton { 11 | private: 12 | std::unordered_map m_cuda_geometry_map; 13 | 14 | public: 15 | [[nodiscard]] CUdeviceptr GetCudaMemPtr(void *p_data, size_t size) noexcept; 16 | 17 | void Clear() noexcept; 18 | }; 19 | }// namespace Pupil::cuda -------------------------------------------------------------------------------- /example/cuda_test/main.cpp: -------------------------------------------------------------------------------- 1 | #include "system/system.h" 2 | #include "pass.h" 3 | #include "system/gui/gui.h" 4 | #include "util/event.h" 5 | 6 | using namespace Pupil; 7 | 8 | int main() { 9 | auto system = Pupil::util::Singleton::instance(); 10 | system->Init(true); 11 | { 12 | auto pass = std::make_unique(); 13 | system->AddPass(pass.get()); 14 | EventDispatcher(); 15 | system->Run(); 16 | } 17 | 18 | system->Destroy(); 19 | 20 | return 0; 21 | } -------------------------------------------------------------------------------- /framework/optix/context.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "util/util.h" 4 | #include 5 | 6 | namespace Pupil::optix { 7 | class Context : public Pupil::util::Singleton { 8 | public: 9 | OptixDeviceContext context = nullptr; 10 | 11 | operator OptixDeviceContext() const noexcept { return context; } 12 | 13 | void Init() noexcept; 14 | void Destroy() noexcept; 15 | 16 | [[nodiscard]] bool IsInitialized() noexcept { return m_init_flag; } 17 | 18 | private: 19 | bool m_init_flag = false; 20 | }; 21 | }// namespace Pupil::optix -------------------------------------------------------------------------------- /framework/cuda/shape.cpp: -------------------------------------------------------------------------------- 1 | #include "shape.h" 2 | #include "util.h" 3 | 4 | namespace Pupil::cuda { 5 | CUdeviceptr CudaShapeDataManager::GetCudaMemPtr(void *p_data, size_t size) noexcept { 6 | if (p_data == nullptr || size == 0) return 0; 7 | auto it = m_cuda_geometry_map.find(p_data); 8 | if (it == m_cuda_geometry_map.end()) { 9 | m_cuda_geometry_map[p_data] = Pupil::cuda::CudaMemcpyToDevice(p_data, size); 10 | } 11 | return m_cuda_geometry_map[p_data]; 12 | } 13 | 14 | void CudaShapeDataManager::Clear() noexcept { 15 | m_cuda_geometry_map.clear(); 16 | } 17 | }// namespace Pupil::cuda -------------------------------------------------------------------------------- /example/path_tracer/main.cpp: -------------------------------------------------------------------------------- 1 | #include "system/system.h" 2 | #include "pt_pass.h" 3 | #include "static.h" 4 | 5 | int main() { 6 | auto system = Pupil::util::Singleton::instance(); 7 | system->Init(true); 8 | 9 | { 10 | auto pt_pass = std::make_unique("Path Tracing"); 11 | system->AddPass(pt_pass.get()); 12 | std::filesystem::path scene_file_path{ Pupil::DATA_DIR }; 13 | scene_file_path /= "static/default.xml"; 14 | system->SetScene(scene_file_path); 15 | 16 | system->Run(); 17 | } 18 | 19 | system->Destroy(); 20 | 21 | return 0; 22 | } -------------------------------------------------------------------------------- /framework/cuda/stream.cpp: -------------------------------------------------------------------------------- 1 | #include "stream.h" 2 | 3 | #include "util.h" 4 | 5 | namespace Pupil::cuda { 6 | Stream::Stream() noexcept { 7 | // CUDA_CHECK(cudaStreamCreateWithFlags(&m_stream, cudaStreamNonBlocking)); 8 | CUDA_CHECK(cudaStreamCreate(&m_stream)); 9 | //CUDA_CHECK(cudaEventCreateWithFlags(&m_event, cudaEventDisableTiming)); 10 | } 11 | Stream::~Stream() noexcept { 12 | if (m_stream) { 13 | Synchronize(); 14 | CUDA_CHECK(cudaStreamDestroy(m_stream)); 15 | } 16 | m_stream = nullptr; 17 | //CUDA_CHECK(cudaEventDestroy(m_event)); 18 | } 19 | 20 | void Stream::Synchronize() noexcept { CUDA_CHECK(cudaStreamSynchronize(m_stream)); } 21 | }// namespace Pupil::cuda -------------------------------------------------------------------------------- /framework/render/emitter/types.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "cuda/vec_math.h" 4 | #include "cuda/texture.h" 5 | 6 | namespace Pupil::optix { 7 | enum class EEmitterType : unsigned int { 8 | None, 9 | TriArea, 10 | Sphere, 11 | ConstEnv, 12 | EnvMap, 13 | // Point, 14 | // DistDir 15 | }; 16 | 17 | struct EmitterSampleRecord { 18 | float3 radiance; 19 | float3 wi; 20 | float3 pos; 21 | float3 normal; 22 | 23 | float distance; 24 | float pdf = 0.f; 25 | bool is_delta; 26 | }; 27 | struct EmitEvalRecord { 28 | float3 radiance; 29 | float pdf; 30 | }; 31 | }// namespace Pupil::optix 32 | 33 | #include "area.h" 34 | #include "sphere.h" 35 | #include "env.h" -------------------------------------------------------------------------------- /framework/resource/xml/parser.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | namespace Pupil::resource { 8 | class Scene; 9 | 10 | namespace xml { 11 | struct Object; 12 | struct GlobalManager; 13 | 14 | class Parser { 15 | private: 16 | std::unique_ptr m_global_manager; 17 | 18 | public: 19 | Parser() noexcept; 20 | ~Parser() noexcept; 21 | 22 | static void DeregisterContext() noexcept; 23 | 24 | [[nodiscard]] Object *LoadFromFile(std::filesystem::path file_path) noexcept; 25 | [[nodiscard]] GlobalManager *GetXMLGlobalManager() const noexcept { return m_global_manager.get(); } 26 | }; 27 | }// namespace xml 28 | }// namespace Pupil::resource -------------------------------------------------------------------------------- /example/cuda_test/kernel.cu: -------------------------------------------------------------------------------- 1 | #include "cuda/kernel.h" 2 | #include "kernel.h" 3 | #include "cuda/vec_math.h" 4 | 5 | void SetColor(uint2 size, unsigned int m_frame_cnt, Pupil::cuda::RWArrayView &m_output, Pupil::cuda::Stream *stream) { 6 | Pupil::cuda::LaunchKernel2D( 7 | size, [=] __device__(uint2 pixel_id, uint2 size) { 8 | float3 color = 0.5f * make_float3( 9 | cos(((float)pixel_id.x) / size.x + m_frame_cnt / 100.f), 10 | sin(((float)pixel_id.y) / size.y + m_frame_cnt / 100.f), 11 | 0.7f) + 12 | 0.5f; 13 | m_output[pixel_id.x + pixel_id.y * size.x] = make_float4(color, 1.f); 14 | }, 15 | stream); 16 | } -------------------------------------------------------------------------------- /framework/util/enum.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "macro_map.h" 3 | 4 | #define PUPIL_ENUM_NAME(e) _##e 5 | #define PUPIL_ENUM_STR_NAME(e) #e 6 | 7 | #define PUPIL_MACRO_ARGS_NUM(...) \ 8 | std::tuple_size::value 9 | 10 | #define PUPIL_ENUM_DEFINE(ENUM_CLASS_NAME, ...) \ 11 | enum class ENUM_CLASS_NAME : unsigned int { \ 12 | _unknown = 0, \ 13 | MAP_LIST(PUPIL_ENUM_NAME, __VA_ARGS__), \ 14 | _count \ 15 | }; 16 | 17 | #define PUPIL_ENUM_STRING_ARRAY(ENUM_STRING_ARRAY_NAME, ...) \ 18 | const auto \ 19 | ENUM_STRING_ARRAY_NAME = std::array{ MAP_LIST(PUPIL_ENUM_STR_NAME, __VA_ARGS__) }; 20 | -------------------------------------------------------------------------------- /3rdparty/imgui/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | set(imgui_header 2 | imgui/imconfig.h 3 | imgui/imgui.h 4 | imgui/imgui_internal.h 5 | imgui_impl_dx12.h 6 | imgui/backends/imgui_impl_win32.h 7 | imgui-filebrowser/imfilebrowser.h 8 | ImGuizmo/ImGuizmo.h 9 | ) 10 | 11 | set(imgui_source 12 | imgui/imgui.cpp 13 | imgui/imgui_draw.cpp 14 | imgui/imgui_tables.cpp 15 | imgui/imgui_widgets.cpp 16 | imgui_impl_dx12.cpp 17 | imgui/backends/imgui_impl_win32.cpp 18 | ImGuizmo/ImGuizmo.cpp 19 | ) 20 | add_library(imgui SHARED ${imgui_header} ${imgui_source}) 21 | 22 | target_include_directories(imgui INTERFACE ./) 23 | target_include_directories(imgui INTERFACE imgui-filebrowser/) 24 | target_link_libraries(imgui PUBLIC d3d12 d3dcompiler dxgi dxguid) 25 | set_target_properties(imgui PROPERTIES WINDOWS_EXPORT_ALL_SYMBOLS ON) -------------------------------------------------------------------------------- /framework/cuda/context.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "util/util.h" 4 | #include "util.h" 5 | 6 | #include 7 | #include 8 | 9 | #include 10 | 11 | namespace Pupil::DirectX { 12 | class Context; 13 | }; 14 | 15 | namespace Pupil::cuda { 16 | class Stream; 17 | class Context : public Pupil::util::Singleton { 18 | public: 19 | CUcontext context = nullptr; 20 | uint32_t cuda_device_id = 0; 21 | uint32_t cuda_node_mask = 0; 22 | 23 | operator CUcontext() const noexcept { return context; } 24 | 25 | void Init() noexcept; 26 | void Destroy() noexcept; 27 | 28 | [[nodiscard]] bool IsInitialized() noexcept { return m_init_flag; } 29 | void Synchronize() noexcept { CUDA_CHECK(cudaDeviceSynchronize()); } 30 | 31 | private: 32 | bool m_init_flag = false; 33 | }; 34 | }// namespace Pupil::cuda -------------------------------------------------------------------------------- /framework/cuda/preprocessor.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | // clang-format off 4 | #if defined(__CUDACC__) || defined(__CUDABE__) 5 | # if !defined(PUPIL_OPTIX) && !defined(PUPIL_CUDA) 6 | # define PUPIL_CUDA 7 | # endif 8 | # if defined(PUPIL_CPP) 9 | # undef PUPIL_CPP 10 | # endif 11 | # define CUDA_HOST __host__ 12 | # define CUDA_DEVICE __device__ 13 | # define CUDA_HOSTDEVICE __host__ __device__ 14 | # define CUDA_GLOBAL __global__ 15 | # define CUDA_INLINE __forceinline__ 16 | # define CONST_STATIC_INIT(...) 17 | #else 18 | # if defined(PUPIL_OPTIX) 19 | # undef PUPIL_OPTIX 20 | # endif 21 | # if defined(PUPIL_CUDA) 22 | # undef PUPIL_CUDA 23 | # endif 24 | # define CUDA_HOST 25 | # define CUDA_DEVICE 26 | # define CUDA_HOSTDEVICE 27 | # define CUDA_GLOBAL 28 | # define CUDA_INLINE inline 29 | # define CONST_STATIC_INIT(...) = __VA_ARGS__ 30 | #endif 31 | // clang-format on 32 | -------------------------------------------------------------------------------- /framework/cuda/stream.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | namespace Pupil::cuda { 7 | // one task corresponds to one stream, so the event is not necessary 8 | class Stream { 9 | public: 10 | Stream() noexcept; 11 | ~Stream() noexcept; 12 | 13 | operator cudaStream_t() const noexcept { return m_stream; } 14 | [[nodiscard]] cudaStream_t GetStream() const noexcept { return m_stream; } 15 | //[[nodiscard]] cudaEvent_t GetEvent() const noexcept { return m_event; } 16 | //[[nodiscard]] bool IsAvailable() noexcept { return cudaSuccess == cudaEventQuery(m_event); } 17 | 18 | void Synchronize() noexcept; 19 | //void Synchronize() noexcept { CUDA_CHECK(cudaEventSynchronize(m_event)); } 20 | //void Signal() noexcept { CUDA_CHECK(cudaEventRecord(m_event, m_stream)); } 21 | 22 | private: 23 | cudaStream_t m_stream; 24 | //cudaEvent_t m_event; 25 | }; 26 | }// namespace Pupil::cuda -------------------------------------------------------------------------------- /framework/system/system.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "util/util.h" 4 | #include "util/timer.h" 5 | 6 | #include 7 | #include 8 | 9 | namespace Pupil { 10 | class Pass; 11 | class GuiPass; 12 | 13 | enum class ESystemEvent { 14 | Quit, 15 | Precompute, 16 | StartRendering, 17 | StopRendering, 18 | SceneLoad, 19 | FrameFinished 20 | }; 21 | 22 | class System : public util::Singleton { 23 | friend class GuiPass; 24 | 25 | public: 26 | bool render_flag = true; 27 | bool quit_flag = false; 28 | 29 | void Init(bool has_window = true) noexcept; 30 | void Run() noexcept; 31 | void Destroy() noexcept; 32 | 33 | void AddPass(Pass *) noexcept; 34 | void SetScene(std::filesystem::path) noexcept; 35 | 36 | private: 37 | std::vector m_passes; 38 | std::vector m_pre_passes; 39 | GuiPass *m_gui_pass = nullptr; 40 | Timer m_render_timer; 41 | }; 42 | }// namespace Pupil -------------------------------------------------------------------------------- /framework/util/util.cpp: -------------------------------------------------------------------------------- 1 | #include "util.h" 2 | 3 | namespace Pupil::util { 4 | std::vector Split(std::string_view str, std::string_view deli) { 5 | std::vector ret; 6 | size_t pos_start = 0, pos_end; 7 | 8 | while ((pos_end = str.find(deli, pos_start)) != std::string::npos) { 9 | ret.emplace_back(str.substr(pos_start, pos_end - pos_start)); 10 | pos_start = pos_end + deli.size(); 11 | } 12 | 13 | ret.emplace_back(str.substr(pos_start)); 14 | return ret; 15 | } 16 | 17 | Float3 StrToFloat3(std::string_view str) { 18 | if (str.empty()) return Float3{ 0.f }; 19 | 20 | auto values = Split(str, ","); 21 | 22 | if (values.size() == 1) { 23 | return Float3{ std::stof(values[0]) }; 24 | } else if (values.size() == 3) { 25 | return Float3{ std::stof(values[0]), std::stof(values[1]), std::stof(values[2]) }; 26 | } 27 | 28 | return Float3{ 0.f }; 29 | } 30 | }// namespace Pupil::util -------------------------------------------------------------------------------- /framework/render/material/predefine.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "cuda/preprocessor.h" 4 | #include "util/enum.h" 5 | 6 | #include 7 | 8 | namespace Pupil { 9 | /// Add a new material 10 | /// 1. declare material name in decl/material_decl.inl 11 | /// 2. xml obj to `material` struct 12 | /// 3. `material` struct to `optix_material` struct 13 | /// 4. implemente bsdf 14 | 15 | enum class EMatType : unsigned int { 16 | Unknown = 0, 17 | #define PUPIL_MATERIAL_TYPE_DEFINE(type) type, 18 | #include "decl/material_decl.inl" 19 | #undef PUPIL_MATERIAL_TYPE_DEFINE 20 | Twosided, 21 | Count 22 | }; 23 | 24 | const auto S_MAT_TYPE_NAME = std::array{ 25 | #define PUPIL_MATERIAL_NAME_DEFINE(name) #name, 26 | #include "decl/material_decl.inl" 27 | #undef PUPIL_MATERIAL_NAME_DEFINE 28 | "twosided" 29 | }; 30 | 31 | #define PUPIL_MAT_SAMPLE_CALL(mat) __direct_callable__##mat##_sample 32 | #define PUPIL_MAT_EVAL_CALL(mat) __direct_callable__##mat##_eval 33 | 34 | }// namespace Pupil -------------------------------------------------------------------------------- /framework/util/timer.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | namespace Pupil { 5 | class Timer { 6 | public: 7 | void Start() noexcept { 8 | m_start_time = std::chrono::system_clock::now(); 9 | m_is_running = true; 10 | } 11 | 12 | void Stop() noexcept { 13 | m_end_time = std::chrono::system_clock::now(); 14 | m_is_running = false; 15 | } 16 | 17 | double ElapsedMilliseconds() noexcept { 18 | std::chrono::time_point end_time = 19 | m_is_running ? std::chrono::system_clock::now() : m_end_time; 20 | return std::chrono::duration_cast(end_time - m_start_time).count() / 1000.; 21 | } 22 | 23 | double ElapsedSeconds() noexcept { 24 | return ElapsedMilliseconds() / 1000.0; 25 | } 26 | 27 | private: 28 | std::chrono::time_point m_start_time; 29 | std::chrono::time_point m_end_time; 30 | bool m_is_running = false; 31 | }; 32 | }// namespace Pupil -------------------------------------------------------------------------------- /framework/system/denoise_pass.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "system/pass.h" 4 | #include "world/world.h" 5 | #include "optix/denoiser.h" 6 | 7 | #include "cuda/stream.h" 8 | 9 | #include "util/timer.h" 10 | 11 | #include 12 | #include 13 | 14 | namespace Pupil { 15 | class DenoisePass : public Pass { 16 | public: 17 | static bool s_enabled_flag; 18 | 19 | struct Config { 20 | bool default_enable; 21 | std::string noise_name; 22 | bool use_albedo; 23 | std::string albedo_name; 24 | bool use_normal; 25 | std::string normal_name; 26 | }; 27 | 28 | DenoisePass(const Config &config, std::string_view name = "Denoise") noexcept; 29 | virtual void OnRun() noexcept override; 30 | virtual void Inspector() noexcept override; 31 | 32 | void SetScene(world::World *) noexcept; 33 | 34 | private: 35 | std::unique_ptr m_stream; 36 | std::unique_ptr m_denoiser; 37 | 38 | Config m_config; 39 | 40 | Timer m_timer; 41 | }; 42 | }// namespace Pupil -------------------------------------------------------------------------------- /framework/util/transform.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "type.h" 4 | 5 | namespace Pupil::util { 6 | struct Transform { 7 | Mat4 matrix; 8 | 9 | Transform() noexcept 10 | : matrix(1.f, 0.f, 0.f, 0.f, 11 | 0.f, 1.f, 0.f, 0.f, 12 | 0.f, 0.f, 1.f, 0.f, 13 | 0.f, 0.f, 0.f, 1.f) {} 14 | Transform(const Mat4 &m) noexcept : matrix(m) {} 15 | 16 | void Translate(float x, float y, float z) noexcept; 17 | // rotation axis is [ux, uy, uz] 18 | void Rotate(float ux, float uy, float uz, float angle) noexcept; 19 | void Scale(float x, float y, float z) noexcept; 20 | 21 | void LookAt(const Float3 &origin, const Float3 &target, const Float3 &up) noexcept; 22 | 23 | static Float3 TransformPoint(const Float3 point, const Mat4 &transform_matrix) noexcept; 24 | static Float3 TransformVector(const Float3 vector, const Mat4 &transform_matrix) noexcept; 25 | static Float3 TransformNormal(const Float3 normal, const Mat4 &transform_matrix_inv_t) noexcept; 26 | }; 27 | }// namespace Pupil::util -------------------------------------------------------------------------------- /framework/resource/emitter.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "util/texture.h" 4 | #include "util/type.h" 5 | 6 | namespace Pupil::resource { 7 | enum class EEmitterType { 8 | Unknown, 9 | Area, 10 | Point, 11 | ConstEnv, 12 | EnvMap, 13 | DistDir 14 | }; 15 | 16 | struct AreaEmitter { 17 | util::Texture radiance; 18 | }; 19 | 20 | struct PointEmitter { 21 | util::Float3 pos; 22 | util::Float3 intensity; 23 | }; 24 | 25 | struct ConstEnv { 26 | util::Float3 radiance; 27 | }; 28 | 29 | struct EnvMap { 30 | util::Texture radiance; 31 | float scale; 32 | 33 | util::Transform transform{}; 34 | }; 35 | 36 | struct DirectionalEmitter { 37 | util::Float3 irradiance; 38 | util::Float3 dir; 39 | }; 40 | 41 | struct Emitter { 42 | EEmitterType type = EEmitterType::Unknown; 43 | union { 44 | AreaEmitter area; 45 | PointEmitter point; 46 | ConstEnv const_env; 47 | EnvMap env_map; 48 | DirectionalEmitter dir; 49 | }; 50 | 51 | Emitter() noexcept {} 52 | }; 53 | }// namespace Pupil::resource -------------------------------------------------------------------------------- /framework/system/type.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "util/type.h" 4 | #include "cuda/vec_math.h" 5 | #include "cuda/matrix.h" 6 | 7 | namespace Pupil { 8 | inline float3 ToCudaType(const util::Float3 &v) noexcept { return make_float3(v.x, v.y, v.z); } 9 | inline float4 ToCudaType(const util::Float4 &v) noexcept { return make_float4(v.x, v.y, v.z, v.w); } 10 | inline mat4x4 ToCudaType(const util::Mat4 &m) noexcept { 11 | mat4x4 c_m; 12 | c_m.r0 = ToCudaType(m.r0); 13 | c_m.r1 = ToCudaType(m.r1); 14 | c_m.r2 = ToCudaType(m.r2); 15 | c_m.r3 = ToCudaType(m.r3); 16 | return c_m; 17 | } 18 | 19 | inline util::Float3 ToUtilType(const float3 &v) noexcept { return util::Float3(v.x, v.y, v.z); } 20 | inline util::Float4 ToUtilType(const float4 &v) noexcept { return util::Float4(v.x, v.y, v.z, v.w); } 21 | inline util::Mat4 ToUtilType(const mat4x4 &c_m) noexcept { 22 | util::Mat4 m; 23 | m.r0 = ToUtilType(c_m.r0); 24 | m.r1 = ToUtilType(c_m.r1); 25 | m.r2 = ToUtilType(c_m.r2); 26 | m.r3 = ToUtilType(c_m.r3); 27 | return m; 28 | } 29 | 30 | }// namespace Pupil -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "3rdparty/DirectXTK12"] 2 | path = 3rdparty/DirectXTK12 3 | url = https://github.com/microsoft/DirectXTK12.git 4 | [submodule "3rdparty/pugixml"] 5 | path = 3rdparty/pugixml 6 | url = https://github.com/zeux/pugixml.git 7 | [submodule "3rdparty/assimp"] 8 | path = 3rdparty/assimp 9 | url = https://github.com/assimp/assimp.git 10 | [submodule "3rdparty/stb/stb"] 11 | path = 3rdparty/stb/stb 12 | url = https://github.com/nothings/stb.git 13 | [submodule "3rdparty/imgui/imgui"] 14 | path = 3rdparty/imgui/imgui 15 | url = https://github.com/ocornut/imgui.git 16 | [submodule "3rdparty/imgui/imgui-filebrowser"] 17 | path = 3rdparty/imgui/imgui-filebrowser 18 | url = https://github.com/AirGuanZ/imgui-filebrowser.git 19 | [submodule "3rdparty/spdlog"] 20 | path = 3rdparty/spdlog 21 | url = https://github.com/gabime/spdlog.git 22 | [submodule "3rdparty/exr/tinyexr"] 23 | path = 3rdparty/exr/tinyexr 24 | url = https://github.com/syoyo/tinyexr.git 25 | [submodule "3rdparty/imgui/ImGuizmo"] 26 | path = 3rdparty/imgui/ImGuizmo 27 | url = https://github.com/CedricGuillemet/ImGuizmo.git 28 | -------------------------------------------------------------------------------- /example/path_tracer/type.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "render/geometry.h" 4 | #include "render/camera.h" 5 | #include "render/emitter.h" 6 | #include "render/material/optix_material.h" 7 | 8 | namespace Pupil::pt { 9 | struct OptixLaunchParams { 10 | struct { 11 | unsigned int max_depth; 12 | bool accumulated_flag; 13 | 14 | struct { 15 | unsigned int width; 16 | unsigned int height; 17 | } frame; 18 | } config; 19 | unsigned int random_seed; 20 | unsigned int sample_cnt; 21 | 22 | cuda::ConstDataView camera; 23 | optix::EmitterGroup emitters; 24 | 25 | cuda::RWArrayView accum_buffer; 26 | cuda::RWArrayView frame_buffer; 27 | 28 | cuda::RWArrayView normal_buffer; 29 | cuda::RWArrayView albedo_buffer; 30 | cuda::RWArrayView test; 31 | 32 | OptixTraversableHandle handle; 33 | }; 34 | 35 | struct HitGroupData { 36 | optix::material::Material mat; 37 | optix::Geometry geo; 38 | int emitter_index_offset = -1; 39 | }; 40 | 41 | }// namespace Pupil::pt -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # PupilOptixLab 2 | 3 | PupilOptixLab is a lightweight real-time ray tracing framework based on OptiX7 which is designed for rapid implementation of ray tracing algorithms on GPU. 4 | 5 | Some ray tracing methods will be implemented in [PupilRay](https://github.com/mchenwang/PupilRay). 6 | 7 | ## Features 8 | 9 | - mistuba3-style scenes format 10 | - pt with mis 11 | - OptiX7 denoiser 12 | - material: diffuse, conductor, rough conductor, dielectric, rough dielectric, plastic, rough plastic 13 | - asynchronous GUI thread 14 | - support native cuda code 15 | - camera interaction (mouse dragging \ keyboard moving with WASDQR) 16 | - [wavefront path tracer](https://github.com/mchenwang/WavefrontPathTracer): about 3x performance improvement 17 | 18 | ## Prerequisites 19 | 20 | - CMake 3.25.2+ (to support `-std=c++20` option for nvcc) 21 | - Visual Studio 2022 22 | - NVIDIA graphics card with OptiX7 support 23 | - CUDA 12.0+ (tested on 12.1) 24 | - OptiX 7.5 (for the built-in sphere intersection and higher versions are currently not supported due to APIs changes) 25 | 26 | ## screenshot 27 | 28 | ![](https://github.com/mchenwang/PupilOptixLab/raw/main/image/PupilOptixLab.jpg) 29 | 30 | -------------------------------------------------------------------------------- /framework/world/render_object.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "gas_manager.h" 4 | #include "util/transform.h" 5 | #include "util/aabb.h" 6 | #include "resource/shape.h" 7 | #include "render/geometry.h" 8 | #include "render/material/optix_material.h" 9 | 10 | namespace Pupil::world { 11 | struct RenderObject { 12 | public: 13 | std::string name; 14 | uint32_t shape_id; 15 | 16 | GAS *gas = nullptr; 17 | unsigned int visibility_mask = 1; 18 | util::Transform transform{}; 19 | util::AABB aabb{}; 20 | 21 | optix::Geometry geo{}; 22 | optix::material::Material mat{}; 23 | 24 | bool is_emitter = false; 25 | unsigned int sub_emitters_num = 0; 26 | 27 | RenderObject(const resource::ShapeInstance &, unsigned int v_mask = 1) noexcept; 28 | ~RenderObject() noexcept; 29 | 30 | void Reset(const resource::Shape *) noexcept; 31 | 32 | void UpdateTransform(const util::Transform &new_transform) noexcept; 33 | void ApplyTransform(const util::Transform &new_transform) noexcept; 34 | 35 | private: 36 | RenderObject(const RenderObject &) = delete; 37 | RenderObject &operator=(const RenderObject &) = delete; 38 | }; 39 | 40 | }// namespace Pupil::world -------------------------------------------------------------------------------- /framework/render/material/bsdf/bsdf.cu: -------------------------------------------------------------------------------- 1 | #include "../optix_material.h" 2 | #include "../fresnel.h" 3 | 4 | using namespace Pupil::optix; 5 | using namespace material; 6 | 7 | #define PUPIL_MATERIAL_ATTR_DEFINE(attr) \ 8 | extern "C" __device__ void PUPIL_MAT_SAMPLE_CALL(attr)(BsdfSamplingRecord & record, const material::Material::LocalBsdf &bsdf) { \ 9 | bsdf.attr.Sample(record); \ 10 | } \ 11 | extern "C" __device__ void PUPIL_MAT_EVAL_CALL(attr)(BsdfSamplingRecord & record, const material::Material::LocalBsdf &bsdf) { \ 12 | bsdf.attr.GetBsdf(record); \ 13 | bsdf.attr.GetPdf(record); \ 14 | } 15 | #include "decl/material_decl.inl" 16 | #undef PUPIL_MATERIAL_ATTR_DEFINE -------------------------------------------------------------------------------- /example/path_tracer/pt_pass.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "type.h" 4 | 5 | #include "system/pass.h" 6 | #include "system/buffer.h" 7 | #include "world/world.h" 8 | #include "resource/scene.h" 9 | #include "optix/pass.h" 10 | 11 | #include "cuda/stream.h" 12 | 13 | #include 14 | #include 15 | 16 | namespace Pupil::pt { 17 | struct SBTTypes : public optix::EmptySBT { 18 | using HitGroupDataType = Pupil::pt::HitGroupData; 19 | }; 20 | 21 | class PTPass : public Pass { 22 | public: 23 | PTPass(std::string_view name = "Path Tracing") noexcept; 24 | virtual void OnRun() noexcept override; 25 | virtual void Inspector() noexcept override; 26 | 27 | void SetScene(world::World *) noexcept; 28 | 29 | private: 30 | void BindingEventCallback() noexcept; 31 | void InitOptixPipeline() noexcept; 32 | void SetSBT(resource::Scene *) noexcept; 33 | 34 | OptixLaunchParams m_optix_launch_params; 35 | std::unique_ptr m_stream; 36 | std::unique_ptr> m_optix_pass; 37 | size_t m_output_pixel_num = 0; 38 | 39 | std::atomic_bool m_dirty = true; 40 | world::CameraHelper *m_world_camera = nullptr; 41 | }; 42 | }// namespace Pupil::pt -------------------------------------------------------------------------------- /framework/resource/xml/tag.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | #include "util/enum.h" 7 | 8 | namespace Pupil::resource { 9 | namespace xml { 10 | 11 | #define PUPIL_XML_TAGS \ 12 | scene, \ 13 | default, \ 14 | bsdf, \ 15 | emitter, \ 16 | film, \ 17 | integrator, \ 18 | sensor, \ 19 | shape, \ 20 | texture, \ 21 | lookat, \ 22 | transform, /*xml objects*/ \ 23 | integer, \ 24 | string, \ 25 | float, \ 26 | rgb, \ 27 | point, \ 28 | matrix, \ 29 | scale, \ 30 | rotate, \ 31 | translate, \ 32 | boolean, /*properties*/ \ 33 | ref 34 | 35 | PUPIL_ENUM_DEFINE(ETag, PUPIL_XML_TAGS) 36 | PUPIL_ENUM_STRING_ARRAY(S_TAGS_NAME, PUPIL_XML_TAGS) 37 | } 38 | }// namespace Pupil::resource::xml -------------------------------------------------------------------------------- /framework/util/util.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "type.h" 4 | 5 | #include 6 | #include 7 | #include 8 | 9 | namespace Pupil::util { 10 | template 11 | class Singleton { 12 | public: 13 | static T *instance() { 14 | static const std::unique_ptr instance = std::make_unique(); 15 | return instance.get(); 16 | } 17 | 18 | Singleton(const Singleton &) = delete; 19 | Singleton &operator=(const Singleton) = delete; 20 | 21 | protected: 22 | Singleton() {} 23 | }; 24 | 25 | // struct is from "https://www.cppstories.com/2021/heterogeneous-access-cpp20/" 26 | struct StringHash { 27 | using is_transparent = void; 28 | [[nodiscard]] size_t operator()(const char *txt) const { 29 | return std::hash{}(txt); 30 | } 31 | [[nodiscard]] size_t operator()(std::string_view txt) const { 32 | return std::hash{}(txt); 33 | } 34 | [[nodiscard]] size_t operator()(const std::string &txt) const { 35 | return std::hash{}(txt); 36 | } 37 | }; 38 | 39 | std::vector Split(std::string_view str, std::string_view deli); 40 | 41 | Float3 StrToFloat3(std::string_view); 42 | }// namespace Pupil::util -------------------------------------------------------------------------------- /framework/world/gas_manager.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "util/util.h" 4 | 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | namespace Pupil::resource { 12 | struct Shape; 13 | } 14 | 15 | namespace Pupil::world { 16 | class GAS { 17 | public: 18 | const Pupil::resource::Shape *ref_shape = nullptr; 19 | 20 | GAS(const Pupil::resource::Shape *) 21 | noexcept; 22 | ~GAS() noexcept; 23 | 24 | operator OptixTraversableHandle() const noexcept { return m_handle; } 25 | 26 | void Create() noexcept; 27 | 28 | private: 29 | OptixTraversableHandle m_handle; 30 | CUdeviceptr m_buffer; 31 | CUdeviceptr m_temp_buffer; 32 | }; 33 | 34 | class GASManager : util::Singleton { 35 | public: 36 | std::pair RefGAS(const resource::Shape *) noexcept; 37 | 38 | uint32_t GetGASRefCnt(GAS *) const noexcept; 39 | 40 | void Release(GAS *) noexcept; 41 | void ClearDanglingMemory() noexcept; 42 | 43 | void Destroy() noexcept; 44 | 45 | // key: shape id 46 | std::unordered_map> m_gass; 47 | std::unordered_map m_gas_ref_cnt; 48 | }; 49 | }// namespace Pupil::world -------------------------------------------------------------------------------- /framework/optix/module.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "util/util.h" 4 | 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | namespace Pupil::optix { 11 | struct Module { 12 | Module(OptixDeviceContext context, OptixPrimitiveType) noexcept; 13 | Module(OptixDeviceContext context, std::string_view) noexcept; 14 | ~Module() noexcept; 15 | 16 | OptixModule optix_module = nullptr; 17 | operator OptixModule() const noexcept { return optix_module; } 18 | }; 19 | 20 | enum class EModuleBuiltinType { 21 | CustomPrimitive, 22 | RoundQuadraticBsplinePrimitive, 23 | RoundCubicBsplinePrimitive, 24 | RoundLinearPrimitive, 25 | RoundCatmullromPrimitive, 26 | SpherePrimitive, 27 | TrianglePrimitive, 28 | Material, 29 | }; 30 | 31 | class ModuleManager : public Pupil::util::Singleton { 32 | private: 33 | // std::unordered_map, util::StringHash, std::equal_to<>> m_modules; 34 | std::unordered_map> m_modules; 35 | 36 | public: 37 | [[nodiscard]] Module *GetModule(EModuleBuiltinType) noexcept; 38 | [[nodiscard]] Module *GetModule(std::string_view) noexcept; 39 | 40 | void Clear() noexcept; 41 | }; 42 | }// namespace Pupil::optix -------------------------------------------------------------------------------- /framework/system/pass.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include "util/timer.h" 5 | 6 | namespace Pupil { 7 | enum class EPassTag : uint32_t { 8 | None = 0, 9 | Pre = 1 << 0, 10 | Post = 1 << 1, 11 | Asyn = 1 << 2 12 | }; 13 | 14 | inline static bool operator&(const EPassTag &target, const EPassTag &tag) noexcept { 15 | return static_cast(target) & static_cast(tag); 16 | } 17 | inline static EPassTag operator|(const EPassTag &target, const EPassTag &tag) noexcept { 18 | return static_cast( 19 | static_cast(target) | static_cast(tag)); 20 | } 21 | 22 | class Pass { 23 | protected: 24 | Timer m_timer; 25 | double m_last_exec_time = 0.; 26 | bool m_enable = true; 27 | 28 | public: 29 | const std::string name; 30 | const EPassTag tag; 31 | 32 | Pass(std::string_view name, EPassTag tag = EPassTag::None) noexcept 33 | : name(name), tag(tag) {} 34 | 35 | virtual void Run() noexcept; 36 | virtual void Inspector() noexcept; 37 | 38 | virtual void OnRun() noexcept = 0; 39 | 40 | void Toggle() noexcept { m_enable ^= true; } 41 | void SetEnablility(bool enable) noexcept { m_enable = enable; } 42 | bool IsEnabled() const noexcept { return m_enable; } 43 | }; 44 | }// namespace Pupil -------------------------------------------------------------------------------- /framework/world/emitter.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "render/emitter.h" 4 | #include "resource/emitter.h" 5 | #include "resource/shape.h" 6 | 7 | namespace Pupil::world { 8 | class EmitterHelper { 9 | public: 10 | EmitterHelper() noexcept; 11 | ~EmitterHelper() noexcept; 12 | 13 | size_t AddAreaEmitter(const resource::ShapeInstance &) noexcept; 14 | void ResetAreaEmitter(const resource::ShapeInstance &, size_t offset) noexcept; 15 | void AddEmitter(const resource::Emitter &) noexcept; 16 | 17 | void ComputeProbability() noexcept; 18 | 19 | void Clear() noexcept; 20 | optix::EmitterGroup GetEmitterGroup() noexcept; 21 | 22 | private: 23 | void SetMeshAreaEmitter(const resource::ShapeInstance &, size_t offset) noexcept; 24 | void SetSphereAreaEmitter(const resource::ShapeInstance &, size_t offset) noexcept; 25 | 26 | bool m_dirty; 27 | 28 | std::vector m_areas; 29 | std::vector m_points; 30 | std::vector m_directionals; 31 | optix::Emitter m_env; 32 | 33 | CUdeviceptr m_areas_cuda_memory; 34 | CUdeviceptr m_points_cuda_memory; 35 | CUdeviceptr m_directionals_cuda_memory; 36 | CUdeviceptr m_env_cuda_memory; 37 | CUdeviceptr m_env_cdf_weight_cuda_memory; 38 | }; 39 | }// namespace Pupil::world -------------------------------------------------------------------------------- /framework/cuda/random.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "preprocessor.h" 4 | #include "vec_math.h" 5 | 6 | namespace Pupil::cuda { 7 | /// @brief generate random float [0, 1) 8 | class Random { 9 | private: 10 | unsigned int m_seed; 11 | 12 | public: 13 | CUDA_HOSTDEVICE Random() noexcept : m_seed(0) {} 14 | CUDA_HOSTDEVICE void Init(unsigned int N, unsigned int val0, unsigned int val1) noexcept { 15 | unsigned int v0 = val0; 16 | unsigned int v1 = val1; 17 | unsigned int s0 = 0; 18 | 19 | for (unsigned int n = 0; n < N; n++) { 20 | s0 += 0x9e3779b9; 21 | v0 += ((v1 << 4) + 0xa341316c) ^ (v1 + s0) ^ ((v1 >> 5) + 0xc8013ea4); 22 | v1 += ((v0 << 4) + 0xad90777d) ^ (v0 + s0) ^ ((v0 >> 5) + 0x7e95761e); 23 | } 24 | 25 | m_seed = v0; 26 | } 27 | 28 | CUDA_HOSTDEVICE unsigned int GetSeed() const noexcept { return m_seed; } 29 | CUDA_HOSTDEVICE void SetSeed(unsigned int seed) noexcept { m_seed = seed; } 30 | 31 | CUDA_HOSTDEVICE float Next() noexcept { 32 | const unsigned int LCG_A = 1664525u; 33 | const unsigned int LCG_C = 1013904223u; 34 | m_seed = (LCG_A * m_seed + LCG_C); 35 | return static_cast(m_seed & 0x00FFFFFF) / 0x01000000; 36 | } 37 | 38 | CUDA_HOSTDEVICE float2 Next2() noexcept { 39 | return make_float2(Next(), Next()); 40 | } 41 | }; 42 | }// namespace Pupil::cuda -------------------------------------------------------------------------------- /framework/resource/texture.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "util/util.h" 4 | 5 | #include 6 | #include 7 | #include 8 | 9 | namespace Pupil::util { 10 | struct Texture; 11 | } 12 | 13 | namespace Pupil::resource { 14 | class TextureManager : public util::Singleton { 15 | private: 16 | struct ImageData { 17 | size_t w; 18 | size_t h; 19 | std::unique_ptr data; 20 | 21 | ImageData(size_t w, size_t h, float *data) noexcept : w(w), h(h) { 22 | this->data.reset(data); 23 | } 24 | 25 | ImageData(size_t w, size_t h) noexcept : w(w), h(h) { 26 | size_t data_size = w * h * 4; 27 | data = std::make_unique(data_size); 28 | } 29 | }; 30 | 31 | std::unordered_map, util::StringHash, std::equal_to<>> m_image_datas; 32 | 33 | public: 34 | TextureManager() noexcept = default; 35 | 36 | void LoadTextureFromFile(std::string_view) noexcept; 37 | [[nodiscard]] util::Texture GetColorTexture(float r, float g, float b) noexcept; 38 | [[nodiscard]] util::Texture GetColorTexture(util::Float3 color) noexcept; 39 | [[nodiscard]] util::Texture GetCheckerboardTexture(util::Float3 patch1, util::Float3 patch2) noexcept; 40 | [[nodiscard]] util::Texture GetTexture(std::string_view) noexcept; 41 | 42 | void Clear() noexcept; 43 | }; 44 | }// namespace Pupil::resource -------------------------------------------------------------------------------- /framework/render/material/bsdf/diffuse.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "../predefine.h" 4 | #include "cuda/texture.h" 5 | #include "optix/util.h" 6 | 7 | namespace Pupil::optix::material { 8 | 9 | struct Diffuse { 10 | cuda::Texture reflectance; 11 | 12 | struct Local { 13 | float3 reflectance; 14 | CUDA_HOSTDEVICE void GetBsdf(BsdfSamplingRecord &record) const noexcept { 15 | float3 f = make_float3(0.f); 16 | if (record.wi.z > 0.f && record.wo.z > 0.f) 17 | f = reflectance * M_1_PIf; 18 | record.f = f; 19 | } 20 | 21 | CUDA_HOSTDEVICE void GetPdf(BsdfSamplingRecord &record) const noexcept { 22 | float pdf = 0.f; 23 | if (record.wi.z > 0.f && record.wo.z > 0.f) 24 | pdf = optix::CosineSampleHemispherePdf(record.wi); 25 | record.pdf = pdf; 26 | } 27 | 28 | CUDA_HOSTDEVICE void Sample(BsdfSamplingRecord &record) const noexcept { 29 | float2 xi = record.sampler->Next2(); 30 | record.wi = Pupil::optix::CosineSampleHemisphere(xi.x, xi.y); 31 | GetPdf(record); 32 | GetBsdf(record); 33 | record.sampled_type = EBsdfLobeType::DiffuseReflection; 34 | } 35 | }; 36 | 37 | CUDA_DEVICE Local GetLocal(float2 sampled_tex) const noexcept { 38 | Local local_bsdf; 39 | local_bsdf.reflectance = reflectance.Sample(sampled_tex); 40 | return local_bsdf; 41 | } 42 | }; 43 | 44 | }// namespace Pupil::optix::material -------------------------------------------------------------------------------- /framework/render/material/bsdf/conductor.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "../predefine.h" 4 | #include "cuda/texture.h" 5 | #include "../fresnel.h" 6 | 7 | namespace Pupil::optix::material { 8 | 9 | struct Conductor { 10 | cuda::Texture eta; 11 | cuda::Texture k; 12 | cuda::Texture specular_reflectance; 13 | 14 | struct Local { 15 | float3 eta; 16 | float3 k; 17 | float3 specular_reflectance; 18 | 19 | CUDA_HOSTDEVICE void GetBsdf(BsdfSamplingRecord &record) const noexcept { 20 | record.f = make_float3(0.f); 21 | } 22 | 23 | CUDA_HOSTDEVICE void GetPdf(BsdfSamplingRecord &record) const noexcept { 24 | record.pdf = 0.f; 25 | } 26 | 27 | CUDA_HOSTDEVICE void Sample(BsdfSamplingRecord &record) const noexcept { 28 | record.wi = Pupil::optix::Reflect(record.wo); 29 | record.pdf = 1.f; 30 | 31 | float3 fresnel = fresnel::ConductorReflectance(eta, k, record.wo.z); 32 | record.f = specular_reflectance * fresnel / abs(record.wi.z); 33 | record.sampled_type = EBsdfLobeType::DeltaReflection; 34 | } 35 | }; 36 | 37 | CUDA_DEVICE Local GetLocal(float2 sampled_tex) const noexcept { 38 | Local local_bsdf; 39 | local_bsdf.eta = eta.Sample(sampled_tex); 40 | local_bsdf.k = k.Sample(sampled_tex); 41 | local_bsdf.specular_reflectance = specular_reflectance.Sample(sampled_tex); 42 | return local_bsdf; 43 | } 44 | }; 45 | 46 | }// namespace Pupil::optix::material -------------------------------------------------------------------------------- /framework/util/event.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | namespace Pupil { 9 | template 10 | concept EnumType = std::is_enum_v; 11 | 12 | template 13 | class Event { 14 | public: 15 | static void Bind(std::function &&op) noexcept { 16 | std::scoped_lock lock(m_mutex); 17 | m_ops.push_back(op); 18 | } 19 | 20 | static void Respond(void *p = nullptr) noexcept { 21 | for (auto &&op : m_ops) op(p); 22 | } 23 | 24 | private: 25 | static std::mutex m_mutex; 26 | static std::vector> m_ops; 27 | }; 28 | 29 | template 30 | std::mutex Event::m_mutex; 31 | template 32 | std::vector> Event::m_ops; 33 | 34 | template 35 | requires(std::invocable) 36 | inline void EventBinder(Func &&f) { 37 | Event::Bind(std::forward(f)); 38 | } 39 | 40 | template 41 | inline void EventDispatcher() { 42 | Event::Respond(); 43 | } 44 | template 45 | inline void EventDispatcher(void *p) { 46 | Event::Respond(p); 47 | } 48 | template 49 | requires(!std::is_pointer_v) 50 | inline void EventDispatcher(T p) { 51 | Event::Respond((void *)&p); 52 | } 53 | }// namespace Pupil -------------------------------------------------------------------------------- /framework/resource/scene.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "shape.h" 4 | #include "emitter.h" 5 | #include "xml/object.h" 6 | #include "static.h" 7 | 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | 14 | namespace Pupil::resource { 15 | 16 | /// @param max_depth: maximum depth of ray tracing, default = 1 17 | struct Integrator { 18 | int max_depth = 1; 19 | }; 20 | 21 | /// @brief rgb hdr film, only have width and height 22 | struct Film { 23 | int w = 768; 24 | int h = 576; 25 | }; 26 | 27 | /// @brief perspective and right-handed camera 28 | struct Sensor { 29 | float fov = 90.f; 30 | float near_clip = 0.01f; 31 | float far_clip = 10000.f; 32 | util::Transform transform{}; 33 | Film film{}; 34 | }; 35 | 36 | /// integrator: only support path tracing 37 | /// sensor: only support perspective and right-handed camera 38 | /// film: rgb and hdr 39 | class Scene { 40 | public: 41 | // scene resource file root path 42 | std::filesystem::path scene_root_path; 43 | 44 | Integrator integrator; 45 | Sensor sensor; 46 | std::vector shape_instances; 47 | std::vector emitters; 48 | 49 | Scene() noexcept = default; 50 | ~Scene() noexcept = default; 51 | 52 | void LoadXmlObj(const xml::Object *, void *) noexcept; 53 | 54 | void Reset() noexcept; 55 | bool LoadFromXML(std::filesystem::path) noexcept; 56 | bool LoadFromXML(std::string_view, std::string_view root = DATA_DIR) noexcept; 57 | }; 58 | }// namespace Pupil::resource -------------------------------------------------------------------------------- /framework/system/gui/buffer_to_canvas.cu: -------------------------------------------------------------------------------- 1 | #include "buffer_to_canvas.cuh" 2 | #include "cuda/kernel.h" 3 | 4 | namespace Pupil { 5 | 6 | void CopyFloat1BufferToCanvas(CUdeviceptr dst, CUdeviceptr src, unsigned int size, cuda::Stream *stream) noexcept { 7 | Pupil::cuda::LaunchKernel1D( 8 | size, [=] __device__(unsigned int index, unsigned int size) { 9 | auto output = reinterpret_cast(dst); 10 | auto input = reinterpret_cast(src); 11 | output[index] = make_float4(input[index], input[index], input[index], 1.f); 12 | }, 13 | stream); 14 | } 15 | 16 | void CopyFloat2BufferToCanvas(CUdeviceptr dst, CUdeviceptr src, unsigned int size, cuda::Stream *stream) noexcept { 17 | Pupil::cuda::LaunchKernel1D( 18 | size, [=] __device__(unsigned int index, unsigned int size) { 19 | auto output = reinterpret_cast(dst); 20 | auto input = reinterpret_cast(src); 21 | output[index] = make_float4(input[index].x, input[index].y, 0.f, 1.f); 22 | }, 23 | stream); 24 | } 25 | 26 | void CopyFloat3BufferToCanvas(CUdeviceptr dst, CUdeviceptr src, unsigned int size, cuda::Stream *stream) noexcept { 27 | Pupil::cuda::LaunchKernel1D( 28 | size, [=] __device__(unsigned int index, unsigned int size) { 29 | auto output = reinterpret_cast(dst); 30 | auto input = reinterpret_cast(src); 31 | output[index] = make_float4(input[index].x, input[index].y, input[index].z, 1.f); 32 | }, 33 | stream); 34 | } 35 | }// namespace Pupil -------------------------------------------------------------------------------- /framework/render/emitter/sphere.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | namespace Pupil::optix { 4 | struct SphereEmitter { 5 | cuda::Texture radiance; 6 | 7 | float area; 8 | 9 | struct { 10 | float3 center; 11 | float radius; 12 | } geo; 13 | 14 | CUDA_DEVICE void SampleDirect(EmitterSampleRecord &ret, LocalGeometry &hit_geo, float2 xi) const noexcept { 15 | float3 t = Pupil::optix::UniformSampleSphere(xi.x, xi.y); 16 | float3 position = t * geo.radius + geo.center; 17 | float3 normal = normalize(t); 18 | float2 tex = Pupil::optix::GetSphereTexcoord(t); 19 | ret.radiance = radiance.Sample(tex); 20 | 21 | ret.wi = normalize(position - hit_geo.position); 22 | float NoL = dot(hit_geo.normal, ret.wi); 23 | float LNoL = dot(normal, -ret.wi); 24 | if (NoL > 0.f && LNoL > 0.f) { 25 | float distance = length(position - hit_geo.position); 26 | ret.pdf = distance * distance / (LNoL * area); 27 | ret.distance = distance; 28 | } 29 | 30 | ret.pos = position; 31 | ret.normal = normal; 32 | } 33 | 34 | CUDA_DEVICE void Eval(EmitEvalRecord &ret, LocalGeometry &emit_local_geo, float3 scatter_pos) const noexcept { 35 | float3 dir = normalize(scatter_pos - emit_local_geo.position); 36 | float LNoL = dot(emit_local_geo.normal, dir); 37 | if (LNoL > 0.f) { 38 | float distance = length(scatter_pos - emit_local_geo.position); 39 | ret.pdf = distance * distance / (LNoL * area); 40 | ret.radiance = radiance.Sample(emit_local_geo.texcoord); 41 | } 42 | } 43 | }; 44 | }// namespace Pupil::optix -------------------------------------------------------------------------------- /framework/util/texture.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "transform.h" 4 | #include "type.h" 5 | 6 | #include 7 | #include 8 | 9 | namespace Pupil::util { 10 | enum class ETextureAddressMode : unsigned int { 11 | Wrap = 0, 12 | Clamp = 1, 13 | Mirror = 2, 14 | Border = 3 15 | }; 16 | 17 | enum class ETextureFilterMode : unsigned int { 18 | Point = 0, 19 | Linear = 1 20 | }; 21 | 22 | enum class ETextureType : unsigned int { 23 | RGB = 0, 24 | Bitmap, 25 | Checkerboard 26 | }; 27 | 28 | struct RGBTexture { 29 | Float3 color{ 0.f, 0.f, 0.f }; 30 | }; 31 | 32 | struct CheckerboardTexture { 33 | Float3 patch1{ 0.4f, 0.4f, 0.4f }; 34 | Float3 patch2{ 0.2f, 0.2f, 0.2f }; 35 | }; 36 | 37 | struct BitmapTexture { 38 | size_t w = 0; 39 | size_t h = 0; 40 | float *data = nullptr; 41 | 42 | ETextureAddressMode address_mode = ETextureAddressMode::Wrap; 43 | ETextureFilterMode filter_mode = ETextureFilterMode::Linear; 44 | 45 | enum class FileFormat { 46 | HDR, 47 | EXR 48 | }; 49 | constexpr static std::array S_FILE_FORMAT_NAME{ 50 | "hdr", "exr" 51 | }; 52 | 53 | static bool Save(float *data, size_t w, size_t h, std::string_view file_path, FileFormat) noexcept; 54 | static BitmapTexture Load(std::string_view file_path) noexcept; 55 | }; 56 | 57 | struct Texture { 58 | ETextureType type = ETextureType::RGB; 59 | union { 60 | RGBTexture rgb; 61 | BitmapTexture bitmap; 62 | CheckerboardTexture checkerboard; 63 | }; 64 | Transform transform{}; 65 | 66 | Texture() noexcept : rgb(RGBTexture{ 0.f }) {} 67 | }; 68 | }// namespace Pupil::util -------------------------------------------------------------------------------- /framework/decl/material_decl.inl: -------------------------------------------------------------------------------- 1 | // clang-format off 2 | // #define PUPIL_MATERIAL_TYPE_DEFINE(type) 3 | #ifdef PUPIL_MATERIAL_TYPE_DEFINE 4 | PUPIL_MATERIAL_TYPE_DEFINE(Diffuse) 5 | PUPIL_MATERIAL_TYPE_DEFINE(Dielectric) 6 | PUPIL_MATERIAL_TYPE_DEFINE(RoughDielectric) 7 | PUPIL_MATERIAL_TYPE_DEFINE(Conductor) 8 | PUPIL_MATERIAL_TYPE_DEFINE(RoughConductor) 9 | PUPIL_MATERIAL_TYPE_DEFINE(Plastic) 10 | PUPIL_MATERIAL_TYPE_DEFINE(RoughPlastic) 11 | #endif 12 | 13 | #ifdef PUPIL_MATERIAL_ATTR_DEFINE 14 | PUPIL_MATERIAL_ATTR_DEFINE(diffuse) 15 | PUPIL_MATERIAL_ATTR_DEFINE(dielectric) 16 | PUPIL_MATERIAL_ATTR_DEFINE(rough_dielectric) 17 | PUPIL_MATERIAL_ATTR_DEFINE(conductor) 18 | PUPIL_MATERIAL_ATTR_DEFINE(rough_conductor) 19 | PUPIL_MATERIAL_ATTR_DEFINE(plastic) 20 | PUPIL_MATERIAL_ATTR_DEFINE(rough_plastic) 21 | #endif 22 | 23 | #ifdef PUPIL_MATERIAL_NAME_DEFINE 24 | PUPIL_MATERIAL_NAME_DEFINE(diffuse) 25 | PUPIL_MATERIAL_NAME_DEFINE(dielectric) 26 | PUPIL_MATERIAL_NAME_DEFINE(roughdielectric) 27 | PUPIL_MATERIAL_NAME_DEFINE(conductor) 28 | PUPIL_MATERIAL_NAME_DEFINE(roughconductor) 29 | PUPIL_MATERIAL_NAME_DEFINE(plastic) 30 | PUPIL_MATERIAL_NAME_DEFINE(roughplastic) 31 | #endif 32 | 33 | #ifdef PUPIL_MATERIAL_TYPE_ATTR_DEFINE 34 | PUPIL_MATERIAL_TYPE_ATTR_DEFINE(Diffuse, diffuse) 35 | PUPIL_MATERIAL_TYPE_ATTR_DEFINE(Dielectric, dielectric) 36 | PUPIL_MATERIAL_TYPE_ATTR_DEFINE(RoughDielectric, rough_dielectric) 37 | PUPIL_MATERIAL_TYPE_ATTR_DEFINE(Conductor, conductor) 38 | PUPIL_MATERIAL_TYPE_ATTR_DEFINE(RoughConductor, rough_conductor) 39 | PUPIL_MATERIAL_TYPE_ATTR_DEFINE(Plastic, plastic) 40 | PUPIL_MATERIAL_TYPE_ATTR_DEFINE(RoughPlastic, rough_plastic) 41 | #endif 42 | // clang-format on -------------------------------------------------------------------------------- /framework/optix/check.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | namespace Pupil::optix { 9 | inline void OptixCheck(OptixResult res, const char *call, const char *file, unsigned int line) { 10 | if (res != OPTIX_SUCCESS) { 11 | std::wstringstream ss; 12 | ss << "Optix call '" << call << "' failed: " << file << ':' << line << ")\n"; 13 | std::wcerr << ss.str().c_str(); 14 | assert(false); 15 | } 16 | } 17 | inline void OptixCheckLog( 18 | OptixResult res, 19 | const char *log, 20 | size_t sizeof_log, 21 | size_t sizeof_log_returned, 22 | const char *call, 23 | const char *file, 24 | unsigned int line) { 25 | if (res != OPTIX_SUCCESS) { 26 | std::wstringstream ss; 27 | ss << "Optix call '" << call << "' failed: " << file << ':' << line << ")\nLog:\n" 28 | << log << (sizeof_log_returned > sizeof_log ? "" : "") << '\n'; 29 | std::wcerr << ss.str().c_str(); 30 | assert(false); 31 | } 32 | } 33 | }// namespace Pupil::optix 34 | 35 | #define OPTIX_CHECK(call) Pupil::optix::OptixCheck(call, #call, __FILE__, __LINE__) 36 | #define OPTIX_CHECK_LOG(call) \ 37 | do { \ 38 | char LOG[400]; \ 39 | size_t LOG_SIZE = sizeof(LOG); \ 40 | Pupil::optix::OptixCheckLog(call, LOG, sizeof(LOG), LOG_SIZE, #call, \ 41 | __FILE__, __LINE__); \ 42 | } while (false) 43 | -------------------------------------------------------------------------------- /framework/render/material/bsdf/bsdf.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "cuda/preprocessor.h" 4 | #include "cuda/random.h" 5 | 6 | namespace Pupil::optix { 7 | enum class EBsdfLobeType : unsigned int { 8 | Unknown = 0, 9 | Null = 1, 10 | DiffuseReflection = 1 << 1, 11 | DiffuseTransmission = 1 << 2, 12 | GlossyReflection = 1 << 3, 13 | GlossyTransmission = 1 << 4, 14 | DeltaReflection = 1 << 5, 15 | DeltaTransmission = 1 << 6, 16 | 17 | Reflection = DiffuseReflection | GlossyReflection | DeltaReflection, 18 | Transmission = DiffuseTransmission | GlossyTransmission | DeltaTransmission, 19 | Diffuse = DiffuseReflection | DiffuseTransmission, 20 | Glossy = GlossyReflection | GlossyTransmission, 21 | Delta = DeltaReflection | DeltaTransmission, 22 | 23 | All = Reflection | Transmission | Null 24 | }; 25 | 26 | struct BsdfSamplingRecord { 27 | float2 sampled_tex; 28 | float3 wi; 29 | float3 wo; 30 | float3 f = make_float3(0.f); 31 | float pdf = 0.f; 32 | 33 | cuda::Random *sampler = nullptr; 34 | 35 | EBsdfLobeType sampled_type = EBsdfLobeType::Unknown; 36 | }; 37 | 38 | CUDA_INLINE CUDA_HOSTDEVICE bool BsdfLobeTypeMatch(EBsdfLobeType target, EBsdfLobeType type) { 39 | return static_cast(target) & static_cast(type); 40 | } 41 | CUDA_INLINE CUDA_HOSTDEVICE bool operator&(EBsdfLobeType target, EBsdfLobeType type) { 42 | return static_cast(target) & static_cast(type); 43 | } 44 | }// namespace Pupil::optix 45 | 46 | #include "diffuse.h" 47 | #include "dielectric.h" 48 | #include "rough_dielectric.h" 49 | #include "conductor.h" 50 | #include "rough_conductor.h" 51 | #include "plastic.h" 52 | #include "rough_plastic.h" -------------------------------------------------------------------------------- /framework/util/log.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "util.h" 4 | 5 | #if defined(PUPIL_CUDA) 6 | #include 7 | #elif defined(PUPIL_CPP) 8 | #include "spdlog/spdlog.h" 9 | #endif 10 | 11 | namespace Pupil { 12 | class Log : public util::Singleton { 13 | public: 14 | #if defined(PUPIL_CUDA) 15 | void Init() noexcept {} 16 | void Destroy() noexcept {} 17 | 18 | template 19 | static void Info(T &&...) noexcept {} 20 | 21 | template 22 | static void Error(T &&...) noexcept {} 23 | 24 | template 25 | static void Warn(T &&...) noexcept {} 26 | #elif defined(PUPIL_CPP) 27 | void Init() noexcept { 28 | spdlog::set_pattern("[%^%l%$] %v"); 29 | } 30 | void Destroy() noexcept {} 31 | 32 | template 33 | static void Info(spdlog::format_string_t fmt, Args &&...args) noexcept { 34 | spdlog::info(fmt, std::forward(args)...); 35 | } 36 | 37 | template 38 | static void Error(spdlog::format_string_t fmt, Args &&...args) noexcept { 39 | spdlog::error(fmt, std::forward(args)...); 40 | } 41 | 42 | template 43 | static void Warn(spdlog::format_string_t fmt, Args &&...args) noexcept { 44 | spdlog::warn(fmt, std::forward(args)...); 45 | } 46 | #else 47 | void Init() noexcept {} 48 | void Destroy() noexcept {} 49 | 50 | template 51 | static void Info(T &&...) noexcept {} 52 | 53 | template 54 | static void Error(T &&...) noexcept {} 55 | 56 | template 57 | static void Warn(T &&...) noexcept {} 58 | #endif 59 | }; 60 | }// namespace Pupil -------------------------------------------------------------------------------- /framework/util/thread_pool.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "util.h" 4 | 5 | #include 6 | #include 7 | #include 8 | 9 | namespace Pupil::util { 10 | class ThreadPool : public util::Singleton { 11 | public: 12 | void Init(unsigned int threads_num = std::thread::hardware_concurrency() / 2) noexcept; 13 | void Destroy() noexcept; 14 | 15 | template 16 | requires(std::invocable && std::is_same_v>) 17 | void AddTask(Func &&func, Ts &&...args) noexcept { 18 | if (!m_init_flag) { 19 | func(std::forward(args)...); 20 | return; 21 | } 22 | std::function task_function = std::bind(std::forward(func), std::forward(args)...); 23 | Enqueue(std::move(task_function)); 24 | } 25 | 26 | template 27 | requires std::invocable 28 | [[nodiscard]] auto AddTaskWithReturnValue(Func &&func, Ts &&...args) noexcept { 29 | if (!m_init_flag) { 30 | return func(std::forward(args)...); 31 | } 32 | 33 | using ReturnType = std::invoke_result_t; 34 | auto shared_promise = std::make_shared>(); 35 | auto task = [&, promise = shared_promise]() { 36 | promise->set_value(func(std::forward(args)...)); 37 | }; 38 | 39 | auto ret_futre = shared_promise->get_future(); 40 | Enqueue(std::move(task)); 41 | return ret_futre; 42 | } 43 | 44 | private: 45 | void Enqueue(std::function &&) noexcept; 46 | 47 | bool m_init_flag = false; 48 | }; 49 | }// namespace Pupil::util -------------------------------------------------------------------------------- /framework/util/camera.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "type.h" 4 | #include "transform.h" 5 | 6 | namespace Pupil::util { 7 | struct CameraDesc { 8 | float fov_y; 9 | float aspect_ratio; 10 | float near_clip = 0.01f; 11 | float far_clip = 10000.f; 12 | 13 | Transform to_world; 14 | }; 15 | 16 | class Camera { 17 | private: 18 | float m_fov_y; 19 | float m_aspect_ratio; 20 | float m_near_clip; 21 | float m_far_clip; 22 | 23 | Float3 m_position; 24 | 25 | Mat4 m_rotate; 26 | Mat4 m_rotate_inv; 27 | 28 | bool m_to_world_dirty = true; 29 | Mat4 m_to_world;// camera to world 30 | Mat4 m_view; // world to camera 31 | 32 | bool m_projection_dirty = true; 33 | Mat4 m_sample_to_camera;// screen to camera 34 | Mat4 m_proj; // camera to screen 35 | 36 | public: 37 | static float sensitivity; 38 | static float sensitivity_scale; 39 | 40 | constexpr static Float3 X{ 1.f, 0.f, 0.f }; 41 | constexpr static Float3 Y{ 0.f, 1.f, 0.f }; 42 | constexpr static Float3 Z{ 0.f, 0.f, 1.f }; 43 | 44 | Camera() noexcept = default; 45 | 46 | Float3 GetPosition() const noexcept { return m_position; } 47 | 48 | Mat4 GetSampleToCameraMatrix() noexcept; 49 | Mat4 GetProjectionMatrix() noexcept; 50 | Mat4 GetToWorldMatrix() noexcept; 51 | Mat4 GetViewMatrix() noexcept; 52 | 53 | std::tuple GetCameraCoordinateSystem() const noexcept; 54 | 55 | void SetProjectionFactor(float fov_y, float aspect_ratio, float near_clip = 0.01f, float far_clip = 10000.f) noexcept; 56 | void SetFov(float fov) noexcept; 57 | void SetWorldTransform(Transform to_world) noexcept; 58 | 59 | void Rotate(float delta_x, float delta_y) noexcept; 60 | void Move(Float3 delta) noexcept; 61 | }; 62 | }// namespace Pupil::util -------------------------------------------------------------------------------- /framework/world/ias_manager.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | namespace Pupil::world { 9 | struct RenderObject; 10 | 11 | class IAS { 12 | public: 13 | IAS() 14 | noexcept; 15 | ~IAS() noexcept; 16 | 17 | operator OptixTraversableHandle() const noexcept { return m_handle; } 18 | 19 | void Create(std::vector &instances, unsigned int gas_offset, bool allow_update) noexcept; 20 | void Update(std::vector &instances) noexcept; 21 | 22 | private: 23 | OptixTraversableHandle m_handle = 0; 24 | 25 | CUdeviceptr m_instances_memory = 0; 26 | size_t m_instances_num = 0; 27 | 28 | CUdeviceptr m_ias_buffer = 0; 29 | size_t m_ias_buffer_size = 0; 30 | 31 | CUdeviceptr m_ias_build_update_temp_buffer = 0; 32 | size_t m_ias_update_temp_buffer_size = 0; 33 | }; 34 | 35 | // IASs have different instance sbt offsets for one scene. 36 | class IASManager { 37 | public: 38 | friend struct RenderObject; 39 | 40 | IASManager() noexcept; 41 | ~IASManager() noexcept; 42 | 43 | void SetInstance(const std::vector &render_objs) noexcept; 44 | void UpdateInstance(RenderObject *ro) noexcept; 45 | OptixTraversableHandle GetIASHandle(unsigned int gas_offset, bool allow_update) noexcept; 46 | 47 | void SetDirty() noexcept; 48 | bool IsDirty() const noexcept; 49 | void SetDirty(unsigned int gas_offset, bool allow_update) noexcept; 50 | bool IsDirty(unsigned int gas_offset, bool allow_update) const noexcept; 51 | 52 | private: 53 | std::unique_ptr m_iass[2][32]{}; 54 | std::vector m_instances; 55 | std::unordered_map m_ro_index; 56 | 57 | unsigned int m_dirty_flag = 0; 58 | }; 59 | }// namespace Pupil::world -------------------------------------------------------------------------------- /framework/resource/xml/parser.cpp: -------------------------------------------------------------------------------- 1 | #include "parser.h" 2 | #include "tag.h" 3 | #include "object.h" 4 | #include "visitor.h" 5 | 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | namespace Pupil::resource::xml { 12 | 13 | std::unique_ptr> s_tag_map = nullptr; 14 | 15 | void RegisterContext() { 16 | s_tag_map = std::make_unique>(); 17 | for (unsigned int i = 1u; i < (unsigned int)ETag::_count; i++) { 18 | s_tag_map->emplace(std::string{ S_TAGS_NAME[i - 1] }, static_cast(i)); 19 | } 20 | } 21 | 22 | void DfsParse(Parser *parser, pugi::xml_node node) { 23 | ETag tag = ETag::_unknown; 24 | if (s_tag_map->find(node.name()) != s_tag_map->end()) 25 | tag = s_tag_map->operator[](node.name()); 26 | 27 | auto xml_g_mngr = parser->GetXMLGlobalManager(); 28 | auto obj_parent = xml_g_mngr->current_obj; 29 | 30 | bool flag = Visit(tag, xml_g_mngr, node); 31 | if (!flag) return; 32 | 33 | for (pugi::xml_node &ch : node.children()) 34 | DfsParse(parser, ch); 35 | 36 | xml_g_mngr->current_obj = obj_parent; 37 | } 38 | 39 | void Parser::DeregisterContext() noexcept { 40 | s_tag_map.reset(); 41 | } 42 | 43 | Parser::Parser() noexcept { 44 | if (s_tag_map == nullptr) { 45 | RegisterContext(); 46 | } 47 | m_global_manager = std::make_unique(); 48 | } 49 | 50 | Parser::~Parser() noexcept { 51 | } 52 | 53 | Object *Parser::LoadFromFile(std::filesystem::path file_path) noexcept { 54 | pugi::xml_document doc; 55 | pugi::xml_parse_result result = doc.load_file(file_path.c_str()); 56 | DfsParse(this, doc.document_element()); 57 | 58 | return m_global_manager->objects_pool[0].get(); 59 | } 60 | }// namespace Pupil::resource::xml -------------------------------------------------------------------------------- /framework/render/emitter/area.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | namespace Pupil::optix { 4 | struct TriAreaEmitter { 5 | cuda::Texture radiance; 6 | 7 | float area; 8 | 9 | struct { 10 | struct { 11 | float3 pos; 12 | float3 normal; 13 | float2 tex; 14 | } v0, v1, v2; 15 | } geo; 16 | 17 | CUDA_DEVICE void SampleDirect(EmitterSampleRecord &ret, LocalGeometry &hit_geo, float2 xi) const noexcept { 18 | float3 t = Pupil::optix::UniformSampleTriangle(xi.x, xi.y); 19 | float3 position = geo.v0.pos * t.x + geo.v1.pos * t.y + geo.v2.pos * t.z; 20 | float3 normal = normalize(geo.v0.normal * t.x + geo.v1.normal * t.y + geo.v2.normal * t.z); 21 | auto tex = geo.v0.tex * t.x + geo.v1.tex * t.y + geo.v2.tex * t.z; 22 | ret.radiance = radiance.Sample(tex); 23 | 24 | ret.wi = normalize(position - hit_geo.position); 25 | float NoL = dot(hit_geo.normal, ret.wi); 26 | float LNoL = dot(normal, -ret.wi); 27 | if (NoL > 0.f && LNoL > 0.f) { 28 | float distance = length(position - hit_geo.position); 29 | ret.pdf = distance * distance / (LNoL * area); 30 | ret.distance = distance; 31 | } 32 | 33 | ret.pos = position; 34 | ret.normal = normal; 35 | } 36 | 37 | CUDA_DEVICE void Eval(EmitEvalRecord &ret, LocalGeometry &emit_local_geo, float3 scatter_pos) const noexcept { 38 | float3 dir = normalize(scatter_pos - emit_local_geo.position); 39 | float LNoL = dot(emit_local_geo.normal, dir); 40 | if (LNoL > 0.f) { 41 | float distance = length(scatter_pos - emit_local_geo.position); 42 | ret.pdf = distance * distance / (LNoL * area); 43 | ret.radiance = radiance.Sample(emit_local_geo.texcoord); 44 | } 45 | } 46 | }; 47 | }// namespace Pupil::optix -------------------------------------------------------------------------------- /framework/optix/context.cpp: -------------------------------------------------------------------------------- 1 | #include "context.h" 2 | #include "check.h" 3 | 4 | #include "cuda/context.h" 5 | #include "cuda/util.h" 6 | 7 | #include "util/log.h" 8 | 9 | #include 10 | #include 11 | 12 | #include 13 | #include 14 | 15 | namespace { 16 | void ContextLogCB(unsigned int level, const char *tag, const char *message, void * /*cbdata */) { 17 | switch (level) { 18 | case 1:// fatal 19 | Pupil::Log::Error("OPTIX [{}][{}]: {}", level, tag, message); 20 | break; 21 | case 2:// error 22 | Pupil::Log::Error("OPTIX [{}][{}]: {}", level, tag, message); 23 | break; 24 | case 3:// warning 25 | Pupil::Log::Warn("OPTIX [{}][{}]: {}", level, tag, message); 26 | break; 27 | case 4:// print 28 | Pupil::Log::Info("OPTIX [{}][{}]: {}", level, tag, message); 29 | break; 30 | default: 31 | Pupil::Log::Error("OPTIX [{}][{}]: {}", level, tag, message); 32 | break; 33 | } 34 | } 35 | }// namespace 36 | 37 | namespace Pupil::optix { 38 | void Context::Init() noexcept { 39 | if (IsInitialized()) return; 40 | auto cuda_ctx = util::Singleton::instance(); 41 | assert(cuda_ctx->IsInitialized() && "cuda should be initialized before optix."); 42 | 43 | OPTIX_CHECK(optixInit()); 44 | OptixDeviceContextOptions options{}; 45 | options.logCallbackFunction = &ContextLogCB; 46 | options.logCallbackLevel = 4; 47 | OPTIX_CHECK(optixDeviceContextCreate(*cuda_ctx, &options, &context)); 48 | m_init_flag = true; 49 | Pupil::Log::Info("OPTIX is initialized."); 50 | } 51 | 52 | void Context::Destroy() noexcept { 53 | if (IsInitialized()) { 54 | CUDA_SYNC_CHECK(); 55 | m_init_flag = false; 56 | } 57 | } 58 | }// namespace Pupil::optix -------------------------------------------------------------------------------- /framework/resource/hair/cemyuksel_hair.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "util/type.h" 4 | #include "util/aabb.h" 5 | 6 | #include 7 | 8 | namespace Pupil::resource { 9 | struct CyHair { 10 | struct Header { 11 | // Bytes 0 - 3 Must be "HAIR" in ascii code(48 41 49 52) 12 | char magic[4]; 13 | // Bytes 4 - 7 Number of hair strands as unsigned int 14 | uint32_t strands_num; 15 | // Bytes 8 - 11 Total number of points of all strands as unsigned int 16 | uint32_t points_num; 17 | // Bytes 12 - 15 Bit array of data in the file 18 | // Bit - 5 to Bit - 31 are reserved for future extension(must be 0). 19 | uint32_t flags; 20 | // Bytes 16 - 19 Default number of segments of hair strands as unsigned int 21 | // If the file does not have a segments array, this default value is used. 22 | uint32_t default_segments_num; 23 | // Bytes 20 - 23 Default thickness hair strands as float 24 | // If the file does not have a thickness array, this default value is used. 25 | float default_thickness; 26 | // Bytes 24 - 27 Default transparency hair strands as float 27 | // If the file does not have a transparency array, this default value is used. 28 | float default_alpha; 29 | // Bytes 28 - 39 Default color hair strands as float array of size 3 30 | // If the file does not have a color array, this default value is used. 31 | util::Float3 default_color; 32 | // Bytes 40 - 127 File information as char array of size 88 in ascii 33 | char file_info[88]; 34 | }; 35 | 36 | Header header; 37 | std::vector strands_index; 38 | std::vector positions; 39 | std::vector widths; 40 | 41 | util::AABB aabb; 42 | 43 | static CyHair LoadFromFile(std::string_view) noexcept; 44 | }; 45 | }// namespace Pupil::resource -------------------------------------------------------------------------------- /framework/util/aabb.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "type.h" 4 | #include "transform.h" 5 | 6 | namespace Pupil::util { 7 | struct AABB { 8 | Float3 min; 9 | Float3 max; 10 | 11 | AABB() 12 | noexcept : min(std::numeric_limits::max()), 13 | max(std::numeric_limits::lowest()) {} 14 | 15 | AABB(const Float3 &min_, const Float3 &max_) 16 | noexcept : min(min_), max(max_) {} 17 | 18 | AABB(const AABB &) = default; 19 | 20 | void Merge(const Float3 &point) noexcept { 21 | min.x = std::min(min.x, point.x); 22 | min.y = std::min(min.y, point.y); 23 | min.z = std::min(min.z, point.z); 24 | max.x = std::max(max.x, point.x); 25 | max.y = std::max(max.y, point.y); 26 | max.z = std::max(max.z, point.z); 27 | } 28 | 29 | void Merge(float x, float y, float z) noexcept { 30 | min.x = std::min(min.x, x); 31 | min.y = std::min(min.y, y); 32 | min.z = std::min(min.z, z); 33 | max.x = std::max(max.x, x); 34 | max.y = std::max(max.y, y); 35 | max.z = std::max(max.z, z); 36 | } 37 | 38 | void Merge(const AABB &other) noexcept { 39 | Merge(other.min); 40 | Merge(other.max); 41 | } 42 | 43 | void Transform(const Transform &trans) noexcept { 44 | Float3 vertex[8] = { 45 | { min.x, min.y, min.z }, 46 | { min.x, min.y, max.z }, 47 | { min.x, max.y, min.z }, 48 | { min.x, max.y, max.z }, 49 | { max.x, min.y, min.z }, 50 | { max.x, min.y, max.z }, 51 | { max.x, max.y, min.z }, 52 | { max.x, max.y, max.z } 53 | }; 54 | min = { std::numeric_limits::max() }; 55 | max = { std::numeric_limits::lowest() }; 56 | for (auto &&v : vertex) Merge(Transform::TransformPoint(v, trans.matrix)); 57 | } 58 | }; 59 | }// namespace Pupil::util -------------------------------------------------------------------------------- /framework/optix/denoiser.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include "cuda/stream.h" 5 | 6 | namespace Pupil::optix { 7 | class Denoiser { 8 | public: 9 | enum EMode : unsigned int { 10 | None = 0, 11 | UseAlbedo = 1, 12 | UseNormal = 1 << 1, 13 | ApplyToAOV = 1 << 2, 14 | UseTemporal = 1 << 3, 15 | UseUpscale2X = 1 << 4, 16 | Tiled = 1 << 5 17 | }; 18 | 19 | Denoiser(unsigned int mode = EMode::UseAlbedo | EMode::UseNormal, 20 | cuda::Stream *stream = nullptr) noexcept; 21 | ~Denoiser() noexcept; 22 | 23 | void SetMode(unsigned int mode) noexcept; 24 | void Setup(unsigned int w, unsigned int h) noexcept; 25 | 26 | void Destroy() noexcept; 27 | 28 | void SetTile(unsigned int tile_w, unsigned int tile_h) noexcept; 29 | 30 | struct ExecutionData { 31 | CUdeviceptr input = 0; 32 | CUdeviceptr output = 0; 33 | CUdeviceptr prev_output = 0; 34 | CUdeviceptr albedo = 0; 35 | CUdeviceptr normal = 0; 36 | CUdeviceptr motion_vector = 0; 37 | }; 38 | void Execute(const ExecutionData &) noexcept; 39 | 40 | operator OptixDenoiser() const noexcept { return m_denoiser; } 41 | 42 | public: 43 | unsigned int mode = EMode::UseAlbedo | EMode::UseNormal; 44 | 45 | unsigned int input_w = 0; 46 | unsigned int input_h = 0; 47 | unsigned int tile_w = 100; 48 | unsigned int tile_h = 100; 49 | 50 | private: 51 | cuda::Stream *m_stream = nullptr; 52 | OptixDenoiser m_denoiser = nullptr; 53 | OptixDenoiserParams m_params{}; 54 | OptixDenoiserGuideLayer m_guide_layer{}; 55 | 56 | CUdeviceptr m_scratch = 0; 57 | size_t m_scratch_size = 0; 58 | unsigned int m_overlap = 0; 59 | 60 | CUdeviceptr m_state = 0; 61 | size_t m_state_size = 0; 62 | 63 | CUdeviceptr m_hdr_intensity = 0; 64 | CUdeviceptr m_hdr_average_color = 0; 65 | }; 66 | }// namespace Pupil::optix -------------------------------------------------------------------------------- /framework/resource/xml/object.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "tag.h" 4 | 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | namespace pugi { 11 | class xml_node; 12 | } 13 | 14 | namespace Pupil::resource { 15 | namespace xml { 16 | /// @brief xml object's property 17 | /// support integer, string, float, boolean, rgb, (for transform matrix, scale, translate) 18 | struct Property { 19 | std::string name; 20 | std::string value; 21 | 22 | Property(std::string_view name, std::string_view value) noexcept : name(name), value(value) {} 23 | }; 24 | 25 | // support bsdf, emitter, film, integrator, sensor, shape, texture, transform; 26 | // sub obj for transform: lookat, rotate 27 | // non-unique sub obj: bsdf, shape, texture 28 | struct Object { 29 | const ETag tag; 30 | std::string obj_name; 31 | std::string var_name; 32 | std::string id; 33 | std::string type; 34 | std::vector properties; 35 | std::vector sub_object; 36 | 37 | Object(std::string_view obj_name, std::string_view type, ETag obj_tag = ETag::_unknown) noexcept 38 | : obj_name(obj_name), type(type), tag(obj_tag) {} 39 | 40 | std::string GetProperty(std::string_view) const noexcept; 41 | Object *GetUniqueSubObject(std::string_view) const noexcept; 42 | std::vector GetSubObjects(std::string_view) const noexcept; 43 | 44 | std::pair GetParameter(std::string_view) const noexcept; 45 | }; 46 | 47 | struct GlobalManager { 48 | Object *current_obj = nullptr; 49 | std::vector> objects_pool; 50 | std::unordered_map global_params; 51 | std::unordered_map ref_objects_map; 52 | 53 | void AddGlobalParam(std::string, std::string) noexcept; 54 | 55 | void ReplaceDefaultValue(pugi::xml_node *) noexcept; 56 | }; 57 | } 58 | }// namespace Pupil::resource::xml -------------------------------------------------------------------------------- /framework/cuda/kernel.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "preprocessor.h" 4 | #include "stream.h" 5 | 6 | #ifdef PUPIL_CUDA 7 | #include 8 | #include 9 | 10 | namespace Pupil::cuda { 11 | namespace detail { 12 | template 13 | __global__ void Execute(Func kernel) { 14 | kernel(); 15 | } 16 | 17 | template 18 | __global__ void Execute(unsigned int task_size, Func kernel) { 19 | unsigned int task_id = blockIdx.x * blockDim.x + threadIdx.x; 20 | 21 | if (task_id >= task_size) return; 22 | kernel(task_id, task_size); 23 | } 24 | 25 | template 26 | __global__ void Execute(uint2 task_size, Func kernel) { 27 | uint2 task_id; 28 | task_id.x = threadIdx.x + blockIdx.x * blockDim.x; 29 | task_id.y = threadIdx.y + blockIdx.y * blockDim.y; 30 | 31 | if (task_id.x >= task_size.x) return; 32 | if (task_id.y >= task_size.y) return; 33 | kernel(task_id, task_size); 34 | } 35 | }// namespace detail 36 | 37 | template 38 | void LaunchKernel(Func kernel, Stream *stream) { 39 | detail::Execute<<<1, 1, 0, *stream>>>(kernel); 40 | } 41 | 42 | template 43 | void LaunchKernel1D(unsigned int task_size, Func kernel, Stream *stream) { 44 | static int block_size = 32; 45 | int grid_size = (task_size + block_size - 1) / block_size; 46 | detail::Execute<<>>(task_size, kernel); 47 | } 48 | 49 | template 50 | void LaunchKernel2D(uint2 task_size, Func kernel, Stream *stream) { 51 | int block_size_x = 32; 52 | int block_size_y = 32; 53 | int grid_size_x = (task_size.x + block_size_x - 1) / block_size_x; 54 | int grid_size_y = (task_size.y + block_size_y - 1) / block_size_y; 55 | detail::Execute<<>>(task_size, kernel); 58 | } 59 | }// namespace Pupil::cuda 60 | #endif -------------------------------------------------------------------------------- /framework/system/buffer.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | #include 7 | 8 | #include 9 | #include 10 | 11 | #include "util/util.h" 12 | 13 | namespace Pupil { 14 | 15 | enum class EBufferFlag : unsigned int { 16 | None = 0, 17 | SharedWithDX12 = 1, 18 | AllowDisplay = 1 << 1, 19 | }; 20 | 21 | inline bool operator&(const EBufferFlag &target, const EBufferFlag &flag) noexcept { 22 | return (static_cast(target) & static_cast(flag)) == 23 | static_cast(flag); 24 | } 25 | 26 | struct BufferDesc { 27 | const char *name = nullptr; 28 | EBufferFlag flag = EBufferFlag::None; 29 | uint32_t width = 1; 30 | uint32_t height = 1; 31 | uint32_t stride_in_byte = 1; 32 | }; 33 | 34 | struct Buffer { 35 | BufferDesc desc{}; 36 | CUdeviceptr cuda_ptr = 0; 37 | winrt::com_ptr dx12_ptr = nullptr; 38 | 39 | Buffer() noexcept = default; 40 | Buffer(const BufferDesc &desc) noexcept : desc(desc) {} 41 | ~Buffer() noexcept; 42 | }; 43 | 44 | class BufferManager : public util::Singleton { 45 | public: 46 | constexpr static std::string_view DEFAULT_FINAL_RESULT_BUFFER_NAME = "final result"; 47 | 48 | BufferManager() noexcept; 49 | ~BufferManager() noexcept; 50 | 51 | void Destroy() noexcept; 52 | 53 | [[nodiscard]] Buffer *GetBuffer(std::string_view) noexcept; 54 | Buffer *AllocBuffer(const BufferDesc &) noexcept; 55 | void AddBuffer(std::string_view id, std::unique_ptr &) noexcept; 56 | 57 | [[nodiscard]] const std::vector &GetBufferNameList() const noexcept { return m_buffer_names; } 58 | 59 | private: 60 | std::unordered_map, util::StringHash, std::equal_to<>> m_buffers; 61 | std::unordered_map m_cuda_ext_memorys; 62 | std::vector m_buffer_names; 63 | }; 64 | }// namespace Pupil -------------------------------------------------------------------------------- /framework/resource/xml/util_loader.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "object.h" 4 | #include "util/util.h" 5 | #include "util/texture.h" 6 | 7 | namespace Pupil::resource { 8 | class Scene; 9 | 10 | namespace xml { 11 | 12 | bool LoadInt(std::string_view param_name, std::string_view value, 13 | int ¶m, int default_value = 0) noexcept; 14 | bool LoadInt(const resource::xml::Object *obj, std::string_view param_name, 15 | int ¶m, int default_value = 0) noexcept; 16 | 17 | bool LoadFloat(std::string_view param_name, std::string_view value, 18 | float ¶m, float default_value = 0.f) noexcept; 19 | bool LoadFloat(const resource::xml::Object *obj, std::string_view param_name, 20 | float ¶m, float default_value = 0.f) noexcept; 21 | 22 | bool LoadFloat3(std::string_view param_name, std::string_view value, 23 | util::Float3 ¶m, util::Float3 default_value = { 0.f }) noexcept; 24 | bool LoadFloat3(const resource::xml::Object *obj, std::string_view param_name, 25 | util::Float3 ¶m, util::Float3 default_value = { 0.f }) noexcept; 26 | bool Load3Float(std::string_view param_name, std::string_view value, 27 | util::Float3 ¶m, util::Float3 default_value = { 0.f }) noexcept; 28 | bool Load3Float(const resource::xml::Object *obj, std::string_view param_name, 29 | util::Float3 ¶m, util::Float3 default_value = { 0.f }) noexcept; 30 | 31 | bool LoadTextureOrRGB(const resource::xml::Object *obj, resource::Scene *scene, std::string_view param_name, 32 | util::Texture ¶m, util::Float3 default_value = { 0.f }) noexcept; 33 | 34 | bool LoadBool(const resource::xml::Object *obj, std::string_view param_name, bool ¶m, bool default_value = false) noexcept; 35 | 36 | bool LoadTransform3D(const resource::xml::Object *obj, util::Transform *transform) noexcept; 37 | bool LoadTransform(const resource::xml::Object *obj, void *dst) noexcept; 38 | }// namespace xml 39 | }// namespace Pupil::resource -------------------------------------------------------------------------------- /framework/cuda/context.cpp: -------------------------------------------------------------------------------- 1 | #include "cuda/context.h" 2 | #include "cuda/util.h" 3 | 4 | #include "dx12/context.h" 5 | 6 | #include "util/log.h" 7 | 8 | namespace Pupil::cuda { 9 | void Context::Init() noexcept { 10 | if (IsInitialized()) { 11 | Pupil::Log::Warn("CUDA is initialized repeatedly."); 12 | return; 13 | } 14 | 15 | if (auto dx_context = util::Singleton::instance(); 16 | dx_context->IsInitialized()) { 17 | int num_cuda_devices = 0; 18 | CUDA_CHECK(cudaGetDeviceCount(&num_cuda_devices)); 19 | assert(num_cuda_devices); 20 | 21 | DXGI_ADAPTER_DESC1 dxgi_adapter_desc{}; 22 | dx_context->adapter->GetDesc1(&dxgi_adapter_desc); 23 | 24 | for (int dev_id = 0; dev_id < num_cuda_devices; dev_id++) { 25 | cudaDeviceProp dev_prop{}; 26 | CUDA_CHECK(cudaGetDeviceProperties(&dev_prop, dev_id)); 27 | const auto cmp1 = 28 | memcmp(&dxgi_adapter_desc.AdapterLuid.LowPart, 29 | dev_prop.luid, 30 | sizeof(dxgi_adapter_desc.AdapterLuid.LowPart)) == 0; 31 | const auto cmp2 = 32 | memcmp(&dxgi_adapter_desc.AdapterLuid.HighPart, 33 | dev_prop.luid + sizeof(dxgi_adapter_desc.AdapterLuid.LowPart), 34 | sizeof(dxgi_adapter_desc.AdapterLuid.HighPart)) == 0; 35 | 36 | if (cmp1 && cmp2) { 37 | CUDA_CHECK(cudaSetDevice(dev_id)); 38 | cuda_device_id = (uint32_t)dev_id; 39 | cuda_node_mask = dev_prop.luidDeviceNodeMask; 40 | Pupil::Log::Info("CUDA Device with DirectX12 Used [{}] {}.", dev_id, dev_prop.name); 41 | break; 42 | } 43 | } 44 | } 45 | 46 | CUDA_CHECK(cudaFree(0)); 47 | CUDA_CHECK(cuCtxGetCurrent(&context)); 48 | m_init_flag = true; 49 | } 50 | 51 | void Context::Destroy() noexcept { 52 | if (IsInitialized()) { 53 | m_init_flag = false; 54 | } 55 | } 56 | }// namespace Pupil::cuda -------------------------------------------------------------------------------- /framework/util/thread_pool.cpp: -------------------------------------------------------------------------------- 1 | #include "thread_pool.h" 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | namespace { 8 | unsigned int s_threads_num = 0; 9 | std::unique_ptr s_threads_list; 10 | std::queue> s_task_queue; 11 | 12 | // synchronization 13 | std::mutex s_queue_mutex; 14 | std::condition_variable s_cv; 15 | bool s_stop_flag = true; 16 | }// namespace 17 | 18 | namespace Pupil::util { 19 | void ThreadPool::Init(unsigned int threads_num) noexcept { 20 | s_threads_num = std::max(1u, threads_num); 21 | 22 | s_stop_flag = false; 23 | s_threads_list = std::make_unique(threads_num); 24 | for (unsigned int i = 0; i < threads_num; i++) { 25 | s_threads_list[i] = std::thread([index = i]() { 26 | while (true) { 27 | { 28 | std::unique_lock lock(s_queue_mutex); 29 | s_cv.wait(lock, []() { return s_stop_flag || !s_task_queue.empty(); }); 30 | 31 | if (s_stop_flag && s_task_queue.empty()) return; 32 | } 33 | 34 | auto task = s_task_queue.front(); 35 | s_task_queue.pop(); 36 | task(); 37 | } 38 | }); 39 | } 40 | 41 | m_init_flag = true; 42 | 43 | // PEngine::Log::Info("Thread pool initialized with {} threads", s_threads_num); 44 | } 45 | 46 | void ThreadPool::Destroy() noexcept { 47 | { 48 | std::unique_lock lock(s_queue_mutex); 49 | s_stop_flag = true; 50 | s_cv.notify_all(); 51 | } 52 | 53 | if (s_threads_list) 54 | for (auto i = 0u; i < s_threads_num; i++) 55 | s_threads_list[i].join(); 56 | 57 | s_threads_list.reset(); 58 | m_init_flag = false; 59 | } 60 | 61 | void ThreadPool::Enqueue(std::function &&task) noexcept { 62 | std::unique_lock lock(s_queue_mutex); 63 | 64 | if (!s_stop_flag) { 65 | s_task_queue.emplace(task); 66 | s_cv.notify_one(); 67 | } 68 | } 69 | 70 | }// namespace Pupil::util -------------------------------------------------------------------------------- /framework/system/wsa.cpp: -------------------------------------------------------------------------------- 1 | #include "wsa.h" 2 | #include 3 | 4 | WindowsSecurityAttributes::WindowsSecurityAttributes() { 5 | m_win_p_security_descriptor = (PSECURITY_DESCRIPTOR)calloc(1, SECURITY_DESCRIPTOR_MIN_LENGTH + 2 * sizeof(void **)); 6 | assert(m_win_p_security_descriptor != (PSECURITY_DESCRIPTOR)NULL); 7 | 8 | PSID *ppSID = (PSID *)((PBYTE)m_win_p_security_descriptor + SECURITY_DESCRIPTOR_MIN_LENGTH); 9 | PACL *ppACL = (PACL *)((PBYTE)ppSID + sizeof(PSID *)); 10 | 11 | InitializeSecurityDescriptor(m_win_p_security_descriptor, SECURITY_DESCRIPTOR_REVISION); 12 | 13 | SID_IDENTIFIER_AUTHORITY sidIdentifierAuthority = SECURITY_WORLD_SID_AUTHORITY; 14 | AllocateAndInitializeSid(&sidIdentifierAuthority, 1, SECURITY_WORLD_RID, 0, 0, 0, 0, 0, 0, 0, ppSID); 15 | 16 | EXPLICIT_ACCESS explicitAccess; 17 | ZeroMemory(&explicitAccess, sizeof(EXPLICIT_ACCESS)); 18 | explicitAccess.grfAccessPermissions = STANDARD_RIGHTS_ALL | SPECIFIC_RIGHTS_ALL; 19 | explicitAccess.grfAccessMode = SET_ACCESS; 20 | explicitAccess.grfInheritance = INHERIT_ONLY; 21 | explicitAccess.Trustee.TrusteeForm = TRUSTEE_IS_SID; 22 | explicitAccess.Trustee.TrusteeType = TRUSTEE_IS_WELL_KNOWN_GROUP; 23 | explicitAccess.Trustee.ptstrName = (LPTSTR)*ppSID; 24 | 25 | SetEntriesInAcl(1, &explicitAccess, NULL, ppACL); 26 | 27 | SetSecurityDescriptorDacl(m_win_p_security_descriptor, TRUE, *ppACL, FALSE); 28 | 29 | m_win_security_attributes.nLength = sizeof(m_win_security_attributes); 30 | m_win_security_attributes.lpSecurityDescriptor = m_win_p_security_descriptor; 31 | m_win_security_attributes.bInheritHandle = TRUE; 32 | } 33 | 34 | WindowsSecurityAttributes::~WindowsSecurityAttributes() { 35 | PSID *ppSID = (PSID *)((PBYTE)m_win_p_security_descriptor + SECURITY_DESCRIPTOR_MIN_LENGTH); 36 | PACL *ppACL = (PACL *)((PBYTE)ppSID + sizeof(PSID *)); 37 | 38 | if (*ppSID) 39 | FreeSid(*ppSID); 40 | if (*ppACL) 41 | LocalFree(*ppACL); 42 | free(m_win_p_security_descriptor); 43 | } 44 | 45 | SECURITY_ATTRIBUTES *WindowsSecurityAttributes::operator&() { return &m_win_security_attributes; } -------------------------------------------------------------------------------- /framework/optix/pipeline.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | namespace Pupil::optix { 9 | struct Module; 10 | 11 | // The implementation of pipeline should be constrained in the same module, 12 | // while builtin intersection module can be separated 13 | struct RayTraceProgramDesc { 14 | Module *module_ptr = nullptr; 15 | const char *ray_gen_entry = nullptr; 16 | const char *miss_entry = nullptr; 17 | struct { 18 | const char *ch_entry = nullptr; 19 | const char *ah_entry = nullptr; 20 | Module *intersect_module = nullptr; 21 | const char *is_entry = nullptr; 22 | } hit_group; 23 | }; 24 | struct CallableProgramDesc { 25 | Module *module_ptr = nullptr; 26 | const char *cc_entry = nullptr; 27 | const char *dc_entry = nullptr; 28 | }; 29 | // TODO: user's exception programs 30 | struct PipelineDesc { 31 | std::vector ray_trace_programs; 32 | std::vector callable_programs; 33 | 34 | unsigned int max_trace_depth = 2;// forward ray and shadow ray 35 | unsigned int max_cc_depth = 1; // for material 36 | unsigned int max_dc_depth = 1; // for ...TODO 37 | }; 38 | 39 | // A pipeline object only contains one ray_gen_entry 40 | struct Pipeline { 41 | private: 42 | OptixPipeline m_pipeline = nullptr; 43 | std::vector m_programs; 44 | std::unordered_map m_program_map; 45 | 46 | std::string m_ray_gen_program_name; 47 | OptixProgramGroup m_ray_gen_program = nullptr; 48 | 49 | public: 50 | static OptixPipelineCompileOptions pipeline_compile_options; 51 | 52 | operator OptixPipeline() const noexcept { return m_pipeline; } 53 | 54 | Pipeline(const PipelineDesc &desc) noexcept; 55 | ~Pipeline() noexcept; 56 | 57 | std::string_view GetRayGenProgramName() const noexcept { return m_ray_gen_program_name; } 58 | OptixProgramGroup GetRayGenProgram() const noexcept { return m_ray_gen_program; } 59 | OptixProgramGroup FindProgram(std::string) const noexcept; 60 | }; 61 | }// namespace Pupil::optix -------------------------------------------------------------------------------- /framework/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | 2 | configure_file(static.h.in ${CMAKE_CURRENT_LIST_DIR}/static.h) 3 | set(STATIC_DATA_FILE ${CMAKE_CURRENT_LIST_DIR}/static.h) 4 | 5 | file(GLOB_RECURSE ALL_INCLUDE CONFIGURE_DEPENDS "*.h") 6 | file(GLOB_RECURSE ALL_SOURCES CONFIGURE_DEPENDS "*.cpp" "*.inl") 7 | 8 | set(pupil_cuda_kernel_files 9 | system/gui/buffer_to_canvas.cu 10 | system/gui/buffer_to_canvas.cuh 11 | ) 12 | 13 | source_group(TREE ${CMAKE_CURRENT_SOURCE_DIR} FILES ${ALL_INCLUDE} ${ALL_SOURCES} ${pupil_cuda_kernel_files}) 14 | 15 | cuda_compile_and_embed(g_pupil_material_embedded_ptx_code render/material/bsdf/bsdf.cu) 16 | 17 | set(pupil_builtin_embeding_cu 18 | ${g_pupil_material_embedded_ptx_code} 19 | ) 20 | 21 | add_library(${pupil_framework_name} STATIC ${ALL_INCLUDE} ${ALL_SOURCES} ${pupil_builtin_embeding_cu} ${pupil_cuda_kernel_files}) 22 | target_include_directories(${pupil_framework_name} PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/) 23 | 24 | add_dependencies(${pupil_framework_name} file-copy) 25 | target_link_libraries(${pupil_framework_name} PUBLIC ${3rdparty_target}) 26 | target_link_libraries(${pupil_framework_name} PUBLIC CUDA::cudart CUDA::cuda_driver) 27 | target_link_libraries(${pupil_framework_name} PUBLIC OptiX) 28 | 29 | target_compile_definitions(${pupil_framework_name} PUBLIC _UNICODE UNICODE NOMINMAX) 30 | 31 | target_compile_options(${pupil_framework_name} BEFORE PUBLIC "$<$:/utf-8>") 32 | target_compile_options(${pupil_framework_name} BEFORE PUBLIC "$<$:/await>") 33 | target_compile_options(${pupil_framework_name} BEFORE PUBLIC "$<$:/utf-8>") 34 | target_compile_options(${pupil_framework_name} BEFORE PUBLIC "$<$:/MP>") 35 | target_compile_options(${pupil_framework_name} BEFORE PUBLIC $<$:-Xcompiler "/utf-8 /wd4819 /await" >) 36 | target_compile_options(${pupil_framework_name} BEFORE PUBLIC $<$:--expt-extended-lambda>) 37 | 38 | target_compile_definitions(${pupil_framework_name} PUBLIC "$<$:PUPIL_CPP>") 39 | target_compile_definitions(${pupil_framework_name} PUBLIC "$<$:PUPIL_CUDA>") 40 | -------------------------------------------------------------------------------- /framework/resource/xml/object.cpp: -------------------------------------------------------------------------------- 1 | #include "object.h" 2 | #include "pugixml.hpp" 3 | 4 | namespace Pupil::resource::xml { 5 | void GlobalManager::AddGlobalParam(std::string name, std::string value) noexcept { 6 | global_params[name] = value; 7 | } 8 | 9 | void GlobalManager::ReplaceDefaultValue(pugi::xml_node *node) noexcept { 10 | for (auto attr : node->attributes()) { 11 | std::string a_value = attr.value(); 12 | if (a_value.find('$') == std::string::npos) 13 | continue; 14 | for (auto &[p_name, p_value] : global_params) { 15 | size_t pos = 0; 16 | std::string temp_name = "$" + p_name; 17 | while ((pos = a_value.find(temp_name, pos)) != std::string::npos) { 18 | a_value.replace(pos, temp_name.length(), p_value); 19 | pos += p_value.length(); 20 | } 21 | } 22 | attr.set_value(a_value.c_str()); 23 | } 24 | } 25 | 26 | std::string Object::GetProperty(std::string_view property_name) const noexcept { 27 | for (auto &p : properties) { 28 | if (p.name.compare(property_name) == 0) { 29 | return p.value; 30 | } 31 | } 32 | return ""; 33 | } 34 | 35 | Object *Object::GetUniqueSubObject(std::string_view sub_object_name) const noexcept { 36 | for (auto so : sub_object) { 37 | if (so->obj_name.compare(sub_object_name) == 0) { 38 | return so; 39 | } 40 | } 41 | return nullptr; 42 | } 43 | 44 | std::vector Object::GetSubObjects(std::string_view sub_object_name) const noexcept { 45 | std::vector ret{}; 46 | for (auto so : sub_object) { 47 | if (so->obj_name.compare(sub_object_name) == 0) { 48 | ret.emplace_back(so); 49 | } 50 | } 51 | return ret; 52 | } 53 | 54 | std::pair Object::GetParameter(std::string_view target_name) const noexcept { 55 | for (auto so : sub_object) { 56 | if (so->var_name.compare(target_name) == 0) { 57 | return { so, "" }; 58 | } 59 | } 60 | 61 | return { nullptr, GetProperty(target_name) }; 62 | } 63 | }// namespace Pupil::resource::xml 64 | -------------------------------------------------------------------------------- /framework/world/camera.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "util/camera.h" 4 | #include "render/camera.h" 5 | #include "system/type.h" 6 | 7 | #include 8 | 9 | namespace Pupil::world { 10 | class CameraHelper { 11 | private: 12 | util::CameraDesc m_desc; 13 | Pupil::util::Camera m_camera; 14 | Pupil::optix::Camera m_optix_camera; 15 | 16 | bool m_camera_cuda_memory_dirty; 17 | CUdeviceptr m_camera_cuda_memory = 0; 18 | 19 | public: 20 | CameraHelper() noexcept; 21 | ~CameraHelper() noexcept; 22 | 23 | void Reset(const util::CameraDesc &desc) noexcept; 24 | util::CameraDesc GetDesc() const noexcept { return m_desc; } 25 | 26 | void SetFov(float fov) noexcept; 27 | void SetFovDelta(float fov_delta) noexcept; 28 | void SetAspectRatio(float aspect_ration) noexcept; 29 | void SetNearClip(float near_clip) noexcept; 30 | void SetFarClip(float far_clip) noexcept; 31 | void SetWorldTransform(util::Transform to_world) noexcept; 32 | 33 | void Rotate(float delta_x, float delta_y) noexcept; 34 | void Move(util::Float3 translation) noexcept; 35 | 36 | CUdeviceptr GetCudaMemory() noexcept; 37 | 38 | Pupil::util::Camera &GetUtilCamera() noexcept { return m_camera; } 39 | Pupil::optix::Camera &GetOptixCamera() noexcept { return m_optix_camera; } 40 | 41 | util::Mat4 GetSampleToCameraMatrix() noexcept { return m_camera.GetSampleToCameraMatrix(); } 42 | util::Mat4 GetProjectionMatrix() noexcept { return m_camera.GetProjectionMatrix(); } 43 | util::Mat4 GetToWorldMatrix() noexcept { return m_camera.GetToWorldMatrix(); } 44 | util::Mat4 GetViewMatrix() noexcept { return m_camera.GetViewMatrix(); } 45 | 46 | mat4x4 GetSampleToCameraCudaMatrix() noexcept { return ToCudaType(GetSampleToCameraMatrix()); } 47 | mat4x4 GetProjectionCudaMatrix() noexcept { return ToCudaType(GetProjectionMatrix()); } 48 | mat4x4 GetToWorldCudaMatrix() noexcept { return ToCudaType(GetToWorldMatrix()); } 49 | mat4x4 GetViewCudaMatrix() noexcept { return ToCudaType(GetViewMatrix()); } 50 | 51 | std::tuple GetCameraCoordinateSystem() const noexcept; 52 | }; 53 | }// namespace Pupil::world -------------------------------------------------------------------------------- /framework/render/material/bsdf/dielectric.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "../predefine.h" 4 | #include "cuda/texture.h" 5 | #include "../fresnel.h" 6 | 7 | namespace Pupil::optix::material { 8 | 9 | struct Dielectric { 10 | float int_ior; 11 | float ext_ior; 12 | cuda::Texture specular_reflectance; 13 | cuda::Texture specular_transmittance; 14 | 15 | struct Local { 16 | float eta; 17 | float3 specular_reflectance; 18 | float3 specular_transmittance; 19 | 20 | CUDA_HOSTDEVICE void GetBsdf(BsdfSamplingRecord &record) const noexcept { 21 | record.f = make_float3(0.f); 22 | } 23 | 24 | CUDA_HOSTDEVICE void GetPdf(BsdfSamplingRecord &record) const noexcept { 25 | record.pdf = 0.f; 26 | } 27 | 28 | CUDA_HOSTDEVICE void Sample(BsdfSamplingRecord &record) const noexcept { 29 | float cos_theta_t; 30 | float fresnel = fresnel::DielectricReflectance(eta, record.wo.z, cos_theta_t); 31 | if (record.sampler->Next() < fresnel) { 32 | record.wi = Pupil::optix::Reflect(record.wo); 33 | record.pdf = fresnel; 34 | record.f = specular_reflectance * fresnel / abs(record.wi.z); 35 | record.sampled_type = EBsdfLobeType::DeltaReflection; 36 | } else { 37 | record.wi = Pupil::optix::Refract(record.wo, cos_theta_t, eta); 38 | record.pdf = 1.f - fresnel; 39 | // ret.f = local_transmittance * (1.f - fresnel); 40 | float factor = cos_theta_t < 0.f ? 1.f / eta : eta; 41 | record.f = specular_transmittance * (1.f - fresnel) * factor * factor / abs(record.wi.z); 42 | record.sampled_type = EBsdfLobeType::DeltaTransmission; 43 | } 44 | } 45 | }; 46 | 47 | CUDA_DEVICE Local GetLocal(float2 sampled_tex) const noexcept { 48 | Local local_bsdf; 49 | local_bsdf.eta = int_ior / ext_ior; 50 | local_bsdf.specular_reflectance = specular_reflectance.Sample(sampled_tex); 51 | local_bsdf.specular_transmittance = specular_transmittance.Sample(sampled_tex); 52 | return local_bsdf; 53 | } 54 | }; 55 | 56 | }// namespace Pupil::optix::material -------------------------------------------------------------------------------- /framework/world/world.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "camera.h" 4 | #include "emitter.h" 5 | #include "ias_manager.h" 6 | #include "render_object.h" 7 | 8 | #include "resource/scene.h" 9 | #include "util/util.h" 10 | 11 | #include 12 | #include 13 | 14 | namespace Pupil { 15 | enum class EWorldEvent { 16 | CameraChange, 17 | CameraMove, 18 | CameraFovChange, 19 | CameraViewChange, 20 | RenderInstanceTransform, 21 | RenderInstanceUpdate, 22 | RenderInstanceRemove 23 | }; 24 | 25 | namespace world { 26 | class World : public util::Singleton { 27 | public: 28 | std::unique_ptr scene = nullptr; 29 | std::unique_ptr camera = nullptr; 30 | std::unique_ptr emitters = nullptr; 31 | 32 | void Init() noexcept; 33 | void Destroy() noexcept; 34 | 35 | bool LoadScene(std::filesystem::path) noexcept; 36 | bool LoadScene(resource::Scene *) noexcept; 37 | 38 | OptixTraversableHandle GetIASHandle(unsigned int gas_offset = 2, bool allow_update = false) noexcept; 39 | 40 | RenderObject *GetRenderObject(std::string_view name) const noexcept; 41 | RenderObject *GetRenderObject(size_t index) const noexcept; 42 | 43 | void RemoveRenderObject(std::string_view name) noexcept; 44 | void RemoveRenderObject(size_t index) noexcept; 45 | 46 | void UpdateRenderObject(RenderObject *) noexcept; 47 | 48 | std::vector GetRenderobjects() noexcept; 49 | 50 | void SetDirty() noexcept; 51 | bool IsDirty() const noexcept; 52 | void SetDirty(unsigned int gas_offset, bool allow_update) noexcept; 53 | bool IsDirty(unsigned int gas_offset, bool allow_update) const noexcept; 54 | 55 | util::Camera &GetUtilCamera() noexcept { return camera->GetUtilCamera(); } 56 | optix::Camera &GetOptixCamera() noexcept { return camera->GetOptixCamera(); } 57 | 58 | util::AABB GetAABB() noexcept; 59 | 60 | private: 61 | std::vector> m_ros; 62 | std::unique_ptr m_ias_manager; 63 | 64 | std::unordered_map m_ro_emitter_offset; 65 | std::unordered_map m_ro_in_scene_index; 66 | }; 67 | }// namespace world 68 | }// namespace Pupil -------------------------------------------------------------------------------- /framework/system/gui/output.hlsl: -------------------------------------------------------------------------------- 1 | struct VSInput { 2 | float3 position : POSITION; 3 | float2 texcoord : TEXCOORD; 4 | }; 5 | 6 | struct PSInput { 7 | float4 position : SV_POSITION; 8 | float2 texcoord : TEXCOORD; 9 | }; 10 | 11 | struct CanvasDesc 12 | { 13 | uint w; 14 | uint h; 15 | uint tone_mapping; 16 | uint gamma_correct; 17 | }; 18 | ConstantBuffer desc : register(b0); 19 | 20 | // Texture2D tex : register(t0); 21 | Buffer render_result; 22 | 23 | PSInput VSMain(VSInput input) { 24 | PSInput result; 25 | result.position = float4(input.position, 1.0f); 26 | result.texcoord = input.position.xy; 27 | return result; 28 | } 29 | 30 | float3 ACESToneMapping(float3 color, float adapted_lum) { 31 | const float A = 2.51f; 32 | const float B = 0.03f; 33 | const float C = 2.43f; 34 | const float D = 0.59f; 35 | const float E = 0.14f; 36 | 37 | color *= adapted_lum; 38 | return (color * (A * color + B)) / (color * (C * color + D) + E); 39 | } 40 | 41 | float3 GammaCorrection(float3 color, float gamma) { 42 | float3 ret; 43 | ret.x = pow(color.x, 1.f / gamma); 44 | ret.y = pow(color.y, 1.f / gamma); 45 | ret.z = pow(color.z, 1.f / gamma); 46 | return ret; 47 | } 48 | 49 | float4 GammaCorrection(float4 color, float gamma) { 50 | float4 ret; 51 | ret.x = pow(color.x, 1.f / gamma); 52 | ret.y = pow(color.y, 1.f / gamma); 53 | ret.z = pow(color.z, 1.f / gamma); 54 | ret.w = color.w; 55 | return ret; 56 | } 57 | 58 | float4 PSMain(PSInput input) : SV_TARGET { 59 | // texcoord: 60 | // [-1, 1]-----------------[1, 1] 61 | // | | 62 | // | | 63 | // | | 64 | // [-1,-1]-----------------[1,-1] 65 | int tex_x = (int)((input.texcoord.x + 1.f) / 2.f * desc.w); 66 | int tex_y = (int)((input.texcoord.y + 1.f) / 2.f * desc.h); 67 | 68 | float3 color = render_result.Load(tex_x + tex_y * (int)desc.w).xyz; 69 | if (desc.tone_mapping == 1) color = ACESToneMapping(color, 1.f); 70 | if (desc.gamma_correct == 1) color = GammaCorrection(color, 2.2f); 71 | // return GammaCorrection(render_result.Load(tex_x + tex_y * (int)desc.w), 2.2f); 72 | return float4(color, 1.f); 73 | } 74 | -------------------------------------------------------------------------------- /framework/render/material/bsdf/rough_conductor.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "../predefine.h" 4 | #include "cuda/texture.h" 5 | #include "../ggx.h" 6 | 7 | namespace Pupil::optix::material { 8 | 9 | struct RoughConductor { 10 | cuda::Texture alpha; 11 | cuda::Texture eta; 12 | cuda::Texture k; 13 | cuda::Texture specular_reflectance; 14 | 15 | struct Local { 16 | float alpha; 17 | float3 eta; 18 | float3 k; 19 | float3 specular_reflectance; 20 | 21 | CUDA_HOSTDEVICE void GetBsdf(BsdfSamplingRecord &record) const noexcept { 22 | record.f = make_float3(0.f); 23 | if (record.wi.z <= 0.f || record.wo.z <= 0.f) return; 24 | 25 | float3 wh = normalize(record.wi + record.wo); 26 | float3 fresnel_o = fresnel::ConductorReflectance(eta, k, dot(record.wo, wh)); 27 | record.f = specular_reflectance * 28 | ggx::D(wh, alpha) * fresnel_o * ggx::G(record.wi, record.wo, alpha) / 29 | (4.f * record.wi.z * record.wo.z); 30 | } 31 | 32 | CUDA_HOSTDEVICE void GetPdf(BsdfSamplingRecord &record) const noexcept { 33 | record.pdf = 0.f; 34 | if (record.wi.z <= 0.f || record.wo.z <= 0.f) return; 35 | float3 wh = normalize(record.wi + record.wo); 36 | wh = normalize(wh); 37 | record.pdf = ggx::Pdf(record.wo, wh, alpha) / (4.f * dot(record.wo, wh)); 38 | } 39 | 40 | CUDA_HOSTDEVICE void Sample(BsdfSamplingRecord &record) const noexcept { 41 | float2 xi = record.sampler->Next2(); 42 | record.wi = optix::Reflect(record.wo, ggx::Sample(record.wo, alpha, xi)); 43 | GetPdf(record); 44 | GetBsdf(record); 45 | record.sampled_type = EBsdfLobeType::DiffuseReflection; 46 | } 47 | }; 48 | 49 | CUDA_DEVICE Local GetLocal(float2 sampled_tex) const noexcept { 50 | Local local_bsdf; 51 | local_bsdf.alpha = alpha.Sample(sampled_tex).x; 52 | local_bsdf.eta = eta.Sample(sampled_tex); 53 | local_bsdf.k = k.Sample(sampled_tex); 54 | local_bsdf.specular_reflectance = specular_reflectance.Sample(sampled_tex); 55 | return local_bsdf; 56 | } 57 | }; 58 | 59 | }// namespace Pupil::optix::material -------------------------------------------------------------------------------- /data/static/default.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | -------------------------------------------------------------------------------- /data/static/denoised_scene.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | -------------------------------------------------------------------------------- /framework/cuda/texture.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "util/texture.h" 4 | 5 | #include "preprocessor.h" 6 | #include "cuda/vec_math.h" 7 | #include 8 | 9 | namespace Pupil::cuda { 10 | struct Texture { 11 | util::ETextureType type = util::ETextureType::RGB; 12 | union { 13 | cudaTextureObject_t bitmap CONST_STATIC_INIT(0); 14 | float3 rgb; 15 | struct { 16 | float3 patch1; 17 | float3 patch2; 18 | }; 19 | }; 20 | 21 | struct { 22 | float4 r0; 23 | float4 r1; 24 | float4 r2; 25 | float4 r3; 26 | } transform; 27 | 28 | CUDA_HOSTDEVICE Texture() noexcept {} 29 | 30 | #ifdef PUPIL_CPP 31 | float3 Sample(float2 texcoord) const noexcept { return {}; } 32 | #else 33 | CUDA_DEVICE float3 Sample(float2 texcoord) const noexcept { 34 | const float4 tex = make_float4(texcoord, 0.f, 1.f); 35 | float tex_x = dot(transform.r0, tex); 36 | float tex_y = dot(transform.r1, tex); 37 | float3 color; 38 | switch (type) { 39 | case util::ETextureType::RGB: 40 | color = rgb; 41 | break; 42 | case util::ETextureType::Checkerboard: { 43 | tex_x = tex_x - (tex_x > 0.f ? floorf(tex_x) : ceilf(tex_x)); 44 | tex_y = tex_y - (tex_y > 0.f ? floorf(tex_y) : ceilf(tex_y)); 45 | if (tex_x < 0.f) tex_x += 1.f; 46 | if (tex_y < 0.f) tex_y += 1.f; 47 | if (tex_x > 0.5f) 48 | color = tex_y > 0.5f ? patch1 : patch2; 49 | else 50 | color = tex_y > 0.5f ? patch2 : patch1; 51 | } break; 52 | case util::ETextureType::Bitmap: 53 | color = make_float3(tex2D(bitmap, tex_x, tex_y)); 54 | break; 55 | } 56 | return color; 57 | } 58 | #endif 59 | }; 60 | }// namespace Pupil::cuda 61 | 62 | #ifndef PUPIL_OPTIX 63 | #include "util/util.h" 64 | 65 | namespace Pupil::cuda { 66 | class CudaTextureManager : public util::Singleton { 67 | private: 68 | std::vector m_cuda_memory_array; 69 | 70 | [[nodiscard]] cudaTextureObject_t GetCudaTextureObject(util::Texture) noexcept; 71 | 72 | public: 73 | [[nodiscard]] cuda::Texture GetCudaTexture(util::Texture) noexcept; 74 | 75 | void Clear() noexcept; 76 | }; 77 | }// namespace Pupil::cuda 78 | #endif -------------------------------------------------------------------------------- /framework/resource/texture.cpp: -------------------------------------------------------------------------------- 1 | #include "texture.h" 2 | #include "cuda/util.h" 3 | #include "util/texture.h" 4 | #include "util/log.h" 5 | 6 | #include 7 | 8 | namespace Pupil::resource { 9 | void TextureManager::LoadTextureFromFile(std::string_view file_path) noexcept { 10 | if (m_image_datas.find(file_path) != m_image_datas.end()) return; 11 | auto image = util::BitmapTexture::Load(file_path); 12 | if (image.data != nullptr) { 13 | std::unique_ptr image_data = 14 | std::make_unique(image.w, image.h, image.data); 15 | 16 | m_image_datas.emplace(file_path, std::move(image_data)); 17 | } 18 | } 19 | 20 | util::Texture TextureManager::GetColorTexture(float r, float g, float b) noexcept { 21 | util::Texture texture{}; 22 | texture.type = util::ETextureType::RGB; 23 | texture.rgb.color.r = r; 24 | texture.rgb.color.g = g; 25 | texture.rgb.color.b = b; 26 | 27 | return texture; 28 | } 29 | 30 | util::Texture TextureManager::GetColorTexture(util::Float3 color) noexcept { 31 | util::Texture texture{}; 32 | texture.type = util::ETextureType::RGB; 33 | texture.rgb.color = color; 34 | 35 | return texture; 36 | } 37 | 38 | util::Texture TextureManager::GetCheckerboardTexture(util::Float3 patch1, util::Float3 patch2) noexcept { 39 | util::Texture texture{}; 40 | texture.type = util::ETextureType::Checkerboard; 41 | texture.checkerboard.patch1.r = patch1.r; 42 | texture.checkerboard.patch1.g = patch1.g; 43 | texture.checkerboard.patch1.b = patch1.b; 44 | texture.checkerboard.patch2.r = patch2.r; 45 | texture.checkerboard.patch2.g = patch2.g; 46 | texture.checkerboard.patch2.b = patch2.b; 47 | 48 | return texture; 49 | } 50 | 51 | util::Texture TextureManager::GetTexture(std::string_view id) noexcept { 52 | auto it = m_image_datas.find(id); 53 | if (it == m_image_datas.end()) { 54 | this->LoadTextureFromFile(id); 55 | it = m_image_datas.find(id); 56 | [[unlikely]] if (it == m_image_datas.end()) { 57 | return GetColorTexture(0.f, 0.f, 0.f); 58 | } 59 | } 60 | 61 | util::Texture texture{}; 62 | texture.type = util::ETextureType::Bitmap; 63 | texture.bitmap.data = it->second->data.get(); 64 | texture.bitmap.w = it->second->w; 65 | texture.bitmap.h = it->second->h; 66 | 67 | return texture; 68 | } 69 | 70 | void TextureManager::Clear() noexcept { 71 | m_image_datas.clear(); 72 | } 73 | }// namespace Pupil::resource -------------------------------------------------------------------------------- /framework/resource/hair/cemyuksel_hair.cpp: -------------------------------------------------------------------------------- 1 | #include "cemyuksel_hair.h" 2 | #include "util/log.h" 3 | 4 | #include 5 | 6 | namespace Pupil::resource { 7 | CyHair CyHair::LoadFromFile(std::string_view file_path) noexcept { 8 | CyHair hair; 9 | hair.header.file_info[87] = 0; 10 | 11 | std::ifstream in(file_path.data(), std::ios::binary); 12 | 13 | if (!in.is_open()) { 14 | Pupil::Log::Warn("[{}] cannot be opened.", file_path); 15 | Pupil::Log::Warn("Cem Yuksel's hair file load failed."); 16 | return hair; 17 | } 18 | 19 | in.read(reinterpret_cast(&hair.header), sizeof(hair.header)); 20 | if (std::strncmp(hair.header.magic, "HAIR", 4) != 0) { 21 | Pupil::Log::Warn("[{}] format error.", file_path); 22 | Pupil::Log::Warn("Cem Yuksel's hair file load failed."); 23 | return hair; 24 | } 25 | 26 | auto segments = std::vector(hair.header.strands_num, hair.header.default_segments_num); 27 | if (hair.header.flags & 1) {// has segements 28 | in.read(reinterpret_cast(segments.data()), hair.header.strands_num * sizeof(unsigned short)); 29 | } 30 | 31 | hair.strands_index.resize(segments.size() + 1); 32 | hair.strands_index[0] = 0; 33 | for (int i = 1; i < hair.strands_index.size(); ++i) { 34 | hair.strands_index[i] = hair.strands_index[i - 1] + 1 + segments[i - 1]; 35 | } 36 | 37 | if (hair.header.flags & (1 << 1)) {// has points 38 | hair.positions.resize(hair.header.points_num); 39 | in.read(reinterpret_cast(hair.positions.data()), hair.header.points_num * sizeof(util::Float3)); 40 | } else { 41 | Pupil::Log::Warn("[{}] format error(has no points).", file_path); 42 | Pupil::Log::Warn("Cem Yuksel's hair file load failed."); 43 | return hair; 44 | } 45 | 46 | float max_width = hair.header.default_thickness; 47 | hair.widths.resize(hair.header.points_num, hair.header.default_thickness); 48 | if (hair.header.flags & (1 << 2)) {// has thickness 49 | in.read(reinterpret_cast(hair.widths.data()), hair.header.points_num * sizeof(float)); 50 | for (float width : hair.widths) max_width = std::max(max_width, width); 51 | } 52 | 53 | // if (hair.header.flags & (1 << 3)) {// has alpha 54 | // } 55 | // if (hair.header.flags & (1 << 4)) {// has color 56 | // } 57 | 58 | hair.aabb = util::AABB{}; 59 | for (auto &pos : hair.positions) { 60 | hair.aabb.Merge(pos); 61 | } 62 | 63 | hair.aabb.max -= util::Float3(max_width); 64 | hair.aabb.max += util::Float3(max_width); 65 | 66 | return hair; 67 | } 68 | }// namespace Pupil::resource -------------------------------------------------------------------------------- /3rdparty/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | 2 | set(3rdparty_folder third-party) 3 | 4 | add_library(${3rdparty_target} SHARED dummy.cpp) 5 | set_target_properties(${3rdparty_target} PROPERTIES WINDOWS_EXPORT_ALL_SYMBOLS ON) 6 | 7 | # ######################################################## 8 | # common function 9 | function(set_ide_folder target_name folder_name) 10 | if(TARGET ${target_name}) 11 | set_target_properties(${target_name} PROPERTIES FOLDER ${folder_name}) 12 | else() 13 | message(STATUS "${target_name} does not exist.") 14 | endif() 15 | endfunction() 16 | 17 | function(add_sub_target lib_name target_name folder_name include_dir) 18 | if(NOT TARGET ${target_name}) 19 | message(STATUS "============start config ${target_name}============") 20 | add_subdirectory(${lib_name}) 21 | 22 | target_link_libraries(${3rdparty_target} PUBLIC ${target_name}) 23 | 24 | if(NOT(include_dir EQUAL "")) 25 | target_include_directories(${target_name} PUBLIC ${include_dir}) 26 | endif() 27 | 28 | set_ide_folder(${target_name} ${folder_name}) 29 | endif() 30 | endfunction() 31 | 32 | # ######################################################## 33 | set_ide_folder(${3rdparty_target} ${3rdparty_folder}) 34 | 35 | add_custom_target(file-copy COMMENT "Copy necessary files for third-party libraries") 36 | set_ide_folder(file-copy ${3rdparty_folder}) 37 | 38 | set(ASSIMP_BUILD_OBJ_IMPORTER ON CACHE INTERNAL "" FORCE) 39 | set(ASSIMP_WARNINGS_AS_ERRORS OFF CACHE INTERNAL "" FORCE) 40 | set(ASSIMP_BUILD_ASSIMP_TOOLS OFF CACHE INTERNAL "" FORCE) 41 | set(ASSIMP_BUILD_TESTS OFF CACHE INTERNAL "" FORCE) 42 | set(ASSIMP_INSTALL OFF CACHE INTERNAL "" FORCE) 43 | add_sub_target(assimp assimp ${3rdparty_folder}/assimp "") 44 | set_ide_folder(UpdateAssimpLibsDebugSymbolsAndDLLs ${3rdparty_folder}/assimp) 45 | set_ide_folder(zlibstatic ${3rdparty_folder}/assimp) 46 | 47 | target_link_libraries(${3rdparty_target} PUBLIC assimp) 48 | target_include_directories(${3rdparty_target} PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/assimp) 49 | 50 | add_sub_target(stb stb ${3rdparty_folder} ${CMAKE_CURRENT_SOURCE_DIR}/stb) 51 | add_sub_target(imgui imgui ${3rdparty_folder} ${CMAKE_CURRENT_SOURCE_DIR}/imgui/imgui) 52 | 53 | set(BUILD_TESTING OFF CACHE INTERNAL "" FORCE) 54 | add_sub_target(DirectXTK12 DirectXTK12 ${3rdparty_folder} "") 55 | 56 | set_property(GLOBAL PROPERTY CTEST_TARGETS_ADDED 1) 57 | set(BUILD_SHARED_LIBS ON CACHE INTERNAL "" FORCE) 58 | add_sub_target(pugixml pugixml-shared ${3rdparty_folder} "") 59 | 60 | add_sub_target(spdlog spdlog ${3rdparty_folder} "") 61 | 62 | add_sub_target(exr tinyexr ${3rdparty_folder}/tinyexr "") 63 | set_ide_folder(miniz ${3rdparty_folder}/tinyexr) -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.25.2) 2 | project(PupilOptixLab LANGUAGES CXX CUDA) 3 | 4 | set(CMAKE_CXX_STANDARD 20) 5 | set(CMAKE_CUDA_STANDARD 20) 6 | set(CMAKE_EXPORT_COMPILE_COMMANDS ON) 7 | 8 | set_property(GLOBAL PROPERTY USE_FOLDERS ON) 9 | 10 | if(NOT DEFINED pupil_target_output_dir) 11 | set(pupil_target_output_dir ${PROJECT_SOURCE_DIR}/target) 12 | endif() 13 | 14 | # set target folder 15 | set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${pupil_target_output_dir}/bin) 16 | set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${pupil_target_output_dir}/lib) 17 | set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${pupil_target_output_dir}/lib) 18 | 19 | list(APPEND CMAKE_MODULE_PATH ${PROJECT_SOURCE_DIR}/cmake) 20 | 21 | set(pupil_framework_root ${CMAKE_CURRENT_SOURCE_DIR}/framework CACHE INTERNAL "") 22 | set(3rdparty_target third-party-targets CACHE INTERNAL "") 23 | set(pupil_framework_name PupilOptixLab CACHE INTERNAL "") 24 | 25 | find_package(OptiX REQUIRED) 26 | 27 | set(pupil_cuda_architectures ${CMAKE_CUDA_ARCHITECTURES} CACHE INTERNAL "") 28 | set(pupil_cuda_version ${CUDA_VERSION} CACHE INTERNAL "") 29 | message(STATUS "cuda arch: ${pupil_cuda_architectures} ") 30 | message(STATUS "cuda version: ${pupil_cuda_version} ") 31 | 32 | find_program(BIN2C bin2c 33 | DOC "Path to the cuda-sdk bin2c executable.") 34 | 35 | macro(cuda_compile_and_embed output_var cuda_file) 36 | set(c_var_name ${output_var}) 37 | add_custom_command( 38 | OUTPUT ${cuda_file}.ptx 39 | DEPENDS 40 | ${cuda_file} 41 | ${pupil_framework_name} 42 | COMMAND 43 | nvcc ${CMAKE_CURRENT_LIST_DIR}/${cuda_file} 44 | -ptx -o ${CMAKE_CURRENT_BINARY_DIR}/${cuda_file}.ptx 45 | -std=c++20 --generate-line-info -use_fast_math --keep 46 | --keep-device-functions --relocatable-device-code=true # for separate .cu files 47 | -D PUPIL_OPTIX 48 | -Xcompiler "/utf-8" 49 | -I ${OptiX_ROOT_DIR}/include 50 | -I ${CMAKE_CURRENT_SOURCE_DIR} 51 | -I ${pupil_framework_root} 52 | ) 53 | set(ptx_file ${CMAKE_CURRENT_BINARY_DIR}/${cuda_file}.ptx) 54 | set(embedded_file ${CMAKE_CURRENT_BINARY_DIR}/${cuda_file}.embedded.c) 55 | add_custom_command( 56 | OUTPUT ${embedded_file} 57 | COMMAND ${BIN2C} -c --padd 0 --type char --name ${c_var_name} ${ptx_file} > ${embedded_file} 58 | DEPENDS ${ptx_file} 59 | COMMENT "compiling (and embedding ptx from) ${cuda_file} (to ${embedded_file})" 60 | ) 61 | set(${output_var} ${embedded_file}) 62 | endmacro() 63 | 64 | add_subdirectory(3rdparty) 65 | add_subdirectory(framework) 66 | 67 | option(pupil_build_example "build framework examples" ON) 68 | add_subdirectory(example) -------------------------------------------------------------------------------- /3rdparty/imgui/imgui_impl_dx12.h: -------------------------------------------------------------------------------- 1 | 2 | // custom impl dx12 bankend 3 | // modified staticSampler.Filter 4 | // from D3D12_FILTER_MIN_MAG_MIP_LINEAR to D3D12_FILTER_MIN_MAG_MIP_POINT 5 | // to support nearest image sampling 6 | 7 | // dear imgui: Renderer Backend for DirectX12 8 | // This needs to be used along with a Platform Backend (e.g. Win32) 9 | 10 | // Implemented features: 11 | // [X] Renderer: User texture binding. Use 'D3D12_GPU_DESCRIPTOR_HANDLE' as ImTextureID. Read the FAQ about ImTextureID! 12 | // [X] Renderer: Large meshes support (64k+ vertices) with 16-bit indices. 13 | // [X] Renderer: Multi-viewport support (multiple windows). Enable with 'io.ConfigFlags |= ImGuiConfigFlags_ViewportsEnable'. 14 | 15 | // Important: to compile on 32-bit systems, this backend requires code to be compiled with '#define ImTextureID ImU64'. 16 | // See imgui_impl_dx12.cpp file for details. 17 | 18 | // You can use unmodified imgui_impl_* files in your project. See examples/ folder for examples of using this. 19 | // Prefer including the entire imgui/ repository into your project (either as a copy or as a submodule), and only build the backends you need. 20 | // If you are new to Dear ImGui, read documentation from the docs/ folder + read the top of imgui.cpp. 21 | // Read online: https://github.com/ocornut/imgui/tree/master/docs 22 | 23 | #pragma once 24 | #include "imgui/imgui.h"// IMGUI_IMPL_API 25 | #include // DXGI_FORMAT 26 | 27 | struct ID3D12Device; 28 | struct ID3D12DescriptorHeap; 29 | struct ID3D12GraphicsCommandList; 30 | struct D3D12_CPU_DESCRIPTOR_HANDLE; 31 | struct D3D12_GPU_DESCRIPTOR_HANDLE; 32 | 33 | // cmd_list is the command list that the implementation will use to render imgui draw lists. 34 | // Before calling the render function, caller must prepare cmd_list by resetting it and setting the appropriate 35 | // render target and descriptor heap that contains font_srv_cpu_desc_handle/font_srv_gpu_desc_handle. 36 | // font_srv_cpu_desc_handle and font_srv_gpu_desc_handle are handles to a single SRV descriptor to use for the internal font texture. 37 | IMGUI_IMPL_API bool ImGui_ImplDX12_Init(ID3D12Device *device, int num_frames_in_flight, DXGI_FORMAT rtv_format, ID3D12DescriptorHeap *cbv_srv_heap, 38 | D3D12_CPU_DESCRIPTOR_HANDLE font_srv_cpu_desc_handle, D3D12_GPU_DESCRIPTOR_HANDLE font_srv_gpu_desc_handle); 39 | IMGUI_IMPL_API void ImGui_ImplDX12_Shutdown(); 40 | IMGUI_IMPL_API void ImGui_ImplDX12_NewFrame(); 41 | IMGUI_IMPL_API void ImGui_ImplDX12_RenderDrawData(ImDrawData *draw_data, ID3D12GraphicsCommandList *graphics_command_list); 42 | 43 | // Use if you want to reset your rendering device without losing Dear ImGui state. 44 | IMGUI_IMPL_API void ImGui_ImplDX12_InvalidateDeviceObjects(); 45 | IMGUI_IMPL_API bool ImGui_ImplDX12_CreateDeviceObjects(); 46 | -------------------------------------------------------------------------------- /.clang-format: -------------------------------------------------------------------------------- 1 | Language: Cpp 2 | BasedOnStyle: LLVM 3 | AccessModifierOffset: -4 4 | AlignAfterOpenBracket: Align 5 | AlignConsecutiveAssignments: None 6 | AlignOperands: Align 7 | ConstructorInitializerAllOnOneLineOrOnePerLine: false 8 | AllowAllArgumentsOnNextLine: true 9 | AllowAllConstructorInitializersOnNextLine: true 10 | AllowAllParametersOfDeclarationOnNextLine: true 11 | AllowShortBlocksOnASingleLine: Always 12 | AllowShortCaseLabelsOnASingleLine: true 13 | AllowShortFunctionsOnASingleLine: All 14 | AllowShortIfStatementsOnASingleLine: true 15 | AllowShortLambdasOnASingleLine: All 16 | AllowShortLoopsOnASingleLine: true 17 | AllowShortEnumsOnASingleLine: true 18 | AlwaysBreakAfterReturnType: None 19 | AlwaysBreakTemplateDeclarations: MultiLine 20 | BreakBeforeBraces: Attach 21 | Cpp11BracedListStyle: false 22 | IndentWrappedFunctionNames: false 23 | BraceWrapping: 24 | AfterCaseLabel: false 25 | AfterClass: false 26 | AfterControlStatement: Never 27 | AfterEnum: false 28 | AfterFunction: false 29 | AfterNamespace: false 30 | AfterUnion: false 31 | BeforeCatch: false 32 | BeforeElse: false 33 | BeforeLambdaBody: false 34 | IndentBraces: false 35 | SplitEmptyFunction: false 36 | SplitEmptyRecord: true 37 | SplitEmptyNamespace: false 38 | AfterObjCDeclaration: false 39 | AfterExternBlock: false 40 | AfterStruct: false 41 | BreakBeforeBinaryOperators: None 42 | BreakBeforeTernaryOperators: false 43 | BreakBeforeConceptDeclarations: true 44 | BreakBeforeInheritanceComma: false 45 | BreakAfterJavaFieldAnnotations: true 46 | BreakStringLiterals: true 47 | BreakConstructorInitializersBeforeComma: false 48 | AlwaysBreakAfterDefinitionReturnType: None 49 | AlwaysBreakBeforeMultilineStrings: false 50 | ColumnLimit: 0 51 | SortIncludes: false 52 | CompactNamespaces: true 53 | ContinuationIndentWidth: 4 54 | IndentCaseLabels: true 55 | IndentPPDirectives: None 56 | IndentExternBlock: NoIndent 57 | IndentWidth: 4 58 | KeepEmptyLinesAtTheStartOfBlocks: true 59 | MaxEmptyLinesToKeep: 1 60 | NamespaceIndentation: None 61 | ObjCSpaceAfterProperty: false 62 | ObjCSpaceBeforeProtocolList: false 63 | PointerAlignment: Right 64 | ReflowComments: false 65 | SpaceAfterCStyleCast: false 66 | SpaceAfterLogicalNot: false 67 | SpaceAfterTemplateKeyword: false 68 | SpaceBeforeAssignmentOperators: true 69 | SpaceBeforeCpp11BracedList: false 70 | SpaceBeforeCtorInitializerColon: true 71 | SpaceBeforeInheritanceColon: true 72 | SpaceBeforeParens: ControlStatements 73 | SpaceBeforeRangeBasedForLoopColon: true 74 | SpaceInEmptyParentheses: false 75 | SpacesBeforeTrailingComments: 0 76 | SpacesInAngles: false 77 | SpacesInCStyleCastParentheses: false 78 | SpacesInContainerLiterals: false 79 | SpacesInParentheses: false 80 | SpacesInSquareBrackets: false 81 | TabWidth: 4 82 | UseTab: Never 83 | IndentRequires: true 84 | SpacesInConditionalStatement: false 85 | SortUsingDeclarations: true 86 | AlignEscapedNewlines: Left -------------------------------------------------------------------------------- /framework/cuda/data_view.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "preprocessor.h" 4 | #include 5 | 6 | namespace Pupil::cuda { 7 | // cuda memory needs to be released explicitly 8 | template 9 | class ConstArrayView { 10 | private: 11 | CUdeviceptr m_data CONST_STATIC_INIT(0); 12 | size_t m_num CONST_STATIC_INIT(0); 13 | 14 | public: 15 | CUDA_HOSTDEVICE ConstArrayView() noexcept {} 16 | 17 | CUDA_HOST void SetData(CUdeviceptr cuda_data, size_t num) noexcept { 18 | m_data = cuda_data; 19 | m_num = num; 20 | } 21 | CUDA_HOSTDEVICE const T *GetDataPtr() const noexcept { return reinterpret_cast(m_data); } 22 | 23 | CUDA_HOSTDEVICE operator bool() const noexcept { return m_data != 0; } 24 | CUDA_HOSTDEVICE size_t GetNum() const noexcept { return m_num; } 25 | CUDA_HOSTDEVICE const T &operator[](unsigned int index) const noexcept { 26 | return *reinterpret_cast(m_data + index * sizeof(T)); 27 | } 28 | }; 29 | 30 | // cuda memory needs to be released explicitly 31 | template 32 | class ConstDataView { 33 | private: 34 | CUdeviceptr m_data CONST_STATIC_INIT(0); 35 | 36 | public: 37 | CUDA_HOSTDEVICE ConstDataView() noexcept {} 38 | 39 | CUDA_HOST void SetData(CUdeviceptr cuda_data) noexcept { m_data = cuda_data; } 40 | CUDA_HOSTDEVICE const T *GetDataPtr() const noexcept { return reinterpret_cast(m_data); } 41 | 42 | CUDA_HOSTDEVICE operator bool() const noexcept { return m_data != 0; } 43 | CUDA_HOSTDEVICE const T *operator->() const noexcept { 44 | return reinterpret_cast(m_data); 45 | } 46 | }; 47 | 48 | template 49 | class RWArrayView { 50 | private: 51 | CUdeviceptr m_data CONST_STATIC_INIT(0); 52 | size_t m_num CONST_STATIC_INIT(0); 53 | 54 | public: 55 | CUDA_HOSTDEVICE RWArrayView() noexcept {} 56 | 57 | CUDA_HOST void SetData(CUdeviceptr cuda_data, size_t num) noexcept { 58 | m_data = cuda_data; 59 | m_num = num; 60 | } 61 | CUDA_HOSTDEVICE T *GetDataPtr() const noexcept { return reinterpret_cast(m_data); } 62 | 63 | CUDA_HOSTDEVICE operator bool() const noexcept { return m_data != 0; } 64 | CUDA_HOSTDEVICE size_t GetNum() const noexcept { return m_num; } 65 | CUDA_HOSTDEVICE T &operator[](unsigned int index) const noexcept { 66 | return *reinterpret_cast(m_data + index * sizeof(T)); 67 | } 68 | }; 69 | 70 | template 71 | class RWDataView { 72 | private: 73 | CUdeviceptr m_data CONST_STATIC_INIT(0); 74 | 75 | public: 76 | CUDA_HOSTDEVICE RWDataView() noexcept {} 77 | 78 | CUDA_HOST void SetData(CUdeviceptr cuda_data) noexcept { m_data = cuda_data; } 79 | CUDA_HOSTDEVICE T *GetDataPtr() const noexcept { return reinterpret_cast(m_data); } 80 | 81 | CUDA_HOSTDEVICE operator bool() const noexcept { return m_data != 0; } 82 | CUDA_HOSTDEVICE T *operator->() const noexcept { 83 | return reinterpret_cast(m_data); 84 | } 85 | }; 86 | }// namespace Pupil::cuda -------------------------------------------------------------------------------- /framework/optix/pass.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "pipeline.h" 4 | #include "sbt.h" 5 | 6 | #include 7 | 8 | namespace Pupil::optix { 9 | template 10 | class Pass { 11 | private: 12 | std::unique_ptr> m_sbt = nullptr; 13 | std::unique_ptr m_pipeline = nullptr; 14 | 15 | void *m_param_cuda_memory = nullptr; 16 | 17 | const OptixDeviceContext m_device_context; 18 | const cudaStream_t m_cuda_stream; 19 | bool m_use_inner_param = true; 20 | 21 | public: 22 | Pass(const OptixDeviceContext device_context, const cudaStream_t cuda_stream, const bool use_inner_param = true) noexcept 23 | : m_device_context(device_context), m_cuda_stream(cuda_stream), m_use_inner_param(use_inner_param) {} 24 | 25 | ~Pass() noexcept { 26 | if (m_use_inner_param) 27 | CUDA_FREE(m_param_cuda_memory); 28 | m_sbt.reset(); 29 | m_pipeline.reset(); 30 | } 31 | 32 | void InitPipeline(const PipelineDesc &desc) noexcept { 33 | m_pipeline = std::make_unique(desc); 34 | } 35 | 36 | void InitSBT(const SBTDesc &desc) noexcept { 37 | m_sbt = std::make_unique>(desc, m_pipeline.get()); 38 | } 39 | 40 | SBT *GetSBT() noexcept { return m_sbt.get(); } 41 | 42 | void SetExternalLaunchParamPtr(CUdeviceptr cuda_memory) noexcept { 43 | m_param_cuda_memory = reinterpret_cast(cuda_memory); 44 | m_use_inner_param = false; 45 | } 46 | 47 | CUdeviceptr GetLaunchParamPtr() noexcept { 48 | if (m_use_inner_param) { 49 | if (m_param_cuda_memory == nullptr) 50 | CUDA_CHECK(cudaMalloc(&m_param_cuda_memory, sizeof(LaunchParamT))); 51 | } else { 52 | assert(m_param_cuda_memory != nullptr && "CUDA memory for OptiX launch parameters should be allocated"); 53 | } 54 | 55 | return reinterpret_cast(m_param_cuda_memory); 56 | } 57 | 58 | void Run(CUdeviceptr param_cuda_memory, const unsigned int launch_w, const unsigned int launch_h) noexcept { 59 | OPTIX_CHECK(optixLaunch( 60 | *m_pipeline.get(), 61 | m_cuda_stream, 62 | param_cuda_memory, 63 | sizeof(LaunchParamT), 64 | &m_sbt->sbt, 65 | launch_w, 66 | launch_h, 67 | 1// launch depth 68 | )); 69 | } 70 | 71 | void Run(const LaunchParamT ¶ms, const unsigned int launch_w, const unsigned int launch_h) noexcept { 72 | auto param_cuda_memory = GetLaunchParamPtr(); 73 | 74 | CUDA_CHECK(cudaMemcpyAsync( 75 | reinterpret_cast(param_cuda_memory), 76 | ¶ms, sizeof(LaunchParamT), 77 | cudaMemcpyHostToDevice, m_cuda_stream)); 78 | 79 | OPTIX_CHECK(optixLaunch( 80 | *m_pipeline.get(), 81 | m_cuda_stream, 82 | param_cuda_memory, 83 | sizeof(LaunchParamT), 84 | &m_sbt->sbt, 85 | launch_w, 86 | launch_h, 87 | 1// launch depth 88 | )); 89 | } 90 | 91 | void Synchronize() noexcept { 92 | CUDA_CHECK(cudaStreamSynchronize(m_cuda_stream)); 93 | } 94 | }; 95 | }// namespace Pupil::optix -------------------------------------------------------------------------------- /example/cuda_test/pass.cpp: -------------------------------------------------------------------------------- 1 | #include "kernel.h" 2 | 3 | #include "pass.h" 4 | #include "system/gui/gui.h" 5 | #include "cuda/data_view.h" 6 | #include "cuda/kernel.h" 7 | 8 | using namespace Pupil; 9 | 10 | unsigned int m_frame_cnt = 0; 11 | cuda::RWArrayView m_output; 12 | 13 | CudaPass::CudaPass(std::string_view name) noexcept 14 | : Pass(name) { 15 | m_stream = std::make_unique(); 16 | 17 | auto buf_mngr = util::Singleton::instance(); 18 | BufferDesc default_frame_buffer_desc{ 19 | .name = buf_mngr->DEFAULT_FINAL_RESULT_BUFFER_NAME.data(), 20 | .flag = (util::Singleton::instance()->IsInitialized() ? 21 | EBufferFlag::AllowDisplay : 22 | EBufferFlag::None), 23 | .width = static_cast(512), 24 | .height = static_cast(512), 25 | .stride_in_byte = sizeof(float) * 4 26 | }; 27 | buf_mngr->AllocBuffer(default_frame_buffer_desc); 28 | 29 | BufferDesc test_buffer_desc{ 30 | .name = "test buffer", 31 | .flag = EBufferFlag::AllowDisplay, 32 | .width = static_cast(1080), 33 | .height = static_cast(720), 34 | .stride_in_byte = sizeof(float) * 4 35 | }; 36 | buf_mngr->AllocBuffer(test_buffer_desc); 37 | 38 | BufferDesc test_buffer_desc2{ 39 | .name = "test buffer2", 40 | .flag = EBufferFlag::AllowDisplay, 41 | .width = static_cast(5), 42 | .height = static_cast(5), 43 | .stride_in_byte = sizeof(float) * 4 44 | }; 45 | buf_mngr->AllocBuffer(test_buffer_desc2); 46 | } 47 | 48 | void CudaPass::OnRun() noexcept { 49 | m_frame_cnt++; 50 | auto buf_mngr = util::Singleton::instance(); 51 | { 52 | auto frame_buffer = buf_mngr->GetBuffer(BufferManager::DEFAULT_FINAL_RESULT_BUFFER_NAME); 53 | m_output.SetData(frame_buffer->cuda_ptr, frame_buffer->desc.height * frame_buffer->desc.width); 54 | SetColor(uint2{ frame_buffer->desc.width, frame_buffer->desc.height }, m_frame_cnt, m_output, m_stream.get()); 55 | } 56 | { 57 | auto frame_buffer = buf_mngr->GetBuffer("test buffer"); 58 | m_output.SetData(frame_buffer->cuda_ptr, frame_buffer->desc.height * frame_buffer->desc.width); 59 | SetColor(uint2{ frame_buffer->desc.width, frame_buffer->desc.height }, m_frame_cnt, m_output, m_stream.get()); 60 | } 61 | { 62 | auto frame_buffer = buf_mngr->GetBuffer("test buffer2"); 63 | m_output.SetData(frame_buffer->cuda_ptr, frame_buffer->desc.height * frame_buffer->desc.width); 64 | SetColor(uint2{ frame_buffer->desc.width, frame_buffer->desc.height }, m_frame_cnt, m_output, m_stream.get()); 65 | } 66 | // FIXME: To be mixed compiling 67 | // Pupil::cuda::LaunchKernel2D( 68 | // uint2{ 512, 512 }, [out = m_output, f = m_frame_cnt] __device__(uint2 pixel_id, uint2 size) { 69 | // float3 color = 0.5f * make_float3( 70 | // cos(((float)pixel_id.x) / size.x + f / 100.f), 71 | // sin(((float)pixel_id.y) / size.y + f / 100.f), 72 | // 0.7f) + 73 | // 0.5f; 74 | // out[pixel_id.x + pixel_id.y * size.x] = make_float4(color, 1.f); 75 | // }, 76 | // m_stream.get()); 77 | 78 | m_stream->Synchronize(); 79 | } -------------------------------------------------------------------------------- /framework/resource/material.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #ifndef PUPIL_OPTIX 3 | 4 | #include "util/texture.h" 5 | #include "render/material/predefine.h" 6 | #include "optix/pipeline.h" 7 | #include "optix/module.h" 8 | 9 | namespace Pupil::resource { 10 | class Scene; 11 | 12 | namespace xml { 13 | struct Object; 14 | } 15 | 16 | struct Diffuse { 17 | util::Texture reflectance; 18 | }; 19 | 20 | struct Dielectric { 21 | float int_ior; 22 | float ext_ior; 23 | util::Texture specular_reflectance; 24 | util::Texture specular_transmittance; 25 | }; 26 | 27 | struct RoughDielectric { 28 | float int_ior; 29 | float ext_ior; 30 | util::Texture alpha; 31 | util::Texture specular_reflectance; 32 | util::Texture specular_transmittance; 33 | }; 34 | 35 | struct Conductor { 36 | util::Texture eta; 37 | util::Texture k; 38 | util::Texture specular_reflectance; 39 | }; 40 | 41 | struct RoughConductor { 42 | util::Texture alpha; 43 | util::Texture eta; 44 | util::Texture k; 45 | util::Texture specular_reflectance; 46 | }; 47 | 48 | struct Plastic { 49 | float int_ior; 50 | float ext_ior; 51 | bool nonlinear; 52 | util::Texture diffuse_reflectance; 53 | util::Texture specular_reflectance; 54 | }; 55 | 56 | struct RoughPlastic { 57 | float int_ior; 58 | float ext_ior; 59 | bool nonlinear; 60 | util::Texture alpha; 61 | util::Texture diffuse_reflectance; 62 | util::Texture specular_reflectance; 63 | }; 64 | 65 | // struct PrincipledBSDF { 66 | // }; 67 | 68 | struct Material { 69 | EMatType type = EMatType::Unknown; 70 | bool twosided = false; 71 | 72 | union { 73 | Diffuse diffuse{}; 74 | Dielectric dielectric; 75 | RoughDielectric rough_dielectric; 76 | Conductor conductor; 77 | RoughConductor rough_conductor; 78 | Plastic plastic; 79 | RoughPlastic rough_plastic; 80 | }; 81 | 82 | Material() noexcept {} 83 | }; 84 | 85 | Material LoadMaterialFromXml(const Pupil::resource::xml::Object *, Pupil::resource::Scene *) noexcept; 86 | 87 | inline auto GetMaterialProgramDesc() noexcept { 88 | auto module_mngr = util::Singleton::instance(); 89 | auto mat_module_ptr = module_mngr->GetModule(optix::EModuleBuiltinType::Material); 90 | return std::array{ 91 | #define _PUPIL_TO_STRING(var) #var 92 | #define PUPIL_TO_STRING(var) _PUPIL_TO_STRING(var) 93 | #define PUPIL_MATERIAL_ATTR_DEFINE(attr) \ 94 | optix::CallableProgramDesc{ \ 95 | .module_ptr = mat_module_ptr, \ 96 | .cc_entry = nullptr, \ 97 | .dc_entry = PUPIL_TO_STRING(PUPIL_MAT_SAMPLE_CALL(attr)), \ 98 | }, \ 99 | optix::CallableProgramDesc{ \ 100 | .module_ptr = mat_module_ptr, \ 101 | .cc_entry = nullptr, \ 102 | .dc_entry = PUPIL_TO_STRING(PUPIL_MAT_EVAL_CALL(attr)), \ 103 | }, 104 | #include "decl/material_decl.inl" 105 | #undef PUPIL_MATERIAL_ATTR_DEFINE 106 | #undef PUPIL_TO_STRING 107 | #undef _PUPIL_TO_STRING 108 | }; 109 | } 110 | }// namespace Pupil::resource 111 | 112 | #endif -------------------------------------------------------------------------------- /framework/world/render_object.cpp: -------------------------------------------------------------------------------- 1 | #include "render_object.h" 2 | #include "ias_manager.h" 3 | #include "cuda/util.h" 4 | 5 | #include "util/event.h" 6 | #include "world.h" 7 | 8 | namespace Pupil::world { 9 | 10 | RenderObject::RenderObject(const resource::ShapeInstance &ins, unsigned int v_mask) noexcept 11 | : name(ins.name), transform(ins.transform), visibility_mask(v_mask) { 12 | Reset(ins.shape); 13 | is_emitter = ins.is_emitter; 14 | 15 | mat.LoadMaterial(ins.mat); 16 | } 17 | 18 | void RenderObject::Reset(const resource::Shape *shape) noexcept { 19 | auto gas_mngr = util::Singleton::instance(); 20 | auto [new_gas, is_reuse] = gas_mngr->RefGAS(shape); 21 | if (is_reuse) new_gas->Create(); 22 | 23 | gas_mngr->Release(gas); 24 | gas = new_gas; 25 | 26 | shape_id = shape->id; 27 | aabb = shape->aabb; 28 | aabb.Transform(transform); 29 | 30 | if (shape->type == resource::EShapeType::_sphere) { 31 | geo.type = optix::Geometry::EType::Sphere; 32 | geo.sphere.center = make_float3(0.f); 33 | geo.sphere.radius = 1.f; 34 | geo.sphere.flip_normal = shape->sphere.flip_normals; 35 | sub_emitters_num = 1; 36 | } else if (shape->type == resource::EShapeType::_hair) { 37 | if ((shape->hair.flags & 0b11) == 0) 38 | geo.type = optix::Geometry::EType::LinearBSpline; 39 | else if ((shape->hair.flags & 0b11) == 1) 40 | geo.type = optix::Geometry::EType::QuadraticBSpline; 41 | else if ((shape->hair.flags & 0b11) == 2) 42 | geo.type = optix::Geometry::EType::CubicBSpline; 43 | else 44 | geo.type = optix::Geometry::EType::CatromSpline; 45 | 46 | auto device_memory = 47 | util::Singleton::instance()->GetMeshDeviceMemory(shape); 48 | geo.curve.positions.SetData(device_memory.position, shape->hair.point_num * 3); 49 | geo.curve.indices.SetData(device_memory.index, shape->hair.segments_num); 50 | } else { 51 | geo.type = optix::Geometry::EType::TriMesh; 52 | auto device_memory = 53 | util::Singleton::instance()->GetMeshDeviceMemory(shape); 54 | uint32_t vertex_num = shape->mesh.vertex_num; 55 | uint32_t face_num = shape->mesh.face_num; 56 | geo.tri_mesh.flip_normals = shape->mesh.flip_normals; 57 | geo.tri_mesh.flip_tex_coords = shape->mesh.flip_tex_coords; 58 | geo.tri_mesh.positions.SetData(device_memory.position, vertex_num * 3); 59 | geo.tri_mesh.normals.SetData(device_memory.normal, vertex_num * 3); 60 | geo.tri_mesh.texcoords.SetData(device_memory.texcoord, vertex_num * 2); 61 | geo.tri_mesh.indices.SetData(device_memory.index, face_num * 3); 62 | sub_emitters_num = face_num; 63 | } 64 | EventDispatcher(this); 65 | } 66 | 67 | RenderObject::~RenderObject() noexcept { 68 | // util::Singleton::instance()->Release(gas->ref_shape); 69 | util::Singleton::instance()->Release(gas); 70 | } 71 | 72 | void RenderObject::UpdateTransform(const util::Transform &new_transform) noexcept { 73 | transform = new_transform; 74 | EventDispatcher(this); 75 | } 76 | 77 | void RenderObject::ApplyTransform(const util::Transform &new_transform) noexcept { 78 | transform.matrix = new_transform.matrix * transform.matrix; 79 | EventDispatcher(this); 80 | } 81 | }// namespace Pupil::world -------------------------------------------------------------------------------- /framework/render/material/fresnel.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "cuda/preprocessor.h" 4 | #include "cuda/vec_math.h" 5 | 6 | namespace Pupil::optix::material::fresnel { 7 | CUDA_INLINE CUDA_HOSTDEVICE float DielectricReflectance(float eta, float cos_theta_i, float &cos_theta_t) { 8 | float scale = cos_theta_i > 0.f ? 1.f / eta : eta; 9 | float cos_theta_t2 = 1.f - (1.f - cos_theta_i * cos_theta_i) * (scale * scale); 10 | 11 | if (cos_theta_t2 <= 0.0f) { 12 | cos_theta_t = 0.0f; 13 | return 1.0f; 14 | } 15 | 16 | float o_cos_theta_i = cos_theta_i; 17 | cos_theta_i = abs(cos_theta_i); 18 | cos_theta_t = sqrtf(fmaxf(0.f, cos_theta_t2)); 19 | 20 | float rs = (cos_theta_i - eta * cos_theta_t) / (cos_theta_i + eta * cos_theta_t); 21 | float rp = (eta * cos_theta_i - cos_theta_t) / (eta * cos_theta_i + cos_theta_t); 22 | 23 | cos_theta_t = o_cos_theta_i > 0.f ? -cos_theta_t : cos_theta_t; 24 | return 0.5f * (rs * rs + rp * rp); 25 | } 26 | CUDA_INLINE CUDA_HOSTDEVICE float DielectricReflectance(float eta, float cos_theta_i) { 27 | float cos_theta_t; 28 | return DielectricReflectance(eta, cos_theta_i, cos_theta_t); 29 | } 30 | 31 | CUDA_INLINE CUDA_HOSTDEVICE float ConductorReflectance(float eta, float k, float cos_theta_i) { 32 | float cos_theta_i2 = cos_theta_i * cos_theta_i; 33 | float sin_theta_i2 = 1.f - cos_theta_i2; 34 | float sin_theta_i4 = sin_theta_i2 * sin_theta_i2; 35 | 36 | float t1 = eta * eta - k * k - sin_theta_i2; 37 | float a2pb2 = sqrtf(fmaxf(0.f, t1 * t1 + 4.f * k * k * eta * eta)); 38 | float a = sqrtf(fmaxf(0.f, 0.5f * (a2pb2 + t1))); 39 | 40 | float term1 = a2pb2 + cos_theta_i2; 41 | float term2 = 2.f * a * cos_theta_i; 42 | float rs2 = (term1 - term2) / (term1 + term2); 43 | 44 | float term3 = a2pb2 * cos_theta_i2 + sin_theta_i4; 45 | float term4 = term2 * sin_theta_i2; 46 | float rp2 = rs2 * (term3 - term4) / (term3 + term4); 47 | 48 | return 0.5f * (rp2 + rs2); 49 | } 50 | 51 | CUDA_INLINE CUDA_HOSTDEVICE float3 ConductorReflectance(const float3 eta, const float3 k, float cos_theta_i) { 52 | return make_float3( 53 | ConductorReflectance(eta.x, k.x, cos_theta_i), 54 | ConductorReflectance(eta.y, k.y, cos_theta_i), 55 | ConductorReflectance(eta.z, k.z, cos_theta_i)); 56 | } 57 | 58 | CUDA_INLINE CUDA_HOSTDEVICE float DiffuseReflectance(float eta) { 59 | if (eta < 1) { 60 | /* Fit by Egan and Hilgeman (1973). Works 61 | reasonably well for "normal" IOR values (<2). 62 | 63 | Max rel. error in 1.0 - 1.5 : 0.1% 64 | Max rel. error in 1.5 - 2 : 0.6% 65 | Max rel. error in 2.0 - 5 : 9.5% 66 | */ 67 | return -1.4399f * (eta * eta) + 0.7099f * eta + 0.6681f + 0.0636f / eta; 68 | } else { 69 | /* Fit by d'Eon and Irving (2011) 70 | * 71 | * Maintains a good accuracy even for 72 | * unrealistic IOR values. 73 | * 74 | * Max rel. error in 1.0 - 2.0 : 0.1% 75 | * Max rel. error in 2.0 - 10.0 : 0.2% 76 | */ 77 | float inv_eta = 1.0f / eta; 78 | float inv_eta2 = inv_eta * inv_eta; 79 | float inv_eta3 = inv_eta2 * inv_eta; 80 | float inv_eta4 = inv_eta3 * inv_eta; 81 | float inv_eta5 = inv_eta4 * inv_eta; 82 | 83 | return 0.919317f - 3.4793f * inv_eta + 6.75335f * inv_eta2 - 7.80989f * inv_eta3 + 4.98554f * inv_eta4 - 1.36881f * inv_eta5; 84 | } 85 | } 86 | }// namespace Pupil::optix::material::fresnel -------------------------------------------------------------------------------- /framework/world/camera.cpp: -------------------------------------------------------------------------------- 1 | #include "camera.h" 2 | 3 | #include "cuda/util.h" 4 | 5 | namespace Pupil::world { 6 | CameraHelper::CameraHelper() noexcept { 7 | } 8 | 9 | CameraHelper::~CameraHelper() noexcept { 10 | CUDA_FREE(m_camera_cuda_memory); 11 | } 12 | 13 | void CameraHelper::Reset(const util::CameraDesc &desc) noexcept { 14 | m_camera.SetProjectionFactor(desc.fov_y, desc.aspect_ratio, desc.near_clip, desc.far_clip); 15 | m_camera.SetWorldTransform(desc.to_world); 16 | m_desc = desc; 17 | m_camera_cuda_memory_dirty = true; 18 | } 19 | 20 | void CameraHelper::SetFov(float fov) noexcept { 21 | m_desc.fov_y = fov; 22 | if (m_desc.fov_y < 0.012f) 23 | m_desc.fov_y = 0.012f; 24 | else if (m_desc.fov_y > 180.f) 25 | m_desc.fov_y = 180.f; 26 | m_camera.SetFov(m_desc.fov_y); 27 | m_camera_cuda_memory_dirty = true; 28 | } 29 | void CameraHelper::SetFovDelta(float fov_delta) noexcept { 30 | m_desc.fov_y += fov_delta; 31 | // Minimum angle is 0.00001 * 2 * 180 / pi (XMMatrixPerspectiveFovRH) 32 | if (m_desc.fov_y < 0.012f) 33 | m_desc.fov_y = 0.012f; 34 | else if (m_desc.fov_y > 180.f) 35 | m_desc.fov_y = 180.f; 36 | m_camera.SetFov(m_desc.fov_y); 37 | m_camera_cuda_memory_dirty = true; 38 | } 39 | void CameraHelper::SetAspectRatio(float aspect_ration) noexcept { 40 | m_desc.aspect_ratio = aspect_ration; 41 | m_camera.SetProjectionFactor( 42 | m_desc.fov_y, m_desc.aspect_ratio, m_desc.near_clip, m_desc.far_clip); 43 | m_camera_cuda_memory_dirty = true; 44 | } 45 | void CameraHelper::SetNearClip(float near_clip) noexcept { 46 | m_desc.near_clip = near_clip; 47 | m_camera.SetProjectionFactor( 48 | m_desc.fov_y, m_desc.aspect_ratio, m_desc.near_clip, m_desc.far_clip); 49 | m_camera_cuda_memory_dirty = true; 50 | } 51 | void CameraHelper::SetFarClip(float far_clip) noexcept { 52 | m_desc.far_clip = far_clip; 53 | m_camera.SetProjectionFactor( 54 | m_desc.fov_y, m_desc.aspect_ratio, m_desc.near_clip, m_desc.far_clip); 55 | m_camera_cuda_memory_dirty = true; 56 | } 57 | void CameraHelper::SetWorldTransform(util::Transform to_world) noexcept { 58 | m_desc.to_world = to_world; 59 | m_camera.SetWorldTransform(to_world); 60 | m_camera_cuda_memory_dirty = true; 61 | } 62 | 63 | void CameraHelper::Rotate(float delta_x, float delta_y) noexcept { 64 | m_camera.Rotate(delta_x, delta_y); 65 | m_camera_cuda_memory_dirty = true; 66 | } 67 | void CameraHelper::Move(util::Float3 translation) noexcept { 68 | m_camera.Move(translation); 69 | m_camera_cuda_memory_dirty = true; 70 | } 71 | 72 | CUdeviceptr CameraHelper::GetCudaMemory() noexcept { 73 | if (m_camera_cuda_memory_dirty) { 74 | auto sample_to_camera = m_camera.GetSampleToCameraMatrix(); 75 | auto camera_to_world = m_camera.GetToWorldMatrix(); 76 | 77 | m_desc.to_world.matrix = camera_to_world; 78 | 79 | m_optix_camera.sample_to_camera = ToCudaType(sample_to_camera); 80 | m_optix_camera.camera_to_world = ToCudaType(camera_to_world); 81 | } 82 | 83 | if (m_camera_cuda_memory == 0) { 84 | m_camera_cuda_memory = cuda::CudaMemcpyToDevice(&m_optix_camera, sizeof(m_optix_camera)); 85 | } else if (m_camera_cuda_memory_dirty) { 86 | cuda::CudaMemcpyToDevice(m_camera_cuda_memory, &m_optix_camera, sizeof(m_optix_camera)); 87 | } 88 | 89 | m_camera_cuda_memory_dirty = false; 90 | 91 | return m_camera_cuda_memory; 92 | } 93 | 94 | std::tuple CameraHelper::GetCameraCoordinateSystem() const noexcept { 95 | return m_camera.GetCameraCoordinateSystem(); 96 | } 97 | }// namespace Pupil::world -------------------------------------------------------------------------------- /cmake/FindOptiX.cmake: -------------------------------------------------------------------------------- 1 | if(DEFINED ENV{OptiX_INSTALL_DIR}) 2 | message(STATUS "Detected the OptiX_INSTALL_DIR env variable (pointing to $ENV{OptiX_INSTALL_DIR}; going to use this for finding optix.h") 3 | find_path(OptiX_ROOT_DIR NAMES include/optix.h PATHS $ENV{OptiX_INSTALL_DIR}) 4 | elseif(WIN32) 5 | find_path(OptiX_ROOT_DIR 6 | NAMES include/optix.h 7 | PATHS "C:/ProgramData/NVIDIA Corporation/OptiX SDK 7.5.0" 8 | ) 9 | else() 10 | find_path(OptiX_ROOT_DIR NAMES include/optix.h) 11 | endif() 12 | 13 | include(FindPackageHandleStandardArgs) 14 | find_package_handle_standard_args(OptiX 15 | FAIL_MESSAGE "Failed to find OptiX install dir. Please instal OptiX or set OptiX_INSTALL_DIR env variable." 16 | REQUIRED_VARS OptiX_ROOT_DIR 17 | ) 18 | 19 | add_library(OptiX INTERFACE IMPORTED) 20 | target_include_directories(OptiX INTERFACE ${OptiX_ROOT_DIR}/include) 21 | 22 | enable_language(CUDA) 23 | find_package(CUDAToolkit 12.0 REQUIRED) 24 | 25 | # Adapted from the CMake source code at https://github.com/Kitware/CMake/blob/master/Modules/FindCUDA/select_compute_arch.cmake 26 | # Simplified to return a semicolon-separated list of the compute capabilities of installed devices 27 | function(TCNN_AUTODETECT_CUDA_ARCHITECTURES OUT_VARIABLE) 28 | if(NOT TCNN_AUTODETECT_CUDA_ARCHITECTURES_OUTPUT) 29 | if(CMAKE_CUDA_COMPILER_LOADED) # CUDA as a language 30 | set(file "${PROJECT_BINARY_DIR}/detect_tcnn_cuda_architectures.cu") 31 | else() 32 | set(file "${PROJECT_BINARY_DIR}/detect_tcnn_cuda_architectures.cpp") 33 | endif() 34 | 35 | file(WRITE ${file} "" 36 | "#include \n" 37 | "#include \n" 38 | "int main() {\n" 39 | " int count = 0;\n" 40 | " if (cudaSuccess != cudaGetDeviceCount(&count)) return -1;\n" 41 | " if (count == 0) return -1;\n" 42 | " for (int device = 0; device < count; ++device) {\n" 43 | " cudaDeviceProp prop;\n" 44 | " if (cudaSuccess == cudaGetDeviceProperties(&prop, device)) {\n" 45 | " std::printf(\"%d%d\", prop.major, prop.minor);\n" 46 | " if (device < count - 1) std::printf(\";\");\n" 47 | " }\n" 48 | " }\n" 49 | " return 0;\n" 50 | "}\n" 51 | ) 52 | 53 | if(CMAKE_CUDA_COMPILER_LOADED) # CUDA as a language 54 | try_run(run_result compile_result ${PROJECT_BINARY_DIR} ${file} RUN_OUTPUT_VARIABLE compute_capabilities) 55 | else() 56 | try_run( 57 | run_result compile_result ${PROJECT_BINARY_DIR} ${file} 58 | CMAKE_FLAGS "-DINCLUDE_DIRECTORIES=${CUDA_INCLUDE_DIRS}" 59 | LINK_LIBRARIES ${CUDA_LIBRARIES} 60 | RUN_OUTPUT_VARIABLE compute_capabilities 61 | ) 62 | endif() 63 | 64 | if(run_result EQUAL 0) 65 | # If the user has multiple GPUs with the same compute capability installed, list that capability only once. 66 | list(REMOVE_DUPLICATES compute_capabilities) 67 | set(TCNN_AUTODETECT_CUDA_ARCHITECTURES_OUTPUT ${compute_capabilities} CACHE INTERNAL "Returned GPU architectures from detect_gpus tool" FORCE) 68 | endif() 69 | endif() 70 | 71 | if(NOT TCNN_AUTODETECT_CUDA_ARCHITECTURES_OUTPUT) 72 | message(STATUS "Automatic GPU detection failed. Building for Turing and Ampere as a best guess.") 73 | set(${OUT_VARIABLE} "75;86" PARENT_SCOPE) 74 | else() 75 | set(${OUT_VARIABLE} ${TCNN_AUTODETECT_CUDA_ARCHITECTURES_OUTPUT} PARENT_SCOPE) 76 | endif() 77 | endfunction() 78 | 79 | message(STATUS "Obtained CUDA architectures automatically from installed GPUs") 80 | TCNN_AUTODETECT_CUDA_ARCHITECTURES(CMAKE_CUDA_ARCHITECTURES) 81 | -------------------------------------------------------------------------------- /framework/render/emitter/env.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "cuda/data_view.h" 4 | 5 | namespace Pupil::optix { 6 | struct EnvMapEmitter { 7 | cuda::Texture radiance; 8 | float3 center; 9 | uint2 map_size; 10 | cuda::ConstArrayView row_cdf; 11 | cuda::ConstArrayView col_cdf; 12 | cuda::ConstArrayView row_weight; 13 | 14 | struct { 15 | float3 r0; 16 | float3 r1; 17 | float3 r2; 18 | } to_world, to_local; 19 | 20 | float normalization; 21 | float scale; 22 | 23 | CUDA_DEVICE void SampleDirect(EmitterSampleRecord &ret, LocalGeometry &hit_geo, float2 xi) const noexcept { 24 | unsigned int row_index = 0; 25 | for (; row_index < row_cdf.GetNum() - 1; ++row_index) { 26 | if (xi.x <= row_cdf[row_index]) break; 27 | } 28 | unsigned int col_index = 0; 29 | for (int i = row_index * (map_size.x + 1); col_index < map_size.x - 1; ++i, ++col_index) { 30 | if (xi.y <= col_cdf[i]) break; 31 | } 32 | 33 | const float phi = col_index * M_PIf * 2.f / map_size.x; 34 | const float theta = row_index * M_PIf / map_size.y; 35 | 36 | const auto local_wi = make_float3(sin(theta) * sinf(M_PIf - phi), cos(theta), sin(theta) * cosf(M_PIf - phi)); 37 | 38 | ret.wi = make_float3(dot(to_world.r0, local_wi), dot(to_world.r1, local_wi), dot(to_world.r2, local_wi)); 39 | ret.distance = Pupil::optix::MAX_DISTANCE; 40 | const float2 tex = make_float2(phi * 0.5f * M_1_PIf, theta * M_1_PIf); 41 | ret.radiance = radiance.Sample(tex) * scale; 42 | ret.is_delta = false; 43 | 44 | ret.pdf = Pupil::optix::GetLuminance(ret.radiance) * row_weight[row_index] * normalization / 45 | max(1e-4f, abs(sin(theta))); 46 | if (ret.pdf < 0.f) ret.pdf = 0.f; 47 | ret.pos = hit_geo.position + ret.wi * ret.distance; 48 | ret.normal = normalize(center - ret.pos); 49 | } 50 | 51 | CUDA_DEVICE void Eval(EmitEvalRecord &ret, LocalGeometry &emit_local_geo, float3 scatter_pos) const noexcept { 52 | float3 dir = normalize(emit_local_geo.position - scatter_pos); 53 | dir = make_float3(dot(to_local.r0, dir), dot(to_local.r1, dir), dot(to_local.r2, dir)); 54 | const float phi = M_PIf - atan2(dir.x, dir.z); 55 | const float theta = acos(dir.y); 56 | const float2 tex = make_float2(phi * 0.5f * M_1_PIf, theta * M_1_PIf); 57 | 58 | unsigned int row_index = static_cast(tex.y * map_size.y); 59 | row_index = clamp(row_index, 0u, map_size.y - 2u); 60 | ret.radiance = radiance.Sample(tex) * scale; 61 | ret.pdf = optix::GetLuminance(ret.radiance) * 62 | optix::Lerp(row_weight[row_index], row_weight[row_index + 1], tex.y * map_size.y - 1.f * row_index) * 63 | normalization / max(1e-4f, abs(sin(theta))); 64 | } 65 | }; 66 | 67 | struct ConstEnvEmitter { 68 | float3 color; 69 | float3 center; 70 | CUDA_DEVICE void SampleDirect(EmitterSampleRecord &ret, LocalGeometry &hit_geo, float2 xi) const noexcept { 71 | float3 local_wi = Pupil::optix::UniformSampleHemisphere(xi.x, xi.y); 72 | ret.wi = Pupil::optix::ToWorld(local_wi, hit_geo.normal); 73 | ret.pdf = Pupil::optix::UniformSampleHemispherePdf(local_wi); 74 | ret.distance = Pupil::optix::MAX_DISTANCE; 75 | ret.radiance = color; 76 | ret.is_delta = false; 77 | 78 | ret.pos = hit_geo.position + ret.wi * ret.distance; 79 | ret.normal = normalize(center - ret.pos); 80 | } 81 | 82 | CUDA_DEVICE void Eval(EmitEvalRecord &ret, LocalGeometry &emit_local_geo, float3 scatter_pos) const noexcept { 83 | ret.pdf = 0.25f * M_1_PIf; 84 | ret.radiance = color; 85 | } 86 | }; 87 | }// namespace Pupil::optix -------------------------------------------------------------------------------- /framework/dx12/context.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "util/util.h" 4 | 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | namespace Pupil::DirectX { 13 | 14 | inline void StopIfFailed(HRESULT hr) { 15 | if (FAILED(hr)) { 16 | _com_error err(hr); 17 | OutputDebugString(err.ErrorMessage()); 18 | assert(false); 19 | } 20 | } 21 | 22 | class Context : public Pupil::util::Singleton { 23 | public: 24 | constexpr static uint32_t FRAMES_NUM = 3; 25 | 26 | winrt::com_ptr device; 27 | winrt::com_ptr adapter; 28 | 29 | // 0 ---- imgui font 30 | // 1 ---- output flip texture 0 for imgui 31 | // 2 ---- output flip buffer 0 32 | // 3 ---- output flip texture 1 for imgui 33 | // 4 ---- output flip buffer 1 34 | // 5... ---- for custom 35 | winrt::com_ptr srv_heap; 36 | constexpr static uint32_t SRV_NUM = 20; 37 | // 0 ---- swapchain back buffer 0 38 | // 1 ---- swapchain back buffer 1 39 | // 2 ---- swapchain back buffer 2 40 | // 3 ---- output flip texture 0 41 | // 4 ---- output flip texture 1 42 | // 5... ---- for custom 43 | winrt::com_ptr rtv_heap; 44 | constexpr static uint32_t RTV_NUM = 20; 45 | 46 | winrt::com_ptr cmd_queue; 47 | uint64_t global_fence_value = 0; 48 | 49 | void Init(uint32_t w, uint32_t h, HWND wnd_handle) noexcept; 50 | void Destroy() noexcept; 51 | static void ReportLiveObjects(); 52 | 53 | void Flush() noexcept; 54 | void Resize(uint32_t w, uint32_t h) noexcept; 55 | 56 | struct Frame { 57 | winrt::com_ptr buffer; 58 | D3D12_CPU_DESCRIPTOR_HANDLE handle; 59 | }; 60 | [[nodiscard]] Frame GetCurrentFrame() noexcept { 61 | std::scoped_lock lock{ m_flip_model_mutex }; 62 | return Frame{ m_back_buffers[m_current_index], m_back_buffer_handles[m_current_index] }; 63 | } 64 | [[nodiscard]] uint32_t GetCurrentFrameIndex() noexcept { return m_current_index; } 65 | [[nodiscard]] uint64_t GetGlobalFenceValue() noexcept { return global_fence_value; } 66 | 67 | [[nodiscard]] winrt::com_ptr GetCmdList() noexcept; 68 | uint64_t ExecuteCommandLists(winrt::com_ptr) noexcept; 69 | 70 | void StartRenderScreen(winrt::com_ptr) noexcept; 71 | void Present(winrt::com_ptr) noexcept; 72 | 73 | [[nodiscard]] bool IsInitialized() noexcept { return m_init_flag; } 74 | 75 | private: 76 | uint32_t m_frame_w = 1; 77 | uint32_t m_frame_h = 1; 78 | 79 | bool m_init_flag = false; 80 | 81 | bool m_use_warp = false; 82 | bool m_v_sync = true; 83 | bool m_tearing_supported = false; 84 | winrt::com_ptr m_swapchain; 85 | 86 | HANDLE m_fence_event{}; 87 | winrt::com_ptr m_fence; 88 | std::array m_frame_fence_values{}; 89 | 90 | struct CmdContext { 91 | winrt::com_ptr allocator; 92 | uint64_t fence_value; 93 | }; 94 | 95 | std::queue> m_command_lists; 96 | std::queue m_context_pool; 97 | 98 | uint32_t m_current_index; 99 | std::mutex m_flip_model_mutex; 100 | std::array, Context::FRAMES_NUM> m_back_buffers{}; 101 | std::array m_back_buffer_handles{}; 102 | 103 | void CreateAdapter() noexcept; 104 | void CreateDevice() noexcept; 105 | void CreateDescHeap() noexcept; 106 | void CreateCmdContext() noexcept; 107 | void CreateSwapchain(HWND hWnd) noexcept; 108 | void UpdateRenderTarget() noexcept; 109 | 110 | [[nodiscard]] winrt::com_ptr GetCmdAllocator() noexcept; 111 | }; 112 | }// namespace Pupil::DirectX -------------------------------------------------------------------------------- /framework/system/gui/gui.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "util/util.h" 4 | 5 | #include "../pass.h" 6 | #include "../buffer.h" 7 | #include "cuda/stream.h" 8 | 9 | #include 10 | #include 11 | 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | 19 | namespace Pupil { 20 | struct Buffer; 21 | enum class EWindowEvent { 22 | Quit, 23 | Resize, 24 | Minimized 25 | }; 26 | 27 | enum class ECanvasEvent { 28 | Resize, 29 | Display, 30 | MouseDragging, 31 | MouseWheel, 32 | CameraMove 33 | }; 34 | 35 | class GuiPass : public Pass, public util::Singleton { 36 | public: 37 | constexpr static uint32_t SWAP_BUFFER_NUM = 2; 38 | constexpr static std::array 39 | OUTPUT_FLIP_BUFFER = { 40 | "output flip buffer0", "output flip buffer1" 41 | }; 42 | constexpr static std::array 43 | OUTPUT_FLIP_TEXTURE = { 44 | "output flip texture0", "output flip texture1" 45 | }; 46 | struct { 47 | bool console = true; 48 | bool scene = true; 49 | bool bottom = false; 50 | } show_window; 51 | 52 | GuiPass() noexcept : Pass("GUI") {} 53 | 54 | virtual void OnRun() noexcept override; 55 | 56 | void Init() noexcept; 57 | void Destroy() noexcept; 58 | void Resize(uint32_t, uint32_t) noexcept; 59 | void ResizeCanvas(uint32_t w, uint32_t h) noexcept; 60 | void UpdateCanvasOutput() noexcept; 61 | void AdjustWindowSize() noexcept; 62 | 63 | using CustomInspector = std::function; 64 | void RegisterInspector(std::string_view, CustomInspector &&) noexcept; 65 | 66 | [[nodiscard]] bool IsInitialized() noexcept { return m_init_flag; } 67 | 68 | void FlipSwapBuffer() noexcept; 69 | [[nodiscard]] uint32_t GetCurrentRenderOutputBufferIndex() const noexcept { return m_current_buffer_index.load(); } 70 | [[nodiscard]] auto &GetCurrentRenderOutputBuffer() const noexcept { return m_flip_buffers[m_current_buffer_index.load()]; } 71 | [[nodiscard]] uint32_t GetReadyOutputBufferIndex() const noexcept { return m_ready_buffer_index.load(); } 72 | [[nodiscard]] auto &GetReadyOutputBuffer() const noexcept { return m_flip_buffers[m_ready_buffer_index.load()]; } 73 | 74 | protected: 75 | void OnDraw() noexcept; 76 | void Docking() noexcept; 77 | void Menu(bool show = true) noexcept; 78 | void Console(bool show = true) noexcept; 79 | void Canvas(bool show = true) noexcept; 80 | void Scene(bool show = true) noexcept; 81 | void Bottom(bool show = true) noexcept; 82 | 83 | void InitRenderToTexturePipeline() noexcept; 84 | void RenderFlipBufferToTexture(winrt::com_ptr) noexcept; 85 | 86 | std::vector> m_inspectors; 87 | bool m_init_flag = false; 88 | std::atomic_bool m_render_flip_buffer_to_texture_flag = false; 89 | std::unique_ptr m_memcpy_stream = nullptr; 90 | 91 | // one for rendering output, the other for showing on gui 92 | struct FlipBuffer { 93 | winrt::com_ptr res = nullptr; 94 | Buffer *system_buffer = nullptr; 95 | D3D12_GPU_DESCRIPTOR_HANDLE output_buffer_srv{}; 96 | D3D12_GPU_DESCRIPTOR_HANDLE output_texture_srv{}; 97 | D3D12_CPU_DESCRIPTOR_HANDLE output_rtv{}; 98 | }; 99 | 100 | FlipBuffer m_flip_buffers[SWAP_BUFFER_NUM]; 101 | 102 | std::atomic m_current_buffer_index = 0; 103 | std::atomic m_ready_buffer_index = 1; 104 | std::mutex m_flip_model_mutex; 105 | 106 | // render buffer to texture 107 | winrt::com_ptr m_root_signature; 108 | winrt::com_ptr m_pipeline_state; 109 | 110 | winrt::com_ptr m_vb; 111 | D3D12_VERTEX_BUFFER_VIEW m_vbv; 112 | 113 | winrt::com_ptr m_frame_constant_buffer; 114 | void *m_frame_constant_buffer_mapped_ptr = nullptr; 115 | }; 116 | }// namespace Pupil -------------------------------------------------------------------------------- /framework/system/denoise_pass.cpp: -------------------------------------------------------------------------------- 1 | #include "denoise_pass.h" 2 | #include "system/system.h" 3 | #include "util/event.h" 4 | 5 | #include "imgui.h" 6 | #include "system/gui/gui.h" 7 | 8 | #include 9 | 10 | namespace { 11 | uint32_t m_denoiser_mode = Pupil::optix::Denoiser::EMode::None; 12 | bool m_mode_dirty = false; 13 | 14 | uint32_t m_film_w = 0; 15 | uint32_t m_film_h = 0; 16 | bool m_film_dirty = false; 17 | 18 | double m_time_cost = 0.; 19 | 20 | uint32_t m_tile_w = 500; 21 | uint32_t m_tile_h = 500; 22 | bool m_tile_dirty = false; 23 | 24 | Pupil::optix::Denoiser::ExecutionData m_data; 25 | }// namespace 26 | 27 | namespace Pupil { 28 | bool DenoisePass::s_enabled_flag = false; 29 | 30 | DenoisePass::DenoisePass(const Config &config, std::string_view name) noexcept 31 | : Pass(name), m_config(config) { 32 | m_stream = std::make_unique(); 33 | 34 | m_denoiser_mode = (config.use_albedo ? optix::Denoiser::EMode::UseAlbedo : 0) | 35 | (config.use_normal ? optix::Denoiser::EMode::UseNormal : 0); 36 | 37 | m_denoiser = std::make_unique(m_denoiser_mode, m_stream.get()); 38 | 39 | EventBinder([this](void *p) { 40 | SetScene((world::World *)p); 41 | }); 42 | 43 | s_enabled_flag = config.default_enable; 44 | 45 | if (!s_enabled_flag) { 46 | EventDispatcher(std::string_view{ config.noise_name }); 47 | } 48 | } 49 | 50 | void DenoisePass::OnRun() noexcept { 51 | if (!s_enabled_flag) return; 52 | 53 | m_timer.Start(); 54 | 55 | if (m_mode_dirty) { 56 | m_denoiser->SetMode(m_denoiser_mode); 57 | m_film_dirty = true; 58 | m_mode_dirty = false; 59 | } 60 | 61 | if (m_film_dirty) { 62 | if (m_tile_dirty) { 63 | m_denoiser->SetTile(m_tile_w, m_tile_h); 64 | m_tile_dirty = false; 65 | } 66 | m_denoiser->Setup(m_film_w, m_film_h); 67 | m_film_dirty = false; 68 | } 69 | 70 | m_denoiser->Execute(m_data); 71 | m_stream->Synchronize(); 72 | 73 | m_timer.Stop(); 74 | m_time_cost = m_timer.ElapsedMilliseconds(); 75 | } 76 | 77 | void DenoisePass::SetScene(world::World *world) noexcept { 78 | if (world->scene->sensor.film.w != m_film_w || world->scene->sensor.film.h != m_film_h) { 79 | m_film_w = world->scene->sensor.film.w; 80 | m_film_h = world->scene->sensor.film.h; 81 | m_film_dirty = true; 82 | } 83 | m_denoiser->tile_w = m_tile_w; 84 | m_denoiser->tile_h = m_tile_h; 85 | 86 | auto buf_mngr = util::Singleton::instance(); 87 | 88 | m_data.input = buf_mngr->GetBuffer(m_config.noise_name)->cuda_ptr; 89 | m_data.albedo = buf_mngr->GetBuffer(m_config.albedo_name)->cuda_ptr; 90 | m_data.normal = buf_mngr->GetBuffer(m_config.normal_name)->cuda_ptr; 91 | // m_data.motion_vector = buf_mngr->GetBuffer("motion vector")->cuda_ptr; 92 | m_data.prev_output = m_data.input; 93 | m_data.output = buf_mngr->GetBuffer(buf_mngr->DEFAULT_FINAL_RESULT_BUFFER_NAME)->cuda_ptr; 94 | } 95 | 96 | void DenoisePass::Inspector() noexcept { 97 | ImGui::Checkbox("enable", &s_enabled_flag); 98 | ImGui::Text("cost: %.3lf ms", m_time_cost); 99 | uint32_t mode = m_denoiser_mode; 100 | 101 | if (bool albedo = mode & optix::Denoiser::EMode::UseAlbedo; 102 | ImGui::Checkbox("use albedo", &albedo)) { 103 | mode ^= optix::Denoiser::EMode::UseAlbedo; 104 | } 105 | if (bool normal = mode & optix::Denoiser::EMode::UseNormal; 106 | ImGui::Checkbox("use normal", &normal)) { 107 | mode ^= optix::Denoiser::EMode::UseNormal; 108 | } 109 | // if (bool temporal = mode & optix::Denoiser::EMode::UseTemporal; 110 | // ImGui::Checkbox("use temporal", &temporal)) { 111 | // mode ^= optix::Denoiser::EMode::UseTemporal; 112 | // } 113 | // if (bool tiled = mode & optix::Denoiser::EMode::Tiled; 114 | // ImGui::Checkbox("use tile", &tiled)) { 115 | // mode ^= optix::Denoiser::EMode::Tiled; 116 | // } 117 | 118 | if (mode != m_denoiser_mode) { 119 | m_denoiser_mode = mode; 120 | m_mode_dirty = true; 121 | } 122 | } 123 | }// namespace Pupil -------------------------------------------------------------------------------- /framework/render/material/bsdf/plastic.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "../predefine.h" 4 | #include "cuda/texture.h" 5 | #include "optix/util.h" 6 | #include "../fresnel.h" 7 | 8 | namespace Pupil::optix::material { 9 | 10 | struct Plastic { 11 | // float int_ior; 12 | // float ext_ior; 13 | float eta; 14 | bool nonlinear; 15 | cuda::Texture diffuse_reflectance; 16 | cuda::Texture specular_reflectance; 17 | 18 | // pre compute 19 | float m_int_fdr; 20 | // float m_ext_fdr; 21 | float m_specular_sampling_weight; 22 | 23 | struct Local { 24 | float eta; 25 | float int_fdr; 26 | // float ext_fdr; 27 | float specular_sampling_weight; 28 | bool nonlinear; 29 | float3 diffuse_reflectance; 30 | float3 specular_reflectance; 31 | 32 | CUDA_HOSTDEVICE void GetBsdf(BsdfSamplingRecord &record) const noexcept { 33 | record.f = make_float3(0.f); 34 | if (record.wi.z <= 0.f || record.wo.z <= 0.f) return; 35 | 36 | float fresnel_o = fresnel::DielectricReflectance(eta, record.wo.z); 37 | float fresnel_i = fresnel::DielectricReflectance(eta, record.wi.z); 38 | float3 diff = diffuse_reflectance / (1.f - (nonlinear ? diffuse_reflectance * int_fdr : make_float3(int_fdr))); 39 | record.f = diff * (1.f - fresnel_i) * (1.f - fresnel_o) * optix::CosineSampleHemispherePdf(record.wi) / (eta * eta * record.wi.z); 40 | // record.f = diff * (1.f - fresnel_i) * (1.f - fresnel_o) * M_1_PIf; 41 | } 42 | 43 | CUDA_HOSTDEVICE void GetPdf(BsdfSamplingRecord &record) const noexcept { 44 | record.pdf = 0.f; 45 | if (record.wi.z <= 0.f || record.wo.z <= 0.f) return; 46 | 47 | float fresnel_o = fresnel::DielectricReflectance(eta, record.wo.z); 48 | float specular_prob = (fresnel_o * specular_sampling_weight) / 49 | (fresnel_o * specular_sampling_weight + 50 | (1 - fresnel_o) * (1.f - specular_sampling_weight)); 51 | 52 | record.pdf = optix::CosineSampleHemispherePdf(record.wi) * (1.f - specular_prob); 53 | } 54 | 55 | CUDA_HOSTDEVICE void Sample(BsdfSamplingRecord &record) const noexcept { 56 | if (record.wo.z <= 0.f) return; 57 | float fresnel_o = fresnel::DielectricReflectance(eta, record.wo.z); 58 | 59 | float2 xi = record.sampler->Next2(); 60 | float specular_prob = (fresnel_o * specular_sampling_weight) / 61 | (fresnel_o * specular_sampling_weight + 62 | (1 - fresnel_o) * (1.f - specular_sampling_weight)); 63 | 64 | if (xi.x < specular_prob) { 65 | record.sampled_type = EBsdfLobeType::DeltaReflection; 66 | record.wi = optix::Reflect(record.wo); 67 | 68 | record.f = specular_reflectance * fresnel_o / record.wi.z; 69 | record.pdf = specular_prob; 70 | } else { 71 | record.sampled_type = EBsdfLobeType::DiffuseReflection; 72 | record.wi = optix::CosineSampleHemisphere( 73 | (xi.x - specular_prob) / (1.f - specular_prob), xi.y); 74 | 75 | float fresnel_i = fresnel::DielectricReflectance(eta, record.wi.z); 76 | float3 diff = diffuse_reflectance / (1.f - (nonlinear ? diffuse_reflectance * int_fdr : make_float3(int_fdr))); 77 | record.f = diff * (1.f - fresnel_i) * (1.f - fresnel_o) * optix::CosineSampleHemispherePdf(record.wi) / (eta * eta * record.wi.z); 78 | record.pdf = optix::CosineSampleHemispherePdf(record.wi) * (1.f - specular_prob); 79 | } 80 | } 81 | }; 82 | 83 | CUDA_DEVICE Local GetLocal(float2 sampled_tex) const noexcept { 84 | Local local_bsdf; 85 | local_bsdf.eta = eta; 86 | local_bsdf.nonlinear = nonlinear; 87 | local_bsdf.int_fdr = m_int_fdr; 88 | // local_bsdf.ext_fdr = m_ext_fdr; 89 | local_bsdf.diffuse_reflectance = diffuse_reflectance.Sample(sampled_tex); 90 | local_bsdf.specular_reflectance = specular_reflectance.Sample(sampled_tex); 91 | local_bsdf.specular_sampling_weight = m_specular_sampling_weight; 92 | return local_bsdf; 93 | } 94 | }; 95 | 96 | }// namespace Pupil::optix::material -------------------------------------------------------------------------------- /framework/render/material/bsdf/rough_plastic.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "../predefine.h" 4 | #include "cuda/texture.h" 5 | #include "optix/util.h" 6 | #include "../fresnel.h" 7 | #include "../ggx.h" 8 | 9 | namespace Pupil::optix::material { 10 | 11 | struct RoughPlastic { 12 | float eta; 13 | bool nonlinear; 14 | cuda::Texture alpha; 15 | cuda::Texture diffuse_reflectance; 16 | cuda::Texture specular_reflectance; 17 | 18 | // pretreatment var 19 | float m_specular_sampling_weight; 20 | float m_int_fdr; 21 | 22 | struct Local { 23 | float eta; 24 | float int_fdr; 25 | float specular_sampling_weight; 26 | float alpha; 27 | bool nonlinear; 28 | float3 diffuse_reflectance; 29 | float3 specular_reflectance; 30 | 31 | CUDA_HOSTDEVICE void GetBsdf(BsdfSamplingRecord &record) const noexcept { 32 | record.f = make_float3(0.f); 33 | if (record.wi.z <= 0.f || record.wo.z <= 0.f) return; 34 | 35 | float fresnel_o = fresnel::DielectricReflectance(eta, record.wo.z); 36 | 37 | float3 wh = normalize(record.wi + record.wo); 38 | record.f = specular_reflectance * 39 | fresnel::DielectricReflectance(eta, dot(wh, record.wo)) * ggx::D(wh, alpha) * 40 | ggx::G(record.wi, record.wo, alpha) / (4.f * record.wo.z * record.wi.z); 41 | 42 | float fresnel_i = fresnel::DielectricReflectance(eta, record.wi.z); 43 | float3 diff = diffuse_reflectance / (1.f - (nonlinear ? diffuse_reflectance * int_fdr : make_float3(int_fdr))); 44 | record.f += diff * (1.f - fresnel_i) * (1.f - fresnel_o) * M_1_PIf / (eta * eta); 45 | } 46 | 47 | CUDA_HOSTDEVICE void GetPdf(BsdfSamplingRecord &record) const noexcept { 48 | record.pdf = 0.f; 49 | if (record.wi.z <= 0.f || record.wo.z <= 0.f) return; 50 | 51 | float fresnel_o = fresnel::DielectricReflectance(eta, record.wo.z); 52 | float specular_prob = (fresnel_o * specular_sampling_weight) / 53 | (fresnel_o * specular_sampling_weight + 54 | (1 - fresnel_o) * (1.f - specular_sampling_weight)); 55 | float diffuse_prob = 1.f - specular_prob; 56 | 57 | float3 wh = normalize(record.wi + record.wo); 58 | record.pdf = specular_prob * ggx::Pdf(record.wo, wh, alpha) / 59 | (4.f * dot(record.wi, wh)); 60 | record.pdf += diffuse_prob * optix::CosineSampleHemispherePdf(record.wi); 61 | } 62 | 63 | CUDA_HOSTDEVICE void Sample(BsdfSamplingRecord &record) const noexcept { 64 | record.wi = make_float3(0.f); 65 | if (record.wo.z <= 0.f) return; 66 | 67 | float fresnel_o = fresnel::DielectricReflectance(eta, record.wo.z); 68 | float specular_prob = (fresnel_o * specular_sampling_weight) / 69 | (fresnel_o * specular_sampling_weight + 70 | (1 - fresnel_o) * (1.f - specular_sampling_weight)); 71 | 72 | float2 xi = record.sampler->Next2(); 73 | if (xi.y < specular_prob) { 74 | xi.y /= specular_prob; 75 | float3 wh = ggx::Sample(record.wo, alpha, xi); 76 | record.wi = optix::Reflect(record.wo, wh); 77 | record.sampled_type = EBsdfLobeType::GlossyReflection; 78 | } else { 79 | xi.y = (xi.y - specular_prob) / (1.f - specular_prob); 80 | record.wi = optix::CosineSampleHemisphere(xi.x, xi.y); 81 | record.sampled_type = EBsdfLobeType::DiffuseReflection; 82 | } 83 | GetPdf(record); 84 | GetBsdf(record); 85 | } 86 | }; 87 | 88 | CUDA_DEVICE Local GetLocal(float2 sampled_tex) const noexcept { 89 | Local local_bsdf; 90 | local_bsdf.eta = eta; 91 | local_bsdf.nonlinear = nonlinear; 92 | local_bsdf.int_fdr = m_int_fdr; 93 | local_bsdf.alpha = alpha.Sample(sampled_tex).x; 94 | local_bsdf.diffuse_reflectance = diffuse_reflectance.Sample(sampled_tex); 95 | local_bsdf.specular_reflectance = specular_reflectance.Sample(sampled_tex); 96 | local_bsdf.specular_sampling_weight = m_specular_sampling_weight; 97 | return local_bsdf; 98 | } 99 | }; 100 | 101 | }// namespace Pupil::optix::material -------------------------------------------------------------------------------- /framework/util/camera.cpp: -------------------------------------------------------------------------------- 1 | #include "camera.h" 2 | 3 | namespace Pupil::util { 4 | float Camera::sensitivity = 0.05f; 5 | float Camera::sensitivity_scale = 1.f; 6 | 7 | Mat4 Camera::GetSampleToCameraMatrix() noexcept { 8 | if (m_projection_dirty) { 9 | auto proj = DirectX::XMMatrixPerspectiveFovRH(m_fov_y / 180.f * 3.14159265358979323846f, m_aspect_ratio, m_near_clip, m_far_clip); 10 | 11 | m_proj = DirectX::XMMatrixTranspose(proj); 12 | m_sample_to_camera = DirectX::XMMatrixTranspose( 13 | DirectX::XMMatrixInverse( 14 | nullptr, proj * 15 | DirectX::XMMatrixTranslation(1.f, 1.f, 0.f) * 16 | DirectX::XMMatrixScaling(0.5f, 0.5f, 1.f))); 17 | m_projection_dirty = false; 18 | } 19 | return m_sample_to_camera; 20 | } 21 | Mat4 Camera::GetProjectionMatrix() noexcept { 22 | if (m_projection_dirty) { 23 | auto proj = DirectX::XMMatrixPerspectiveFovRH(m_fov_y / 180.f * 3.14159265358979323846f, m_aspect_ratio, m_near_clip, m_far_clip); 24 | 25 | m_proj = DirectX::XMMatrixTranspose(proj); 26 | m_sample_to_camera = DirectX::XMMatrixTranspose( 27 | DirectX::XMMatrixInverse( 28 | nullptr, proj * 29 | DirectX::XMMatrixTranslation(1.f, 1.f, 0.f) * 30 | DirectX::XMMatrixScaling(0.5f, 0.5f, 1.f))); 31 | m_projection_dirty = false; 32 | } 33 | return m_proj; 34 | } 35 | Mat4 Camera::GetToWorldMatrix() noexcept { 36 | if (m_to_world_dirty) { 37 | m_view = 38 | m_rotate * 39 | Mat4(1.f, 0.f, 0.f, -m_position.x, 40 | 0.f, 1.f, 0.f, -m_position.y, 41 | 0.f, 0.f, 1.f, -m_position.z, 42 | 0.f, 0.f, 0.f, 1.f); 43 | m_to_world = m_view.GetInverse(); 44 | m_to_world_dirty = false; 45 | } 46 | return m_to_world; 47 | } 48 | Mat4 Camera::GetViewMatrix() noexcept { 49 | if (m_to_world_dirty) { 50 | m_view = 51 | m_rotate * 52 | Mat4(1.f, 0.f, 0.f, -m_position.x, 53 | 0.f, 1.f, 0.f, -m_position.y, 54 | 0.f, 0.f, 1.f, -m_position.z, 55 | 0.f, 0.f, 0.f, 1.f); 56 | m_to_world = m_view.GetInverse(); 57 | m_to_world_dirty = false; 58 | } 59 | return m_view; 60 | } 61 | 62 | std::tuple Camera::GetCameraCoordinateSystem() const noexcept { 63 | auto right = Transform::TransformVector(X, m_rotate_inv); 64 | auto up = Transform::TransformVector(Y, m_rotate_inv); 65 | auto forward = Transform::TransformVector(Z, m_rotate_inv); 66 | return { right, up, forward }; 67 | } 68 | 69 | void Camera::SetProjectionFactor(float fov_y, float aspect_ratio, float near_clip, float far_clip) noexcept { 70 | m_fov_y = fov_y; 71 | m_aspect_ratio = aspect_ratio; 72 | m_near_clip = near_clip; 73 | m_far_clip = far_clip; 74 | m_projection_dirty = true; 75 | } 76 | void Camera::SetFov(float fov) noexcept { 77 | m_fov_y = fov; 78 | m_projection_dirty = true; 79 | } 80 | void Camera::SetWorldTransform(Transform to_world) noexcept { 81 | m_to_world = to_world.matrix; 82 | m_position.x = m_to_world.r0.w; 83 | m_position.y = m_to_world.r1.w; 84 | m_position.z = m_to_world.r2.w; 85 | 86 | m_rotate = to_world.matrix.GetTranspose(); 87 | m_rotate.r3.x = 0.f; 88 | m_rotate.r3.y = 0.f; 89 | m_rotate.r3.z = 0.f; 90 | 91 | m_rotate_inv = m_rotate.GetTranspose(); 92 | 93 | m_view = 94 | m_rotate * 95 | Mat4(1.f, 0.f, 0.f, -m_position.x, 96 | 0.f, 1.f, 0.f, -m_position.y, 97 | 0.f, 0.f, 1.f, -m_position.z, 98 | 0.f, 0.f, 0.f, 1.f); 99 | 100 | m_to_world_dirty = false; 101 | } 102 | 103 | void Camera::Rotate(float delta_x, float delta_y) noexcept { 104 | Transform pitch; 105 | pitch.Rotate(X.x, X.y, X.z, delta_y); 106 | Transform yaw; 107 | yaw.Rotate(Y.x, Y.y, Y.z, delta_x); 108 | 109 | m_rotate = pitch.matrix * m_rotate * yaw.matrix; 110 | m_rotate_inv = m_rotate.GetTranspose(); 111 | m_to_world_dirty = true; 112 | } 113 | 114 | void Camera::Move(Float3 delta) noexcept { 115 | delta = Transform::TransformVector(delta, m_rotate_inv); 116 | Transform translation; 117 | translation.Translate(delta.x, delta.y, delta.z); 118 | m_position = Transform::TransformPoint(m_position, translation.matrix); 119 | m_to_world_dirty = true; 120 | } 121 | }// namespace Pupil::util 122 | -------------------------------------------------------------------------------- /data/static/restir_test.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | -------------------------------------------------------------------------------- /framework/util/type.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | namespace Pupil::util { 7 | struct Float3 { 8 | union { 9 | struct { 10 | float x, y, z; 11 | }; 12 | struct { 13 | float r, g, b; 14 | }; 15 | float e[3]; 16 | }; 17 | 18 | constexpr Float3(float xyz = 0.f) noexcept : x(xyz), y(xyz), z(xyz) {} 19 | constexpr Float3(float x_, float y_, float z_) noexcept : x(x_), y(y_), z(z_) {} 20 | 21 | inline bool operator==(const Float3 &rhs) const noexcept { return x == rhs.x && y == rhs.y && z == rhs.z; } 22 | inline bool ApproxEqualTo(const Float3 &rhs, float eps = 1e-6) const noexcept { return abs(x - rhs.x) < eps && abs(y - rhs.y) < eps && abs(z - rhs.z) < eps; } 23 | 24 | inline Float3 operator+(const Float3 &t) const noexcept { return { x + t.x, y + t.y, z + t.z }; } 25 | inline Float3 operator-(const Float3 &t) const noexcept { return { x - t.x, y - t.y, z - t.z }; } 26 | inline Float3 operator*(float t) const noexcept { return { x * t, y * t, z * t }; } 27 | inline Float3 operator/(float t) const noexcept { return *this * (1.f / t); } 28 | 29 | inline Float3 &operator+=(const Float3 &t) noexcept { 30 | *this = *this + t; 31 | return *this; 32 | } 33 | inline Float3 &operator-=(const Float3 &t) noexcept { 34 | *this = *this - t; 35 | return *this; 36 | } 37 | }; 38 | 39 | struct Float4 { 40 | union { 41 | struct { 42 | float x, y, z, w; 43 | }; 44 | struct { 45 | float r, g, b, a; 46 | }; 47 | float e[4]; 48 | }; 49 | 50 | constexpr Float4(float xyzw = 0.f) noexcept : x(xyzw), y(xyzw), z(xyzw), w(xyzw) {} 51 | constexpr Float4(float x_, float y_, float z_, float w_) noexcept : x(x_), y(y_), z(z_), w(w_) {} 52 | 53 | inline bool operator==(const Float4 &rhs) const noexcept { return x == rhs.x && y == rhs.y && z == rhs.z && w == rhs.w; } 54 | inline bool ApproxEqualTo(const Float4 &rhs, float eps = 1e-6) const noexcept { 55 | return abs(x - rhs.x) < eps && abs(y - rhs.y) < eps && abs(z - rhs.z) < eps && abs(w - rhs.w) < eps; 56 | } 57 | 58 | inline Float4 operator+(const Float4 &t) const noexcept { return { x + t.x, y + t.y, z + t.z, w + t.w }; } 59 | inline Float4 operator-(const Float4 &t) const noexcept { return { x - t.x, y - t.y, z - t.z, w - t.w }; } 60 | inline Float4 operator*(float t) const noexcept { return { x * t, y * t, z * t, w * t }; } 61 | inline Float4 operator/(float t) const noexcept { return *this * (1.f / t); } 62 | 63 | inline Float4 &operator+=(const Float4 &t) noexcept { 64 | *this = *this + t; 65 | return *this; 66 | } 67 | inline Float4 &operator-=(const Float4 &t) noexcept { 68 | *this = *this - t; 69 | return *this; 70 | } 71 | }; 72 | 73 | struct Mat4 { 74 | union { 75 | struct { 76 | Float4 r0; 77 | Float4 r1; 78 | Float4 r2; 79 | Float4 r3; 80 | }; 81 | float e[16]; 82 | float re[4][4]; 83 | DirectX::XMMATRIX dx_mat; 84 | }; 85 | 86 | constexpr Mat4() noexcept : r0(0.f), r1(0.f), r2(0.f), r3(0.f) {} 87 | constexpr Mat4(float m00, float m01, float m02, float m03, 88 | float m10, float m11, float m12, float m13, 89 | float m20, float m21, float m22, float m23, 90 | float m30, float m31, float m32, float m33) noexcept 91 | : r0(m00, m01, m02, m03), r1(m10, m11, m12, m13), r2(m20, m21, m22, m23), r3(m30, m31, m32, m33) {} 92 | 93 | Mat4(float e_[16]) noexcept { std::copy(e_, e_ + 16, e); } 94 | Mat4(DirectX::XMMATRIX m) noexcept { dx_mat = m; } 95 | operator DirectX::XMMATRIX() const noexcept { return dx_mat; } 96 | 97 | Mat4 operator*(const Mat4 &t) const noexcept { return dx_mat * t.dx_mat; } 98 | bool operator==(const Mat4 &rhs) const noexcept { 99 | return r0 == rhs.r0 && r1 == rhs.r1 && r2 == rhs.r2 && r3 == rhs.r3; 100 | } 101 | 102 | bool ApproxEqualTo(const Mat4 &rhs, float eps = 1e-6) const noexcept { 103 | return r0.ApproxEqualTo(rhs.r0, eps) && r1.ApproxEqualTo(rhs.r1, eps) && r2.ApproxEqualTo(rhs.r2, eps) && r3.ApproxEqualTo(rhs.r3, eps); 104 | } 105 | 106 | Mat4 GetInverse() const noexcept { 107 | auto ret = DirectX::XMMatrixInverse(nullptr, dx_mat); 108 | return ret; 109 | } 110 | 111 | Mat4 GetTranspose() const noexcept { return DirectX::XMMatrixTranspose(dx_mat); } 112 | }; 113 | }// namespace Pupil::util -------------------------------------------------------------------------------- /framework/system/buffer.cpp: -------------------------------------------------------------------------------- 1 | #include "buffer.h" 2 | #include "cuda/util.h" 3 | #include "cuda/context.h" 4 | #include "dx12/d3dx12.h" 5 | #include "dx12/context.h" 6 | #include "util/log.h" 7 | 8 | #include "wsa.h" 9 | 10 | namespace Pupil { 11 | Buffer::~Buffer() noexcept { 12 | CUDA_FREE(cuda_ptr); 13 | dx12_ptr = nullptr; 14 | } 15 | 16 | BufferManager::BufferManager() noexcept { 17 | m_buffer_names.emplace_back(DEFAULT_FINAL_RESULT_BUFFER_NAME); 18 | } 19 | 20 | BufferManager::~BufferManager() noexcept { 21 | Destroy(); 22 | } 23 | 24 | void BufferManager::Destroy() noexcept { 25 | CUDA_SYNC_CHECK(); 26 | m_buffers.clear(); 27 | for (auto &&[dx12_ptr, cuda_ext_memory] : m_cuda_ext_memorys) { 28 | if (cuda_ext_memory) 29 | CUDA_CHECK(cudaDestroyExternalMemory(cuda_ext_memory)); 30 | } 31 | m_cuda_ext_memorys.clear(); 32 | } 33 | 34 | Buffer *BufferManager::GetBuffer(std::string_view id) noexcept { 35 | auto it = m_buffers.find(id); 36 | return it == m_buffers.end() ? nullptr : it->second.get(); 37 | } 38 | 39 | Buffer *BufferManager::AllocBuffer(const BufferDesc &desc) noexcept { 40 | auto buffer = std::make_unique(desc); 41 | 42 | auto size = static_cast(desc.width * desc.height * desc.stride_in_byte); 43 | if (!(desc.flag & EBufferFlag::SharedWithDX12)) { 44 | CUDA_CHECK(cudaMalloc(reinterpret_cast(&buffer->cuda_ptr), size)); 45 | CUDA_CHECK(cudaMemset(reinterpret_cast(buffer->cuda_ptr), 0, size)); 46 | } else { 47 | auto d3d12_buffer_desc = CD3DX12_RESOURCE_DESC::Buffer(size, D3D12_RESOURCE_FLAG_NONE); 48 | auto properties = CD3DX12_HEAP_PROPERTIES(D3D12_HEAP_TYPE_DEFAULT); 49 | 50 | auto dx12_context = util::Singleton::instance(); 51 | winrt::com_ptr temp_res; 52 | DirectX::StopIfFailed(dx12_context->device->CreateCommittedResource( 53 | &properties, D3D12_HEAP_FLAG_SHARED, &d3d12_buffer_desc, 54 | D3D12_RESOURCE_STATE_COMMON, nullptr, 55 | winrt::guid_of(), temp_res.put_void())); 56 | buffer->dx12_ptr = temp_res; 57 | if (desc.name != nullptr) 58 | buffer->dx12_ptr->SetName(std::wstring{ desc.name, desc.name + strlen(desc.name) }.c_str()); 59 | 60 | HANDLE shared_handle{}; 61 | WindowsSecurityAttributes sec_attr{}; 62 | 63 | DirectX::StopIfFailed(dx12_context->device->CreateSharedHandle(buffer->dx12_ptr.get(), &sec_attr, GENERIC_ALL, 0, &shared_handle)); 64 | 65 | auto cuda_contex = util::Singleton::instance(); 66 | const auto texAllocInfo = dx12_context->device->GetResourceAllocationInfo(cuda_contex->cuda_node_mask, 1, &d3d12_buffer_desc); 67 | 68 | cudaExternalMemory_t cuda_ext_memory; 69 | cudaExternalMemoryHandleDesc cuda_ext_handle_desc{}; 70 | cuda_ext_handle_desc.type = cudaExternalMemoryHandleTypeD3D12Heap; 71 | cuda_ext_handle_desc.handle.win32.handle = shared_handle; 72 | cuda_ext_handle_desc.size = texAllocInfo.SizeInBytes; 73 | cuda_ext_handle_desc.flags = cudaExternalMemoryDedicated; 74 | CUDA_CHECK(cudaImportExternalMemory(&cuda_ext_memory, &cuda_ext_handle_desc)); 75 | CloseHandle(shared_handle); 76 | 77 | cudaExternalMemoryBufferDesc cuda_ext_buffer_desc{}; 78 | cuda_ext_buffer_desc.offset = 0; 79 | cuda_ext_buffer_desc.size = size; 80 | CUDA_CHECK(cudaExternalMemoryGetMappedBuffer( 81 | reinterpret_cast(&buffer->cuda_ptr), 82 | cuda_ext_memory, &cuda_ext_buffer_desc)); 83 | 84 | m_cuda_ext_memorys[buffer->dx12_ptr.get()] = cuda_ext_memory; 85 | } 86 | 87 | auto ret = buffer.get(); 88 | if (m_buffers.find(desc.name) == m_buffers.end()) { 89 | if (desc.name != DEFAULT_FINAL_RESULT_BUFFER_NAME) { 90 | m_buffer_names.emplace_back(desc.name); 91 | } 92 | } 93 | // else { 94 | // Pupil::Log::Warn("buffer[{}] is reset.", desc.name); 95 | // } 96 | m_buffers[desc.name] = std::move(buffer); 97 | 98 | return ret; 99 | } 100 | 101 | void BufferManager::AddBuffer(std::string_view id, std::unique_ptr &buffer) noexcept { 102 | if (m_buffers.find(id) == m_buffers.end()) { 103 | if (id != DEFAULT_FINAL_RESULT_BUFFER_NAME) { 104 | m_buffer_names.emplace_back(id); 105 | } 106 | } 107 | // else { 108 | // Pupil::Log::Warn("buffer[{}] is reset.", id); 109 | // } 110 | 111 | m_buffers[id.data()] = std::move(buffer); 112 | } 113 | }// namespace Pupil -------------------------------------------------------------------------------- /framework/optix/sbt.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #include 6 | #include 7 | #include 8 | 9 | #include "check.h" 10 | #include "cuda/util.h" 11 | 12 | namespace Pupil::optix { 13 | template 14 | struct alignas(OPTIX_SBT_RECORD_ALIGNMENT) Record { 15 | alignas(OPTIX_SBT_RECORD_ALIGNMENT) char header[OPTIX_SBT_RECORD_HEADER_SIZE]; 16 | T data; 17 | }; 18 | 19 | template 20 | concept SBTTypes = 21 | requires { 22 | typename T::RayGenDataType; 23 | typename T::MissDataType; 24 | typename T::HitGroupDataType; 25 | typename T::CallablesDataType; 26 | typename T::ExceptionDataType; 27 | }; 28 | 29 | struct EmptyData {}; 30 | 31 | struct EmptySBT { 32 | using RayGenDataType = EmptyData; 33 | using MissDataType = EmptyData; 34 | using HitGroupDataType = EmptyData; 35 | using CallablesDataType = EmptyData; 36 | using ExceptionDataType = EmptyData; 37 | }; 38 | 39 | template 40 | struct ProgDataPair { 41 | using DataType = U; 42 | T program; 43 | DataType data; 44 | }; 45 | 46 | template 47 | struct ProgDataPair { 48 | using DataType = EmptyData; 49 | T program; 50 | DataType data; 51 | }; 52 | 53 | template 54 | using ProgDataDescPair = ProgDataPair; 55 | template 56 | using ProgDataBindingPair = ProgDataPair; 57 | 58 | template 59 | struct SBTDesc { 60 | ProgDataDescPair ray_gen_data; 61 | std::vector> miss_datas; 62 | std::vector> hit_datas; 63 | std::vector> callables_datas; 64 | ProgDataDescPair exception_data; 65 | }; 66 | 67 | template 68 | struct BindingInfo { 69 | using BindingPair = ProgDataBindingPair; 70 | std::vector datas; 71 | }; 72 | 73 | template 74 | struct SBT { 75 | private: 76 | CUdeviceptr m_ray_gen_record = 0; 77 | CUdeviceptr m_miss_record = 0; 78 | CUdeviceptr m_hitgroup_record = 0; 79 | CUdeviceptr m_callables_record = 0; 80 | CUdeviceptr m_exception_record = 0; 81 | 82 | public: 83 | OptixShaderBindingTable sbt{}; 84 | 85 | SBT(const SBTDesc &desc, const Pipeline *pipeline) 86 | noexcept; 87 | 88 | using RayGenBindingType = BindingInfo; 89 | using MissBindingType = BindingInfo; 90 | using HitGroupBindingType = BindingInfo; 91 | using CallablesBindingType = BindingInfo; 92 | using ExceptionBindingType = BindingInfo; 93 | 94 | using RayGenDataRecord = Record; 95 | using MissDataRecord = Record; 96 | using HitGroupDataRecord = Record; 97 | using CallablesDataRecord = Record; 98 | using ExceptionDataRecord = Record; 99 | 100 | void SetRayGenData(const RayGenBindingType &binding_info) noexcept; 101 | void SetMissData(const MissBindingType &binding_info) noexcept; 102 | void SetHitGroupData(const HitGroupBindingType &binding_info) noexcept; 103 | void SetCallablesData(const CallablesBindingType &binding_info) noexcept; 104 | void SetExceptionData(const ExceptionBindingType &binding_info) noexcept; 105 | 106 | void UpdateRayGenRecord(const RayGenDataRecord &record) noexcept; 107 | void UpdateMissRecord(const MissDataRecord &record, unsigned int offset) noexcept; 108 | void UpdateHitGroupRecord(const HitGroupDataRecord &record, unsigned int offset) noexcept; 109 | void UpdateCallablesRecord(const CallablesDataRecord &record, unsigned int offset) noexcept; 110 | void UpdateExceptionRecord(const ExceptionDataRecord &record) noexcept; 111 | 112 | void UpdateMissRecords(const MissDataRecord *records, unsigned int cnt, unsigned int offset) noexcept; 113 | void UpdateHitGroupRecords(const HitGroupDataRecord *records, unsigned int cnt, unsigned int offset) noexcept; 114 | void UpdateCallablesRecords(const CallablesDataRecord *records, unsigned int cnt, unsigned int offset) noexcept; 115 | 116 | ~SBT() noexcept; 117 | }; 118 | 119 | }// namespace Pupil::optix 120 | 121 | #include "sbt.inl" 122 | -------------------------------------------------------------------------------- /data/static/cornellbox.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | -------------------------------------------------------------------------------- /framework/render/material/bsdf/rough_dielectric.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "../predefine.h" 4 | #include "cuda/texture.h" 5 | #include "../ggx.h" 6 | 7 | namespace Pupil::optix::material { 8 | 9 | struct RoughDielectric { 10 | float eta; 11 | cuda::Texture alpha; 12 | cuda::Texture specular_reflectance; 13 | cuda::Texture specular_transmittance; 14 | 15 | struct Local { 16 | float alpha; 17 | float eta; 18 | float3 specular_reflectance; 19 | float3 specular_transmittance; 20 | 21 | CUDA_HOSTDEVICE void GetBsdf(BsdfSamplingRecord &record) const noexcept { 22 | record.f = make_float3(0.f); 23 | if (optix::IsZero(record.wo.z)) return; 24 | 25 | float3 wh; 26 | bool sample_reflect = record.wo.z * record.wi.z > 0.f; 27 | if (sample_reflect) { 28 | wh = normalize(record.wo + record.wi); 29 | } else { 30 | wh = normalize(record.wo + 31 | record.wi * (record.wo.z > 0.f ? eta : 1.f / eta)); 32 | } 33 | 34 | wh = wh * (wh.z > 0.f ? 1.f : -1.f); 35 | 36 | float F = fresnel::DielectricReflectance(eta, dot(record.wo, wh)); 37 | float G = ggx::G(record.wi, record.wo, alpha); 38 | float D = ggx::D(wh, alpha); 39 | if (sample_reflect) { 40 | record.f = specular_reflectance * 41 | F * G * D / (4.f * abs(record.wi.z) * abs(record.wo.z)); 42 | } else { 43 | float _eta = record.wo.z > 0.f ? eta : 1.f / eta; 44 | float sqrt_denom = dot(record.wo, wh) + _eta * dot(record.wi, wh); 45 | record.f = specular_transmittance * 46 | abs((1.f - F) * D * G * dot(record.wi, wh) * dot(record.wo, wh) / 47 | (sqrt_denom * sqrt_denom * record.wi.z * record.wo.z)); 48 | } 49 | } 50 | 51 | CUDA_HOSTDEVICE void GetPdf(BsdfSamplingRecord &record) const noexcept { 52 | record.pdf = 0.f; 53 | bool sample_reflect = record.wo.z * record.wi.z > 0.f; 54 | float3 wh; 55 | float dwh_dwo; 56 | if (sample_reflect) { 57 | wh = normalize(record.wo + record.wi); 58 | dwh_dwo = 1.f / (4.f * dot(record.wi, wh)); 59 | } else { 60 | float _eta = record.wo.z > 0.f ? eta : 1.f / eta; 61 | wh = normalize(record.wo + record.wi * _eta); 62 | float sqrt_denom = dot(record.wo, wh) + _eta * dot(record.wi, wh); 63 | dwh_dwo = (_eta * _eta * dot(record.wi, wh)) / (sqrt_denom * sqrt_denom); 64 | } 65 | 66 | wh = wh * (wh.z > 0.f ? 1.f : -1.f); 67 | float3 wo = record.wo * (record.wo.z > 0.f ? 1.f : -1.f); 68 | 69 | float F = fresnel::DielectricReflectance(eta, dot(record.wo, wh)); 70 | record.pdf = abs(ggx::Pdf(wo, wh, alpha) * (sample_reflect ? F : 1.f - F) * dwh_dwo); 71 | } 72 | 73 | CUDA_HOSTDEVICE void Sample(BsdfSamplingRecord &record) const noexcept { 74 | float2 xi = record.sampler->Next2(); 75 | float3 wo = record.wo * (record.wo.z > 0.f ? 1.f : -1.f); 76 | float3 wh = ggx::Sample(wo, alpha, xi); 77 | float cos_theta_t = 0.f; 78 | float F = fresnel::DielectricReflectance(eta, dot(record.wo, wh), cos_theta_t); 79 | if (record.sampler->Next() < F) { 80 | record.wi = optix::Reflect(record.wo, wh); 81 | record.sampled_type = EBsdfLobeType::GlossyReflection; 82 | 83 | #ifndef GGX_Sample_Visible_Area 84 | if (record.wi.z * record.wo.z <= 0.f) 85 | return; 86 | #endif 87 | } else { 88 | if (optix::IsZero(cos_theta_t)) return; 89 | record.wi = optix::Refract(record.wo, wh, cos_theta_t, eta); 90 | record.sampled_type = EBsdfLobeType::GlossyTransmission; 91 | if (record.wi.z * record.wo.z >= 0.f) 92 | return; 93 | } 94 | GetPdf(record); 95 | GetBsdf(record); 96 | } 97 | }; 98 | 99 | CUDA_DEVICE Local GetLocal(float2 sampled_tex) const noexcept { 100 | Local local_bsdf; 101 | local_bsdf.alpha = alpha.Sample(sampled_tex).x; 102 | local_bsdf.eta = eta; 103 | local_bsdf.specular_reflectance = specular_reflectance.Sample(sampled_tex); 104 | local_bsdf.specular_transmittance = specular_transmittance.Sample(sampled_tex); 105 | return local_bsdf; 106 | } 107 | }; 108 | 109 | }// namespace Pupil::optix::material --------------------------------------------------------------------------------