diff options
10 files changed, 456 insertions, 35 deletions
diff --git a/release/scripts/startup/bl_ui/properties_render.py b/release/scripts/startup/bl_ui/properties_render.py index bd599bc0cb4..86d454c3254 100644 --- a/release/scripts/startup/bl_ui/properties_render.py +++ b/release/scripts/startup/bl_ui/properties_render.py @@ -631,6 +631,7 @@ class RENDER_PT_eevee_poststack_settings(RenderButtonsPanel, Panel): props = scene.layer_properties['BLENDER_EEVEE'] col = layout.column() + col.prop(props, "volumetric_enable") col.prop(props, "gtao_enable") col.prop(props, "motion_blur_enable") col.prop(props, "dof_enable") diff --git a/release/scripts/startup/bl_ui/properties_render_layer.py b/release/scripts/startup/bl_ui/properties_render_layer.py index 4de48f00b04..9e563fc6236 100644 --- a/release/scripts/startup/bl_ui/properties_render_layer.py +++ b/release/scripts/startup/bl_ui/properties_render_layer.py @@ -167,6 +167,7 @@ class RENDERLAYER_PT_eevee_poststack_settings(RenderLayerButtonsPanel, Panel): layer_props = layer.engine_overrides['BLENDER_EEVEE'] col = layout.column() + col.template_override_property(layer_props, scene_props, "volumetric_enable") col.template_override_property(layer_props, scene_props, "gtao_enable") col.template_override_property(layer_props, scene_props, "motion_blur_enable") col.template_override_property(layer_props, scene_props, "dof_enable") diff --git a/source/blender/draw/CMakeLists.txt b/source/blender/draw/CMakeLists.txt index 765e6a2fda2..4b760f68705 100644 --- a/source/blender/draw/CMakeLists.txt +++ b/source/blender/draw/CMakeLists.txt @@ -157,6 +157,7 @@ data_to_c_simple(engines/eevee/shaders/irradiance_lib.glsl SRC) data_to_c_simple(engines/eevee/shaders/octahedron_lib.glsl SRC) data_to_c_simple(engines/eevee/shaders/bsdf_sampling_lib.glsl SRC) data_to_c_simple(engines/eevee/shaders/ltc_lib.glsl SRC) +data_to_c_simple(engines/eevee/shaders/volumetric_frag.glsl SRC) data_to_c_simple(modes/shaders/common_globals_lib.glsl SRC) data_to_c_simple(modes/shaders/edit_mesh_overlay_frag.glsl SRC) diff --git a/source/blender/draw/engines/eevee/eevee_effects.c b/source/blender/draw/engines/eevee/eevee_effects.c index 4191c21eea1..96b168cdeee 100644 --- a/source/blender/draw/engines/eevee/eevee_effects.c +++ b/source/blender/draw/engines/eevee/eevee_effects.c @@ -38,6 +38,8 @@ #include "BKE_animsys.h" #include "BKE_screen.h" +#include "BLI_dynstr.h" + #include "eevee_private.h" #include "GPU_texture.h" @@ -45,11 +47,6 @@ typedef struct EEVEE_LightProbeData { short probe_id, shadow_id; } EEVEE_LightProbeData; -/* TODO Option */ -#define ENABLE_EFFECT_MOTION_BLUR 1 -#define ENABLE_EFFECT_BLOOM 1 -#define ENABLE_EFFECT_DOF 1 - static struct { /* Downsample Depth */ struct GPUShader *minmaxz_downlevel_sh; @@ -70,7 +67,10 @@ static struct { struct GPUShader *dof_scatter_sh; struct GPUShader *dof_resolve_sh; - struct GPUTexture *minmmaxz_depth_src; + /* Volumetric */ + struct GPUShader *volumetric_upsample_sh; + + struct GPUTexture *depth_src; } e_data = {NULL}; /* Engine data */ extern char datatoc_effect_minmaxz_frag_glsl[]; @@ -80,6 +80,7 @@ extern char datatoc_effect_dof_vert_glsl[]; extern char datatoc_effect_dof_geom_glsl[]; extern char datatoc_effect_dof_frag_glsl[]; extern char datatoc_tonemap_frag_glsl[]; +extern char datatoc_volumetric_frag_glsl[]; static void eevee_motion_blur_camera_get_matrix_at_time( Scene *scene, ARegion *ar, RegionView3D *rv3d, View3D *v3d, Object *camera, float time, float r_mat[4][4]) @@ -151,6 +152,8 @@ void EEVEE_effects_init(EEVEE_Data *vedata) /* Shaders */ if (!e_data.motion_blur_sh) { + e_data.volumetric_upsample_sh = DRW_shader_create_fullscreen(datatoc_volumetric_frag_glsl, "#define STEP_UPSAMPLE\n"); + 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" @@ -190,7 +193,6 @@ void EEVEE_effects_init(EEVEE_Data *vedata) int enabled_effects = 0; -#if ENABLE_EFFECT_MOTION_BLUR if (BKE_collection_engine_property_value_get_bool(props, "motion_blur_enable")) { /* Update Motion Blur Matrices */ if (rv3d->persp == RV3D_CAMOB && v3d->camera) { @@ -221,9 +223,7 @@ void EEVEE_effects_init(EEVEE_Data *vedata) } } } -#endif /* ENABLE_EFFECT_MOTION_BLUR */ -#if ENABLE_EFFECT_BLOOM if (BKE_collection_engine_property_value_get_bool(props, "bloom_enable")) { /* Bloom */ int blitsize[2], texsize[2]; @@ -294,9 +294,7 @@ void EEVEE_effects_init(EEVEE_Data *vedata) enabled_effects |= EFFECT_BLOOM; } -#endif /* ENABLE_EFFECT_BLOOM */ -#if ENABLE_EFFECT_DOF if (BKE_collection_engine_property_value_get_bool(props, "dof_enable")) { /* Depth Of Field */ if (rv3d->persp == RV3D_CAMOB && v3d->camera) { @@ -375,7 +373,6 @@ void EEVEE_effects_init(EEVEE_Data *vedata) enabled_effects |= EFFECT_DOF; } } -#endif /* ENABLE_EFFECT_DOF */ effects->enabled_effects = enabled_effects; @@ -404,6 +401,16 @@ void EEVEE_effects_init(EEVEE_Data *vedata) (int)viewport_size[0] / 2, (int)viewport_size[1] / 2, &tex, 1); + if (BKE_collection_engine_property_value_get_bool(props, "volumetric_enable")) { + /* MinMax Pyramid */ + DRWFboTexture tex_vol = {&stl->g_data->volumetric, DRW_TEX_RGBA_16, DRW_TEX_MIPMAP | DRW_TEX_FILTER | DRW_TEX_TEMP}; + + DRW_framebuffer_init(&fbl->volumetric_fb, &draw_engine_eevee_type, + (int)viewport_size[0] / 2, (int)viewport_size[1] / 2, + &tex_vol, 1); + + effects->enabled_effects |= EFFECT_VOLUMETRIC; + } } static DRWShadingGroup *eevee_create_bloom_pass(const char *name, EEVEE_EffectsInfo *effects, struct GPUShader *sh, DRWPass **pass, bool upsample) @@ -424,7 +431,7 @@ static DRWShadingGroup *eevee_create_bloom_pass(const char *name, EEVEE_EffectsI return grp; } -void EEVEE_effects_cache_init(EEVEE_Data *vedata) +void EEVEE_effects_cache_init(EEVEE_SceneLayerData *sldata, EEVEE_Data *vedata) { EEVEE_PassList *psl = vedata->psl; EEVEE_StorageList *stl = vedata->stl; @@ -435,6 +442,29 @@ void EEVEE_effects_cache_init(EEVEE_Data *vedata) struct Gwn_Batch *quad = DRW_cache_fullscreen_quad_get(); { + struct GPUShader *sh = EEVEE_material_world_volume_get(NULL, NULL); + psl->volumetric_integrate_ps = DRW_pass_create("Volumetric Integration", DRW_STATE_WRITE_COLOR); + DRWShadingGroup *grp = DRW_shgroup_create(sh, psl->volumetric_integrate_ps); + DRW_shgroup_uniform_buffer(grp, "depthFull", &e_data.depth_src); + DRW_shgroup_uniform_buffer(grp, "shadowCubes", &sldata->shadow_depth_cube_pool); + DRW_shgroup_uniform_buffer(grp, "irradianceGrid", &sldata->irradiance_pool); + DRW_shgroup_uniform_block(grp, "light_block", sldata->light_ubo); + DRW_shgroup_uniform_block(grp, "grid_block", sldata->grid_ubo); + DRW_shgroup_uniform_block(grp, "shadow_block", sldata->shadow_ubo); + DRW_shgroup_uniform_int(grp, "light_count", &sldata->lamps->num_light, 1); + DRW_shgroup_uniform_int(grp, "grid_count", &sldata->probes->num_render_grid, 1); + DRW_shgroup_uniform_texture(grp, "utilTex", EEVEE_materials_get_util_tex()); + DRW_shgroup_uniform_vec4(grp, "viewvecs[0]", (float *)stl->g_data->viewvecs, 2); + DRW_shgroup_call_add(grp, quad, NULL); + + psl->volumetric_resolve_ps = DRW_pass_create("Volumetric Resolve", DRW_STATE_WRITE_COLOR | DRW_STATE_TRANSMISSION); + grp = DRW_shgroup_create(e_data.volumetric_upsample_sh, psl->volumetric_resolve_ps); + DRW_shgroup_uniform_buffer(grp, "depthFull", &e_data.depth_src); + DRW_shgroup_uniform_buffer(grp, "volumetricBuffer", &stl->g_data->volumetric); + DRW_shgroup_call_add(grp, quad, NULL); + } + + { 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); @@ -442,12 +472,12 @@ void EEVEE_effects_cache_init(EEVEE_Data *vedata) 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", &e_data.minmmaxz_depth_src); + DRW_shgroup_uniform_buffer(grp, "depthBuffer", &e_data.depth_src); 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", &e_data.minmmaxz_depth_src); + DRW_shgroup_uniform_buffer(grp, "depthBuffer", &e_data.depth_src); DRW_shgroup_call_add(grp, quad, NULL); } @@ -574,7 +604,7 @@ void EEVEE_create_minmax_buffer(EEVEE_Data *vedata, GPUTexture *depth_src) EEVEE_FramebufferList *fbl = vedata->fbl; EEVEE_StorageList *stl = vedata->stl; - e_data.minmmaxz_depth_src = depth_src; + e_data.depth_src = depth_src; /* Copy depth buffer to minmax texture top level */ DRW_framebuffer_texture_attach(fbl->minmaxz_fb, stl->g_data->minmaxz, 0, 0); @@ -586,6 +616,33 @@ void EEVEE_create_minmax_buffer(EEVEE_Data *vedata, GPUTexture *depth_src) DRW_framebuffer_recursive_downsample(fbl->minmaxz_fb, stl->g_data->minmaxz, 6, &minmax_downsample_cb, vedata); } +void EEVEE_effects_do_volumetrics(EEVEE_Data *vedata) +{ + EEVEE_PassList *psl = vedata->psl; + EEVEE_FramebufferList *fbl = vedata->fbl; + EEVEE_StorageList *stl = vedata->stl; + EEVEE_EffectsInfo *effects = stl->effects; + + if ((effects->enabled_effects & EFFECT_VOLUMETRIC) != 0) { + DefaultTextureList *dtxl = DRW_viewport_texture_list_get(); + + e_data.depth_src = dtxl->depth; + + /* Compute volumetric integration at halfres. */ + DRW_framebuffer_texture_attach(fbl->volumetric_fb, stl->g_data->volumetric, 0, 0); + DRW_framebuffer_bind(fbl->volumetric_fb); + DRW_draw_pass(psl->volumetric_integrate_ps); + + /* Resolve at fullres */ + DRW_framebuffer_texture_detach(dtxl->depth); + DRW_framebuffer_bind(fbl->main); + DRW_draw_pass(psl->volumetric_resolve_ps); + + /* Restore */ + DRW_framebuffer_texture_attach(fbl->main, dtxl->depth, 0, 0); + } +} + void EEVEE_draw_effects(EEVEE_Data *vedata) { EEVEE_PassList *psl = vedata->psl; @@ -709,6 +766,8 @@ void EEVEE_draw_effects(EEVEE_Data *vedata) void EEVEE_effects_free(void) { + DRW_SHADER_FREE_SAFE(e_data.volumetric_upsample_sh); + 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); diff --git a/source/blender/draw/engines/eevee/eevee_engine.c b/source/blender/draw/engines/eevee/eevee_engine.c index 88f16e41d6b..c72e1f03f4d 100644 --- a/source/blender/draw/engines/eevee/eevee_engine.c +++ b/source/blender/draw/engines/eevee/eevee_engine.c @@ -62,7 +62,7 @@ static void EEVEE_engine_init(void *ved) } stl->g_data->background_alpha = 1.0f; - EEVEE_materials_init(); + EEVEE_materials_init(stl); EEVEE_lights_init(sldata); EEVEE_lightprobes_init(sldata, vedata); EEVEE_effects_init(vedata); @@ -76,7 +76,7 @@ static void EEVEE_cache_init(void *vedata) EEVEE_materials_cache_init(vedata); EEVEE_lights_cache_init(sldata, psl); EEVEE_lightprobes_cache_init(sldata, vedata); - EEVEE_effects_cache_init(vedata); + EEVEE_effects_cache_init(sldata, vedata); } static void EEVEE_cache_populate(void *vedata, Object *ob) @@ -160,6 +160,9 @@ static void EEVEE_draw_scene(void *vedata) EEVEE_draw_default_passes(psl); DRW_draw_pass(psl->material_pass); + /* Volumetrics */ + EEVEE_effects_do_volumetrics(vedata); + /* Post Process */ EEVEE_draw_effects(vedata); } @@ -187,6 +190,8 @@ static void EEVEE_scene_layer_settings_create(RenderEngine *UNUSED(engine), IDPr props->type == IDP_GROUP && props->subtype == IDP_GROUP_SUB_ENGINE_RENDER); + BKE_collection_engine_property_add_bool(props, "volumetric_enable", false); + BKE_collection_engine_property_add_bool(props, "gtao_enable", false); BKE_collection_engine_property_add_bool(props, "gtao_use_bent_normals", true); BKE_collection_engine_property_add_float(props, "gtao_distance", 0.2f); diff --git a/source/blender/draw/engines/eevee/eevee_materials.c b/source/blender/draw/engines/eevee/eevee_materials.c index d7494430f76..f7c28530aef 100644 --- a/source/blender/draw/engines/eevee/eevee_materials.c +++ b/source/blender/draw/engines/eevee/eevee_materials.c @@ -64,6 +64,7 @@ static struct { char *frag_shader_lib; + struct GPUShader *default_volume_sh; struct GPUShader *default_prepass_sh; struct GPUShader *default_prepass_clip_sh; struct GPUShader *default_lit[VAR_MAT_MAX]; @@ -99,6 +100,7 @@ extern char datatoc_shadow_vert_glsl[]; extern char datatoc_lightprobe_geom_glsl[]; extern char datatoc_lightprobe_vert_glsl[]; extern char datatoc_background_vert_glsl[]; +extern char datatoc_volumetric_frag_glsl[]; extern Material defmaterial; extern GlobalsUboStorage ts; @@ -167,6 +169,12 @@ static struct GPUTexture *create_ggx_lut_texture(int UNUSED(w), int UNUSED(h)) } #endif +/* XXX TODO define all shared resources in a shared place without duplication */ +struct GPUTexture *EEVEE_materials_get_util_tex(void) +{ + return e_data.util_tex; +} + static char *eevee_get_defines(int options) { char *str = NULL; @@ -222,7 +230,7 @@ static void add_standard_uniforms(DRWShadingGroup *shgrp, EEVEE_SceneLayerData * DRW_shgroup_uniform_buffer(shgrp, "shadowCubes", &sldata->shadow_depth_cube_pool); DRW_shgroup_uniform_buffer(shgrp, "shadowCascades", &sldata->shadow_depth_cascade_pool); if (vedata->stl->effects->use_ao) { - DRW_shgroup_uniform_vec4(shgrp, "viewvecs[0]", (float *)e_data.viewvecs, 2); + DRW_shgroup_uniform_vec4(shgrp, "viewvecs[0]", (float *)&vedata->stl->g_data->viewvecs, 2); DRW_shgroup_uniform_buffer(shgrp, "minMaxDepthTex", &vedata->stl->g_data->minmaxz); DRW_shgroup_uniform_vec3(shgrp, "aoParameters", &vedata->stl->effects->ao_dist, 1); } @@ -244,7 +252,7 @@ static void create_default_shader(int options) MEM_freeN(frag_str); } -void EEVEE_materials_init(void) +void EEVEE_materials_init(EEVEE_StorageList *stl) { if (!e_data.frag_shader_lib) { char *frag_str = NULL; @@ -282,6 +290,16 @@ void EEVEE_materials_init(void) MEM_freeN(frag_str); + ds_frag = BLI_dynstr_new(); + BLI_dynstr_append(ds_frag, e_data.frag_shader_lib); + BLI_dynstr_append(ds_frag, datatoc_volumetric_frag_glsl); + frag_str = BLI_dynstr_get_cstring(ds_frag); + BLI_dynstr_free(ds_frag); + + e_data.default_volume_sh = DRW_shader_create_fullscreen(frag_str, SHADER_DEFINES "#define STEP_INTEGRATE\n"); + + MEM_freeN(frag_str); + /* Textures */ const int layers = 3; float (*texels)[4] = MEM_mallocN(sizeof(float[4]) * 64 * 64 * layers, "utils texels"); @@ -338,19 +356,19 @@ void EEVEE_materials_init(void) viewvecs[i][3] = 1.0; } - copy_v4_v4(e_data.viewvecs[0], viewvecs[0]); - copy_v4_v4(e_data.viewvecs[1], viewvecs[1]); + copy_v4_v4(stl->g_data->viewvecs[0], viewvecs[0]); + copy_v4_v4(stl->g_data->viewvecs[1], viewvecs[1]); /* we need to store the differences */ - e_data.viewvecs[1][0] -= viewvecs[0][0]; - e_data.viewvecs[1][1] = viewvecs[2][1] - viewvecs[0][1]; + stl->g_data->viewvecs[1][0] -= viewvecs[0][0]; + stl->g_data->viewvecs[1][1] = viewvecs[2][1] - viewvecs[0][1]; /* calculate a depth offset as well */ if (!is_persp) { float vec_far[] = {-1.0f, -1.0f, 1.0f, 1.0f}; mul_m4_v4(invproj, vec_far); mul_v3_fl(vec_far, 1.0f / vec_far[3]); - e_data.viewvecs[1][2] = vec_far[2] - viewvecs[0][2]; + stl->g_data->viewvecs[1][2] = vec_far[2] - viewvecs[0][2]; } } } @@ -385,6 +403,11 @@ struct GPUMaterial *EEVEE_material_world_background_get(struct Scene *scene, Wor SHADER_DEFINES "#define WORLD_BACKGROUND\n"); } +struct GPUShader *EEVEE_material_world_volume_get(struct Scene *UNUSED(scene), World *UNUSED(wo)) +{ + return e_data.default_volume_sh; +} + struct GPUMaterial *EEVEE_material_mesh_get( struct Scene *scene, Material *ma, bool use_ao, bool use_bent_normals) @@ -762,6 +785,7 @@ void EEVEE_materials_free(void) DRW_SHADER_FREE_SAFE(e_data.default_lit[i]); } MEM_SAFE_FREE(e_data.frag_shader_lib); + DRW_SHADER_FREE_SAFE(e_data.default_volume_sh); DRW_SHADER_FREE_SAFE(e_data.default_prepass_sh); DRW_SHADER_FREE_SAFE(e_data.default_prepass_clip_sh); DRW_SHADER_FREE_SAFE(e_data.default_background); diff --git a/source/blender/draw/engines/eevee/eevee_private.h b/source/blender/draw/engines/eevee/eevee_private.h index 0c4de70be1b..727edfb9607 100644 --- a/source/blender/draw/engines/eevee/eevee_private.h +++ b/source/blender/draw/engines/eevee/eevee_private.h @@ -93,6 +93,8 @@ typedef struct EEVEE_PassList { struct DRWPass *minmaxz_downlevel; struct DRWPass *minmaxz_downdepth; struct DRWPass *minmaxz_copydepth; + struct DRWPass *volumetric_integrate_ps; + struct DRWPass *volumetric_resolve_ps; struct DRWPass *depth_pass; struct DRWPass *depth_pass_cull; @@ -106,17 +108,18 @@ 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 */ - struct GPUFrameBuffer *bloom_accum_fb[MAX_BLOOM_STEP-1]; /* HDR */ + struct GPUFrameBuffer *effect_fb; + struct GPUFrameBuffer *bloom_blit_fb; + struct GPUFrameBuffer *bloom_down_fb[MAX_BLOOM_STEP]; + struct GPUFrameBuffer *bloom_accum_fb[MAX_BLOOM_STEP-1]; struct GPUFrameBuffer *dof_down_fb; struct GPUFrameBuffer *dof_scatter_far_fb; struct GPUFrameBuffer *dof_scatter_near_fb; + struct GPUFrameBuffer *volumetric_fb; struct GPUFrameBuffer *planarref_fb; - struct GPUFrameBuffer *main; /* HDR */ + struct GPUFrameBuffer *main; } EEVEE_FramebufferList; typedef struct EEVEE_TextureList { @@ -312,6 +315,7 @@ enum { EFFECT_MOTION_BLUR = (1 << 0), EFFECT_BLOOM = (1 << 1), EFFECT_DOF = (1 << 2), + EFFECT_VOLUMETRIC = (1 << 3), }; /* ************** SCENE LAYER DATA ************** */ @@ -399,7 +403,9 @@ typedef struct EEVEE_PrivateData { struct GHash *material_hash; struct GHash *hair_material_hash; struct GPUTexture *minmaxz; + struct GPUTexture *volumetric; float background_alpha; /* TODO find a better place for this. */ + float viewvecs[2][4]; /* For planar probes */ float texel_size[2]; } EEVEE_PrivateData; /* Transient data */ @@ -411,12 +417,14 @@ EEVEE_LightProbeEngineData *EEVEE_lightprobe_data_get(Object *ob); EEVEE_LampEngineData *EEVEE_lamp_data_get(Object *ob); /* eevee_materials.c */ -void EEVEE_materials_init(void); +struct GPUTexture *EEVEE_materials_get_util_tex(void); /* XXX */ +void EEVEE_materials_init(EEVEE_StorageList *stl); void EEVEE_materials_cache_init(EEVEE_Data *vedata); void EEVEE_materials_cache_populate(EEVEE_Data *vedata, EEVEE_SceneLayerData *sldata, Object *ob, struct Gwn_Batch *geom); void EEVEE_materials_cache_finish(EEVEE_Data *vedata); struct GPUMaterial *EEVEE_material_world_lightprobe_get(struct Scene *scene, struct World *wo); struct GPUMaterial *EEVEE_material_world_background_get(struct Scene *scene, struct World *wo); +struct GPUShader *EEVEE_material_world_volume_get(struct Scene *scene, struct World *wo); struct GPUMaterial *EEVEE_material_mesh_lightprobe_get(struct Scene *scene, Material *ma); struct GPUMaterial *EEVEE_material_mesh_get(struct Scene *scene, Material *ma, bool use_ao, bool use_bent_normals); struct GPUMaterial *EEVEE_material_hair_get(struct Scene *scene, Material *ma, bool use_ao, bool use_bent_normals); @@ -443,8 +451,9 @@ 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_effects_cache_init(EEVEE_SceneLayerData *sldata, EEVEE_Data *vedata); void EEVEE_create_minmax_buffer(EEVEE_Data *vedata, struct GPUTexture *depth_src); +void EEVEE_effects_do_volumetrics(EEVEE_Data *vedata); void EEVEE_draw_effects(EEVEE_Data *vedata); void EEVEE_effects_free(void); 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 40dc6d4e6f7..6f255583340 100644 --- a/source/blender/draw/engines/eevee/shaders/bsdf_common_lib.glsl +++ b/source/blender/draw/engines/eevee/shaders/bsdf_common_lib.glsl @@ -225,12 +225,21 @@ float buffer_depth(bool is_persp, float z, float zf, float zn) } } -vec3 get_view_space_from_depth(vec2 uvcoords, float depth) +float get_view_z_from_depth(float depth) { if (ProjectionMatrix[3][3] == 0.0) { float d = 2.0 * depth - 1.0; - float zview = -ProjectionMatrix[3][2] / (d + ProjectionMatrix[2][2]); - return (viewvecs[0].xyz + vec3(uvcoords, 0.0) * viewvecs[1].xyz) * zview; + return -ProjectionMatrix[3][2] / (d + ProjectionMatrix[2][2]); + } + else { + return viewvecs[0].z + depth * viewvecs[1].z; + } +} + +vec3 get_view_space_from_depth(vec2 uvcoords, float depth) +{ + if (ProjectionMatrix[3][3] == 0.0) { + return (viewvecs[0].xyz + vec3(uvcoords, 0.0) * viewvecs[1].xyz) * get_view_z_from_depth(depth); } else { return viewvecs[0].xyz + vec3(uvcoords, depth) * viewvecs[1].xyz; diff --git a/source/blender/draw/engines/eevee/shaders/volumetric_frag.glsl b/source/blender/draw/engines/eevee/shaders/volumetric_frag.glsl new file mode 100644 index 00000000000..8e7393a2f58 --- /dev/null +++ b/source/blender/draw/engines/eevee/shaders/volumetric_frag.glsl @@ -0,0 +1,303 @@ + +out vec4 FragColor; + +#ifdef STEP_INTEGRATE + +uniform sampler2D depthFull; + +float find_next_step(float near, float far, float noise, int iter, int iter_count) +{ + const float lambda = 0.8f; /* TODO : Parameter */ + + float progress = (float(iter) + noise) / float(iter_count); + + float linear_split = mix(near, far, progress); + + if (ProjectionMatrix[3][3] == 0.0) { + float exp_split = near * pow(far / near, progress); + return mix(linear_split, exp_split, lambda); + } + else { + return linear_split; + } +} + +void participating_media_properties(vec3 wpos, out vec3 absorption, out vec3 scattering, out float anisotropy) +{ + /* TODO Call nodetree from here. */ + absorption = vec3(0.00); + scattering = vec3(1.0) * step(-1.0, -wpos.z); + + anisotropy = -0.8; +} + +float phase_function_isotropic() +{ + return 1.0 / (4.0 * M_PI); +} + +float phase_function(vec3 v, vec3 l, float g) +{ +#if 0 + /* Henyey-Greenstein */ + float cos_theta = dot(v, l); + float sqr_g = g * g; + return (1- sqr_g) / (4.0 * M_PI * pow(1 + sqr_g - 2 * g * cos_theta, 3.0 / 2.0)); +#else + return phase_function_isotropic(); +#endif +} + +vec3 light_volume(LightData ld, vec4 l_vector, vec3 l_col) +{ + float dist = max(1e-4, abs(l_vector.w - ld.l_radius)); + return l_col * (4.0 * ld.l_radius * ld.l_radius * M_PI * M_PI) / (dist * dist); +} + +/* Based on Frosbite Unified Volumetric. + * https://www.ea.com/frostbite/news/physically-based-unified-volumetric-rendering-in-frostbite */ +void main() +{ + vec2 uv = (gl_FragCoord.xy * 2.0) / ivec2(textureSize(depthFull, 0)); + float scene_depth = texelFetch(depthFull, ivec2(gl_FragCoord.xy) * 2, 0).r; /* use the same depth as in the upsample step */ + vec3 vpos = get_view_space_from_depth(uv, scene_depth); + vec3 wpos = (ViewMatrixInverse * vec4(vpos, 1.0)).xyz; + vec3 wdir = (ProjectionMatrix[3][3] == 0.0) ? normalize(cameraPos - wpos) : cameraForward; + + /* Note: this is NOT the distance to the camera. */ + float max_z = vpos.z; + + /* project ray to clip plane so we can integrate in even steps in clip space. */ + vec3 wdir_proj = wdir / abs(dot(cameraForward, wdir)); + float wlen = length(wdir_proj); + + /* Transmittance: How much light can get through. */ + vec3 transmittance = vec3(1.0); + + /* Scattering: Light that has been accumulated from scattered light sources. */ + vec3 scattering = vec3(0.0); + + vec3 ray_origin = (ProjectionMatrix[3][3] == 0.0) + ? cameraPos + : (ViewMatrixInverse * vec4(get_view_space_from_depth(uv, 0.5), 1.0)).xyz; + + /* Start from near clip. TODO make start distance an option. */ + float rand = texture(utilTex, vec3(gl_FragCoord.xy / LUT_SIZE, 2.0)).r; + /* Less noisy but noticeable patterns, could work better with temporal AA. */ + // float rand = (1.0 / 16.0) * float(((int(gl_FragCoord.x + gl_FragCoord.y) & 0x3) << 2) + (int(gl_FragCoord.x) & 0x3)); + float near = get_view_z_from_depth(0.0); + float far = get_view_z_from_depth(1.0); + float dist = near; + for (int i = 1; i < 64; ++i) { + float new_dist = find_next_step(near, far, rand, i, 64); + float step = dist - new_dist; /* Marching step */ + dist = new_dist; + + vec3 ray_wpos = ray_origin + wdir_proj * dist; + + /* Volume Sample */ + vec3 s_absorption, s_scattering; /* mu_a, mu_s */ + float s_anisotropy; + participating_media_properties(ray_wpos, s_absorption, s_scattering, s_anisotropy); + + vec3 s_extinction = max(vec3(1e-8), s_absorption + s_scattering); /* mu_t */ + + /* Evaluate each light */ + vec3 Lscat = vec3(0.0); + +#if 1 /* Lights */ + for (int i = 0; i < MAX_LIGHT && i < light_count; ++i) { + LightData ld = lights_data[i]; + + vec4 l_vector; + l_vector.xyz = ld.l_position - ray_wpos; + l_vector.w = length(l_vector.xyz); + +#if 1 /* Shadows & Spots */ + float Vis = light_visibility(ld, ray_wpos, l_vector); +#else + float Vis = 1.0; +#endif + vec3 Li = light_volume(ld, l_vector, ld.l_color); + + Lscat += Li * Vis * s_scattering * phase_function(-wdir, l_vector.xyz / l_vector.w, s_anisotropy); + } +#endif + + /* Environment : Average color. */ + IrradianceData ir_data = load_irradiance_cell(0, vec3(1.0)); + Lscat += (ir_data.cubesides[0] + ir_data.cubesides[1] + ir_data.cubesides[2]) * 0.333333 * s_scattering * phase_function_isotropic(); + + ir_data = load_irradiance_cell(0, vec3(-1.0)); + Lscat += (ir_data.cubesides[0] + ir_data.cubesides[1] + ir_data.cubesides[2]) * 0.333333 * s_scattering * phase_function_isotropic(); + + /* Evaluate Scattering */ + float s_len = wlen * step; + vec3 Tr = exp(-s_extinction * s_len); + + /* integrate along the current step segment */ + Lscat = (Lscat - Lscat * Tr) / s_extinction; + /* accumulate and also take into account the transmittance from previous steps */ + scattering += transmittance * Lscat; + + /* Evaluate transmittance to view independantely */ + transmittance *= Tr; + + if (dist < max_z) + break; + } + + float mono_transmittance = dot(transmittance, vec3(1.0)) / 3.0; + + FragColor = vec4(scattering, mono_transmittance); +} + +#else /* STEP_UPSAMPLE */ + +uniform sampler2D depthFull; +uniform sampler2D volumetricBuffer; + +uniform mat4 ProjectionMatrix; + +vec4 get_view_z_from_depth(vec4 depth) +{ + vec4 d = 2.0 * depth - 1.0; + return -ProjectionMatrix[3][2] / (d + ProjectionMatrix[2][2]); +} + +void main() +{ +#if 0 /* 2 x 2 with bilinear */ + + const vec4 bilinear_weights[4] = vec4[4]( + vec4(9.0 / 16.0, 3.0 / 16.0, 3.0 / 16.0, 1.0 / 16.0 ), + vec4(3.0 / 16.0, 9.0 / 16.0, 1.0 / 16.0, 3.0 / 16.0 ), + vec4(3.0 / 16.0, 1.0 / 16.0, 9.0 / 16.0, 3.0 / 16.0 ), + vec4(1.0 / 16.0, 3.0 / 16.0, 3.0 / 16.0, 9.0 / 16.0 ) + ); + + /* Depth aware upsampling */ + vec4 depths; + ivec2 texel_co = ivec2(gl_FragCoord.xy * 0.5) * 2; + + /* TODO use textureGather on glsl 4.0 */ + depths.x = texelFetchOffset(depthFull, texel_co, 0, ivec2(0, 0)).r; + depths.y = texelFetchOffset(depthFull, texel_co, 0, ivec2(2, 0)).r; + depths.z = texelFetchOffset(depthFull, texel_co, 0, ivec2(0, 2)).r; + depths.w = texelFetchOffset(depthFull, texel_co, 0, ivec2(2, 2)).r; + + vec4 target_depth = texelFetch(depthFull, ivec2(gl_FragCoord.xy), 0).rrrr; + + depths = get_view_z_from_depth(depths); + target_depth = get_view_z_from_depth(target_depth); + + vec4 weights = 1.0 - step(0.05, abs(depths - target_depth)); + + /* Index in range [0-3] */ + int pix_id = int(dot(mod(ivec2(gl_FragCoord.xy), 2), ivec2(1, 2))); + weights *= bilinear_weights[pix_id]; + + float weight_sum = dot(weights, vec4(1.0)); + + if (weight_sum == 0.0) { + weights.x = 1.0; + weight_sum = 1.0; + } + + texel_co = ivec2(gl_FragCoord.xy * 0.5); + + vec4 integration_result; + integration_result = texelFetchOffset(volumetricBuffer, texel_co, 0, ivec2(0, 0)) * weights.x; + integration_result += texelFetchOffset(volumetricBuffer, texel_co, 0, ivec2(1, 0)) * weights.y; + integration_result += texelFetchOffset(volumetricBuffer, texel_co, 0, ivec2(0, 1)) * weights.z; + integration_result += texelFetchOffset(volumetricBuffer, texel_co, 0, ivec2(1, 1)) * weights.w; + +#else /* 4 x 4 */ + + /* Depth aware upsampling */ + vec4 depths[4]; + ivec2 texel_co = ivec2(gl_FragCoord.xy * 0.5) * 2; + + /* TODO use textureGather on glsl 4.0 */ + texel_co += ivec2(-2, -2); + depths[0].x = texelFetchOffset(depthFull, texel_co, 0, ivec2(0, 0)).r; + depths[0].y = texelFetchOffset(depthFull, texel_co, 0, ivec2(2, 0)).r; + depths[0].z = texelFetchOffset(depthFull, texel_co, 0, ivec2(0, 2)).r; + depths[0].w = texelFetchOffset(depthFull, texel_co, 0, ivec2(2, 2)).r; + + texel_co += ivec2(4, 0); + depths[1].x = texelFetchOffset(depthFull, texel_co, 0, ivec2(0, 0)).r; + depths[1].y = texelFetchOffset(depthFull, texel_co, 0, ivec2(2, 0)).r; + depths[1].z = texelFetchOffset(depthFull, texel_co, 0, ivec2(0, 2)).r; + depths[1].w = texelFetchOffset(depthFull, texel_co, 0, ivec2(2, 2)).r; + + texel_co += ivec2(-4, 4); + depths[2].x = texelFetchOffset(depthFull, texel_co, 0, ivec2(0, 0)).r; + depths[2].y = texelFetchOffset(depthFull, texel_co, 0, ivec2(2, 0)).r; + depths[2].z = texelFetchOffset(depthFull, texel_co, 0, ivec2(0, 2)).r; + depths[2].w = texelFetchOffset(depthFull, texel_co, 0, ivec2(2, 2)).r; + + texel_co += ivec2(4, 0); + depths[3].x = texelFetchOffset(depthFull, texel_co, 0, ivec2(0, 0)).r; + depths[3].y = texelFetchOffset(depthFull, texel_co, 0, ivec2(2, 0)).r; + depths[3].z = texelFetchOffset(depthFull, texel_co, 0, ivec2(0, 2)).r; + depths[3].w = texelFetchOffset(depthFull, texel_co, 0, ivec2(2, 2)).r; + + vec4 target_depth = texelFetch(depthFull, ivec2(gl_FragCoord.xy), 0).rrrr; + + depths[0] = get_view_z_from_depth(depths[0]); + depths[1] = get_view_z_from_depth(depths[1]); + depths[2] = get_view_z_from_depth(depths[2]); + depths[3] = get_view_z_from_depth(depths[3]); + + target_depth = get_view_z_from_depth(target_depth); + + vec4 weights[4]; + weights[0] = 1.0 - step(0.05, abs(depths[0] - target_depth)); + weights[1] = 1.0 - step(0.05, abs(depths[1] - target_depth)); + weights[2] = 1.0 - step(0.05, abs(depths[2] - target_depth)); + weights[3] = 1.0 - step(0.05, abs(depths[3] - target_depth)); + + float weight_sum; + weight_sum = dot(weights[0], vec4(1.0)); + weight_sum += dot(weights[1], vec4(1.0)); + weight_sum += dot(weights[2], vec4(1.0)); + weight_sum += dot(weights[3], vec4(1.0)); + + if (weight_sum == 0.0) { + weights[0].x = 1.0; + weight_sum = 1.0; + } + + texel_co = ivec2(gl_FragCoord.xy * 0.5); + + vec4 integration_result; + + texel_co += ivec2(-1, -1); + integration_result = texelFetchOffset(volumetricBuffer, texel_co, 0, ivec2(0, 0)) * weights[0].x; + integration_result += texelFetchOffset(volumetricBuffer, texel_co, 0, ivec2(1, 0)) * weights[0].y; + integration_result += texelFetchOffset(volumetricBuffer, texel_co, 0, ivec2(0, 1)) * weights[0].z; + integration_result += texelFetchOffset(volumetricBuffer, texel_co, 0, ivec2(1, 1)) * weights[0].w; + + texel_co += ivec2(2, 0); + integration_result += texelFetchOffset(volumetricBuffer, texel_co, 0, ivec2(0, 0)) * weights[1].x; + integration_result += texelFetchOffset(volumetricBuffer, texel_co, 0, ivec2(1, 0)) * weights[1].y; + integration_result += texelFetchOffset(volumetricBuffer, texel_co, 0, ivec2(0, 1)) * weights[1].z; + integration_result += texelFetchOffset(volumetricBuffer, texel_co, 0, ivec2(1, 1)) * weights[1].w; + + texel_co += ivec2(-2, 2); + integration_result += texelFetchOffset(volumetricBuffer, texel_co, 0, ivec2(0, 0)) * weights[2].x; + integration_result += texelFetchOffset(volumetricBuffer, texel_co, 0, ivec2(1, 0)) * weights[2].y; + integration_result += texelFetchOffset(volumetricBuffer, texel_co, 0, ivec2(0, 1)) * weights[2].z; + integration_result += texelFetchOffset(volumetricBuffer, texel_co, 0, ivec2(1, 1)) * weights[2].w; + + texel_co += ivec2(2, 0); + integration_result += texelFetchOffset(volumetricBuffer, texel_co, 0, ivec2(0, 0)) * weights[3].x; + integration_result += texelFetchOffset(volumetricBuffer, texel_co, 0, ivec2(1, 0)) * weights[3].y; + integration_result += texelFetchOffset(volumetricBuffer, texel_co, 0, ivec2(0, 1)) * weights[3].z; + integration_result += texelFetchOffset(volumetricBuffer, texel_co, 0, ivec2(1, 1)) * weights[3].w; +#endif + + FragColor = integration_result / weight_sum; +} +#endif diff --git a/source/blender/makesrna/intern/rna_scene.c b/source/blender/makesrna/intern/rna_scene.c index eec7c8b910d..8ff88af2df4 100644 --- a/source/blender/makesrna/intern/rna_scene.c +++ b/source/blender/makesrna/intern/rna_scene.c @@ -2612,6 +2612,7 @@ RNA_LAYER_ENGINE_EEVEE_GET_SET_FLOAT(bloom_intensity) RNA_LAYER_ENGINE_EEVEE_GET_SET_BOOL(motion_blur_enable) RNA_LAYER_ENGINE_EEVEE_GET_SET_INT(motion_blur_samples) RNA_LAYER_ENGINE_EEVEE_GET_SET_FLOAT(motion_blur_shutter) +RNA_LAYER_ENGINE_EEVEE_GET_SET_BOOL(volumetric_enable) /* object engine */ RNA_LAYER_MODE_OBJECT_GET_SET_BOOL(show_wire) @@ -6166,6 +6167,14 @@ static void rna_def_scene_layer_engine_settings_eevee(BlenderRNA *brna) /* see RNA_LAYER_ENGINE_GET_SET macro */ + /* Volumetrics */ + prop = RNA_def_property(srna, "volumetric_enable", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_funcs(prop, "rna_LayerEngineSettings_Eevee_volumetric_enable_get", + "rna_LayerEngineSettings_Eevee_volumetric_enable_set"); + RNA_def_property_ui_text(prop, "Volumetrics", "Enable scattering and absorbance of volumetric material"); + RNA_def_property_flag(prop, PROP_CONTEXT_UPDATE); + RNA_def_property_update(prop, NC_SCENE | ND_LAYER_CONTENT, "rna_SceneLayerEngineSettings_update"); + /* Ambient Occlusion */ prop = RNA_def_property(srna, "gtao_enable", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_funcs(prop, "rna_LayerEngineSettings_Eevee_gtao_enable_get", |