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
15 changed files with 420 additions and 65 deletions
Showing only changes of commit efeeb80bfc - Show all commits

View File

@ -505,6 +505,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_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_light_culling_debug_frag.glsl

View File

@ -61,6 +61,7 @@ void RayTraceModule::sync()
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("horizon_dispatch_buf", &horizon_dispatch_buf_);
inst_.bind_uniform_data(&pass);
inst_.gbuffer.bind_resources(pass);
pass.dispatch(&tile_classify_dispatch_size_);
@ -73,8 +74,10 @@ void RayTraceModule::sync()
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("horizon_dispatch_buf", &horizon_dispatch_buf_);
pass.bind_ssbo("ray_tiles_buf", &ray_tiles_buf_);
pass.bind_ssbo("denoise_tiles_buf", &denoise_tiles_buf_);
pass.bind_ssbo("horizon_tiles_buf", &horizon_tiles_buf_);
inst_.bind_uniform_data(&pass);
pass.dispatch(&tile_compact_dispatch_size_);
pass.barrier(GPU_BARRIER_SHADER_STORAGE);
@ -200,7 +203,21 @@ void RayTraceModule::sync()
pass.dispatch(denoise_dispatch_buf_);
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("out_radiance_img", &horizon_scan_output_tx_);
pass.bind_ssbo("tiles_coord_buf", &horizon_tiles_buf_);
pass.bind_texture("screen_radiance_tx", &screen_radiance_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);
}
#undef SHADER_VARIATION
#undef PASS_VARIATION
}
@ -236,6 +253,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 +263,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 +272,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 +281,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) {
@ -289,13 +310,14 @@ 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");
@ -316,6 +338,7 @@ RayTraceResult RayTraceModule::trace(RayTraceBuffer &rt_buffer,
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(denoise_tile_count, 512));
ray_tiles_buf_.resize(ceil_to_multiple_u(ray_tile_count, 512));
/* Ray setup. */
@ -411,6 +434,11 @@ RayTraceResult RayTraceModule::trace(RayTraceBuffer &rt_buffer,
tile_mask_tx_.release();
denoise_variance_tx_.release();
if (use_horizon_scan) {
horizon_scan_output_tx_ = result.get();
inst_.manager->submit(*horizon_scan_ps, render_view);
}
DRW_stats_group_end();
return result;

View File

@ -117,6 +117,9 @@ 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_scan_diffuse_ps_ = {"HorizonScan.Diffuse"};
draw::PassSimple horizon_scan_reflect_ps_ = {"HorizonScan.Reflection"};
draw::PassSimple horizon_scan_refract_ps_ = {"HorizonScan.Refraction"};
/** Dispatch with enough tiles for the whole screen. */
int3 tile_classify_dispatch_size_ = int3(1);
@ -128,9 +131,14 @@ class RayTraceModule {
DispatchIndirectBuf ray_dispatch_buf_ = {"ray_dispatch_buf_"};
/** Indirect dispatch denoise full-resolution tiles. */
DispatchIndirectBuf denoise_dispatch_buf_ = {"denoise_dispatch_buf_"};
/** Indirect dispatch horizon scan. Avoid dispatching work-groups that will not scan anything.*/
DispatchIndirectBuf horizon_dispatch_buf_ = {"horizon_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 horizon_tiles_buf_ = {"horizon_tiles_buf_"};
/** Texture containing the ray direction and PDF. */
TextureFromPool ray_data_tx_ = {"ray_data_tx"};
/** Texture containing the ray hit time. */

View File

@ -106,6 +106,12 @@ 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_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 MOTION_BLUR_GATHER:
return "eevee_motion_blur_gather";
case MOTION_BLUR_TILE_DILATE:

View File

@ -64,6 +64,10 @@ enum eShaderType {
HIZ_UPDATE_LAYER,
HIZ_DEBUG,
HORIZON_SCAN_DIFFUSE,
HORIZON_SCAN_REFLECT,
HORIZON_SCAN_REFRACT,
LIGHT_CULLING_DEBUG,
LIGHT_CULLING_SELECT,
LIGHT_CULLING_SORT,

View File

@ -29,23 +29,25 @@ 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. */
if (ambient_occlusion.r >= 0.98) {
ambient_occlusion = vec3(1.0);
}
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,
hiz_tx,
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,86 @@
/* 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_horizon_scan_eval_lib.glsl)
#pragma BLENDER_REQUIRE(eevee_gbuffer_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 extent = imageSize(gbuf_header_tx).xy;
if (any(greaterThanEqual(texel, extent))) {
return;
}
vec2 uv = (vec2(texel) + 0.5) * uniform_buf.raytrace.full_resolution_inv;
float depth = texelFetch(hiz_tx, texel, 0).r;
if (depth == 1.0) {
/* Do not trace for background */
imageStore(out_radiance_img, texel, vec4(0.0));
return;
}
GBufferData gbuf = gbuffer_read(gbuf_header_tx, gbuf_closure_tx, gbuf_color_tx, texel);
HorizonScanContext ctx;
#ifdef HORIZON_DIFFUSE
if (gbuf.has_diffuse == false) {
return;
}
ctx.diffuse = gbuf.diffuse;
ctx.diffuse.N = drw_normal_world_to_view(ctx.diffuse.N);
#endif
#ifdef HORIZON_REFLECT
if (gbuf.has_reflection == false) {
return;
}
ctx.reflection = gbuf.reflection;
ctx.reflection.N = drw_normal_world_to_view(ctx.reflection.N);
#endif
#ifdef HORIZON_REFRACT
if (gbuf.has_refraction == false) {
return;
}
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,
hiz_tx,
noise,
uniform_buf.ao.pixel_size,
1.0e16,
uniform_buf.ao.thickness,
uniform_buf.ao.angle_bias,
8);
vec4 radiance_raytrace = imageLoad(out_radiance_img, texel);
vec4 radiance_horizon = vec4(1.0, 0.0, 1.0, 1.0);
#ifdef HORIZON_DIFFUSE
radiance_horizon = vec4(ctx.diffuse_result, 1.0);
#endif
#ifdef HORIZON_REFLECT
radiance_horizon = vec4(ctx.reflection_result, 1.0);
#endif
#ifdef HORIZON_REFRACT
radiance_horizon = vec4(ctx.refraction_result, 1.0);
#endif
float mix_fac = 1.0;
imageStore(out_radiance_img, texel, radiance_horizon);
}

View File

@ -14,6 +14,199 @@
#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)
#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)
{
return vec3(0.0);
}
/* Note: Expects all normals to be in view-space. */
struct HorizonScanContextCommon {
float N_angle;
float N_length;
uint bitmask;
vec3 light_slice;
vec4 light_accum;
};
struct HorizonScanContext {
#ifdef HORIZON_OCCLUSION
ClosureOcclusion occlusion;
HorizonScanContextCommon occlusion_common;
vec3 occlusion_result;
#endif
#ifdef HORIZON_DIFFUSE
ClosureDiffuse diffuse;
HorizonScanContextCommon diffuse_common;
vec3 diffuse_result;
#endif
#ifdef HORIZON_REFLECT
ClosureReflection reflection;
HorizonScanContextCommon reflection_common;
vec3 reflection_result;
#endif
#ifdef HORIZON_REFRACT
ClosureRefraction refraction;
HorizonScanContextCommon refraction_common;
vec3 refraction_result;
#endif
};
void horizon_scan_context_accumulation_reset(inout HorizonScanContext context)
{
#ifdef HORIZON_OCCLUSION
context.occlusion_common.light_accum = vec4(0.0);
#endif
#ifdef HORIZON_DIFFUSE
context.diffuse_common.light_accum = vec4(0.0);
#endif
#ifdef HORIZON_REFLECT
context.reflection_common.light_accum = vec4(0.0);
#endif
#ifdef HORIZON_REFRACT
context.refraction_common.light_accum = vec4(0.0);
#endif
}
void horizon_scan_context_slice_start(
inout HorizonScanContextCommon context, vec3 vN, vec3 vV, vec3 vT, vec3 vB)
{
context.bitmask = 0u;
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,
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);
float sample_visibility = horizon_scan_bitmask_to_visibility_uniform(sample_bitmask &
~context.bitmask);
context.light_slice += sample_radiance * sample_visibility;
context.bitmask |= sample_bitmask;
}
void horizon_scan_context_sample_finish(
inout HorizonScanContext context, vec3 L, vec3 V, vec2 sample_uv, vec2 theta, float bias)
{
vec3 sample_radiance = horizon_scan_sample_radiance(sample_uv);
#if 0
/* TODO: Optionally take emitter surface normal into consideration. */
vec3 sample_normal = horizon_scan_sample_normal(sample_uv);
sample_radiance *= dot(sample_normal, -L);
#endif
#ifdef HORIZON_OCCLUSION
horizon_scan_context_sample_finish(context.occlusion_common, vec3(0.0), theta, bias);
#endif
#ifdef HORIZON_DIFFUSE
sample_radiance *= bsdf_eval(context.diffuse.N, L, V);
horizon_scan_context_sample_finish(context.diffuse_common, sample_radiance, theta, bias);
#endif
#ifdef HORIZON_REFLECT
sample_radiance *= bsdf_ggx(context.reflection.N, L, V, context.reflection.roughness);
horizon_scan_context_sample_finish(context.reflection_common, sample_radiance, theta, bias);
#endif
#ifdef HORIZON_REFRACT
sample_radiance *= bsdf_eval(context.refraction.N, L, V);
horizon_scan_context_sample_finish(context.refraction_common, sample_radiance, theta, bias);
#endif
}
void horizon_scan_context_slice_finish_occlusion(inout HorizonScanContextCommon context)
{
float slice_occlusion = horizon_scan_bitmask_to_occlusion_cosine(context.bitmask);
/* Correct normal not on plane (Eq. 8 of GTAO paper). */
context.light_accum += vec4(vec3(slice_occlusion), 1.0) * context.N_length;
}
void horizon_scan_context_slice_finish_distant_light(inout HorizonScanContextCommon context)
{
/* Add distant lighting. */
/* TODO(fclem): Add missing lighting from missing sectors. */
vec3 slice = context.light_slice;
/* Correct normal not on plane (Eq. 8 of GTAO paper). */
context.light_accum += vec4(slice, 1.0) * context.N_length;
}
void horizon_scan_context_slice_finish(inout HorizonScanContext context)
{
#ifdef HORIZON_OCCLUSION
horizon_scan_context_slice_finish_occlusion(context.occlusion_common);
#endif
#ifdef HORIZON_DIFFUSE
horizon_scan_context_slice_finish_distant_light(context.diffuse_common);
#endif
#ifdef HORIZON_REFLECT
horizon_scan_context_slice_finish_distant_light(context.reflection_common);
#endif
#ifdef HORIZON_REFRACT
horizon_scan_context_slice_finish_distant_light(context.refraction_common);
#endif
}
void horizon_scan_context_accumulation_finish(HorizonScanContextCommon context, out vec3 result)
{
result = context.light_accum.xyz * safe_rcp(context.light_accum.w);
}
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,10 +240,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,
void horizon_scan_eval(vec3 vP,
inout HorizonScanContext context,
sampler2D depth_tx,
vec2 noise,
vec2 pixel_size,
@ -66,24 +259,14 @@ vec3 horizon_scan_eval(vec3 vP,
* since all tracing direction will be in the same quadrant. */
vec2 v_dir = sample_circle(noise.x * 0.25);
vec3 accum_light = vec3(0.0);
float accum_weight = 0.0;
horizon_scan_context_accumulation_reset(context);
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++) {
@ -111,7 +294,8 @@ vec3 horizon_scan_eval(vec3 vP,
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 +321,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

@ -30,6 +30,9 @@ void main()
denoise_dispatch_buf.num_groups_x = 0u;
denoise_dispatch_buf.num_groups_y = 1u;
denoise_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;
ray_dispatch_buf.num_groups_x = 0u;
ray_dispatch_buf.num_groups_y = 1u;
ray_dispatch_buf.num_groups_z = 1u;

View File

@ -39,6 +39,11 @@ void main()
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;
/* Dispatch full resolution horizon scan. */
/* TODO(fclem): Limit that to high roughness pixels. */
uint tile_horizon_index = atomicAdd(horizon_dispatch_buf.num_groups_x, 1u);
horizon_tiles_buf[tile_horizon_index] = packUvec2x16(uvec2(full_res_tile));
}
else {
/* This denoise tile will sample the target tracing tile. Make sure it is cleared. */

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

@ -34,6 +34,7 @@ GPU_SHADER_CREATE_INFO(eevee_ray_tile_classify)
.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(2, Qualifier::WRITE, "DispatchCommand", "horizon_dispatch_buf")
.compute_source("eevee_ray_tile_classify_comp.glsl");
GPU_SHADER_CREATE_INFO(eevee_ray_tile_compact)
@ -44,8 +45,10 @@ GPU_SHADER_CREATE_INFO(eevee_ray_tile_compact)
.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(2, Qualifier::READ_WRITE, "DispatchCommand", "horizon_dispatch_buf")
.storage_buf(3, Qualifier::WRITE, "uint", "ray_tiles_buf[]")
.storage_buf(4, Qualifier::WRITE, "uint", "denoise_tiles_buf[]")
.storage_buf(5, Qualifier::WRITE, "uint", "horizon_tiles_buf[]")
.compute_source("eevee_ray_tile_compact_comp.glsl");
GPU_SHADER_CREATE_INFO(eevee_ray_generate)
@ -162,4 +165,24 @@ GPU_SHADER_CREATE_INFO(eevee_ray_denoise_bilateral)
EEVEE_RAYTRACE_CLOSURE_VARIATION(eevee_ray_denoise_bilateral)
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")
.image(2,
RAYTRACE_RADIANCE_FORMAT,
Qualifier::READ_WRITE,
ImageType::FLOAT_2D,
"out_radiance_img")
.storage_buf(4, Qualifier::READ, "uint", "tiles_coord_buf[]")
.compute_source("eevee_horizon_scan_comp.glsl");
EEVEE_RAYTRACE_CLOSURE_VARIATION(eevee_horizon_scan)
/** \} */

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;