EEVEE-Next: Add horizon scan to raytracing module #114259
|
@ -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
|
||||
|
|
|
@ -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_);
|
||||
|
||||
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;
|
||||
|
|
|
@ -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. */
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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)));
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
|
@ -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);
|
||||
}
|
|
@ -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);
|
||||
}
|
|
@ -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;
|
||||
|
|
|
@ -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. */
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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")
|
||||
|
|
|
@ -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)
|
||||
|
||||
/** \} */
|
||||
|
|
|
@ -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;
|
||||
|
|
Loading…
Reference in New Issue
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.