Eevee: SSR: Add two hit option.

This option add another raytrace per pixel, clearing some noise.
But multiplying the raytrace cost.
This commit is contained in:
2017-07-23 00:03:45 +02:00
parent 76bf4f2cd3
commit 1d99d08d50
8 changed files with 166 additions and 85 deletions

View File

@@ -727,6 +727,7 @@ class RENDER_PT_eevee_shading(RenderButtonsPanel, Panel):
col = layout.column()
col.prop(props, "ssr_enable")
col.prop(props, "ssr_halfres")
col.prop(props, "ssr_two_rays")
col.prop(props, "ssr_normalize_weight")
col.prop(props, "ssr_stride")
col.prop(props, "ssr_thickness")

View File

@@ -269,6 +269,7 @@ class RENDERLAYER_PT_eevee_shading(RenderLayerButtonsPanel, Panel):
col = layout.column()
col.template_override_property(layer_props, scene_props, "ssr_enable")
col.template_override_property(layer_props, scene_props, "ssr_halfres")
col.template_override_property(layer_props, scene_props, "ssr_two_rays")
col.template_override_property(layer_props, scene_props, "ssr_normalize_weight")
col.template_override_property(layer_props, scene_props, "ssr_stride")
col.template_override_property(layer_props, scene_props, "ssr_thickness")

View File

