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:
@@ -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)
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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 */
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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 */
|
||||
|
||||
Reference in New Issue
Block a user