diff --git a/scripts/startup/bl_ui/properties_render.py b/scripts/startup/bl_ui/properties_render.py index 5b7105383a0..877d54bee7d 100644 --- a/scripts/startup/bl_ui/properties_render.py +++ b/scripts/startup/bl_ui/properties_render.py @@ -198,6 +198,7 @@ class RENDER_PT_eevee_next_horizon_scan(RenderButtonsPanel, Panel): col.prop(props, "horizon_quality", text="Precision") col.prop(props, "horizon_thickness", text="Thickness") col.prop(props, "horizon_bias", text="Bias") + col.prop(props, "horizon_resolution", text="Resolution") class RENDER_PT_eevee_motion_blur(RenderButtonsPanel, Panel): diff --git a/source/blender/blenkernel/BKE_blender_version.h b/source/blender/blenkernel/BKE_blender_version.h index dd074b2c94d..63f37f185e5 100644 --- a/source/blender/blenkernel/BKE_blender_version.h +++ b/source/blender/blenkernel/BKE_blender_version.h @@ -29,7 +29,7 @@ extern "C" { /* Blender file format version. */ #define BLENDER_FILE_VERSION BLENDER_VERSION -#define BLENDER_FILE_SUBVERSION 9 +#define BLENDER_FILE_SUBVERSION 10 /* Minimum Blender version that supports reading file written with the current * version. Older Blender versions will test this and cancel loading the file, showing a warning to diff --git a/source/blender/blenloader/intern/versioning_400.cc b/source/blender/blenloader/intern/versioning_400.cc index d83842e44bf..758995a8154 100644 --- a/source/blender/blenloader/intern/versioning_400.cc +++ b/source/blender/blenloader/intern/versioning_400.cc @@ -3056,6 +3056,14 @@ void blo_do_versions_400(FileData *fd, Library * /*lib*/, Main *bmain) } } + if (!MAIN_VERSION_FILE_ATLEAST(bmain, 402, 10)) { + if (!DNA_struct_member_exists(fd->filesdna, "SceneEEVEE", "int", "gtao_resolution")) { + LISTBASE_FOREACH (Scene *, scene, &bmain->scenes) { + scene->eevee.gtao_resolution = 2; + } + } + } + /** * Always bump subversion in BKE_blender_version.h when adding versioning * code here, and wrap it inside a MAIN_VERSION_FILE_ATLEAST check. diff --git a/source/blender/draw/CMakeLists.txt b/source/blender/draw/CMakeLists.txt index 417072c5d77..21f124c3df3 100644 --- a/source/blender/draw/CMakeLists.txt +++ b/source/blender/draw/CMakeLists.txt @@ -498,6 +498,7 @@ set(GLSL_SRC engines/eevee_next/shaders/eevee_film_cryptomatte_post_comp.glsl engines/eevee_next/shaders/eevee_film_frag.glsl engines/eevee_next/shaders/eevee_film_lib.glsl + engines/eevee_next/shaders/eevee_filter_lib.glsl engines/eevee_next/shaders/eevee_forward_lib.glsl engines/eevee_next/shaders/eevee_gbuffer_lib.glsl engines/eevee_next/shaders/eevee_gbuffer_closure_test.glsl @@ -511,6 +512,7 @@ set(GLSL_SRC engines/eevee_next/shaders/eevee_hiz_debug_frag.glsl engines/eevee_next/shaders/eevee_hiz_update_comp.glsl engines/eevee_next/shaders/eevee_horizon_denoise_comp.glsl + engines/eevee_next/shaders/eevee_horizon_resolve_comp.glsl engines/eevee_next/shaders/eevee_horizon_scan_eval_lib.glsl engines/eevee_next/shaders/eevee_horizon_scan_comp.glsl engines/eevee_next/shaders/eevee_horizon_scan_lib.glsl diff --git a/source/blender/draw/engines/eevee_next/eevee_raytrace.cc b/source/blender/draw/engines/eevee_next/eevee_raytrace.cc index e46e6e254cc..1a34b6a1b48 100644 --- a/source/blender/draw/engines/eevee_next/eevee_raytrace.cc +++ b/source/blender/draw/engines/eevee_next/eevee_raytrace.cc @@ -60,19 +60,14 @@ void RayTraceModule::sync() GPUShader *sh = inst_.shaders.static_shader_get(RAY_TILE_COMPACT); pass.init(); pass.specialize_constant(sh, "closure_index", &data_.closure_index); + pass.specialize_constant(sh, "resolution_scale", &data_.resolution_scale); pass.shader_set(sh); pass.bind_image("tile_raytrace_denoise_img", &tile_raytrace_denoise_tx_); pass.bind_image("tile_raytrace_tracing_img", &tile_raytrace_tracing_tx_); - pass.bind_image("tile_horizon_denoise_img", &tile_horizon_denoise_tx_); - pass.bind_image("tile_horizon_tracing_img", &tile_horizon_tracing_tx_); pass.bind_ssbo("raytrace_tracing_dispatch_buf", &raytrace_tracing_dispatch_buf_); pass.bind_ssbo("raytrace_denoise_dispatch_buf", &raytrace_denoise_dispatch_buf_); - pass.bind_ssbo("horizon_tracing_dispatch_buf", &horizon_tracing_dispatch_buf_); - pass.bind_ssbo("horizon_denoise_dispatch_buf", &horizon_denoise_dispatch_buf_); pass.bind_ssbo("raytrace_tracing_tiles_buf", &raytrace_tracing_tiles_buf_); pass.bind_ssbo("raytrace_denoise_tiles_buf", &raytrace_denoise_tiles_buf_); - pass.bind_ssbo("horizon_tracing_tiles_buf", &horizon_tracing_tiles_buf_); - pass.bind_ssbo("horizon_denoise_tiles_buf", &horizon_denoise_tiles_buf_); pass.bind_resources(inst_.uniform_data); pass.dispatch(&tile_compact_dispatch_size_); pass.barrier(GPU_BARRIER_SHADER_STORAGE); @@ -220,6 +215,24 @@ void RayTraceModule::sync() pass.dispatch(raytrace_denoise_dispatch_buf_); pass.barrier(GPU_BARRIER_SHADER_IMAGE_ACCESS); } + { + PassSimple &pass = horizon_schedule_ps_; + /* Reuse tile compaction shader but feed it with horizon scan specific buffers. */ + GPUShader *sh = inst_.shaders.static_shader_get(RAY_TILE_COMPACT); + pass.init(); + pass.specialize_constant(sh, "closure_index", 0); + pass.specialize_constant(sh, "resolution_scale", &data_.horizon_resolution_scale); + pass.shader_set(sh); + pass.bind_image("tile_raytrace_denoise_img", &tile_horizon_denoise_tx_); + pass.bind_image("tile_raytrace_tracing_img", &tile_horizon_tracing_tx_); + pass.bind_ssbo("raytrace_tracing_dispatch_buf", &horizon_tracing_dispatch_buf_); + pass.bind_ssbo("raytrace_denoise_dispatch_buf", &horizon_denoise_dispatch_buf_); + pass.bind_ssbo("raytrace_tracing_tiles_buf", &horizon_tracing_tiles_buf_); + pass.bind_ssbo("raytrace_denoise_tiles_buf", &horizon_denoise_tiles_buf_); + pass.bind_resources(inst_.uniform_data); + pass.dispatch(&horizon_schedule_dispatch_size_); + pass.barrier(GPU_BARRIER_SHADER_STORAGE); + } { PassSimple &pass = horizon_setup_ps_; pass.init(); @@ -232,39 +245,64 @@ void RayTraceModule::sync() pass.bind_image("out_normal_img", &downsampled_in_normal_tx_); pass.bind_resources(inst_.uniform_data); pass.bind_resources(inst_.gbuffer); - pass.dispatch(&tracing_dispatch_size_); + pass.dispatch(&horizon_tracing_dispatch_size_); pass.barrier(GPU_BARRIER_SHADER_IMAGE_ACCESS); } { PassSimple &pass = horizon_scan_ps_; pass.init(); GPUShader *sh = inst_.shaders.static_shader_get(HORIZON_SCAN); - pass.specialize_constant(sh, "closure_index", &data_.closure_index); pass.shader_set(sh); - pass.bind_image("horizon_radiance_img", &horizon_radiance_tx_); - pass.bind_image("horizon_occlusion_img", &horizon_occlusion_tx_); - pass.bind_ssbo("tiles_coord_buf", &horizon_tracing_tiles_buf_); pass.bind_texture("screen_radiance_tx", &downsampled_in_radiance_tx_); pass.bind_texture("screen_normal_tx", &downsampled_in_normal_tx_); + pass.bind_image("horizon_radiance_0_img", &horizon_radiance_tx_[0]); + pass.bind_image("horizon_radiance_1_img", &horizon_radiance_tx_[1]); + pass.bind_image("horizon_radiance_2_img", &horizon_radiance_tx_[2]); + pass.bind_image("horizon_radiance_3_img", &horizon_radiance_tx_[3]); + pass.bind_ssbo("tiles_coord_buf", &horizon_tracing_tiles_buf_); pass.bind_texture(RBUFS_UTILITY_TEX_SLOT, inst_.pipelines.utility_tx); pass.bind_resources(inst_.uniform_data); pass.bind_resources(inst_.hiz_buffer.front); pass.bind_resources(inst_.sampling); pass.bind_resources(inst_.gbuffer); pass.dispatch(horizon_tracing_dispatch_buf_); - pass.barrier(GPU_BARRIER_SHADER_IMAGE_ACCESS); + pass.barrier(GPU_BARRIER_TEXTURE_FETCH); } { PassSimple &pass = horizon_denoise_ps_; pass.init(); GPUShader *sh = inst_.shaders.static_shader_get(HORIZON_DENOISE); - pass.specialize_constant(sh, "closure_index", &data_.closure_index); + pass.shader_set(sh); + pass.bind_texture("in_sh_0_tx", &horizon_radiance_tx_[0]); + pass.bind_texture("in_sh_1_tx", &horizon_radiance_tx_[1]); + pass.bind_texture("in_sh_2_tx", &horizon_radiance_tx_[2]); + pass.bind_texture("in_sh_3_tx", &horizon_radiance_tx_[3]); + pass.bind_texture("screen_normal_tx", &downsampled_in_normal_tx_); + pass.bind_image("out_sh_0_img", &horizon_radiance_denoised_tx_[0]); + pass.bind_image("out_sh_1_img", &horizon_radiance_denoised_tx_[1]); + pass.bind_image("out_sh_2_img", &horizon_radiance_denoised_tx_[2]); + pass.bind_image("out_sh_3_img", &horizon_radiance_denoised_tx_[3]); + pass.bind_ssbo("tiles_coord_buf", &horizon_tracing_tiles_buf_); + pass.bind_resources(inst_.uniform_data); + pass.bind_resources(inst_.sampling); + pass.bind_resources(inst_.hiz_buffer.front); + pass.dispatch(horizon_tracing_dispatch_buf_); + pass.barrier(GPU_BARRIER_TEXTURE_FETCH); + } + { + PassSimple &pass = horizon_resolve_ps_; + pass.init(); + GPUShader *sh = inst_.shaders.static_shader_get(HORIZON_RESOLVE); pass.shader_set(sh); pass.bind_texture("depth_tx", &depth_tx); - pass.bind_image("horizon_radiance_img", &horizon_radiance_tx_); - pass.bind_image("horizon_occlusion_img", &horizon_occlusion_tx_); - pass.bind_image("radiance_img", &horizon_scan_output_tx_); - pass.bind_image("tile_mask_img", &tile_horizon_denoise_tx_); + pass.bind_texture("horizon_radiance_0_tx", &horizon_radiance_denoised_tx_[0]); + pass.bind_texture("horizon_radiance_1_tx", &horizon_radiance_denoised_tx_[1]); + pass.bind_texture("horizon_radiance_2_tx", &horizon_radiance_denoised_tx_[2]); + pass.bind_texture("horizon_radiance_3_tx", &horizon_radiance_denoised_tx_[3]); + pass.bind_texture("screen_normal_tx", &downsampled_in_normal_tx_); + pass.bind_image("closure0_img", &horizon_scan_output_tx_[0]); + pass.bind_image("closure1_img", &horizon_scan_output_tx_[1]); + pass.bind_image("closure2_img", &horizon_scan_output_tx_[2]); pass.bind_ssbo("tiles_coord_buf", &horizon_denoise_tiles_buf_); pass.bind_resources(inst_.uniform_data); pass.bind_resources(inst_.sampling); @@ -297,19 +335,26 @@ RayTraceResult RayTraceModule::render(RayTraceBuffer &rt_buffer, bool use_horizon_scan = options.screen_trace_max_roughness < 1.0f; const int resolution_scale = max_ii(1, power_of_2_max_i(options.resolution_scale)); + const int horizon_resolution_scale = max_ii( + 1, power_of_2_max_i(inst_.scene->eevee.gtao_resolution)); const int2 extent = inst_.film.render_extent_get(); const int2 tracing_res = math::divide_ceil(extent, int2(resolution_scale)); + const int2 tracing_res_horizon = math::divide_ceil(extent, int2(horizon_resolution_scale)); const int2 dummy_extent(1, 1); const int2 group_size(RAYTRACE_GROUP_SIZE); const int2 denoise_tiles = divide_ceil(extent, group_size); const int2 raytrace_tiles = divide_ceil(tracing_res, group_size); + const int2 raytrace_tiles_horizon = divide_ceil(tracing_res_horizon, group_size); const int denoise_tile_count = denoise_tiles.x * denoise_tiles.y; const int raytrace_tile_count = raytrace_tiles.x * raytrace_tiles.y; + const int raytrace_tile_count_horizon = raytrace_tiles_horizon.x * raytrace_tiles_horizon.y; tile_classify_dispatch_size_ = int3(denoise_tiles, 1); + horizon_schedule_dispatch_size_ = int3(divide_ceil(raytrace_tiles_horizon, group_size), 1); tile_compact_dispatch_size_ = int3(divide_ceil(raytrace_tiles, group_size), 1); - tracing_dispatch_size_ = int3(divide_ceil(tracing_res, group_size), 1); + tracing_dispatch_size_ = int3(raytrace_tiles, 1); + horizon_tracing_dispatch_size_ = int3(raytrace_tiles_horizon, 1); /* TODO(fclem): Use real max closure count from shader. */ const int closure_count = 3; @@ -317,15 +362,16 @@ RayTraceResult RayTraceModule::render(RayTraceBuffer &rt_buffer, eGPUTextureUsage usage_rw = GPU_TEXTURE_USAGE_SHADER_READ | GPU_TEXTURE_USAGE_SHADER_WRITE; tile_raytrace_denoise_tx_.ensure_2d_array(format, denoise_tiles, closure_count, usage_rw); tile_raytrace_tracing_tx_.ensure_2d_array(format, raytrace_tiles, closure_count, usage_rw); - tile_horizon_denoise_tx_.ensure_2d_array(format, denoise_tiles, closure_count, usage_rw); - tile_horizon_tracing_tx_.ensure_2d_array(format, raytrace_tiles, closure_count, usage_rw); + /* Kept as 2D array for compatibility with the tile compaction shader. */ + tile_horizon_denoise_tx_.ensure_2d_array(format, denoise_tiles, 1, usage_rw); + tile_horizon_tracing_tx_.ensure_2d_array(format, raytrace_tiles_horizon, 1, usage_rw); tile_raytrace_denoise_tx_.clear(uint4(0u)); tile_raytrace_tracing_tx_.clear(uint4(0u)); tile_horizon_denoise_tx_.clear(uint4(0u)); tile_horizon_tracing_tx_.clear(uint4(0u)); - horizon_tracing_tiles_buf_.resize(ceil_to_multiple_u(raytrace_tile_count, 512)); + horizon_tracing_tiles_buf_.resize(ceil_to_multiple_u(raytrace_tile_count_horizon, 512)); horizon_denoise_tiles_buf_.resize(ceil_to_multiple_u(denoise_tile_count, 512)); raytrace_tracing_tiles_buf_.resize(ceil_to_multiple_u(raytrace_tile_count, 512)); raytrace_denoise_tiles_buf_.resize(ceil_to_multiple_u(denoise_tile_count, 512)); @@ -344,6 +390,9 @@ RayTraceResult RayTraceModule::render(RayTraceBuffer &rt_buffer, data_.full_resolution = extent; data_.full_resolution_inv = 1.0f / float2(extent); + data_.horizon_resolution_scale = horizon_resolution_scale; + data_.horizon_resolution_bias = int2(inst_.sampling.rng_2d_get(SAMPLING_RAYTRACE_V) * + horizon_resolution_scale); /* TODO(fclem): Eventually all uniform data is setup here. */ inst_.uniform_data.push_update(); @@ -352,15 +401,9 @@ RayTraceResult RayTraceModule::render(RayTraceBuffer &rt_buffer, DRW_stats_group_start("Raytracing"); - if (use_horizon_scan) { - downsampled_in_radiance_tx_.acquire(tracing_res, RAYTRACE_RADIANCE_FORMAT, usage_rw); - downsampled_in_normal_tx_.acquire(tracing_res, GPU_RGBA8, usage_rw); + const bool has_active_closure = active_closures != CLOSURE_NONE; - screen_radiance_front_tx_ = screen_radiance_front_tx; - inst_.manager->submit(horizon_setup_ps_, render_view); - } - - if (active_closures != CLOSURE_NONE) { + if (has_active_closure) { inst_.manager->submit(tile_classify_ps_); } @@ -375,12 +418,47 @@ RayTraceResult RayTraceModule::render(RayTraceBuffer &rt_buffer, screen_radiance_front_tx, screen_radiance_persmat, main_view, - render_view, - use_horizon_scan); + render_view); } - downsampled_in_radiance_tx_.release(); - downsampled_in_normal_tx_.release(); + if (has_active_closure) { + if (use_horizon_scan) { + DRW_stats_group_start("Horizon Scan"); + + screen_radiance_front_tx_ = screen_radiance_front_tx; + + downsampled_in_radiance_tx_.acquire(tracing_res_horizon, RAYTRACE_RADIANCE_FORMAT, usage_rw); + downsampled_in_normal_tx_.acquire(tracing_res_horizon, GPU_RGB10_A2, usage_rw); + + horizon_radiance_tx_[0].acquire(tracing_res_horizon, GPU_RGBA16F, usage_rw); + horizon_radiance_denoised_tx_[0].acquire(tracing_res_horizon, GPU_RGBA16F, usage_rw); + for (int i : IndexRange(1, 3)) { + horizon_radiance_tx_[i].acquire(tracing_res_horizon, GPU_RGBA8, usage_rw); + horizon_radiance_denoised_tx_[i].acquire(tracing_res_horizon, GPU_RGBA8, usage_rw); + } + for (int i : IndexRange(3)) { + horizon_scan_output_tx_[i] = result.closures[i].get(); + } + + horizon_tracing_dispatch_buf_.clear_to_zero(); + horizon_denoise_dispatch_buf_.clear_to_zero(); + inst_.manager->submit(horizon_schedule_ps_); + + inst_.manager->submit(horizon_setup_ps_, render_view); + inst_.manager->submit(horizon_scan_ps_, render_view); + inst_.manager->submit(horizon_denoise_ps_, render_view); + inst_.manager->submit(horizon_resolve_ps_, render_view); + + for (int i : IndexRange(4)) { + horizon_radiance_tx_[i].release(); + horizon_radiance_denoised_tx_[i].release(); + } + downsampled_in_radiance_tx_.release(); + downsampled_in_normal_tx_.release(); + + DRW_stats_group_end(); + } + } DRW_stats_group_end(); @@ -397,8 +475,7 @@ RayTraceResultTexture RayTraceModule::trace( const float4x4 &screen_radiance_persmat, /* TODO(fclem): Maybe wrap these two in some other class. */ View &main_view, - View &render_view, - bool use_horizon_scan) + View &render_view) { RayTraceBuffer::DenoiseBuffer *denoise_buf = &rt_buffer.closures[closure_index]; @@ -452,8 +529,6 @@ RayTraceResultTexture RayTraceModule::trace( /* Ray setup. */ raytrace_tracing_dispatch_buf_.clear_to_zero(); raytrace_denoise_dispatch_buf_.clear_to_zero(); - horizon_tracing_dispatch_buf_.clear_to_zero(); - horizon_denoise_dispatch_buf_.clear_to_zero(); inst_.manager->submit(tile_compact_ps_); { @@ -555,20 +630,6 @@ RayTraceResultTexture RayTraceModule::trace( denoise_variance_tx_.release(); - if (use_horizon_scan) { - horizon_occlusion_tx_.acquire(tracing_res, GPU_R8, usage_rw); - horizon_radiance_tx_.acquire(tracing_res, RAYTRACE_RADIANCE_FORMAT, usage_rw); - - inst_.manager->submit(horizon_scan_ps_, render_view); - - horizon_scan_output_tx_ = result.get(); - - inst_.manager->submit(horizon_denoise_ps_, render_view); - - horizon_occlusion_tx_.release(); - horizon_radiance_tx_.release(); - } - DRW_stats_group_end(); return result; diff --git a/source/blender/draw/engines/eevee_next/eevee_raytrace.hh b/source/blender/draw/engines/eevee_next/eevee_raytrace.hh index 2a06d91a8b5..a181aba2c77 100644 --- a/source/blender/draw/engines/eevee_next/eevee_raytrace.hh +++ b/source/blender/draw/engines/eevee_next/eevee_raytrace.hh @@ -120,16 +120,20 @@ class RayTraceModule { draw::PassSimple denoise_spatial_ps_ = {"DenoiseSpatial"}; draw::PassSimple denoise_temporal_ps_ = {"DenoiseTemporal"}; draw::PassSimple denoise_bilateral_ps_ = {"DenoiseBilateral"}; + draw::PassSimple horizon_schedule_ps_ = {"HorizonScan.Schedule"}; draw::PassSimple horizon_setup_ps_ = {"HorizonScan.Setup"}; draw::PassSimple horizon_scan_ps_ = {"HorizonScan.Trace"}; draw::PassSimple horizon_denoise_ps_ = {"HorizonScan.Denoise"}; + draw::PassSimple horizon_resolve_ps_ = {"HorizonScan.Resolve"}; /** Dispatch with enough tiles for the whole screen. */ int3 tile_classify_dispatch_size_ = int3(1); /** Dispatch with enough tiles for the tile mask. */ int3 tile_compact_dispatch_size_ = int3(1); + int3 horizon_schedule_dispatch_size_ = int3(1); /** Dispatch with enough tiles for the tracing resolution. */ int3 tracing_dispatch_size_ = int3(1); + int3 horizon_tracing_dispatch_size_ = int3(1); /** 2D tile mask to check which unused adjacent tile we need to clear and which tile we need to * dispatch for each work type. */ Texture tile_raytrace_denoise_tx_ = {"tile_raytrace_denoise_tx_"}; @@ -145,7 +149,7 @@ class RayTraceModule { /** Indirect dispatch denoise full-resolution tiles. */ DispatchIndirectBuf horizon_denoise_dispatch_buf_ = {"horizon_denoise_dispatch_buf_"}; /** Pointer to the texture to store the result of horizon scan in. */ - GPUTexture *horizon_scan_output_tx_ = nullptr; + GPUTexture *horizon_scan_output_tx_[3] = {nullptr}; /** Tile buffer that contains tile coordinates. */ RayTraceTileBuf raytrace_tracing_tiles_buf_ = {"raytrace_tracing_tiles_buf_"}; RayTraceTileBuf raytrace_denoise_tiles_buf_ = {"raytrace_denoise_tiles_buf_"}; @@ -157,10 +161,9 @@ class RayTraceModule { TextureFromPool ray_time_tx_ = {"ray_data_tx"}; /** Texture containing the ray hit radiance (tracing-res). */ TextureFromPool ray_radiance_tx_ = {"ray_radiance_tx"}; - /** Texture containing the horizon visibility mask. */ - TextureFromPool horizon_occlusion_tx_ = {"horizon_occlusion_tx_"}; /** Texture containing the horizon local radiance. */ - TextureFromPool horizon_radiance_tx_ = {"horizon_radiance_tx_"}; + TextureFromPool horizon_radiance_tx_[4] = {{"horizon_radiance_tx_"}}; + TextureFromPool horizon_radiance_denoised_tx_[4] = {{"horizon_radiance_denoised_tx_"}}; /** Texture containing the input screen radiance but re-projected. */ TextureFromPool downsampled_in_radiance_tx_ = {"downsampled_in_radiance_tx_"}; /** Texture containing the view space normal. The BSDF normal is arbitrarily chosen. */ @@ -241,8 +244,7 @@ class RayTraceModule { const float4x4 &screen_radiance_persmat, /* TODO(fclem): Maybe wrap these two in some other class. */ View &main_view, - View &render_view, - bool use_horizon_scan); + View &render_view); }; /** \} */ diff --git a/source/blender/draw/engines/eevee_next/eevee_shader.cc b/source/blender/draw/engines/eevee_next/eevee_shader.cc index 4d33aecf9c0..7f1212371cd 100644 --- a/source/blender/draw/engines/eevee_next/eevee_shader.cc +++ b/source/blender/draw/engines/eevee_next/eevee_shader.cc @@ -119,6 +119,8 @@ const char *ShaderModule::static_shader_create_info_name_get(eShaderType shader_ return "eevee_hiz_update_layer"; case HORIZON_DENOISE: return "eevee_horizon_denoise"; + case HORIZON_RESOLVE: + return "eevee_horizon_resolve"; case HORIZON_SCAN: return "eevee_horizon_scan"; case HORIZON_SETUP: diff --git a/source/blender/draw/engines/eevee_next/eevee_shader.hh b/source/blender/draw/engines/eevee_next/eevee_shader.hh index f82b59bc6ed..75e3b51d37b 100644 --- a/source/blender/draw/engines/eevee_next/eevee_shader.hh +++ b/source/blender/draw/engines/eevee_next/eevee_shader.hh @@ -73,6 +73,7 @@ enum eShaderType { HIZ_DEBUG, HORIZON_DENOISE, + HORIZON_RESOLVE, HORIZON_SCAN, HORIZON_SETUP, diff --git a/source/blender/draw/engines/eevee_next/eevee_shader_shared.hh b/source/blender/draw/engines/eevee_next/eevee_shader_shared.hh index 665840bf427..9d265e60a8f 100644 --- a/source/blender/draw/engines/eevee_next/eevee_shader_shared.hh +++ b/source/blender/draw/engines/eevee_next/eevee_shader_shared.hh @@ -1333,6 +1333,9 @@ struct RayTraceData { int resolution_scale; /** View space thickness the objects. */ float thickness; + /** Scale and bias to go from horizon-trace resolution to input resolution. */ + int2 horizon_resolution_bias; + int horizon_resolution_scale; /** Determine how fast the sample steps are getting bigger. */ float quality; /** Maximum brightness during lighting evaluation. */ diff --git a/source/blender/draw/engines/eevee_next/shaders/eevee_ambient_occlusion_pass_comp.glsl b/source/blender/draw/engines/eevee_next/shaders/eevee_ambient_occlusion_pass_comp.glsl index 4006455db65..8eab6f9228e 100644 --- a/source/blender/draw/engines/eevee_next/shaders/eevee_ambient_occlusion_pass_comp.glsl +++ b/source/blender/draw/engines/eevee_next/shaders/eevee_ambient_occlusion_pass_comp.glsl @@ -31,22 +31,15 @@ void main() noise.y = utility_tx_fetch(utility_tx, vec2(texel), UTIL_BLUE_NOISE_LAYER).r; noise = fract(noise + sampling_rng_2D_get(SAMPLING_AO_U)); - ClosureOcclusion occlusion; - occlusion.N = vN; + HorizonScanResult scan = horizon_scan_eval(vP, + vN, + noise, + uniform_buf.ao.pixel_size, + uniform_buf.ao.distance, + uniform_buf.ao.thickness, + uniform_buf.ao.angle_bias, + 10, + false); - HorizonScanContext ctx; - ctx.occlusion = occlusion; - - horizon_scan_eval(vP, - ctx, - noise, - uniform_buf.ao.pixel_size, - uniform_buf.ao.distance, - uniform_buf.ao.thickness, - uniform_buf.ao.angle_bias, - 10, - false); - - imageStore( - out_ao_img, ivec3(texel, out_ao_img_layer_index), vec4(saturate(ctx.occlusion_result.r))); + imageStore(out_ao_img, ivec3(texel, out_ao_img_layer_index), vec4(saturate(scan.result))); } diff --git a/source/blender/draw/engines/eevee_next/shaders/eevee_filter_lib.glsl b/source/blender/draw/engines/eevee_next/shaders/eevee_filter_lib.glsl new file mode 100644 index 00000000000..c3ab5a827d9 --- /dev/null +++ b/source/blender/draw/engines/eevee_next/shaders/eevee_filter_lib.glsl @@ -0,0 +1,59 @@ +/* SPDX-FileCopyrightText: 2023 Blender Authors + * + * SPDX-License-Identifier: GPL-2.0-or-later */ + +/** + * Filtering utilities. + */ + +#pragma BLENDER_REQUIRE(gpu_shader_math_base_lib.glsl) + +/** + * Return the factor to filter_gaussian_weight. This is handy utility function to compute your + * gaussian parameter in a documented manner. + * - `linear_distance` is the distance at which the filter will have covered the given amount of + * `standard_deviation`. Must not be null. + * - `standard_deviation` is the shape of the bell. Higher values sharpens the filter. + * + * https://en.wikipedia.org/wiki/Standard_deviation#/media/File:Standard_deviation_diagram.svg + * + * Example: for a 5px 1d gaussian filter, one would set `linear_distance` of 2.5. + * `standard_deviation = 1.0` will cover 68% of the gaussian weight inside the 5px radius. + * `standard_deviation = 2.0` will cover 95% of the gaussian weight inside the 5px radius. + */ +float filter_gaussian_factor(float linear_distance, float standard_deviation) +{ + /* Account for `filter_gaussian_factor` using `exp2` for speed (`exp(x) = exp2(x / log(2))`). */ + const float log_2_inv = 1.442695041; + return log_2_inv * standard_deviation / square(linear_distance); +} + +/** + * Gaussian distance weighting. Allow weighting based on distance without null weight whatever the + * distance. `factor` is supposed to be a scaling parameter given by `filter_gaussian_factor`. + */ +float filter_gaussian_weight(float factor, float square_distance) +{ + /* Using exp2 since it is faster on GPU. `filter_gaussian_factor` account for that. */ + return exp2(-factor * square_distance); +} + +/** + * Planar distance weighting. Allow to weight based on geometric neighborhood. + */ +float filter_planar_weight(vec3 plane_N, vec3 plane_P, vec3 P, float scale) +{ + vec4 plane_eq = vec4(plane_N, -dot(plane_N, plane_P)); + float plane_distance = dot(plane_eq, vec4(P, 1.0)); + return filter_gaussian_weight(scale, square(plane_distance)); +} + +/** + * Angle weighting. Mostly used for normals. + * Expects both normals to be normalized. + */ +float filter_angle_weight(vec3 center_N, vec3 sample_N) +{ + float facing_ratio = dot(center_N, sample_N); + return saturate(pow8f(facing_ratio)); +} diff --git a/source/blender/draw/engines/eevee_next/shaders/eevee_horizon_denoise_comp.glsl b/source/blender/draw/engines/eevee_next/shaders/eevee_horizon_denoise_comp.glsl index 80db3b8e832..b15c97b8c11 100644 --- a/source/blender/draw/engines/eevee_next/shaders/eevee_horizon_denoise_comp.glsl +++ b/source/blender/draw/engines/eevee_next/shaders/eevee_horizon_denoise_comp.glsl @@ -6,161 +6,103 @@ #pragma BLENDER_REQUIRE(eevee_sampling_lib.glsl) #pragma BLENDER_REQUIRE(gpu_shader_utildefines_lib.glsl) #pragma BLENDER_REQUIRE(gpu_shader_math_vector_lib.glsl) -#pragma BLENDER_REQUIRE(eevee_gbuffer_lib.glsl) -#pragma BLENDER_REQUIRE(eevee_lightprobe_eval_lib.glsl) -#pragma BLENDER_REQUIRE(eevee_closure_lib.glsl) +#pragma BLENDER_REQUIRE(eevee_spherical_harmonics_lib.glsl) +#pragma BLENDER_REQUIRE(eevee_filter_lib.glsl) -float bilateral_depth_weight(vec3 center_N, vec3 center_P, vec3 sample_P) +vec3 sample_normal_get(ivec2 texel, out bool is_processed) { - vec4 center_plane_eq = vec4(center_N, -dot(center_N, center_P)); - /* Only compare distance to the center plane formed by the normal. */ - float depth_delta = dot(center_plane_eq, vec4(sample_P, 1.0)); - /* TODO(fclem): Scene parameter. This is dependent on scene scale. */ - const float scale = 10000.0; - float weight = exp2(-scale * square(depth_delta)); - return weight; + vec4 normal = texelFetch(screen_normal_tx, texel, 0); + is_processed = (normal.w != 0.0); + return normal.xyz * 2.0 - 1.0; } -float bilateral_spatial_weight(float sigma, vec2 offset_from_center) +float sample_weight_get( + vec3 center_N, vec3 center_P, ivec2 sample_texel, vec2 sample_uv, ivec2 sample_offset) { - /* From https://github.com/tranvansang/bilateral-filter/blob/master/fshader.frag */ - float fac = -1.0 / square(sigma); - /* Take two standard deviation. */ - fac *= 2.0; - float weight = exp2(fac * length_squared(offset_from_center)); - return weight; + ivec2 sample_texel_fullres = sample_texel * uniform_buf.raytrace.horizon_resolution_scale + + uniform_buf.raytrace.horizon_resolution_bias; + float sample_depth = texelFetch(hiz_tx, sample_texel_fullres, 0).r; + + bool is_valid; + vec3 sample_N = sample_normal_get(sample_texel, is_valid); + vec3 sample_P = drw_point_screen_to_world(vec3(sample_uv, sample_depth)); + + if (!is_valid) { + return 0.0; + } + + float gauss = filter_gaussian_factor(1.5, 1.5); + + /* TODO(fclem): Scene parameter. 100.0 is dependent on scene scale. */ + float depth_weight = filter_planar_weight(center_N, center_P, sample_P, 100.0); + float spatial_weight = filter_gaussian_weight(gauss, length_squared(vec2(sample_offset))); + float normal_weight = filter_angle_weight(center_N, sample_N); + + return depth_weight * spatial_weight * normal_weight; } -float bilateral_normal_weight(vec3 center_N, vec3 sample_N) +SphericalHarmonicL1 load_spherical_harmonic(ivec2 texel) { - float facing_ratio = dot(center_N, sample_N); - float weight = saturate(pow8f(facing_ratio)); - return weight; -} - -/* In order to remove some more fireflies, "tone-map" the color samples during the accumulation. */ -vec3 to_accumulation_space(vec3 color) -{ - /* This 4 factor is to avoid killing too much energy. */ - /* TODO(fclem): Parameter? */ - color /= 4.0; - color = color / (1.0 + reduce_add(color)); - return color; -} -vec3 from_accumulation_space(vec3 color) -{ - color = color / (1.0 - reduce_add(color)); - color *= 4.0; - return color; -} - -vec3 load_normal(ivec2 texel) -{ - return gbuffer_read(gbuf_header_tx, gbuf_closure_tx, gbuf_normal_tx, texel).surface_N; + SphericalHarmonicL1 sh; + sh.L0.M0 = texelFetch(in_sh_0_tx, texel, 0); + sh.L1.Mn1 = texelFetch(in_sh_1_tx, texel, 0); + sh.L1.M0 = texelFetch(in_sh_2_tx, texel, 0); + sh.L1.Mp1 = texelFetch(in_sh_3_tx, texel, 0); + sh = spherical_harmonics_decompress(sh); + return sh; } void main() { const uint tile_size = RAYTRACE_GROUP_SIZE; uvec2 tile_coord = unpackUvec2x16(tiles_coord_buf[gl_WorkGroupID.x]); + ivec2 texel = ivec2(gl_LocalInvocationID.xy + tile_coord * tile_size); - ivec2 texel_fullres = ivec2(gl_LocalInvocationID.xy + tile_coord * tile_size); - ivec2 texel = (texel_fullres) / uniform_buf.raytrace.resolution_scale; + vec2 texel_size = 1.0 / vec2(textureSize(in_sh_0_tx, 0).xy); + ivec2 texel_fullres = texel * uniform_buf.raytrace.horizon_resolution_scale + + uniform_buf.raytrace.horizon_resolution_bias; - ivec2 extent = textureSize(gbuf_header_tx, 0).xy; - if (any(greaterThanEqual(texel_fullres, extent))) { - return; - } - - vec2 center_uv = (vec2(texel_fullres) + 0.5) * uniform_buf.raytrace.full_resolution_inv; - float center_depth = texelFetch(depth_tx, texel_fullres, 0).r; + bool is_valid; + float center_depth = texelFetch(hiz_tx, texel_fullres, 0).r; + vec2 center_uv = vec2(texel) * texel_size; vec3 center_P = drw_point_screen_to_world(vec3(center_uv, center_depth)); + vec3 center_N = sample_normal_get(texel, is_valid); - if (center_depth == 1.0) { - /* Do not trace for background */ + if (!is_valid) { +#if 0 /* This is not needed as the next stage doesn't do bilinear filtering. */ + imageStore(out_sh_0_img, texel, vec4(0.0)); + imageStore(out_sh_1_img, texel, vec4(0.0)); + imageStore(out_sh_2_img, texel, vec4(0.0)); + imageStore(out_sh_3_img, texel, vec4(0.0)); +#endif return; } - ClosureUndetermined closure_center = gbuffer_read_bin( - gbuf_header_tx, gbuf_closure_tx, gbuf_normal_tx, texel_fullres, closure_index); - - if (closure_center.type == CLOSURE_NONE_ID) { - return; - } - - vec3 center_N = closure_center.N; - float roughness = closure_apparent_roughness_get(closure_center); - - float mix_fac = saturate(roughness * uniform_buf.raytrace.roughness_mask_scale - - uniform_buf.raytrace.roughness_mask_bias); - bool use_raytrace = mix_fac < 1.0; - bool use_horizon = mix_fac > 0.0; - - if (use_horizon == false) { - return; - } - - vec3 accum_radiance = vec3(0.0); - float accum_occlusion = 0.0; + SphericalHarmonicL1 accum_sh = spherical_harmonics_L1_new(); float accum_weight = 0.0; - for (int x = -1; x <= 1; x++) { - for (int y = -1; y <= 1; y++) { - ivec2 offset = ivec2(x, y); - ivec2 sample_texel = texel + ivec2(x, y); - ivec2 sample_texel_fullres = sample_texel * uniform_buf.raytrace.resolution_scale + - uniform_buf.raytrace.resolution_bias; - ivec3 sample_tile = ivec3(sample_texel_fullres / RAYTRACE_GROUP_SIZE, closure_index); - /* Make sure the sample has been processed and do not contain garbage data. */ - if (imageLoad(tile_mask_img, sample_tile).r == 0u) { - continue; + /* 3x3 filter. */ + for (int y = -1; y <= 1; y++) { + for (int x = -1; x <= 1; x++) { + ivec2 sample_offset = ivec2(x, y); + ivec2 sample_texel = texel + sample_offset; + vec2 sample_uv = (vec2(sample_texel) + 0.5) * texel_size; + float sample_weight = sample_weight_get( + center_N, center_P, sample_texel, sample_uv, sample_offset); + /* We need to avoid sampling if there no weight as the texture values could be undefined + * (is_valid is false). */ + if (sample_weight > 0.0) { + SphericalHarmonicL1 sample_sh = load_spherical_harmonic(sample_texel); + accum_sh = spherical_harmonics_madd(sample_sh, sample_weight, accum_sh); + accum_weight += sample_weight; } - - float sample_depth = texelFetch(depth_tx, sample_texel_fullres, 0).r; - vec2 sample_uv = (vec2(sample_texel_fullres) + 0.5) * - uniform_buf.raytrace.full_resolution_inv; - vec3 sample_P = drw_point_screen_to_world(vec3(sample_uv, sample_depth)); - - /* Background case. */ - if (sample_depth == 0.0) { - continue; - } - - vec3 sample_N = load_normal(sample_texel_fullres); - - float depth_weight = bilateral_depth_weight(center_N, center_P, sample_P); - float spatial_weight = bilateral_spatial_weight(1.5, vec2(offset)); - float normal_weight = bilateral_normal_weight(center_N, sample_N); - - float weight = depth_weight * spatial_weight * normal_weight; - - vec3 radiance = imageLoad(horizon_radiance_img, sample_texel).rgb; - /* Do not gather unprocessed pixels. */ - if (all(equal(radiance, FLT_11_11_10_MAX))) { - continue; - } - float occlusion = imageLoad(horizon_occlusion_img, sample_texel).r; - accum_radiance += to_accumulation_space(radiance) * weight; - accum_occlusion += occlusion * weight; - accum_weight += weight; } } - float occlusion = accum_occlusion * safe_rcp(accum_weight); - vec3 radiance = from_accumulation_space(accum_radiance * safe_rcp(accum_weight)); + accum_sh = spherical_harmonics_mul(accum_sh, safe_rcp(accum_weight)); - vec3 P = center_P; - vec3 N = center_N; - vec3 Ng = center_N; - vec3 V = drw_world_incident_vector(P); - /* Fallback to nearest light-probe. */ - LightProbeSample samp = lightprobe_load(P, Ng, V); - vec3 radiance_probe = spherical_harmonics_evaluate_lambert(N, samp.volume_irradiance); - /* Apply missing distant lighting. */ - radiance += occlusion * radiance_probe; + accum_sh = spherical_harmonics_compress(accum_sh); - vec4 radiance_horizon = vec4(radiance, 0.0); - vec4 radiance_raytrace = use_raytrace ? imageLoad(radiance_img, texel_fullres) : vec4(0.0); - - vec4 radiance_mixed = mix(radiance_raytrace, radiance_horizon, mix_fac); - - imageStore(radiance_img, texel_fullres, radiance_mixed); + imageStore(out_sh_0_img, texel, accum_sh.L0.M0); + imageStore(out_sh_1_img, texel, accum_sh.L1.Mn1); + imageStore(out_sh_2_img, texel, accum_sh.L1.M0); + imageStore(out_sh_3_img, texel, accum_sh.L1.Mp1); } diff --git a/source/blender/draw/engines/eevee_next/shaders/eevee_horizon_resolve_comp.glsl b/source/blender/draw/engines/eevee_next/shaders/eevee_horizon_resolve_comp.glsl new file mode 100644 index 00000000000..8a6b99ce345 --- /dev/null +++ b/source/blender/draw/engines/eevee_next/shaders/eevee_horizon_resolve_comp.glsl @@ -0,0 +1,205 @@ +/* SPDX-FileCopyrightText: 2023 Blender Authors + * + * SPDX-License-Identifier: GPL-2.0-or-later */ + +#pragma BLENDER_REQUIRE(draw_view_lib.glsl) +#pragma BLENDER_REQUIRE(eevee_sampling_lib.glsl) +#pragma BLENDER_REQUIRE(gpu_shader_utildefines_lib.glsl) +#pragma BLENDER_REQUIRE(gpu_shader_math_vector_lib.glsl) +#pragma BLENDER_REQUIRE(eevee_gbuffer_lib.glsl) +#pragma BLENDER_REQUIRE(eevee_lightprobe_eval_lib.glsl) +#pragma BLENDER_REQUIRE(eevee_closure_lib.glsl) +#pragma BLENDER_REQUIRE(eevee_filter_lib.glsl) + +vec3 sample_normal_get(ivec2 texel, out bool is_processed) +{ + vec4 normal = texelFetch(screen_normal_tx, texel, 0); + is_processed = (normal.w != 0.0); + return drw_normal_view_to_world(normal.xyz * 2.0 - 1.0); +} + +float sample_weight_get(vec3 center_N, vec3 center_P, ivec2 center_texel, ivec2 sample_offset) +{ + ivec2 sample_texel = center_texel + sample_offset; + ivec2 sample_texel_fullres = sample_texel * uniform_buf.raytrace.horizon_resolution_scale + + uniform_buf.raytrace.horizon_resolution_bias; + vec2 sample_uv = (vec2(sample_texel_fullres) + 0.5) * uniform_buf.raytrace.full_resolution_inv; + + float sample_depth = texelFetch(depth_tx, sample_texel_fullres, 0).r; + + bool is_valid; + vec3 sample_N = sample_normal_get(sample_texel, is_valid); + vec3 sample_P = drw_point_screen_to_world(vec3(sample_uv, sample_depth)); + + if (!is_valid) { + return 0.0; + } + + /* TODO(fclem): Scene parameter. 10000.0 is dependent on scene scale. */ + float depth_weight = filter_planar_weight(center_N, center_P, sample_P, 10000.0); + float normal_weight = filter_angle_weight(center_N, sample_N); + + return depth_weight * normal_weight; +} + +SphericalHarmonicL1 load_spherical_harmonic(ivec2 texel, bool valid) +{ + if (!valid) { + /* We need to avoid sampling if there no weight as the texture values could be undefined + * (is_valid is false). */ + return spherical_harmonics_L1_new(); + } + SphericalHarmonicL1 sh; + sh.L0.M0 = texelFetch(horizon_radiance_0_tx, texel, 0); + sh.L1.Mn1 = texelFetch(horizon_radiance_1_tx, texel, 0); + sh.L1.M0 = texelFetch(horizon_radiance_2_tx, texel, 0); + sh.L1.Mp1 = texelFetch(horizon_radiance_3_tx, texel, 0); + return spherical_harmonics_decompress(sh); +} + +void main() +{ + const uint tile_size = RAYTRACE_GROUP_SIZE; + uvec2 tile_coord = unpackUvec2x16(tiles_coord_buf[gl_WorkGroupID.x]); + ivec2 texel_fullres = ivec2(gl_LocalInvocationID.xy + tile_coord * tile_size); + + ivec2 texel = max(ivec2(0), texel_fullres - uniform_buf.raytrace.horizon_resolution_bias) / + uniform_buf.raytrace.horizon_resolution_scale; + + ivec2 extent = textureSize(gbuf_header_tx, 0).xy; + if (any(greaterThanEqual(texel_fullres, extent))) { + return; + } + + GBufferReader gbuf = gbuffer_read( + gbuf_header_tx, gbuf_closure_tx, gbuf_normal_tx, texel_fullres); + + if (gbuf.header == 0u) { + return; + } + + vec2 center_uv = (vec2(texel_fullres) + 0.5) * uniform_buf.raytrace.full_resolution_inv; + float center_depth = texelFetch(depth_tx, texel_fullres, 0).r; + vec3 center_P = drw_point_screen_to_world(vec3(center_uv, center_depth)); + vec3 center_N = gbuf.surface_N; + + SphericalHarmonicL1 accum_sh; + if (uniform_buf.raytrace.horizon_resolution_scale == 1) { + accum_sh = load_spherical_harmonic(texel, true); + } + else { + vec2 interp = vec2(texel_fullres - texel * uniform_buf.raytrace.horizon_resolution_scale - + uniform_buf.raytrace.horizon_resolution_bias) / + vec2(uniform_buf.raytrace.horizon_resolution_scale); + vec4 interp4 = vec4(interp, 1.0 - interp); + vec4 bilinear_weight = interp4.zxzx * interp4.wwyy; + + vec4 bilateral_weights; + bilateral_weights.x = sample_weight_get(center_N, center_P, texel, ivec2(0, 0)); + bilateral_weights.y = sample_weight_get(center_N, center_P, texel, ivec2(1, 0)); + bilateral_weights.z = sample_weight_get(center_N, center_P, texel, ivec2(0, 1)); + bilateral_weights.w = sample_weight_get(center_N, center_P, texel, ivec2(1, 1)); + + vec4 weights = bilateral_weights * bilinear_weight; + + SphericalHarmonicL1 sh_00 = load_spherical_harmonic(texel + ivec2(0, 0), weights.x > 0.0); + SphericalHarmonicL1 sh_10 = load_spherical_harmonic(texel + ivec2(1, 0), weights.y > 0.0); + SphericalHarmonicL1 sh_01 = load_spherical_harmonic(texel + ivec2(0, 1), weights.z > 0.0); + SphericalHarmonicL1 sh_11 = load_spherical_harmonic(texel + ivec2(1, 1), weights.w > 0.0); + + /* Avoid another division at the end. Normalize the weights upfront. */ + weights *= safe_rcp(reduce_add(weights)); + + accum_sh = spherical_harmonics_mul(sh_00, weights.x); + accum_sh = spherical_harmonics_madd(sh_10, weights.y, accum_sh); + accum_sh = spherical_harmonics_madd(sh_01, weights.z, accum_sh); + accum_sh = spherical_harmonics_madd(sh_11, weights.w, accum_sh); + } + + vec3 P = center_P; + vec3 Ng = center_N; + vec3 V = drw_world_incident_vector(P); + + LightProbeSample samp = lightprobe_load(P, Ng, V); + + for (int i = 0; i < GBUFFER_LAYER_MAX && i < gbuf.closure_count; i++) { + ClosureUndetermined cl = gbuffer_closure_get(gbuf, i); + + float roughness = closure_apparent_roughness_get(cl); + + float mix_fac = saturate(roughness * uniform_buf.raytrace.roughness_mask_scale - + uniform_buf.raytrace.roughness_mask_bias); + bool use_raytrace = mix_fac < 1.0; + bool use_horizon = mix_fac > 0.0; + + if (!use_horizon) { + continue; + } + + vec3 N = cl.N; + + vec3 L; + switch (cl.type) { + case CLOSURE_BSDF_MICROFACET_GGX_REFLECTION_ID: + L = lightprobe_reflection_dominant_dir(cl.N, V, roughness); + break; + case CLOSURE_BSDF_MICROFACET_GGX_REFRACTION_ID: + L = lightprobe_refraction_dominant_dir(cl.N, V, to_closure_refraction(cl).ior, roughness); + break; + case CLOSURE_BSDF_TRANSLUCENT_ID: + L = -N; + break; + default: + L = N; + break; + } + vec3 vL = drw_normal_world_to_view(L); + + /* Evaluate lighting from horizon scan. */ + /* TODO(fclem): Evaluate depending on BSDF. */ + vec3 radiance = spherical_harmonics_evaluate_lambert(vL, accum_sh); + + /* Evaluate visibility from horizon scan. */ + SphericalHarmonicL1 sh_visibility = spherical_harmonics_swizzle_wwww(accum_sh); + float occlusion = spherical_harmonics_evaluate_lambert(vL, sh_visibility).x; + /* FIXME(fclem): Tried to match the old occlusion look. I don't know why it's needed. */ + occlusion *= 0.5; + /* TODO(fclem): Ideally, we should just combine both local and distant irradiance and evaluate + * once. Unfortunately, I couldn't find a way to do the same (1.0 - occlusion) with the + * spherical harmonic coefficients. */ + float visibility = saturate(1.0 - occlusion); + + /* Apply missing distant lighting. */ + vec3 radiance_probe = spherical_harmonics_evaluate_lambert(L, samp.volume_irradiance); + radiance += visibility * radiance_probe; + + int layer_index = gbuffer_closure_get_bin_index(gbuf, i); + + vec4 radiance_horizon = vec4(radiance, 0.0); + vec4 radiance_raytrace = vec4(0.0); + if (use_raytrace) { + /* TODO(fclem): Layered texture. */ + if (layer_index == 0) { + radiance_raytrace = imageLoad(closure0_img, texel_fullres); + } + else if (layer_index == 1) { + radiance_raytrace = imageLoad(closure1_img, texel_fullres); + } + else if (layer_index == 2) { + radiance_raytrace = imageLoad(closure2_img, texel_fullres); + } + } + vec4 radiance_mixed = mix(radiance_raytrace, radiance_horizon, mix_fac); + + /* TODO(fclem): Layered texture. */ + if (layer_index == 0) { + imageStore(closure0_img, texel_fullres, radiance_mixed); + } + else if (layer_index == 1) { + imageStore(closure1_img, texel_fullres, radiance_mixed); + } + else if (layer_index == 2) { + imageStore(closure2_img, texel_fullres, radiance_mixed); + } + } +} diff --git a/source/blender/draw/engines/eevee_next/shaders/eevee_horizon_scan_comp.glsl b/source/blender/draw/engines/eevee_next/shaders/eevee_horizon_scan_comp.glsl index a4c2dc2a0b7..e5a0ba85921 100644 --- a/source/blender/draw/engines/eevee_next/shaders/eevee_horizon_scan_comp.glsl +++ b/source/blender/draw/engines/eevee_next/shaders/eevee_horizon_scan_comp.glsl @@ -6,7 +6,6 @@ #pragma BLENDER_REQUIRE(eevee_sampling_lib.glsl) #pragma BLENDER_REQUIRE(gpu_shader_utildefines_lib.glsl) #pragma BLENDER_REQUIRE(gpu_shader_math_vector_lib.glsl) -#pragma BLENDER_REQUIRE(eevee_gbuffer_lib.glsl) #pragma BLENDER_REQUIRE(eevee_horizon_scan_eval_lib.glsl) void main() @@ -15,48 +14,52 @@ void main() uvec2 tile_coord = unpackUvec2x16(tiles_coord_buf[gl_WorkGroupID.x]); ivec2 texel = ivec2(gl_LocalInvocationID.xy + tile_coord * tile_size); - ivec2 texel_fullres = texel * uniform_buf.raytrace.resolution_scale + - uniform_buf.raytrace.resolution_bias; + ivec2 texel_fullres = texel * uniform_buf.raytrace.horizon_resolution_scale + + uniform_buf.raytrace.horizon_resolution_bias; + /* Avoid tracing the outside border if dispatch is too big. */ ivec2 extent = textureSize(gbuf_header_tx, 0).xy; - if (any(greaterThanEqual(texel_fullres, extent))) { + if (any(greaterThanEqual(texel * uniform_buf.raytrace.horizon_resolution_scale, extent))) { + return; + } + + /* Avoid loading texels outside texture range. + * This can happen even after the check above in non-power-of-2 textures. */ + texel_fullres = min(texel_fullres, extent - 1); + + /* Do not trace where nothing was rendered. */ + if (texelFetch(gbuf_header_tx, texel_fullres, 0).r == 0u) { +#if 0 /* This is not needed as the next stage doesn't do bilinear filtering. */ + imageStore(horizon_radiance_0_img, texel, vec4(0.0)); + imageStore(horizon_radiance_1_img, texel, vec4(0.0)); + imageStore(horizon_radiance_2_img, texel, vec4(0.0)); + imageStore(horizon_radiance_3_img, texel, vec4(0.0)); +#endif return; } vec2 uv = (vec2(texel_fullres) + 0.5) * uniform_buf.raytrace.full_resolution_inv; float depth = texelFetch(hiz_tx, texel_fullres, 0).r; - - if (depth == 1.0) { - /* Do not trace for background */ - imageStore(horizon_radiance_img, texel, vec4(FLT_11_11_10_MAX, 0.0)); - return; - } - - HorizonScanContext ctx; - ctx.closure = gbuffer_read_bin( - gbuf_header_tx, gbuf_closure_tx, gbuf_normal_tx, texel_fullres, closure_index); - ctx.closure.N = drw_normal_world_to_view(ctx.closure.N); - - if (ctx.closure.type == CLOSURE_NONE_ID) { - imageStore(horizon_radiance_img, texel, vec4(FLT_11_11_10_MAX, 0.0)); - return; - } - vec3 vP = drw_point_screen_to_view(vec3(uv, depth)); + vec3 vN = horizon_scan_sample_normal(uv); vec2 noise = utility_tx_fetch(utility_tx, vec2(texel), UTIL_BLUE_NOISE_LAYER).rg; noise = fract(noise + sampling_rng_2D_get(SAMPLING_AO_U)); - horizon_scan_eval(vP, - ctx, - noise, - uniform_buf.ao.pixel_size, - 1.0e16, - uniform_buf.ao.thickness, - uniform_buf.ao.angle_bias, - 8, - false); + HorizonScanResult scan = horizon_scan_eval(vP, + vN, + noise, + uniform_buf.ao.pixel_size, + 1.0e16, + uniform_buf.ao.thickness, + uniform_buf.ao.angle_bias, + 8, + false); - imageStore(horizon_radiance_img, texel, ctx.closure_result); - imageStore(horizon_occlusion_img, texel, ctx.closure_result.wwww); + scan.result = spherical_harmonics_compress(scan.result); + + imageStore(horizon_radiance_0_img, texel, scan.result.L0.M0); + imageStore(horizon_radiance_1_img, texel, scan.result.L1.Mn1); + imageStore(horizon_radiance_2_img, texel, scan.result.L1.M0); + imageStore(horizon_radiance_3_img, texel, scan.result.L1.Mp1); } diff --git a/source/blender/draw/engines/eevee_next/shaders/eevee_horizon_scan_eval_lib.glsl b/source/blender/draw/engines/eevee_next/shaders/eevee_horizon_scan_eval_lib.glsl index e6b143d6aaf..ad6464e720e 100644 --- a/source/blender/draw/engines/eevee_next/shaders/eevee_horizon_scan_eval_lib.glsl +++ b/source/blender/draw/engines/eevee_next/shaders/eevee_horizon_scan_eval_lib.glsl @@ -22,7 +22,9 @@ #pragma BLENDER_REQUIRE(eevee_bxdf_lib.glsl) #pragma BLENDER_REQUIRE(eevee_spherical_harmonics_lib.glsl) -#if defined(MAT_DEFERRED) || defined(MAT_FORWARD) +#ifdef HORIZON_OCCLUSION +/* Do nothing. */ +#elif defined(MAT_DEFERRED) || defined(MAT_FORWARD) /* Enable AO node computation for material shaders. */ # define HORIZON_OCCLUSION #else @@ -47,138 +49,6 @@ vec3 horizon_scan_sample_normal(vec2 uv) #endif } -/* Note: Expects all normals to be in view-space. */ -struct HorizonScanContextCommon { - float N_angle; - float N_length; - uint bitmask; - float weight_slice; - float weight_accum; - vec3 light_slice; - vec4 light_accum; -}; - -struct HorizonScanContext { -#ifdef HORIZON_OCCLUSION - ClosureOcclusion occlusion; - HorizonScanContextCommon occlusion_common; - vec4 occlusion_result; -#endif -#ifdef HORIZON_CLOSURE - ClosureUndetermined closure; - HorizonScanContextCommon closure_common; - vec4 closure_result; -#endif -}; - -void horizon_scan_context_accumulation_reset(inout HorizonScanContext context) -{ -#ifdef HORIZON_OCCLUSION - context.occlusion_common.light_accum = vec4(0.0); - context.occlusion_common.weight_accum = 0.0; -#endif -#ifdef HORIZON_CLOSURE - context.closure_common.light_accum = vec4(0.0); - context.closure_common.weight_accum = 0.0; -#endif -} - -void horizon_scan_context_slice_start( - inout HorizonScanContextCommon context, vec3 vN, vec3 vV, vec3 vT, vec3 vB) -{ - context.bitmask = 0u; - context.weight_slice = 0.0; - context.light_slice = vec3(0.0); - horizon_scan_projected_normal_to_plane_angle_and_length( - vN, vV, vT, vB, context.N_length, context.N_angle); -} - -void horizon_scan_context_slice_start(inout HorizonScanContext context, vec3 vV, vec3 vT, vec3 vB) -{ -#ifdef HORIZON_OCCLUSION - horizon_scan_context_slice_start(context.occlusion_common, context.occlusion.N, vV, vT, vB); -#endif -#ifdef HORIZON_CLOSURE - horizon_scan_context_slice_start(context.closure_common, context.closure.N, vV, vT, vB); -#endif -} - -void horizon_scan_context_sample_finish(inout HorizonScanContextCommon context, - vec3 sample_radiance, - float sample_weight, - vec2 sample_theta, - float angle_bias) -{ - /* Angular bias shrinks the visibility bitmask around the projected normal. */ - sample_theta = (sample_theta - context.N_angle) * angle_bias; - uint sample_bitmask = horizon_scan_angles_to_bitmask(sample_theta); - sample_weight *= horizon_scan_bitmask_to_visibility_uniform(sample_bitmask & ~context.bitmask); - - context.weight_slice += sample_weight; - context.light_slice += sample_radiance * sample_weight; - context.bitmask |= sample_bitmask; -} - -void horizon_scan_context_sample_finish( - inout HorizonScanContext ctx, vec3 L, vec3 V, vec2 sample_uv, vec2 theta, float bias) -{ - vec3 sample_radiance = horizon_scan_sample_radiance(sample_uv); - /* Take emitter surface normal into consideration. */ - vec3 sample_normal = horizon_scan_sample_normal(sample_uv); - /* Discard back-facing samples. - * The paper suggests a smooth test which is not physically correct since we - * already consider the sample reflected radiance. - * Set the weight to allow energy conservation. If we modulate the radiance, we loose energy. */ - float weight = step(dot(sample_normal, L), 0.0); - -#ifdef HORIZON_OCCLUSION - horizon_scan_context_sample_finish(ctx.occlusion_common, sample_radiance, 1.0, theta, bias); -#endif -#ifdef HORIZON_CLOSURE - weight *= bsdf_lambert(ctx.closure.N, L); - horizon_scan_context_sample_finish(ctx.closure_common, sample_radiance, weight, theta, bias); -#endif -} - -void horizon_scan_context_slice_finish(inout HorizonScanContextCommon context) -{ - /* Use uniform visibility since this is what we use for near field lighting. - * Also the lighting we are going to mask is already containing the cosine lobe. */ - float slice_occlusion = horizon_scan_bitmask_to_visibility_uniform(~context.bitmask); - /* Normalize radiance since BxDF is applied when merging direct and indirect light. */ - context.light_slice *= safe_rcp(context.weight_slice) * (1.0 - slice_occlusion); - /* Correct normal not on plane (Eq. 8 of GTAO paper). */ - context.light_accum += vec4(context.light_slice, slice_occlusion) * context.N_length; - context.weight_accum += context.N_length; -} - -void horizon_scan_context_slice_finish(inout HorizonScanContext context) -{ -#ifdef HORIZON_OCCLUSION - float occlusion = horizon_scan_bitmask_to_occlusion_cosine(context.occlusion_common.bitmask); - context.occlusion_common.light_accum += vec4(occlusion) * context.occlusion_common.N_length; - context.occlusion_common.weight_accum += context.occlusion_common.N_length; -#endif -#ifdef HORIZON_CLOSURE - horizon_scan_context_slice_finish(context.closure_common); -#endif -} - -void horizon_scan_context_accumulation_finish(HorizonScanContextCommon context, out vec4 result) -{ - result = context.light_accum * safe_rcp(context.weight_accum); -} - -void horizon_scan_context_accumulation_finish(inout HorizonScanContext context) -{ -#ifdef HORIZON_OCCLUSION - horizon_scan_context_accumulation_finish(context.occlusion_common, context.occlusion_result); -#endif -#ifdef HORIZON_CLOSURE - horizon_scan_context_accumulation_finish(context.closure_common, context.closure_result); -#endif -} - /** * Returns the start and end point of a ray clipped to its intersection * with a sphere. @@ -209,27 +79,38 @@ void horizon_scan_occluder_intersection_ray_sphere_clip(Ray ray, P_exit = ray.origin + ray.direction * t_exit; } +struct HorizonScanResult { +#ifdef HORIZON_OCCLUSION + float result; +#endif +#ifdef HORIZON_CLOSURE + SphericalHarmonicL1 result; +#endif +}; + /** * Scans the horizon in many directions and returns the indirect lighting radiance. * Returned lighting is stored inside the context in `_accum` members already normalized. * If `reversed` is set to true, the input normal must be negated. */ -void horizon_scan_eval(vec3 vP, - inout HorizonScanContext context, - vec2 noise, - vec2 pixel_size, - float search_distance, - float global_thickness, - float angle_bias, - const int sample_count, - const bool reversed) +HorizonScanResult horizon_scan_eval(vec3 vP, + vec3 vN, + vec2 noise, + vec2 pixel_size, + float search_distance, + float global_thickness, + float angle_bias, + const int sample_count, + const bool reversed) { vec3 vV = drw_view_incident_vector(vP); const int slice_len = 2; vec2 v_dir = sample_circle(noise.x * (0.5 / float(slice_len))); - horizon_scan_context_accumulation_reset(context); + float weight_accum = 0.0; + float occlusion_accum = 0.0; + SphericalHarmonicL1 sh_accum = spherical_harmonics_L1_new(); for (int slice = 0; slice < slice_len; slice++) { #if 0 /* For debug purpose. For when slice_len is greater than 2. */ @@ -240,7 +121,18 @@ void horizon_scan_eval(vec3 vP, vec3 vB = normalize(cross(vV, vec3(v_dir, 0.0))); vec3 vT = cross(vB, vV); - horizon_scan_context_slice_start(context, vV, vT, vB); + /* Bitmask representing the occluded sectors on the slice. */ + uint slice_bitmask = 0u; + + /* Angle between vN and the horizon slice plane. */ + float vN_angle; + /* Length of vN projected onto the horizon slice plane. */ + float vN_length; + + horizon_scan_projected_normal_to_plane_angle_and_length(vN, vV, vT, vB, vN_length, vN_angle); + + SphericalHarmonicL1 sh_slice = spherical_harmonics_L1_new(); + float weight_slice; /* For both sides of the view vector. */ for (int side = 0; side < 2; side++) { @@ -306,15 +198,54 @@ void horizon_scan_eval(vec3 vP, /* If we are tracing backward, the angles are negative. Swizzle to keep correct order. */ theta = (side == 0) ? theta.xy : -theta.yx; - horizon_scan_context_sample_finish(context, vL_front, vV, sample_uv, theta, angle_bias); + vec3 sample_radiance = horizon_scan_sample_radiance(sample_uv); + /* Take emitter surface normal into consideration. */ + vec3 sample_normal = horizon_scan_sample_normal(sample_uv); + /* Discard back-facing samples. + * The 2 factor is to avoid loosing too much energy (which is something not + * explained in the paper...). Likely to be wrong, but we need a soft falloff. */ + float facing_weight = saturate(-dot(sample_normal, vL_front) * 2.0); + + float sample_weight = facing_weight * bsdf_lambert(vN, vL_front); + + /* Angular bias shrinks the visibility bitmask around the projected normal. */ + vec2 biased_theta = (theta - vN_angle) * angle_bias; + uint sample_bitmask = horizon_scan_angles_to_bitmask(biased_theta); + float weight_bitmask = horizon_scan_bitmask_to_visibility_uniform(sample_bitmask & + ~slice_bitmask); + + sample_radiance *= facing_weight * weight_bitmask; + /* Encoding using front sample direction gives better result than + * `normalize(vL_front + vL_back)` */ + spherical_harmonics_encode_signal_sample( + vL_front, vec4(sample_radiance, weight_bitmask), sh_slice); + + slice_bitmask |= sample_bitmask; } } - horizon_scan_context_slice_finish(context); + float occlusion_slice = horizon_scan_bitmask_to_occlusion_cosine(slice_bitmask); + + /* Correct normal not on plane (Eq. 8 of GTAO paper). */ + occlusion_accum += occlusion_slice * vN_length; + /* Use uniform visibility since this is what we use for near field lighting. */ + sh_accum = spherical_harmonics_madd(sh_slice, vN_length, sh_accum); + + weight_accum += vN_length; /* Rotate 90 degrees. */ v_dir = orthogonal(v_dir); } - horizon_scan_context_accumulation_finish(context); + float weight_rcp = safe_rcp(weight_accum); + + HorizonScanResult res; +#ifdef HORIZON_OCCLUSION + res.result = occlusion_accum * weight_rcp; +#endif +#ifdef HORIZON_CLOSURE + /* Weight by area of the sphere. This is expected for correct SH evaluation. */ + res.result = spherical_harmonics_mul(sh_accum, weight_rcp * 4.0 * M_PI); +#endif + return res; } diff --git a/source/blender/draw/engines/eevee_next/shaders/eevee_horizon_setup_comp.glsl b/source/blender/draw/engines/eevee_next/shaders/eevee_horizon_setup_comp.glsl index 1b64a80ca6d..439234fee29 100644 --- a/source/blender/draw/engines/eevee_next/shaders/eevee_horizon_setup_comp.glsl +++ b/source/blender/draw/engines/eevee_next/shaders/eevee_horizon_setup_comp.glsl @@ -16,8 +16,17 @@ void main() { ivec2 texel = ivec2(gl_GlobalInvocationID.xy); - ivec2 texel_fullres = texel * uniform_buf.raytrace.resolution_scale + - uniform_buf.raytrace.resolution_bias; + ivec2 texel_fullres = texel * uniform_buf.raytrace.horizon_resolution_scale + + uniform_buf.raytrace.horizon_resolution_bias; + + /* Return early for padding threads so we can use imageStoreFast. */ + if (any(greaterThanEqual(texel, imageSize(out_radiance_img).xy))) { + return; + } + + /* Avoid loading texels outside texture range. */ + ivec2 extent = textureSize(gbuf_header_tx, 0).xy; + texel_fullres = min(texel_fullres, extent - 1); /* Load Gbuffer. */ GBufferReader gbuf = gbuffer_read( @@ -25,12 +34,16 @@ void main() /* Export normal. */ vec3 N = gbuf.surface_N; + /* Background has invalid data. */ + /* FIXME: This is zero for opaque layer when we are processing the refraction layer. */ if (is_zero(N)) { /* Avoid NaN. But should be fixed in any case. */ N = vec3(1.0, 0.0, 0.0); } vec3 vN = drw_normal_world_to_view(N); - imageStore(out_normal_img, texel, vec4(vN * 0.5 + 0.5, 0.0)); + /* Tag processed pixel in the normal buffer for denoising speed. */ + bool is_processed = gbuf.header != 0u; + imageStore(out_normal_img, texel, vec4(vN * 0.5 + 0.5, float(is_processed))); /* Re-project radiance. */ vec2 uv = (vec2(texel_fullres) + 0.5) / vec2(textureSize(depth_tx, 0).xy); diff --git a/source/blender/draw/engines/eevee_next/shaders/eevee_ray_denoise_bilateral_comp.glsl b/source/blender/draw/engines/eevee_next/shaders/eevee_ray_denoise_bilateral_comp.glsl index 5800af70c82..e0c40ef2a8c 100644 --- a/source/blender/draw/engines/eevee_next/shaders/eevee_ray_denoise_bilateral_comp.glsl +++ b/source/blender/draw/engines/eevee_next/shaders/eevee_ray_denoise_bilateral_comp.glsl @@ -22,34 +22,7 @@ #pragma BLENDER_REQUIRE(eevee_sampling_lib.glsl) #pragma BLENDER_REQUIRE(eevee_gbuffer_lib.glsl) #pragma BLENDER_REQUIRE(eevee_closure_lib.glsl) - -float bilateral_depth_weight(vec3 center_N, vec3 center_P, vec3 sample_P) -{ - vec4 center_plane_eq = vec4(center_N, -dot(center_N, center_P)); - /* Only compare distance to the center plane formed by the normal. */ - float depth_delta = dot(center_plane_eq, vec4(sample_P, 1.0)); - /* TODO(fclem): Scene parameter. This is dependent on scene scale. */ - const float scale = 10000.0; - float weight = exp2(-scale * square(depth_delta)); - return weight; -} - -float bilateral_spatial_weight(float sigma, vec2 offset_from_center) -{ - /* From https://github.com/tranvansang/bilateral-filter/blob/master/fshader.frag */ - float fac = -1.0 / square(sigma); - /* Take two standard deviation. */ - fac *= 2.0; - float weight = exp2(fac * length_squared(offset_from_center)); - return weight; -} - -float bilateral_normal_weight(vec3 center_N, vec3 sample_N) -{ - float facing_ratio = dot(center_N, sample_N); - float weight = saturate(pow8f(facing_ratio)); - return weight; -} +#pragma BLENDER_REQUIRE(eevee_filter_lib.glsl) /* In order to remove some more fireflies, "tone-map" the color samples during the accumulation. */ vec3 to_accumulation_space(vec3 color) @@ -141,9 +114,12 @@ void main() continue; } - float depth_weight = bilateral_depth_weight(center_closure.N, center_P, sample_P); - float spatial_weight = bilateral_spatial_weight(filter_size, vec2(offset)); - float normal_weight = bilateral_normal_weight(center_closure.N, sample_closure.N); + float gauss = filter_gaussian_factor(filter_size, 1.5); + + /* TODO(fclem): Scene parameter. 10000.0 is dependent on scene scale. */ + float depth_weight = filter_planar_weight(center_closure.N, center_P, sample_P, 10000.0); + float spatial_weight = filter_gaussian_weight(gauss, length_squared(vec2(offset))); + float normal_weight = filter_angle_weight(center_closure.N, sample_closure.N); float weight = depth_weight * spatial_weight * normal_weight; accum_radiance += to_accumulation_space(radiance) * weight; diff --git a/source/blender/draw/engines/eevee_next/shaders/eevee_ray_tile_classify_comp.glsl b/source/blender/draw/engines/eevee_next/shaders/eevee_ray_tile_classify_comp.glsl index c35b7570dcd..478a22d7b4d 100644 --- a/source/blender/draw/engines/eevee_next/shaders/eevee_ray_tile_classify_comp.glsl +++ b/source/blender/draw/engines/eevee_next/shaders/eevee_ray_tile_classify_comp.glsl @@ -14,7 +14,7 @@ #pragma BLENDER_REQUIRE(eevee_closure_lib.glsl) shared uint tile_contains_ray_tracing[GBUFFER_LAYER_MAX]; -shared uint tile_contains_horizon_scan[GBUFFER_LAYER_MAX]; +shared uint tile_contains_horizon_scan; /* Returns a blend factor between different tracing method. */ float ray_roughness_factor(RayTraceData raytrace, float roughness) @@ -26,9 +26,9 @@ void main() { if (gl_LocalInvocationIndex == 0u) { /* Init shared variables. */ + tile_contains_horizon_scan = 0; for (int i = 0; i < GBUFFER_LAYER_MAX; i++) { tile_contains_ray_tracing[i] = 0; - tile_contains_horizon_scan[i] = 0; } } @@ -51,7 +51,7 @@ void main() /* We don't care about race condition here. */ if (ray_roughness_fac > 0.0) { - tile_contains_horizon_scan[i] = 1; + tile_contains_horizon_scan = 1; } if (ray_roughness_fac < 1.0) { tile_contains_ray_tracing[i] = 1; @@ -70,11 +70,12 @@ void main() imageStore(tile_raytrace_denoise_img, ivec3(denoise_tile_co, i), uvec4(1)); imageStore(tile_raytrace_tracing_img, ivec3(tracing_tile_co, i), uvec4(1)); } + } - if (tile_contains_horizon_scan[i] > 0) { - imageStore(tile_horizon_denoise_img, ivec3(denoise_tile_co, i), uvec4(1)); - imageStore(tile_horizon_tracing_img, ivec3(tracing_tile_co, i), uvec4(1)); - } + if (tile_contains_horizon_scan > 0) { + ivec2 tracing_tile_co = denoise_tile_co / uniform_buf.raytrace.horizon_resolution_scale; + imageStore(tile_horizon_denoise_img, ivec3(denoise_tile_co, 0), uvec4(1)); + imageStore(tile_horizon_tracing_img, ivec3(tracing_tile_co, 0), uvec4(1)); } } } diff --git a/source/blender/draw/engines/eevee_next/shaders/eevee_ray_tile_compact_comp.glsl b/source/blender/draw/engines/eevee_next/shaders/eevee_ray_tile_compact_comp.glsl index eb480976065..dde795c558a 100644 --- a/source/blender/draw/engines/eevee_next/shaders/eevee_ray_tile_compact_comp.glsl +++ b/source/blender/draw/engines/eevee_next/shaders/eevee_ray_tile_compact_comp.glsl @@ -19,13 +19,9 @@ void main() if (all(equal(tile, ivec2(0)))) { raytrace_tracing_dispatch_buf.num_groups_y = 1; raytrace_denoise_dispatch_buf.num_groups_y = 1; - horizon_tracing_dispatch_buf.num_groups_y = 1; - horizon_denoise_dispatch_buf.num_groups_y = 1; raytrace_tracing_dispatch_buf.num_groups_z = 1; raytrace_denoise_dispatch_buf.num_groups_z = 1; - horizon_tracing_dispatch_buf.num_groups_z = 1; - horizon_denoise_dispatch_buf.num_groups_z = 1; } if (!in_image_range(tile, tile_raytrace_tracing_img)) { @@ -34,8 +30,6 @@ void main() /* True if this tile is shooting and tracing rays. */ bool is_ray_tracing = imageLoad(tile_raytrace_tracing_img, ivec3(tile, closure_index)).r != 0; - /* True if this tile is using horizon scan. */ - bool is_horizon_tracing = imageLoad(tile_horizon_tracing_img, ivec3(tile, closure_index)).r != 0; /* True if an adjacent tile is ray tracing and will need this tile data for denoising. */ bool tile_is_ray_sampled = false; @@ -54,23 +48,6 @@ void main() } } - /* True if an adjacent tile is horizon tracing and will need this tile data for denoising. */ - bool tile_is_horizon_sampled = false; - /* Could be optimized if that becomes an issue (3x3 cross gather + 3x3 "X" shape scatter). */ - for (int x_tile = -1; x_tile <= 1; x_tile++) { - for (int y_tile = -1; y_tile <= 1; y_tile++) { - ivec2 tile_adj = tile + ivec2(x_tile, y_tile); - bool is_center_tile = (x_tile == 0 && y_tile == 0); - if (in_image_range(tile_adj, tile_horizon_tracing_img) && !is_center_tile) { - if (imageLoad(tile_horizon_tracing_img, ivec3(tile_adj, closure_index)).r != 0) { - /* This tile will sample the target tracing tile. Make sure it is cleared. */ - tile_is_horizon_sampled = true; - break; - } - } - } - } - /* TODO(fclem): we might want to dispatch another type of shader only for clearing. */ if (is_ray_tracing || tile_is_ray_sampled) { /* Dispatch trace resolution tracing tile. */ @@ -78,15 +55,7 @@ void main() raytrace_tracing_tiles_buf[tile_index] = packUvec2x16(uvec2(tile)); } - /* TODO(fclem): we might want to dispatch another type of shader only for clearing. */ - if (is_horizon_tracing || tile_is_horizon_sampled) { - /* Dispatch trace resolution tracing tile. */ - uint tile_index = atomicAdd(horizon_tracing_dispatch_buf.num_groups_x, 1u); - horizon_tracing_tiles_buf[tile_index] = packUvec2x16(uvec2(tile)); - } - /* Dispatch denoise tiles. */ - int resolution_scale = uniform_buf.raytrace.resolution_scale; if (is_ray_tracing) { for (int x_tile = 0; x_tile < resolution_scale; x_tile++) { for (int y_tile = 0; y_tile < resolution_scale; y_tile++) { @@ -100,17 +69,4 @@ void main() } } } - if (is_horizon_tracing) { - for (int x_tile = 0; x_tile < resolution_scale; x_tile++) { - for (int y_tile = 0; y_tile < resolution_scale; y_tile++) { - ivec2 tile_adj = tile * resolution_scale + ivec2(x_tile, y_tile); - if (in_image_range(tile_adj, tile_horizon_denoise_img)) { - if (imageLoad(tile_horizon_denoise_img, ivec3(tile_adj, closure_index)).r != 0) { - uint tile_index = atomicAdd(horizon_denoise_dispatch_buf.num_groups_x, 1u); - horizon_denoise_tiles_buf[tile_index] = packUvec2x16(uvec2(tile_adj)); - } - } - } - } - } } diff --git a/source/blender/draw/engines/eevee_next/shaders/eevee_spherical_harmonics_lib.glsl b/source/blender/draw/engines/eevee_next/shaders/eevee_spherical_harmonics_lib.glsl index 6f1e10305e4..f6f33e56233 100644 --- a/source/blender/draw/engines/eevee_next/shaders/eevee_spherical_harmonics_lib.glsl +++ b/source/blender/draw/engines/eevee_next/shaders/eevee_spherical_harmonics_lib.glsl @@ -102,6 +102,102 @@ struct SphericalHarmonicL2 { SphericalHarmonicBandL2 L2; }; +SphericalHarmonicBandL0 spherical_harmonics_band_L0_new() +{ + SphericalHarmonicBandL0 L0; + L0.M0 = vec4(0.0); + return L0; +} + +SphericalHarmonicBandL1 spherical_harmonics_band_L1_new() +{ + SphericalHarmonicBandL1 L1; + L1.Mn1 = vec4(0.0); + L1.M0 = vec4(0.0); + L1.Mp1 = vec4(0.0); + return L1; +} + +SphericalHarmonicBandL2 spherical_harmonics_band_L2_new() +{ + SphericalHarmonicBandL2 L2; + L2.Mn2 = vec4(0.0); + L2.Mn1 = vec4(0.0); + L2.M0 = vec4(0.0); + L2.Mp1 = vec4(0.0); + L2.Mp2 = vec4(0.0); + return L2; +} + +SphericalHarmonicL0 spherical_harmonics_L0_new() +{ + SphericalHarmonicL0 sh; + sh.L0 = spherical_harmonics_band_L0_new(); + return sh; +} + +SphericalHarmonicL1 spherical_harmonics_L1_new() +{ + SphericalHarmonicL1 sh; + sh.L0 = spherical_harmonics_band_L0_new(); + sh.L1 = spherical_harmonics_band_L1_new(); + return sh; +} + +SphericalHarmonicL2 spherical_harmonics_L2_new() +{ + SphericalHarmonicL2 sh; + sh.L0 = spherical_harmonics_band_L0_new(); + sh.L1 = spherical_harmonics_band_L1_new(); + sh.L2 = spherical_harmonics_band_L2_new(); + return sh; +} + +SphericalHarmonicBandL0 spherical_harmonics_band_L0_swizzle_wwww(SphericalHarmonicBandL0 L0) +{ + L0.M0 = L0.M0.wwww; + return L0; +} + +SphericalHarmonicBandL1 spherical_harmonics_band_L1_swizzle_wwww(SphericalHarmonicBandL1 L1) +{ + L1.Mn1 = L1.Mn1.wwww; + L1.M0 = L1.M0.wwww; + L1.Mp1 = L1.Mp1.wwww; + return L1; +} + +SphericalHarmonicBandL2 spherical_harmonics_band_L2_swizzle_wwww(SphericalHarmonicBandL2 L2) +{ + L2.Mn2 = L2.Mn2.wwww; + L2.Mn1 = L2.Mn1.wwww; + L2.M0 = L2.M0.wwww; + L2.Mp1 = L2.Mp1.wwww; + L2.Mp2 = L2.Mp2.wwww; + return L2; +} + +SphericalHarmonicL0 spherical_harmonics_swizzle_wwww(SphericalHarmonicL0 sh) +{ + sh.L0 = spherical_harmonics_band_L0_swizzle_wwww(sh.L0); + return sh; +} + +SphericalHarmonicL1 spherical_harmonics_swizzle_wwww(SphericalHarmonicL1 sh) +{ + sh.L0 = spherical_harmonics_band_L0_swizzle_wwww(sh.L0); + sh.L1 = spherical_harmonics_band_L1_swizzle_wwww(sh.L1); + return sh; +} + +SphericalHarmonicL2 spherical_harmonics_swizzle_wwww(SphericalHarmonicL2 sh) +{ + sh.L0 = spherical_harmonics_band_L0_swizzle_wwww(sh.L0); + sh.L1 = spherical_harmonics_band_L1_swizzle_wwww(sh.L1); + sh.L2 = spherical_harmonics_band_L2_swizzle_wwww(sh.L2); + return sh; +} + /** \} */ /* -------------------------------------------------------------------- */ @@ -581,3 +677,33 @@ vec4 spherical_harmonics_dot(SphericalHarmonicL1 a, SphericalHarmonicL1 b) } /** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Compression + * + * Described by Josh Hobson in "The indirect Lighting Pipeline of God of War" p. 120 + * \{ */ + +SphericalHarmonicL1 spherical_harmonics_compress(SphericalHarmonicL1 in_sh) +{ + SphericalHarmonicL1 out_sh; + out_sh.L0 = in_sh.L0; + vec4 fac = safe_rcp(in_sh.L0.M0 * M_SQRT3); + out_sh.L1.Mn1 = (in_sh.L1.Mn1 * fac) * 0.5 + 0.5; + out_sh.L1.M0 = (in_sh.L1.M0 * fac) * 0.5 + 0.5; + out_sh.L1.Mp1 = (in_sh.L1.Mp1 * fac) * 0.5 + 0.5; + return out_sh; +} + +SphericalHarmonicL1 spherical_harmonics_decompress(SphericalHarmonicL1 in_sh) +{ + SphericalHarmonicL1 out_sh; + out_sh.L0 = in_sh.L0; + vec4 fac = in_sh.L0.M0 * M_SQRT3; + out_sh.L1.Mn1 = (in_sh.L1.Mn1 * 2.0 - 1.0) * fac; + out_sh.L1.M0 = (in_sh.L1.M0 * 2.0 - 1.0) * fac; + out_sh.L1.Mp1 = (in_sh.L1.Mp1 * 2.0 - 1.0) * fac; + return out_sh; +} + +/** \} */ diff --git a/source/blender/draw/engines/eevee_next/shaders/infos/eevee_tracing_info.hh b/source/blender/draw/engines/eevee_next/shaders/infos/eevee_tracing_info.hh index a3997efd7d4..b4a16525f86 100644 --- a/source/blender/draw/engines/eevee_next/shaders/infos/eevee_tracing_info.hh +++ b/source/blender/draw/engines/eevee_next/shaders/infos/eevee_tracing_info.hh @@ -32,17 +32,12 @@ GPU_SHADER_CREATE_INFO(eevee_ray_tile_compact) .typedef_source("draw_shader_shared.h") .image_in(0, RAYTRACE_TILEMASK_FORMAT, ImageType::UINT_2D_ARRAY, "tile_raytrace_denoise_img") .image_in(1, RAYTRACE_TILEMASK_FORMAT, ImageType::UINT_2D_ARRAY, "tile_raytrace_tracing_img") - .image_in(2, RAYTRACE_TILEMASK_FORMAT, ImageType::UINT_2D_ARRAY, "tile_horizon_denoise_img") - .image_in(3, RAYTRACE_TILEMASK_FORMAT, ImageType::UINT_2D_ARRAY, "tile_horizon_tracing_img") .storage_buf(0, Qualifier::READ_WRITE, "DispatchCommand", "raytrace_tracing_dispatch_buf") .storage_buf(1, Qualifier::READ_WRITE, "DispatchCommand", "raytrace_denoise_dispatch_buf") - .storage_buf(2, Qualifier::READ_WRITE, "DispatchCommand", "horizon_tracing_dispatch_buf") - .storage_buf(3, Qualifier::READ_WRITE, "DispatchCommand", "horizon_denoise_dispatch_buf") .storage_buf(4, Qualifier::WRITE, "uint", "raytrace_tracing_tiles_buf[]") .storage_buf(5, Qualifier::WRITE, "uint", "raytrace_denoise_tiles_buf[]") - .storage_buf(6, Qualifier::WRITE, "uint", "horizon_tracing_tiles_buf[]") - .storage_buf(7, Qualifier::WRITE, "uint", "horizon_denoise_tiles_buf[]") .specialization_constant(Type::INT, "closure_index", 0) + .specialization_constant(Type::INT, "resolution_scale", 2) .compute_source("eevee_ray_tile_compact_comp.glsl"); GPU_SHADER_CREATE_INFO(eevee_ray_generate) @@ -188,7 +183,7 @@ GPU_SHADER_CREATE_INFO(eevee_horizon_setup) .sampler(0, ImageType::DEPTH_2D, "depth_tx") .sampler(1, ImageType::FLOAT_2D, "in_radiance_tx") .image(2, RAYTRACE_RADIANCE_FORMAT, Qualifier::WRITE, ImageType::FLOAT_2D, "out_radiance_img") - .image(3, GPU_RGBA8, Qualifier::WRITE, ImageType::FLOAT_2D, "out_normal_img") + .image(3, GPU_RGB10_A2, Qualifier::WRITE, ImageType::FLOAT_2D, "out_normal_img") .compute_source("eevee_horizon_setup_comp.glsl"); GPU_SHADER_CREATE_INFO(eevee_horizon_scan) @@ -203,14 +198,31 @@ GPU_SHADER_CREATE_INFO(eevee_horizon_scan) "draw_view") .sampler(0, ImageType::FLOAT_2D, "screen_radiance_tx") .sampler(1, ImageType::FLOAT_2D, "screen_normal_tx") - .image( - 2, RAYTRACE_RADIANCE_FORMAT, Qualifier::WRITE, ImageType::FLOAT_2D, "horizon_radiance_img") - .image(3, GPU_R8, Qualifier::WRITE, ImageType::FLOAT_2D, "horizon_occlusion_img") + .image(2, GPU_RGBA16F, Qualifier::WRITE, ImageType::FLOAT_2D, "horizon_radiance_0_img") + .image(3, GPU_RGBA8, Qualifier::WRITE, ImageType::FLOAT_2D, "horizon_radiance_1_img") + .image(4, GPU_RGBA8, Qualifier::WRITE, ImageType::FLOAT_2D, "horizon_radiance_2_img") + .image(5, GPU_RGBA8, Qualifier::WRITE, ImageType::FLOAT_2D, "horizon_radiance_3_img") .storage_buf(7, Qualifier::READ, "uint", "tiles_coord_buf[]") - .specialization_constant(Type::INT, "closure_index", 0) .compute_source("eevee_horizon_scan_comp.glsl"); GPU_SHADER_CREATE_INFO(eevee_horizon_denoise) + .do_static_compilation(true) + .local_group_size(RAYTRACE_GROUP_SIZE, RAYTRACE_GROUP_SIZE) + .additional_info( + "eevee_shared", "eevee_global_ubo", "eevee_sampling_data", "eevee_hiz_data", "draw_view") + .sampler(2, ImageType::FLOAT_2D, "in_sh_0_tx") + .sampler(4, ImageType::FLOAT_2D, "in_sh_1_tx") + .sampler(5, ImageType::FLOAT_2D, "in_sh_2_tx") + .sampler(6, ImageType::FLOAT_2D, "in_sh_3_tx") + .sampler(7, ImageType::FLOAT_2D, "screen_normal_tx") + .image(2, GPU_RGBA16F, Qualifier::WRITE, ImageType::FLOAT_2D, "out_sh_0_img") + .image(3, GPU_RGBA8, Qualifier::WRITE, ImageType::FLOAT_2D, "out_sh_1_img") + .image(4, GPU_RGBA8, Qualifier::WRITE, ImageType::FLOAT_2D, "out_sh_2_img") + .image(5, GPU_RGBA8, Qualifier::WRITE, ImageType::FLOAT_2D, "out_sh_3_img") + .storage_buf(7, Qualifier::READ, "uint", "tiles_coord_buf[]") + .compute_source("eevee_horizon_denoise_comp.glsl"); + +GPU_SHADER_CREATE_INFO(eevee_horizon_resolve) .do_static_compilation(true) .local_group_size(RAYTRACE_GROUP_SIZE, RAYTRACE_GROUP_SIZE) .additional_info("eevee_shared", @@ -220,14 +232,16 @@ GPU_SHADER_CREATE_INFO(eevee_horizon_denoise) "eevee_lightprobe_data", "draw_view") .sampler(1, ImageType::DEPTH_2D, "depth_tx") - .image( - 2, RAYTRACE_RADIANCE_FORMAT, Qualifier::READ, ImageType::FLOAT_2D, "horizon_radiance_img") - .image(3, GPU_R8, Qualifier::READ, ImageType::FLOAT_2D, "horizon_occlusion_img") - .image(4, RAYTRACE_RADIANCE_FORMAT, Qualifier::READ_WRITE, ImageType::FLOAT_2D, "radiance_img") - .image(6, RAYTRACE_TILEMASK_FORMAT, Qualifier::READ, ImageType::UINT_2D_ARRAY, "tile_mask_img") + .sampler(2, ImageType::FLOAT_2D, "horizon_radiance_0_tx") + .sampler(3, ImageType::FLOAT_2D, "horizon_radiance_1_tx") + .sampler(4, ImageType::FLOAT_2D, "horizon_radiance_2_tx") + .sampler(5, ImageType::FLOAT_2D, "horizon_radiance_3_tx") + .sampler(8, ImageType::FLOAT_2D, "screen_normal_tx") + .image(3, RAYTRACE_RADIANCE_FORMAT, Qualifier::READ_WRITE, ImageType::FLOAT_2D, "closure0_img") + .image(4, RAYTRACE_RADIANCE_FORMAT, Qualifier::READ_WRITE, ImageType::FLOAT_2D, "closure1_img") + .image(5, RAYTRACE_RADIANCE_FORMAT, Qualifier::READ_WRITE, ImageType::FLOAT_2D, "closure2_img") .storage_buf(7, Qualifier::READ, "uint", "tiles_coord_buf[]") - .specialization_constant(Type::INT, "closure_index", 0) - .compute_source("eevee_horizon_denoise_comp.glsl"); + .compute_source("eevee_horizon_resolve_comp.glsl"); #undef image_out #undef image_in diff --git a/source/blender/gpu/shaders/common/gpu_shader_math_vector_lib.glsl b/source/blender/gpu/shaders/common/gpu_shader_math_vector_lib.glsl index f538de374b8..8476dd5a9c2 100644 --- a/source/blender/gpu/shaders/common/gpu_shader_math_vector_lib.glsl +++ b/source/blender/gpu/shaders/common/gpu_shader_math_vector_lib.glsl @@ -176,6 +176,7 @@ vec3 orthogonal(vec3 v); * \note Returned vector is always rotated 90 degrees counter clock wise. */ vec2 orthogonal(vec2 v); +ivec2 orthogonal(ivec2 v); /** * Return true if the difference between`a` and `b` is below the `epsilon` value. @@ -613,6 +614,10 @@ vec2 orthogonal(vec2 v) { return vec2(-v.y, v.x); } +ivec2 orthogonal(ivec2 v) +{ + return ivec2(-v.y, v.x); +} bool is_equal(vec2 a, vec2 b, const float epsilon) { diff --git a/source/blender/makesdna/DNA_scene_defaults.h b/source/blender/makesdna/DNA_scene_defaults.h index 8cdd1d5098a..b6e52bf32c9 100644 --- a/source/blender/makesdna/DNA_scene_defaults.h +++ b/source/blender/makesdna/DNA_scene_defaults.h @@ -212,6 +212,7 @@ .gtao_quality = 0.25f, \ .gtao_thickness = 0.5f, \ .gtao_focus = 0.05f, \ + .gtao_resolution = 2, \ \ .bokeh_overblur = 5.0f, \ .bokeh_max_size = 100.0f, \ diff --git a/source/blender/makesdna/DNA_scene_types.h b/source/blender/makesdna/DNA_scene_types.h index 631d6c9da98..f87876706ea 100644 --- a/source/blender/makesdna/DNA_scene_types.h +++ b/source/blender/makesdna/DNA_scene_types.h @@ -1895,6 +1895,7 @@ typedef struct SceneEEVEE { float gtao_quality; float gtao_thickness; float gtao_focus; + int gtao_resolution; float bokeh_overblur; float bokeh_max_size; @@ -1923,7 +1924,6 @@ typedef struct SceneEEVEE { int shadow_ray_count; int shadow_step_count; float shadow_normal_bias; - float _pad0; int ray_tracing_method; diff --git a/source/blender/makesrna/intern/rna_scene.cc b/source/blender/makesrna/intern/rna_scene.cc index a622916d158..e2a79d62a62 100644 --- a/source/blender/makesrna/intern/rna_scene.cc +++ b/source/blender/makesrna/intern/rna_scene.cc @@ -7818,6 +7818,13 @@ static void rna_def_scene_eevee(BlenderRNA *brna) {0, nullptr, 0, nullptr, nullptr}, }; + static const EnumPropertyItem eevee_horizon_pixel_rate_items[] = { + {1, "1", 0, "1 px", ""}, + {2, "2", 0, "4 px", ""}, + {4, "4", 0, "16 px", ""}, + {0, nullptr, 0, nullptr, nullptr}, + }; + static const EnumPropertyItem ray_tracing_method_items[] = { {RAYTRACE_EEVEE_METHOD_NONE, "NONE", 0, "None", "No intersection with scene geometry"}, {RAYTRACE_EEVEE_METHOD_SCREEN, @@ -8173,6 +8180,16 @@ static void rna_def_scene_eevee(BlenderRNA *brna) RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY); RNA_def_property_update(prop, NC_SCENE | ND_RENDER_OPTIONS, nullptr); + prop = RNA_def_property(srna, "horizon_resolution", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_sdna(prop, nullptr, "gtao_resolution"); + RNA_def_property_enum_items(prop, eevee_horizon_pixel_rate_items); + RNA_def_property_ui_text(prop, + "Resolution", + "Control the quality of the horizon scan lighting " + "(lower size increase vram usage and quality)"); + RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY); + RNA_def_property_update(prop, NC_SCENE | ND_RENDER_OPTIONS, nullptr); + /* Depth of Field */ prop = RNA_def_property(srna, "bokeh_max_size", PROP_FLOAT, PROP_PIXEL);