@@ -58,6 +58,15 @@ typedef struct EEVEE_LightProbeData {
short probe_id, shadow_id;
} EEVEE_LightProbeData;
/* SSR shader variations */
enum {
SSR_RESOLVE = (1 << 0),
SSR_TWO_HIT = (1 << 1),
SSR_FULL_TRACE = (1 << 2),
SSR_NORMALIZE = (1 << 3),
SSR_MAX_SHADER = (1 << 4),
};
static struct {
/* Downsample Depth */
struct GPUShader *minz_downlevel_sh;
@@ -85,12 +94,7 @@ static struct {
struct GPUShader *volumetric_upsample_sh;
/* Screen Space Reflection */
struct GPUShader *ssr_raytrace_sh;
struct GPUShader *ssr_raytrace_full_sh;
struct GPUShader *ssr_resolve_sh;
struct GPUShader *ssr_resolve_full_sh;
struct GPUShader *ssr_resolve_norm_sh;
struct GPUShader *ssr_resolve_full_norm_sh;
struct GPUShader *ssr_sh[SSR_MAX_SHADER];
/* Simple Downsample */
struct GPUShader *downsample_sh;
@@ -171,6 +175,48 @@ static void eevee_motion_blur_camera_get_matrix_at_time(
mul_m4_m4m4(r_mat, params.winmat, obmat);
}
static struct GPUShader *eevee_effects_ssr_shader_get(int options)
{
if (e_data.ssr_sh[options] == NULL) {
DynStr *ds_frag = BLI_dynstr_new();
BLI_dynstr_append(ds_frag, datatoc_bsdf_common_lib_glsl);
BLI_dynstr_append(ds_frag, datatoc_bsdf_sampling_lib_glsl);
BLI_dynstr_append(ds_frag, datatoc_octahedron_lib_glsl);
BLI_dynstr_append(ds_frag, datatoc_lightprobe_lib_glsl);
BLI_dynstr_append(ds_frag, datatoc_raytrace_lib_glsl);
BLI_dynstr_append(ds_frag, datatoc_effect_ssr_frag_glsl);
char *ssr_shader_str = BLI_dynstr_get_cstring(ds_frag);
BLI_dynstr_free(ds_frag);
DynStr *ds_defines = BLI_dynstr_new();
BLI_dynstr_appendf(ds_defines, SHADER_DEFINES);
if (options & SSR_RESOLVE) {
BLI_dynstr_appendf(ds_defines, "#define STEP_RESOLVE\n");
}
else {
BLI_dynstr_appendf(ds_defines, "#define STEP_RAYTRACE\n");
}
if (options & SSR_TWO_HIT) {
BLI_dynstr_appendf(ds_defines, "#define TWO_HIT\n");
}
if (options & SSR_FULL_TRACE) {
BLI_dynstr_appendf(ds_defines, "#define FULLRES\n");
}
if (options & SSR_NORMALIZE) {
BLI_dynstr_appendf(ds_defines, "#define USE_NORMALIZATION\n");
}
char *ssr_define_str = BLI_dynstr_get_cstring(ds_defines);
BLI_dynstr_free(ds_defines);
e_data.ssr_sh[options] = DRW_shader_create_fullscreen(ssr_shader_str, ssr_define_str);
MEM_freeN(ssr_shader_str);
MEM_freeN(ssr_define_str);
}
return e_data.ssr_sh[options];
}
void EEVEE_effects_init(EEVEE_SceneLayerData *sldata, EEVEE_Data *vedata)
{
EEVEE_StorageList *stl = vedata->stl;
@@ -190,30 +236,6 @@ void EEVEE_effects_init(EEVEE_SceneLayerData *sldata, EEVEE_Data *vedata)
/* Shaders */
if (!e_data.motion_blur_sh) {
DynStr *ds_frag = BLI_dynstr_new();
BLI_dynstr_append(ds_frag, datatoc_bsdf_common_lib_glsl);
BLI_dynstr_append(ds_frag, datatoc_bsdf_sampling_lib_glsl);
BLI_dynstr_append(ds_frag, datatoc_octahedron_lib_glsl);
BLI_dynstr_append(ds_frag, datatoc_lightprobe_lib_glsl);
BLI_dynstr_append(ds_frag, datatoc_raytrace_lib_glsl);
BLI_dynstr_append(ds_frag, datatoc_effect_ssr_frag_glsl);
char *ssr_shader_str = BLI_dynstr_get_cstring(ds_frag);
BLI_dynstr_free(ds_frag);
e_data.ssr_raytrace_sh = DRW_shader_create_fullscreen(ssr_shader_str, SHADER_DEFINES "#define STEP_RAYTRACE\n");
e_data.ssr_raytrace_full_sh = DRW_shader_create_fullscreen(ssr_shader_str, SHADER_DEFINES "#define STEP_RAYTRACE\n"
"#define FULLRES\n");
e_data.ssr_resolve_sh = DRW_shader_create_fullscreen(ssr_shader_str, SHADER_DEFINES "#define STEP_RESOLVE\n");
e_data.ssr_resolve_norm_sh = DRW_shader_create_fullscreen(ssr_shader_str, SHADER_DEFINES "#define STEP_RESOLVE\n"
"#define USE_NORMALIZATION\n");
e_data.ssr_resolve_full_sh = DRW_shader_create_fullscreen(ssr_shader_str, SHADER_DEFINES "#define STEP_RESOLVE\n"
"#define FULLRES\n");
e_data.ssr_resolve_full_norm_sh = DRW_shader_create_fullscreen(ssr_shader_str, SHADER_DEFINES "#define STEP_RESOLVE\n"
"#define USE_NORMALIZATION\n"
"#define FULLRES\n");
MEM_freeN(ssr_shader_str);
e_data.downsample_sh = DRW_shader_create_fullscreen(datatoc_effect_downsample_frag_glsl, NULL);
e_data.volumetric_upsample_sh = DRW_shader_create_fullscreen(datatoc_volumetric_frag_glsl, "#define STEP_UPSAMPLE\n");
@@ -555,6 +577,7 @@ void EEVEE_effects_init(EEVEE_SceneLayerData *sldata, EEVEE_Data *vedata)
/* Enable double buffering to be able to read previous frame color */
effects->enabled_effects |= EFFECT_DOUBLE_BUFFER;
effects->ssr_use_two_hit = BKE_collection_engine_property_value_get_bool(props, "ssr_two_rays");
effects->reflection_trace_full = !BKE_collection_engine_property_value_get_bool(props, "ssr_halfres");
effects->ssr_use_normalization = BKE_collection_engine_property_value_get_bool(props, "ssr_normalize_weight");
effects->ssr_stride = (float)BKE_collection_engine_property_value_get_int(props, "ssr_stride");
@@ -563,7 +586,6 @@ void EEVEE_effects_init(EEVEE_SceneLayerData *sldata, EEVEE_Data *vedata)
const int divisor = (effects->reflection_trace_full) ? 1 : 2;
int tracing_res[2] = {(int)viewport_size[0] / divisor, (int)viewport_size[1] / divisor};
const bool record_two_hit = false;
const bool high_qual_input = true; /* TODO dither low quality input */
/* MRT for the shading pass in order to output needed data for the SSR pass. */
@@ -587,8 +609,8 @@ void EEVEE_effects_init(EEVEE_SceneLayerData *sldata, EEVEE_Data *vedata)
/* Raytracing output */
/* TODO try integer format for hit coord to increase precision */
DRWFboTexture tex_output[2] = {{&stl->g_data->ssr_hit_output, (record_two_hit) ? DRW_TEX_RGBA_16 : DRW_TEX_RG_16, DRW_TEX_TEMP},
{&stl->g_data->ssr_pdf_output, (record_two_hit) ? DRW_TEX_RG_16 : DRW_TEX_R_16, DRW_TEX_TEMP}};
DRWFboTexture tex_output[2] = {{&stl->g_data->ssr_hit_output, (effects->ssr_use_two_hit) ? DRW_TEX_RGBA_16 : DRW_TEX_RG_16, DRW_TEX_TEMP},
{&stl->g_data->ssr_pdf_output, (effects->ssr_use_two_hit) ? DRW_TEX_RG_16 : DRW_TEX_R_16, DRW_TEX_TEMP}};
DRW_framebuffer_init(&fbl->screen_tracing_fb, &draw_engine_eevee_type, tracing_res[0], tracing_res[1], tex_output, 2);
@@ -626,9 +648,6 @@ void EEVEE_effects_init(EEVEE_SceneLayerData *sldata, EEVEE_Data *vedata)
DRW_framebuffer_init(&fbl->double_buffer, &draw_engine_eevee_type,
(int)viewport_size[0], (int)viewport_size[1],
&tex_double_buffer, 1);
copy_m4_m4(stl->g_data->prev_persmat, stl->g_data->next_persmat);
DRW_viewport_matrix_get(stl->g_data->next_persmat, DRW_MAT_PERS);
}
else {
/* Cleanup to release memory */
@@ -724,14 +743,13 @@ void EEVEE_effects_cache_init(EEVEE_SceneLayerData *sldata, EEVEE_Data *vedata)
}
if ((effects->enabled_effects & EFFECT_SSR) != 0) {
struct GPUShader *trace_shader = (effects->reflection_trace_full) ? e_data.ssr_raytrace_full_sh : e_data.ssr_raytrace_sh;
struct GPUShader *resolve_shader = NULL;
if (effects->ssr_use_normalization) {
resolve_shader = (effects->reflection_trace_full) ? e_data.ssr_resolve_full_norm_sh : e_data.ssr_resolve_norm_sh;
}
else {
resolve_shader = (effects->reflection_trace_full) ? e_data.ssr_resolve_full_sh : e_data.ssr_resolve_sh;
}
int options = 0;
options |= (effects->ssr_use_two_hit) ? SSR_TWO_HIT : 0;
options |= (effects->reflection_trace_full) ? SSR_FULL_TRACE : 0;
options |= (effects->ssr_use_normalization) ? SSR_NORMALIZE : 0;
struct GPUShader *trace_shader = eevee_effects_ssr_shader_get(options);
struct GPUShader *resolve_shader = eevee_effects_ssr_shader_get(SSR_RESOLVE | options);
psl->ssr_raytrace = DRW_pass_create("SSR Raytrace", DRW_STATE_WRITE_COLOR);
DRWShadingGroup *grp = DRW_shgroup_create(trace_shader, psl->ssr_raytrace);
@@ -1230,22 +1248,21 @@ void EEVEE_draw_effects(EEVEE_Data *vedata)
DRW_viewport_request_redraw();
}
/* Record pers matrix for the next frame. */
DRW_viewport_matrix_get(stl->g_data->prev_persmat, DRW_MAT_PERS);
/* Update double buffer status if render mode. */
if (DRW_state_is_image_render()) {
stl->g_data->valid_double_buffer = (txl->color_double_buffer != NULL);
DRW_viewport_matrix_get(stl->g_data->prev_persmat, DRW_MAT_PERS);
}
}
void EEVEE_effects_free(void)
{
for (int i = 0; i < SSR_MAX_SHADER; ++i) {
DRW_SHADER_FREE_SAFE(e_data.ssr_sh[i]);
}
DRW_SHADER_FREE_SAFE(e_data.downsample_sh);
DRW_SHADER_FREE_SAFE(e_data.ssr_raytrace_sh);
DRW_SHADER_FREE_SAFE(e_data.ssr_raytrace_full_sh);
DRW_SHADER_FREE_SAFE(e_data.ssr_resolve_sh);
DRW_SHADER_FREE_SAFE(e_data.ssr_resolve_full_sh);
DRW_SHADER_FREE_SAFE(e_data.ssr_resolve_norm_sh);
DRW_SHADER_FREE_SAFE(e_data.ssr_resolve_full_norm_sh);
DRW_SHADER_FREE_SAFE(e_data.volumetric_upsample_sh);

View File

@@ -218,6 +218,7 @@ static void EEVEE_scene_layer_settings_create(RenderEngine *UNUSED(engine), IDPr
props->subtype == IDP_GROUP_SUB_ENGINE_RENDER);
BKE_collection_engine_property_add_bool(props, "ssr_enable", false);
BKE_collection_engine_property_add_bool(props, "ssr_two_rays", false);
BKE_collection_engine_property_add_bool(props, "ssr_normalize_weight", false);
BKE_collection_engine_property_add_bool(props, "ssr_halfres", true);
BKE_collection_engine_property_add_int(props, "ssr_stride", 16);

View File

@@ -319,6 +319,7 @@ typedef struct EEVEE_EffectsInfo {
/* SSR */
bool use_ssr;
bool reflection_trace_full;
bool ssr_use_two_hit;
bool ssr_use_normalization;
float ssr_border_fac;
float ssr_stride;
@@ -466,7 +467,6 @@ typedef struct EEVEE_PrivateData {
/* For double buffering */
bool valid_double_buffer;
float prev_persmat[4][4];
float next_persmat[4][4];
} EEVEE_PrivateData; /* Transient data */
/* eevee_data.c */

View File

@@ -72,6 +72,10 @@ void main()
float pdf;
vec3 rand = texelFetch(utilTex, ivec3(halfres_texel % LUT_SIZE, 2), 0).rba;
vec3 R = generate_ray(V, N, a2, rand, pdf);
#ifdef TWO_HIT
float pdf2;
vec3 R2 = generate_ray(V, N, a2, rand * vec3(1.0, -1.0, -1.0), pdf2);
#endif
/* Search for the planar reflection affecting this pixel */
/* If no planar is found, fallback to screen space */
@@ -89,19 +93,35 @@ void main()
/* Raycast over screen */
float hit_dist = -1.0;
#ifdef TWO_HIT
float hit_dist2 = -1.0;
#endif
/* Only raytrace if ray is above the surface normal */
/* Note : this still fails in some cases like with normal map.
* We should check against the geometric normal but we don't have it at this stage. */
if (dot(R, N) > 0.0001) {
hit_dist = raycast(depthBuffer, viewPosition, R, rand.x);
}
#ifdef TWO_HIT
/* TODO do double raytrace at the same time */
if (dot(R2, N) > 0.0001) {
hit_dist2 = raycast(depthBuffer, viewPosition, R2, rand.x);
}
#endif
/* TODO Do reprojection here */
vec2 hit_co = project_point(ProjectionMatrix, viewPosition + R * hit_dist).xy * 0.5 + 0.5;
#ifdef TWO_HIT
vec2 hit_co2 = project_point(ProjectionMatrix, viewPosition + R2 * hit_dist2).xy * 0.5 + 0.5;
#endif
/* Check if has hit a backface */
vec3 hit_N = normal_decode(textureLod(normalBuffer, hit_co, 0.0).rg, V);
hit_dist *= step(0.0, dot(-R, hit_N));
#ifdef TWO_HIT
hit_N = normal_decode(textureLod(normalBuffer, hit_co2, 0.0).rg, V);
hit_dist2 *= step(0.0, dot(-R2, hit_N));
#endif
if (hit_dist > 0.0) {
hitData = hit_co.xyxy;
@@ -109,8 +129,20 @@ void main()
else {
hitData = vec4(-1.0);
}
#ifdef TWO_HIT
if (hit_dist2 > 0.0) {
hitData.zw = hit_co2;
}
else {
hitData.zw = vec2(-1.0);
}
#endif
#ifdef TWO_HIT
pdfData = vec4(pdf, pdf2, 0.0, 0.0);
#else
pdfData = vec4(pdf);
#endif
}
#else /* STEP_RESOLVE */
@@ -222,6 +254,45 @@ float view_facing_mask(vec3 V, vec3 R)
return smoothstep(0.95, 0.80, dot(V, R));
}
vec4 get_ssr_sample(
vec2 hit_co, vec3 worldPosition, vec3 N, vec3 V, float roughnessSquared,
float cone_tan, vec2 source_uvs, vec2 texture_size, ivec2 target_texel,
out float weight)
{
/* Reconstruct ray */
float hit_depth = textureLod(depthBuffer, hit_co, 0.0).r;
vec3 hit_pos = get_world_space_from_depth(hit_co, hit_depth);
/* Find hit position in previous frame */
vec2 ref_uvs = get_reprojected_reflection(hit_pos, worldPosition, N);
/* Estimate a cone footprint to sample a corresponding mipmap level */
/* compute cone footprint Using UV distance because we are using screen space filtering */
float cone_footprint = 1.5 * cone_tan * distance(ref_uvs, source_uvs);
float mip = BRDF_BIAS * clamp(log2(cone_footprint * max(texture_size.x, texture_size.y)), 0.0, MAX_MIP);
vec3 L = normalize(hit_pos - worldPosition);
#ifdef USE_NORMALIZATION
/* Evaluate BSDF */
float bsdf = bsdf_ggx(N, L, V, roughnessSquared);
float pdf = texelFetch(pdfBuffer, target_texel, 0).r;
weight = step(0.001, pdf) * bsdf / pdf;
#else
weight = 1.0;
#endif
vec3 sample = textureLod(colorBuffer, ref_uvs, mip).rgb ;
/* Firefly removal */
sample /= 1.0 + brightness(sample);
float mask = screen_border_mask(ref_uvs, hit_pos);
mask *= view_facing_mask(V, N);
/* Check if there was a hit */
return vec4(sample, mask) * weight * step(0.0, hit_co.x);
}
#define NUM_NEIGHBORS 9
@@ -280,42 +351,24 @@ void main()
for (int i = 0; i < NUM_NEIGHBORS; i++) {
ivec2 target_texel = halfres_texel + neighbors[i] * invert_neighbor;
vec2 hit_co = texelFetch(hitBuffer, target_texel, 0).rg;
/* Reconstruct ray */
float hit_depth = textureLod(depthBuffer, hit_co, 0.0).r;
vec3 hit_pos = get_world_space_from_depth(hit_co, hit_depth);
/* Find hit position in previous frame */
vec2 ref_uvs = get_reprojected_reflection(hit_pos, worldPosition, N);
/* Estimate a cone footprint to sample a corresponding mipmap level */
/* compute cone footprint Using UV distance because we are using screen space filtering */
float cone_footprint = 1.5 * cone_tan * distance(ref_uvs, source_uvs);
float mip = BRDF_BIAS * clamp(log2(cone_footprint * max(texture_size.x, texture_size.y)), 0.0, MAX_MIP);
vec3 L = normalize(hit_pos - worldPosition);
#ifdef USE_NORMALIZATION
/* Evaluate BSDF */
float bsdf = bsdf_ggx(N, L, V, roughnessSquared);
float pdf = texelFetch(pdfBuffer, target_texel, 0).r;
float weight = step(0.001, pdf) * bsdf / pdf;
#ifdef TWO_HIT
vec4 hit_co = texelFetch(hitBuffer, target_texel, 0).rgba;
#else
float weight = 1.0;
vec2 hit_co = texelFetch(hitBuffer, target_texel, 0).rg;
#endif
vec3 sample = textureLod(colorBuffer, ref_uvs, mip).rgb ;
/* Firefly removal */
sample /= 1.0 + brightness(sample);
float mask = screen_border_mask(ref_uvs, hit_pos);
mask *= view_facing_mask(V, N);
/* Check if there was a hit */
ssr_accum += vec4(sample, mask) * weight * step(0.0, hit_co.x);
float weight;
ssr_accum += get_ssr_sample(hit_co.xy, worldPosition, N, V,
roughnessSquared, cone_tan, source_uvs,
texture_size, target_texel, weight);
weight_acc += weight;
#ifdef TWO_HIT
ssr_accum += get_ssr_sample(hit_co.zw, worldPosition, N, V,
roughnessSquared, cone_tan, source_uvs,
texture_size, target_texel, weight);
weight_acc += weight;
#endif
}
/* Compute SSR contribution */

View File

@@ -113,7 +113,7 @@ float raycast(sampler2D depth_texture, vec3 ray_origin, vec3 ray_dir, float ray_
float zmin = prev_zmax;
zmax = (dPQK.z * 0.5 + pqk.z) / (dPQK.w * 0.5 + pqk.w);
prev_zmax = zmax;
swapIfBigger(zmin, zmax); /* ??? why don't we need this ??? */
swapIfBigger(zmin, zmax);
float vmax = get_view_z_from_depth(raw_depth);
float vmin = vmax - ssrThickness;

View File

@@ -2623,6 +2623,7 @@ RNA_LAYER_ENGINE_EEVEE_GET_SET_BOOL(volumetric_shadows)
RNA_LAYER_ENGINE_EEVEE_GET_SET_INT(volumetric_shadow_samples)
RNA_LAYER_ENGINE_EEVEE_GET_SET_BOOL(volumetric_colored_transmittance)
RNA_LAYER_ENGINE_EEVEE_GET_SET_BOOL(ssr_enable)
RNA_LAYER_ENGINE_EEVEE_GET_SET_BOOL(ssr_two_rays)
RNA_LAYER_ENGINE_EEVEE_GET_SET_BOOL(ssr_halfres)
RNA_LAYER_ENGINE_EEVEE_GET_SET_BOOL(ssr_normalize_weight)
RNA_LAYER_ENGINE_EEVEE_GET_SET_INT(ssr_stride)
@@ -6197,6 +6198,13 @@ static void rna_def_scene_layer_engine_settings_eevee(BlenderRNA *brna)
RNA_def_property_flag(prop, PROP_CONTEXT_UPDATE);
RNA_def_property_update(prop, NC_SCENE | ND_LAYER_CONTENT, "rna_SceneLayerEngineSettings_update");
prop = RNA_def_property(srna, "ssr_two_rays", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_funcs(prop, "rna_LayerEngineSettings_Eevee_ssr_two_rays_get",
"rna_LayerEngineSettings_Eevee_ssr_two_rays_set");
RNA_def_property_ui_text(prop, "Double Trace", "Raytrace two rays instead of just one");
RNA_def_property_flag(prop, PROP_CONTEXT_UPDATE);
RNA_def_property_update(prop, NC_SCENE | ND_LAYER_CONTENT, "rna_SceneLayerEngineSettings_update");
prop = RNA_def_property(srna, "ssr_normalize_weight", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_funcs(prop, "rna_LayerEngineSettings_Eevee_ssr_normalize_weight_get",
"rna_LayerEngineSettings_Eevee_ssr_normalize_weight_set");