EEVEE-Next: Add horizon scan to raytracing module #114259

Merged
Clément Foucault merged 51 commits from fclem/blender:eevee-next-horizon-gi into main 2023-11-21 16:24:23 +01:00
26 changed files with 945 additions and 128 deletions

View File

@ -615,6 +615,7 @@ class EeveeRaytracingScreenOption(RenderButtonsPanel, Panel):
layout.prop(props, "screen_trace_quality", text="Precision")
layout.prop(props, "screen_trace_thickness", text="Thickness")
layout.prop(props, "screen_trace_max_roughness", text="Max Roughness")
class EeveeRaytracingDenoisePanel(RenderButtonsPanel, Panel):

View File

@ -2504,5 +2504,15 @@ void blo_do_versions_400(FileData *fd, Library * /*lib*/, Main *bmain)
LISTBASE_FOREACH (Mesh *, mesh, &bmain->meshes) {
blender::bke::mesh_sculpt_mask_to_generic(*mesh);
}
if (!DNA_struct_member_exists(
fd->filesdna, "RaytraceEEVEE", "float", "screen_trace_max_roughness"))
{
LISTBASE_FOREACH (Scene *, scene, &bmain->scenes) {
scene->eevee.reflection_options.screen_trace_max_roughness = 0.5f;
scene->eevee.refraction_options.screen_trace_max_roughness = 0.5f;
scene->eevee.diffuse_options.screen_trace_max_roughness = 0.5f;
}
}
}
}

View File

