├── .gitignore ├── demo ├── src │ ├── volume │ │ ├── leb_volume_gpu.cpp │ │ ├── leb_3d_cache.cpp │ │ ├── grid_volume.cpp │ │ └── heuristic_cache.cpp │ ├── tools │ │ ├── security.cpp │ │ ├── string_utilities.cpp │ │ ├── shader_utils.cpp │ │ ├── stream.cpp │ │ └── profiling_helper.cpp │ ├── CMakeLists.txt │ ├── graphics │ │ ├── event_collector.cpp │ │ ├── dx12_backend_fence.cpp │ │ ├── dx12_backend_profiling_scope.cpp │ │ ├── dx12_backend_imgui.cpp │ │ ├── dx12_backend_swap_chain.cpp │ │ ├── dx12_backend_command_queue.cpp │ │ └── dx12_backend_compute_shader.cpp │ ├── rendering │ │ └── frustum.cpp │ └── render_pipeline │ │ ├── morton_cache.cpp │ │ ├── frustum_renderer.cpp │ │ └── camera_controller.cpp └── include │ ├── render_pipeline │ ├── rendering_mode.h │ ├── camera.h │ ├── morton_cache.h │ ├── frustum_renderer.h │ ├── sky.h │ ├── grid_renderer.h │ ├── camera_controller.h │ ├── constant_buffers.h │ ├── leb_renderer.h │ └── volume_pipeline.h │ ├── rendering │ ├── aabb.h │ └── frustum.h │ ├── volume │ ├── leb_3d_cache.h │ ├── grid_volume.h │ ├── volume_generation.h │ ├── heuristic_cache.h │ ├── leb_volume_gpu.h │ ├── leb_volume.h │ └── leb_3d_eval.h │ ├── tools │ ├── shader_utils.h │ ├── security.h │ ├── imgui_helpers.h │ ├── string_utilities.h │ ├── profiling_helper.h │ ├── stream.h │ └── stream.inl │ ├── graphics │ ├── event_collector.h │ ├── dx12_helpers.h │ ├── descriptors.h │ └── types.h │ ├── math │ ├── types.h │ └── operators.h │ └── imgui │ ├── imgui_impl_dx12.h │ └── imgui_impl_win32.h ├── cmake ├── FindD3D12.cmake ├── CMakePlatforms.cmake └── CMakeBuildSettings.cmake ├── shaders ├── TonemapFrame.compute ├── shader_lib │ ├── sky.hlsl │ ├── constant_buffers.hlsl │ ├── rand.hlsl │ ├── common.hlsl │ ├── grid_utilities.hlsl │ ├── intersection.hlsl │ └── leb_utilities.hlsl ├── Blit.graphics ├── AccumulateFrame.compute ├── LEB │ ├── IntersectBVH.compute │ ├── Density.compute │ ├── Rasterizer.graphics │ └── ForwardPT.compute ├── Grid │ ├── Density.compute │ ├── Rasterizer.graphics │ └── ForwardPT.compute ├── Frustum.graphics └── Sky │ └── SkyPreCompute.compute ├── projects ├── CMakeLists.txt ├── render_volume.cpp └── convert_grid_to_leb3d.cpp ├── README.md ├── LICENSE ├── CMakeLists.txt └── dependencies.bat /.gitignore: -------------------------------------------------------------------------------- 1 | build/ 2 | output/ 3 | 3rd/ 4 | volumes/ -------------------------------------------------------------------------------- /demo/src/volume/leb_volume_gpu.cpp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AnisB/leb3D/HEAD/demo/src/volume/leb_volume_gpu.cpp -------------------------------------------------------------------------------- /demo/include/render_pipeline/rendering_mode.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | enum class RenderingMode 4 | { 5 | DebugView = 0, 6 | DensityIntegration, 7 | ForwardPT, 8 | Count 9 | }; -------------------------------------------------------------------------------- /demo/include/rendering/aabb.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | // Project includes 4 | #include "math/types.h" 5 | 6 | // System includes 7 | #include 8 | 9 | struct AABB 10 | { 11 | float3 min = { FLT_MAX, FLT_MAX, FLT_MAX }; 12 | float3 max = { -FLT_MAX, -FLT_MAX, -FLT_MAX }; 13 | }; -------------------------------------------------------------------------------- /demo/include/volume/leb_3d_cache.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | // Internal includes 4 | #include "math/types.h" 5 | 6 | // External includes 7 | #include 8 | 9 | class Leb3DCache 10 | { 11 | public: 12 | // Cst & Dst 13 | Leb3DCache(); 14 | ~Leb3DCache(); 15 | const float4x4* get_cache() const{ return m_Cache.data(); } 16 | 17 | private: 18 | uint32_t m_CacheDepth = 0; 19 | std::vector m_Cache = {}; 20 | }; 21 | -------------------------------------------------------------------------------- /demo/src/tools/security.cpp: -------------------------------------------------------------------------------- 1 | // Internal includes 2 | #include "tools/security.h" 3 | 4 | // External includes 5 | #include 6 | #include 7 | #include 8 | 9 | void __handle_fail(const char* msg, const char* file_name, int) 10 | { 11 | printf("[ERROR] %s\n", msg); 12 | printf("Triggered at %s\n", file_name); 13 | #if WIN32 14 | __debugbreak(); 15 | #else 16 | __builtin_trap(); 17 | #endif 18 | exit(-1); 19 | } 20 | -------------------------------------------------------------------------------- /demo/include/tools/shader_utils.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | // SDK includes 4 | #include "graphics/descriptors.h" 5 | 6 | // Compile a shader and replace if succeded 7 | void compile_and_replace_compute_shader(GraphicsDevice device, const ComputeShaderDescriptor& csd, ComputeShader& oldCS, bool experimental = false); 8 | 9 | // Compile a graphics pipeline and replace if succeded 10 | void compile_and_replace_graphics_pipeline(GraphicsDevice device, const GraphicsPipelineDescriptor& gpd, GraphicsPipeline& oldGP); -------------------------------------------------------------------------------- /demo/include/tools/security.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #ifdef assert 4 | #undef assert 5 | #endif 6 | 7 | // Function that handles a fail 8 | void __handle_fail(const char* msg, const char* file_name, int line); 9 | 10 | // Assert functions 11 | #define assert_fail_msg(msg) __handle_fail(msg, __FILE__, __LINE__) 12 | #define assert_fail() assert_fail_msg("") 13 | #define assert_msg(condition, msg) if(!(condition)) assert_fail_msg(msg) 14 | #define assert(condition) assert_msg(condition, "") 15 | #define not_implemented() assert_fail_msg("function not implemented") 16 | -------------------------------------------------------------------------------- /cmake/FindD3D12.cmake: -------------------------------------------------------------------------------- 1 | # Point to the local includes 2 | set(D3D12_INCLUDE_DIRS "${PROJECT_3RD_INCLUDES}") 3 | 4 | # List of required libraries 5 | set(D3D12_LIBRARIES d3d12.lib dxgi.lib d3dcompiler.lib dxcompiler.lib) 6 | 7 | # Handle the QUIETLY and REQUIRED arguments and set D3D12_FOUND to TRUE 8 | include(FindPackageHandleStandardArgs) 9 | 10 | # if all listed variables are TRUE 11 | find_package_handle_standard_args(D3D12 DEFAULT_MSG D3D12_INCLUDE_DIRS D3D12_LIBRARIES) 12 | 13 | # Hide from UI 14 | mark_as_advanced(D3D12_INCLUDE_DIRS D3D12_LIBRARIES) -------------------------------------------------------------------------------- /demo/include/render_pipeline/camera.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | // SDK includes 4 | #include "math/types.h" 5 | 6 | struct Camera 7 | { 8 | // Projection parameters 9 | float fov; 10 | float2 nearFar; 11 | float aspectRatio; 12 | 13 | // View parameters 14 | float3 position; 15 | float3 angles; 16 | 17 | // Zoom parameters 18 | float3 scaleOffset; 19 | 20 | // Transformation matrices 21 | float4x4 view; 22 | float4x4 projection; 23 | 24 | // Compound matrices 25 | float4x4 viewProjection; 26 | float4x4 invViewProjection; 27 | }; 28 | -------------------------------------------------------------------------------- /demo/include/rendering/frustum.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | // SDK includes 4 | #include "math/types.h" 5 | #include "rendering/aabb.h" 6 | 7 | // Structure that describes a frustum plane 8 | struct Plane 9 | { 10 | float3 normal; 11 | float d; 12 | }; 13 | 14 | struct Frustum 15 | { 16 | Plane planes[6]; 17 | }; 18 | 19 | // Function that extracts the frustum planes from a view projection matrix 20 | void extract_planes_from_view_projection_matrix(const float4x4 viewProj, Frustum& frustum); 21 | 22 | // Intersect an aabb and a frustum 23 | bool frustum_aabb_intersect(const Frustum& frustum, const AABB& aabb); -------------------------------------------------------------------------------- /demo/include/volume/grid_volume.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | // Internal includes 4 | #include "math/types.h" 5 | 6 | // External includes 7 | #include 8 | 9 | struct GridVolume 10 | { 11 | // Properties of the grid 12 | float3 scale; 13 | uint3 resolution; 14 | 15 | // Density array of the cells 16 | std::vector densityArray; 17 | }; 18 | 19 | namespace grid_volume 20 | { 21 | // Export a packed mesh to disk 22 | void export_grid_volume(const GridVolume& gridVolume, const char* path); 23 | 24 | // Import a packed mesh from disk 25 | void import_grid_volume(const char* path, GridVolume& gridVolume); 26 | } -------------------------------------------------------------------------------- /shaders/TonemapFrame.compute: -------------------------------------------------------------------------------- 1 | #define WORKGROUP_RES 8 2 | 3 | // CBVs 4 | #define GLOBAL_CB_BINDING_SLOT b0 5 | 6 | // Includes 7 | #include "shader_lib/common.hlsl" 8 | #include "shader_lib/constant_buffers.hlsl" 9 | 10 | // UAVs 11 | RWTexture2D _ColorTexture: register(u0); 12 | 13 | [numthreads(WORKGROUP_RES, WORKGROUP_RES, 1)] 14 | void TonemapFrame(uint2 threadID : SV_DispatchThreadID) 15 | { 16 | // Accumulate the new sample with the previous history 17 | float3 finalColor = _ColorTexture[threadID].xyz; 18 | 19 | // Tonemap and export 20 | _ColorTexture[threadID] = float4(1.0 - tonemap(finalColor), 1.0); 21 | } -------------------------------------------------------------------------------- /demo/include/volume/volume_generation.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | // Internal includes 4 | #include "volume/leb_volume.h" 5 | #include "volume/grid_volume.h" 6 | #include "volume/heuristic_cache.h" 7 | 8 | namespace leb_volume 9 | { 10 | // Sample the grid at a single value 11 | float evaluate_grid_value(const GridVolume& gridVolume, float3 position); 12 | 13 | // Get the means in the tetrahedron 14 | float mean_density_element(const GridVolume& gridVolume, uint32_t depth, const Tetrahedron& tetra); 15 | 16 | // Fit volume to grid 17 | uint32_t fit_volume_to_grid(LEBVolume& lebVolume, const GridVolume& gridVolume, const HeuristicCache& heuristicCache, const FittingParameters& parameters); 18 | } -------------------------------------------------------------------------------- /projects/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # Convert grid to leb3d 2 | bacasable_exe(convert_grid_to_leb3d "projects" "convert_grid_to_leb3d.cpp;" "${DEMO_SDK_INCLUDES};") 3 | target_link_libraries(convert_grid_to_leb3d "demo" "${D3D12_LIBRARIES}") 4 | set_target_properties(convert_grid_to_leb3d PROPERTIES VS_DEBUGGER_COMMAND_ARGUMENTS "${PROJECT_SOURCE_DIR}") 5 | 6 | # Render volume 7 | bacasable_exe(render_volume "projects" "render_volume.cpp;" "${DEMO_SDK_INCLUDES}") 8 | target_link_libraries(render_volume "demo" "${D3D12_LIBRARIES}") 9 | copy_dir_next_to_binary(render_volume "${PROJECT_3RD_BINARY}/D3D12" "D3D12") 10 | set_target_properties(render_volume PROPERTIES VS_DEBUGGER_COMMAND_ARGUMENTS "${PROJECT_SOURCE_DIR}") 11 | -------------------------------------------------------------------------------- /demo/src/volume/leb_3d_cache.cpp: -------------------------------------------------------------------------------- 1 | // Internal includes 2 | #include "volume/leb_3d_eval.h" 3 | #include "volume/leb_3d_cache.h" 4 | #include "math/operators.h" 5 | 6 | Leb3DCache::Leb3DCache() 7 | { 8 | // Keep the cache depth 9 | m_CacheDepth = LEB_CACHE_SIZE; 10 | uint32_t matrixCount = 2ULL << m_CacheDepth; 11 | m_Cache.resize(matrixCount); 12 | 13 | // Build the CPU table 14 | leb__IdentityMatrix4x4(m_Cache[0]); 15 | 16 | // Build the caches 17 | for (uint64_t heapID = 1ULL; heapID < (2ULL << m_CacheDepth); ++heapID) 18 | { 19 | float4x4 mat; 20 | leb__DecodeTransformationMatrix(heapID, 0, mat); 21 | m_Cache[heapID] = mat; 22 | } 23 | } 24 | 25 | Leb3DCache::~Leb3DCache() 26 | { 27 | } 28 | -------------------------------------------------------------------------------- /demo/include/graphics/event_collector.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | // External includes 4 | #include 5 | #include 6 | 7 | enum class MouseButton 8 | { 9 | Left, 10 | Middle, 11 | Right, 12 | Other 13 | }; 14 | 15 | enum class FrameEvent 16 | { 17 | Close, 18 | Destroy, 19 | MouseMovement, 20 | MouseWheel, 21 | MouseButton, 22 | KeyDown, 23 | KeyUp, 24 | Raw 25 | }; 26 | 27 | struct EventData 28 | { 29 | FrameEvent type; 30 | uint32_t data0; 31 | uint64_t data1; 32 | int64_t data2; 33 | }; 34 | 35 | namespace event_collector 36 | { 37 | void push_event(const EventData& event); 38 | bool peek_event(EventData& event); 39 | void request_draw(); 40 | bool active_draw_request(); 41 | void draw_done(); 42 | void clear(); 43 | } 44 | -------------------------------------------------------------------------------- /demo/src/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # The defines we need for the library 2 | set(DEMO_SDK_SOURCE ${DEMO_SDK_ROOT}/src) 3 | 4 | sub_directory_list(sub_projects_headers "${DEMO_SDK_INCLUDES}") 5 | foreach(header_dir ${sub_projects_headers}) 6 | bacasable_headers(tmp_header_list "${DEMO_SDK_INCLUDES}/${header_dir}" "${header_dir}") 7 | list(APPEND header_files "${tmp_header_list}") 8 | endforeach() 9 | 10 | sub_directory_list(sub_projects_sources "${DEMO_SDK_SOURCE}") 11 | foreach(source_dir ${sub_projects_sources}) 12 | bacasable_sources(tmp_source_list "${DEMO_SDK_SOURCE}/${source_dir}" "${source_dir}") 13 | list(APPEND source_files "${tmp_source_list}") 14 | endforeach() 15 | 16 | # Generate the static library 17 | bacasable_static_lib(demo "demo" "${header_files};${source_files};" "${DEMO_SDK_INCLUDES};${PROJECT_3RD_INCLUDES};${D3D12_INCLUDE_DIRS};") -------------------------------------------------------------------------------- /shaders/shader_lib/sky.hlsl: -------------------------------------------------------------------------------- 1 | #ifndef SKY_HLSL 2 | #define SKY_HLSL 3 | 4 | // Sky includes 5 | #include "shader_lib/sky_utilities.hlsl" 6 | 7 | float3 sky_color(float3 rayDir) 8 | { 9 | // We need to ensure that this is never lower than a certain radius. 10 | float3 luminance, throughput; 11 | IntegrateLuminanceThroughput(float3(0.0, 6371.0, 0.0), rayDir.xyz, _SunDirection, 0.0, 4, _TransmittanceLUTTexture, _MultiScatteringLUTTexture, _sampler_linear_clamp, luminance, throughput); 12 | return luminance * _SkyIntensity; 13 | } 14 | 15 | float3 sun_color() 16 | { 17 | float viewZenithCosAngle = dot(_SunDirection, float3(0, 1, 0)); 18 | float2 uv; 19 | LutTransmittanceParamsToUv(6371.0, viewZenithCosAngle, uv); 20 | return _TransmittanceLUTTexture.SampleLevel(_sampler_linear_clamp, uv, 0).rgb * _SunIntensity; 21 | } 22 | 23 | #endif // SKY_HLSL -------------------------------------------------------------------------------- /demo/include/tools/imgui_helpers.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | // Internal includes 4 | #include "imgui/imgui.h" 5 | 6 | template 7 | void imgui_dropdown_enum(T& currentValue, const char* label, const char* mode_labels[]) 8 | { 9 | const char* current_item = mode_labels[(uint32_t)currentValue]; 10 | if (ImGui::BeginCombo(label, current_item)) // "Dropdown" is the label of the dropdown 11 | { 12 | for (int i = 0; i < (uint32_t)T::Count; i++) 13 | { 14 | bool is_selected = (current_item == mode_labels[i]); // Check if the item is selected 15 | if (ImGui::Selectable(mode_labels[i], is_selected)) 16 | currentValue = (T)i; 17 | 18 | // Set the initial focus when opening the combo (optional) 19 | if (is_selected) 20 | ImGui::SetItemDefaultFocus(); 21 | } 22 | ImGui::EndCombo(); 23 | } 24 | } -------------------------------------------------------------------------------- /demo/include/tools/string_utilities.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | // External includes 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | // String manipulation 10 | std::wstring convert_to_wide(const std::string& str); 11 | std::wstring convert_to_wide(const char* str, uint32_t strLength); 12 | std::string convert_to_regular(const std::wstring& str); 13 | void split(const std::string& parString, char parSeparator, std::vector& _out); 14 | 15 | template 16 | T convert_from_string(const std::string& _string) 17 | { 18 | std::stringstream stream(_string); 19 | T val; 20 | stream >> val; 21 | return val; 22 | } 23 | 24 | template 25 | std::string to_string_with_precision(const T a_value, const int n = 6) 26 | { 27 | std::ostringstream out; 28 | out.precision(n); 29 | out << std::fixed << a_value; 30 | return std::move(out).str(); 31 | } 32 | -------------------------------------------------------------------------------- /demo/include/tools/profiling_helper.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | // Internal includes 4 | #include "graphics/types.h" 5 | 6 | // External includes 7 | #include 8 | 9 | class ProfilingHelper 10 | { 11 | public: 12 | // Cst & Dst 13 | ProfilingHelper(); 14 | ~ProfilingHelper(); 15 | 16 | // Initialization and release 17 | void initialize(GraphicsDevice device, uint32_t numScopes); 18 | void release(); 19 | 20 | // Runtime functions 21 | void start_profiling(CommandBuffer cmd, uint32_t index); 22 | void end_profiling(CommandBuffer cmd, uint32_t index); 23 | void process_scopes(CommandQueue cmdQ); 24 | uint64_t get_scope_last_duration(uint32_t index); 25 | uint64_t get_scope_max_duration(uint32_t index); 26 | void reset_durations(); 27 | 28 | private: 29 | uint32_t m_NumScopes = 0; 30 | std::vector m_Scopes; 31 | std::vector m_LastDurationArray; 32 | std::vector m_MaxDurationArray; 33 | }; -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | This repository contains a project that implements the technique described in the paper [**Adaptive Tetrahedral Grids for Volumetric Path-Tracing**](https://arxiv.org/abs/2506.11510) published at the conference *Siggraph 2025*. 2 | 3 | ![gensub_273s2-file2](https://github.com/user-attachments/assets/99a0f720-08b5-4355-952a-cdcd5fc91b91) 4 | 5 | This demo relies on **DX12** and some **Shader model 6.6** features. Here are the steps to get it running: 6 | 7 | - Clone the repo and open a terminal in the root folder of the repo 8 | - Run the script **dependencies.bat** 9 | - Create a build folder or **mkdir build** 10 | - Move to that folder or **cd build** 11 | - Open a terminal if it is not already the case and type **cmake ..** 12 | 13 | This will generate a solution that you can open in Microsoft Visual Studio and the two executables. One that converts a grid structure to a leb3D structure and one that allows to visualize both. 14 | 15 | Cheers! 16 | -------------------------------------------------------------------------------- /demo/include/render_pipeline/morton_cache.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | // Project includes 4 | #include "math/types.h" 5 | 6 | // System includes 7 | #include 8 | #include 9 | 10 | class MortonCache 11 | { 12 | public: 13 | MortonCache(); 14 | ~MortonCache(); 15 | 16 | // Helper functions 17 | void build_cache(const float3* positionArray, uint32_t numPositions); 18 | uint32_t get_closest_element(const float3& position); 19 | 20 | // Internal element holder 21 | struct Element 22 | { 23 | // Morton code of the element 24 | uint64_t code; 25 | 26 | // Index of the element 27 | uint32_t index; 28 | }; 29 | private: 30 | uint64_t evaluate_morton_code(const float3& targetPosition); 31 | 32 | private: 33 | std::vector m_Cache; 34 | uint32_t m_NumElements = 0; 35 | float3 m_MinPosition = { FLT_MAX, FLT_MAX, FLT_MAX }; 36 | float3 m_MaxPosition = { -FLT_MAX, -FLT_MAX, -FLT_MAX }; 37 | }; -------------------------------------------------------------------------------- /demo/include/volume/heuristic_cache.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | // Internal includes 4 | #include "volume/grid_volume.h" 5 | 6 | // External includes 7 | #include 8 | 9 | // Structure that allows us to evalute our heuristic 10 | struct HeuristicCache 11 | { 12 | // Number of levels in our cache 13 | uint32_t numLevels; 14 | // Top resolution of our grid (assume it's cubic texture, but doesn't have to be) 15 | uint32_t resolution; 16 | // Per level resolution of our grid 17 | std::vector resolutions; 18 | // Per level offsets to access the heuristic cache 19 | std::vector offsets; 20 | // Global moment buffer 21 | std::vector momentArray; 22 | }; 23 | 24 | namespace heuristic_cache 25 | { 26 | // Build the cache 27 | void build_heuristic_cache(const GridVolume& volume, HeuristicCache& cache); 28 | 29 | // Sample the cache 30 | float4 sample_cache(const HeuristicCache& cache, const float3& position, uint32_t depth); 31 | } -------------------------------------------------------------------------------- /demo/src/tools/string_utilities.cpp: -------------------------------------------------------------------------------- 1 | // Internal includes 2 | #include "tools/string_utilities.h" 3 | 4 | std::wstring convert_to_wide(const std::string& str) 5 | { 6 | size_t stringSize = str.size(); 7 | std::wstring wc(stringSize, L'#'); 8 | mbstowcs(&wc[0], str.c_str(), stringSize); 9 | return wc; 10 | } 11 | 12 | std::wstring convert_to_wide(const char* str, uint32_t strLength) 13 | { 14 | size_t stringSize = strLength; 15 | std::wstring wc(stringSize, L'#'); 16 | mbstowcs(&wc[0], str, stringSize); 17 | return wc; 18 | } 19 | 20 | std::string convert_to_regular(const std::wstring& wstr) 21 | { 22 | size_t stringSize = wstr.size(); 23 | std::string str(stringSize, '#'); 24 | wcstombs(&str[0], wstr.c_str(), stringSize); 25 | return str; 26 | } 27 | 28 | void split(const std::string& parString, char parSeparator, std::vector& _out) 29 | { 30 | std::stringstream streamObj(parString); 31 | std::string item; 32 | while (std::getline(streamObj, item, parSeparator)) 33 | { 34 | _out.push_back(item); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /shaders/Blit.graphics: -------------------------------------------------------------------------------- 1 | // SRVs 2 | Texture2D _InputTexture : register(t0); 3 | 4 | struct VertexInput 5 | { 6 | uint instanceID : SV_InstanceID; 7 | uint vertexID : SV_VertexID; 8 | }; 9 | 10 | struct VertexOutput 11 | { 12 | float4 positionCS : SV_POSITION; 13 | }; 14 | 15 | VertexOutput vert(VertexInput input) 16 | { 17 | VertexOutput output; 18 | float2 uv = float2((input.vertexID << 1) & 2, input.vertexID & 2); 19 | output.positionCS = float4(uv * 2.0 - 1.0, 0.0, 1.0); 20 | return output; 21 | } 22 | 23 | struct PixelInput 24 | { 25 | float4 positionCS : SV_POSITION; 26 | }; 27 | 28 | float4 frag_rgba(PixelInput input) : SV_Target0 29 | { 30 | return _InputTexture.Load(float3(input.positionCS.xy, 0.0)); 31 | } 32 | 33 | float frag_r32(PixelInput input) : SV_Target0 34 | { 35 | return _InputTexture.Load(float3(input.positionCS.xy, 0.0)).x; 36 | } 37 | 38 | float2 frag_rg32(PixelInput input) : SV_Target0 39 | { 40 | return _InputTexture.Load(float3(input.positionCS.xy, 0.0)).xy; 41 | } 42 | -------------------------------------------------------------------------------- /shaders/AccumulateFrame.compute: -------------------------------------------------------------------------------- 1 | #define WORKGROUP_RES 8 2 | 3 | // CBVs 4 | #define GLOBAL_CB_BINDING_SLOT b0 5 | 6 | // UAVs 7 | #define SAMPLE_TEXTURE_RW_BINDING_SLOT u0 8 | #define HISTORY_TEXTURE_RW_BINDING_SLOT u1 9 | 10 | // Includes 11 | #include "shader_lib/common.hlsl" 12 | #include "shader_lib/constant_buffers.hlsl" 13 | 14 | // UAVs 15 | RWTexture2D _SampleTexture: register(SAMPLE_TEXTURE_RW_BINDING_SLOT); 16 | RWTexture2D _HistoryTexture: register(HISTORY_TEXTURE_RW_BINDING_SLOT); 17 | 18 | [numthreads(WORKGROUP_RES, WORKGROUP_RES, 1)] 19 | void AccumulateFrame(uint2 threadID : SV_DispatchThreadID) 20 | { 21 | // Accumulate the new sample with the previous history 22 | float3 finalColor = _SampleTexture[threadID].xyz * _FrameAccumulationFactors.x + _HistoryTexture[threadID].xyz * _FrameAccumulationFactors.y; 23 | 24 | // Export raw for next frame 25 | _HistoryTexture[threadID] = float4(finalColor, 1.0); 26 | 27 | // Tonemap and export 28 | _SampleTexture[threadID] = float4(tonemap(finalColor), 1.0); 29 | } -------------------------------------------------------------------------------- /demo/src/graphics/event_collector.cpp: -------------------------------------------------------------------------------- 1 | // Internal includes 2 | #include "graphics/event_collector.h" 3 | 4 | namespace event_collector 5 | { 6 | // Queue that is used to keep track of the events 7 | static std::queue eventQueue; 8 | 9 | // Flag that tracks if a rendering should be done 10 | static bool drawRequested = false; 11 | 12 | // If an event has been recorded process it 13 | bool peek_event(EventData& event) 14 | { 15 | if (eventQueue.size() > 0) 16 | { 17 | event = eventQueue.front(); 18 | eventQueue.pop(); 19 | return true; 20 | } 21 | return false; 22 | } 23 | 24 | // Keep track of this event 25 | void push_event(const EventData& event) 26 | { 27 | eventQueue.push(event); 28 | } 29 | 30 | void request_draw() 31 | { 32 | drawRequested = true; 33 | } 34 | 35 | bool active_draw_request() 36 | { 37 | return drawRequested; 38 | } 39 | 40 | void draw_done() 41 | { 42 | drawRequested = false; 43 | } 44 | 45 | void clear() 46 | { 47 | drawRequested = false; 48 | std::queue empty; 49 | std::swap(eventQueue, empty); 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2025 Anis Benyoub 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /projects/render_volume.cpp: -------------------------------------------------------------------------------- 1 | // Internal includes 2 | #include "render_pipeline/volume_pipeline.h" 3 | 4 | // External includes 5 | #define NOMINMAX 6 | #include 7 | #include 8 | 9 | int CALLBACK main(HINSTANCE hInstance, HINSTANCE, PWSTR, int) 10 | { 11 | // Path of the exe 12 | const std::string& exePath(__argv[0]); 13 | uint32_t loc = (uint32_t)exePath.find_last_of('\\'); 14 | 15 | // Evaluate the project directory 16 | const std::string& exeDir = exePath.substr(0, loc); 17 | const std::string& projectDir = __argv[1]; 18 | 19 | // Create and init the pipeline 20 | const std::string& gridVolume = projectDir + "/volumes/wdas_cloud_grid.bin"; 21 | const std::string& lebVolume = projectDir + "/volumes/wdas_cloud_leb.bin"; 22 | 23 | // Volume pipeline 24 | VolumePipeline pipeline; 25 | pipeline.initialize(hInstance, projectDir.c_str(), exeDir.c_str(), gridVolume.c_str(), lebVolume.c_str()); 26 | 27 | // Trigger the render loop 28 | pipeline.render_loop(); 29 | 30 | // release the pipeline 31 | pipeline.release(); 32 | return 0; 33 | } -------------------------------------------------------------------------------- /cmake/CMakePlatforms.cmake: -------------------------------------------------------------------------------- 1 | # This module is shared; use include blocker. 2 | if( _PLATFORMS_ ) 3 | return() 4 | endif() 5 | 6 | # Mark it as processed 7 | set(_PLATFORMS_ 1) 8 | 9 | # Detect target platform 10 | set(PLATFORM_WINDOWS 1) 11 | set(PLATFORM_NAME "windows") 12 | add_definitions(-DWINDOWSPC) 13 | 14 | message(STATUS "Detected platform: ${PLATFORM_NAME}") 15 | 16 | # Detect target architecture 17 | if(PLATFORM_WINDOWS AND CMAKE_CL_64) 18 | set(PLATFORM_64BIT 1) 19 | endif() 20 | 21 | # Configure CMake global variables 22 | set(CMAKE_INSTALL_MESSAGE LAZY) 23 | 24 | # Set the output folders based on the identifier 25 | set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${PROJECT_SOURCE_DIR}/output/lib) 26 | set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${PROJECT_SOURCE_DIR}/output/lib) 27 | set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${PROJECT_SOURCE_DIR}/output/bin) 28 | set(CMAKE_VS_INCLUDE_INSTALL_TO_DEFAULT_BUILD 1) 29 | 30 | # Find D3D12 and enable it if possible 31 | FIND_PACKAGE(D3D12) 32 | if (D3D12_FOUND) 33 | add_definitions(-DD3D12_SUPPORTED) 34 | add_definitions(-DDX12_SDK_VERSION=717) 35 | endif() 36 | 37 | # OpenMP required for the parallel computation 38 | find_package(OpenMP REQUIRED) -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.10) 2 | 3 | project(leb3D) 4 | 5 | # Declare the global variables for the cmake project 6 | set(PROJECT_3RD_INCLUDES "${PROJECT_SOURCE_DIR}/3rd/include") 7 | set(PROJECT_3RD_BINARY "${PROJECT_SOURCE_DIR}/3rd/bin") 8 | set(PROJECT_3RD_LIBRARY "${PROJECT_SOURCE_DIR}/3rd/lib") 9 | 10 | # SDK 11 | set(DEMO_SDK_ROOT ${PROJECT_SOURCE_DIR}/demo) 12 | set(DEMO_SDK_INCLUDES ${DEMO_SDK_ROOT}/include) 13 | 14 | # Projecs 15 | set(DEMO_PROJECTS ${PROJECT_SOURCE_DIR}/projects) 16 | 17 | # Set the cmake path variable 18 | set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_SOURCE_DIR}/cmake") 19 | 20 | # This flag must be activated in order to handle properly folder flags 21 | set_property(GLOBAL PROPERTY USE_FOLDERS ON) 22 | 23 | # Define the cmake macros 24 | include(CMakePlatforms) 25 | include(CMakeMacros) 26 | include(CMakeBuildSettings) 27 | 28 | # Define the build options 29 | define_plaform_settings() 30 | 31 | # Create the list of allowed files to be included 32 | set(bacasable_source_extensions) 33 | list(APPEND bacasable_source_extensions ".h" ".cpp" ".inl") 34 | 35 | # Compile the sdk 36 | add_subdirectory(${DEMO_SDK_ROOT}/src) 37 | 38 | # Compile the projects 39 | add_subdirectory(${DEMO_PROJECTS}) 40 | -------------------------------------------------------------------------------- /demo/include/render_pipeline/frustum_renderer.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | // SDK includes 4 | #include "graphics/types.h" 5 | #include "volume/leb_volume_gpu.h" 6 | 7 | // System includes 8 | #include 9 | 10 | class FrustumRenderer 11 | { 12 | public: 13 | // Cst & Dst 14 | FrustumRenderer(); 15 | ~FrustumRenderer(); 16 | 17 | // Init and release 18 | void initialize(GraphicsDevice device, const LEBVolumeGPU& volume); 19 | void release(); 20 | 21 | // Reload the shaders 22 | void reload_shader(const std::string& shaderLibrary); 23 | 24 | // Rendering 25 | void upload_constant_buffers(CommandBuffer cmdB); 26 | void render_above(CommandBuffer cmdB, ConstantBuffer globalCB); 27 | void render_under(CommandBuffer cmdB, ConstantBuffer globalCB); 28 | 29 | private: 30 | // Device 31 | GraphicsDevice m_Device = 0; 32 | 33 | // Graphics pipeline 34 | GraphicsPipeline m_FrustumAboveGP = 0; 35 | GraphicsPipeline m_FrustumUnderGP = 0; 36 | ConstantBuffer m_UpdateCB = 0; 37 | float3 m_Position = { 0.0, 0.0, 0.0 }; 38 | float4x4 m_ViewProj = { 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0 }; 39 | float3 m_VolumeMin = { 0.0, 0.0, 0.0 }; 40 | float3 m_VolumeMax = { 0.0, 0.0, 0.0 }; 41 | }; -------------------------------------------------------------------------------- /demo/include/tools/stream.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | // External includes 4 | #include 5 | #include 6 | 7 | // Functions to pack/unpack a raw buffer (knwoing its size in both cases 8 | void pack_buffer(std::vector& buffer, size_t write_size, const char* data); 9 | void unpack_buffer(const char*& stream, size_t read_size, char* data); 10 | 11 | // Functions to pack/unpack a type T as bytes 12 | template 13 | void pack_bytes(std::vector& buffer, const T& type); 14 | template 15 | void unpack_bytes(const char*& stream, T& type); 16 | 17 | // Functions to pack/unpack a vector and its elements as types 18 | template 19 | void pack_vector_types(std::vector& buffer, const std::vector& data); 20 | template 21 | void unpack_vector_types(const char*& stream, std::vector& data); 22 | 23 | // Function to pack/unpack a buffer and its content as bytes 24 | template 25 | void pack_vector_bytes(std::vector& buffer, const std::vector& data); 26 | template 27 | void unpack_vector_bytes(const char*& stream, std::vector& data); 28 | 29 | // Function to pack types 30 | // Functions to pack/unpack a type T as bytes 31 | template 32 | void pack_type(std::vector& buffer, const T& type); 33 | template 34 | void unpack_type(const char*& stream, T& type); 35 | 36 | #include "stream.inl" -------------------------------------------------------------------------------- /demo/src/tools/shader_utils.cpp: -------------------------------------------------------------------------------- 1 | // Internal includes 2 | #include "graphics/dx12_backend.h" 3 | #include "tools/shader_utils.h" 4 | #include "tools/security.h" 5 | 6 | void compile_and_replace_compute_shader(GraphicsDevice device, const ComputeShaderDescriptor& csd, ComputeShader& oldCS, bool experimental) 7 | { 8 | // Compile the shader 9 | ComputeShader newCs = d3d12::compute_shader::create_compute_shader(device, csd, experimental); 10 | 11 | // If it succeded to compile replace. 12 | if (newCs != 0) 13 | { 14 | // Destroy the previously existing 15 | if (oldCS != 0) 16 | d3d12::compute_shader::destroy_compute_shader(oldCS); 17 | oldCS = newCs; 18 | } 19 | assert_msg(oldCS != 0, (csd.filename + " failed to compile.").c_str()); 20 | } 21 | 22 | void compile_and_replace_graphics_pipeline(GraphicsDevice device, const GraphicsPipelineDescriptor& gpd, GraphicsPipeline& oldGP) 23 | { 24 | // Compile the graphics pipeline 25 | GraphicsPipeline newGP = d3d12::graphics_pipeline::create_graphics_pipeline(device, gpd); 26 | 27 | // If it succeded to compile replace. 28 | if (newGP != 0) 29 | { 30 | // Destroy the previously existing 31 | if (oldGP != 0) 32 | d3d12::graphics_pipeline::destroy_graphics_pipeline(oldGP); 33 | oldGP = newGP; 34 | } 35 | assert_msg(oldGP != 0, (gpd.filename + " failed to compile.").c_str()); 36 | } -------------------------------------------------------------------------------- /demo/include/render_pipeline/sky.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | // Internal includes 4 | #include "graphics/types.h" 5 | #include "render_pipeline/camera.h" 6 | 7 | // External includes 8 | #include 9 | 10 | class Sky 11 | { 12 | public: 13 | Sky(); 14 | ~Sky(); 15 | 16 | // Init and Release functions 17 | void initialize(GraphicsDevice device); 18 | void release(); 19 | 20 | // Reload the shaders 21 | void reload_shaders(const std::string& shaderLibrary); 22 | 23 | // Pre-rendering steps 24 | void pre_render(CommandBuffer cmd); 25 | 26 | // Access the resources 27 | Texture transmittance_lut() const { return m_TransmittanceLutTex; } 28 | Texture multi_scattering_lut() const { return m_MultiScatteringLutTex; } 29 | ConstantBuffer constant_buffer() const { return m_SkyAtmosphereCB; } 30 | 31 | private: 32 | void update_constant_buffer(CommandBuffer cmdB); 33 | 34 | private: 35 | // Generic graphics resources 36 | GraphicsDevice m_Device = 0; 37 | 38 | // Buffers that hold the precomputations 39 | Texture m_TransmittanceLutTex = 0; 40 | Texture m_MultiScatteringLutTex = 0; 41 | Texture m_SkyViewLutTex = 0; 42 | 43 | // Required shaders 44 | ComputeShader m_TransmittanceLutCS = 0; 45 | ComputeShader m_MultiScatteringLutCS = 0; 46 | 47 | // Sky constant buffer 48 | ConstantBuffer m_SkyAtmosphereCB = 0; 49 | 50 | // Sampler 51 | Sampler m_LinearClampSampler = 0; 52 | }; -------------------------------------------------------------------------------- /demo/src/graphics/dx12_backend_fence.cpp: -------------------------------------------------------------------------------- 1 | #ifdef D3D12_SUPPORTED 2 | // Internal includes 3 | #include "graphics/dx12_backend.h" 4 | #include "graphics/dx12_containers.h" 5 | #include "graphics/dx12_helpers.h" 6 | #include "tools/string_utilities.h" 7 | #include "tools/security.h" 8 | 9 | namespace d3d12 10 | { 11 | namespace fence 12 | { 13 | Fence create_fence(GraphicsDevice graphicsDevice, uint64_t initialValue) 14 | { 15 | // Cast the types 16 | DX12GraphicsDevice* dx12_device = (DX12GraphicsDevice*)graphicsDevice; 17 | 18 | // Create the host object 19 | ID3D12Fence* fence = nullptr; 20 | assert_msg(dx12_device->device->CreateFence(initialValue, D3D12_FENCE_FLAG_SHARED, IID_PPV_ARGS(&fence)) == S_OK, "Failed to create Fence"); 21 | 22 | // Opaque cast and return 23 | return (Fence)fence; 24 | } 25 | 26 | void destroy_fence(Fence fence) 27 | { 28 | ID3D12Fence* dx12_fence = (ID3D12Fence*)fence; 29 | dx12_fence->Release(); 30 | } 31 | 32 | void set_value(Fence fence, uint64_t value) 33 | { 34 | ID3D12Fence* dx12_fence = (ID3D12Fence*)fence; 35 | dx12_fence->Signal(value); 36 | } 37 | 38 | uint64_t get_value(Fence fence) 39 | { 40 | ID3D12Fence* dx12_fence = (ID3D12Fence*)fence; 41 | return dx12_fence->GetCompletedValue(); 42 | } 43 | } 44 | } 45 | #endif -------------------------------------------------------------------------------- /cmake/CMakeBuildSettings.cmake: -------------------------------------------------------------------------------- 1 | # Minimal cmake version 2 | cmake_minimum_required(VERSION 3.10) 3 | 4 | macro(define_plaform_settings) 5 | add_compile_options(/Zi) 6 | add_compile_options($<$:/Od> $<$>:/Ox>) 7 | add_compile_options(/Ob2) 8 | add_compile_options($<$>:/Oi>) 9 | add_compile_options(/Ot) 10 | add_compile_options($<$>:/GT>) 11 | add_compile_options(/GF) 12 | 13 | if( PLATFORM_WINDOWS AND RUNTIME_TYPE STREQUAL "mt") 14 | add_compile_options($<$:/MTd> $<$>:/MT>) 15 | elseif( PLATFORM_WINDOWS AND RUNTIME_TYPE STREQUAL "md") 16 | add_compile_options($<$:/MDd> $<$>:/MD>) 17 | endif() 18 | 19 | add_compile_options(/Gy) 20 | add_compile_options(/fp:fast) 21 | replace_compile_flags("/GR" "/GR-") 22 | 23 | add_compile_options(/W4) 24 | add_compile_options(/WX) 25 | 26 | add_exe_linker_flags(/DEBUG) 27 | add_exe_linker_flags(/MAP) 28 | replace_linker_flags("/INCREMENTAL" "/INCREMENTAL:NO" debug) 29 | add_compile_options(/MP) 30 | add_compile_options(-D_HAS_EXCEPTIONS=0) 31 | replace_linker_flags("/debug" "/DEBUG" debug) 32 | replace_linker_flags("/machine:x64" "/MACHINE:X64") 33 | add_compile_options(-D_SCL_SECURE_NO_WARNINGS -D_CRT_SECURE_NO_WARNINGS -D_CRT_SECURE_NO_DEPRECATE) 34 | add_compile_options(-DSECURITY_WIN32) 35 | 36 | set(CMAKE_CXX_STANDARD 20) 37 | set(CMAKE_CXX_STANDARD_REQUIRED ON) 38 | set(CMAKE_CXX_EXTENSIONS OFF) 39 | endmacro() 40 | -------------------------------------------------------------------------------- /demo/src/tools/stream.cpp: -------------------------------------------------------------------------------- 1 | // Internal includes 2 | #include "tools/stream.h" 3 | 4 | // External includes 5 | #include 6 | #include 7 | 8 | void pack_buffer(std::vector& buffer, size_t write_size, const char* data) 9 | { 10 | if (write_size) { 11 | size_t old_size = buffer.size(); 12 | buffer.resize(old_size + write_size); 13 | memcpy(buffer.data() + old_size, data, write_size); 14 | } 15 | } 16 | 17 | void unpack_buffer(const char*& stream, size_t read_size, char* data) 18 | { 19 | if (read_size) { 20 | memcpy(data, stream, read_size); 21 | stream += read_size; 22 | } 23 | } 24 | 25 | template<> 26 | void pack_type(std::vector& buffer, const std::string& str) 27 | { 28 | // Previous size 29 | const size_t old_size = buffer.size(); 30 | 31 | // Num chars 32 | const uint32_t numChars = (uint32_t)str.size(); 33 | 34 | // Allocate the required memory 35 | buffer.resize(old_size + numChars + sizeof(uint32_t)); 36 | 37 | // Copy the size 38 | memcpy(buffer.data() + old_size, &numChars, sizeof(uint32_t)); 39 | 40 | // Copy the char 41 | if (numChars > 0) 42 | memcpy(buffer.data() + old_size + sizeof(uint32_t), str.data(), numChars); 43 | } 44 | 45 | template<> 46 | void unpack_type(const char*& stream, std::string& str) 47 | { 48 | // Read the size 49 | uint32_t numChars; 50 | memcpy(&numChars, stream, sizeof(uint32_t)); 51 | stream += sizeof(uint32_t); 52 | 53 | if (numChars > 0) 54 | { 55 | // Allocate memory space 56 | str.resize(numChars); 57 | memcpy((void*)str.data(), stream, numChars); 58 | stream += numChars; 59 | } 60 | } -------------------------------------------------------------------------------- /shaders/shader_lib/constant_buffers.hlsl: -------------------------------------------------------------------------------- 1 | #ifndef CONSTANT_BUFFERS_HLSL 2 | #define CONSTANT_BUFFERS_HLSL 3 | 4 | // Global constant buffer 5 | #if defined(GLOBAL_CB_BINDING_SLOT) 6 | cbuffer _GlobalCB : register(GLOBAL_CB_BINDING_SLOT) 7 | { 8 | // View projection matrix 9 | float4x4 _ViewProjectionMatrix; 10 | 11 | // Inverse view projection matrix 12 | float4x4 _InvViewProjectionMatrix; 13 | 14 | // Camera position in double as we are operating on planeraty coordinates 15 | float3 _CameraPosition; 16 | uint32_t _FrameIndex; 17 | 18 | // Screen size and inverse screen size 19 | float4 _ScreenSize; 20 | 21 | // Accumulation Factors 22 | float2 _FrameAccumulationFactors; 23 | // Density multiplier 24 | float _DensityMultiplier; 25 | // Volume albedo 26 | float _VolumeAlbedo; 27 | 28 | // Sun intensity 29 | float _SunIntensity; 30 | // Sky intensity 31 | float _SkyIntensity; 32 | // Padding 33 | float2 _PaddingGB0; 34 | 35 | // Padding 36 | float3 _SunDirection; 37 | float _PaddingGB2; 38 | }; 39 | #endif 40 | 41 | #if defined(LEB_CB_BINDING_SLOT) 42 | cbuffer _LEBCB : register(LEB_CB_BINDING_SLOT) 43 | { 44 | // Number of tetrahedrons in our structure 45 | uint32_t _NumTetrahedrons; 46 | // Initial primitive 47 | uint32_t _InitialPrimitive; 48 | float2 _PaddingLB0; 49 | 50 | // Volume scale 51 | float3 _LEBScale; 52 | 53 | // Padding 54 | float _PaddingLB1; 55 | }; 56 | #endif 57 | 58 | #endif // CONSTANT_BUFFERS_HLSL -------------------------------------------------------------------------------- /demo/include/tools/stream.inl: -------------------------------------------------------------------------------- 1 | template 2 | void pack_bytes(std::vector& buffer, const T& type) 3 | { 4 | pack_buffer(buffer, (uint32_t)sizeof(T), (const char*)&type); 5 | } 6 | 7 | template 8 | void unpack_bytes(const char*& stream, T& type) 9 | { 10 | unpack_buffer(stream, (uint32_t)sizeof(T), (char*)&type); 11 | } 12 | 13 | template 14 | void pack_vector_types(std::vector& buffer, const std::vector& data) 15 | { 16 | size_t num_elements = data.size(); 17 | pack_bytes(buffer, num_elements); 18 | if (num_elements) { 19 | for (uint32_t ele_idx = 0; ele_idx < num_elements; ++ele_idx) 20 | { 21 | pack_type(buffer, data[ele_idx]); 22 | } 23 | } 24 | } 25 | 26 | template 27 | void unpack_vector_types(const char*& stream, std::vector& data) 28 | { 29 | size_t num_elements; 30 | unpack_bytes(stream, num_elements); 31 | data.resize(num_elements); 32 | if (num_elements) { 33 | for (uint32_t ele_idx = 0; ele_idx < num_elements; ++ele_idx) 34 | { 35 | unpack_type(stream, data[ele_idx]); 36 | } 37 | } 38 | } 39 | 40 | template 41 | void pack_vector_bytes(std::vector& buffer, const std::vector& data) 42 | { 43 | size_t num_elements = data.size(); 44 | pack_bytes(buffer, num_elements); 45 | if (num_elements) { 46 | pack_buffer(buffer, num_elements * sizeof(T), (const char*)data.data()); 47 | } 48 | } 49 | 50 | template 51 | void unpack_vector_bytes(const char*& stream, std::vector& data) 52 | { 53 | size_t num_elements; 54 | unpack_bytes(stream, num_elements); 55 | data.resize(num_elements); 56 | if (num_elements) { 57 | unpack_buffer(stream, num_elements * sizeof(T), (char*)data.data()); 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /demo/src/volume/grid_volume.cpp: -------------------------------------------------------------------------------- 1 | // Internal includes 2 | #include "volume/grid_volume.h" 3 | #include "tools/stream.h" 4 | 5 | namespace grid_volume 6 | { 7 | // Export a packed mesh to disk 8 | void export_grid_volume(const GridVolume& gridVolume, const char* path) 9 | { 10 | // Vector that will hold our packed mesh 11 | std::vector binaryFile; 12 | 13 | // Pack the structure in a buffer 14 | pack_bytes(binaryFile, gridVolume.scale); 15 | pack_bytes(binaryFile, gridVolume.resolution); 16 | pack_vector_bytes(binaryFile, gridVolume.densityArray); 17 | 18 | // Write to disk 19 | FILE* pFile; 20 | pFile = fopen(path, "wb"); 21 | fwrite(binaryFile.data(), sizeof(char), binaryFile.size(), pFile); 22 | fclose(pFile); 23 | } 24 | 25 | // Export a packed mesh to disk 26 | void import_grid_volume(const char* path, GridVolume& gridVolume) 27 | { 28 | // Vector that will hold our packed mesh 29 | std::vector binaryFile; 30 | 31 | // Read from disk 32 | FILE* pFile; 33 | pFile = fopen(path, "rb"); 34 | _fseeki64(pFile, 0L, SEEK_END); 35 | __int64 fileSize = _ftelli64(pFile); 36 | binaryFile.resize(fileSize); 37 | _fseeki64(pFile, 0L, SEEK_SET); 38 | rewind(pFile); 39 | fread(binaryFile.data(), sizeof(char), fileSize, pFile); 40 | fclose(pFile); 41 | 42 | // Pack the structure in a buffer 43 | const char* binaryPtr = binaryFile.data(); 44 | unpack_bytes(binaryPtr, gridVolume.scale); 45 | unpack_bytes(binaryPtr, gridVolume.resolution); 46 | unpack_vector_bytes(binaryPtr, gridVolume.densityArray); 47 | } 48 | } -------------------------------------------------------------------------------- /demo/include/volume/leb_volume_gpu.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | // Internal includes 4 | #include "math/types.h" 5 | #include "volume/leb_volume.h" 6 | #include "volume/grid_volume.h" 7 | 8 | // External includes 9 | #include 10 | 11 | struct TetraData 12 | { 13 | // 4 Compressed plane equations (faces 0 -> 3) 14 | uint4 compressedEquations; 15 | // 4 neighbors 16 | uint4 neighbors; 17 | // Per element density 18 | float density; 19 | }; 20 | 21 | // Structure that holds everything we need to ray trace 22 | struct LEBVolumeGPU 23 | { 24 | // Camera and volume data (used for debug vis) 25 | bool frustumCull = false; 26 | float4x4 vpMat = { 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0 }; 27 | float3 cameraPosition = { 0.0, 0.0, 0.0 }; 28 | 29 | // Volume scale 30 | float3 scale = { 1.0, 1.0, 1.0 }; 31 | 32 | // Per-tetra data 33 | std::vector tetraData; 34 | std::vector centerArray; 35 | std::vector densityArray; 36 | 37 | // Outside interface data 38 | std::vector rtasIndexArray; 39 | std::vector rtasPositionArray; 40 | std::vector outsideElements; 41 | 42 | // Debug data 43 | std::vector positionArray; 44 | }; 45 | 46 | namespace leb_volume 47 | { 48 | // Convert leb volume CPUto leb volume GPU 49 | uint64_t convert_to_leb_volume_to_gpu(const LEBVolume& lebVolume, const GridVolume& gridVolume, const FittingParameters& fitParam, uint32_t maxDepth, LEBVolumeGPU& lebVolumeGPU); 50 | 51 | // Import a packed mesh from disk 52 | void import_leb_volume_gpu(const char* path, LEBVolumeGPU& lebVolumeGPU); 53 | 54 | // Export a packed mesh to disk 55 | void export_leb_volume_gpu(const LEBVolumeGPU& lebVolumeGPU, const char* path); 56 | } -------------------------------------------------------------------------------- /demo/include/render_pipeline/grid_renderer.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | // Includes 4 | #include "graphics/types.h" 5 | #include "graphics/descriptors.h" 6 | #include "render_pipeline/constant_buffers.h" 7 | #include "render_pipeline/camera.h" 8 | #include "render_pipeline/rendering_mode.h" 9 | #include "render_pipeline/sky.h" 10 | #include "volume/grid_volume.h" 11 | 12 | // System includes 13 | #include 14 | 15 | class GridRenderer 16 | { 17 | public: 18 | // Cst & Dst 19 | GridRenderer(); 20 | ~GridRenderer(); 21 | 22 | // Initialization and release 23 | void initialize(GraphicsDevice device); 24 | void release(); 25 | 26 | // Reloading 27 | void load_geometry(const std::string& filePath); 28 | void reload_shaders(const std::string& shaderLibrary); 29 | void upload_geometry(CommandQueue cmdQ, CommandBuffer cmdB); 30 | 31 | // Rendering 32 | void upload_constant_buffers(CommandBuffer cmdB); 33 | void render_volume(CommandBuffer cmdB, ConstantBuffer globalCB, RenderTexture colorRT, RenderTexture depthRT, RenderingMode mode, Sky& sky, const Camera& camera); 34 | 35 | // Resources 36 | ConstantBuffer grid_cb() const { return m_GridCB; } 37 | 38 | protected: 39 | // Graphics Device 40 | GraphicsDevice m_Device = 0; 41 | 42 | // CPU resources 43 | GridVolume m_Volume = GridVolume(); 44 | uint64_t m_NumCells = 0; 45 | bool m_SplitBuffer = false; 46 | std::vector m_ShaderDefines; 47 | Sampler m_LinearClampSampler = 0; 48 | 49 | // GPU Resources 50 | ConstantBuffer m_GridCB = 0; 51 | GraphicsBuffer m_DensityBuffer[2] = { 0, 0 }; 52 | 53 | // Shaders 54 | GraphicsPipeline m_RasterizerGP = 0; 55 | ComputeShader m_InsideDensityCS = 0; 56 | ComputeShader m_OutsideDensityCS = 0; 57 | ComputeShader m_InsidePTCS = 0; 58 | ComputeShader m_OutsidePTCS = 0; 59 | }; -------------------------------------------------------------------------------- /shaders/LEB/IntersectBVH.compute: -------------------------------------------------------------------------------- 1 | #define WORKGROUP_RES 8 2 | #define WORKGROUP_SIZE WORKGROUP_RES * WORKGROUP_RES 3 | 4 | // CBVs 5 | #define GLOBAL_CB_BINDING_SLOT b0 6 | #define LEB_CB_BINDING_SLOT b1 7 | 8 | // Includes 9 | #include "shader_lib/common.hlsl" 10 | #include "shader_lib/constant_buffers.hlsl" 11 | #include "shader_lib/intersection.hlsl" 12 | 13 | // SRVs 14 | StructuredBuffer _ElementIndexBuffer : register(t0); 15 | RaytracingAccelerationStructure _InterfaceRTAS : register(t1); 16 | 17 | // UAVs 18 | RWStructuredBuffer _PrimitiveBufferRW: register(u0); 19 | RWStructuredBuffer _DistanceBufferRW: register(u1); 20 | 21 | [numthreads(8, 8, 1)] 22 | void IntersectBVH(uint2 threadID : SV_DispatchThreadID) 23 | { 24 | // Ray description 25 | RayDesc ray; 26 | ray.Origin = _CameraPosition * _LEBScale; 27 | ray.Direction = normalize(evaluate_ray_direction(threadID.xy) * _LEBScale); 28 | ray.TMin = 0.000; 29 | ray.TMax = 100.0f; 30 | 31 | // Initialize the query 32 | RayQuery query; 33 | 34 | // Set up a trace. No work is done yet. 35 | query.TraceRayInline(_InterfaceRTAS, RAY_FLAG_FORCE_OPAQUE, 0xff, ray); 36 | 37 | // Run the traversal 38 | query.Proceed(); 39 | 40 | // Did we hit something? 41 | uint32_t primitiveIdx = threadID.x + _ScreenSize.x * threadID.y; 42 | if (query.CommittedStatus() == COMMITTED_TRIANGLE_HIT) 43 | { 44 | // Output to the visibility buffer 45 | uint32_t triangleID = query.CommittedPrimitiveIndex(); 46 | _PrimitiveBufferRW[primitiveIdx] = _ElementIndexBuffer[triangleID]; 47 | _DistanceBufferRW[primitiveIdx] = query.CommittedRayT(); 48 | } 49 | else 50 | { 51 | _PrimitiveBufferRW[primitiveIdx] = UINT32_MAX; 52 | } 53 | } -------------------------------------------------------------------------------- /demo/include/render_pipeline/camera_controller.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | // SDK includes 4 | #include "render_pipeline/camera.h" 5 | #include "graphics/types.h" 6 | #include "graphics/event_collector.h" 7 | 8 | // System includes 9 | #include 10 | 11 | // Buttons that control the camera movements 12 | enum class NavigationButtons 13 | { 14 | Forward = 0, 15 | Backward, 16 | Left, 17 | Right, 18 | Up, 19 | Down, 20 | Shift, 21 | Count 22 | }; 23 | 24 | class CameraController 25 | { 26 | public: 27 | CameraController(); 28 | ~CameraController(); 29 | 30 | // Initialize the controller 31 | virtual void initialize(RenderWindow renderWindow, uint32_t width, uint32_t height, float fov); 32 | 33 | // Process key event 34 | virtual void process_key_event(uint32_t keyCode, bool state); 35 | 36 | // Process a mouse mouvement 37 | virtual bool process_mouse_movement(int2 mouse, uint2 windowCenter, float4 screenSize); 38 | virtual void process_mouse_wheel(int wheel); 39 | virtual bool process_mouse_button(MouseButton button, bool state); 40 | 41 | // Apply the delta time 42 | virtual void update(double deltaTime); 43 | virtual void evaluate_camera_matrices(); 44 | 45 | // Get camera 46 | const Camera& get_camera() const { return m_Camera; } 47 | Camera& get_camera() { return m_Camera; } 48 | 49 | protected: 50 | // Render window 51 | RenderWindow m_Window = 0; 52 | 53 | // The camera that the controller is handeling 54 | Camera m_Camera = Camera(); 55 | 56 | // Button controls 57 | bool m_ControllerStates[(uint32_t)NavigationButtons::Count] = { false, false, false, false, false, false, false }; 58 | 59 | // Flag that defines if we can interact with the camera 60 | bool m_ActiveInteraction = false; 61 | 62 | // Speed 63 | float m_Speed = 0.0f; 64 | }; 65 | -------------------------------------------------------------------------------- /shaders/shader_lib/rand.hlsl: -------------------------------------------------------------------------------- 1 | #ifndef RAND_HLSL 2 | #define RAND_HLSL 3 | 4 | // See: http://www.reedbeta.com/blog/quick-and-easy-gpu-random-numbers-in-d3d11/ 5 | uint Hash(uint seed) 6 | { 7 | seed = (seed ^ 61u) ^ (seed >> 16u); 8 | seed *= 9u; 9 | seed = seed ^ (seed >> 4u); 10 | seed *= 0x27d4eb2du; 11 | seed = seed ^ (seed >> 15u); 12 | return seed; 13 | } 14 | 15 | uint BitReverse(uint x) 16 | { 17 | x = ((x & 0x55555555u) << 1u) | ((x & 0xAAAAAAAAu) >> 1u); 18 | x = ((x & 0x33333333u) << 2u) | ((x & 0xCCCCCCCCu) >> 2u); 19 | x = ((x & 0x0F0F0F0Fu) << 4u) | ((x & 0xF0F0F0F0u) >> 4u); 20 | x = ((x & 0x00FF00FFu) << 8u) | ((x & 0xFF00FF00u) >> 8u); 21 | x = ((x & 0x0000FFFFu) << 16u) | ((x & 0xFFFF0000u) >> 16u); 22 | 23 | return x; 24 | } 25 | 26 | uint Dilate(uint x) { 27 | x = (x | (x << 8)) & 0x00FF00FFu; 28 | x = (x | (x << 4)) & 0x0F0F0F0Fu; 29 | x = (x | (x << 2)) & 0x33333333u; 30 | x = (x | (x << 1)) & 0x55555555u; 31 | 32 | return x; 33 | } 34 | 35 | uint BayerMatrixCoefficient(uint i, uint j, uint matrixSize) 36 | { 37 | uint x = i ^ j; 38 | uint y = j; 39 | uint z = Dilate(x) | (Dilate(y) << 1); 40 | uint b = BitReverse(z) >> (32 - (matrixSize << 1)); 41 | 42 | return b; 43 | } 44 | 45 | uint IRng(uint rngState) 46 | { 47 | // LCG values from Numerical Recipes 48 | return 1664525u * rngState + 1013904223u; 49 | } 50 | 51 | // Random float in range [0, 1) 52 | // Implementation adapted from ispc 53 | float URng(inout uint seed) 54 | { 55 | uint tmp = IRng(seed); 56 | uint irn = tmp & ((1u << 23) - 1u); 57 | 58 | seed = tmp; 59 | 60 | return asfloat(0x3F800000u | irn) - 1.0f; 61 | } 62 | 63 | uint32_t pixel_seed(uint2 coordinates, float2 screenSize, uint32_t frameIndex) 64 | { 65 | return Hash(BayerMatrixCoefficient(coordinates.x & 255, coordinates.y & 255, 16) + frameIndex); 66 | } 67 | 68 | #endif // RAND_HLSL -------------------------------------------------------------------------------- /demo/include/math/types.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | // External includes 4 | #include 5 | #include 6 | 7 | // Constants 8 | #define QUARTER_PI 0.78539816339 9 | #define HALF_PI 1.57079632679 10 | #define PI 3.14159265359 11 | #define TWO_PI 6.28318530718 12 | #define INV_PI 0.31830988618 13 | #define DEG_TO_RAD (PI / 180.0) 14 | 15 | // Half not native to C++ 16 | #define float16_t uint16_t 17 | 18 | struct half2 19 | { 20 | uint16_t x, y; 21 | }; 22 | 23 | struct half3 24 | { 25 | uint16_t x, y, z; 26 | }; 27 | 28 | struct half4 29 | { 30 | uint16_t x, y, z, w; 31 | }; 32 | 33 | struct float2 34 | { 35 | float x,y; 36 | }; 37 | 38 | struct float3 39 | { 40 | float x, y, z; 41 | }; 42 | 43 | struct float4 44 | { 45 | float x, y, z, w; 46 | }; 47 | 48 | struct double2 49 | { 50 | double x, y; 51 | }; 52 | 53 | struct double3 54 | { 55 | double x, y, z; 56 | }; 57 | 58 | struct double4 59 | { 60 | double x, y, z, w; 61 | }; 62 | 63 | struct uint2 64 | { 65 | uint32_t x,y; 66 | }; 67 | 68 | struct uint3 69 | { 70 | uint32_t x, y, z; 71 | }; 72 | 73 | struct uint4 74 | { 75 | uint32_t x,y,z,w; 76 | }; 77 | 78 | struct int2 79 | { 80 | int32_t x, y; 81 | }; 82 | 83 | struct int3 84 | { 85 | int32_t x, y, z; 86 | }; 87 | 88 | struct int4 89 | { 90 | int32_t x, y, z, w; 91 | }; 92 | 93 | struct float2x2 94 | { 95 | union 96 | { 97 | float m[4]; 98 | float rc[2][2]; 99 | }; 100 | }; 101 | 102 | struct float3x3 103 | { 104 | union 105 | { 106 | float m[9]; 107 | float rc[3][3]; 108 | }; 109 | }; 110 | 111 | struct float4x4 112 | { 113 | union 114 | { 115 | float m[16]; 116 | float rc[4][4]; 117 | }; 118 | }; 119 | 120 | struct double2x2 121 | { 122 | union 123 | { 124 | double m[4]; 125 | double rc[2][2]; 126 | }; 127 | }; 128 | 129 | struct double3x3 130 | { 131 | union 132 | { 133 | double m[9]; 134 | double rc[3][3]; 135 | }; 136 | }; 137 | 138 | struct double4x4 139 | { 140 | union 141 | { 142 | double m[16]; 143 | double rc[4][4]; 144 | }; 145 | }; 146 | -------------------------------------------------------------------------------- /demo/src/tools/profiling_helper.cpp: -------------------------------------------------------------------------------- 1 | // Internal includes 2 | #include "tools/profiling_helper.h" 3 | #include "graphics/dx12_backend.h" 4 | 5 | // External includes 6 | #include 7 | 8 | ProfilingHelper::ProfilingHelper() 9 | { 10 | } 11 | 12 | ProfilingHelper::~ProfilingHelper() 13 | { 14 | } 15 | 16 | void ProfilingHelper::initialize(GraphicsDevice device, uint32_t numScopes) 17 | { 18 | m_NumScopes = numScopes; 19 | m_Scopes.resize(m_NumScopes); 20 | m_LastDurationArray.resize(m_NumScopes); 21 | m_MaxDurationArray.resize(m_NumScopes); 22 | for (uint32_t scopeIdx = 0; scopeIdx < m_NumScopes; ++scopeIdx) 23 | { 24 | m_Scopes[scopeIdx] = d3d12::profiling_scope::create_profiling_scope(device); 25 | } 26 | } 27 | 28 | void ProfilingHelper::release() 29 | { 30 | for (uint32_t scopeIdx = 0; scopeIdx < m_NumScopes; ++scopeIdx) 31 | d3d12::profiling_scope::destroy_profiling_scope(m_Scopes[scopeIdx]); 32 | } 33 | 34 | void ProfilingHelper::start_profiling(CommandBuffer cmd, uint32_t index) 35 | { 36 | d3d12::command_buffer::enable_profiling_scope(cmd, m_Scopes[index]); 37 | } 38 | 39 | void ProfilingHelper::end_profiling(CommandBuffer cmd, uint32_t index) 40 | { 41 | d3d12::command_buffer::disable_profiling_scope(cmd, m_Scopes[index]); 42 | } 43 | 44 | void ProfilingHelper::process_scopes(CommandQueue cmdQ) 45 | { 46 | for (uint32_t scopeIdx = 0; scopeIdx < m_NumScopes; ++scopeIdx) 47 | { 48 | uint64_t updateDuration = d3d12::profiling_scope::get_duration_us(m_Scopes[scopeIdx], cmdQ); 49 | m_MaxDurationArray[scopeIdx] = std::max(m_MaxDurationArray[scopeIdx], updateDuration); 50 | m_LastDurationArray[scopeIdx] = updateDuration; 51 | } 52 | } 53 | 54 | uint64_t ProfilingHelper::get_scope_last_duration(uint32_t index) 55 | { 56 | return m_LastDurationArray[index]; 57 | } 58 | 59 | uint64_t ProfilingHelper::get_scope_max_duration(uint32_t index) 60 | { 61 | return m_MaxDurationArray[index]; 62 | } 63 | 64 | void ProfilingHelper::reset_durations() 65 | { 66 | for (uint32_t scopeIdx = 0; scopeIdx < m_NumScopes; ++scopeIdx) 67 | { 68 | m_MaxDurationArray[scopeIdx] = 0; 69 | m_LastDurationArray[scopeIdx] = 0; 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /demo/src/rendering/frustum.cpp: -------------------------------------------------------------------------------- 1 | // Project includes 2 | #include "rendering/frustum.h" 3 | #include "math/operators.h" 4 | 5 | void normalize_plane(Plane& plane) 6 | { 7 | float l = sqrtf(plane.normal.x * plane.normal.x + plane.normal.y * plane.normal.y + plane.normal.z * plane.normal.z); 8 | plane.normal.x /= l; 9 | plane.normal.y /= l; 10 | plane.normal.z /= l; 11 | plane.d /= l; 12 | } 13 | 14 | void extract_planes_from_view_projection_matrix(const float4x4 viewProj, Frustum& frustum) 15 | { 16 | // Compute all the planes 17 | frustum.planes[0] = {viewProj.m[0] + viewProj.m[3], viewProj.m[4] + viewProj.m[7], viewProj.m[8] + viewProj.m[11], viewProj.m[12] + viewProj.m[15]}; 18 | frustum.planes[1] = {-viewProj.m[0] + viewProj.m[3], -viewProj.m[4] + viewProj.m[7], -viewProj.m[8] + viewProj.m[11], -viewProj.m[12] + viewProj.m[15]}; 19 | 20 | frustum.planes[2] = {viewProj.m[1] + viewProj.m[3], viewProj.m[5] + viewProj.m[7], viewProj.m[9] + viewProj.m[11], viewProj.m[13] + viewProj.m[15]}; 21 | frustum.planes[3] = {-viewProj.m[1] + viewProj.m[3], -viewProj.m[5] + viewProj.m[7], -viewProj.m[9] + viewProj.m[11], -viewProj.m[13] + viewProj.m[15]}; 22 | 23 | frustum.planes[4] = {viewProj.m[2] + viewProj.m[3], viewProj.m[6] + viewProj.m[7], viewProj.m[10] + viewProj.m[11], viewProj.m[14] + viewProj.m[15]}; 24 | frustum.planes[5] = {-viewProj.m[2] + viewProj.m[3], -viewProj.m[6] + viewProj.m[7], -viewProj.m[10] + viewProj.m[11], -viewProj.m[14] + viewProj.m[15]}; 25 | 26 | // Normalize all the planes 27 | for (uint32_t planeIdx = 0; planeIdx < 6; ++planeIdx) 28 | normalize_plane(frustum.planes[planeIdx]); 29 | } 30 | 31 | bool frustum_aabb_intersect(const Frustum& frustum, const AABB& aabb) 32 | { 33 | const float3& center = (aabb.max + aabb.min) * 0.5; 34 | const float3& extents = (aabb.max - aabb.min) * 0.5; 35 | for (int i = 0; i < 4; i++) 36 | { 37 | const Plane& plane = frustum.planes[i]; 38 | const float3& normal_sign = sign(plane.normal); 39 | const float3& test_point = center + extents * normal_sign; 40 | 41 | float dotProd = dot(test_point, plane.normal); 42 | if (dotProd + plane.d < 0) 43 | return false; 44 | } 45 | return true; 46 | } -------------------------------------------------------------------------------- /projects/convert_grid_to_leb3d.cpp: -------------------------------------------------------------------------------- 1 | // Project includes 2 | #include "volume/grid_volume.h" 3 | #include "volume/leb_volume.h" 4 | #include "volume/leb_volume_gpu.h" 5 | #include "volume/volume_generation.h" 6 | #include "math/operators.h" 7 | #include "tools/security.h" 8 | 9 | // System includes 10 | #define NOMINMAX 11 | #include 12 | #include 13 | #include 14 | 15 | int CALLBACK main(HINSTANCE, HINSTANCE, PWSTR, int) 16 | { 17 | // Check the parameter count 18 | assert_msg(__argc == 2, "Not enough parameters to the call. One parameter expected ."); 19 | 20 | // Project directory 21 | const std::string& projectDir = __argv[1]; 22 | 23 | // Volume that holds our intial structure 24 | LEBVolume lebVolume; 25 | leb_volume::create_type0_cube(lebVolume); 26 | std::cout << "Base LEB structured built." << std::endl; 27 | 28 | // Import the grid 29 | GridVolume gridVolume; 30 | grid_volume::import_grid_volume((projectDir + "/volumes/wdas_cloud_grid.bin").c_str(), gridVolume); 31 | std::cout << "Grid volume imported." << std::endl; 32 | 33 | // Cache 34 | HeuristicCache heuristicCache; 35 | heuristic_cache::build_heuristic_cache(gridVolume, heuristicCache); 36 | std::cout << "Heuristic cache built." << std::endl; 37 | 38 | // Subdivide the volume 39 | FittingParameters fittingParams = { false, false }; 40 | uint32_t maxDepth = leb_volume::fit_volume_to_grid(lebVolume, gridVolume, heuristicCache, fittingParams); 41 | std::cout << "LEB3D volume generated." << std::endl; 42 | 43 | // Export the volume 44 | LEBVolumeGPU lebVolumeGPU; 45 | uint64_t compressedSize = leb_volume::convert_to_leb_volume_to_gpu(lebVolume, gridVolume, fittingParams, maxDepth, lebVolumeGPU); 46 | std::cout << "LEB3D converted for the GPU." << std::endl; 47 | 48 | // Display the compressed size 49 | std::cout << "LEB3D compressed size " << compressedSize << " bytes." << std::endl; 50 | 51 | // Export to disk 52 | leb_volume::export_leb_volume_gpu(lebVolumeGPU, (projectDir + "/volumes/wdas_cloud_leb.bin").c_str()); 53 | std::cout << "LEB3D GPU exported." << std::endl; 54 | return 0; 55 | } -------------------------------------------------------------------------------- /demo/include/graphics/dx12_helpers.h: -------------------------------------------------------------------------------- 1 | #ifdef D3D12_SUPPORTED 2 | #pragma once 3 | 4 | // Internal includes 5 | #include "graphics/dx12_backend.h" 6 | #include "graphics/dx12_containers.h" 7 | 8 | namespace d3d12 9 | { 10 | // Textures 11 | DXGI_FORMAT format_to_dxgi_format(TextureFormat textureFormat); 12 | DXGI_FORMAT sanitize_dxgi_format_clear(DXGI_FORMAT format); 13 | DXGI_FORMAT sanitize_dxgi_format_srv(DXGI_FORMAT format); 14 | D3D12_RESOURCE_DIMENSION texture_dimension_to_dx12_resource_dimension(TextureType type); 15 | bool is_depth_format(TextureFormat format); 16 | uint8_t format_alignment(TextureFormat format); 17 | uint8_t format_alignment(DXGI_FORMAT format); 18 | D3D12_FILTER filter_mode_to_dxgi_filter(FilterMode mode); 19 | 20 | // Command 21 | D3D12_COMMAND_LIST_TYPE convert_command_buffer_type(CommandBufferType type); 22 | D3D12_COMMAND_QUEUE_PRIORITY convert_command_queue_priority(CommandQueuePriority priority); 23 | 24 | // Descriptor heaps 25 | ID3D12DescriptorHeap* create_descriptor_heap_internal(DX12GraphicsDevice* deviceI, uint32_t numDescriptors, uint32_t opaqueType); 26 | DX12DescriptorHeap create_descriptor_heap_suc(DX12GraphicsDevice* deviceI, uint32_t srvCount, uint32_t uavCount, uint32_t cbvCount); 27 | DX12DescriptorHeap create_descriptor_heap_sampler(DX12GraphicsDevice* deviceI, uint32_t samplerCount); 28 | void destroy_descriptor_heap(DX12DescriptorHeap& descriptorHeap); 29 | 30 | // Root signature 31 | DX12RootSignature* create_root_signature(DX12GraphicsDevice* device, uint32_t srvCount, uint32_t uavCount, uint32_t cbvCount, uint32_t samplerCount); 32 | void destroy_root_signature(DX12RootSignature* rootSignature); 33 | 34 | // Binding 35 | void query_bindings(IDxcBlob* blob, uint32_t& cbvCount, uint32_t& srvCount, uint32_t& uavCount, uint32_t& samplerCount, std::map& outBindings); 36 | bool request_binding(const std::map& bindings, const char* name, DX12Binding& outBind); 37 | 38 | // Compute shaders 39 | void validate_compute_shader_heap(DX12ComputeShader* computeShader, uint32_t cmdBatchIndex); 40 | 41 | // Graphics pipeline 42 | void validate_graphics_pipeline_heap(DX12GraphicsPipeline* graphicsPipeline, uint32_t cmdBatchIndex); 43 | 44 | // Graphics device 45 | uint32_t vendor_to_vendor_id(GPUVendor vendor); 46 | GPUVendor vendor_id_to_vendor(uint32_t vendorID); 47 | } 48 | #endif -------------------------------------------------------------------------------- /shaders/Grid/Density.compute: -------------------------------------------------------------------------------- 1 | #define WORKGROUP_RES 8 2 | #define WORKGROUP_SIZE WORKGROUP_RES * WORKGROUP_RES 3 | 4 | // CBVs 5 | #define GLOBAL_CB_BINDING_SLOT b0 6 | #define GRID_CB_BINDING_SLOT b1 7 | 8 | // SRVs 9 | #define DENSITY_BUFFER_0_BINDING_SLOT t0 10 | #define DENSITY_BUFFER_1_BINDING_SLOT t1 11 | 12 | // Includes 13 | #include "shader_lib/common.hlsl" 14 | #include "shader_lib/constant_buffers.hlsl" 15 | #include "shader_lib/grid_utilities.hlsl" 16 | #include "shader_lib/intersection.hlsl" 17 | 18 | // UAVs 19 | RWTexture2D _ColorTexture: register(u0); 20 | 21 | [numthreads(WORKGROUP_RES, WORKGROUP_RES, 1)] 22 | void InsideVolumeIntegrator(uint2 threadID : SV_DispatchThreadID) 23 | { 24 | // Evaluate the ray direction 25 | float3 rayOrigin = _CameraPosition * _GridScale; 26 | float3 rayDir = evaluate_ray_direction(threadID.xy) * _GridScale; 27 | 28 | // Evaluate the cell index 29 | int3 cellCoords = evaluate_cell_coords(rayOrigin); 30 | 31 | // Evaluate the density 32 | float totalDensity = integrate_density(rayOrigin, rayDir, cellCoords); 33 | totalDensity = fast_tonemap(totalDensity); 34 | _ColorTexture[threadID] = float4(totalDensity, totalDensity, totalDensity, 1.0); 35 | } 36 | 37 | [numthreads(WORKGROUP_RES, WORKGROUP_RES, 1)] 38 | void OutsideVolumeIntegrator(uint2 threadID : SV_DispatchThreadID) 39 | { 40 | // Evaluate the ray direction 41 | float3 rayOrigin = _CameraPosition * _GridScale; 42 | float3 rayDir = evaluate_ray_direction(threadID.xy) * _GridScale; 43 | 44 | // First we intersect with the box 45 | float2 inters = intersect_ray_aabb(rayOrigin, rayDir, _GridMinPosition, _GridMaxPosition); 46 | 47 | // Do we intersect the volume in front of us? 48 | if (inters.x > 0.0 && inters.x < inters.y) 49 | { 50 | // Compute the entry point intersection 51 | float3 initialPosition = clamp(rayOrigin + rayDir * inters.x, _GridMinPosition, _GridMaxPosition); 52 | 53 | // Evaluate the cell index 54 | int3 cellCoords = evaluate_cell_coords(initialPosition); 55 | 56 | // Evaluate the density 57 | float totalDensity = integrate_density(initialPosition, rayDir, cellCoords); 58 | totalDensity = fast_tonemap(totalDensity); 59 | _ColorTexture[threadID] = float4(totalDensity, totalDensity, totalDensity, 1.0); 60 | } 61 | else 62 | { 63 | _ColorTexture[threadID] = float4(0.0, 0.0, 0.0, 1.0); 64 | } 65 | } -------------------------------------------------------------------------------- /demo/include/imgui/imgui_impl_dx12.h: -------------------------------------------------------------------------------- 1 | // dear imgui: Renderer Backend for DirectX12 2 | // This needs to be used along with a Platform Backend (e.g. Win32) 3 | 4 | // Implemented features: 5 | // [X] Renderer: User texture binding. Use 'D3D12_GPU_DESCRIPTOR_HANDLE' as ImTextureID. Read the FAQ about ImTextureID! 6 | // [X] Renderer: Large meshes support (64k+ vertices) with 16-bit indices. 7 | 8 | // Important: to compile on 32-bit systems, this backend requires code to be compiled with '#define ImTextureID ImU64'. 9 | // See imgui_impl_dx12.cpp file for details. 10 | 11 | // You can use unmodified imgui_impl_* files in your project. See examples/ folder for examples of using this. 12 | // Prefer including the entire imgui/ repository into your project (either as a copy or as a submodule), and only build the backends you need. 13 | // If you are new to Dear ImGui, read documentation from the docs/ folder + read the top of imgui.cpp. 14 | // Read online: https://github.com/ocornut/imgui/tree/master/docs 15 | 16 | #pragma once 17 | #include "imgui.h" // IMGUI_IMPL_API 18 | #ifndef IMGUI_DISABLE 19 | #include // DXGI_FORMAT 20 | 21 | struct ID3D12Device; 22 | struct ID3D12DescriptorHeap; 23 | struct ID3D12GraphicsCommandList; 24 | struct D3D12_CPU_DESCRIPTOR_HANDLE; 25 | struct D3D12_GPU_DESCRIPTOR_HANDLE; 26 | 27 | // cmd_list is the command list that the implementation will use to render imgui draw lists. 28 | // Before calling the render function, caller must prepare cmd_list by resetting it and setting the appropriate 29 | // render target and descriptor heap that contains font_srv_cpu_desc_handle/font_srv_gpu_desc_handle. 30 | // 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. 31 | IMGUI_IMPL_API bool ImGui_ImplDX12_Init(ID3D12Device* device, int num_frames_in_flight, DXGI_FORMAT rtv_format, ID3D12DescriptorHeap* cbv_srv_heap, 32 | D3D12_CPU_DESCRIPTOR_HANDLE font_srv_cpu_desc_handle, D3D12_GPU_DESCRIPTOR_HANDLE font_srv_gpu_desc_handle); 33 | IMGUI_IMPL_API void ImGui_ImplDX12_Shutdown(); 34 | IMGUI_IMPL_API void ImGui_ImplDX12_NewFrame(); 35 | IMGUI_IMPL_API void ImGui_ImplDX12_RenderDrawData(ImDrawData* draw_data, ID3D12GraphicsCommandList* graphics_command_list); 36 | 37 | // Use if you want to reset your rendering device without losing Dear ImGui state. 38 | IMGUI_IMPL_API void ImGui_ImplDX12_InvalidateDeviceObjects(); 39 | IMGUI_IMPL_API bool ImGui_ImplDX12_CreateDeviceObjects(); 40 | 41 | #endif // #ifndef IMGUI_DISABLE 42 | -------------------------------------------------------------------------------- /demo/include/render_pipeline/constant_buffers.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | // SDK includes 4 | #include "math/types.h" 5 | 6 | struct GlobalCB 7 | { 8 | // View projection matrix 9 | float4x4 _ViewProjectionMatrix; 10 | 11 | // Inverse view projection matrix 12 | float4x4 _InvViewProjectionMatrix; 13 | 14 | // Camera position in double as we are operating on planeraty coordinates 15 | float3 _CameraPosition; 16 | uint32_t _FrameIndex; 17 | 18 | // Screen size and inverse screen size 19 | float4 _ScreenSize; 20 | 21 | // Accumulation Factors 22 | float2 _FrameAccumulationFactors; 23 | // Density multiplier 24 | float _DensityMultiplier; 25 | // Volume albedo 26 | float _VolumeAlbedo; 27 | 28 | // Sun intensity 29 | float _SunIntensity; 30 | // Sky intensity 31 | float _SkyIntensity; 32 | // Padding 33 | float2 _PaddingGB0; 34 | 35 | // Padding 36 | float3 _SunDirection; 37 | float _PaddingGB1; 38 | }; 39 | 40 | struct UpdateCB 41 | { 42 | // View projection matrix used for the update 43 | float4x4 _UpdateViewProjectionMatrix; 44 | 45 | // Inverse View projection matrix used for the update 46 | float4x4 _UpdateInvViewProjectionMatrix; 47 | 48 | // Camera position used for the update 49 | float3 _UpdateCameraPosition; 50 | // FOV 51 | float _UpdateFOV; 52 | 53 | // Volume min 54 | float3 _VolumeMinPosition; 55 | // Far plane 56 | float _UpdateFarPlaneDistance; 57 | 58 | // Volume max 59 | float3 _VolumeMaxPosition; 60 | // Padding 61 | float _PaddingUB0; 62 | }; 63 | 64 | struct LEBCB 65 | { 66 | // Number of tetrahedrons in our structure 67 | uint32_t _NumTetrahedrons; 68 | // Initial primitive 69 | uint32_t _InitialPrimitive; 70 | float2 _PaddingLB0; 71 | 72 | // Volume scale 73 | float3 _LEBScale; 74 | 75 | // Padding 76 | float _PaddingLB1; 77 | }; 78 | 79 | struct GridCB 80 | { 81 | // Resolution of the grid 82 | uint3 _GridResolution; 83 | 84 | // Padding 85 | float _PaddingGRB0; 86 | 87 | // Grid min position 88 | float3 _GridMinPosition; 89 | 90 | // Padding 91 | float _PaddingGRB1; 92 | 93 | // Grid max position 94 | float3 _GridMaxPosition; 95 | 96 | // Padding 97 | float _PaddingGRB2; 98 | 99 | // Grid scale position 100 | float3 _GridScale; 101 | 102 | // Padding 103 | float _PaddingGRB3; 104 | }; -------------------------------------------------------------------------------- /demo/include/volume/leb_volume.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | // Internal includes 4 | #include "math/types.h" 5 | #include "volume/leb_3d_cache.h" 6 | 7 | // External includes 8 | #include 9 | #include 10 | 11 | // Flags for the subdivision routine 12 | #define ELEMENT_INCLUDED 0x1 13 | #define ELEMENT_INVALID_CACHE 0x2 14 | #define ELEMENT_REQUESTED 0x4 15 | 16 | struct Diamond 17 | { 18 | uint64_t heapID[8]; 19 | uint32_t size; 20 | }; 21 | 22 | struct Tetrahedron 23 | { 24 | float3 p[4]; 25 | }; 26 | 27 | struct LEBVolume 28 | { 29 | // Total number of elements 30 | uint32_t totalNumElements = 0; 31 | 32 | // Minimal depth of the mesh 33 | uint32_t minimalDepth = 0; 34 | 35 | // Bisector 36 | std::vector heapIDArray; 37 | std::vector typeArray; 38 | std::vector neighborsArray; 39 | 40 | // Base attributes 41 | std::vector basePoints; 42 | std::vector baseTypes; 43 | 44 | // Used for subdivision 45 | std::vector tetraCacheArray; 46 | std::vector modifArray; 47 | std::vector depthArray; 48 | 49 | // Debug Attribute to track diamond splits 50 | std::vector diamonds; 51 | }; 52 | 53 | struct FittingParameters 54 | { 55 | // Should we fit using a frustum? 56 | bool frustumCull = false; 57 | 58 | // Should we cull using pixel size 59 | bool pixelCull = false; 60 | 61 | // Camera position 62 | float3 cameraPosition; 63 | 64 | // Camera projection matrix 65 | float4x4 viewProjectionMatrix; 66 | 67 | // Screen Size 68 | uint2 screenSize; 69 | 70 | // Data used for the fitting 71 | float ratioThreshold = 2.0f; 72 | float minThreshold = 5.0f; 73 | float pixelSize = 5.0f; 74 | }; 75 | 76 | namespace leb_volume 77 | { 78 | // Creates the base leb structure for a cube 79 | void create_type0_cube(LEBVolume& lebVolume); 80 | 81 | // Function that will, for every element, evaluate the 4 vertices of each tetrahedron 82 | void evaluate_positions(const LEBVolume& lebVolume, std::vector& vertices); 83 | 84 | // Function that returns if two types are equivalent 85 | bool equivalent_types(uint8_t type0, uint8_t type1); 86 | 87 | // Evaluate tetrahedron postion 88 | void evaluate_tetrahedron(uint64_t heapID, uint32_t minDepth, const std::vector& basePoints, const std::vector& baseTypes, Tetrahedron& tetra); 89 | void evaluate_tetrahedron(uint64_t heapID, uint32_t minDepth, const std::vector& basePoints, const std::vector& baseTypes, const Leb3DCache& cache, Tetrahedron& tetra); 90 | } -------------------------------------------------------------------------------- /shaders/LEB/Density.compute: -------------------------------------------------------------------------------- 1 | #define WORKGROUP_RES 8 2 | #define WORKGROUP_SIZE WORKGROUP_RES * WORKGROUP_RES 3 | 4 | // CBVs 5 | #define GLOBAL_CB_BINDING_SLOT b0 6 | #define LEB_CB_BINDING_SLOT b1 7 | 8 | // SRVs 9 | #define TETRA_BUFFER_0_BINDING_SLOT t0 10 | #define TETRA_BUFFER_1_BINDING_SLOT t1 11 | #define PRIMITIVE_BUFFER_BINDING_SLOT t2 12 | #define DISTANCE_BUFFER_BINDING_SLOT t3 13 | #define DIRECTION_BUFFER_BINDING_SLOT t4 14 | 15 | // Includes 16 | #include "shader_lib/common.hlsl" 17 | #include "shader_lib/constant_buffers.hlsl" 18 | #include "shader_lib/leb_utilities.hlsl" 19 | 20 | // SRVs 21 | StructuredBuffer _PrimitiveBuffer: register(PRIMITIVE_BUFFER_BINDING_SLOT); 22 | StructuredBuffer _DistanceBuffer: register(DISTANCE_BUFFER_BINDING_SLOT); 23 | 24 | // UAVs 25 | RWTexture2D _ColorTexture: register(u0); 26 | 27 | [numthreads(WORKGROUP_RES, WORKGROUP_RES, 1)] 28 | void InsideVolumeIntegrator(uint2 threadID : SV_DispatchThreadID, uint groupIndex : SV_GroupIndex) 29 | { 30 | // Load the directions to shared memory 31 | load_direction_to_sm(groupIndex); 32 | 33 | if (_InitialPrimitive == UINT32_MAX) 34 | { 35 | _ColorTexture[threadID] = float4(0.0, 0.0, 0.0, 1.0); 36 | return; 37 | } 38 | 39 | // Evaluate the density 40 | float3 rayOriginLS = _CameraPosition * _LEBScale; 41 | float3 rayDirLS = transform_dir(evaluate_ray_direction(threadID.xy), _LEBScale); 42 | 43 | // Evaluate the density 44 | float totalDensity = integrate_density(rayOriginLS, rayDirLS, _InitialPrimitive); 45 | totalDensity = fast_tonemap(totalDensity); 46 | _ColorTexture[threadID] = float4(totalDensity, totalDensity, totalDensity, 1.0); 47 | } 48 | 49 | [numthreads(WORKGROUP_RES, WORKGROUP_RES, 1)] 50 | void OutsideVolumeIntegrator(uint2 threadID : SV_DispatchThreadID, uint groupIndex : SV_GroupIndex) 51 | { 52 | // Load the directions to shared memory 53 | load_direction_to_sm(groupIndex); 54 | 55 | // Ray direction 56 | float3 rayOriginLS = _CameraPosition * _LEBScale; 57 | float3 rayDirLS = transform_dir(evaluate_ray_direction(threadID.xy), _LEBScale); 58 | 59 | // Any primitive to intersect? 60 | uint32_t primitiveIdx = threadID.x + _ScreenSize.x * threadID.y; 61 | if (_PrimitiveBuffer[primitiveIdx] == UINT32_MAX) 62 | { 63 | _ColorTexture[threadID] = float4(0.0, 0.0, 0.0, 1.0); 64 | return; 65 | } 66 | 67 | // Evalute the inscattering 68 | float totalDensity = integrate_density(rayOriginLS + rayDirLS * _DistanceBuffer[primitiveIdx], rayDirLS, _PrimitiveBuffer[primitiveIdx]); 69 | totalDensity = fast_tonemap(totalDensity); 70 | 71 | // Return the result 72 | _ColorTexture[threadID] = float4(totalDensity, totalDensity, totalDensity, 1.0); 73 | } -------------------------------------------------------------------------------- /demo/include/render_pipeline/leb_renderer.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | // Includes 4 | #include "graphics/types.h" 5 | #include "graphics/descriptors.h" 6 | #include "render_pipeline/constant_buffers.h" 7 | #include "render_pipeline/rendering_mode.h" 8 | #include "render_pipeline/camera.h" 9 | #include "render_pipeline/morton_cache.h" 10 | #include "render_pipeline/sky.h" 11 | #include "render_pipeline/grid_renderer.h" 12 | #include "volume/leb_volume_gpu.h" 13 | 14 | // System includes 15 | #include 16 | 17 | class LEBRenderer 18 | { 19 | public: 20 | // Cst & Dst 21 | LEBRenderer(); 22 | ~LEBRenderer(); 23 | 24 | // Initialization and release 25 | void initialize(GraphicsDevice device, uint2 screenRes); 26 | void release(); 27 | 28 | // Reloading 29 | void load_geometry(const std::string& filePath); 30 | void reload_shaders(const std::string& shaderLibrary); 31 | void upload_geometry(CommandQueue cmdQ, CommandBuffer cmdB); 32 | LEBVolumeGPU& volume() { return m_Volume; } 33 | 34 | // Rendering 35 | void upload_constant_buffers(CommandBuffer cmdB, const float3& cameraPosition); 36 | void render_volume(CommandBuffer cmdB, ConstantBuffer globalCB, 37 | RenderTexture colorRT, RenderTexture depthRT, 38 | RenderingMode mode, Sky& sky, const Camera& camera); 39 | 40 | private: 41 | // Build the RTAS 42 | void build_rtas(CommandQueue cmdQ, CommandBuffer cmdB); 43 | 44 | // Build the morton cache 45 | void build_morton_cache(); 46 | 47 | private: 48 | // Graphics device 49 | GraphicsDevice m_Device = 0; 50 | 51 | // Resources 52 | uint32_t m_NumTetrahedron = 0; 53 | bool m_SplitBuffer = false; 54 | 55 | // Volume CPU data 56 | LEBVolumeGPU m_Volume = LEBVolumeGPU(); 57 | MortonCache m_MortonCache = MortonCache(); 58 | std::vector m_ShaderDefines; 59 | Sampler m_LinearClampSampler = 0; 60 | uint32_t m_NumOutsideElements = 0; 61 | 62 | // LEB structure 63 | GraphicsBuffer m_TetraDataBuffer[2] = { 0, 0 }; 64 | GraphicsBuffer m_DirectionBuffer = 0; 65 | GraphicsBuffer m_PositionBuffer = 0; 66 | 67 | // RTAS 68 | GraphicsBuffer m_RTASIndexBuffer = 0; 69 | GraphicsBuffer m_RTASPositionBuffer = 0; 70 | GraphicsBuffer m_ElementIndexBuffer = 0; 71 | BottomLevelAS m_BLAS = 0; 72 | TopLevelAS m_TLAS = 0; 73 | 74 | // Runtime buffers 75 | ConstantBuffer m_LEBCB = 0; 76 | GraphicsBuffer m_PrimitiveBuffer = 0; 77 | GraphicsBuffer m_DistanceBuffer = 0; 78 | 79 | // Shaders 80 | ComputeShader m_IntersectBVHCS = 0; 81 | // Density 82 | ComputeShader m_InsideDensityCS = 0; 83 | ComputeShader m_OutsideDensityCS = 0; 84 | // PT 85 | ComputeShader m_InsidePTCS = 0; 86 | ComputeShader m_OutsidePTCS = 0; 87 | // Debug 88 | GraphicsPipeline m_DrawVolumeGP = 0; 89 | }; -------------------------------------------------------------------------------- /demo/src/graphics/dx12_backend_profiling_scope.cpp: -------------------------------------------------------------------------------- 1 | #ifdef D3D12_SUPPORTED 2 | // Internal includes 3 | #include "graphics/dx12_backend.h" 4 | #include "graphics/dx12_containers.h" 5 | #include "graphics/dx12_helpers.h" 6 | #include "tools/string_utilities.h" 7 | #include "tools/security.h" 8 | 9 | namespace d3d12 10 | { 11 | namespace profiling_scope 12 | { 13 | ProfilingScope create_profiling_scope(GraphicsDevice graphicsDevice) 14 | { 15 | // Grab the device 16 | DX12GraphicsDevice* deviceI = (DX12GraphicsDevice*)graphicsDevice; 17 | 18 | // Create the query heap 19 | D3D12_QUERY_HEAP_DESC queryHeapDesc = {}; 20 | queryHeapDesc.Count = 2; 21 | queryHeapDesc.Type = D3D12_QUERY_HEAP_TYPE_TIMESTAMP; 22 | ID3D12QueryHeap* queryHeap; 23 | assert_msg(deviceI->device->CreateQueryHeap(&queryHeapDesc, IID_PPV_ARGS(&queryHeap)) == S_OK, "Failed to create query."); 24 | 25 | // Define the resource descriptor 26 | D3D12_RESOURCE_DESC resourceDescriptor = { D3D12_RESOURCE_DIMENSION_BUFFER, 0, sizeof(uint64_t) * 2, 1, 1, 1, DXGI_FORMAT_UNKNOWN, 1, 0, D3D12_TEXTURE_LAYOUT_ROW_MAJOR, D3D12_RESOURCE_FLAG_NONE }; 27 | 28 | D3D12_HEAP_PROPERTIES heap; 29 | memset(&heap, 0, sizeof(heap)); 30 | heap.Type = D3D12_HEAP_TYPE_READBACK; 31 | 32 | // Create the resource 33 | ID3D12Resource* buffer; 34 | assert_msg(deviceI->device->CreateCommittedResource(&heap, D3D12_HEAP_FLAG_NONE, &resourceDescriptor, D3D12_RESOURCE_STATE_COPY_DEST, nullptr, IID_PPV_ARGS(&buffer)) == S_OK, "Failed to create the graphics buffer."); 35 | 36 | // Create and fill the internal structure 37 | DX12Query* queryI = new DX12Query(); 38 | queryI->heap = queryHeap; 39 | queryI->state = D3D12_RESOURCE_STATE_PREDICATION; 40 | queryI->result = buffer; 41 | 42 | // Convert to the opaque structure 43 | return (ProfilingScope)queryI; 44 | } 45 | 46 | void destroy_profiling_scope(ProfilingScope profilingScope) 47 | { 48 | DX12Query* query = (DX12Query*)profilingScope; 49 | query->heap->Release(); 50 | query->result->Release(); 51 | delete query; 52 | } 53 | 54 | uint64_t get_duration_us(ProfilingScope profilingScope, CommandQueue cmdQ) 55 | { 56 | DX12Query* query = (DX12Query*)profilingScope; 57 | DX12CommandQueue* dx12_cmdQ = (DX12CommandQueue*)cmdQ; 58 | 59 | char* data = nullptr; 60 | D3D12_RANGE range = { 0, sizeof(uint64_t) * 2 }; 61 | query->result->Map(0, &range, (void**)&data); 62 | uint64_t profileDuration = ((uint64_t*)data)[1] - ((uint64_t*)data)[0]; 63 | query->result->Unmap(0, nullptr); 64 | return (uint64_t)(profileDuration / (double)dx12_cmdQ->directSubQueue.frequency * 1e6); 65 | } 66 | } 67 | } 68 | #endif 69 | -------------------------------------------------------------------------------- /shaders/shader_lib/common.hlsl: -------------------------------------------------------------------------------- 1 | #ifndef COMMON_H 2 | #define COMMON_H 3 | 4 | // Generic constants 5 | #define PI 3.14159265358979323846 6 | #define HALF_PI (PI / 2.0) 7 | #define TWO_PI (PI * 2.0) 8 | #define FLT_EPSILON 1.192092896e-07 9 | #define INV_PI (1.0 / PI) 10 | #define FLT_MIN 1.175494351e-38 11 | #define FLT_MAX 3.402823466e+38 12 | #define PHI 1.61803398874989484820459 13 | #define UINT32_MAX 0xffffffff 14 | 15 | // Binding slot helpers 16 | #define CBV_SLOT(NUM_SLOT) b##NUM_SLOT 17 | #define SRV_SLOT(NUM_SLOT) t##NUM_SLOT 18 | #define UAV_SLOT(NUM_SLOT) u##NUM_SLOT 19 | #define SPL_SLOT(NUM_SLOT) s##NUM_SLOT 20 | 21 | // NDC to clip Space 22 | float4 evaluate_clip_space_position(float2 positionNDC, float depthValue) 23 | { 24 | float4 positionCS = float4(positionNDC * 2.0 - 1.0, depthValue, 1.0); 25 | positionCS.y = -positionCS.y; 26 | return positionCS; 27 | } 28 | 29 | // NDC to RWS 30 | float3 evaluate_world_space_position(float2 positionNDC, float depthValue, float4x4 invViewProjMatrix) 31 | { 32 | float4 positionCS = evaluate_clip_space_position(positionNDC, depthValue); 33 | float4 hpositionWS = mul(invViewProjMatrix, positionCS); 34 | return hpositionWS.xyz / hpositionWS.w; 35 | } 36 | 37 | float2 clip_space_to_pixel(float4 positionCS, float2 screenSize) 38 | { 39 | float2 p = positionCS.xy / positionCS.w; 40 | p.y = -p.y; 41 | p.xy = (p.xy * 0.5 + 0.5); 42 | p.xy *= screenSize.xy; 43 | return p.xy; 44 | } 45 | 46 | float3 sample_sphere(float2 randomSample) 47 | { 48 | // Convert randomSample from [0,1] to [-1,1] for uniform hemisphere mapping 49 | float z = randomSample.x * 2.0 - 1.0; // Z coordinate in [-1,1] 50 | float phi = randomSample.y * TWO_PI; // Azimuthal angle in [0, 2*PI] 51 | 52 | // Calculate radius of the circle at height z 53 | float radius = sqrt(-(z * z - 1.0)); 54 | 55 | // Convert spherical to Cartesian coordinates 56 | float x = radius * cos(phi); 57 | float y = radius * sin(phi); 58 | 59 | return float3(x, y, z); 60 | } 61 | 62 | float luminance(float3 color) 63 | { 64 | return 0.2126 * color.x + 0.7152* color.y + 0.0722 * color.z; 65 | } 66 | 67 | float fast_tonemap(float color) 68 | { 69 | return color / (1.0 + color); 70 | } 71 | 72 | float3 fast_tonemap(float3 color) 73 | { 74 | color /= (1.0 + color); 75 | return color; 76 | } 77 | 78 | float3 tonemap(float3 color) 79 | { 80 | float lum = luminance(color); 81 | return color / (1.0 + lum); 82 | } 83 | 84 | float4 fast_tonemap(float4 color) 85 | { 86 | color.xyz /= (1.0 + color.xyz); 87 | return color; 88 | } 89 | 90 | float3 transform_dir(float3 dir, float3 scale) 91 | { 92 | return normalize(dir * scale); 93 | } 94 | 95 | float3 transform_dir_inv(float3 dir, float3 scale) 96 | { 97 | return normalize(dir / scale); 98 | } 99 | 100 | #include "shader_lib/rand.hlsl" 101 | 102 | #define NUM_MAX_SEGMENTS 1024 103 | 104 | #endif 105 | -------------------------------------------------------------------------------- /demo/include/render_pipeline/volume_pipeline.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | // Internal incldues 4 | #include "rendering/frustum.h" 5 | #include "tools/profiling_helper.h" 6 | #include "render_pipeline/camera_controller.h" 7 | #include "render_pipeline/rendering_mode.h" 8 | #include "render_pipeline/leb_renderer.h" 9 | #include "render_pipeline/grid_renderer.h" 10 | #include "render_pipeline/frustum_renderer.h" 11 | #include "render_pipeline/sky.h" 12 | 13 | // External includes includes 14 | #define NOMINMAX 15 | #include 16 | #include 17 | 18 | class VolumePipeline 19 | { 20 | public: 21 | // Cst & Dst 22 | VolumePipeline(); 23 | ~VolumePipeline(); 24 | 25 | // Initialization and release 26 | void initialize(HINSTANCE hInstance, const char* projectDirectory, const char* exeDirectory, const char* gridVolume, const char* lebVolume); 27 | void release(); 28 | 29 | // Runtime loop 30 | void render_loop(); 31 | 32 | private: 33 | // Init 34 | void reload_shaders(); 35 | 36 | // Rendering 37 | void prepare_rendering(CommandBuffer cmd); 38 | void render_ui(CommandBuffer cmd, RenderTexture rTexture); 39 | void update_constant_buffers(CommandBuffer cmd); 40 | 41 | // render pipelines 42 | void render_pipeline(CommandBuffer cmd); 43 | 44 | // Update 45 | void update(double deltaTime); 46 | 47 | // Controls 48 | void process_key_event(uint32_t keyCode, bool state); 49 | 50 | private: 51 | // Graphics Backend 52 | GraphicsDevice m_Device = 0; 53 | RenderWindow m_Window = 0; 54 | CommandQueue m_CmdQueue = 0; 55 | CommandBuffer m_CmdBuffer = 0; 56 | SwapChain m_SwapChain = 0; 57 | 58 | // Project directory 59 | std::string m_ProjectDir = ""; 60 | std::string m_ExeDir = ""; 61 | 62 | // Global Rendering Resources 63 | RenderTexture m_ColorTexture = 0; 64 | RenderTexture m_DepthBuffer = 0; 65 | RenderTexture m_HistoryTexture = 0; 66 | ConstantBuffer m_GlobalCB = 0; 67 | ComputeShader m_AccumulateFrameCS = 0; 68 | ComputeShader m_TonemapFrameCS = 0; 69 | 70 | // Components 71 | LEBRenderer m_LEBRenderer = LEBRenderer(); 72 | GridRenderer m_GridRenderer = GridRenderer(); 73 | FrustumRenderer m_FrustumRenderer = FrustumRenderer(); 74 | Sky m_Sky = Sky(); 75 | CameraController m_CameraController = CameraController(); 76 | ProfilingHelper m_ProfilingHelper = ProfilingHelper(); 77 | 78 | // Global rendering properties 79 | uint32_t m_FrameIndex = 0; 80 | double m_Time = 0.0; 81 | float4 m_ScreenSize = { 0.0, 0.0, 0.0, 0.0 }; 82 | uint2 m_ScreenSizeI = { 0, 0 }; 83 | 84 | // UI controls 85 | bool m_DisplayUI = false; 86 | RenderingMode m_RenderingMode = RenderingMode::Count; 87 | bool m_LEBPath = false; 88 | bool m_RenderFrustum = false; 89 | float m_DensityMultipler = 0.75; 90 | float m_Albedo = 0.75; 91 | float m_SunIntensity = 1.0; 92 | float m_SkyIntensity = 1.0; 93 | float m_SunElevation = 1.0; 94 | float m_SunRotation = 1.0; 95 | }; -------------------------------------------------------------------------------- /demo/include/imgui/imgui_impl_win32.h: -------------------------------------------------------------------------------- 1 | #ifdef D3D12_SUPPORTED 2 | // dear imgui: Platform Backend for Windows (standard windows API for 32-bits AND 64-bits applications) 3 | // This needs to be used along with a Renderer (e.g. DirectX11, OpenGL3, Vulkan..) 4 | 5 | // Implemented features: 6 | // [X] Platform: Clipboard support (for Win32 this is actually part of core dear imgui) 7 | // [X] Platform: Mouse support. Can discriminate Mouse/TouchScreen/Pen. 8 | // [X] Platform: Keyboard support. Since 1.87 we are using the io.AddKeyEvent() function. Pass ImGuiKey values to all key functions e.g. ImGui::IsKeyPressed(ImGuiKey_Space). [Legacy VK_* values will also be supported unless IMGUI_DISABLE_OBSOLETE_KEYIO is set] 9 | // [X] Platform: Gamepad support. Enabled with 'io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad'. 10 | // [X] Platform: Mouse cursor shape and visibility. Disable with 'io.ConfigFlags |= ImGuiConfigFlags_NoMouseCursorChange'. 11 | 12 | // You can use unmodified imgui_impl_* files in your project. See examples/ folder for examples of using this. 13 | // Prefer including the entire imgui/ repository into your project (either as a copy or as a submodule), and only build the backends you need. 14 | // If you are new to Dear ImGui, read documentation from the docs/ folder + read the top of imgui.cpp. 15 | // Read online: https://github.com/ocornut/imgui/tree/master/docs 16 | 17 | #pragma once 18 | #include "imgui.h" // IMGUI_IMPL_API 19 | #ifndef IMGUI_DISABLE 20 | 21 | IMGUI_IMPL_API bool ImGui_ImplWin32_Init(void* hwnd); 22 | IMGUI_IMPL_API bool ImGui_ImplWin32_InitForOpenGL(void* hwnd); 23 | IMGUI_IMPL_API void ImGui_ImplWin32_Shutdown(); 24 | IMGUI_IMPL_API void ImGui_ImplWin32_NewFrame(); 25 | 26 | // Win32 message handler your application need to call. 27 | // - Intentionally commented out in a '#if 0' block to avoid dragging dependencies on from this helper. 28 | // - You should COPY the line below into your .cpp code to forward declare the function and then you can call it. 29 | // - Call from your application's message handler. Keep calling your message handler unless this function returns TRUE. 30 | 31 | #if 0 32 | extern IMGUI_IMPL_API LRESULT ImGui_ImplWin32_WndProcHandler(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam); 33 | #endif 34 | 35 | // DPI-related helpers (optional) 36 | // - Use to enable DPI awareness without having to create an application manifest. 37 | // - Your own app may already do this via a manifest or explicit calls. This is mostly useful for our examples/ apps. 38 | // - In theory we could call simple functions from Windows SDK such as SetProcessDPIAware(), SetProcessDpiAwareness(), etc. 39 | // but most of the functions provided by Microsoft require Windows 8.1/10+ SDK at compile time and Windows 8/10+ at runtime, 40 | // neither we want to require the user to have. So we dynamically select and load those functions to avoid dependencies. 41 | IMGUI_IMPL_API void ImGui_ImplWin32_EnableDpiAwareness(); 42 | IMGUI_IMPL_API float ImGui_ImplWin32_GetDpiScaleForHwnd(void* hwnd); // HWND hwnd 43 | IMGUI_IMPL_API float ImGui_ImplWin32_GetDpiScaleForMonitor(void* monitor); // HMONITOR monitor 44 | 45 | // Transparency related helpers (optional) [experimental] 46 | // - Use to enable alpha compositing transparency with the desktop. 47 | // - Use together with e.g. clearing your framebuffer with zero-alpha. 48 | IMGUI_IMPL_API void ImGui_ImplWin32_EnableAlphaCompositing(void* hwnd); // HWND hwnd 49 | 50 | #endif // #ifndef IMGUI_DISABLE 51 | #endif -------------------------------------------------------------------------------- /demo/src/graphics/dx12_backend_imgui.cpp: -------------------------------------------------------------------------------- 1 | #ifdef D3D12_SUPPORTED 2 | // SDK incldues 3 | #include "graphics/dx12_containers.h" 4 | #include "graphics/dx12_backend.h" 5 | #include "graphics/dx12_helpers.h" 6 | #include "imgui/imgui.h" 7 | #include "imgui/imgui_impl_dx12.h" 8 | #include "imgui/imgui_impl_win32.h" 9 | 10 | // Static descriptor heap 11 | static ID3D12DescriptorHeap* imguiDescHeap = nullptr; 12 | 13 | // Forward declaration 14 | extern IMGUI_IMPL_API LRESULT ImGui_ImplWin32_WndProcHandler(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam); 15 | 16 | namespace d3d12 17 | { 18 | namespace imgui 19 | { 20 | bool initialize_imgui(GraphicsDevice device, RenderWindow window, TextureFormat format) 21 | { 22 | // Convert the opaque types 23 | d3d12::DX12Window* dx12_window = (d3d12::DX12Window*)window; 24 | d3d12::DX12GraphicsDevice* dx12_device = (d3d12::DX12GraphicsDevice*)device; 25 | 26 | // Create the context 27 | ImGui::CreateContext(); 28 | ImGuiIO& io = ImGui::GetIO(); 29 | io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard; // Enable Keyboard Controls 30 | io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad; // Enable Gamepad Controls 31 | 32 | // Create the descriptor heap 33 | D3D12_DESCRIPTOR_HEAP_DESC desc = {}; 34 | desc.Type = D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV; 35 | desc.NumDescriptors = 1; 36 | desc.Flags = D3D12_DESCRIPTOR_HEAP_FLAG_SHADER_VISIBLE; 37 | if (dx12_device->device->CreateDescriptorHeap(&desc, IID_PPV_ARGS(&imguiDescHeap)) != S_OK) 38 | return false; 39 | 40 | ImGui_ImplWin32_Init(dx12_window->window); 41 | ImGui_ImplDX12_Init(dx12_device->device, 2, format_to_dxgi_format(format), imguiDescHeap, imguiDescHeap->GetCPUDescriptorHandleForHeapStart(), imguiDescHeap->GetGPUDescriptorHandleForHeapStart()); 42 | 43 | // Set the style 44 | ImGui::StyleColorsClassic(); 45 | return true; 46 | } 47 | 48 | void release_imgui() 49 | { 50 | ImGui_ImplDX12_Shutdown(); 51 | ImGui_ImplWin32_Shutdown(); 52 | imguiDescHeap->Release(); 53 | imguiDescHeap = nullptr; 54 | ImGui::DestroyContext(); 55 | } 56 | 57 | void start_frame() 58 | { 59 | ImGui_ImplDX12_NewFrame(); 60 | ImGui_ImplWin32_NewFrame(); 61 | ImGui::NewFrame(); 62 | } 63 | 64 | void end_frame() 65 | { 66 | ImGui::Render(); 67 | } 68 | 69 | void draw_frame(CommandBuffer cmd, RenderTexture renderTexture) 70 | { 71 | d3d12::DX12CommandBuffer* dx12_cmd = (d3d12::DX12CommandBuffer*)cmd; 72 | d3d12::DX12RenderTexture* dx12_rt = (d3d12::DX12RenderTexture*)renderTexture; 73 | 74 | // Create the handle 75 | D3D12_CPU_DESCRIPTOR_HANDLE rtvHandle(dx12_rt->descriptorHeap->GetCPUDescriptorHandleForHeapStart()); 76 | rtvHandle.ptr += dx12_rt->heapOffset; 77 | 78 | // Set the render target and enqueue into the command list 79 | dx12_cmd->cmdList()->OMSetRenderTargets(1, &rtvHandle, FALSE, nullptr); 80 | dx12_cmd->cmdList()->SetDescriptorHeaps(1, &imguiDescHeap); 81 | ImGui_ImplDX12_RenderDrawData(ImGui::GetDrawData(), dx12_cmd->cmdList()); 82 | } 83 | 84 | void handle_input(RenderWindow window, const EventData& data) 85 | { 86 | d3d12::DX12Window* dx12_window = (d3d12::DX12Window*)window; 87 | ImGui_ImplWin32_WndProcHandler(dx12_window->window, data.data0, data.data1, data.data2); 88 | } 89 | } 90 | } 91 | #endif 92 | -------------------------------------------------------------------------------- /demo/src/render_pipeline/morton_cache.cpp: -------------------------------------------------------------------------------- 1 | // Internal includes 2 | #include "render_pipeline/morton_cache.h" 3 | #include "math/operators.h" 4 | 5 | // External includes 6 | #include 7 | 8 | // Adds two empty bits every bit 9 | uint64_t interleave_bits(uint32_t x) 10 | { 11 | // Cap to 21 bits 12 | uint64_t v = x & 0x1fffff; 13 | v = (v | (v << 16)) & 0x0000003F0000FFFFull; 14 | v = (v | (v << 16)) & 0x003F0000FF0000FFull; 15 | v = (v | (v << 8)) & 0x300F00F00F00F00Full; 16 | v = (v | (v << 4)) & 0x30C30C30C30C30C3ull; 17 | v = (v | (v << 2)) & 0x9249249249249249ull; 18 | return v; 19 | } 20 | 21 | uint64_t morton_encode_3D(uint32_t x, uint32_t y, uint32_t z) 22 | { 23 | uint64_t xBits = interleave_bits(x); 24 | uint64_t yBits = interleave_bits(y); 25 | uint64_t zBits = interleave_bits(z); 26 | return (xBits << 2) | (yBits << 1) | zBits; 27 | } 28 | 29 | struct 30 | { 31 | bool operator()(const MortonCache::Element& a, const MortonCache::Element& b) const { return a.code < b.code; } 32 | } ElementLess; 33 | 34 | MortonCache::MortonCache() 35 | { 36 | } 37 | 38 | MortonCache::~MortonCache() 39 | { 40 | } 41 | 42 | uint64_t MortonCache::evaluate_morton_code(const float3& targetPosition) 43 | { 44 | const float3& normPos = (targetPosition - m_MinPosition) / (m_MaxPosition - m_MinPosition); 45 | 46 | // Evaluate and store the morton code 47 | uint32_t cx = (uint32_t)(normPos.x * (1 << 21)); 48 | uint32_t cy = (uint32_t)(normPos.y * (1 << 21)); 49 | uint32_t cz = (uint32_t)(normPos.z * (1 << 21)); 50 | return morton_encode_3D(cx, cy, cz); 51 | } 52 | 53 | void MortonCache::build_cache(const float3* positionArray, uint32_t numPositions) 54 | { 55 | // Allocate space for the cache 56 | m_NumElements = numPositions; 57 | m_Cache.resize(numPositions); 58 | 59 | // First we need to find the min and max values of the range 60 | for (uint32_t idx = 0; idx < m_NumElements; ++idx) 61 | { 62 | // Contribute to the min max 63 | const float3& currentPos = positionArray[idx]; 64 | m_MinPosition = min(currentPos, m_MinPosition); 65 | m_MaxPosition = max(currentPos, m_MaxPosition); 66 | } 67 | 68 | // Now generate the morton codes for every position 69 | for (uint32_t idx = 0; idx < m_NumElements; ++idx) 70 | { 71 | const float3& currentPos = positionArray[idx]; 72 | m_Cache[idx] = { evaluate_morton_code(currentPos), idx }; 73 | } 74 | 75 | // Sort the elements 76 | std::sort(m_Cache.begin(), m_Cache.end(), ElementLess); 77 | } 78 | 79 | uint32_t MortonCache::get_closest_element(const float3& position) 80 | { 81 | // Evaluate the morton code 82 | const uint64_t& target = evaluate_morton_code(position); 83 | 84 | // Do the binary search 85 | int low = 0; 86 | int high = m_NumElements - 1; 87 | Element& closest = m_Cache[0]; 88 | while (low <= high) 89 | { 90 | int mid = low + (high - low) / 2; 91 | 92 | // Update closest if the current element is closer to the target 93 | uint64_t leftT = m_Cache[mid].code > target ? m_Cache[mid].code - target : target - m_Cache[mid].code; 94 | uint64_t rightT = closest.code > target ? closest.code - target : target - closest.code; 95 | if (leftT < rightT) 96 | closest = m_Cache[mid]; 97 | 98 | // Did we find it? Otherwise, search left or right 99 | if (m_Cache[mid].code == target) 100 | return m_Cache[mid].index; // Exact match found 101 | else if (m_Cache[mid].code < target) 102 | low = mid + 1; // Search in the right half 103 | else 104 | high = mid - 1; // Search in the left half 105 | } 106 | 107 | return closest.index; 108 | } -------------------------------------------------------------------------------- /demo/include/graphics/descriptors.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | // Internal include 4 | #include "graphics/types.h" 5 | 6 | // External includes 7 | #include 8 | #include 9 | 10 | // Type of shader code 11 | enum class ShaderCodeType 12 | { 13 | Code = 0, 14 | Binary, 15 | Count 16 | }; 17 | 18 | // Descriptor for a given sampler 19 | struct SamplerDescriptor 20 | { 21 | FilterMode filterMode = FilterMode::Linear; 22 | SamplerMode modeX = SamplerMode::Wrap; 23 | SamplerMode modeY = SamplerMode::Wrap; 24 | SamplerMode modeZ = SamplerMode::Wrap; 25 | uint8_t anisotropy = 1; 26 | float minLOD = 0.0f; 27 | float maxLOD = 15.0f; 28 | }; 29 | 30 | // Descriptor for a given texture 31 | struct TextureDescriptor 32 | { 33 | // Dimension of the texture 34 | TextureType type = TextureType::Count; 35 | 36 | // Dimensions of the texture 37 | uint32_t width = 0; 38 | uint32_t height = 0; 39 | uint32_t depth = 0; 40 | 41 | // Number of mips 42 | uint32_t mipCount = 0; 43 | 44 | // Is the texture an UAV? 45 | bool isUAV = false; 46 | 47 | // Format 48 | TextureFormat format = TextureFormat::Count; 49 | 50 | // Clear color 51 | float4 clearColor = { 0.0, 0.0, 0.0, 0.0 }; 52 | 53 | // Optional debug name 54 | std::string debugName = ""; 55 | }; 56 | 57 | // Structure that describes a compute shader input 58 | struct ComputeShaderDescriptor 59 | { 60 | // File where the compute shader is stored 61 | std::string filename = ""; 62 | 63 | // Name of the kernel in the target file 64 | std::string kernelname = "main"; 65 | 66 | // List of include directories for the compute shader 67 | std::vector includeDirectories; 68 | 69 | // List of defines for the compute shader 70 | std::vector defines; 71 | 72 | // Code type 73 | ShaderCodeType codeType = ShaderCodeType::Code; 74 | 75 | // Other data 76 | bool debugFlag = false; 77 | }; 78 | 79 | // Descriptor for the whole blend state 80 | struct BlendState 81 | { 82 | bool enableBlend = false; 83 | BlendFactor SrcBlend = BlendFactor::One; 84 | BlendFactor DestBlend = BlendFactor::Zero; 85 | BlendOperator BlendOp = BlendOperator::Add; 86 | BlendFactor SrcBlendAlpha = BlendFactor::One; 87 | BlendFactor DestBlendAlpha = BlendFactor::Zero; 88 | BlendOperator BlendOpAlpha = BlendOperator::Add; 89 | }; 90 | 91 | struct DepthStencilState 92 | { 93 | // Common parameters 94 | bool enableDepth = false; 95 | bool enableStencil = false; 96 | TextureFormat depthStencilFormat = TextureFormat::Depth32Stencil8; 97 | 98 | // Depth parameters 99 | DepthTest depthtest = DepthTest::AlwaysPass; 100 | bool depthWrite = true; 101 | int32_t depthBias = 0; 102 | 103 | // Stencil test 104 | StencilTest stencilTest = StencilTest::AlwaysPass; 105 | uint8_t stencilWriteMask = 0xff; 106 | uint8_t stencilReadMask = 0xff; 107 | uint8_t stencilRef = 0; 108 | StencilOp stencilOperation = StencilOp::Keep; 109 | }; 110 | 111 | struct GraphicsPipelineDescriptor 112 | { 113 | // File where the stages are stored 114 | std::string filename; 115 | 116 | // First pipeline 117 | std::string vertexKernelName = "vert"; 118 | std::string hullKernelName = ""; 119 | std::string domainKernelName = ""; 120 | std::string geometryKernelName = ""; 121 | 122 | // Fragment shader 123 | std::string fragmentKernelName = "frag"; 124 | 125 | // List of defines for the graphics pipeline 126 | std::vector defines; 127 | 128 | // Include Dirs 129 | std::vector includeDirectories; 130 | 131 | // Render target formats 132 | uint32_t numRenderTargets = 1; 133 | TextureFormat rtFormat[8] = { TextureFormat::R16G16B16A16_Float }; 134 | 135 | // Depth stencil state 136 | DepthStencilState depthStencilState = { false, false }; 137 | 138 | // Blend mode 139 | BlendState blendState = { false }; 140 | 141 | // Rendering properties of the pipeline 142 | bool wireframe = false; 143 | CullMode cullMode = CullMode::Back; 144 | bool isProcedural = false; 145 | bool debugFlag = false; 146 | }; 147 | -------------------------------------------------------------------------------- /demo/src/render_pipeline/frustum_renderer.cpp: -------------------------------------------------------------------------------- 1 | // Internal includes 2 | #include "graphics/dx12_backend.h" 3 | #include "render_pipeline/constant_buffers.h" 4 | #include "render_pipeline/frustum_renderer.h" 5 | #include "math/operators.h" 6 | #include "tools/shader_utils.h" 7 | 8 | // Cst & Dst 9 | FrustumRenderer::FrustumRenderer() 10 | { 11 | } 12 | 13 | FrustumRenderer::~FrustumRenderer() 14 | { 15 | } 16 | 17 | // Init and release 18 | void FrustumRenderer::initialize(GraphicsDevice device, const LEBVolumeGPU& volume) 19 | { 20 | m_Device = device; 21 | m_UpdateCB = d3d12::resources::create_constant_buffer(m_Device, sizeof(UpdateCB), ConstantBufferType::Mixed); 22 | m_Position = volume.cameraPosition; 23 | m_ViewProj = volume.vpMat; 24 | m_VolumeMin = volume.scale * float3(-0.5, -0.5, -0.5); 25 | m_VolumeMax = volume.scale * float3(0.5, 0.5, 0.5); 26 | } 27 | 28 | void FrustumRenderer::release() 29 | { 30 | d3d12::graphics_pipeline::destroy_graphics_pipeline(m_FrustumAboveGP); 31 | d3d12::graphics_pipeline::destroy_graphics_pipeline(m_FrustumUnderGP); 32 | d3d12::resources::destroy_constant_buffer(m_UpdateCB); 33 | } 34 | 35 | // Reload the shaders 36 | void FrustumRenderer::reload_shader(const std::string& shaderLibrary) 37 | { 38 | GraphicsPipelineDescriptor gpd; 39 | gpd.includeDirectories.push_back(shaderLibrary); 40 | gpd.filename = shaderLibrary + "\\Frustum.graphics"; 41 | gpd.geometryKernelName = "geom"; 42 | gpd.depthStencilState.enableDepth = true; 43 | gpd.depthStencilState.depthtest = DepthTest::AlwaysPass; 44 | gpd.depthStencilState.depthWrite = false; 45 | gpd.depthStencilState.depthStencilFormat = TextureFormat::Depth32Stencil8; 46 | gpd.cullMode = CullMode::None; 47 | gpd.blendState.enableBlend = true; 48 | gpd.blendState.SrcBlend = BlendFactor::SrcAlpha; 49 | gpd.blendState.DestBlend = BlendFactor::InvSrcAlpha; 50 | gpd.blendState.SrcBlendAlpha = BlendFactor::Zero; 51 | gpd.blendState.DestBlendAlpha = BlendFactor::One; 52 | gpd.blendState.BlendOp = BlendOperator::Add; 53 | 54 | { 55 | gpd.fragmentKernelName = "frag_above"; 56 | compile_and_replace_graphics_pipeline(m_Device, gpd, m_FrustumAboveGP); 57 | 58 | gpd.fragmentKernelName = "frag_under"; 59 | compile_and_replace_graphics_pipeline(m_Device, gpd, m_FrustumUnderGP); 60 | } 61 | } 62 | 63 | void FrustumRenderer::upload_constant_buffers(CommandBuffer cmdB) 64 | { 65 | // Global constant buffer 66 | UpdateCB updateCB; 67 | updateCB._UpdateCameraPosition = m_Position; 68 | updateCB._UpdateViewProjectionMatrix = m_ViewProj; 69 | updateCB._UpdateInvViewProjectionMatrix = inverse(updateCB._UpdateViewProjectionMatrix); 70 | updateCB._UpdateFarPlaneDistance = 2.5; 71 | updateCB._UpdateFOV = 30.0 * DEG_TO_RAD; 72 | updateCB._VolumeMinPosition = m_VolumeMin; 73 | updateCB._VolumeMaxPosition = m_VolumeMax; 74 | d3d12::resources::set_constant_buffer(m_UpdateCB, (const char*)&updateCB, sizeof(UpdateCB)); 75 | d3d12::command_buffer::upload_constant_buffer(cmdB, m_UpdateCB); 76 | } 77 | 78 | void FrustumRenderer::render_above(CommandBuffer cmdB, ConstantBuffer globalCB) 79 | { 80 | d3d12::command_buffer::start_section(cmdB, "Render Frustum"); 81 | { 82 | // CBVs 83 | d3d12::command_buffer::set_graphics_pipeline_cbuffer(cmdB, m_FrustumAboveGP, "_GlobalCB", globalCB); 84 | d3d12::command_buffer::set_graphics_pipeline_cbuffer(cmdB, m_FrustumAboveGP, "_UpdateCB", m_UpdateCB); 85 | 86 | // Draw a full screen quad 87 | d3d12::command_buffer::draw_procedural(cmdB, m_FrustumAboveGP, 4, 1); 88 | } 89 | d3d12::command_buffer::end_section(cmdB); 90 | } 91 | 92 | void FrustumRenderer::render_under(CommandBuffer cmdB, ConstantBuffer globalCB) 93 | { 94 | d3d12::command_buffer::start_section(cmdB, "Render Frustum"); 95 | { 96 | // CBVs 97 | d3d12::command_buffer::set_graphics_pipeline_cbuffer(cmdB, m_FrustumUnderGP, "_GlobalCB", globalCB); 98 | d3d12::command_buffer::set_graphics_pipeline_cbuffer(cmdB, m_FrustumUnderGP, "_UpdateCB", m_UpdateCB); 99 | 100 | // Draw a full screen quad 101 | d3d12::command_buffer::draw_procedural(cmdB, m_FrustumUnderGP, 4, 1); 102 | } 103 | d3d12::command_buffer::end_section(cmdB); 104 | } -------------------------------------------------------------------------------- /shaders/shader_lib/grid_utilities.hlsl: -------------------------------------------------------------------------------- 1 | #ifndef GRID_UTILITIES_HLSL 2 | #define GRID_UTILITIES_HLSL 3 | 4 | // SRVs 5 | #if defined(DENSITY_BUFFER_0_BINDING_SLOT) 6 | StructuredBuffer _DensityBuffer0 : register(DENSITY_BUFFER_0_BINDING_SLOT); 7 | #endif 8 | 9 | #if defined(DENSITY_BUFFER_1_BINDING_SLOT) 10 | StructuredBuffer _DensityBuffer1 : register(DENSITY_BUFFER_1_BINDING_SLOT); 11 | #endif 12 | 13 | #if defined(GRID_CB_BINDING_SLOT) 14 | cbuffer _GridCB : register(GRID_CB_BINDING_SLOT) 15 | { 16 | // Resolution of the grid 17 | uint3 _GridResolution; 18 | 19 | // Padding 20 | float _PaddingGRB0; 21 | 22 | // Grid min position 23 | float3 _GridMinPosition; 24 | 25 | // Padding 26 | float _PaddingGRB1; 27 | 28 | // Grid max position 29 | float3 _GridMaxPosition; 30 | 31 | // Padding 32 | float _PaddingGRB2; 33 | 34 | // Grid scale position 35 | float3 _GridScale; 36 | 37 | // Padding 38 | float _PaddingGRB3; 39 | }; 40 | 41 | int3 evaluate_cell_coords(float3 position) 42 | { 43 | // Compute the normalized positon 44 | float3 normalizedPosition = (position - _GridMinPosition) / (_GridMaxPosition - _GridMinPosition); 45 | 46 | // Convert to the coordinates 47 | return clamp((int3)(normalizedPosition * _GridResolution), 0, _GridResolution - 1); 48 | } 49 | 50 | uint32_t cell_coord_to_index(int3 cellCoord) 51 | { 52 | uint3 coords = cellCoord; 53 | return coords.x + coords.y * _GridResolution.x + coords.z * _GridResolution.x * _GridResolution.y; 54 | } 55 | 56 | bool valid_cell_coord(int3 coord) 57 | { 58 | return coord.x >= 0 && coord.x < _GridResolution.x 59 | && coord.y >= 0 && coord.y < _GridResolution.y 60 | && coord.z >= 0 && coord.z < _GridResolution.z; 61 | } 62 | 63 | #if defined(DENSITY_BUFFER_0_BINDING_SLOT) && defined(DENSITY_BUFFER_1_BINDING_SLOT) 64 | float grid_density(int3 cellCoord) 65 | { 66 | // Evaluate the global index 67 | uint32_t globalIdx = cell_coord_to_index(cellCoord); 68 | 69 | // Read the data 70 | uint32_t page = globalIdx / 536870912; 71 | uint32_t cellIndex = globalIdx & 0x1FFFFFFF; 72 | if (page == 0) 73 | return _DensityBuffer0[cellIndex] * _DensityMultiplier; 74 | else 75 | return _DensityBuffer1[cellIndex] * _DensityMultiplier; 76 | } 77 | #endif 78 | 79 | #if defined(DENSITY_BUFFER_0_BINDING_SLOT) && defined(DENSITY_BUFFER_1_BINDING_SLOT) 80 | float integrate_density(float3 rayOrigin, float3 rayDir, int3 currentCellCoords) 81 | { 82 | // Initialize our loop 83 | float totalDensity = 0.0f; 84 | int3 moveIdx = sign(rayDir); 85 | 86 | // March our structure 87 | while (valid_cell_coord(currentCellCoords)) 88 | { 89 | // Evaluate the density of the current cell 90 | float density = grid_density(currentCellCoords); 91 | 92 | // Compute the normalized grid positon 93 | float3 positionGS = (rayOrigin - _GridMinPosition) / (_GridMaxPosition - _GridMinPosition) * _GridResolution; 94 | 95 | // Compute the position in the cell 96 | float3 positionInCell = positionGS - currentCellCoords; 97 | 98 | // Which direction are we moving in? 99 | if (moveIdx.x >= 0) 100 | positionInCell.x = 1.0 - positionInCell.x; 101 | 102 | if (moveIdx.y >= 0) 103 | positionInCell.y = 1.0 - positionInCell.y; 104 | 105 | if (moveIdx.z >= 0) 106 | positionInCell.z = 1.0 - positionInCell.z; 107 | 108 | // Project this position along each ray axis 109 | float3 projected = positionInCell / abs(rayDir) / _GridResolution; 110 | 111 | // Take the minimal dimension of the 3 112 | float t = 0.0; 113 | if (projected.x <= projected.y && projected.x <= projected.z) 114 | { 115 | t = projected.x; 116 | currentCellCoords.x += moveIdx.x; 117 | } 118 | else if (projected.y <= projected.x && projected.y <= projected.z) 119 | { 120 | t = projected.y; 121 | currentCellCoords.y += moveIdx.y; 122 | } 123 | else 124 | { 125 | t = projected.z; 126 | currentCellCoords.z += moveIdx.z; 127 | } 128 | 129 | // Move along the and accumulate the density 130 | rayOrigin += t * rayDir; 131 | totalDensity += t * density; 132 | } 133 | 134 | return totalDensity; 135 | } 136 | #endif 137 | #endif 138 | 139 | #endif // GRID_UTILITIES_HLSL -------------------------------------------------------------------------------- /demo/include/graphics/types.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | // Internal includes 4 | #include "math/types.h" 5 | 6 | // General graphics objects 7 | typedef uint64_t GraphicsDevice; 8 | typedef uint64_t RenderWindow; 9 | typedef uint64_t CommandQueue; 10 | typedef uint64_t SwapChain; 11 | typedef uint64_t CommandBuffer; 12 | 13 | // Shaders objects 14 | typedef uint64_t ComputeShader; 15 | typedef uint64_t GraphicsPipeline; 16 | typedef uint64_t RayTracingShader; 17 | 18 | // Syncrhonization 19 | typedef uint64_t Fence; 20 | 21 | // Resources objects 22 | typedef uint64_t Texture; 23 | typedef uint64_t RenderTexture; 24 | typedef uint64_t GraphicsBuffer; 25 | typedef uint64_t ConstantBuffer; 26 | typedef uint64_t Sampler; 27 | typedef uint64_t TopLevelAS; 28 | typedef uint64_t BottomLevelAS; 29 | 30 | // Profiling 31 | typedef uint64_t ProfilingScope; 32 | 33 | enum class GraphicsAPI 34 | { 35 | DX12 = 0, 36 | Vulkan, 37 | Count 38 | }; 39 | 40 | // Device pick strategy 41 | enum class DevicePickStrategy 42 | { 43 | VRAMSize = 0, 44 | VendorID, 45 | AdapterID, 46 | Count 47 | }; 48 | 49 | // Vendors for the GPUs 50 | enum class GPUVendor 51 | { 52 | Intel = 0, 53 | AMD, 54 | Nvidia, 55 | Other, 56 | Count 57 | }; 58 | 59 | enum class GPUFeature 60 | { 61 | RayTracing = 0, 62 | DoubleOps, 63 | HalfOps, 64 | Count 65 | }; 66 | 67 | // Types of constant buffers 68 | enum class ConstantBufferType 69 | { 70 | Static = 0x01, 71 | Runtime = 0x02, 72 | Mixed = 0x03 73 | }; 74 | 75 | // Types of graphics buffers 76 | enum class GraphicsBufferType 77 | { 78 | Default = 0, 79 | Upload, 80 | Readback, 81 | RTAS, 82 | Count 83 | }; 84 | 85 | // Types of the command buffer 86 | enum class CommandBufferType 87 | { 88 | Default = 0, 89 | Compute = 2, 90 | Copy = 3 91 | }; 92 | 93 | // Command queue priority 94 | enum class CommandQueuePriority 95 | { 96 | Normal = 0, 97 | High = 1, 98 | Realtime = 2, 99 | Count 100 | }; 101 | 102 | // Types of textures 103 | enum class TextureType 104 | { 105 | Tex1D = 0, 106 | Tex1DArray, 107 | Tex2D, 108 | Tex2DArray, 109 | Tex3D, 110 | TexCube, 111 | TexCubeArray, 112 | Count 113 | }; 114 | 115 | // Texture formats supported 116 | enum class TextureFormat 117 | { 118 | // 8 Formats 119 | R8_SNorm, 120 | R8_UNorm, 121 | R8_SInt, 122 | R8_UInt, 123 | R8G8_SNorm, 124 | R8G8_UNorm, 125 | R8G8_SInt, 126 | R8G8_UInt, 127 | R8G8B8A8_SNorm, 128 | R8G8B8A8_UNorm, 129 | R8G8B8A8_UNorm_SRGB, 130 | R8G8B8A8_UInt, 131 | R8G8B8A8_SInt, 132 | 133 | // 16 Formats 134 | R16_Float, 135 | R16_SInt, 136 | R16_UInt, 137 | R16G16_Float, 138 | R16G16_SInt, 139 | R16G16_UInt, 140 | R16G16B16A16_Float, 141 | R16G16B16A16_UInt, 142 | R16G16B16A16_SInt, 143 | 144 | // 32 Formats 145 | R32_Float, 146 | R32_SInt, 147 | R32_UInt, 148 | R32G32_Float, 149 | R32G32_SInt, 150 | R32G32_UInt, 151 | R32G32B32_UInt, 152 | R32G32B32_Float, 153 | R32G32B32A32_Float, 154 | R32G32B32A32_UInt, 155 | R32G32B32A32_SInt, 156 | 157 | // Depth buffer formats 158 | Depth32, 159 | Depth32Stencil8, 160 | Depth24Stencil8, 161 | 162 | // Other Formats 163 | R10G10B10A2_UNorm, 164 | R10G10B10A2_UInt, 165 | R11G11B10_Float, 166 | BC1_RGB, 167 | BC6_RGB, 168 | 169 | // Count 170 | Count 171 | }; 172 | 173 | enum class FilterMode 174 | { 175 | Point = 0, 176 | Linear, 177 | Anisotropic, 178 | Count 179 | }; 180 | 181 | enum class SamplerMode 182 | { 183 | Wrap = 1, 184 | Mirror, 185 | Clamp, 186 | Border, 187 | MirrorOnce, 188 | Count 189 | }; 190 | 191 | enum class DepthTest 192 | { 193 | Never = 1, 194 | Less, 195 | Equal, 196 | LEqual, 197 | Greater, 198 | NotEqual, 199 | GEqual, 200 | AlwaysPass, 201 | }; 202 | 203 | enum class StencilTest 204 | { 205 | Never = 1, 206 | Less, 207 | Equal, 208 | LEqual, 209 | Greater, 210 | NotEqual, 211 | GEqual, 212 | AlwaysPass, 213 | }; 214 | 215 | enum class StencilOp 216 | { 217 | Keep = 1, 218 | Zero = 2, 219 | Replace = 3, 220 | IncrementClamp = 4, 221 | DecrementClamp = 5, 222 | Invert = 6, 223 | IncrementWrap = 7, 224 | DecrementWrap = 8 225 | }; 226 | 227 | enum class BlendOperator 228 | { 229 | Add = 1, 230 | Subtract, 231 | RevSubstract, 232 | Min, 233 | Max 234 | }; 235 | 236 | enum class BlendFactor 237 | { 238 | Zero = 1, 239 | One = 2, 240 | SrcColor = 3, 241 | InvSrcColor = 4, 242 | SrcAlpha = 5, 243 | InvSrcAlpha = 6, 244 | DestAlpha = 7, 245 | InvDestAlpha = 8, 246 | DestColor = 9, 247 | InvDestColor = 10 248 | }; 249 | 250 | enum class CullMode 251 | { 252 | None = 1, 253 | Front = 2, 254 | Back = 3, 255 | }; 256 | 257 | enum class DrawPrimitive 258 | { 259 | Line, 260 | Triangle 261 | }; 262 | 263 | struct VertexData 264 | { 265 | float3 position; 266 | float3 normal; 267 | float3 tangent; 268 | float2 texCoord; 269 | uint32_t matID; 270 | }; -------------------------------------------------------------------------------- /shaders/LEB/Rasterizer.graphics: -------------------------------------------------------------------------------- 1 | // CBVs 2 | #define GLOBAL_CB_BINDING_SLOT b0 3 | #define LEB_CB_BINDING_SLOT b1 4 | 5 | // SRVs 6 | #define POSITION_BUFFER_BINDING_SLOT t0 7 | #define TETRA_BUFFER_0_BINDING_SLOT t1 8 | #define TETRA_BUFFER_1_BINDING_SLOT t2 9 | 10 | // Includes 11 | #include "shader_lib/common.hlsl" 12 | #include "shader_lib/constant_buffers.hlsl" 13 | #include "shader_lib/leb_utilities.hlsl" 14 | 15 | // SRVs 16 | StructuredBuffer _PositionBuffer : register(POSITION_BUFFER_BINDING_SLOT); 17 | 18 | static const uint g_indices[12] = {0, 2, 1, 0, 1, 3, 1, 2, 3, 2, 0, 3}; 19 | 20 | struct VertexInput 21 | { 22 | uint instanceID : SV_InstanceID; 23 | uint vertexID : SV_VertexID; 24 | }; 25 | 26 | struct VertexOutput 27 | { 28 | float4 position : SV_POSITION; 29 | float3 positionRWS : POSITION_RWS; 30 | float3 color : COLOR; 31 | float density : DENSITY; 32 | }; 33 | 34 | VertexOutput vert(VertexInput input) 35 | { 36 | // Initialize the output structure 37 | VertexOutput output; 38 | output = (VertexOutput)0; 39 | 40 | // Get the vertex position 41 | uint instanceOffset = input.instanceID * 4; 42 | float3 instanceCenter = (_PositionBuffer[instanceOffset] + _PositionBuffer[instanceOffset + 1] + _PositionBuffer[instanceOffset + 2] + _PositionBuffer[instanceOffset + 3]) / 4.0f; 43 | float3 vertexPos = _PositionBuffer[instanceOffset + g_indices[input.vertexID]]; 44 | float3 rescaledPos = (vertexPos - instanceCenter) + instanceCenter; 45 | 46 | // Output for the next stage 47 | output.positionRWS = rescaledPos / _LEBScale - _CameraPosition; 48 | output.position = mul(_ViewProjectionMatrix, float4(output.positionRWS, 1.0)); 49 | 50 | // Read the tetra data 51 | float density = leb_density(get_tetra_data(input.instanceID)); 52 | output.color = density > 0.0 ? float3(1.0, 0.0, 0.0) : float3(0.5, 1.0, 0.5); 53 | output.density = density; 54 | return output; 55 | } 56 | 57 | struct GeometryInput 58 | { 59 | float4 positionCS : SV_POSITION; 60 | float3 positionRWS : POSITION_RWS; 61 | float3 color : COLOR; 62 | float density : DENSITY; 63 | }; 64 | 65 | struct GeometryOutput 66 | { 67 | float4 positionCS : SV_POSITION; 68 | float3 positionRWS : POSITION_RWS; 69 | float3 color : COLOR; 70 | float3 dist : DISTANCE; 71 | float density : DENSITY; 72 | }; 73 | 74 | [maxvertexcount(3)] 75 | void geom(triangle GeometryInput input[3], inout TriangleStream outStream) 76 | { 77 | // Compute the vectors and the area 78 | float2 p0 = clip_space_to_pixel(input[0].positionCS, _ScreenSize.xy); 79 | float2 p1 = clip_space_to_pixel(input[1].positionCS, _ScreenSize.xy); 80 | float2 p2 = clip_space_to_pixel(input[2].positionCS, _ScreenSize.xy); 81 | float2 v[3] = {p2 - p1, p2 - p0, p1 - p0}; 82 | float area = abs(v[1].x * v[2].y - v[1].y * v[2].x); 83 | 84 | // Emit the vertices 85 | GeometryOutput output = (GeometryOutput)0; 86 | for (uint vertID = 0; vertID < 3; ++vertID) 87 | { 88 | output.positionCS = input[vertID].positionCS; 89 | output.color = input[vertID].color; 90 | output.positionRWS = input[vertID].positionRWS; 91 | output.dist = 0.0; 92 | output.density = input[vertID].density; 93 | output.dist[vertID] = area * rsqrt(dot(v[vertID],v[vertID])); 94 | outStream.Append(output); 95 | } 96 | outStream.RestartStrip(); 97 | } 98 | 99 | struct PixelInput 100 | { 101 | float4 positionCS : SV_POSITION; 102 | float3 positionRWS : POSITION_RWS; 103 | float3 color : COLOR; 104 | float3 dist : DISTANCE; 105 | float density : DENSITY; 106 | }; 107 | 108 | struct PixelOutput 109 | { 110 | float4 attachment0 : SV_Target0; 111 | }; 112 | 113 | float3 apply_wireframe(float3 color, float3 wireFrameColor, float wireframeSize, float3 dist) 114 | { 115 | if (wireframeSize > 0.0) 116 | { 117 | float3 d2 = dist * dist; 118 | float nearest = min(min(d2.x, d2.y), d2.z); 119 | float f = exp2(-nearest / wireframeSize); 120 | color = lerp(color, wireFrameColor, f); 121 | } 122 | return color; 123 | } 124 | 125 | PixelOutput frag(PixelInput input) 126 | { 127 | // Evaluate the distance to camera 128 | float distanceToCamera = length(input.positionRWS); 129 | 130 | // View vector 131 | float3 viewWS = -input.positionRWS / max(distanceToCamera, 0.00001); 132 | 133 | // Normal vector 134 | float3 normalWS = normalize(cross(ddx(input.positionRWS), ddy(input.positionRWS))); 135 | float3 roughness = 0.8; 136 | float3 diffuseColor = float3(1.0, 1.0, 1.0); 137 | 138 | // NdotV 139 | float NdotV = dot(viewWS, normalWS); 140 | 141 | // Evaluate the wireframe 142 | input.color = apply_wireframe(input.color * 0.0, 0.1, 0.4, input.dist); 143 | 144 | PixelOutput output; 145 | output.attachment0 = float4(input.color, 1.0); 146 | return output; 147 | } -------------------------------------------------------------------------------- /demo/src/volume/heuristic_cache.cpp: -------------------------------------------------------------------------------- 1 | // Internal includes 2 | #include "math/operators.h" 3 | #include "volume/heuristic_cache.h" 4 | #include "tools/security.h" 5 | 6 | // External includes incldues 7 | #include 8 | 9 | namespace heuristic_cache 10 | { 11 | void build_heuristic_cache(const GridVolume& volume, HeuristicCache& cache) 12 | { 13 | assert_msg(volume.resolution.x == volume.resolution.y 14 | && volume.resolution.x == volume.resolution.z, 15 | "This code assumes the grid is cubic, but the code can be extended."); 16 | 17 | // Keep track of the top resolution 18 | cache.resolution = volume.resolution.x >> 1; 19 | 20 | // Count the total number of cells 21 | uint64_t currentRes = cache.resolution; 22 | uint64_t initialOffset = 0; 23 | while (currentRes > 0) 24 | { 25 | // Level offset 26 | cache.offsets.push_back(initialOffset); 27 | cache.resolutions.push_back((uint32_t)currentRes); 28 | 29 | // Global offset 30 | initialOffset += currentRes * currentRes * currentRes; 31 | 32 | // Reduce resolution 33 | currentRes >>= 1; 34 | } 35 | 36 | // Allocate the memory space 37 | cache.momentArray.resize(initialOffset); 38 | cache.numLevels = (uint32_t)cache.offsets.size(); 39 | 40 | // First we evaluate the lowest level 41 | #pragma omp parallel for num_threads(32) 42 | for (int32_t z = 0; z < (int32_t)cache.resolution; ++z) 43 | { 44 | for (uint32_t y = 0; y < cache.resolution; ++y) 45 | { 46 | for (uint32_t x = 0; x < cache.resolution; ++x) 47 | { 48 | // Compute the statistics 49 | float minV = FLT_MAX; 50 | float maxV = -FLT_MAX; 51 | float mean = 0.0; 52 | float mean2 = 0.0; 53 | for (uint32_t lz = 0; lz < 2; ++lz) 54 | { 55 | for (uint32_t ly = 0; ly < 2; ++ly) 56 | { 57 | for (uint32_t lx = 0; lx < 2; ++lx) 58 | { 59 | // Offset of the input cell 60 | uint64_t offset = (uint64_t)x * 2 + (uint64_t)lx + (uint64_t)(y * 2ull + ly) * volume.resolution.x + (uint64_t)(z * 2ull + lz) * volume.resolution.x * volume.resolution.y; 61 | 62 | // Grab the density 63 | float density = volume.densityArray[offset]; 64 | 65 | // Contribute 66 | mean += density; 67 | mean2 += density * density; 68 | minV = std::min(minV, density); 69 | maxV = std::max(maxV, density); 70 | } 71 | } 72 | } 73 | // Normalize the average 74 | uint64_t cacheOffset = (uint64_t)x + (uint64_t)y * cache.resolution + (uint64_t)z * cache.resolution * cache.resolution; 75 | cache.momentArray[cacheOffset] = { mean * 0.125f, mean2 * 0.125f, minV, maxV }; 76 | } 77 | } 78 | } 79 | 80 | // Then process the remaining levels 81 | uint32_t inputRes = cache.resolution; 82 | uint32_t outputRes = inputRes >> 1; 83 | for (uint32_t lvlIdx = 1; lvlIdx < cache.offsets.size(); ++lvlIdx) 84 | { 85 | // First we evaluate the lowest level 86 | #pragma omp parallel for num_threads(32) 87 | for (int32_t z = 0; z < (int32_t)outputRes; ++z) 88 | { 89 | for (uint32_t y = 0; y < outputRes; ++y) 90 | { 91 | for (uint32_t x = 0; x < outputRes; ++x) 92 | { 93 | float minV = FLT_MAX; 94 | float maxV = -FLT_MAX; 95 | float mean = 0.0; 96 | float mean2 = 0.0; 97 | for (uint32_t lz = 0; lz < 2; ++lz) 98 | { 99 | for (uint32_t ly = 0; ly < 2; ++ly) 100 | { 101 | for (uint32_t lx = 0; lx < 2; ++lx) 102 | { 103 | // Offset of the input cell 104 | uint64_t offset = cache.offsets[lvlIdx - 1] + (uint64_t)x * 2 + (uint64_t)lx + (uint64_t)(y * 2 + ly) * inputRes + (uint64_t)(z * 2 + lz) * inputRes * inputRes; 105 | const float4& data = cache.momentArray[offset]; 106 | // Contribute 107 | mean += data.x; 108 | mean2 += data.y; 109 | minV = std::min(minV, data.z); 110 | maxV = std::max(maxV, data.w); 111 | } 112 | } 113 | } 114 | 115 | // Normalize the average 116 | uint64_t cacheOffset = cache.offsets[lvlIdx] + (uint64_t)x + (uint64_t)y * outputRes + (uint64_t)z * outputRes * outputRes; 117 | cache.momentArray[cacheOffset] = { mean * 0.125f, mean2 * 0.125f, minV, maxV }; 118 | } 119 | } 120 | } 121 | 122 | inputRes = outputRes; 123 | outputRes = inputRes >> 1; 124 | } 125 | } 126 | 127 | float4 sample_cache(const HeuristicCache& cache, const float3& position, uint32_t depth) 128 | { 129 | int32_t cacheDepth = std::max((int32_t)(cache.numLevels - (depth - 4) / 3 - 1), 0); 130 | uint32_t resolution = cache.resolutions[cacheDepth]; 131 | uint64_t offset = cache.offsets[cacheDepth]; 132 | 133 | // Evalute the normalized positon 134 | float3 normPos = position + float3({ 0.5, 0.5, 0.5 }); 135 | 136 | // Evalute the coords 137 | int64_t coordX = int64_t(normPos.x * resolution); 138 | int64_t coordY = int64_t(normPos.y * resolution); 139 | int64_t coordZ = int64_t(normPos.z * resolution); 140 | return cache.momentArray[offset + coordX + coordY * resolution + resolution * resolution * coordZ]; 141 | } 142 | } 143 | 144 | -------------------------------------------------------------------------------- /dependencies.bat: -------------------------------------------------------------------------------- 1 | @echo off 2 | setlocal 3 | 4 | :: === AGILITY SDK === 5 | set "ASDK_PACKAGE_NAME=Microsoft.Direct3D.D3D12" 6 | set "ASDK_PACKAGE_VERSION=1.717.0-preview" 7 | set "ASDK_DOWNLOAD_URL=https://www.nuget.org/api/v2/package/%ASDK_PACKAGE_NAME%/%ASDK_PACKAGE_VERSION%" 8 | set "ASDK_OUTPUT_DIR=%~dp0nuget_packages\%ASDK_PACKAGE_NAME%" 9 | set "ASDK_PACKAGE_NUPKG=%ASDK_OUTPUT_DIR%\%ASDK_PACKAGE_NAME%.%ASDK_PACKAGE_VERSION%.nupkg" 10 | set "ASDK_PACKAGE_ZIP=%ASDK_OUTPUT_DIR%\%ASDK_PACKAGE_NAME%.%ASDK_PACKAGE_VERSION%.zip" 11 | set "ASDK_EXTRACT_DIR=%ASDK_OUTPUT_DIR%/agility_package" 12 | 13 | :: === Create output directory === 14 | if not exist "%ASDK_OUTPUT_DIR%" ( 15 | mkdir "%ASDK_OUTPUT_DIR%" 16 | ) 17 | 18 | :: === Download NuGet package === 19 | echo Downloading %PACKAGE_NAME% version %ASDK_PACKAGE_VERSION%... 20 | powershell -Command "Invoke-WebRequest -Uri '%ASDK_DOWNLOAD_URL%' -OutFile '%ASDK_PACKAGE_NUPKG%'" 21 | 22 | if not exist "%ASDK_PACKAGE_NUPKG%" ( 23 | echo Failed to download package. 24 | exit /b 1 25 | ) 26 | 27 | :: === Rename .nupkg to .zip === 28 | copy /Y "%ASDK_PACKAGE_NUPKG%" "%ASDK_PACKAGE_ZIP%" >nul 29 | 30 | :: === Extract the ZIP archive === 31 | echo Extracting package... 32 | powershell -Command "Expand-Archive -Path '%ASDK_PACKAGE_ZIP%' -DestinationPath '%ASDK_EXTRACT_DIR%' -Force" 33 | 34 | :: === Copy the include files === 35 | set "TARGET_DIR=%~dp0\3rd\include" 36 | if not exist "%TARGET_DIR%" ( 37 | mkdir "%TARGET_DIR%" 38 | ) 39 | 40 | set "SOURCE_INCLUDE=%ASDK_EXTRACT_DIR%\build\native\include\d3d12.h" 41 | copy /Y "%SOURCE_INCLUDE%" "%TARGET_DIR%\" 42 | 43 | set "SOURCE_INCLUDE=%ASDK_EXTRACT_DIR%\build\native\include\d3d12compatibility.h" 44 | copy /Y "%SOURCE_INCLUDE%" "%TARGET_DIR%\" 45 | 46 | set "SOURCE_INCLUDE=%ASDK_EXTRACT_DIR%\build\native\include\d3d12sdklayers.h" 47 | copy /Y "%SOURCE_INCLUDE%" "%TARGET_DIR%\" 48 | 49 | set "SOURCE_INCLUDE=%ASDK_EXTRACT_DIR%\build\native\include\d3d12shader.h" 50 | copy /Y "%SOURCE_INCLUDE%" "%TARGET_DIR%\" 51 | 52 | set "SOURCE_INCLUDE=%ASDK_EXTRACT_DIR%\build\native\include\d3d12video.h" 53 | copy /Y "%SOURCE_INCLUDE%" "%TARGET_DIR%\" 54 | 55 | set "SOURCE_INCLUDE=%ASDK_EXTRACT_DIR%\build\native\include\d3dcommon.h" 56 | copy /Y "%SOURCE_INCLUDE%" "%TARGET_DIR%\" 57 | 58 | set "SOURCE_INCLUDE=%ASDK_EXTRACT_DIR%\build\native\include\dxgiformat.h" 59 | copy /Y "%SOURCE_INCLUDE%" "%TARGET_DIR%\" 60 | 61 | :: === Copy the binary files === 62 | set "TARGET_DIR=%~dp0\3rd\bin\D3D12" 63 | if not exist "%TARGET_DIR%" ( 64 | mkdir "%TARGET_DIR%" 65 | ) 66 | set "SOURCE_DLL=%ASDK_EXTRACT_DIR%\build\native\bin\x64\d3d12SDKLayers.dll" 67 | copy /Y "%SOURCE_DLL%" "%TARGET_DIR%\" 68 | 69 | set "SOURCE_DLL=%ASDK_EXTRACT_DIR%\build\native\bin\x64\D3D12Core.dll" 70 | copy /Y "%SOURCE_DLL%" "%TARGET_DIR%\" 71 | 72 | :: === DXC Compiler === 73 | set "DXC_PACKAGE_NAME=Microsoft.Direct3D.DXC" 74 | set "DXC_PACKAGE_VERSION=1.8.2505.28" 75 | set "DXC_DOWNLOAD_URL=https://www.nuget.org/api/v2/package/%DXC_PACKAGE_NAME%/%DXC_PACKAGE_VERSION%" 76 | set "DXC_OUTPUT_DIR=%~dp0nuget_packages\%DXC_PACKAGE_NAME%" 77 | set "DXC_PACKAGE_NUPKG=%DXC_OUTPUT_DIR%\%DXC_PACKAGE_NAME%.%DXC_PACKAGE_VERSION%.nupkg" 78 | set "DXC_PACKAGE_ZIP=%DXC_OUTPUT_DIR%\%DXC_PACKAGE_NAME%.%DXC_PACKAGE_VERSION%.zip" 79 | set "DXC_EXTRACT_DIR=%DXC_OUTPUT_DIR%/dxc_package" 80 | 81 | :: === Create output directory === 82 | if not exist "%DXC_OUTPUT_DIR%" ( 83 | mkdir "%DXC_OUTPUT_DIR%" 84 | ) 85 | 86 | :: === Download NuGet package === 87 | echo Downloading %PACKAGE_NAME% version %DXC_PACKAGE_VERSION%... 88 | powershell -Command "Invoke-WebRequest -Uri '%DXC_DOWNLOAD_URL%' -OutFile '%DXC_PACKAGE_NUPKG%'" 89 | 90 | if not exist "%DXC_PACKAGE_NUPKG%" ( 91 | echo Failed to download package. 92 | exit /b 1 93 | ) 94 | 95 | :: === Rename .nupkg to .zip === 96 | copy /Y "%DXC_PACKAGE_NUPKG%" "%DXC_PACKAGE_ZIP%" >nul 97 | 98 | :: === Extract the ZIP archive === 99 | echo Extracting package... 100 | powershell -Command "Expand-Archive -Path '%DXC_PACKAGE_ZIP%' -DestinationPath '%DXC_EXTRACT_DIR%' -Force" 101 | 102 | :: === Copy the binary files === 103 | set "TARGET_DIR=%~dp0\3rd\bin" 104 | if not exist "%TARGET_DIR%" ( 105 | mkdir "%TARGET_DIR%" 106 | ) 107 | set "SOURCE_DLL=%DXC_EXTRACT_DIR%\build\native\bin\x64\dxcompiler.dll" 108 | copy /Y "%SOURCE_DLL%" "%TARGET_DIR%\" 109 | 110 | set "SOURCE_DLL=%DXC_EXTRACT_DIR%\build\native\bin\x64\dxil.dll" 111 | copy /Y "%SOURCE_DLL%" "%TARGET_DIR%\" 112 | 113 | :: === Copy the library files === 114 | set "TARGET_DIR=%~dp0\3rd\lib" 115 | if not exist "%TARGET_DIR%" ( 116 | mkdir "%TARGET_DIR%" 117 | ) 118 | set "SOURCE_LIB=%DXC_EXTRACT_DIR%\build\native\lib\x64\dxcompiler.lib" 119 | copy /Y "%SOURCE_LIB%" "%TARGET_DIR%\" 120 | 121 | set "SOURCE_LIB=%DXC_EXTRACT_DIR%\build\native\lib\x64\dxil.lib" 122 | copy /Y "%SOURCE_LIB%" "%TARGET_DIR%\" 123 | 124 | if exist "%~dp0nuget_packages%" ( 125 | rmdir /s /q "%~dp0nuget_packages%" 126 | ) 127 | 128 | echo Dependencies have been downloaded. 129 | exit /b 0 130 | -------------------------------------------------------------------------------- /demo/include/math/operators.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | // SDK includes 4 | #include "math/types.h" 5 | 6 | #pragma region float 7 | float min3(float v0, float v1, float v2); 8 | float max3(float v0, float v1, float v2); 9 | float lerp(const float& v0, const float& v1, float f); 10 | #pragma endregion 11 | 12 | #pragma region float2 13 | float2 lerp(const float2& v0, const float2& v1, float f); 14 | float2 min(const float2& v0, const float2& v1); 15 | float2 max(const float2& v0, const float2& v1); 16 | float length(const float2& v); 17 | float2 operator-(const float2& v0, const float2& v1); 18 | float2 operator+(const float2& v0, const float2& v1); 19 | float2 operator*(const float2& v, float s); 20 | float2 operator/(const float2& v, float s); 21 | #pragma endregion 22 | 23 | #pragma region float3 24 | float3 negate(const float3& v); 25 | float3 normalize(const float3& v); 26 | float length(const float3& v); 27 | float3 max_zero(const float3& v); 28 | float3 cross(const float3& v0, const float3& v1); 29 | float dot(const float3& v0, const float3& v1); 30 | float3 lerp(const float3& v0, const float3& v1, float f); 31 | float3 min(const float3& v0, const float3& v1); 32 | float3 max(const float3& v0, const float3& v1); 33 | float3 rcp(const float3& v); 34 | float3 operator+(const float3& v0, const float3& v1); 35 | float3 operator-(const float3& v0, const float3& v1); 36 | float3 operator*(const float3& v, float s); 37 | float3 operator*(const float3& v0, const float3& v1); 38 | float3 operator/(const float3& v, float s); 39 | float3 operator/(const float3& v0, const float3& v1); 40 | float3 sign(const float3& v); 41 | #pragma endregion 42 | 43 | #pragma region float4 44 | float4 negate(const float4& v); 45 | float4 normalize(const float4& v); 46 | float length(const float4& v); 47 | float4 max_zero(const float4& v); 48 | float3& xyz(float4& q); 49 | const float3& xyz(const float4& q); 50 | float4 lerp(const float4& v0, const float4& v1, float f); 51 | float4 operator+(const float4& v0, const float4& v1); 52 | float4 operator-(const float4& v0, const float4& v1); 53 | float4 operator*(const float4& v, float s); 54 | float4 operator/(const float4& v, float s); 55 | #pragma endregion 56 | 57 | #pragma region double3 58 | double length(const double3& v); 59 | double3 lerp(const double3& v0, const double3& v1, double f); 60 | double3 operator+(const double3& v0, const double3& v1); 61 | double3 operator-(const double3& v0, const double3& v1); 62 | double3 operator*(const double3& v, double s); 63 | double3 operator/(const double3& v, double s); 64 | #pragma endregion 65 | 66 | #pragma region double4 67 | double4 operator*(const double4& v, double s); 68 | double4 operator/(const double4& v, double s); 69 | #pragma endregion 70 | 71 | #pragma region uint2 72 | uint2 operator+(const uint2& v0, const uint2& v1); 73 | #pragma endregion 74 | 75 | #pragma region uint3 76 | uint32_t at(const uint3& v, uint32_t idx); 77 | uint32_t& at(uint3& v, uint32_t idx); 78 | uint3 operator*(const uint3& v0, const uint3& v1); 79 | uint3 operator*(const uint3& v, uint32_t s); 80 | uint3 operator&(const uint3& v, uint32_t s); 81 | #pragma endregion 82 | 83 | #pragma region uint4 84 | uint32_t at(const uint4& v, uint32_t idx); 85 | uint32_t& at(uint4& v, uint32_t idx); 86 | #pragma endregion 87 | 88 | #pragma region float3x3 89 | float3x3 transpose(const float3x3& m); 90 | float3x3 mul(const float3x3& m0, const float3x3& m1); 91 | #pragma region end 92 | 93 | #pragma region float4x4 94 | float4x4 identity_float4x4(); 95 | float4x4 translation_matrix(const float3& translation); 96 | float4x4 rotation_matrix_x(const float angle); 97 | float4x4 rotation_matrix_y(const float angle); 98 | float4x4 rotation_matrix_z(const float angle); 99 | float4x4 rotation_matrix_axis(const float angle, const float3& axis); 100 | float4x4 projection_matrix(float fov, float nearZ, float farZ, float aspectRatio); 101 | float4x4 zoom_matrix(const float2& scale, const float2& offset); 102 | float4x4 look_at_matrix(const float3& eye, const float3& focus, const float3& up); 103 | float4x4 mul(const float4x4& m0, const float4x4& m1); 104 | float4x4 transpose(const float4x4& m); 105 | float4x4 inverse(const float4x4& m); 106 | float4 mul(const float4x4& m0, const float4& v); 107 | float3 mul(const float4x4& m0, const float3& v); 108 | float4 mul_transpose(const float4x4& m0, const float4& v); 109 | #pragma endregion 110 | 111 | #pragma region double4x4 112 | double4x4 identity_double4x4(); 113 | double4x4 zoom_matrix(const double2& scale, const double2& offset); 114 | double4x4 mul(const double4x4& m0, const double4x4& m1); 115 | double4x4 convert_to_double(const float4x4& m); 116 | double4x4 projection_matrix(double fov, double nearZ, double farZ, double aspectRatio); 117 | #pragma endregion 118 | 119 | #pragma region quaternion 120 | float4 slerp(const float4& a, const float4& b, float t); 121 | float4 mul(const float4& q0, const float4& q1); 122 | float4x4 quaternion_to_matrix(const float4& quat); 123 | float4 matrix_to_quaternion(const float4x4& m); 124 | #pragma endregion 125 | 126 | uint32_t find_msb(uint32_t x); 127 | uint32_t find_msb_64(uint64_t x); 128 | int32_t round_up_power2(uint32_t v); -------------------------------------------------------------------------------- /shaders/shader_lib/intersection.hlsl: -------------------------------------------------------------------------------- 1 | #ifndef INTERSECTION_HLSL 2 | #define INTERSECTION_HLSL 3 | 4 | float3 evaluate_ray_direction(uint2 threadID) 5 | { 6 | float3 depthBufferPosition = evaluate_world_space_position((threadID.xy + float2(0.5, 0.5)) * _ScreenSize.zw, 0.5, _InvViewProjectionMatrix); 7 | float distanceToCamera = length(depthBufferPosition); 8 | return depthBufferPosition / max(distanceToCamera, 0.000001); 9 | } 10 | 11 | float2 intersect_ray_aabb(float3 rayOrigin, float3 rayDir, float3 boxMin, float3 boxMax) 12 | { 13 | float3 tMin = (boxMin - rayOrigin) / rayDir; 14 | float3 tMax = (boxMax - rayOrigin) / rayDir; 15 | float3 t1 = min(tMin, tMax); 16 | float3 t2 = max(tMin, tMax); 17 | float tNear = max(max(t1.x, t1.y), t1.z); 18 | float tFar = min(min(t2.x, t2.y), t2.z); 19 | return float2(tNear, tFar); 20 | }; 21 | 22 | float ray_triangle_intersection(float3 rayOrigin, float3 rayDirection, float3 p0, float3 p1, float3 p2) 23 | { 24 | // Tolerance for avoiding floating-point precision issues 25 | float epsilon = 1e-10; 26 | 27 | // Edge vectors 28 | float3 edge1 = p1 - p0; 29 | float3 edge2 = p2 - p0; 30 | 31 | // Begin calculating determinant - also used to calculate U parameter 32 | float3 pvec = cross(rayDirection, edge2); 33 | 34 | // If determinant is near zero, ray lies in plane of triangle 35 | float det = dot(edge1, pvec); 36 | 37 | if (det > -epsilon && det < epsilon) 38 | return -1.0; 39 | 40 | float invDet = 1.0 / det; 41 | 42 | // Calculate distance from v0 to ray origin 43 | float3 tvec = rayOrigin - p0; 44 | 45 | // Calculate U parameter and test bounds 46 | float u = dot(tvec, pvec) * invDet; 47 | if (u < 0.0 || u > 1.0) 48 | return -1.0; 49 | 50 | // Prepare to test V parameter 51 | float3 qvec = cross(tvec, edge1); 52 | 53 | // Calculate V parameter and test bounds 54 | float v = dot(rayDirection, qvec) * invDet; 55 | if (v < 0.0 || u + v > 1.0) 56 | return -1.0; 57 | 58 | // Calculate t, ray intersects triangle 59 | float t = dot(edge2, qvec) * invDet; 60 | 61 | // If t is negative, the intersection is behind the ray origin 62 | if (t < 0) 63 | return -1.0; 64 | return t; 65 | } 66 | 67 | float ray_box_intersection(float3 rayOrigin, float3 rayDir, float3 boxMin, float3 boxMax) 68 | { 69 | float3 invDir = 1.0 / rayDir; 70 | float3 t0 = (boxMin - rayOrigin) * invDir; 71 | float3 t1 = (boxMax - rayOrigin) * invDir; 72 | 73 | float3 tMinVec = min(t0, t1); 74 | float3 tMaxVec = max(t0, t1); 75 | 76 | float tMin = max(tMinVec.x, max(tMinVec.y, tMinVec.z)); 77 | float tMax = min(tMaxVec.x, min(tMaxVec.y, tMaxVec.z)); 78 | 79 | if (tMax < tMin || tMax < 0.0) 80 | return -1.0; 81 | 82 | // Select the first positive intersection 83 | return (tMin > 0.0) ? tMin : tMax; 84 | } 85 | 86 | float signed_volume(float3 p1, float3 p2, float3 p3, float3 p4) 87 | { 88 | return (1.0f / 6.0f) * ( 89 | (p1.x - p4.x) * (p2.y - p4.y) * (p3.z - p4.z) + 90 | (p2.x - p4.x) * (p3.y - p4.y) * (p1.z - p4.z) + 91 | (p3.x - p4.x) * (p1.y - p4.y) * (p2.z - p4.z) - 92 | (p3.x - p4.x) * (p2.y - p4.y) * (p1.z - p4.z) - 93 | (p2.x - p4.x) * (p1.y - p4.y) * (p3.z - p4.z) - 94 | (p1.x - p4.x) * (p3.y - p4.y) * (p2.z - p4.z) 95 | ); 96 | } 97 | 98 | // Function to check if a point is inside a tetrahedron 99 | int point_inside_tetrahedron(float3 p, float3 v0, float3 v1, float3 v2, float3 v3) 100 | { 101 | float v0v1v2v3 = signed_volume(v0, v1, v2, v3); 102 | float pv1v2v3 = signed_volume(p, v1, v2, v3); 103 | float v0pv2v3 = signed_volume(v0, p, v2, v3); 104 | float v0v1pv3 = signed_volume(v0, v1, p, v3); 105 | float v0v1v2p = signed_volume(v0, v1, v2, p); 106 | return (v0v1v2v3 >= 0 && pv1v2v3 >= 0 && v0pv2v3 >= 0 && v0v1pv3 >= 0 && v0v1v2p >= 0) || 107 | (v0v1v2v3 <= 0 && pv1v2v3 <= 0 && v0pv2v3 <= 0 && v0v1pv3 <= 0 && v0v1v2p <= 0); 108 | } 109 | 110 | float ray_plane_intersection(float3 rayOrigin, float3 rayDirection, float3 planeNormal, float planeOffset) 111 | { 112 | float denom = dot(rayDirection, planeNormal); 113 | if (denom < 1e-6) 114 | return FLT_MAX; 115 | float t = -(dot(rayOrigin, planeNormal) + planeOffset); 116 | return t > -1e-6 ? t / denom : FLT_MAX; 117 | } 118 | 119 | float ray_sphere_intersect_nearest(float3 r0, float3 rd, float3 s0, float sR) 120 | { 121 | float a = dot(rd, rd); 122 | float3 s0_r0 = r0 - s0; 123 | float b = 2.0 * dot(rd, s0_r0); 124 | float c = dot(s0_r0, s0_r0) - (sR * sR); 125 | float delta = b * b - 4.0*a*c; 126 | if (delta < 0.0 || a == 0.0) 127 | return -1.0; 128 | float sol0 = (-b - sqrt(delta)) / (2.0*a); 129 | float sol1 = (-b + sqrt(delta)) / (2.0*a); 130 | if (sol0 < 0.0 && sol1 < 0.0) 131 | return -1.0; 132 | if (sol0 < 0.0) 133 | return max(0.0, sol1); 134 | else if (sol1 < 0.0) 135 | return max(0.0, sol0); 136 | return max(0.0, min(sol0, sol1)); 137 | } 138 | 139 | #endif // INTERSECTION_HLSL -------------------------------------------------------------------------------- /demo/src/render_pipeline/camera_controller.cpp: -------------------------------------------------------------------------------- 1 | // Internal includes 2 | #include "graphics/dx12_backend.h" 3 | #include "render_pipeline/camera_controller.h" 4 | #include "imgui/imgui.h" 5 | #include "math/operators.h" 6 | 7 | // External includes 8 | #include 9 | #include 10 | #include 11 | 12 | CameraController::CameraController() 13 | { 14 | } 15 | 16 | CameraController::~CameraController() 17 | { 18 | } 19 | 20 | void CameraController::initialize(RenderWindow renderWindow, uint32_t width, uint32_t height, float fov) 21 | { 22 | // Keep track of the window 23 | m_Window = renderWindow; 24 | 25 | // Camera properties 26 | m_Camera.fov = fov; 27 | m_Camera.aspectRatio = width / (float)height; 28 | m_Camera.position = {0.0, 0.0, 0.0}; 29 | m_Camera.angles = { 0.5, 0.0, 0.0 }; 30 | m_Camera.scaleOffset = { 0.0f, 0.0f, 0.0f }; 31 | m_Speed = 0.1; 32 | 33 | // Init control 34 | m_ActiveInteraction = false; 35 | } 36 | 37 | void CameraController::process_key_event(uint32_t keyCode, bool state) 38 | { 39 | switch (keyCode) 40 | { 41 | case 0x44: // D 42 | m_ControllerStates[(uint32_t)NavigationButtons::Right] = state; 43 | break; 44 | case 0x51: // Q 45 | m_ControllerStates[(uint32_t)NavigationButtons::Left] = state; 46 | break; 47 | case 0x5A: // Z 48 | m_ControllerStates[(uint32_t)NavigationButtons::Forward] = state; 49 | break; 50 | case 0x53: // S 51 | m_ControllerStates[(uint32_t)NavigationButtons::Backward] = state; 52 | break; 53 | case 0x41: // A 54 | m_ControllerStates[(uint32_t)NavigationButtons::Up] = state; 55 | break; 56 | case 0x45: // E 57 | m_ControllerStates[(uint32_t)NavigationButtons::Down] = state; 58 | break; 59 | case 0x10: // Shift 60 | m_ControllerStates[(uint32_t)NavigationButtons::Shift] = state; 61 | break; 62 | } 63 | } 64 | 65 | bool CameraController:: process_mouse_button(MouseButton button, bool) 66 | { 67 | if (button == MouseButton::Right) 68 | { 69 | d3d12::window::set_cursor_visibility(m_Window, m_ActiveInteraction); 70 | m_ActiveInteraction = !m_ActiveInteraction; 71 | return true; 72 | } 73 | return false; 74 | } 75 | 76 | bool CameraController::process_mouse_movement(int2 mouse, uint2 windowCenter, float4 screenSize) 77 | { 78 | if (m_ActiveInteraction) 79 | { 80 | int2 mouv = int2({ mouse.x - (int)windowCenter.x, mouse.y - (int)windowCenter.y }); 81 | m_Camera.angles.x -= mouv.x / screenSize.x * 5.0f; 82 | m_Camera.angles.y -= mouv.y / screenSize.y * 5.0f; 83 | return true; 84 | } 85 | return false; 86 | } 87 | 88 | void CameraController::process_mouse_wheel(int wheel) 89 | { 90 | if (m_ActiveInteraction) 91 | { 92 | if (wheel > 0) 93 | m_Speed *= 2.0; 94 | else 95 | m_Speed /= 2.0; 96 | } 97 | } 98 | 99 | void CameraController::update(double deltaTime) 100 | { 101 | if (m_ActiveInteraction) 102 | { 103 | float3 forwardDir = float3({ m_Camera.view.m[2], m_Camera.view.m[6], m_Camera.view.m[10] }); 104 | float3 rightDir = float3({ m_Camera.view.m[0], m_Camera.view.m[4], m_Camera.view.m[8] }); 105 | float speed = m_Speed * (float)deltaTime; 106 | 107 | float3 displacement = float3({ 0.0, 0.0, 0.0 }); 108 | if (m_ControllerStates[(uint32_t)NavigationButtons::Forward]) 109 | { 110 | displacement.x += forwardDir.x * speed; 111 | displacement.y += forwardDir.y * speed; 112 | displacement.z += forwardDir.z * speed; 113 | } 114 | 115 | if (m_ControllerStates[(uint32_t)NavigationButtons::Backward]) 116 | { 117 | displacement.x -= forwardDir.x * speed; 118 | displacement.y -= forwardDir.y * speed; 119 | displacement.z -= forwardDir.z * speed; 120 | } 121 | 122 | if (m_ControllerStates[(uint32_t)NavigationButtons::Left]) 123 | { 124 | displacement.x -= rightDir.x * speed; 125 | displacement.y -= rightDir.y * speed; 126 | displacement.z -= rightDir.z * speed; 127 | } 128 | 129 | if (m_ControllerStates[(uint32_t)NavigationButtons::Right]) 130 | { 131 | displacement.x += rightDir.x * speed; 132 | displacement.y += rightDir.y * speed; 133 | displacement.z += rightDir.z * speed; 134 | } 135 | 136 | m_Camera.position = m_Camera.position + displacement; 137 | } 138 | 139 | // Position has been updated, update the matrices 140 | evaluate_camera_matrices(); 141 | } 142 | 143 | void CameraController::evaluate_camera_matrices() 144 | { 145 | // Evaluate the projection matrix 146 | m_Camera.projection = projection_matrix(m_Camera.fov, m_Camera.nearFar.x, m_Camera.nearFar.y, m_Camera.aspectRatio); 147 | 148 | // Update the view matrix 149 | const float4x4 rotation_z = rotation_matrix_z(m_Camera.angles.z); 150 | const float4x4 rotation_y = rotation_matrix_y(m_Camera.angles.x); 151 | const float4x4 rotation_x = rotation_matrix_x(m_Camera.angles.y); 152 | m_Camera.view = mul(rotation_z, mul(rotation_x, rotation_y)); 153 | 154 | 155 | // Update the compound matrices 156 | m_Camera.viewProjection = mul(m_Camera.projection, m_Camera.view); 157 | 158 | // Compute the inverse matrices 159 | m_Camera.invViewProjection = inverse(m_Camera.viewProjection); 160 | } -------------------------------------------------------------------------------- /shaders/Grid/Rasterizer.graphics: -------------------------------------------------------------------------------- 1 | // CBVs 2 | #define GLOBAL_CB_BINDING_SLOT b0 3 | #define GRID_CB_BINDING_SLOT b1 4 | 5 | // SRVs 6 | #define DENSITY_BUFFER_0_BINDING_SLOT t0 7 | #define DENSITY_BUFFER_1_BINDING_SLOT t1 8 | 9 | // Includes 10 | #include "shader_lib/common.hlsl" 11 | #include "shader_lib/constant_buffers.hlsl" 12 | #include "shader_lib/grid_utilities.hlsl" 13 | 14 | static const float3 vertices[] = { 15 | float3(0.0, 0.0, 0.0), // 0: Bottom-left-back 16 | float3(1.0f, 0.0, 0.0), // 1: Bottom-right-back 17 | float3(1.0f, 1.0f, 0.0), // 2: Top-right-back 18 | float3(0.0, 1.0f, 0.0), // 3: Top-left-back 19 | float3(0.0, 0.0, 1.0f), // 4: Bottom-left-front 20 | float3(1.0f, 0.0, 1.0f), // 5: Bottom-right-front 21 | float3(1.0f, 1.0f, 1.0f), // 6: Top-right-front 22 | float3(0.0, 1.0f, 1.0f) // 7: Top-left-front 23 | }; 24 | 25 | // Define the indices to form triangles using the vertices (12 triangles, 6 faces) 26 | static const uint32_t indices[] = { 27 | // Front face 28 | 4, 5, 6, // Triangle 1 29 | 4, 6, 7, // Triangle 2 30 | 31 | // Back face 32 | 0, 1, 2, // Triangle 1 33 | 0, 2, 3, // Triangle 2 34 | 35 | // Left face 36 | 0, 3, 7, // Triangle 1 37 | 0, 7, 4, // Triangle 2 38 | 39 | // Right face 40 | 1, 5, 6, // Triangle 1 41 | 1, 6, 2, // Triangle 2 42 | 43 | // Top face 44 | 3, 2, 6, // Triangle 1 45 | 3, 6, 7, // Triangle 2 46 | 47 | // Bottom face 48 | 0, 1, 5, // Triangle 1 49 | 0, 5, 4 // Triangle 2 50 | }; 51 | 52 | struct VertexInput 53 | { 54 | uint instanceID : SV_InstanceID; 55 | uint vertexID : SV_VertexID; 56 | }; 57 | 58 | struct VertexOutput 59 | { 60 | float4 position : SV_POSITION; 61 | float3 positionRWS : POSITION_RWS; 62 | float3 color : COLOR; 63 | }; 64 | 65 | VertexOutput vert(VertexInput input) 66 | { 67 | // Initialize the output structure 68 | VertexOutput output; 69 | output = (VertexOutput)0; 70 | 71 | // Coordinates of this 72 | uint32_t x = input.instanceID % _GridResolution.x; 73 | uint32_t y = (input.instanceID / _GridResolution.x) % _GridResolution.y; 74 | uint32_t z = input.instanceID / _GridResolution.x / _GridResolution.y; 75 | 76 | // Read the density of this instance 77 | float density = grid_density(uint3(x, y, z)) * 0.05; 78 | 79 | // index of the vertex 80 | uint idx = indices[input.vertexID]; 81 | float3 v = vertices[idx] / (float3)_GridResolution + float3(x, y, z) / (float3)_GridResolution - float3(0.5, 0.5, 0.5); 82 | 83 | // Output for the next stage 84 | output.positionRWS = v / _GridScale - _CameraPosition; 85 | output.position = mul(_ViewProjectionMatrix, float4(output.positionRWS, 1.0)); 86 | output.color = density > 0.0 ? float3(1.0, 0.0, 0.0) : float3(0.5, 0.5, 0.5); 87 | return output; 88 | } 89 | 90 | struct GeometryInput 91 | { 92 | float4 positionCS : SV_POSITION; 93 | float3 positionRWS : POSITION_RWS; 94 | float3 color : COLOR; 95 | }; 96 | 97 | struct GeometryOutput 98 | { 99 | float4 positionCS : SV_POSITION; 100 | float3 positionRWS : POSITION_RWS; 101 | float3 color : COLOR; 102 | float3 dist : DISTANCE; 103 | }; 104 | 105 | [maxvertexcount(3)] 106 | void geom(triangle GeometryInput input[3], inout TriangleStream outStream) 107 | { 108 | // Compute the vectors and the area 109 | float2 p0 = clip_space_to_pixel(input[0].positionCS, _ScreenSize.xy); 110 | float2 p1 = clip_space_to_pixel(input[1].positionCS, _ScreenSize.xy); 111 | float2 p2 = clip_space_to_pixel(input[2].positionCS, _ScreenSize.xy); 112 | float2 v[3] = {p2 - p1, p2 - p0, p1 - p0}; 113 | float area = abs(v[1].x * v[2].y - v[1].y * v[2].x); 114 | 115 | // Emit the vertices 116 | GeometryOutput output = (GeometryOutput)0; 117 | for (uint vertID = 0; vertID < 3; ++vertID) 118 | { 119 | output.positionCS = input[vertID].positionCS; 120 | output.color = input[vertID].color; 121 | output.positionRWS = input[vertID].positionRWS; 122 | output.dist = 0.0; 123 | output.dist[vertID] = area * rsqrt(dot(v[vertID],v[vertID])); 124 | outStream.Append(output); 125 | } 126 | outStream.RestartStrip(); 127 | } 128 | 129 | struct PixelInput 130 | { 131 | float4 positionCS : SV_POSITION; 132 | float3 positionRWS : POSITION_RWS; 133 | float3 color : COLOR; 134 | float3 dist : DISTANCE; 135 | }; 136 | 137 | struct PixelOutput 138 | { 139 | float4 attachment0 : SV_Target0; 140 | }; 141 | 142 | float3 apply_wireframe(float3 color, float3 wireFrameColor, float wireframeSize, float3 dist) 143 | { 144 | if (wireframeSize > 0.0) 145 | { 146 | float3 d2 = dist * dist; 147 | float nearest = min(min(d2.x, d2.y), d2.z); 148 | float f = exp2(-nearest / wireframeSize); 149 | color = lerp(color, wireFrameColor, f); 150 | } 151 | return color; 152 | } 153 | 154 | PixelOutput frag(PixelInput input) 155 | { 156 | // Evaluate the distance to camera 157 | float distanceToCamera = length(input.positionRWS); 158 | 159 | // View vector 160 | float3 viewWS = -input.positionRWS / max(distanceToCamera, 0.00001); 161 | 162 | // Normal vector 163 | float3 normalWS = normalize(cross(ddx(input.positionRWS), ddy(input.positionRWS))); 164 | float3 roughness = 0.8; 165 | float3 diffuseColor = float3(1.0, 1.0, 1.0); 166 | 167 | // NdotV 168 | float NdotV = dot(viewWS, normalWS); 169 | 170 | // Evaluate the wireframe 171 | input.color = apply_wireframe(input.color * 0.01, float3(0.2, 0.2, 0.2), 0.5, input.dist); 172 | 173 | PixelOutput output; 174 | output.attachment0 = float4(input.color, 1.0); 175 | return output; 176 | } -------------------------------------------------------------------------------- /demo/src/graphics/dx12_backend_swap_chain.cpp: -------------------------------------------------------------------------------- 1 | #ifdef D3D12_SUPPORTED 2 | // Internal includes 3 | #include "graphics/dx12_backend.h" 4 | #include "graphics/dx12_containers.h" 5 | #include "graphics/dx12_helpers.h" 6 | #include "tools/string_utilities.h" 7 | #include "tools/security.h" 8 | 9 | namespace d3d12 10 | { 11 | // Function to create the swap chain 12 | IDXGISwapChain4* CreateSwapChain(HWND hWnd, ID3D12CommandQueue* commandQueue, uint32_t width, uint32_t height, uint32_t bufferCount, DXGI_FORMAT format) 13 | { 14 | // Grab the DXGI factory 2 15 | IDXGIFactory2* dxgiFactory2; 16 | UINT createFactoryFlags = 0; 17 | assert_msg(CreateDXGIFactory2(createFactoryFlags, IID_PPV_ARGS(&dxgiFactory2)) == S_OK, "DXGI Factory 2 request failed."); 18 | 19 | // Describe the swap chain 20 | DXGI_SWAP_CHAIN_DESC1 swapChainDesc = {}; 21 | swapChainDesc.Width = width; 22 | swapChainDesc.Height = height; 23 | swapChainDesc.Format = format; 24 | swapChainDesc.Stereo = FALSE; 25 | swapChainDesc.SampleDesc = { 1, 0 }; 26 | swapChainDesc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT; 27 | swapChainDesc.BufferCount = bufferCount; 28 | swapChainDesc.Scaling = DXGI_SCALING_NONE; 29 | swapChainDesc.SwapEffect = DXGI_SWAP_EFFECT_FLIP_DISCARD; 30 | swapChainDesc.AlphaMode = DXGI_ALPHA_MODE_UNSPECIFIED; 31 | swapChainDesc.Flags = 0; 32 | 33 | // Create the swapchain 34 | IDXGISwapChain1* swapChain1; 35 | HRESULT res = dxgiFactory2->CreateSwapChainForHwnd(commandQueue, hWnd, &swapChainDesc, nullptr, nullptr, &swapChain1); 36 | assert_msg(res == S_OK, "Create Swap Chain failed."); 37 | 38 | // Cnonvert to the Swap Chain 4 structure 39 | IDXGISwapChain4* dxgiSwapChain4 = (IDXGISwapChain4*)swapChain1; 40 | 41 | // Release the resources 42 | dxgiFactory2->Release(); 43 | 44 | // Return the swap chain 45 | return dxgiSwapChain4; 46 | } 47 | 48 | // Swap Chain API 49 | namespace swap_chain 50 | { 51 | // Creation and Destruction 52 | SwapChain create_swap_chain(RenderWindow renderWindow, GraphicsDevice graphicsDevice, CommandQueue commandQueue, TextureFormat format) 53 | { 54 | // Grab the actual structures 55 | DX12Window* dx12_window = (DX12Window*)renderWindow; 56 | DX12CommandQueue* dx12_commandQueue = (DX12CommandQueue*)commandQueue; 57 | DX12GraphicsDevice* deviceI = (DX12GraphicsDevice*)graphicsDevice; 58 | ID3D12Device1* device = deviceI->device; 59 | 60 | // Create the render environment internal structure 61 | DX12SwapChain* swapChainI = new DX12SwapChain(); 62 | 63 | // Get the dx12 format 64 | DXGI_FORMAT dxgi_format = format_to_dxgi_format(format); 65 | 66 | // Create the swap chain 67 | uint2 viewportSize; 68 | window::viewport_size(renderWindow, viewportSize); 69 | swapChainI->swapChain = CreateSwapChain(dx12_window->window, dx12_commandQueue->directSubQueue.queue, viewportSize.x, viewportSize.y, DX12_NUM_FRAMES, dxgi_format); 70 | 71 | // Grab the current back buffer 72 | swapChainI->currentBackBuffer = swapChainI->swapChain->GetCurrentBackBufferIndex(); 73 | 74 | // Create the descriptor heap for the swap chain 75 | swapChainI->descriptorHeap = create_descriptor_heap_internal(deviceI, DX12_NUM_FRAMES, (uint32_t)D3D12_DESCRIPTOR_HEAP_TYPE_RTV); 76 | 77 | // Start of the heap 78 | D3D12_CPU_DESCRIPTOR_HANDLE rtvHandle(swapChainI->descriptorHeap->GetCPUDescriptorHandleForHeapStart()); 79 | 80 | // Create a RTV for each frame. 81 | for (uint32_t n = 0; n < DX12_NUM_FRAMES; n++) 82 | { 83 | // Keep track of the descriptor heap where this is stored 84 | DX12RenderTexture& currentTexture = swapChainI->backBufferRenderTextures[n]; 85 | currentTexture.texture.width = viewportSize.x; 86 | currentTexture.texture.height = viewportSize.y; 87 | currentTexture.texture.depth = 1; 88 | currentTexture.texture.mipLevels = 1; 89 | currentTexture.texture.format = dxgi_format; 90 | currentTexture.texture.state = D3D12_RESOURCE_STATE_PRESENT; 91 | currentTexture.texture.type = TextureType::Tex2D; 92 | currentTexture.descriptorHeap = swapChainI->descriptorHeap; 93 | currentTexture.heapOffset = deviceI->descriptorSize[D3D12_DESCRIPTOR_HEAP_TYPE_RTV] * n; 94 | 95 | // Grab the buffer of the swap chain 96 | assert_msg(swapChainI->swapChain->GetBuffer(n, IID_PPV_ARGS(&swapChainI->backBufferRenderTextures[n].texture.resource)) == S_OK, "Failed to get the swap chain buffer."); 97 | 98 | // Create a render target view for it 99 | device->CreateRenderTargetView(swapChainI->backBufferRenderTextures[n].texture.resource, nullptr, rtvHandle); 100 | 101 | // Move on to the next pointer 102 | rtvHandle.ptr += (1 * deviceI->descriptorSize[D3D12_DESCRIPTOR_HEAP_TYPE_RTV]); 103 | } 104 | 105 | // Return the opaque structure 106 | return (SwapChain)swapChainI; 107 | } 108 | 109 | void destroy_swap_chain(SwapChain swapChain) 110 | { 111 | // Grab the actual structure 112 | DX12SwapChain* dx12_swapChain = (DX12SwapChain*)swapChain; 113 | 114 | // Release the render target views 115 | for (uint32_t n = 0; n < DX12_NUM_FRAMES; n++) 116 | dx12_swapChain->backBufferRenderTextures[n].texture.resource->Release(); 117 | 118 | // Release the DX12 structures 119 | dx12_swapChain->descriptorHeap->Release(); 120 | dx12_swapChain->swapChain->Release(); 121 | 122 | // Release the internal structure 123 | delete dx12_swapChain; 124 | } 125 | 126 | RenderTexture get_current_render_texture(SwapChain swapChain) 127 | { 128 | DX12SwapChain* dx12_swapChain = (DX12SwapChain*)swapChain; 129 | return (RenderTexture)(&dx12_swapChain->backBufferRenderTextures[dx12_swapChain->currentBackBuffer]); 130 | } 131 | 132 | void present(SwapChain swapChain) 133 | { 134 | // Convert to the internal structure 135 | DX12SwapChain* dx12_swapChain = (DX12SwapChain*)swapChain; 136 | 137 | // Present the frame buffer 138 | assert_msg(dx12_swapChain->swapChain->Present(0, 0) == S_OK, "Swap Chain Present failed."); 139 | 140 | // Update the current back buffer 141 | dx12_swapChain->currentBackBuffer = dx12_swapChain->swapChain->GetCurrentBackBufferIndex(); 142 | } 143 | } 144 | } 145 | #endif 146 | -------------------------------------------------------------------------------- /shaders/shader_lib/leb_utilities.hlsl: -------------------------------------------------------------------------------- 1 | #ifndef LEB_UTILITIES_HLSL 2 | #define LEB_UTILITIES_HLSL 3 | 4 | // Includes 5 | #include "shader_lib/intersection.hlsl" 6 | 7 | // Useful for debugging 8 | #define DIRECTION_USE_SM 9 | 10 | #if defined(LEB_COMPRESSED_NEIGHBORS) 11 | #define INVALID_NEIGHBOR 0xFFFFFF 12 | #define NEIGHBORS_TYPE uint3 13 | #else 14 | #define INVALID_NEIGHBOR 0xFFFFFFFF 15 | #define NEIGHBORS_TYPE uint4 16 | #endif 17 | 18 | // Tetra data 19 | struct TetraData 20 | { 21 | uint4 equations; 22 | NEIGHBORS_TYPE cmpNeighbors; 23 | float density; 24 | }; 25 | 26 | // SRVs 27 | StructuredBuffer _TetraDataBuffer0 : register(TETRA_BUFFER_0_BINDING_SLOT); 28 | StructuredBuffer _TetraDataBuffer1 : register(TETRA_BUFFER_1_BINDING_SLOT); 29 | 30 | #if defined(DIRECTION_BUFFER_BINDING_SLOT) 31 | StructuredBuffer _DirectionBuffer: register(DIRECTION_BUFFER_BINDING_SLOT); 32 | #endif 33 | 34 | // Ray directions 35 | groupshared float gs_DirectionsRAW[27]; 36 | 37 | // Array that tells us which vertices define the 4 triangles of the tetrahedron 38 | static const uint3 g_TriangleIndices[4] = {uint3(0, 1, 2), uint3(0, 1, 3), uint3(1, 3, 2), uint3(3, 0, 2)}; 39 | 40 | NEIGHBORS_TYPE compress_neighbors(uint4 neighbors) 41 | { 42 | #if defined(LEB_COMPRESSED_NEIGHBORS) 43 | uint3 cmpNeighbors; 44 | cmpNeighbors.x = neighbors.x & 0xFFFFFF; 45 | cmpNeighbors.x |= (neighbors.w & 0x000000FF) << 24; 46 | 47 | cmpNeighbors.y = neighbors.y & 0xFFFFFF; 48 | cmpNeighbors.y |= (neighbors.w & 0x0000FF00) << 16; 49 | 50 | cmpNeighbors.z = neighbors.z & 0xFFFFFF; 51 | cmpNeighbors.z |= (neighbors.w & 0x00FF0000) << 8; 52 | return cmpNeighbors; 53 | #else 54 | return neighbors; 55 | #endif 56 | } 57 | 58 | uint4 decompress_neighbors(NEIGHBORS_TYPE compressedNeighbors) 59 | { 60 | #if defined(LEB_COMPRESSED_NEIGHBORS) 61 | uint4 neighbors; 62 | neighbors.x = compressedNeighbors.x & 0xFFFFFF; 63 | neighbors.y = compressedNeighbors.y & 0xFFFFFF; 64 | neighbors.z = compressedNeighbors.z & 0xFFFFFF; 65 | neighbors.w = ((compressedNeighbors.x & 0xff000000) >> 24) | ((compressedNeighbors.y & 0xff000000) >> 16) | ((compressedNeighbors.z & 0xff000000) >> 8); 66 | return neighbors; 67 | #else 68 | return compressedNeighbors; 69 | #endif 70 | } 71 | 72 | uint compress_plane_equation(uint32_t planeIdx, float offsetToOrigin) 73 | { 74 | // Is it the second set of directions or the first? 75 | uint signV = planeIdx / 9; 76 | uint planeID = planeIdx % 9; 77 | 78 | // Compress the plane equation 79 | return (asuint(offsetToOrigin) & 0xFFFFFFE0) | (signV << 4) | planeID; 80 | } 81 | 82 | #if defined(DIRECTION_BUFFER_BINDING_SLOT) 83 | void load_direction_to_sm(uint groupIndex) 84 | { 85 | #if defined(DIRECTION_USE_SM) 86 | if (groupIndex < 27) 87 | gs_DirectionsRAW[groupIndex] = _DirectionBuffer[groupIndex]; 88 | GroupMemoryBarrierWithGroupSync(); 89 | #endif 90 | } 91 | #endif 92 | 93 | #if defined(DIRECTION_BUFFER_BINDING_SLOT) 94 | void decompress_plane_equation(uint cE, out float3 planeDir, out float offsetToOrigin) 95 | { 96 | uint normIdx = cE & 0xf; 97 | 98 | // Read it from shared memory 99 | uint dirOffset = 3 * normIdx; 100 | #if defined(DIRECTION_USE_SM) 101 | planeDir = float3(gs_DirectionsRAW[dirOffset], gs_DirectionsRAW[dirOffset + 1], gs_DirectionsRAW[dirOffset + 2]); 102 | #else 103 | planeDir = float3(_DirectionBuffer[dirOffset], _DirectionBuffer[dirOffset + 1], _DirectionBuffer[dirOffset + 2]); 104 | #endif 105 | // Inverse if required 106 | planeDir *= (cE & 0x10) ? -1.0 : 1.0; 107 | 108 | // offset to origin 109 | offsetToOrigin = asfloat(cE & 0xFFFFFFE0); 110 | } 111 | #endif 112 | 113 | float leb_density(in TetraData data) 114 | { 115 | return data.density * _DensityMultiplier; 116 | } 117 | 118 | TetraData get_tetra_data(uint32_t currentPrimitive) 119 | { 120 | // Read the data 121 | uint32_t page = currentPrimitive / 100663296; 122 | uint32_t cellIndex = currentPrimitive % 100663296; 123 | if (page == 0) 124 | return _TetraDataBuffer0[cellIndex]; 125 | else 126 | return _TetraDataBuffer1[cellIndex]; 127 | } 128 | 129 | #if defined(DIRECTION_BUFFER_BINDING_SLOT) 130 | float integrate_density(float3 rayOrigin, float3 rayDir, uint32_t currentPrimitive) 131 | { 132 | // Initialize our loop 133 | float totalDensity = 0.0; 134 | uint32_t prevPrimitive = INVALID_NEIGHBOR; 135 | 136 | // March our structure 137 | float prevL = 0.0; 138 | while (currentPrimitive != INVALID_NEIGHBOR) 139 | { 140 | // Read the additional data 141 | TetraData data = get_tetra_data(currentPrimitive); 142 | uint4 neighbors = decompress_neighbors(data.cmpNeighbors); 143 | 144 | // If we hiy the first face 145 | float l = FLT_MAX; 146 | 147 | // Process the faces 148 | uint32_t candidate = INVALID_NEIGHBOR; 149 | [unroll] 150 | for (uint32_t faceIdx = 0; faceIdx < 4; ++faceIdx) 151 | { 152 | if ((neighbors[faceIdx] == INVALID_NEIGHBOR || neighbors[faceIdx] != prevPrimitive)) 153 | { 154 | // Get the plane equation 155 | float3 planeDir; 156 | float offset; 157 | decompress_plane_equation(data.equations[faceIdx], planeDir, offset); 158 | 159 | // Intersect 160 | float t = ray_plane_intersection(rayOrigin, rayDir, planeDir, offset); 161 | if (t < l) 162 | { 163 | l = t; 164 | candidate = neighbors[faceIdx]; 165 | } 166 | } 167 | } 168 | 169 | // Current step 170 | float cl = l - prevL; 171 | 172 | // Update the primitive 173 | prevPrimitive = currentPrimitive; 174 | currentPrimitive = candidate; 175 | 176 | // Move along the ray 177 | totalDensity += max(cl, 0) * leb_density(data); 178 | 179 | // Move along the ray 180 | prevL += cl; 181 | } 182 | 183 | return totalDensity; 184 | } 185 | #endif 186 | 187 | #endif // LEB_UTILITIES_HLSL -------------------------------------------------------------------------------- /shaders/Frustum.graphics: -------------------------------------------------------------------------------- 1 | // Includes 2 | #include "shader_lib/common.hlsl" 3 | #define GLOBAL_CB_BINDING_SLOT b0 4 | #include "shader_lib/constant_buffers.hlsl" 5 | #include "shader_lib/intersection.hlsl" 6 | 7 | struct VertexInput 8 | { 9 | uint instanceID : SV_InstanceID; 10 | uint vertexID : SV_VertexID; 11 | }; 12 | 13 | struct VertexOutput 14 | { 15 | float4 positionCS : SV_POSITION; 16 | float3 positionWS : POSITION_WS; 17 | }; 18 | 19 | // Points of the frustum 20 | static const float3 pointsCS[] = { float3(1, 0.0, 1.0), float3(1, 1, 1.0), float3(0, 0, 0.0), 21 | float3(0.0, 0.0, 1.0), float3(0.0, 1, 1.0), float3(0.0, 1, 0.0), 22 | float3(1.0, 1.0, 1.0), float3(0.0, 1, 1.0), float3(0.0, 1, 0.0), 23 | float3(1.0, 0.0, 1.0), float3(0.0, 0, 1.0), float3(0.0, 1, 0.0), 24 | }; 25 | 26 | cbuffer _UpdateCB : register(b1) 27 | { 28 | // View projection matrix used for the update 29 | float4x4 _UpdateViewProjectionMatrix; 30 | 31 | // Inverse View projection matrix used for the update 32 | float4x4 _UpdateInvViewProjectionMatrix; 33 | 34 | // Camera position used for the update 35 | float3 _UpdateCameraPosition; 36 | // FOV 37 | float _UpdateFOV; 38 | 39 | // Volume min 40 | float3 _VolumeMinPosition; 41 | // Far plane 42 | float _UpdateFarPlaneDistance; 43 | 44 | // Volume max 45 | float3 _VolumeMaxPosition; 46 | // Padding 47 | float _PaddingUB0; 48 | }; 49 | 50 | VertexOutput vert(VertexInput input) 51 | { 52 | VertexOutput output; 53 | float3 pt = pointsCS[input.vertexID]; 54 | float3 positionRWS = float3(0.0, 0.0, 0.0); 55 | if (pt.z != 0.0) 56 | { 57 | // Evaluate the position 58 | positionRWS = evaluate_world_space_position(pt.xy, 1.0, _UpdateInvViewProjectionMatrix); 59 | 60 | // Normalize 61 | float distToCamera = length(positionRWS); 62 | positionRWS /= distToCamera; 63 | 64 | // Corner distance 65 | float aspectRatio = _ScreenSize.x / _ScreenSize.y; 66 | positionRWS *= (_UpdateFarPlaneDistance / cos(_UpdateFOV) / cos(_UpdateFOV * aspectRatio)); 67 | 68 | // Camera relative 69 | positionRWS += float3(_UpdateCameraPosition - _CameraPosition); 70 | } 71 | else 72 | { 73 | positionRWS = evaluate_world_space_position(float2(0.5, 0.5), 0.1, _UpdateInvViewProjectionMatrix); 74 | positionRWS += float3(_UpdateCameraPosition - _CameraPosition); 75 | } 76 | 77 | // Make it render camera relative 78 | output.positionCS = mul(_ViewProjectionMatrix, float4(positionRWS, 1.0)); 79 | output.positionWS = positionRWS + _CameraPosition; 80 | return output; 81 | } 82 | 83 | struct GeometryInput 84 | { 85 | float4 positionCS : SV_POSITION; 86 | float3 positionWS : POSITION_WS; 87 | }; 88 | 89 | struct GeometryOutput 90 | { 91 | float4 positionCS : SV_POSITION; 92 | float3 positionWS : POSITION_WS; 93 | nointerpolation float2 p0 : POSITION0; 94 | nointerpolation float2 p1 : POSITION1; 95 | nointerpolation float2 p2 : POSITION2; 96 | }; 97 | 98 | [maxvertexcount(3)] 99 | void geom(triangle GeometryInput input[3], inout TriangleStream outStream) 100 | { 101 | GeometryOutput output = (GeometryOutput)0; 102 | output.p0 = clip_space_to_pixel(input[0].positionCS, _ScreenSize.xy); 103 | output.p1 = clip_space_to_pixel(input[1].positionCS, _ScreenSize.xy); 104 | output.p2 = clip_space_to_pixel(input[2].positionCS, _ScreenSize.xy); 105 | // Emit the vertices 106 | for (uint vertID = 0; vertID < 3; ++vertID) 107 | { 108 | output.positionCS = input[vertID].positionCS; 109 | output.positionWS = input[vertID].positionWS; 110 | outStream.Append(output); 111 | } 112 | outStream.RestartStrip(); 113 | } 114 | 115 | struct PixelInput 116 | { 117 | float4 positionCS : SV_POSITION; 118 | float3 positionWS : POSITION_WS; 119 | nointerpolation float2 p0 : POSITION0; 120 | nointerpolation float2 p1 : POSITION1; 121 | nointerpolation float2 p2 : POSITION2; 122 | }; 123 | 124 | struct PixelOutput 125 | { 126 | float4 attachment0 : SV_Target0; 127 | }; 128 | 129 | void getLine(float2 B1, float2 B2, out float a, out float b, out float c) 130 | { 131 | a = B1.y - B2.y; 132 | b = B2.x - B1.x; 133 | c = B1.x * B2.y - B2.x * B1.y; 134 | } 135 | 136 | float dist(float2 p0, float2 p1, float2 p) 137 | { 138 | float a, b, c; 139 | getLine(p0, p1, a, b, c); 140 | return abs(a * p.x + b * p.y + c) / sqrt(a * a + b * b); 141 | } 142 | 143 | PixelOutput frag_above(PixelInput it) 144 | { 145 | // compute the wireframe 146 | PixelOutput output; 147 | float d0 = dist(it.p0, it.p1, it.positionCS.xy); 148 | float d1 = dist(it.p1, it.p2, it.positionCS.xy); 149 | float d2 = dist(it.p2, it.p0, it.positionCS.xy); 150 | float minD = min(min(d0, d1), d2); 151 | float f = (saturate(minD / 3.0)); 152 | 153 | // Intersect the ray leaving from the pixel and heading to the current camera position, if it intersects the volume, adjust 154 | float3 rayOrigin = it.positionWS; 155 | float3 rayDir = normalize(_CameraPosition - it.positionWS); 156 | float t = ray_box_intersection(rayOrigin, rayDir, _VolumeMinPosition, _VolumeMaxPosition); 157 | if (t > 0.0) 158 | discard; 159 | output.attachment0 = float4(float3(0.8, 0.2, 0.0), 1.0 - f * f); 160 | return output; 161 | } 162 | 163 | PixelOutput frag_under(PixelInput it) 164 | { 165 | // compute the wireframe 166 | PixelOutput output; 167 | float d0 = dist(it.p0, it.p1, it.positionCS.xy); 168 | float d1 = dist(it.p1, it.p2, it.positionCS.xy); 169 | float d2 = dist(it.p2, it.p0, it.positionCS.xy); 170 | float minD = min(min(d0, d1), d2); 171 | float f = (saturate(minD / 3.0)); 172 | 173 | // Intersect the ray leaving from the pixel and heading to the current camera position, if it intersects the volume, adjust 174 | float3 rayOrigin = it.positionWS; 175 | float3 rayDir = normalize(_CameraPosition - it.positionWS); 176 | float t = ray_box_intersection(rayOrigin, rayDir, _VolumeMinPosition, _VolumeMaxPosition); 177 | if (t <= 0.0) 178 | discard; 179 | output.attachment0 = float4(1.0 - float3(0.8, 0.2, 0.0), 1.0 - f * f); 180 | return output; 181 | } -------------------------------------------------------------------------------- /shaders/Grid/ForwardPT.compute: -------------------------------------------------------------------------------- 1 | #define WORKGROUP_RES 8 2 | #define WORKGROUP_SIZE WORKGROUP_RES * WORKGROUP_RES 3 | 4 | // CBVs 5 | #define GLOBAL_CB_BINDING_SLOT b0 6 | #define SKY_ATMOSPHERE_BUFFER_SLOT b1 7 | #define GRID_CB_BINDING_SLOT b2 8 | 9 | // SRVs 10 | #define DENSITY_BUFFER_0_BINDING_SLOT t0 11 | #define DENSITY_BUFFER_1_BINDING_SLOT t1 12 | #define TRANSMITTANCE_LUT_TEXTURE_SLOT t2 13 | #define MULTI_SCATTERING_LUT_TEXTURE_SLOT t3 14 | 15 | // Samplers 16 | SamplerState _sampler_linear_clamp : register(s0); 17 | 18 | // Includes 19 | #include "shader_lib/common.hlsl" 20 | #include "shader_lib/constant_buffers.hlsl" 21 | #include "shader_lib/grid_utilities.hlsl" 22 | #include "shader_lib/intersection.hlsl" 23 | #include "shader_lib/sky.hlsl" 24 | 25 | // UAVs 26 | RWTexture2D _ColorTexture: register(u0); 27 | 28 | float4 forward_pt(float3 rayOrigin, inout float3 rayDir, float3 sunDir, int3 currentCellCoords, uint32_t seed) 29 | { 30 | // Initialize our loop 31 | float4 inScatAtt = float4(0.0, 0.0, 0.0, 1.0); 32 | uint32_t sampleIdx = 0; 33 | 34 | // While we are looping 35 | uint32_t segementIdx = 0; 36 | while (segementIdx < NUM_MAX_SEGMENTS && valid_cell_coord(currentCellCoords)) 37 | { 38 | // Generate the mean ray distance undivided 39 | float maxRayDistanceUD = -log(1.0f - URng(seed)); 40 | 41 | // Generate the direction we will be exploring 42 | if (segementIdx != 0) 43 | rayDir = sample_sphere(float2(URng(seed), URng(seed))); 44 | 45 | int3 moveIdx = sign(rayDir); 46 | while (maxRayDistanceUD > 0.0 && valid_cell_coord(currentCellCoords)) 47 | { 48 | // Evaluate the density of the current cell 49 | float density = grid_density(currentCellCoords); 50 | 51 | // Compute the normalized grid positon 52 | float3 positionGS = (rayOrigin - _GridMinPosition) / (_GridMaxPosition - _GridMinPosition) * _GridResolution; 53 | 54 | // Compute the position in the cell 55 | float3 positionInCell = positionGS - currentCellCoords; 56 | 57 | if (moveIdx.x >= 0) 58 | positionInCell.x = 1.0 - positionInCell.x; 59 | 60 | if (moveIdx.y >= 0) 61 | positionInCell.y = 1.0 - positionInCell.y; 62 | 63 | if (moveIdx.z >= 0) 64 | positionInCell.z = 1.0 - positionInCell.z; 65 | 66 | // Project this position along each ray axis 67 | float3 projected = positionInCell / abs(rayDir) / _GridResolution; 68 | 69 | // Take the minimal dimension of the 3 70 | float t = 0.0; 71 | uint3 candidateCell = currentCellCoords; 72 | if (projected.x <= projected.y && projected.x <= projected.z) 73 | { 74 | t = projected.x; 75 | candidateCell.x += moveIdx.x; 76 | } 77 | else if (projected.y <= projected.x && projected.y <= projected.z) 78 | { 79 | t = projected.y; 80 | candidateCell.y += moveIdx.y; 81 | } 82 | else 83 | { 84 | t = projected.z; 85 | candidateCell.z += moveIdx.z; 86 | } 87 | 88 | // Two options here, either we've reached the end of the segment, or we continue to the next primitive 89 | float normalizedDistance = t * density; 90 | if (normalizedDistance < maxRayDistanceUD) 91 | { 92 | maxRayDistanceUD -= normalizedDistance; 93 | currentCellCoords = candidateCell; 94 | } 95 | else 96 | { 97 | t = maxRayDistanceUD / density; 98 | maxRayDistanceUD = 0.0; 99 | } 100 | 101 | // Move along the ray 102 | rayOrigin += t * rayDir; 103 | } 104 | 105 | // Post scattering event 106 | if (valid_cell_coord(currentCellCoords)) 107 | { 108 | // Alebdo of the particle 109 | inScatAtt.w *= _VolumeAlbedo; 110 | 111 | // Russian roulette 112 | float rr = URng(seed); 113 | if (rr > inScatAtt.w && valid_cell_coord(currentCellCoords)) 114 | { 115 | inScatAtt.w = 0.0; 116 | break; 117 | } 118 | 119 | // Next event estimation, add light contribution 120 | float density = integrate_density(rayOrigin, sunDir, currentCellCoords); 121 | inScatAtt.xyz += inScatAtt.w * exp(-density) * sun_color(); 122 | } 123 | 124 | segementIdx++; 125 | } 126 | 127 | // Done 128 | return inScatAtt; 129 | } 130 | 131 | [numthreads(WORKGROUP_RES, WORKGROUP_RES, 1)] 132 | void InsideVolumeIntegrator(uint2 threadID : SV_DispatchThreadID) 133 | { 134 | // Evaluate the cell index 135 | float3 rayOriginLS = _CameraPosition * _GridScale; 136 | float3 rayDirLS = transform_dir(evaluate_ray_direction(threadID.xy), _GridScale); 137 | float3 sunDirLS = _SunDirection * _GridScale; 138 | 139 | // Evaluate the density 140 | int3 cellCoords = evaluate_cell_coords(rayOriginLS); 141 | uint32_t seed = pixel_seed(threadID, _ScreenSize.xy, _FrameIndex); 142 | float4 inScatAtt = forward_pt(rayOriginLS, rayDirLS, sunDirLS, cellCoords, seed); 143 | 144 | // Combine and return the result 145 | float3 skyColor = sky_color(transform_dir_inv(rayDirLS, _GridScale)); 146 | float3 finalColor = inScatAtt.xyz + inScatAtt.w * skyColor; 147 | _ColorTexture[threadID] = float4(finalColor, 1.0); 148 | } 149 | 150 | [numthreads(WORKGROUP_RES, WORKGROUP_RES, 1)] 151 | void OutsideVolumeIntegrator(uint2 threadID : SV_DispatchThreadID) 152 | { 153 | // Evaluate the ray direction 154 | float3 rayOriginLS = _CameraPosition * _GridScale; 155 | float3 rayDirLS = transform_dir(evaluate_ray_direction(threadID.xy), _GridScale); 156 | float3 sunDirLS = _SunDirection * _GridScale; 157 | 158 | // First we intersect with the box 159 | float2 inters = intersect_ray_aabb(rayOriginLS, rayDirLS, _GridMinPosition, _GridMaxPosition); 160 | 161 | // Do we intersect the volume in front of us? 162 | if (inters.x > 0.0 && inters.x < inters.y) 163 | { 164 | // Compute the entry point intersection 165 | float3 initialPosition = clamp(rayOriginLS + rayDirLS * inters.x, _GridMinPosition, _GridMaxPosition); 166 | 167 | // Evaluate the cell index 168 | int3 cellCoords = evaluate_cell_coords(initialPosition); 169 | 170 | // Inscattering 171 | uint32_t seed = pixel_seed(threadID, _ScreenSize.xy, _FrameIndex); 172 | 173 | // Combine and return the result 174 | float4 inScatAtt = forward_pt(rayOriginLS + rayDirLS * inters.x, rayDirLS, sunDirLS, cellCoords, seed); 175 | float3 skyColor = sky_color(transform_dir_inv(rayDirLS, _GridScale)); 176 | _ColorTexture[threadID] = float4(inScatAtt.xyz + inScatAtt.w * skyColor, 1.0); 177 | } 178 | else 179 | { 180 | // Evaluate the sky color and return 181 | float3 skyColor = sky_color(transform_dir_inv(rayDirLS, _GridScale)); 182 | _ColorTexture[threadID] = float4(skyColor, 1.0); 183 | } 184 | } -------------------------------------------------------------------------------- /shaders/LEB/ForwardPT.compute: -------------------------------------------------------------------------------- 1 | #define WORKGROUP_RES 8 2 | #define WORKGROUP_SIZE WORKGROUP_RES * WORKGROUP_RES 3 | 4 | // CBVs 5 | #define GLOBAL_CB_BINDING_SLOT b0 6 | #define SKY_ATMOSPHERE_BUFFER_SLOT b1 7 | #define LEB_CB_BINDING_SLOT b2 8 | #define GRID_CB_BINDING_SLOT b3 9 | 10 | // SRVs 11 | #define TETRA_BUFFER_0_BINDING_SLOT t0 12 | #define TETRA_BUFFER_1_BINDING_SLOT t1 13 | #define DIRECTION_BUFFER_BINDING_SLOT t2 14 | #define TRANSMITTANCE_LUT_TEXTURE_SLOT t3 15 | #define MULTI_SCATTERING_LUT_TEXTURE_SLOT t4 16 | 17 | // Samplers 18 | SamplerState _sampler_linear_clamp : register(s0); 19 | 20 | // Includes 21 | #include "shader_lib/common.hlsl" 22 | #include "shader_lib/constant_buffers.hlsl" 23 | #include "shader_lib/intersection.hlsl" 24 | #include "shader_lib/leb_utilities.hlsl" 25 | #include "shader_lib/grid_utilities.hlsl" 26 | #include "shader_lib/sky.hlsl" 27 | 28 | // SRVs 29 | StructuredBuffer _PrimitiveBuffer: register(t5); 30 | StructuredBuffer _DistanceBuffer: register(t6); 31 | 32 | // UAVs 33 | RWTexture2D _ColorTexture: register(u0); 34 | 35 | 36 | // Integration function 37 | float4 forward_pt(float3 rayOrigin, inout float3 rayDir, float3 sunDir, uint32_t currentPrimitive, uint32_t seed) 38 | { 39 | // Initialize our loop 40 | float4 inScatAtt = float4(0.0f, 0.0, 0.0, 1.0f); 41 | uint32_t sampleIdx = 0; 42 | 43 | // While we are looping 44 | uint32_t segementIdx = 0; 45 | while (segementIdx < NUM_MAX_SEGMENTS && currentPrimitive != INVALID_NEIGHBOR) 46 | { 47 | // Generate the mean ray distance undivided 48 | float maxRayDistanceUD = -log(1.0f - URng(seed)); 49 | 50 | // Generate the direction we will be exploring 51 | if (segementIdx != 0) 52 | rayDir = sample_sphere(float2(URng(seed), URng(seed))); 53 | 54 | // Initialize our loop 55 | uint32_t prevPrimitive = INVALID_NEIGHBOR; 56 | 57 | // March our structure 58 | float prevL = 0.0; 59 | while (maxRayDistanceUD > 0.0 && currentPrimitive != INVALID_NEIGHBOR) 60 | { 61 | // Read the tetra data 62 | TetraData data = get_tetra_data(currentPrimitive); 63 | uint4 neighbors = decompress_neighbors(data.cmpNeighbors); 64 | 65 | // Max intersection 66 | float l = FLT_MAX; 67 | 68 | // Process the faces 69 | uint32_t candidate = INVALID_NEIGHBOR; 70 | 71 | // Define our exit interface 72 | [unroll] 73 | for (uint32_t faceIdx = 0; faceIdx < 4; ++faceIdx) 74 | { 75 | if ((neighbors[faceIdx] == INVALID_NEIGHBOR || neighbors[faceIdx] != prevPrimitive)) 76 | { 77 | // Get the plane equation 78 | float3 planeDir; 79 | float offset; 80 | decompress_plane_equation(data.equations[faceIdx], planeDir, offset); 81 | 82 | // Intersect 83 | float t = ray_plane_intersection(rayOrigin, rayDir, planeDir, offset); 84 | if (t < l) 85 | { 86 | l = t; 87 | candidate = neighbors[faceIdx]; 88 | } 89 | } 90 | } 91 | 92 | // Current step 93 | float cl = l - prevL; 94 | 95 | // Two options here, either we've reached the end of the segment, or we continue to the next primitive 96 | float density = leb_density(data); 97 | float normalizedDistance = cl * density; 98 | if (normalizedDistance < maxRayDistanceUD) 99 | { 100 | maxRayDistanceUD -= normalizedDistance; 101 | prevPrimitive = currentPrimitive; 102 | currentPrimitive = candidate; 103 | } 104 | else 105 | { 106 | // Adjust the step 107 | cl = maxRayDistanceUD / density; 108 | maxRayDistanceUD = 0.0; 109 | } 110 | 111 | // Move along the ray 112 | prevL += cl; 113 | } 114 | 115 | // Move the origin 116 | rayOrigin += prevL * rayDir; 117 | 118 | // Post scattering event 119 | if (currentPrimitive != INVALID_NEIGHBOR) 120 | { 121 | // Attenuation 122 | inScatAtt.w *= _VolumeAlbedo; 123 | 124 | // Russian roulette 125 | float rr = URng(seed); 126 | if (rr > inScatAtt.w && currentPrimitive != INVALID_NEIGHBOR) 127 | { 128 | inScatAtt.w = 0.0; 129 | break; 130 | } 131 | 132 | // Next event estimation 133 | float density = integrate_density(rayOrigin, sunDir, currentPrimitive); 134 | inScatAtt.xyz += inScatAtt.w * exp(-density) * sun_color(); 135 | } 136 | 137 | // New segment 138 | segementIdx++; 139 | } 140 | 141 | // Done 142 | return inScatAtt; 143 | } 144 | 145 | [numthreads(WORKGROUP_RES, WORKGROUP_RES, 1)] 146 | void InsideVolumeIntegrator(uint2 threadID : SV_DispatchThreadID, uint groupIndex : SV_GroupIndex) 147 | { 148 | // Load the directions to shared memory 149 | load_direction_to_sm(groupIndex); 150 | 151 | if (_InitialPrimitive == UINT32_MAX) 152 | { 153 | _ColorTexture[threadID] = float4(0.0, 0.0, 0.0, 1.0); 154 | return; 155 | } 156 | 157 | // Evaluate the density 158 | float3 rayOriginLS = _CameraPosition * _LEBScale; 159 | float3 rayDirLS = transform_dir(evaluate_ray_direction(threadID.xy), _LEBScale); 160 | float3 sunDirLS = _SunDirection * _LEBScale; 161 | 162 | // Integration 163 | uint32_t seed = pixel_seed(threadID, _ScreenSize.xy, _FrameIndex); 164 | float4 inScatAtt = forward_pt(rayOriginLS, rayDirLS, sunDirLS, _InitialPrimitive, seed); 165 | 166 | // Combine and return the result 167 | float3 skyColor = sky_color(transform_dir_inv(rayDirLS, _LEBScale)); 168 | float3 finalColor = inScatAtt.xyz + inScatAtt.w * skyColor; 169 | _ColorTexture[threadID] = float4(finalColor, 1.0); 170 | } 171 | 172 | [numthreads(WORKGROUP_RES, WORKGROUP_RES, 1)] 173 | void OutsideVolumeIntegrator(uint2 threadID : SV_DispatchThreadID, uint groupIndex : SV_GroupIndex) 174 | { 175 | // Load the directions to shared memory 176 | load_direction_to_sm(groupIndex); 177 | 178 | // Ray direction 179 | float3 rayOriginLS = _CameraPosition * _LEBScale; 180 | float3 rayDirLS = transform_dir(evaluate_ray_direction(threadID.xy), _LEBScale); 181 | float3 sunDirLS = _SunDirection * _LEBScale; 182 | 183 | // Any primitive to intersect? 184 | uint32_t primitiveIdx = threadID.x + _ScreenSize.x * threadID.y; 185 | if (_PrimitiveBuffer[primitiveIdx] == UINT32_MAX) 186 | { 187 | float3 skyColor = sky_color(transform_dir_inv(rayDirLS, _LEBScale)); 188 | _ColorTexture[threadID] = float4(skyColor, 1.0); 189 | return; 190 | } 191 | 192 | // Evaluate the seed of this pixel 193 | uint32_t seed = pixel_seed(threadID, _ScreenSize.xy, _FrameIndex); 194 | 195 | // Evalute the inscattering 196 | float4 inScatAtt = forward_pt(rayOriginLS + rayDirLS * _DistanceBuffer[primitiveIdx], rayDirLS, sunDirLS, _PrimitiveBuffer[primitiveIdx], seed); 197 | 198 | // Combine and return the result 199 | float3 skyColor = sky_color(transform_dir_inv(rayDirLS, _LEBScale)); 200 | float3 finalColor = inScatAtt.xyz + inScatAtt.w * skyColor; 201 | _ColorTexture[threadID] = float4(finalColor, 1.0); 202 | } -------------------------------------------------------------------------------- /demo/src/graphics/dx12_backend_command_queue.cpp: -------------------------------------------------------------------------------- 1 | #ifdef D3D12_SUPPORTED 2 | // Internal includes 3 | #include "graphics/dx12_backend.h" 4 | #include "graphics/dx12_containers.h" 5 | #include "graphics/dx12_helpers.h" 6 | #include "tools/string_utilities.h" 7 | #include "tools/security.h" 8 | 9 | namespace d3d12 10 | { 11 | // Function to create the command queue 12 | ID3D12CommandQueue* CreateCommandQueue(ID3D12Device2* device, D3D12_COMMAND_LIST_TYPE type, D3D12_COMMAND_QUEUE_PRIORITY priority) 13 | { 14 | D3D12_COMMAND_QUEUE_DESC desc = {}; 15 | desc.Type = type; 16 | desc.Priority = priority; 17 | desc.Flags = D3D12_COMMAND_QUEUE_FLAG_NONE; 18 | desc.NodeMask = 0; 19 | 20 | // Create the command queue and ensure it's been succesfully created 21 | ID3D12CommandQueue* d3d12CommandQueue; 22 | assert_msg(device->CreateCommandQueue(&desc, IID_PPV_ARGS(&d3d12CommandQueue)) == S_OK, "Command queue creation failed."); 23 | 24 | // Return the command queue 25 | return d3d12CommandQueue; 26 | } 27 | 28 | void create_sub_command_queue(DX12GraphicsDevice* dx12_device, DX12CommandSubQueue& subQueue, D3D12_COMMAND_LIST_TYPE type, CommandQueuePriority priority) 29 | { 30 | subQueue.priority = convert_command_queue_priority(priority); 31 | subQueue.queue = CreateCommandQueue(dx12_device->device, type, subQueue.priority); 32 | assert_msg(subQueue.queue != nullptr, "Failed to create the direct command queue."); 33 | assert_msg(subQueue.queue->GetTimestampFrequency(&subQueue.frequency) == S_OK, "Failed to get the GPU frequency."); 34 | assert_msg(dx12_device->device->CreateFence(0, D3D12_FENCE_FLAG_NONE, IID_PPV_ARGS(&subQueue.fence)) == S_OK, "Failed to create Fence"); 35 | subQueue.fenceEvent = CreateEvent(nullptr, FALSE, FALSE, nullptr); 36 | subQueue.fenceValue = 0; 37 | } 38 | 39 | void destroy_sub_command_queue(DX12CommandSubQueue& subQueue) 40 | { 41 | subQueue.queue->Release(); 42 | subQueue.fence->Release(); 43 | CloseHandle(subQueue.fenceEvent); 44 | } 45 | 46 | namespace command_queue 47 | { 48 | CommandQueue create_command_queue(GraphicsDevice graphicsDevice, CommandQueuePriority directPriority, CommandQueuePriority computePriority, CommandQueuePriority copyPriority) 49 | { 50 | // Cast the types 51 | DX12GraphicsDevice* dx12_device = (DX12GraphicsDevice*)graphicsDevice; 52 | 53 | // Create the host object 54 | DX12CommandQueue* dx12_commandQueue = new DX12CommandQueue(); 55 | dx12_commandQueue->deviceI = dx12_device; 56 | 57 | // Create the direct queue 58 | create_sub_command_queue(dx12_device, dx12_commandQueue->directSubQueue, D3D12_COMMAND_LIST_TYPE_DIRECT, directPriority); 59 | 60 | // Create the compute queue 61 | create_sub_command_queue(dx12_device, dx12_commandQueue->computeSubQueue, D3D12_COMMAND_LIST_TYPE_COMPUTE, computePriority); 62 | 63 | // Create the copy queue 64 | create_sub_command_queue(dx12_device, dx12_commandQueue->copySubQueue, D3D12_COMMAND_LIST_TYPE_COPY, copyPriority); 65 | 66 | // Opaque cast and return 67 | return (CommandQueue)dx12_commandQueue; 68 | } 69 | 70 | void destroy_command_queue(CommandQueue commandQueue) 71 | { 72 | DX12CommandQueue* dx12_commandQueue = (DX12CommandQueue*)commandQueue; 73 | destroy_sub_command_queue(dx12_commandQueue->directSubQueue); 74 | destroy_sub_command_queue(dx12_commandQueue->computeSubQueue); 75 | destroy_sub_command_queue(dx12_commandQueue->copySubQueue); 76 | delete dx12_commandQueue; 77 | } 78 | 79 | void execute_command_buffer(CommandQueue commandQueue, CommandBuffer commandBuffer, bool) 80 | { 81 | // Grab the internal structures 82 | DX12CommandBuffer* dx12_commandBuffer = (DX12CommandBuffer*)commandBuffer; 83 | DX12CommandQueue* dx12_commandQueue = (DX12CommandQueue*)commandQueue; 84 | 85 | ID3D12CommandList* const commandLists[] = { dx12_commandBuffer->cmdList()}; 86 | switch (dx12_commandBuffer->type) 87 | { 88 | case D3D12_COMMAND_LIST_TYPE_DIRECT: 89 | dx12_commandQueue->directSubQueue.queue->ExecuteCommandLists(1, commandLists); 90 | break; 91 | case D3D12_COMMAND_LIST_TYPE_COMPUTE: 92 | dx12_commandQueue->computeSubQueue.queue->ExecuteCommandLists(1, commandLists); 93 | break; 94 | case D3D12_COMMAND_LIST_TYPE_COPY: 95 | dx12_commandQueue->copySubQueue.queue->ExecuteCommandLists(1, commandLists); 96 | break; 97 | } 98 | } 99 | 100 | void signal_event_wait(DX12CommandSubQueue& subQueue) 101 | { 102 | subQueue.fenceValue++; 103 | subQueue.queue->Signal(subQueue.fence, subQueue.fenceValue); 104 | subQueue.fence->SetEventOnCompletion(subQueue.fenceValue, subQueue.fenceEvent); 105 | WaitForSingleObject(subQueue.fenceEvent, INFINITE); 106 | } 107 | 108 | void flush(CommandQueue commandQueue, CommandBufferType type) 109 | { 110 | DX12CommandQueue* dx12_commandQueue = (DX12CommandQueue*)commandQueue; 111 | 112 | switch (type) 113 | { 114 | case CommandBufferType::Default: 115 | signal_event_wait(dx12_commandQueue->directSubQueue); 116 | break; 117 | case CommandBufferType::Compute: 118 | signal_event_wait(dx12_commandQueue->computeSubQueue); 119 | break; 120 | case CommandBufferType::Copy: 121 | signal_event_wait(dx12_commandQueue->copySubQueue); 122 | break; 123 | } 124 | } 125 | 126 | void signal(CommandQueue commandQueue, Fence fence, uint64_t value, CommandBufferType type) 127 | { 128 | DX12CommandQueue* dx12_commandQueue = (DX12CommandQueue*)commandQueue; 129 | ID3D12Fence* dx12_fence = (ID3D12Fence*)fence; 130 | switch (type) 131 | { 132 | case CommandBufferType::Default: 133 | dx12_commandQueue->directSubQueue.queue->Signal(dx12_fence, value); 134 | break; 135 | case CommandBufferType::Compute: 136 | dx12_commandQueue->computeSubQueue.queue->Signal(dx12_fence, value); 137 | break; 138 | case CommandBufferType::Copy: 139 | dx12_commandQueue->copySubQueue.queue->Signal(dx12_fence, value); 140 | break; 141 | } 142 | } 143 | 144 | void wait(CommandQueue commandQueue, Fence fence, uint64_t value, CommandBufferType type) 145 | { 146 | DX12CommandQueue* dx12_commandQueue = (DX12CommandQueue*)commandQueue; 147 | ID3D12Fence* dx12_fence = (ID3D12Fence*)fence; 148 | switch (type) 149 | { 150 | case CommandBufferType::Default: 151 | dx12_commandQueue->directSubQueue.queue->Wait(dx12_fence, value); 152 | break; 153 | case CommandBufferType::Compute: 154 | dx12_commandQueue->computeSubQueue.queue->Wait(dx12_fence, value); 155 | break; 156 | case CommandBufferType::Copy: 157 | dx12_commandQueue->copySubQueue.queue->Wait(dx12_fence, value); 158 | break; 159 | } 160 | } 161 | } 162 | } 163 | #endif -------------------------------------------------------------------------------- /shaders/Sky/SkyPreCompute.compute: -------------------------------------------------------------------------------- 1 | // Initial includes 2 | #include "shader_lib/common.hlsl" 3 | 4 | // CBVs 5 | #define GLOBAL_CB_BINDING_SLOT CBV_SLOT(0) 6 | #define SKY_ATMOSPHERE_BUFFER_SLOT CBV_SLOT(1) 7 | 8 | // SRVs 9 | #define TRANSMITTANCE_LUT_TEXTURE_SLOT SRV_SLOT(0) 10 | #define MULTI_SCATTERING_LUT_TEXTURE_SLOT SRV_SLOT(1) 11 | // UAVs 12 | #define TRANSMITTANCE_LUT_RW_TEXTURE_SLOT UAV_SLOT(0) 13 | #define MULTI_SCATTERING_LUT_RW_TEXTURE_SLOT UAV_SLOT(1) 14 | 15 | // Samplers 16 | #define SKY_SAMPLER_BINDING_SLOT SPL_SLOT(0) 17 | 18 | // Other Includes 19 | #include "shader_lib/constant_buffers.hlsl" 20 | #define SKY_VIEW_EVAL 21 | #include "shader_lib/intersection.hlsl" 22 | #include "shader_lib/sky_utilities.hlsl" 23 | 24 | // UAVs 25 | RWTexture2D _TransmittanceLUTTextureRW: register(TRANSMITTANCE_LUT_RW_TEXTURE_SLOT); 26 | RWTexture2D _MultiScatteringLUTTextureRW: register(MULTI_SCATTERING_LUT_RW_TEXTURE_SLOT); 27 | 28 | // Samplers 29 | SamplerState _sampler_linear_clamp : register(SKY_SAMPLER_BINDING_SLOT); 30 | 31 | [numthreads(8, 8, 1)] 32 | void TransmittanceLUT(uint3 threadID : SV_DispatchThreadID) 33 | { 34 | // UV of this pixel 35 | float2 uv = (threadID.xy + float2(0.5, 0.5)) / float2(TRANSMITTANCE_TEXTURE_WIDTH, TRANSMITTANCE_TEXTURE_HEIGHT); 36 | 37 | // Compute camera position from LUT coords 38 | float viewHeight; 39 | float viewZenithCosAngle; 40 | UvToLutTransmittanceParams(uv, viewHeight, viewZenithCosAngle); 41 | 42 | // A few extra needed constants 43 | float3 WorldPos = float3(0.0f, 0.0f, viewHeight); 44 | float3 WorldDir = float3(0.0f, sqrt(1.0 - viewZenithCosAngle * viewZenithCosAngle), viewZenithCosAngle); 45 | 46 | // Evaluate the transmittance 47 | float3 transmittance = exp(-IntegrateOpticalDepth(threadID.xy, WorldPos, WorldDir)); 48 | 49 | // Export the transmittance 50 | _TransmittanceLUTTextureRW[threadID.xy] = float4(transmittance, 1.0f); 51 | } 52 | 53 | // Shared memory required for the evaluation 54 | groupshared float3 MultiScatAs1SharedMem[64]; 55 | groupshared float3 LSharedMem[64]; 56 | 57 | [numthreads(1, 1, 64)] 58 | void MultiScatteringLUT(uint3 threadID : SV_DispatchThreadID) 59 | { 60 | // Shift the position by half a pixel 61 | float2 pixPos = float2(threadID.xy) + float2(0.5f, 0.5); 62 | 63 | // UV of this pixel 64 | float2 uv = pixPos / MULTI_SCATTERING_TEXTURE_RESOLUTION; 65 | 66 | // Convert to units 67 | uv = float2(fromSubUvsToUnit(uv.x, MULTI_SCATTERING_TEXTURE_RESOLUTION), fromSubUvsToUnit(uv.y, MULTI_SCATTERING_TEXTURE_RESOLUTION)); 68 | 69 | // Evaluate the sun direction for this pixel 70 | float cosSunZenithAngle = uv.x * 2.0 - 1.0; 71 | float3 sunDir = float3(0.0, sqrt(saturate(1.0 - cosSunZenithAngle * cosSunZenithAngle)), cosSunZenithAngle); 72 | 73 | // We adjust again viewHeight according to PLANET_RADIUS_OFFSET to be in a valid range. 74 | float viewHeight = _BottomRadius + saturate(uv.y + PLANET_RADIUS_OFFSET) * (_TopRadius - _BottomRadius - PLANET_RADIUS_OFFSET); 75 | 76 | // Ray data 77 | float3 WorldPos = float3(0.0f, 0.0f, viewHeight); 78 | float3 WorldDir = float3(0.0f, 0.0f, 1.0f); 79 | 80 | // Reference. Since there are many sample, it requires MULTI_SCATTERING_POWER_SERIE to be true for accuracy and to avoid divergences (see declaration for explanations) 81 | const uint sqrt_sample_count = 8; 82 | const float sqrtSample = float(sqrt_sample_count); 83 | float i = 0.5f + float(threadID.z / sqrt_sample_count); 84 | float j = 0.5f + float(threadID.z - float((threadID.z / sqrt_sample_count) * sqrt_sample_count)); 85 | float randA = i / sqrtSample; 86 | float randB = j / sqrtSample; 87 | float theta = 2.0f * PI * randA; 88 | float phi = acos(1.0f - 2.0f * randB); // uniform distribution https://mathworld.wolfram.com/SpherePointPicking.html 89 | 90 | float cosPhi = cos(phi); 91 | float sinPhi = sin(phi); 92 | float cosTheta = cos(theta); 93 | float sinTheta = sin(theta); 94 | WorldDir.x = cosTheta * sinPhi; 95 | WorldDir.y = sinTheta * sinPhi; 96 | WorldDir.z = cosPhi; 97 | 98 | // Evaluate the luminance and the multi scattering 99 | float3 luminance; 100 | float3 multiScat; 101 | IntegrateLuminanceMultiScatt(pixPos, WorldPos, WorldDir, sunDir, _TransmittanceLUTTexture, _sampler_linear_clamp, luminance, multiScat); 102 | 103 | // Store into the shared memory 104 | const float SphereSolidAngle = 4.0 * PI; 105 | MultiScatAs1SharedMem[threadID.z] = multiScat * SphereSolidAngle / (sqrtSample * sqrtSample); 106 | LSharedMem[threadID.z] = luminance * SphereSolidAngle / (sqrtSample * sqrtSample); 107 | 108 | GroupMemoryBarrierWithGroupSync(); 109 | 110 | // 64 to 32 111 | if (threadID.z < 32) 112 | { 113 | MultiScatAs1SharedMem[threadID.z] += MultiScatAs1SharedMem[threadID.z + 32]; 114 | LSharedMem[threadID.z] += LSharedMem[threadID.z + 32]; 115 | } 116 | GroupMemoryBarrierWithGroupSync(); 117 | 118 | // 32 to 16 119 | if (threadID.z < 16) 120 | { 121 | MultiScatAs1SharedMem[threadID.z] += MultiScatAs1SharedMem[threadID.z + 16]; 122 | LSharedMem[threadID.z] += LSharedMem[threadID.z + 16]; 123 | } 124 | GroupMemoryBarrierWithGroupSync(); 125 | 126 | // 16 to 8 (16 is thread group min hardware size with intel, no sync required from there) 127 | if (threadID.z < 8) 128 | { 129 | MultiScatAs1SharedMem[threadID.z] += MultiScatAs1SharedMem[threadID.z + 8]; 130 | LSharedMem[threadID.z] += LSharedMem[threadID.z + 8]; 131 | } 132 | GroupMemoryBarrierWithGroupSync(); 133 | if (threadID.z < 4) 134 | { 135 | MultiScatAs1SharedMem[threadID.z] += MultiScatAs1SharedMem[threadID.z + 4]; 136 | LSharedMem[threadID.z] += LSharedMem[threadID.z + 4]; 137 | } 138 | GroupMemoryBarrierWithGroupSync(); 139 | if (threadID.z < 2) 140 | { 141 | MultiScatAs1SharedMem[threadID.z] += MultiScatAs1SharedMem[threadID.z + 2]; 142 | LSharedMem[threadID.z] += LSharedMem[threadID.z + 2]; 143 | } 144 | GroupMemoryBarrierWithGroupSync(); 145 | if (threadID.z < 1) 146 | { 147 | MultiScatAs1SharedMem[threadID.z] += MultiScatAs1SharedMem[threadID.z + 1]; 148 | LSharedMem[threadID.z] += LSharedMem[threadID.z + 1]; 149 | } 150 | GroupMemoryBarrierWithGroupSync(); 151 | if (threadID.z > 0) 152 | return; 153 | 154 | const float IsotropicPhase = 1.0 / SphereSolidAngle; 155 | float3 MultiScatAs1 = MultiScatAs1SharedMem[0] * IsotropicPhase; // Equation 7 f_ms 156 | float3 InScatteredLuminance = LSharedMem[0] * IsotropicPhase; // Equation 5 L_2ndOrder 157 | 158 | // MultiScatAs1 represents the amount of luminance scattered as if the integral of scattered luminance over the sphere would be 1. 159 | // - 1st order of scattering: one can ray-march a straight path as usual over the sphere. That is InScatteredLuminance. 160 | // - 2nd order of scattering: the inscattered luminance is InScatteredLuminance at each of samples of fist order integration. Assuming a uniform phase function that is represented by MultiScatAs1, 161 | // - 3nd order of scattering: the inscattered luminance is (InScatteredLuminance * MultiScatAs1 * MultiScatAs1) 162 | // - etc. 163 | // For a serie, sum_{n=0}^{n=+inf} = 1 + r + r^2 + r^3 + ... + r^n = 1 / (1.0 - r), see https://en.wikipedia.org/wiki/Geometric_series 164 | const float3 r = MultiScatAs1; 165 | const float3 SumOfAllMultiScatteringEventsContribution = 1.0f / (1.0 - r); 166 | float3 L = InScatteredLuminance * SumOfAllMultiScatteringEventsContribution;// Equation 10 Psi_ms 167 | 168 | // Write to the output texture 169 | _MultiScatteringLUTTextureRW[threadID.xy] = float4(MULTIPLE_SCATTERING_FACTOR * L, 1.0f); 170 | } -------------------------------------------------------------------------------- /demo/include/volume/leb_3d_eval.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | // Internal includes 4 | #include "math/types.h" 5 | #include "math/operators.h" 6 | 7 | // External includes 8 | #include 9 | #include 10 | #include 11 | 12 | // LEB cache size 13 | #define LEB_CACHE_SIZE 9 14 | 15 | static uint32_t leb__FindMSB(uint64_t x) 16 | { 17 | uint32_t depth = 0; 18 | 19 | while (x > 1u) { 20 | ++depth; 21 | x>>= 1u; 22 | } 23 | 24 | return depth; 25 | } 26 | 27 | static uint64_t leb__GetBitValue(const uint64_t bitField, int64_t bitID) 28 | { 29 | return ((bitField >> bitID) & 1u); 30 | } 31 | 32 | static void leb__IdentityMatrix4x4(float4x4& m) 33 | { 34 | m = { 1.0, 0.0, 0.0, 0.0, 35 | 0.0, 1.0, 0.0, 0.0, 36 | 0.0, 0.0, 1.0, 0.0, 37 | 0.0, 0.0, 0.0, 1.0 }; 38 | } 39 | 40 | 41 | /******************************************************************************* 42 | * TransposeMatrix4x4 -- Transposes a 4x4 matrix 43 | * 44 | */ 45 | static void leb__TransposeMatrix4x4(const float4x4& m, float4x4& out) 46 | { 47 | for (int64_t i = 0; i < 4; ++i) 48 | for (int64_t j = 0; j < 4; ++j) 49 | out.rc[i][j] = m.rc[j][i]; 50 | } 51 | 52 | 53 | /******************************************************************************* 54 | * DotProduct -- Returns the dot product of two vectors 55 | * 56 | */ 57 | static float leb__DotProduct(int64_t argSize, const float *x, const float *y) 58 | { 59 | float dp = 0.0f; 60 | 61 | for (int64_t i = 0; i < argSize; ++i) 62 | dp+= x[i] * y[i]; 63 | 64 | return dp; 65 | } 66 | 67 | 68 | /******************************************************************************* 69 | * MulMatrix4x4 -- Computes the product of two 4x4 matrices 70 | * 71 | */ 72 | static void 73 | leb__Matrix4x4Product( 74 | const float4x4& m1, 75 | const float4x4& m2, 76 | float4x4& out 77 | ) { 78 | float4x4 tra; 79 | leb__TransposeMatrix4x4(m2, tra); 80 | 81 | for (int64_t j = 0; j < 4; ++j) 82 | for (int64_t i = 0; i < 4; ++i) 83 | out.rc[j][i] = leb__DotProduct(4, m1.rc[j], tra.rc[i]); 84 | } 85 | 86 | 87 | /******************************************************************************* 88 | * SplittingMatrix -- Computes a LEB splitting matrix from a split bit 89 | * 90 | */ 91 | static void 92 | leb__SplittingMatrix(float4x4& matrix, uint64_t bitValue, uint8_t type) 93 | { 94 | float b = (float)bitValue; 95 | float c = 1.0f - b; 96 | 97 | if (type == 0) 98 | { 99 | const float4x4 splitMatrix = { 100 | 0.0f, 1.0f, 0.0f, 0.0f, 101 | 0.0f, 0.0f, 0.5f, 0.5f, 102 | b, 0.0f, c, 0.0, 103 | c, 0.0f, 0.0f, b 104 | }; 105 | float4x4 tmp; 106 | memcpy(tmp.rc, matrix.rc, sizeof(tmp)); 107 | leb__Matrix4x4Product(splitMatrix, tmp, matrix); 108 | } 109 | if (type == 3) 110 | { 111 | const float4x4 splitMatrix = { 112 | 0.0f, 1.0f, 0.0f, 0.0f, 113 | 0.0f, 0.0f, 0.5f, 0.5f, 114 | b, 0.0f, c, 0.0, 115 | c, 0.0f, 0.0f, b 116 | }; 117 | float4x4 tmp; 118 | memcpy(tmp.rc, matrix.rc, sizeof(tmp)); 119 | leb__Matrix4x4Product(splitMatrix, tmp, matrix); 120 | } 121 | else if (type == 1) 122 | { 123 | const float4x4 splitMatrix = { 124 | b, c, 0.0f, 0.0f, 125 | 0.0f, 0.0f, 0.5f, 0.5f, 126 | 0.0f, 0.0f, c, b, 127 | c, b, 0.0f, 0.0f 128 | }; 129 | 130 | float4x4 tmp; 131 | memcpy(tmp.rc, matrix.rc, sizeof(tmp)); 132 | leb__Matrix4x4Product(splitMatrix, tmp, matrix); 133 | } 134 | else if (type == 2) 135 | { 136 | const float4x4 splitMatrix = { 137 | c, b, 0.0f, 0.0f, 138 | 0.0f, 0.0f, 0.5f, 0.5f, 139 | b, c, 0.0f, 0.0f, 140 | 0.0f, 0.0f, c, b 141 | }; 142 | 143 | float4x4 tmp; 144 | memcpy(tmp.rc, matrix.rc, sizeof(tmp)); 145 | leb__Matrix4x4Product(splitMatrix, tmp, matrix); 146 | } 147 | } 148 | 149 | /******************************************************************************* 150 | * DecodeTransformationMatrix -- Computes the matrix associated to a LEB 151 | * node 152 | * 153 | */ 154 | static void 155 | leb__DecodeTransformationMatrix( 156 | uint64_t heapID, 157 | uint8_t currentType, 158 | float4x4& matrix 159 | ) { 160 | uint64_t depth = leb__FindMSB(heapID); 161 | leb__IdentityMatrix4x4(matrix); 162 | 163 | for (int64_t bitID = depth - 1; bitID >= 0; --bitID) { 164 | uint64_t bitValue = leb__GetBitValue(heapID, bitID); 165 | leb__SplittingMatrix(matrix, leb__GetBitValue(heapID, bitID), currentType); 166 | switch (currentType) 167 | { 168 | case 0: 169 | currentType = bitValue == 0 ? 1 : 2; 170 | break; 171 | case 1: 172 | case 2: 173 | currentType = 3; 174 | break; 175 | case 3: 176 | currentType = 0; 177 | break; 178 | } 179 | } 180 | } 181 | 182 | static void leb__DecodeTransformationMatrix(uint64_t heapID, uint8_t, float4x4& matrix, const float4x4* matrixCache) 183 | { 184 | leb__IdentityMatrix4x4(matrix); 185 | const uint64_t msb = (1ULL << LEB_CACHE_SIZE); 186 | const uint64_t mask = ~(~0ULL << LEB_CACHE_SIZE); 187 | uint64_t depth = leb__FindMSB(heapID); 188 | uint32_t remainder = depth % LEB_CACHE_SIZE; 189 | 190 | // Align on the power 191 | if (remainder != 0) 192 | { 193 | uint32_t index = (heapID & ((1ULL << remainder) - 1)) | (1ULL << remainder); 194 | float4x4 tmp; 195 | memcpy(tmp.rc, matrix.rc, sizeof(tmp)); 196 | leb__Matrix4x4Product(tmp, matrixCache[index], matrix); 197 | heapID >>= remainder; 198 | } 199 | 200 | while (heapID > mask) 201 | { 202 | uint32_t index = uint32_t((heapID & mask) | msb); 203 | float4x4 tmp; 204 | memcpy(tmp.rc, matrix.rc, sizeof(tmp)); 205 | leb__Matrix4x4Product(tmp, matrixCache[index], matrix); 206 | heapID >>= LEB_CACHE_SIZE; 207 | } 208 | } 209 | 210 | /******************************************************************************* 211 | * DecodeNodeAttributeArray -- Compute the triangle attributes at the input node 212 | * 213 | */ 214 | inline void leb_DecodeNodeAttributeArray( 215 | uint64_t heapID, 216 | uint8_t baseType, 217 | const float4x4* cache, 218 | float4 attributeArray[3] 219 | ) { 220 | float4x4 m; 221 | float attributeVector[4]; 222 | 223 | // Evaluate the global matrix 224 | leb__DecodeTransformationMatrix(heapID, baseType, m, cache); 225 | 226 | // Apply it to each dimension 227 | for (int64_t i = 0; i < 3; ++i) 228 | { 229 | memcpy(attributeVector, &attributeArray[i].x, sizeof(attributeVector)); 230 | attributeArray[i].x = leb__DotProduct(4, m.rc[0], attributeVector); 231 | attributeArray[i].y = leb__DotProduct(4, m.rc[1], attributeVector); 232 | attributeArray[i].z = leb__DotProduct(4, m.rc[2], attributeVector); 233 | attributeArray[i].w = leb__DotProduct(4, m.rc[3], attributeVector); 234 | } 235 | } 236 | 237 | inline void leb_DecodeNodeAttributeArray( 238 | uint64_t heapID, 239 | uint8_t baseType, 240 | float4 attributeArray[3] 241 | ) { 242 | float4x4 m; 243 | float attributeVector[4]; 244 | 245 | // Evaluate the global matrix 246 | leb__DecodeTransformationMatrix(heapID, baseType, m); 247 | 248 | // Apply it to each dimension 249 | for (int64_t i = 0; i < 3; ++i) 250 | { 251 | memcpy(attributeVector, &attributeArray[i].x, sizeof(attributeVector)); 252 | attributeArray[i].x = leb__DotProduct(4, m.rc[0], attributeVector); 253 | attributeArray[i].y = leb__DotProduct(4, m.rc[1], attributeVector); 254 | attributeArray[i].z = leb__DotProduct(4, m.rc[2], attributeVector); 255 | attributeArray[i].w = leb__DotProduct(4, m.rc[3], attributeVector); 256 | } 257 | } -------------------------------------------------------------------------------- /demo/src/graphics/dx12_backend_compute_shader.cpp: -------------------------------------------------------------------------------- 1 | #ifdef D3D12_SUPPORTED 2 | // Internal includes 3 | #include "graphics/dx12_backend.h" 4 | #include "graphics/dx12_containers.h" 5 | #include "graphics/dx12_helpers.h" 6 | #include "tools/string_utilities.h" 7 | #include "tools/security.h" 8 | 9 | // System includes 10 | #include 11 | #include 12 | #include 13 | 14 | namespace d3d12 15 | { 16 | IDxcBlob* LoadDXILShader(const std::wstring& filename) 17 | { 18 | IDxcUtils* dxcUtils; 19 | DxcCreateInstance(CLSID_DxcUtils, IID_PPV_ARGS(&dxcUtils)); 20 | 21 | IDxcBlobEncoding* blobEncoding; 22 | dxcUtils->LoadFile(filename.c_str(), nullptr, &blobEncoding); 23 | 24 | dxcUtils->Release(); 25 | return blobEncoding; 26 | } 27 | namespace compute_shader 28 | { 29 | ComputeShader create_compute_shader(GraphicsDevice graphicsDevice, const ComputeShaderDescriptor& csd, bool experimental) 30 | { 31 | // Convert the strings to wide 32 | const std::wstring& filename = convert_to_wide(csd.filename.c_str(), (uint32_t)csd.filename.size()); 33 | const std::wstring& kernelName = convert_to_wide(csd.kernelname.c_str(), (uint32_t)csd.kernelname.size()); 34 | 35 | // Convert the device 36 | DX12GraphicsDevice* deviceI = (DX12GraphicsDevice*)graphicsDevice; 37 | ID3D12Device2* device = deviceI->device; 38 | 39 | // Create our internal structure 40 | DX12ComputeShader* cS = new DX12ComputeShader(); 41 | 42 | D3D12_COMPUTE_PIPELINE_STATE_DESC pso_desc = {}; 43 | if (csd.codeType == ShaderCodeType::Code) 44 | { 45 | // Create the library and the compiler 46 | IDxcLibrary* library; 47 | DxcCreateInstance(CLSID_DxcLibrary, IID_PPV_ARGS(&library)); 48 | 49 | // Load the file into a blob 50 | uint32_t code_page = CP_UTF8; 51 | IDxcBlobEncoding* source_blob; 52 | assert_msg(library->CreateBlobFromFile(filename.c_str(), &code_page, &source_blob) == S_OK, "Failed to load the shader code."); 53 | 54 | // Compilation arguments 55 | std::vector includeDirs; 56 | for (int includeDirIdx = 0; includeDirIdx < csd.includeDirectories.size(); ++includeDirIdx) 57 | { 58 | std::wstring includeArg = L"-I "; 59 | includeArg += convert_to_wide(csd.includeDirectories[includeDirIdx]); 60 | includeDirs.push_back(includeArg.c_str()); 61 | } 62 | 63 | std::vector arguments; 64 | arguments.push_back(L"-HV 2021"); 65 | if (!csd.debugFlag) 66 | { 67 | arguments.push_back(L"-O3"); 68 | } 69 | else 70 | { 71 | arguments.push_back(L"-Od"); 72 | arguments.push_back(L"-Fd"); 73 | arguments.push_back(L"-Qembed_debug"); 74 | arguments.push_back(L"-Zi"); 75 | } 76 | 77 | // Enable 16bit types if available 78 | if (deviceI->support16bitShaderOps) 79 | arguments.push_back(L"-enable-16bit-types"); 80 | 81 | // Handle the defines 82 | std::vector definesArray(csd.defines.size()); 83 | std::vector definesWSTR(csd.defines.size()); 84 | for (int defIdx = 0; defIdx < csd.defines.size(); ++defIdx) 85 | { 86 | // Convert and keep it for memory management issues 87 | definesWSTR[defIdx] = convert_to_wide(csd.defines[defIdx]); 88 | 89 | // Add the define 90 | DxcDefine def; 91 | def.Name = definesWSTR[defIdx].c_str(); 92 | def.Value = L""; 93 | definesArray[defIdx] = def; 94 | } 95 | 96 | // Mark the double unsupported if they are 97 | if (!deviceI->supportDoubleShaderOps) 98 | { 99 | DxcDefine def; 100 | def.Name = L"FP64_UNSUPPORTED"; 101 | def.Value = L"1"; 102 | definesArray.push_back(def); 103 | } 104 | 105 | if (deviceI->vendor == GPUVendor::Intel) 106 | { 107 | DxcDefine def; 108 | def.Name = L"UNSUPPORTED_FIRST_BIT_HIGH"; 109 | def.Value = L"1"; 110 | definesArray.push_back(def); 111 | } 112 | 113 | // Handle the include dirs 114 | for (int includeDirIdx = 0; includeDirIdx < csd.includeDirectories.size(); ++includeDirIdx) 115 | arguments.push_back(includeDirs[includeDirIdx].c_str()); 116 | 117 | // Create the compiler 118 | IDxcCompiler* compiler; 119 | DxcCreateInstance(CLSID_DxcCompiler, IID_PPV_ARGS(&compiler)); 120 | 121 | // Create an include handler 122 | IDxcIncludeHandler* includeHandler; 123 | library->CreateIncludeHandler(&includeHandler); 124 | 125 | // Compile the shader 126 | IDxcOperationResult* result; 127 | HRESULT hr = compiler->Compile(source_blob, filename.c_str(), kernelName.c_str(), experimental ? L"cs_6_9" : L"cs_6_6", arguments.data(), (uint32_t)arguments.size(), definesArray.data(), (uint32_t)definesArray.size(), includeHandler, &result); 128 | 129 | if (SUCCEEDED(hr)) 130 | result->GetStatus(&hr); 131 | bool compile_succeed = SUCCEEDED(hr); 132 | 133 | // If the compilation failed, print the error 134 | IDxcBlobEncoding* error_blob; 135 | if (SUCCEEDED(result->GetErrorBuffer(&error_blob)) && error_blob) 136 | { 137 | // Log the compilation message 138 | if (error_blob->GetBufferSize() != 0) 139 | printf("[SHADER COMPILATION] %s, %s\n", csd.kernelname.c_str(), (const char*)error_blob->GetBufferPointer()); 140 | 141 | // Release the error blob 142 | error_blob->Release(); 143 | } 144 | 145 | // Release the library 146 | library->Release(); 147 | 148 | // If succeeded, grab the right pointer 149 | IDxcBlob* shader_blob = 0; 150 | if (compile_succeed) 151 | result->GetResult(&shader_blob); 152 | 153 | // Release all the intermediate resources 154 | result->Release(); 155 | source_blob->Release(); 156 | compiler->Release(); 157 | 158 | // If we were not able to compile, leave. 159 | if (shader_blob == nullptr) 160 | return 0; 161 | 162 | // Keep track of the blob 163 | cS->shaderBlob = shader_blob; 164 | 165 | // Create the pipeline state object for the shader 166 | pso_desc.CS.BytecodeLength = shader_blob->GetBufferSize(); 167 | pso_desc.CS.pShaderBytecode = shader_blob->GetBufferPointer(); 168 | } 169 | else 170 | { 171 | IDxcBlob* blob = LoadDXILShader(filename); 172 | pso_desc.CS.BytecodeLength = blob->GetBufferSize(); 173 | pso_desc.CS.pShaderBytecode = blob->GetBufferPointer(); 174 | cS->shaderBlob = blob; 175 | } 176 | 177 | // Do the reflection 178 | uint32_t cbvCount = 0, srvCount = 0, uavCount = 0, samplerCount = 0; 179 | query_bindings(cS->shaderBlob, cbvCount, srvCount, uavCount, samplerCount, cS->bindings); 180 | 181 | // Build the root signature 182 | cS->rootSignature = create_root_signature(deviceI, srvCount, uavCount, cbvCount, samplerCount); 183 | pso_desc.pRootSignature = cS->rootSignature->rootSignature; 184 | 185 | // Create the pipeline state object 186 | ID3D12PipelineState* pso; 187 | HRESULT hr = device->CreateComputePipelineState(&pso_desc, IID_PPV_ARGS(&pso)); 188 | assert_msg(hr == S_OK, "Failed to create pipeline state object."); 189 | 190 | // Fill the compute shader structure 191 | cS->device = deviceI; 192 | cS->pipelineStateObject = pso; 193 | cS->cbvCount = cbvCount; 194 | cS->srvCount = srvCount; 195 | cS->uavCount = uavCount; 196 | cS->samplerCount = samplerCount; 197 | 198 | // Create the descriptor heap for this compute shader 199 | cS->CSUHeaps.push_back(create_descriptor_heap_suc(deviceI, srvCount, uavCount, cbvCount)); 200 | cS->samplerHeaps.push_back(create_descriptor_heap_sampler(deviceI, max(samplerCount, 1))); 201 | cS->cmdBatchIndex = UINT32_MAX; 202 | cS->nextUsableHeap = 0; 203 | 204 | // Create the command signature and append it 205 | D3D12_INDIRECT_ARGUMENT_DESC argumentDescs[1]; 206 | argumentDescs[0].Type = D3D12_INDIRECT_ARGUMENT_TYPE_DISPATCH; 207 | D3D12_COMMAND_SIGNATURE_DESC commandSignatureDesc = {}; 208 | commandSignatureDesc.pArgumentDescs = argumentDescs; 209 | commandSignatureDesc.NumArgumentDescs = 1; 210 | commandSignatureDesc.ByteStride = sizeof(D3D12_DISPATCH_ARGUMENTS); 211 | assert(deviceI->device->CreateCommandSignature(&commandSignatureDesc, nullptr, IID_PPV_ARGS(&cS->commandSignature)) == S_OK); 212 | 213 | // Resource counting 214 | deviceI->allocatedCS++; 215 | 216 | // Convert to the opaque structure 217 | return (ComputeShader)cS; 218 | } 219 | 220 | void destroy_compute_shader(ComputeShader computeShader) 221 | { 222 | // Grab the internal structure 223 | DX12ComputeShader* dx12_computeShader = (DX12ComputeShader*)computeShader; 224 | 225 | // Destroy all the descriptor heaps 226 | uint32_t numDescriptorHeaps = (uint32_t)dx12_computeShader->CSUHeaps.size(); 227 | for (uint32_t heapIdx = 0; heapIdx < numDescriptorHeaps; ++heapIdx) 228 | { 229 | destroy_descriptor_heap(dx12_computeShader->CSUHeaps[heapIdx]); 230 | destroy_descriptor_heap(dx12_computeShader->samplerHeaps[heapIdx]); 231 | } 232 | 233 | dx12_computeShader->commandSignature->Release(); 234 | dx12_computeShader->shaderBlob->Release(); 235 | dx12_computeShader->pipelineStateObject->Release(); 236 | destroy_root_signature(dx12_computeShader->rootSignature); 237 | 238 | // Resource counting 239 | dx12_computeShader->device->allocatedCS--; 240 | 241 | // Destroy the internal structure 242 | delete dx12_computeShader; 243 | } 244 | } 245 | } 246 | #endif --------------------------------------------------------------------------------