Eevee: SSR: Add double buffer so we can read previous frame color.

Also add simple reprojection and screen fade to the SSR resolve pass.
This commit is contained in:
2017-07-19 14:22:03 +02:00
parent 14bedf80cd
commit 7938848b63
5 changed files with 153 additions and 32 deletions

View File

@@ -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)

View File

@@ -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);

View File

@@ -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 */

View File

@@ -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);

View File

@@ -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 */