@ -510,9 +510,12 @@ set(GLSL_SRC
engines/eevee_next/shaders/eevee_geom_world_vert.glsl
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_scan_eval_lib.glsl
engines/eevee_next/shaders/eevee_horizon_scan_comp.glsl
engines/eevee_next/shaders/eevee_horizon_scan_lib.glsl
engines/eevee_next/shaders/eevee_horizon_scan_test.glsl
engines/eevee_next/shaders/eevee_horizon_setup_comp.glsl
engines/eevee_next/shaders/eevee_light_culling_debug_frag.glsl
engines/eevee_next/shaders/eevee_light_culling_select_comp.glsl
engines/eevee_next/shaders/eevee_light_culling_sort_comp.glsl

View File

@ -103,7 +103,6 @@
/* Keep this as a define to avoid shader variations. */
#define RAYTRACE_RADIANCE_FORMAT GPU_R11F_G11F_B10F
#define RAYTRACE_RAYTIME_FORMAT GPU_R32F
#define RAYTRACE_HORIZON_FORMAT GPU_R32UI
#define RAYTRACE_VARIANCE_FORMAT GPU_R16F
#define RAYTRACE_TILEMASK_FORMAT GPU_R8UI

View File

@ -60,7 +60,9 @@ void RayTraceModule::sync()
pass.shader_set(inst_.shaders.static_shader_get(RAY_TILE_CLASSIFY));
pass.bind_image("tile_mask_img", &tile_mask_tx_);
pass.bind_ssbo("ray_dispatch_buf", &ray_dispatch_buf_);
pass.bind_ssbo("denoise_dispatch_buf", &denoise_dispatch_buf_);

Maybe not for this PR, but I think this function as a whole ( RayTraceModule::sync) could really benefit from comments explaining at a high level what each pass does and how they relate to each other.

Maybe not for this PR, but I think this function as a whole (` RayTraceModule::sync`) could really benefit from comments explaining at a high level what each pass does and how they relate to each other.
pass.bind_ssbo("ray_denoise_dispatch_buf", &ray_denoise_dispatch_buf_);
pass.bind_ssbo("horizon_dispatch_buf", &horizon_dispatch_buf_);
pass.bind_ssbo("horizon_denoise_dispatch_buf", &horizon_denoise_dispatch_buf_);
inst_.bind_uniform_data(&pass);
inst_.gbuffer.bind_resources(pass);
pass.dispatch(&tile_classify_dispatch_size_);
@ -72,9 +74,13 @@ void RayTraceModule::sync()
pass.shader_set(inst_.shaders.static_shader_get(RAY_TILE_COMPACT));
pass.bind_image("tile_mask_img", &tile_mask_tx_);
pass.bind_ssbo("ray_dispatch_buf", &ray_dispatch_buf_);
pass.bind_ssbo("denoise_dispatch_buf", &denoise_dispatch_buf_);
pass.bind_ssbo("ray_denoise_dispatch_buf", &ray_denoise_dispatch_buf_);
pass.bind_ssbo("ray_tiles_buf", &ray_tiles_buf_);
pass.bind_ssbo("denoise_tiles_buf", &denoise_tiles_buf_);
pass.bind_ssbo("ray_denoise_tiles_buf", &ray_denoise_tiles_buf_);
pass.bind_ssbo("horizon_dispatch_buf", &horizon_dispatch_buf_);
pass.bind_ssbo("horizon_denoise_dispatch_buf", &horizon_denoise_dispatch_buf_);
pass.bind_ssbo("horizon_tiles_buf", &horizon_tiles_buf_);
pass.bind_ssbo("horizon_denoise_tiles_buf", &horizon_denoise_tiles_buf_);
inst_.bind_uniform_data(&pass);
pass.dispatch(&tile_compact_dispatch_size_);
pass.barrier(GPU_BARRIER_SHADER_STORAGE);
@ -148,7 +154,7 @@ void RayTraceModule::sync()
PassSimple &pass = PASS_VARIATION(denoise_spatial_, type, _ps_);
pass.init();
pass.shader_set(inst_.shaders.static_shader_get(SHADER_VARIATION(RAY_DENOISE_SPATIAL_, type)));
pass.bind_ssbo("tiles_coord_buf", &denoise_tiles_buf_);
pass.bind_ssbo("tiles_coord_buf", &ray_denoise_tiles_buf_);
pass.bind_texture(RBUFS_UTILITY_TEX_SLOT, inst_.pipelines.utility_tx);
pass.bind_texture("depth_tx", &depth_tx);
pass.bind_image("ray_data_img", &ray_data_tx_);
@ -161,7 +167,7 @@ void RayTraceModule::sync()
inst_.bind_uniform_data(&pass);
inst_.sampling.bind_resources(pass);
inst_.gbuffer.bind_resources(pass);
pass.dispatch(denoise_dispatch_buf_);
pass.dispatch(ray_denoise_dispatch_buf_);
pass.barrier(GPU_BARRIER_SHADER_IMAGE_ACCESS);
}
{
@ -178,9 +184,9 @@ void RayTraceModule::sync()
pass.bind_image("out_radiance_img", &denoised_temporal_tx_);
pass.bind_image("in_variance_img", &hit_variance_tx_);
pass.bind_image("out_variance_img", &denoise_variance_tx_);
pass.bind_ssbo("tiles_coord_buf", &denoise_tiles_buf_);
pass.bind_ssbo("tiles_coord_buf", &ray_denoise_tiles_buf_);
inst_.sampling.bind_resources(pass);
pass.dispatch(denoise_dispatch_buf_);
pass.dispatch(ray_denoise_dispatch_buf_);
pass.barrier(GPU_BARRIER_SHADER_IMAGE_ACCESS);
}
for (auto type : IndexRange(3)) {
@ -193,14 +199,63 @@ void RayTraceModule::sync()
pass.bind_image("out_radiance_img", &denoised_bilateral_tx_);
pass.bind_image("in_variance_img", &denoise_variance_tx_);
pass.bind_image("tile_mask_img", &tile_mask_tx_);
pass.bind_ssbo("tiles_coord_buf", &denoise_tiles_buf_);
pass.bind_ssbo("tiles_coord_buf", &ray_denoise_tiles_buf_);
inst_.bind_uniform_data(&pass);
inst_.sampling.bind_resources(pass);
inst_.gbuffer.bind_resources(pass);
pass.dispatch(denoise_dispatch_buf_);
pass.dispatch(ray_denoise_dispatch_buf_);
pass.barrier(GPU_BARRIER_SHADER_IMAGE_ACCESS);
}
{
PassSimple &pass = horizon_setup_ps_;
pass.init();
pass.shader_set(inst_.shaders.static_shader_get(HORIZON_SETUP));
inst_.bind_uniform_data(&pass);
pass.bind_texture("depth_tx", &depth_tx);
pass.bind_texture("in_radiance_tx", &screen_radiance_tx_, GPUSamplerState::default_sampler());
pass.bind_image("out_radiance_img", &downsampled_in_radiance_tx_);
pass.bind_image("out_normal_img", &downsampled_in_normal_tx_);
inst_.bind_uniform_data(&pass);
inst_.gbuffer.bind_resources(pass);
pass.dispatch(&tracing_dispatch_size_);
pass.barrier(GPU_BARRIER_SHADER_IMAGE_ACCESS);
}
for (auto type : IndexRange(3)) {
PassSimple &pass = PASS_VARIATION(horizon_scan_, type, _ps_);
pass.init();
pass.shader_set(inst_.shaders.static_shader_get(SHADER_VARIATION(HORIZON_SCAN_, type)));
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_tiles_buf_);
pass.bind_texture("screen_radiance_tx", &downsampled_in_radiance_tx_);
pass.bind_texture("screen_normal_tx", &downsampled_in_normal_tx_);
pass.bind_texture(RBUFS_UTILITY_TEX_SLOT, inst_.pipelines.utility_tx);
inst_.bind_uniform_data(&pass);
inst_.hiz_buffer.bind_resources(pass);
inst_.sampling.bind_resources(pass);
inst_.gbuffer.bind_resources(pass);
pass.dispatch(horizon_dispatch_buf_);
pass.barrier(GPU_BARRIER_SHADER_IMAGE_ACCESS);
}
{
PassSimple &pass = horizon_denoise_ps_;
pass.init();
pass.shader_set(inst_.shaders.static_shader_get(HORIZON_DENOISE));
inst_.bind_uniform_data(&pass);
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_mask_tx_);
pass.bind_ssbo("tiles_coord_buf", &horizon_denoise_tiles_buf_);
inst_.bind_uniform_data(&pass);
inst_.sampling.bind_resources(pass);
inst_.gbuffer.bind_resources(pass);
inst_.irradiance_cache.bind_resources(pass);
inst_.reflection_probes.bind_resources(pass);
pass.dispatch(horizon_denoise_dispatch_buf_);
pass.barrier(GPU_BARRIER_SHADER_IMAGE_ACCESS);
}
#undef SHADER_VARIATION
#undef PASS_VARIATION
}
@ -236,6 +291,7 @@ RayTraceResult RayTraceModule::trace(RayTraceBuffer &rt_buffer,
PassSimple *trace_ray_ps = nullptr;
PassSimple *denoise_spatial_ps = nullptr;
PassSimple *denoise_bilateral_ps = nullptr;
PassSimple *horizon_scan_ps = nullptr;
RayTraceBuffer::DenoiseBuffer *denoise_buf = nullptr;
if (raytrace_closure == CLOSURE_DIFFUSE) {
@ -245,6 +301,7 @@ RayTraceResult RayTraceModule::trace(RayTraceBuffer &rt_buffer,
denoise_spatial_ps = &denoise_spatial_diffuse_ps_;
denoise_bilateral_ps = &denoise_bilateral_diffuse_ps_;
denoise_buf = &rt_buffer.diffuse;
horizon_scan_ps = &horizon_scan_diffuse_ps_;
}
else if (raytrace_closure == CLOSURE_REFLECTION) {
options = reflection_options_;
@ -253,6 +310,7 @@ RayTraceResult RayTraceModule::trace(RayTraceBuffer &rt_buffer,
denoise_spatial_ps = &denoise_spatial_reflect_ps_;
denoise_bilateral_ps = &denoise_bilateral_reflect_ps_;
denoise_buf = &rt_buffer.reflection;
horizon_scan_ps = &horizon_scan_reflect_ps_;
}
else if (raytrace_closure == CLOSURE_REFRACTION) {
options = refraction_options_;
@ -261,6 +319,7 @@ RayTraceResult RayTraceModule::trace(RayTraceBuffer &rt_buffer,
denoise_spatial_ps = &denoise_spatial_refract_ps_;
denoise_bilateral_ps = &denoise_bilateral_refract_ps_;
denoise_buf = &rt_buffer.refraction;
horizon_scan_ps = &horizon_scan_refract_ps_;
}
if ((active_closures & raytrace_closure) == 0) {
@ -278,6 +337,8 @@ RayTraceResult RayTraceModule::trace(RayTraceBuffer &rt_buffer,
const int2 tracing_res = math::divide_ceil(extent, int2(resolution_scale));
const int2 dummy_extent(1, 1);
tracing_dispatch_size_ = int3(math::divide_ceil(tracing_res, int2(RAYTRACE_GROUP_SIZE)), 1);
tile_classify_dispatch_size_ = int3(math::divide_ceil(extent, int2(RAYTRACE_GROUP_SIZE)), 1);
const int denoise_tile_count = tile_classify_dispatch_size_.x * tile_classify_dispatch_size_.y;
const int2 tile_mask_extent = tile_classify_dispatch_size_.xy();
@ -289,20 +350,25 @@ RayTraceResult RayTraceModule::trace(RayTraceBuffer &rt_buffer,
renderbuf_stencil_view_ = inst_.render_buffers.depth_tx.stencil_view();
renderbuf_depth_view_ = inst_.render_buffers.depth_tx;
bool use_denoise = (options.flag & RAYTRACE_EEVEE_USE_DENOISE);
bool use_spatial_denoise = (options.denoise_stages & RAYTRACE_EEVEE_DENOISE_SPATIAL) &&
use_denoise;
bool use_temporal_denoise = (options.denoise_stages & RAYTRACE_EEVEE_DENOISE_TEMPORAL) &&
use_spatial_denoise;
bool use_bilateral_denoise = (options.denoise_stages & RAYTRACE_EEVEE_DENOISE_BILATERAL) &&
use_temporal_denoise;
const bool use_denoise = (options.flag & RAYTRACE_EEVEE_USE_DENOISE);
const bool use_spatial_denoise = (options.denoise_stages & RAYTRACE_EEVEE_DENOISE_SPATIAL) &&
use_denoise;
const bool use_temporal_denoise = (options.denoise_stages & RAYTRACE_EEVEE_DENOISE_TEMPORAL) &&
use_spatial_denoise;
const bool use_bilateral_denoise = (options.denoise_stages & RAYTRACE_EEVEE_DENOISE_BILATERAL) &&
use_temporal_denoise;
const bool use_horizon_scan = true;
DRW_stats_group_start("Raytracing");
data_.thickness = options.screen_trace_thickness;
data_.quality = 1.0f - 0.95f * options.screen_trace_quality;
data_.brightness_clamp = (options.sample_clamp > 0.0) ? options.sample_clamp : 1e20;
data_.max_trace_roughness = 1.0f;
float roughness_mask_start = options.screen_trace_max_roughness;
float roughness_mask_fade = 0.2f;
data_.roughness_mask_scale = 1.0 / roughness_mask_fade;
data_.roughness_mask_bias = data_.roughness_mask_scale * roughness_mask_start;
data_.resolution_scale = resolution_scale;
data_.closure_active = raytrace_closure;
@ -315,8 +381,10 @@ RayTraceResult RayTraceModule::trace(RayTraceBuffer &rt_buffer,
inst_.push_uniform_data();
tile_mask_tx_.acquire(tile_mask_extent, RAYTRACE_TILEMASK_FORMAT);
denoise_tiles_buf_.resize(ceil_to_multiple_u(denoise_tile_count, 512));
horizon_tiles_buf_.resize(ceil_to_multiple_u(ray_tile_count, 512));
horizon_denoise_tiles_buf_.resize(ceil_to_multiple_u(denoise_tile_count, 512));
ray_tiles_buf_.resize(ceil_to_multiple_u(ray_tile_count, 512));
ray_denoise_tiles_buf_.resize(ceil_to_multiple_u(denoise_tile_count, 512));
/* Ray setup. */
inst_.manager->submit(tile_classify_ps_);
@ -372,8 +440,6 @@ RayTraceResult RayTraceModule::trace(RayTraceBuffer &rt_buffer,
inst_.manager->submit(denoise_temporal_ps_, render_view);
/* Swap after last use. */
TextureFromPool::swap(tile_mask_tx_, denoise_buf->tilemask_history_tx);
/* Save view-projection matrix for next reprojection. */
denoise_buf->history_persmat = main_view.persmat();
/* Radiance will be swapped with history in #RayTraceResult::release().
@ -393,13 +459,10 @@ RayTraceResult RayTraceModule::trace(RayTraceBuffer &rt_buffer,
if (use_bilateral_denoise) {
denoise_buf->denoised_bilateral_tx.acquire(extent, RAYTRACE_RADIANCE_FORMAT);
denoised_bilateral_tx_ = denoise_buf->denoised_bilateral_tx;
/* Swap back for one last use. */
TextureFromPool::swap(tile_mask_tx_, denoise_buf->tilemask_history_tx);
inst_.manager->submit(*denoise_bilateral_ps, render_view);
/* Swap after last use. */
TextureFromPool::swap(tile_mask_tx_, denoise_buf->tilemask_history_tx);
TextureFromPool::swap(denoise_buf->denoised_temporal_tx, denoise_buf->radiance_history_tx);
TextureFromPool::swap(denoise_variance_tx_, denoise_buf->variance_history_tx);
@ -408,9 +471,37 @@ RayTraceResult RayTraceModule::trace(RayTraceBuffer &rt_buffer,
denoise_buf->denoised_temporal_tx.release();
}
tile_mask_tx_.release();
denoise_variance_tx_.release();
if (use_horizon_scan) {
downsampled_in_radiance_tx_.acquire(tracing_res, RAYTRACE_RADIANCE_FORMAT);
downsampled_in_normal_tx_.acquire(tracing_res, GPU_RGBA8);
inst_.manager->submit(horizon_setup_ps_, render_view);
horizon_occlusion_tx_.acquire(tracing_res, GPU_R8);
horizon_radiance_tx_.acquire(tracing_res, RAYTRACE_RADIANCE_FORMAT);
inst_.manager->submit(*horizon_scan_ps, render_view);
downsampled_in_radiance_tx_.release();
downsampled_in_normal_tx_.release();
horizon_scan_output_tx_ = result.get();
inst_.manager->submit(horizon_denoise_ps_, render_view);
horizon_occlusion_tx_.release();
horizon_radiance_tx_.release();
}
tile_mask_tx_.release();
if (tile_mask_tx_.is_valid()) {
/* Swap after last use. */
TextureFromPool::swap(tile_mask_tx_, denoise_buf->tilemask_history_tx);
}
DRW_stats_group_end();
return result;

View File

@ -117,26 +117,49 @@ class RayTraceModule {
draw::PassSimple denoise_bilateral_diffuse_ps_ = {"DenoiseBilateral.Diffuse"};
draw::PassSimple denoise_bilateral_reflect_ps_ = {"DenoiseBilateral.Reflection"};
draw::PassSimple denoise_bilateral_refract_ps_ = {"DenoiseBilateral.Refraction"};
draw::PassSimple horizon_setup_ps_ = {"HorizonScan.Setup"};
draw::PassSimple horizon_scan_diffuse_ps_ = {"HorizonScan.Diffuse"};
draw::PassSimple horizon_scan_reflect_ps_ = {"HorizonScan.Reflection"};
draw::PassSimple horizon_scan_refract_ps_ = {"HorizonScan.Refraction"};
draw::PassSimple horizon_denoise_ps_ = {"HorizonScan.Denoise"};
/** 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);
/** Dispatch with enough tiles for the tracing resolution. */
int3 tracing_dispatch_size_ = int3(1);
/** 2D tile mask to check which unused adjacent tile we need to clear. */
TextureFromPool tile_mask_tx_ = {"tile_mask_tx"};
/** Indirect dispatch rays. Avoid dispatching work-groups that will not trace anything.*/
DispatchIndirectBuf ray_dispatch_buf_ = {"ray_dispatch_buf_"};
/** Indirect dispatch denoise full-resolution tiles. */
DispatchIndirectBuf denoise_dispatch_buf_ = {"denoise_dispatch_buf_"};
DispatchIndirectBuf ray_denoise_dispatch_buf_ = {"ray_denoise_dispatch_buf_"};
/** Indirect dispatch horizon scan. Avoid dispatching work-groups that will not scan anything.*/
DispatchIndirectBuf horizon_dispatch_buf_ = {"horizon_dispatch_buf_"};
/** 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;
/** Tile buffer that contains tile coordinates. */
RayTraceTileBuf ray_tiles_buf_ = {"ray_tiles_buf_"};
RayTraceTileBuf denoise_tiles_buf_ = {"denoise_tiles_buf_"};
RayTraceTileBuf ray_denoise_tiles_buf_ = {"ray_denoise_tiles_buf_"};
RayTraceTileBuf horizon_tiles_buf_ = {"horizon_tiles_buf_"};
RayTraceTileBuf horizon_denoise_tiles_buf_ = {"horizon_denoise_tiles_buf_"};
/** Texture containing the ray direction and PDF. */
TextureFromPool ray_data_tx_ = {"ray_data_tx"};
/** Texture containing the ray hit time. */
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_"};
/** 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. */
TextureFromPool downsampled_in_normal_tx_ = {"downsampled_in_normal_tx_"};
/** Textures containing the ray hit radiance denoised (full-res). One of them is result_tx. */
GPUTexture *denoised_spatial_tx_ = nullptr;
GPUTexture *denoised_temporal_tx_ = nullptr;

View File

@ -106,6 +106,16 @@ const char *ShaderModule::static_shader_create_info_name_get(eShaderType shader_
return "eevee_hiz_update";
case HIZ_UPDATE_LAYER:
return "eevee_hiz_update_layer";
case HORIZON_DENOISE:
return "eevee_horizon_denoise";
case HORIZON_SCAN_DIFFUSE:
return "eevee_horizon_scan_diffuse";
case HORIZON_SCAN_REFLECT:
return "eevee_horizon_scan_reflect";
case HORIZON_SCAN_REFRACT:
return "eevee_horizon_scan_refract";
case HORIZON_SETUP:
return "eevee_horizon_setup";
case MOTION_BLUR_GATHER:
return "eevee_motion_blur_gather";
case MOTION_BLUR_TILE_DILATE:

View File

@ -66,6 +66,12 @@ enum eShaderType {
HIZ_UPDATE_LAYER,
HIZ_DEBUG,
HORIZON_DENOISE,
HORIZON_SCAN_DIFFUSE,
HORIZON_SCAN_REFLECT,
HORIZON_SCAN_REFRACT,
HORIZON_SETUP,
LIGHT_CULLING_DEBUG,
LIGHT_CULLING_SELECT,
LIGHT_CULLING_SORT,

View File

@ -1200,14 +1200,14 @@ struct RayTraceData {
/** Maximum brightness during lighting evaluation. */
float brightness_clamp;
/** Maximum roughness for which we will trace a ray. */
float max_trace_roughness;
float roughness_mask_scale;
float roughness_mask_bias;
/** If set to true will bypass spatial denoising. */
bool1 skip_denoise;
/** Closure being ray-traced. */
eClosureBits closure_active;
int _pad0;
int _pad1;
int _pad2;
};
BLI_STATIC_ASSERT_ALIGN(RayTraceData, 16)

View File

@ -29,21 +29,24 @@ void main()
vec2 noise;
noise.x = interlieved_gradient_noise(vec2(texel), 3.0, 0.0);
noise.y = utility_tx_fetch(utility_tx, texel, UTIL_BLUE_NOISE_LAYER).r;
noise.y = utility_tx_fetch(utility_tx, vec2(texel), UTIL_BLUE_NOISE_LAYER).r;
noise = fract(noise + sampling_rng_2D_get(SAMPLING_AO_U));
vec3 ambient_occlusion = horizon_scan_eval(vP,
vN,
hiz_tx,
noise,
uniform_buf.ao.pixel_size,
uniform_buf.ao.distance,
uniform_buf.ao.thickness,
uniform_buf.ao.angle_bias,
10);
ClosureOcclusion occlusion;
occlusion.N = vN;
/* We can have some float imprecision because of the weighted accumulation. */
ambient_occlusion = saturate(ambient_occlusion * 1.02);
HorizonScanContext ctx;
ctx.occlusion = occlusion;
imageStore(out_ao_img, ivec3(texel, out_ao_img_layer_index), saturate(ambient_occlusion.rrrr));
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);
imageStore(
out_ao_img, ivec3(texel, out_ao_img_layer_index), vec4(saturate(ctx.occlusion_result.r)));
}

View File

@ -0,0 +1,188 @@
/* 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)
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;
}
/* 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)
{
GBufferData gbuf = gbuffer_read(gbuf_header_tx, gbuf_closure_tx, gbuf_color_tx, texel);
/* TODO(fclem): Load preprocessed Normal. */
vec3 N = vec3(0.0);
if (gbuf.has_diffuse) {
N = gbuf.diffuse.N;
}
if (gbuf.has_reflection) {
N = gbuf.reflection.N;
}
if (gbuf.has_refraction) {
N = gbuf.refraction.N;
}
return N;
}
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 = (texel_fullres) / uniform_buf.raytrace.resolution_scale;
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;
vec3 center_P = drw_point_screen_to_world(vec3(center_uv, center_depth));
if (center_depth == 1.0) {
/* Do not trace for background */
return;
}
GBufferData gbuf = gbuffer_read(gbuf_header_tx, gbuf_closure_tx, gbuf_color_tx, texel_fullres);
uint closure_bits = texelFetch(gbuf_header_tx, texel_fullres, 0).r;
if (!flag_test(closure_bits, uniform_buf.raytrace.closure_active)) {
return;
}
vec3 center_N = gbuf.diffuse.N;
float roughness = 1.0;
if (uniform_buf.raytrace.closure_active == eClosureBits(CLOSURE_REFLECTION)) {
roughness = gbuf.reflection.roughness;
center_N = gbuf.reflection.N;
}
if (uniform_buf.raytrace.closure_active == eClosureBits(CLOSURE_REFRACTION)) {
roughness = 1.0; /* TODO(fclem): Apparent roughness. */
center_N = gbuf.refraction.N;
}
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;
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;
ivec2 sample_tile = sample_texel_fullres / RAYTRACE_GROUP_SIZE;
/* Make sure the sample has been processed and do not contain garbage data. */
uint tile_mask = imageLoad(tile_mask_img, sample_tile).r;
bool unprocessed_tile = !flag_test(tile_mask, 1u << 1u);
if (unprocessed_tile) {
continue;
}
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));
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;
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);
}

View File

@ -0,0 +1,98 @@
/* 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_horizon_scan_eval_lib.glsl)
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 = texel * uniform_buf.raytrace.resolution_scale +
uniform_buf.raytrace.resolution_bias;
ivec2 extent = textureSize(gbuf_header_tx, 0).xy;
if (any(greaterThanEqual(texel_fullres, extent))) {
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;
}
GBufferData gbuf = gbuffer_read(gbuf_header_tx, gbuf_closure_tx, gbuf_color_tx, texel_fullres);
HorizonScanContext ctx;
#ifdef HORIZON_DIFFUSE
if (gbuf.has_diffuse == false) {
imageStore(horizon_radiance_img, texel, vec4(0.0));
return;
}
vec3 Ng = gbuf.diffuse.N;
ctx.diffuse = gbuf.diffuse;
ctx.diffuse.N = drw_normal_world_to_view(ctx.diffuse.N);
#endif
#ifdef HORIZON_REFLECT
if (gbuf.has_reflection == false) {
imageStore(horizon_radiance_img, texel, vec4(0.0));
return;
}
vec3 Ng = gbuf.reflection.N;
ctx.reflection = gbuf.reflection;
ctx.reflection.roughness = max(ctx.reflection.roughness, 0.1);
ctx.reflection.N = drw_normal_world_to_view(ctx.reflection.N);
#endif
#ifdef HORIZON_REFRACT
if (gbuf.has_refraction == false) {
imageStore(horizon_radiance_img, texel, vec4(0.0));
return;
}
vec3 Ng = gbuf.refraction.N;
ctx.refraction = gbuf.refraction;
ctx.refraction.N = drw_normal_world_to_view(ctx.refraction.N);
#endif
vec3 vP = drw_point_screen_to_view(vec3(uv, depth));
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);
float occlusion = 0.0;
vec4 radiance = vec4(1.0, 0.0, 1.0, 1.0);
#ifdef HORIZON_DIFFUSE
radiance.rgb = ctx.diffuse_result.rgb;
occlusion = ctx.diffuse_result.a;
#endif
#ifdef HORIZON_REFLECT
radiance.rgb = ctx.reflection_result.rgb;
occlusion = ctx.reflection_result.a;
#endif
#ifdef HORIZON_REFRACT
radiance.rgb = ctx.refraction_result.rgb;
occlusion = ctx.refraction_result.a;
#endif
imageStore(horizon_radiance_img, texel, radiance);
imageStore(horizon_occlusion_img, texel, vec4(occlusion));
}

View File

@ -8,12 +8,240 @@
* This mostly follows the paper:
* "Screen Space Indirect Lighting with Visibility Bitmask"
* by Olivier Therrien, Yannick Levesque, Guillaume Gilet
*
* Expects `screen_radiance_tx` and `screen_normal_tx` to be bound if `HORIZON_OCCLUSION` is not
* defined.
*/
#pragma BLENDER_REQUIRE(common_shape_lib.glsl)
#pragma BLENDER_REQUIRE(draw_view_lib.glsl)
#pragma BLENDER_REQUIRE(eevee_sampling_lib.glsl)
#pragma BLENDER_REQUIRE(eevee_horizon_scan_lib.glsl)
#pragma BLENDER_REQUIRE(eevee_ray_types_lib.glsl)
#pragma BLENDER_REQUIRE(gpu_shader_codegen_lib.glsl)
#pragma BLENDER_REQUIRE(eevee_bxdf_lib.glsl)
#pragma BLENDER_REQUIRE(eevee_spherical_harmonics_lib.glsl)
#ifdef RAYTRACE_DIFFUSE
# define HORIZON_DIFFUSE
#endif
#ifdef RAYTRACE_REFLECT
# define HORIZON_REFLECT
#endif
#ifdef RAYTRACE_REFRACT
# define HORIZON_REFRACT
#endif
vec3 horizon_scan_sample_radiance(vec2 uv)
{
#ifndef HORIZON_OCCLUSION
return texture(screen_radiance_tx, uv).rgb;
#else
return vec3(0.0);
#endif
}
vec3 horizon_scan_sample_normal(vec2 uv)
{
#ifndef HORIZON_OCCLUSION
return texture(screen_normal_tx, uv).rgb * 2.0 - 1.0;
#else
return vec3(0.0);
#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_DIFFUSE
ClosureDiffuse diffuse;
HorizonScanContextCommon diffuse_common;
vec4 diffuse_result;
#endif
#ifdef HORIZON_REFLECT
ClosureReflection reflection;
HorizonScanContextCommon reflection_common;
vec4 reflection_result;
#endif
#ifdef HORIZON_REFRACT
ClosureRefraction refraction;
HorizonScanContextCommon refraction_common;
vec4 refraction_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_DIFFUSE
context.diffuse_common.light_accum = vec4(0.0);
context.diffuse_common.weight_accum = 0.0;
#endif
#ifdef HORIZON_REFLECT
context.reflection_common.light_accum = vec4(0.0);
context.reflection_common.weight_accum = 0.0;
#endif
#ifdef HORIZON_REFRACT
context.refraction_common.light_accum = vec4(0.0);
context.refraction_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_DIFFUSE
horizon_scan_context_slice_start(context.diffuse_common, context.diffuse.N, vV, vT, vB);
#endif
#ifdef HORIZON_REFLECT
horizon_scan_context_slice_start(context.reflection_common, context.reflection.N, vV, vT, vB);
#endif
#ifdef HORIZON_REFRACT
horizon_scan_context_slice_start(context.refraction_common, context.refraction.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;
}
float bxdf_eval(ClosureDiffuse closure, vec3 L, vec3 V)
{
return bsdf_lambert(closure.N, L);
}
float bxdf_eval(ClosureReflection closure, vec3 L, vec3 V)
{
return bsdf_ggx(closure.N, L, V, closure.roughness);
}
float bxdf_eval(ClosureRefraction closure, vec3 L, vec3 V)
{
return btdf_ggx(closure.N, L, V, closure.roughness, closure.ior);
}
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 backfacing 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_DIFFUSE
weight = bxdf_eval(ctx.diffuse, L, V);
horizon_scan_context_sample_finish(ctx.diffuse_common, sample_radiance, weight, theta, bias);
#endif
#ifdef HORIZON_REFLECT
weight = bxdf_eval(ctx.reflection, L, V);
horizon_scan_context_sample_finish(ctx.reflection_common, sample_radiance, weight, theta, bias);
#endif
#ifdef HORIZON_REFRACT
/* TODO(fclem): Broken: Black. */
weight = bxdf_eval(ctx.refraction, L, V);
horizon_scan_context_sample_finish(ctx.refraction_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);
/* Replace light by occlusion. Should eliminate any reference to the radiance texture fetch */
context.occlusion_common.light_slice = vec3(occlusion);
horizon_scan_context_slice_finish(context.occlusion_common);
#endif
#ifdef HORIZON_DIFFUSE
horizon_scan_context_slice_finish(context.diffuse_common);
#endif
#ifdef HORIZON_REFLECT
horizon_scan_context_slice_finish(context.reflection_common);
#endif
#ifdef HORIZON_REFRACT
horizon_scan_context_slice_finish(context.refraction_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_DIFFUSE
horizon_scan_context_accumulation_finish(context.diffuse_common, context.diffuse_result);
#endif
#ifdef HORIZON_REFLECT
horizon_scan_context_accumulation_finish(context.reflection_common, context.reflection_result);
#endif
#ifdef HORIZON_REFRACT
horizon_scan_context_accumulation_finish(context.refraction_common, context.refraction_result);
#endif
}
/**
* Returns the start and end point of a ray clipped to its intersection
@ -47,11 +275,10 @@ void horizon_scan_occluder_intersection_ray_sphere_clip(Ray ray,
/**
* Scans the horizon in many directions and returns the indirect lighting radiance.
* Returned lighting depends on configuration.
* Returned lighting is stored inside the context in `_accum` members already normalized.
*/
vec3 horizon_scan_eval(vec3 vP,
vec3 vN,
sampler2D depth_tx,
void horizon_scan_eval(vec3 vP,
inout HorizonScanContext context,
vec2 noise,
vec2 pixel_size,
float search_distance,
@ -61,29 +288,21 @@ vec3 horizon_scan_eval(vec3 vP,
{
vec3 vV = drw_view_incident_vector(vP);
/* Only a quarter of a turn because we integrate using 2 slices.
* We use this instead of using full circle noise to improve cache hits
* since all tracing direction will be in the same quadrant. */
vec2 v_dir = sample_circle(noise.x * 0.25);
const int slice_len = 2;
vec2 v_dir = sample_circle(noise.x * (0.5 / float(slice_len)));
vec3 accum_light = vec3(0.0);
float accum_weight = 0.0;
horizon_scan_context_accumulation_reset(context);
for (int slice = 0; slice < slice_len; slice++) {
#if 0 /* For debug purpose. For when slice_len is greater than 2. */
vec2 v_dir = sample_circle(((float(slice) + noise.x) / float(slice_len)));
#endif
for (int i = 0; i < 2; i++) {
/* Setup integration domain around V. */
vec3 vB = normalize(cross(vV, vec3(v_dir, 0.0)));
vec3 vT = cross(vB, vV);
/* Projected view normal onto the integration plane. */
float vN_proj_len;
vec3 vN_proj = normalize_and_get_length(vN - vB * dot(vN, vB), vN_proj_len);
float vN_sin = dot(vN_proj, vT);
float vN_cos = saturate(dot(vN_proj, vV));
/* Angle between normalized projected normal and view vector. */
float vN_angle = sign(vN_sin) * acos_fast(vN_cos);
vec3 slice_light = vec3(0.0);
uint slice_bitmask = 0u;
horizon_scan_context_slice_start(context, vV, vT, vB);
/* For both sides of the view vector. */
for (int side = 0; side < 2; side++) {
@ -100,18 +319,18 @@ vec3 horizon_scan_eval(vec3 vP,
/* Always cross at least one pixel. */
float time = 1.0 + square((float(j) + noise.y) / float(sample_count)) * ssray.max_time;
float lod = float(j >> 2) / (1.0 + uniform_buf.ao.quality);
float lod = 1.0 + (float(j >> 2) / (1.0 + uniform_buf.ao.quality));
vec2 sample_uv = ssray.origin.xy + ssray.direction.xy * time;
float sample_depth =
textureLod(depth_tx, sample_uv * uniform_buf.hiz.uv_scale, floor(lod)).r;
float sample_depth = textureLod(hiz_tx, sample_uv * uniform_buf.hiz.uv_scale, lod).r;
if (sample_depth == 1.0) {
/* Skip background. Avoids making shadow on the geometry near the far plane. */
continue;
}
bool front_facing = vN.z > 0.0;
/* TODO(fclem): Re-introduce bias. But this is difficult to do per closure. */
bool front_facing = true; // vN.z > 0.0;
/* Bias depth a bit to avoid self shadowing issues. */
const float bias = 2.0 * 2.4e-7;
@ -137,35 +356,16 @@ vec3 horizon_scan_eval(vec3 vP,
vec2 theta = acos_fast(vec2(dot(vL_front, vV), dot(vL_back, vV)));
/* If we are tracing backward, the angles are negative. Swizzle to keep correct order. */
theta = (side == 0) ? theta.xy : -theta.yx;
theta -= vN_angle;
/* Angular bias. Shrink the visibility bitmask around the projected normal. */
theta *= angle_bias;
uint sample_bitmask = horizon_scan_angles_to_bitmask(theta);
#ifdef USE_RADIANCE_ACCUMULATION
float sample_visibility = horizon_scan_bitmask_to_visibility_uniform(sample_bitmask &
~slice_bitmask);
if (sample_visibility > 0.0) {
vec3 sample_radiance = horizon_scan_sample_radiance(sample_uv);
# ifdef USE_NORMAL_MASKING
vec3 sample_normal = horizon_scan_sample_normal(sample_uv);
sample_visibility *= dot(sample_normal, -vL_front);
# endif
slice_light += sample_radiance * (bsdf_eval(vN, vL_front) * sample_visibility);
}
#endif
slice_bitmask |= sample_bitmask;
horizon_scan_context_sample_finish(context, vL_front, vV, sample_uv, theta, angle_bias);
}
}
/* Add distant lighting. */
slice_light = vec3(horizon_scan_bitmask_to_occlusion_cosine(slice_bitmask));
/* Correct normal not on plane (Eq. 8 of GTAO paper). */
accum_light += slice_light * vN_proj_len;
accum_weight += vN_proj_len;
horizon_scan_context_slice_finish(context);
/* Rotate 90 degrees. */
v_dir = orthogonal(v_dir);
}
return accum_light * safe_rcp(accum_weight);
horizon_scan_context_accumulation_finish(context);
}

View File

@ -12,6 +12,7 @@
#pragma BLENDER_REQUIRE(gpu_shader_utildefines_lib.glsl)
#pragma BLENDER_REQUIRE(gpu_shader_math_vector_lib.glsl)
#pragma BLENDER_REQUIRE(gpu_shader_math_fast_lib.glsl)
/**
* Returns the bitmask for a given ordered pair of angle in [-pi/2..pi/2] range.
@ -77,7 +78,24 @@ float horizon_scan_bitmask_to_occlusion_cosine(uint bitmask)
#endif
}
float bsdf_eval(vec3 N, vec3 L)
float bsdf_eval(vec3 N, vec3 L, vec3 V)
{
return dot(N, L);
}
/**
* Projects the normal `N` onto a plane defined by `V` and `T`.
* V, T, B forms an orthonormal basis around V.
* Returns the angle of the normal projected normal with `V` and its length.
*/
void horizon_scan_projected_normal_to_plane_angle_and_length(
vec3 N, vec3 V, vec3 T, vec3 B, out float N_proj_len, out float N_angle)
{
/* Projected view normal onto the integration plane. */
vec3 N_proj = normalize_and_get_length(N - B * dot(N, B), N_proj_len);
float N_sin = dot(N_proj, T);
float N_cos = dot(N_proj, V);
/* Angle between normalized projected normal and view vector. */
N_angle = sign(N_sin) * acos_fast(N_cos);
}

View File

@ -0,0 +1,47 @@
/* SPDX-FileCopyrightText: 2023 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
/**
* This pass reprojects the input radiance if needed, downsample it and output the matching normal.
*
* Dispatched as one thread for each trace resolution pixel.
*/
#pragma BLENDER_REQUIRE(draw_view_lib.glsl)
#pragma BLENDER_REQUIRE(eevee_gbuffer_lib.glsl)
#pragma BLENDER_REQUIRE(gpu_shader_math_matrix_lib.glsl)
void main()
{
ivec2 texel = ivec2(gl_GlobalInvocationID.xy);
ivec2 texel_fullres = texel * uniform_buf.raytrace.resolution_scale +
uniform_buf.raytrace.resolution_bias;
/* Load Gbuffer. */
GBufferData gbuf = gbuffer_read(gbuf_header_tx, gbuf_closure_tx, gbuf_color_tx, texel_fullres);
/* Export normal. */
/* TODO(fclem): Export the most visible normal. */
vec3 N = gbuf.has_diffuse ? gbuf.diffuse.N : gbuf.reflection.N;
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));
/* Re-project radiance. */
vec2 uv = (vec2(texel_fullres) + 0.5) / vec2(textureSize(depth_tx, 0).xy);
float depth = texelFetch(depth_tx, texel_fullres, 0).r;
vec3 P = drw_point_screen_to_world(vec3(uv, depth));
vec3 ssP_prev = drw_ndc_to_screen(project_point(uniform_buf.raytrace.radiance_persmat, P));
vec4 radiance = texture(in_radiance_tx, ssP_prev.xy);
float luma = max(1e-8, reduce_max(radiance.rgb));
radiance *= 1.0 - max(0.0, luma - uniform_buf.raytrace.brightness_clamp) / luma;
imageStore(out_radiance_img, texel, radiance);
}

View File

@ -53,11 +53,11 @@ float bilateral_normal_weight(vec3 center_N, vec3 sample_N)
/* In order to remove some more fireflies, "tone-map" the color samples during the accumulation. */
vec3 to_accumulation_space(vec3 color)
{
return color / (1.0 + dot(color, vec3(1.0)));
return color / (1.0 + reduce_add(color));
}
vec3 from_accumulation_space(vec3 color)
{
return color / (1.0 - dot(color, vec3(1.0)));
return color / (1.0 - reduce_add(color));
}
void gbuffer_load_closure_data(sampler2DArray gbuf_closure_tx,
@ -101,7 +101,7 @@ 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);
vec2 center_uv = vec2(texel_fullres) * uniform_buf.raytrace.full_resolution_inv;
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));
@ -157,13 +157,14 @@ void main()
ivec2 sample_texel = texel_fullres + offset;
ivec2 sample_tile = sample_texel / RAYTRACE_GROUP_SIZE;
/* Make sure the sample has been processed and do not contain garbage data. */
bool unprocessed_tile = imageLoad(tile_mask_img, sample_tile).r == 0;
uint tile_mask = imageLoad(tile_mask_img, sample_tile).r;
bool unprocessed_tile = !flag_test(tile_mask, 1u << 0u);
if (unprocessed_tile) {
continue;
}
float sample_depth = texelFetch(depth_tx, sample_texel, 0).r;
vec2 sample_uv = vec2(sample_texel) * uniform_buf.raytrace.full_resolution_inv;
vec2 sample_uv = (vec2(sample_texel) + 0.5) * uniform_buf.raytrace.full_resolution_inv;
vec3 sample_P = drw_point_screen_to_world(vec3(sample_uv, sample_depth));
/* Background case. */
@ -181,7 +182,7 @@ void main()
vec3 radiance = imageLoad(in_radiance_img, sample_texel).rgb;
/* Do not gather unprocessed pixels. */
if (all(equal(in_radiance, FLT_11_11_10_MAX))) {
if (all(equal(radiance, FLT_11_11_10_MAX))) {
continue;
}
accum_radiance += to_accumulation_space(radiance) * weight;

View File

@ -63,7 +63,8 @@ void main()
continue;
}
bool tile_is_unused = imageLoad(tile_mask_img, tile_coord_neighbor).r == 0;
uint tile_mask = imageLoad(tile_mask_img, tile_coord_neighbor).r;
bool tile_is_unused = !flag_test(tile_mask, 1u << 0u);
if (tile_is_unused) {
ivec2 texel_fullres_neighbor = texel_fullres + ivec2(x, y) * int(tile_size);

View File

@ -12,13 +12,13 @@
#pragma BLENDER_REQUIRE(gpu_shader_codegen_lib.glsl)
#pragma BLENDER_REQUIRE(eevee_gbuffer_lib.glsl)
shared uint tile_contains_glossy_rays;
shared uint tile_contains_ray_tracing;
shared uint tile_contains_horizon_scan;
/* Returns a blend factor between different irradiance fetching method for reflections. */
float ray_glossy_factor(float roughness)
/* Returns a blend factor between different tracing method. */
float ray_roughness_factor(RayTraceData raytrace, float roughness)
{
/* TODO */
return 1.0;
return saturate(roughness * raytrace.roughness_mask_scale - raytrace.roughness_mask_bias);
fclem marked this conversation as resolved
Review

It took me some effort to realize this is just a map_range.
Wouldn't make more sense to just do:
saturate(mask_scale * (roughness - mask_start)) ?

Note that I was looking at this because raytracing is not working for materials with high roughness, but the issue must be elsewhere.

It took me some effort to realize this is just a map_range. Wouldn't make more sense to just do: `saturate(mask_scale * (roughness - mask_start))` ? Note that I was looking at this because raytracing is not working for materials with high roughness, but the issue must be elsewhere.

This is made so that it is optimized as single MADD instruction. But I agree should have a map range structure/type + function that improves the semantic.

This is made so that it is optimized as single MADD instruction. But I agree should have a map range structure/type + function that improves the semantic.
}
void main()
@ -27,15 +27,22 @@ void main()
/* Clear num_groups_x to 0 so that we can use it as counter in the compaction phase.
* Note that these writes are subject to race condition, but we write the same value
* from all work-groups. */
denoise_dispatch_buf.num_groups_x = 0u;
denoise_dispatch_buf.num_groups_y = 1u;
denoise_dispatch_buf.num_groups_z = 1u;
ray_denoise_dispatch_buf.num_groups_x = 0u;
ray_denoise_dispatch_buf.num_groups_y = 1u;
ray_denoise_dispatch_buf.num_groups_z = 1u;
ray_dispatch_buf.num_groups_x = 0u;
ray_dispatch_buf.num_groups_y = 1u;
ray_dispatch_buf.num_groups_z = 1u;
horizon_dispatch_buf.num_groups_x = 0u;
horizon_dispatch_buf.num_groups_y = 1u;
horizon_dispatch_buf.num_groups_z = 1u;
horizon_denoise_dispatch_buf.num_groups_x = 0u;
horizon_denoise_dispatch_buf.num_groups_y = 1u;
horizon_denoise_dispatch_buf.num_groups_z = 1u;
/* Init shared variables. */
tile_contains_glossy_rays = 0;
tile_contains_ray_tracing = 0;
tile_contains_horizon_scan = 0;
}
barrier();
@ -48,13 +55,22 @@ void main()
if (flag_test(closure_bits, uniform_buf.raytrace.closure_active)) {
GBufferData gbuf = gbuffer_read(gbuf_header_tx, gbuf_closure_tx, gbuf_color_tx, texel);
float roughness = (uniform_buf.raytrace.closure_active == CLOSURE_REFRACTION) ?
gbuf.refraction.roughness :
gbuf.reflection.roughness;
float roughness = 1.0;
if (uniform_buf.raytrace.closure_active == eClosureBits(CLOSURE_REFLECTION)) {
roughness = gbuf.reflection.roughness;
}
if (uniform_buf.raytrace.closure_active == eClosureBits(CLOSURE_REFRACTION)) {
roughness = 0.0; /* TODO(fclem): Apparent roughness. For now, always raytrace. */
}
if (ray_glossy_factor(roughness) > 0.0) {
float ray_roughness_fac = ray_roughness_factor(uniform_buf.raytrace, roughness);
if (ray_roughness_fac > 0.0) {
/* We don't care about race condition here. */
tile_contains_glossy_rays = 1;
tile_contains_horizon_scan = 1;
}
if (ray_roughness_fac < 1.0) {
/* We don't care about race condition here. */
tile_contains_ray_tracing = 1;
}
}
@ -64,8 +80,11 @@ void main()
ivec2 tile_co = ivec2(gl_WorkGroupID.xy);
uint tile_mask = 0u;
if (tile_contains_glossy_rays > 0) {
tile_mask = 1u;
if (tile_contains_ray_tracing > 0) {
tile_mask |= 1u << 0u;
}
if (tile_contains_horizon_scan > 0) {
tile_mask |= 1u << 1u;
}
imageStore(tile_mask_img, tile_co, uvec4(tile_mask));

View File

@ -19,9 +19,13 @@ void main()
ivec2 tile = ivec2(gl_GlobalInvocationID.xy);
/* True if an adjacent tile is tracing and will need this tile data for denoising. */
bool tile_is_sampled = false;
bool tile_is_ray_sampled = false;
/* True if this tile is shooting and tracing rays. */
bool tile_is_tracing = false;
bool tile_is_ray_tracing = false;
/* True if this tile is using horizon scan. */
bool tile_is_horizon_tracing = false;
/* True if an adjacent tile is tracing and will need this tile data for denoising (horizon). */
bool tile_is_horizon_sampled = false;
/* Could be optimized if that becomes an issue. */
for (int x_tile = -1; x_tile <= 1; x_tile++) {
for (int y_tile = -1; y_tile <= 1; y_tile++) {
@ -32,17 +36,32 @@ void main()
if (any(greaterThanEqual(full_res_tile, imageSize(tile_mask_img)))) {
continue;
}
bool denoise_tile_is_used = imageLoad(tile_mask_img, full_res_tile).r != 0u;
if (denoise_tile_is_used) {
uint tile_mask = imageLoad(tile_mask_img, full_res_tile).r;
bool tile_uses_ray_tracing = flag_test(tile_mask, 1u << 0u);
bool tile_uses_horizon_scan = flag_test(tile_mask, 1u << 1u);
if (tile_uses_ray_tracing) {
if (x_tile == 0 && y_tile == 0) {
/* Dispatch full resolution denoise tile. */
uint tile_index = atomicAdd(denoise_dispatch_buf.num_groups_x, 1u);
denoise_tiles_buf[tile_index] = packUvec2x16(uvec2(full_res_tile));
tile_is_tracing = true;
uint tile_index = atomicAdd(ray_denoise_dispatch_buf.num_groups_x, 1u);
ray_denoise_tiles_buf[tile_index] = packUvec2x16(uvec2(full_res_tile));
tile_is_ray_tracing = true;
}
else {
/* This denoise tile will sample the target tracing tile. Make sure it is cleared. */
tile_is_sampled = true;
tile_is_ray_sampled = true;
}
}
if (tile_uses_horizon_scan) {
if (x_tile == 0 && y_tile == 0) {
/* Dispatch full resolution horizon scan. */
uint tile_horizon_index = atomicAdd(horizon_denoise_dispatch_buf.num_groups_x, 1u);
horizon_denoise_tiles_buf[tile_horizon_index] = packUvec2x16(uvec2(full_res_tile));
tile_is_horizon_tracing = true;
}
else {
/* This denoise tile will sample the target tracing tile. Make sure it is cleared. */
tile_is_horizon_sampled = true;
}
}
}
@ -51,9 +70,16 @@ void main()
}
/* TODO(fclem): we might want to dispatch another type of shader only for clearing. */
if (tile_is_tracing || tile_is_sampled) {
if (tile_is_ray_tracing || tile_is_ray_sampled) {
/* Dispatch trace resolution tracing tile. */
uint tile_index = atomicAdd(ray_dispatch_buf.num_groups_x, 1u);
ray_tiles_buf[tile_index] = packUvec2x16(uvec2(tile));
}
/* TODO(fclem): we might want to dispatch another type of shader only for clearing. */
if (tile_is_horizon_tracing || tile_is_horizon_sampled) {
/* Dispatch trace resolution tracing tile. */
uint tile_index = atomicAdd(horizon_dispatch_buf.num_groups_x, 1u);
horizon_tiles_buf[tile_index] = packUvec2x16(uvec2(tile));
}
}

View File

@ -3,6 +3,7 @@
* SPDX-License-Identifier: GPL-2.0-or-later */
#pragma BLENDER_REQUIRE(draw_view_lib.glsl)
#pragma BLENDER_REQUIRE(draw_math_geom_lib.glsl)
/**
* General purpose 3D ray.

View File

@ -6,6 +6,7 @@
#include "gpu_shader_create_info.hh"
GPU_SHADER_CREATE_INFO(eevee_ambient_occlusion_pass)
.define("HORIZON_OCCLUSION")
.compute_source("eevee_ambient_occlusion_pass_comp.glsl")
.local_group_size(AMBIENT_OCCLUSION_PASS_TILE_SIZE, AMBIENT_OCCLUSION_PASS_TILE_SIZE)
.image(0, GPU_RGBA16F, Qualifier::READ, ImageType::FLOAT_2D_ARRAY, "in_normal_img")

View File

@ -33,7 +33,9 @@ GPU_SHADER_CREATE_INFO(eevee_ray_tile_classify)
.typedef_source("draw_shader_shared.h")
.image(0, RAYTRACE_TILEMASK_FORMAT, Qualifier::WRITE, ImageType::UINT_2D, "tile_mask_img")
.storage_buf(0, Qualifier::WRITE, "DispatchCommand", "ray_dispatch_buf")
.storage_buf(1, Qualifier::WRITE, "DispatchCommand", "denoise_dispatch_buf")
.storage_buf(1, Qualifier::WRITE, "DispatchCommand", "ray_denoise_dispatch_buf")
.storage_buf(2, Qualifier::WRITE, "DispatchCommand", "horizon_dispatch_buf")
.storage_buf(3, Qualifier::WRITE, "DispatchCommand", "horizon_denoise_dispatch_buf")
.compute_source("eevee_ray_tile_classify_comp.glsl");
GPU_SHADER_CREATE_INFO(eevee_ray_tile_compact)
@ -43,9 +45,13 @@ GPU_SHADER_CREATE_INFO(eevee_ray_tile_compact)
.typedef_source("draw_shader_shared.h")
.image(0, RAYTRACE_TILEMASK_FORMAT, Qualifier::READ, ImageType::UINT_2D, "tile_mask_img")
.storage_buf(0, Qualifier::READ_WRITE, "DispatchCommand", "ray_dispatch_buf")
.storage_buf(1, Qualifier::READ_WRITE, "DispatchCommand", "denoise_dispatch_buf")
.storage_buf(2, Qualifier::WRITE, "uint", "ray_tiles_buf[]")
.storage_buf(3, Qualifier::WRITE, "uint", "denoise_tiles_buf[]")
.storage_buf(1, Qualifier::READ_WRITE, "DispatchCommand", "ray_denoise_dispatch_buf")
.storage_buf(2, Qualifier::READ_WRITE, "DispatchCommand", "horizon_dispatch_buf")
.storage_buf(3, Qualifier::READ_WRITE, "DispatchCommand", "horizon_denoise_dispatch_buf")
.storage_buf(4, Qualifier::WRITE, "uint", "ray_tiles_buf[]")
.storage_buf(5, Qualifier::WRITE, "uint", "ray_denoise_tiles_buf[]")
.storage_buf(6, Qualifier::WRITE, "uint", "horizon_tiles_buf[]")
.storage_buf(7, Qualifier::WRITE, "uint", "horizon_denoise_tiles_buf[]")
.compute_source("eevee_ray_tile_compact_comp.glsl");
GPU_SHADER_CREATE_INFO(eevee_ray_generate)
@ -162,4 +168,50 @@ GPU_SHADER_CREATE_INFO(eevee_ray_denoise_bilateral)
EEVEE_RAYTRACE_CLOSURE_VARIATION(eevee_ray_denoise_bilateral)
GPU_SHADER_CREATE_INFO(eevee_horizon_setup)
.do_static_compilation(true)
.local_group_size(RAYTRACE_GROUP_SIZE, RAYTRACE_GROUP_SIZE)
.additional_info("eevee_shared", "eevee_gbuffer_data", "eevee_global_ubo", "draw_view")
.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")
.compute_source("eevee_horizon_setup_comp.glsl");
GPU_SHADER_CREATE_INFO(eevee_horizon_scan)
.local_group_size(RAYTRACE_GROUP_SIZE, RAYTRACE_GROUP_SIZE)
.additional_info("eevee_shared",
"eevee_gbuffer_data",
"eevee_global_ubo",
"eevee_sampling_data",
"eevee_utility_texture",
"eevee_hiz_data",
"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")
.storage_buf(7, Qualifier::READ, "uint", "tiles_coord_buf[]")
.compute_source("eevee_horizon_scan_comp.glsl");
EEVEE_RAYTRACE_CLOSURE_VARIATION(eevee_horizon_scan)
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_gbuffer_data",
"eevee_global_ubo",
"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, "tile_mask_img")
.storage_buf(7, Qualifier::READ, "uint", "tiles_coord_buf[]")
.compute_source("eevee_horizon_denoise_comp.glsl");
/** \} */

View File

@ -128,6 +128,10 @@ vec4 tangent_get(vec4 attr, mat3 normalmat)
# define FrontFacing true
#endif
struct ClosureOcclusion {
vec3 N;
};
struct ClosureDiffuse {
float weight;
vec3 color;

View File

@ -164,6 +164,7 @@
RAYTRACE_EEVEE_DENOISE_BILATERAL, \
.screen_trace_quality = 0.25f, \
.screen_trace_thickness = 0.2f, \
.screen_trace_max_roughness = 0.5f, \
.sample_clamp = 10.0f, \
.resolution_scale = 2, \
}

View File

@ -1807,6 +1807,8 @@ typedef struct RaytraceEEVEE {
float screen_trace_quality;
/** Thickness in world space each surface will have during screen space tracing. */
float screen_trace_thickness;
/** Maximum roughness before using horizon scan. */
float screen_trace_max_roughness;
/** Resolution downscale factor. */
int resolution_scale;
/** Maximum intensity a ray can have. */
@ -1815,6 +1817,8 @@ typedef struct RaytraceEEVEE {
int flag;
/** #RaytraceEEVEE_DenoiseStages. */
int denoise_stages;
char _pad0[4];
} RaytraceEEVEE;
typedef struct SceneEEVEE {

View File

@ -7496,6 +7496,16 @@ static void rna_def_raytrace_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, "screen_trace_max_roughness", PROP_FLOAT, PROP_FACTOR);
RNA_def_property_ui_text(
prop,
"Screen-Trace Max Roughness",
"Maximum roughness to use the tracing pipeline for. Higher "
"roughness surfaces will use horizon scan. A value of 1 will disable horizon scan");
RNA_def_property_range(prop, 0.0f, 1.0f);
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, "screen_trace_quality", PROP_FLOAT, PROP_FACTOR);
RNA_def_property_ui_text(
prop, "Screen-Trace Precision", "Precision of the screen space ray-tracing");