diff options
author | Clément Foucault <foucault.clem@gmail.com> | 2017-07-19 15:22:03 +0300 |
---|---|---|
committer | Clément Foucault <foucault.clem@gmail.com> | 2017-07-24 16:28:27 +0300 |
commit | 7938848b636335b0d59cc7cbb42c223ec309c0d7 (patch) | |
tree | 3ad09aee025491d3fb65989baa23e17a2b3c7c3c /source/blender | |
parent | 14bedf80cd5ed29da70d6f90491d411fccd07ce4 (diff) |
Eevee: SSR: Add double buffer so we can read previous frame color.
Also add simple reprojection and screen fade to the SSR resolve pass.
Diffstat (limited to 'source/blender')
5 files changed, 153 insertions, 32 deletions
diff --git a/source/blender/draw/engines/eevee/eevee_effects.c b/source/blender/draw/engines/eevee/eevee_effects.c index c9ed4970889..f5a943bfb3f 100644 --- a/source/blender/draw/engines/eevee/eevee_effects.c +++ b/source/blender/draw/engines/eevee/eevee_effects.c @@ -45,6 +45,7 @@ #include "eevee_private.h" #include "GPU_texture.h" +#include "GPU_framebuffer.h" #define SHADER_DEFINES \ "#define EEVEE_ENGINE\n" \ @@ -513,6 +514,9 @@ void EEVEE_effects_init(EEVEE_SceneLayerData *sldata, EEVEE_Data *vedata) if (BKE_collection_engine_property_value_get_bool(props, "ssr_enable")) { effects->enabled_effects |= EFFECT_SSR; + /* Enable double buffering to be able to read previous frame color */ + effects->enabled_effects |= EFFECT_DOUBLE_BUFFER; + int tracing_res[2] = {(int)viewport_size[0] / 2, (int)viewport_size[1] / 2}; const bool record_two_hit = false; const bool high_qual_input = true; /* TODO dither low quality input */ @@ -522,20 +526,27 @@ void EEVEE_effects_init(EEVEE_SceneLayerData *sldata, EEVEE_Data *vedata) if (txl->ssr_normal_input == NULL) { DRWTextureFormat nor_format = DRW_TEX_RG_16; txl->ssr_normal_input = DRW_texture_create_2D((int)viewport_size[0], (int)viewport_size[1], nor_format, 0, NULL); - DRW_framebuffer_texture_attach(fbl->main, txl->ssr_normal_input, 1, 0); } if (txl->ssr_specrough_input == NULL) { DRWTextureFormat specrough_format = (high_qual_input) ? DRW_TEX_RGBA_16 : DRW_TEX_RGBA_8; txl->ssr_specrough_input = DRW_texture_create_2D((int)viewport_size[0], (int)viewport_size[1], specrough_format, 0, NULL); - DRW_framebuffer_texture_attach(fbl->main, txl->ssr_specrough_input, 2, 0); } + /* Reattach textures to the right buffer (because we are alternating between buffers) */ + /* TODO multiple FBO per texture!!!! */ + DRW_framebuffer_texture_detach(txl->ssr_normal_input); + DRW_framebuffer_texture_detach(txl->ssr_specrough_input); + DRW_framebuffer_texture_attach(fbl->main, txl->ssr_normal_input, 1, 0); + DRW_framebuffer_texture_attach(fbl->main, txl->ssr_specrough_input, 2, 0); + /* Raytracing output */ /* TODO try integer format for hit coord to increase precision */ DRWFboTexture tex_output[2] = {{&txl->ssr_hit_output, (record_two_hit) ? DRW_TEX_RGBA_16 : DRW_TEX_RG_16, 0}, {&txl->ssr_pdf_output, (record_two_hit) ? DRW_TEX_RG_16 : DRW_TEX_R_16, 0}}; + DRW_framebuffer_init(&fbl->screen_tracing_fb, &draw_engine_eevee_type, tracing_res[0], tracing_res[1], tex_output, 2); + /* Compute pixel projection matrix */ { float uvpix[4][4], ndcuv[4][4], tmp[4][4], winmat[4][4]; @@ -547,14 +558,12 @@ void EEVEE_effects_init(EEVEE_SceneLayerData *sldata, EEVEE_Data *vedata) /* UVs to pixels */ unit_m4(uvpix); - uvpix[0][0] = tracing_res[0]; - uvpix[1][1] = tracing_res[1]; + uvpix[0][0] = viewport_size[0]; + uvpix[1][1] = viewport_size[1]; mul_m4_m4m4(tmp, uvpix, ndcuv); mul_m4_m4m4(e_data.pixelprojmat, tmp, winmat); } - - DRW_framebuffer_init(&fbl->screen_tracing_fb, &draw_engine_eevee_type, tracing_res[0], tracing_res[1], tex_output, 2); } else { /* Cleanup to release memory */ @@ -564,6 +573,23 @@ void EEVEE_effects_init(EEVEE_SceneLayerData *sldata, EEVEE_Data *vedata) DRW_TEXTURE_FREE_SAFE(txl->ssr_pdf_output); DRW_FRAMEBUFFER_FREE_SAFE(fbl->screen_tracing_fb); } + + /* Setup double buffer so we can access last frame as it was before post processes */ + if ((effects->enabled_effects & EFFECT_DOUBLE_BUFFER) != 0) { + DRWFboTexture tex_double_buffer = {&txl->color_double_buffer, DRW_TEX_RGB_11_11_10, DRW_TEX_FILTER}; + + 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 */ + DRW_TEXTURE_FREE_SAFE(txl->color_double_buffer); + DRW_FRAMEBUFFER_FREE_SAFE(fbl->double_buffer); + } } static DRWShadingGroup *eevee_create_bloom_pass(const char *name, EEVEE_EffectsInfo *effects, struct GPUShader *sh, DRWPass **pass, bool upsample) @@ -667,8 +693,10 @@ void EEVEE_effects_cache_init(EEVEE_SceneLayerData *sldata, EEVEE_Data *vedata) DRW_shgroup_uniform_buffer(grp, "depthBuffer", &e_data.depth_src); DRW_shgroup_uniform_buffer(grp, "normalBuffer", &txl->ssr_normal_input); DRW_shgroup_uniform_buffer(grp, "specroughBuffer", &txl->ssr_specrough_input); + DRW_shgroup_uniform_buffer(grp, "colorBuffer", &txl->color_double_buffer); DRW_shgroup_uniform_buffer(grp, "hitBuffer", &txl->ssr_hit_output); DRW_shgroup_uniform_buffer(grp, "pdfBuffer", &txl->ssr_pdf_output); + DRW_shgroup_uniform_mat4(grp, "PastViewProjectionMatrix", (float *)stl->g_data->prev_persmat); DRW_shgroup_uniform_vec4(grp, "viewvecs[0]", (float *)stl->g_data->viewvecs, 2); DRW_shgroup_uniform_int(grp, "probe_count", &sldata->probes->num_render_cube, 1); DRW_shgroup_uniform_float(grp, "lodCubeMax", &sldata->probes->lod_cube_max, 1); @@ -795,17 +823,6 @@ void EEVEE_effects_cache_init(EEVEE_SceneLayerData *sldata, EEVEE_Data *vedata) } } -#define SWAP_BUFFERS() { \ - if (effects->source_buffer == txl->color) { \ - effects->source_buffer = txl->color_post; \ - effects->target_buffer = fbl->main; \ - } \ - else { \ - effects->source_buffer = txl->color; \ - effects->target_buffer = fbl->effect_fb; \ - } \ -} ((void)0) - static void minmax_downsample_cb(void *vedata, int UNUSED(level)) { EEVEE_PassList *psl = ((EEVEE_Data *)vedata)->psl; @@ -878,11 +895,12 @@ void EEVEE_effects_do_ssr(EEVEE_SceneLayerData *UNUSED(sldata), EEVEE_Data *veda EEVEE_TextureList *txl = vedata->txl; EEVEE_EffectsInfo *effects = stl->effects; - if ((effects->enabled_effects & EFFECT_SSR) != 0) { + if ((effects->enabled_effects & EFFECT_SSR) != 0 && stl->g_data->valid_double_buffer) { DefaultTextureList *dtxl = DRW_viewport_texture_list_get(); /* Raytrace at halfres. */ - e_data.depth_src = stl->g_data->minmaxz; + e_data.depth_src = dtxl->depth; + // e_data.depth_src = stl->g_data->minmaxz; DRW_framebuffer_bind(fbl->screen_tracing_fb); DRW_draw_pass(psl->ssr_raytrace); @@ -901,6 +919,26 @@ void EEVEE_effects_do_ssr(EEVEE_SceneLayerData *UNUSED(sldata), EEVEE_Data *veda } } +#define SWAP_DOUBLE_BUFFERS() { \ + if (swap_double_buffer) { \ + SWAP(struct GPUFrameBuffer *, fbl->main, fbl->double_buffer); \ + SWAP(GPUTexture *, txl->color, txl->color_double_buffer); \ + swap_double_buffer = false; \ + } \ +} ((void)0) + +#define SWAP_BUFFERS() { \ + if (effects->source_buffer == txl->color) { \ + effects->source_buffer = txl->color_post; \ + effects->target_buffer = fbl->main; \ + } \ + else { \ + effects->source_buffer = txl->color; \ + effects->target_buffer = fbl->effect_fb; \ + } \ + SWAP_DOUBLE_BUFFERS(); \ +} ((void)0) + void EEVEE_draw_effects(EEVEE_Data *vedata) { EEVEE_PassList *psl = vedata->psl; @@ -909,6 +947,9 @@ void EEVEE_draw_effects(EEVEE_Data *vedata) EEVEE_StorageList *stl = vedata->stl; EEVEE_EffectsInfo *effects = stl->effects; + /* only once per frame after the first post process */ + bool swap_double_buffer = ((effects->enabled_effects & EFFECT_DOUBLE_BUFFER) != 0); + /* Default framebuffer and texture */ DefaultFramebufferList *dfbl = DRW_viewport_framebuffer_list_get(); DefaultTextureList *dtxl = DRW_viewport_texture_list_get(); @@ -1020,6 +1061,15 @@ void EEVEE_draw_effects(EEVEE_Data *vedata) /* Tonemapping */ DRW_transform_to_display(effects->source_buffer); + + /* If no post processes is enabled, buffers are still not swapped, do it now. */ + SWAP_DOUBLE_BUFFERS(); + + if (!stl->g_data->valid_double_buffer) { + /* If history buffer is not valid request another frame. + * This fix black reflections on area resize. */ + DRW_viewport_request_redraw(); + } } void EEVEE_effects_free(void) diff --git a/source/blender/draw/engines/eevee/eevee_engine.c b/source/blender/draw/engines/eevee/eevee_engine.c index c8dc8c67c04..59f2613dd06 100644 --- a/source/blender/draw/engines/eevee/eevee_engine.c +++ b/source/blender/draw/engines/eevee/eevee_engine.c @@ -49,6 +49,13 @@ static void EEVEE_engine_init(void *ved) EEVEE_StorageList *stl = ((EEVEE_Data *)vedata)->stl; EEVEE_SceneLayerData *sldata = EEVEE_scene_layer_data_get(); + if (!stl->g_data) { + /* Alloc transient pointers */ + stl->g_data = MEM_mallocN(sizeof(*stl->g_data), __func__); + } + stl->g_data->background_alpha = 1.0f; + stl->g_data->valid_double_buffer = (txl->color_double_buffer != NULL); + DRWFboTexture tex = {&txl->color, DRW_TEX_RGB_11_11_10, DRW_TEX_FILTER}; const float *viewport_size = DRW_viewport_size_get(); @@ -56,12 +63,6 @@ static void EEVEE_engine_init(void *ved) (int)viewport_size[0], (int)viewport_size[1], &tex, 1); - if (!stl->g_data) { - /* Alloc transient pointers */ - stl->g_data = MEM_mallocN(sizeof(*stl->g_data), __func__); - } - stl->g_data->background_alpha = 1.0f; - EEVEE_materials_init(stl); EEVEE_lights_init(sldata); EEVEE_lightprobes_init(sldata, vedata); diff --git a/source/blender/draw/engines/eevee/eevee_private.h b/source/blender/draw/engines/eevee/eevee_private.h index f773c48dc32..015bc5b6a9e 100644 --- a/source/blender/draw/engines/eevee/eevee_private.h +++ b/source/blender/draw/engines/eevee/eevee_private.h @@ -138,6 +138,7 @@ typedef struct EEVEE_FramebufferList { struct GPUFrameBuffer *planarref_fb; struct GPUFrameBuffer *main; + struct GPUFrameBuffer *double_buffer; } EEVEE_FramebufferList; typedef struct EEVEE_TextureList { @@ -160,6 +161,7 @@ typedef struct EEVEE_TextureList { struct GPUTexture *planar_pool; struct GPUTexture *color; /* R16_G16_B16 */ + struct GPUTexture *color_double_buffer; } EEVEE_TextureList; typedef struct EEVEE_StorageList { @@ -351,6 +353,7 @@ enum { EFFECT_DOF = (1 << 2), EFFECT_VOLUMETRIC = (1 << 3), EFFECT_SSR = (1 << 4), + EFFECT_DOUBLE_BUFFER = (1 << 5), /* Not really an effect but a feature */ }; /* ************** SCENE LAYER DATA ************** */ @@ -447,6 +450,10 @@ typedef struct EEVEE_PrivateData { float viewvecs[2][4]; /* For planar probes */ float texel_size[2]; + /* For double buffering */ + bool valid_double_buffer; + float prev_persmat[4][4]; + float next_persmat[4][4]; } EEVEE_PrivateData; /* Transient data */ /* eevee_data.c */ diff --git a/source/blender/draw/engines/eevee/shaders/bsdf_common_lib.glsl b/source/blender/draw/engines/eevee/shaders/bsdf_common_lib.glsl index c6a56fa7d82..4bae519b52b 100644 --- a/source/blender/draw/engines/eevee/shaders/bsdf_common_lib.glsl +++ b/source/blender/draw/engines/eevee/shaders/bsdf_common_lib.glsl @@ -97,6 +97,7 @@ mat3 mul(mat3 m1, mat3 m2) { return m1 * m2; } vec3 transform_point(mat4 m, vec3 v) { return (m * vec4(v, 1.0)).xyz; } float min_v3(vec3 v) { return min(v.x, min(v.y, v.z)); } +float max_v2(vec2 v) { return max(v.x, v.y); } float saturate(float a) { return clamp(a, 0.0, 1.0); } vec2 saturate(vec2 a) { return clamp(a, 0.0, 1.0); } @@ -125,6 +126,11 @@ float fast_acos(float x) return (x >= 0) ? res : M_PI - res; } +float point_plane_projection_dist(vec3 lineorigin, vec3 planeorigin, vec3 planenormal) +{ + return dot(planenormal, planeorigin - lineorigin); +} + float line_plane_intersect_dist(vec3 lineorigin, vec3 linedirection, vec3 planeorigin, vec3 planenormal) { return dot(planenormal, planeorigin - lineorigin) / dot(planenormal, linedirection); diff --git a/source/blender/draw/engines/eevee/shaders/effect_ssr_frag.glsl b/source/blender/draw/engines/eevee/shaders/effect_ssr_frag.glsl index 62644c0ac74..e51edec5ddf 100644 --- a/source/blender/draw/engines/eevee/shaders/effect_ssr_frag.glsl +++ b/source/blender/draw/engines/eevee/shaders/effect_ssr_frag.glsl @@ -17,13 +17,13 @@ void main() { ivec2 fullres_texel = ivec2(gl_FragCoord.xy) * 2; ivec2 halfres_texel = ivec2(gl_FragCoord.xy); - float depth = texelFetch(depthBuffer, halfres_texel, 0).r; + float depth = texelFetch(depthBuffer, fullres_texel, 0).r; /* Early discard */ if (depth == 1.0) discard; - vec2 uvs = gl_FragCoord.xy / vec2(textureSize(depthBuffer, 0)); + vec2 uvs = gl_FragCoord.xy * 2.0 / vec2(textureSize(depthBuffer, 0)); /* Using view space */ vec3 viewPosition = get_view_space_from_depth(uvs, depth); @@ -61,6 +61,7 @@ void main() #else /* STEP_RESOLVE */ +uniform sampler2D colorBuffer; /* previous frame */ uniform sampler2D depthBuffer; uniform sampler2D normalBuffer; uniform sampler2D specroughBuffer; @@ -71,6 +72,7 @@ uniform sampler2D pdfBuffer; uniform int probe_count; uniform mat4 ViewProjectionMatrix; +uniform mat4 PastViewProjectionMatrix; out vec4 fragColor; @@ -98,6 +100,62 @@ void fallback_cubemap(vec3 N, vec3 V, vec3 W, float roughness, float roughnessSq } } +#if 0 /* Finish reprojection with motion vectors */ +vec3 get_motion_vector(vec3 pos) +{ +} + +/* http://bitsquid.blogspot.fr/2017/06/reprojecting-reflections_22.html */ +vec3 find_reflection_incident_point(vec3 cam, vec3 hit, vec3 pos, vec3 N) +{ + float d_cam = point_plane_projection_dist(cam, pos, N); + float d_hit = point_plane_projection_dist(hit, pos, N); + + if (d_hit < d_cam) { + /* Swap */ + float tmp = d_cam; + d_cam = d_hit; + d_hit = tmp; + } + + vec3 proj_cam = cam - (N * d_cam); + vec3 proj_hit = hit - (N * d_hit); + + return (proj_hit - proj_cam) * d_cam / (d_cam + d_hit) + proj_cam; +} +#endif + +vec2 get_reprojected_reflection(vec3 hit, vec3 pos, vec3 N) +{ + /* TODO real motion vectors */ + /* Transform to viewspace */ + // vec4(get_view_space_from_depth(uvcoords, depth), 1.0); + // vec4(get_view_space_from_depth(uvcoords, depth), 1.0); + + /* Reproject */ + // vec3 hit_reprojected = find_reflection_incident_point(cameraPos, hit, pos, N); + + vec4 hit_co = PastViewProjectionMatrix * vec4(hit, 1.0); + return (hit_co.xy / hit_co.w) * 0.5 + 0.5; +} + +float screen_border_mask(vec2 past_hit_co, vec3 hit) +{ + /* Fade on current and past screen edges */ + vec4 hit_co = ViewProjectionMatrix * vec4(hit, 1.0); + hit_co.xy = (hit_co.xy / hit_co.w) * 0.5 + 0.5; + hit_co.zw = past_hit_co; + + const float margin = 0.002; + const float atten = 0.05 + margin; /* Screen percentage */ + hit_co = smoothstep(margin, atten, hit_co) * (1 - smoothstep(1.0 - atten, 1.0 - margin, hit_co)); + vec2 atten_fac = min(hit_co.xy, hit_co.zw); + + float screenfade = atten_fac.x * atten_fac.y; + + return screenfade; +} + void main() { ivec2 halfres_texel = ivec2(gl_FragCoord.xy / 2.0); @@ -122,17 +180,16 @@ void main() /* Resolve SSR and compute contribution */ - /* We generate the same rays that has been genearted in the raycast step. + /* We generate the same rays that has been generated in the raycast step. * But we add this ray from our resolve pixel position, increassing accuracy. */ vec3 R = generate_ray(-V, N); float ray_length = texelFetch(hitBuffer, halfres_texel, 0).r; if (ray_length != -1.0) { vec3 hit_pos = worldPosition + R * ray_length; - vec4 hit_co = ViewProjectionMatrix * vec4(hit_pos, 1.0); - spec_accum.xy = (hit_co.xy / hit_co.w) * 0.5 + 0.5; - spec_accum.xyz = vec3(mod(dot(floor(hit_pos.xyz * 5.0), vec3(1.0)), 2.0)); - spec_accum.a = 1.0; + vec2 ref_uvs = get_reprojected_reflection(hit_pos, worldPosition, N); + spec_accum.a = screen_border_mask(ref_uvs, hit_pos); + spec_accum.xyz = textureLod(colorBuffer, ref_uvs, 0.0).rgb * spec_accum.a; } /* If SSR contribution is not 1.0, blend with cubemaps */ |