diff --git a/source/blender/draw/CMakeLists.txt b/source/blender/draw/CMakeLists.txt index 5e9b8f84b29..7c4b5428d75 100644 --- a/source/blender/draw/CMakeLists.txt +++ b/source/blender/draw/CMakeLists.txt @@ -118,6 +118,7 @@ data_to_c_simple(engines/clay/shaders/ssao_groundtruth.glsl SRC) data_to_c_simple(engines/eevee/shaders/default_frag.glsl SRC) data_to_c_simple(engines/eevee/shaders/default_world_frag.glsl SRC) data_to_c_simple(engines/eevee/shaders/background_vert.glsl SRC) +data_to_c_simple(engines/eevee/shaders/effect_minmaxz_frag.glsl SRC) data_to_c_simple(engines/eevee/shaders/lightprobe_filter_glossy_frag.glsl SRC) data_to_c_simple(engines/eevee/shaders/lightprobe_filter_diffuse_frag.glsl SRC) data_to_c_simple(engines/eevee/shaders/lightprobe_geom.glsl SRC) diff --git a/source/blender/draw/engines/eevee/eevee_effects.c b/source/blender/draw/engines/eevee/eevee_effects.c index 6aa70e980a7..e7fa51ad929 100644 --- a/source/blender/draw/engines/eevee/eevee_effects.c +++ b/source/blender/draw/engines/eevee/eevee_effects.c @@ -51,6 +51,11 @@ typedef struct EEVEE_LightProbeData { #define ENABLE_EFFECT_DOF 1 static struct { + /* Downsample Depth */ + struct GPUShader *minmaxz_downlevel_sh; + struct GPUShader *minmaxz_downdepth_sh; + struct GPUShader *minmaxz_copydepth_sh; + /* Motion Blur */ struct GPUShader *motion_blur_sh; @@ -66,6 +71,7 @@ static struct { struct GPUShader *dof_resolve_sh; } e_data = {NULL}; /* Engine data */ +extern char datatoc_effect_minmaxz_frag_glsl[]; extern char datatoc_effect_motion_blur_frag_glsl[]; extern char datatoc_effect_bloom_frag_glsl[]; extern char datatoc_effect_dof_vert_glsl[]; @@ -141,20 +147,22 @@ void EEVEE_effects_init(EEVEE_Data *vedata) const float *viewport_size = DRW_viewport_size_get(); + /* Shaders */ if (!e_data.motion_blur_sh) { - e_data.motion_blur_sh = DRW_shader_create_fullscreen(datatoc_effect_motion_blur_frag_glsl, NULL); - } + e_data.minmaxz_downlevel_sh = DRW_shader_create_fullscreen(datatoc_effect_minmaxz_frag_glsl, NULL); + e_data.minmaxz_downdepth_sh = DRW_shader_create_fullscreen(datatoc_effect_minmaxz_frag_glsl, "#define INPUT_DEPTH\n"); + e_data.minmaxz_copydepth_sh = DRW_shader_create_fullscreen(datatoc_effect_minmaxz_frag_glsl, "#define INPUT_DEPTH\n" + "#define COPY_DEPTH\n"); + + e_data.motion_blur_sh = DRW_shader_create_fullscreen(datatoc_effect_motion_blur_frag_glsl, NULL); - if (!e_data.dof_downsample_sh) { e_data.dof_downsample_sh = DRW_shader_create(datatoc_effect_dof_vert_glsl, NULL, datatoc_effect_dof_frag_glsl, "#define STEP_DOWNSAMPLE\n"); e_data.dof_scatter_sh = DRW_shader_create(datatoc_effect_dof_vert_glsl, NULL, datatoc_effect_dof_frag_glsl, "#define STEP_SCATTER\n"); e_data.dof_resolve_sh = DRW_shader_create(datatoc_effect_dof_vert_glsl, NULL, datatoc_effect_dof_frag_glsl, "#define STEP_RESOLVE\n"); - } - if (!e_data.bloom_blit_sh[0]) { e_data.bloom_blit_sh[0] = DRW_shader_create_fullscreen(datatoc_effect_bloom_frag_glsl, "#define STEP_BLIT\n"); e_data.bloom_blit_sh[1] = DRW_shader_create_fullscreen(datatoc_effect_bloom_frag_glsl, "#define STEP_BLIT\n" "#define HIGH_QUALITY\n"); @@ -378,6 +386,14 @@ void EEVEE_effects_init(EEVEE_Data *vedata) (int)viewport_size[0], (int)viewport_size[1], &tex, 1); } + /* MinMax Pyramid */ + /* TODO reduce precision */ + DRWFboTexture tex = {&stl->g_data->minmaxz, DRW_TEX_RG_32, DRW_TEX_MIPMAP | DRW_TEX_TEMP}; + + DRW_framebuffer_init(&fbl->minmaxz_fb, &draw_engine_eevee_type, + (int)viewport_size[0] / 2, (int)viewport_size[1] / 2, + &tex, 1); + } static DRWShadingGroup *eevee_create_bloom_pass(const char *name, EEVEE_EffectsInfo *effects, struct GPUShader *sh, DRWPass **pass, bool upsample) @@ -408,6 +424,23 @@ void EEVEE_effects_cache_init(EEVEE_Data *vedata) struct Gwn_Batch *quad = DRW_cache_fullscreen_quad_get(); + { + psl->minmaxz_downlevel = DRW_pass_create("HiZ Down Level", DRW_STATE_WRITE_COLOR); + DRWShadingGroup *grp = DRW_shgroup_create(e_data.minmaxz_downlevel_sh, psl->minmaxz_downlevel); + DRW_shgroup_uniform_buffer(grp, "depthBuffer", &stl->g_data->minmaxz); + DRW_shgroup_call_add(grp, quad, NULL); + + psl->minmaxz_downdepth = DRW_pass_create("HiZ Down Depth", DRW_STATE_WRITE_COLOR); + grp = DRW_shgroup_create(e_data.minmaxz_downdepth_sh, psl->minmaxz_downdepth); + DRW_shgroup_uniform_buffer(grp, "depthBuffer", &dtxl->depth); + DRW_shgroup_call_add(grp, quad, NULL); + + psl->minmaxz_copydepth = DRW_pass_create("HiZ Copy Depth", DRW_STATE_WRITE_COLOR); + grp = DRW_shgroup_create(e_data.minmaxz_copydepth_sh, psl->minmaxz_copydepth); + DRW_shgroup_uniform_buffer(grp, "depthBuffer", &dtxl->depth); + DRW_shgroup_call_add(grp, quad, NULL); + } + { psl->motion_blur = DRW_pass_create("Motion Blur", DRW_STATE_WRITE_COLOR); @@ -519,6 +552,28 @@ void EEVEE_effects_cache_init(EEVEE_Data *vedata) } \ } ((void)0) +static void minmax_downsample_cb(void *vedata, int UNUSED(level)) +{ + EEVEE_PassList *psl = ((EEVEE_Data *)vedata)->psl; + DRW_draw_pass(psl->minmaxz_downlevel); +} + +void EEVEE_create_minmax_buffer(EEVEE_Data *vedata) +{ + EEVEE_PassList *psl = vedata->psl; + EEVEE_FramebufferList *fbl = vedata->fbl; + EEVEE_StorageList *stl = vedata->stl; + + /* Copy depth buffer to minmax texture top level */ + DRW_framebuffer_texture_attach(fbl->minmaxz_fb, stl->g_data->minmaxz, 0, 0); + DRW_framebuffer_bind(fbl->minmaxz_fb); + DRW_draw_pass(psl->minmaxz_downdepth); + DRW_framebuffer_texture_detach(stl->g_data->minmaxz); + + /* Create lower levels */ + DRW_framebuffer_recursive_downsample(fbl->minmaxz_fb, stl->g_data->minmaxz, 6, &minmax_downsample_cb, vedata); +} + void EEVEE_draw_effects(EEVEE_Data *vedata) { EEVEE_PassList *psl = vedata->psl; @@ -642,6 +697,10 @@ void EEVEE_draw_effects(EEVEE_Data *vedata) void EEVEE_effects_free(void) { + DRW_SHADER_FREE_SAFE(e_data.minmaxz_downlevel_sh); + DRW_SHADER_FREE_SAFE(e_data.minmaxz_downdepth_sh); + DRW_SHADER_FREE_SAFE(e_data.minmaxz_copydepth_sh); + DRW_SHADER_FREE_SAFE(e_data.motion_blur_sh); DRW_SHADER_FREE_SAFE(e_data.dof_downsample_sh); DRW_SHADER_FREE_SAFE(e_data.dof_scatter_sh); diff --git a/source/blender/draw/engines/eevee/eevee_engine.c b/source/blender/draw/engines/eevee/eevee_engine.c index 729e44b0def..a125bb97a91 100644 --- a/source/blender/draw/engines/eevee/eevee_engine.c +++ b/source/blender/draw/engines/eevee/eevee_engine.c @@ -46,6 +46,7 @@ static void EEVEE_engine_init(void *ved) EEVEE_Data *vedata = (EEVEE_Data *)ved; EEVEE_TextureList *txl = vedata->txl; EEVEE_FramebufferList *fbl = vedata->fbl; + EEVEE_StorageList *stl = ((EEVEE_Data *)vedata)->stl; EEVEE_SceneLayerData *sldata = EEVEE_scene_layer_data_get(); DRWFboTexture tex = {&txl->color, DRW_TEX_RGB_11_11_10, DRW_TEX_FILTER}; @@ -55,6 +56,11 @@ 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__); + } + EEVEE_materials_init(); EEVEE_lights_init(sldata); EEVEE_lightprobes_init(sldata, vedata); @@ -67,11 +73,6 @@ static void EEVEE_cache_init(void *vedata) 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__); - } - EEVEE_materials_cache_init(vedata); EEVEE_lights_cache_init(sldata, psl); EEVEE_lightprobes_cache_init(sldata, psl, stl); @@ -148,6 +149,12 @@ static void EEVEE_draw_scene(void *vedata) DRW_draw_pass(psl->depth_pass); DRW_draw_pass(psl->depth_pass_cull); + /* Create minmax texture */ + EEVEE_create_minmax_buffer(vedata); + + /* Restore main FB */ + DRW_framebuffer_bind(fbl->main); + /* Shading pass */ DRW_draw_pass(psl->probe_display); DRW_draw_pass(psl->default_pass); diff --git a/source/blender/draw/engines/eevee/eevee_private.h b/source/blender/draw/engines/eevee/eevee_private.h index 530a9b894a2..ea63919b23d 100644 --- a/source/blender/draw/engines/eevee/eevee_private.h +++ b/source/blender/draw/engines/eevee/eevee_private.h @@ -69,6 +69,9 @@ typedef struct EEVEE_PassList { struct DRWPass *dof_down; struct DRWPass *dof_scatter; struct DRWPass *dof_resolve; + struct DRWPass *minmaxz_downlevel; + struct DRWPass *minmaxz_downdepth; + struct DRWPass *minmaxz_copydepth; struct DRWPass *depth_pass; struct DRWPass *depth_pass_cull; @@ -83,6 +86,7 @@ typedef struct EEVEE_PassList { typedef struct EEVEE_FramebufferList { /* Effects */ + struct GPUFrameBuffer *minmaxz_fb; struct GPUFrameBuffer *effect_fb; /* HDR */ struct GPUFrameBuffer *bloom_blit_fb; /* HDR */ struct GPUFrameBuffer *bloom_down_fb[MAX_BLOOM_STEP]; /* HDR */ @@ -371,6 +375,7 @@ typedef struct EEVEE_PrivateData { struct DRWShadingGroup *cube_display_shgrp; struct GHash *material_hash; struct GHash *hair_material_hash; + struct GPUTexture *minmaxz; } EEVEE_PrivateData; /* Transient data */ /* eevee_data.c */ @@ -413,6 +418,7 @@ void EEVEE_lightprobes_free(void); /* eevee_effects.c */ void EEVEE_effects_init(EEVEE_Data *vedata); void EEVEE_effects_cache_init(EEVEE_Data *vedata); +void EEVEE_create_minmax_buffer(EEVEE_Data *vedata); void EEVEE_draw_effects(EEVEE_Data *vedata); void EEVEE_effects_free(void); diff --git a/source/blender/draw/engines/eevee/shaders/effect_minmaxz_frag.glsl b/source/blender/draw/engines/eevee/shaders/effect_minmaxz_frag.glsl new file mode 100644 index 00000000000..9f81206070b --- /dev/null +++ b/source/blender/draw/engines/eevee/shaders/effect_minmaxz_frag.glsl @@ -0,0 +1,66 @@ +/** + * Shader that downsample depth buffer, + * saving min and max value of each texel in the above mipmaps. + * Adapted from http://rastergrid.com/blog/2010/10/hierarchical-z-map-based-occlusion-culling/ + **/ + +uniform sampler2D depthBuffer; + +out vec4 FragMinMax; + +vec2 sampleLowerMip(ivec2 texel) +{ +#ifdef INPUT_DEPTH + return texelFetch(depthBuffer, texel, 0).rr; +#else + return texelFetch(depthBuffer, texel, 0).rg; +#endif +} + +void minmax(inout vec2 val[2]) +{ + val[0].x = min(val[0].x, val[1].x); + val[0].y = max(val[0].y, val[1].y); +} + +void main() +{ + vec2 val[2]; + ivec2 texelPos = ivec2(gl_FragCoord.xy); + ivec2 mipsize = textureSize(depthBuffer, 0); +#ifndef COPY_DEPTH + texelPos *= 2; +#endif + + val[0] = sampleLowerMip(texelPos); +#ifndef COPY_DEPTH + val[1] = sampleLowerMip(texelPos + ivec2(1, 0)); + minmax(val); + val[1] = sampleLowerMip(texelPos + ivec2(1, 1)); + minmax(val); + val[1] = sampleLowerMip(texelPos + ivec2(0, 1)); + minmax(val); + + /* if we are reducing an odd-width texture then fetch the edge texels */ + if (((mipsize.x & 1) != 0) && (int(gl_FragCoord.x) == mipsize.x-3)) { + /* if both edges are odd, fetch the top-left corner texel */ + if (((mipsize.y & 1) != 0) && (int(gl_FragCoord.y) == mipsize.y-3)) { + val[1] = sampleLowerMip(texelPos + ivec2(-1, -1)); + minmax(val); + } + val[1] = sampleLowerMip(texelPos + ivec2(0, -1)); + minmax(val); + val[1] = sampleLowerMip(texelPos + ivec2(1, -1)); + minmax(val); + } + /* if we are reducing an odd-height texture then fetch the edge texels */ + else if (((mipsize.y & 1) != 0) && (int(gl_FragCoord.y) == mipsize.y-3)) { + val[1] = sampleLowerMip(texelPos + ivec2(0, -1)); + minmax(val); + val[1] = sampleLowerMip(texelPos + ivec2(1, -1)); + minmax(val); + } +#endif + + FragMinMax = vec4(val[0], 0.0, 1.0); +} \ No newline at end of file diff --git a/source/blender/draw/intern/draw_manager.c b/source/blender/draw/intern/draw_manager.c index e390ab33c69..5f73699587c 100644 --- a/source/blender/draw/intern/draw_manager.c +++ b/source/blender/draw/intern/draw_manager.c @@ -1997,6 +1997,7 @@ static GPUTextureFormat convert_tex_format(int fbo_format, int *channels, bool * case DRW_TEX_R_16: *channels = 1; return GPU_R16F; case DRW_TEX_R_32: *channels = 1; return GPU_R32F; case DRW_TEX_RG_16: *channels = 2; return GPU_RG16F; + case DRW_TEX_RG_32: *channels = 2; return GPU_RG32F; case DRW_TEX_RGBA_8: *channels = 4; return GPU_RGBA8; case DRW_TEX_RGBA_16: *channels = 4; return GPU_RGBA16F; case DRW_TEX_RGBA_32: *channels = 4; return GPU_RGBA32F; @@ -2143,6 +2144,7 @@ void DRW_framebuffer_recursive_downsample( { GPU_framebuffer_recursive_downsample(fb, tex, num_iter, callback, userData); } + void DRW_framebuffer_viewport_size(struct GPUFrameBuffer *UNUSED(fb_read), int x, int y, int w, int h) { glViewport(x, y, w, h);