diff options
author | Clément Foucault <foucault.clem@gmail.com> | 2017-10-24 15:49:00 +0300 |
---|---|---|
committer | Clément Foucault <foucault.clem@gmail.com> | 2017-10-27 23:49:15 +0300 |
commit | 66d8f82b832b58cba3273c0a4196fae6db0e1efd (patch) | |
tree | 682a3aba8fbd23ccd72a4e4f2a02fb36ad0a0d01 /source/blender/draw/engines | |
parent | 1c0c63ce5b3914be2d0828260e5ac777a7596d36 (diff) |
Eevee: Overhaul the volumetric system.
The system now uses several 3D textures in order to decouple every steps of the volumetric rendering.
See https://www.ea.com/frostbite/news/physically-based-unified-volumetric-rendering-in-frostbite for more details.
On the technical side, instead of using a compute shader to populate the 3D textures we use layered rendering with a geometry shader to render 1 fullscreen triangle per 3D texture slice.
Diffstat (limited to 'source/blender/draw/engines')
14 files changed, 760 insertions, 595 deletions
diff --git a/source/blender/draw/engines/eevee/eevee_effects.c b/source/blender/draw/engines/eevee/eevee_effects.c index 5f61ed66386..2306dbbab0a 100644 --- a/source/blender/draw/engines/eevee/eevee_effects.c +++ b/source/blender/draw/engines/eevee/eevee_effects.c @@ -40,6 +40,8 @@ #include "BKE_animsys.h" #include "BKE_screen.h" +#include "ED_screen.h" + #include "DEG_depsgraph.h" #include "BLI_dynstr.h" @@ -50,12 +52,6 @@ #include "GPU_framebuffer.h" #include "GPU_texture.h" -#define SHADER_DEFINES \ - "#define EEVEE_ENGINE\n" \ - "#define MAX_PROBE " STRINGIFY(MAX_PROBE) "\n" \ - "#define MAX_GRID " STRINGIFY(MAX_GRID) "\n" \ - "#define MAX_PLANAR " STRINGIFY(MAX_PLANAR) "\n" - typedef struct EEVEE_LightProbeData { short probe_id, shadow_id; } EEVEE_LightProbeData; @@ -69,6 +65,9 @@ enum { }; static struct { + char *volumetric_common_lib; + char *volumetric_common_lamps_lib; + /* Downsample Depth */ struct GPUShader *minz_downlevel_sh; struct GPUShader *maxz_downlevel_sh; @@ -94,7 +93,9 @@ static struct { struct GPUShader *dof_resolve_sh; /* Volumetric */ - struct GPUShader *volumetric_upsample_sh; + struct GPUShader *volumetric_scatter_sh; + struct GPUShader *volumetric_integration_sh; + struct GPUShader *volumetric_resolve_sh; /* Screen Space Reflection */ struct GPUShader *ssr_sh[SSR_MAX_SHADER]; @@ -120,6 +121,7 @@ static struct { extern char datatoc_ambient_occlusion_lib_glsl[]; extern char datatoc_bsdf_common_lib_glsl[]; +extern char datatoc_bsdf_direct_lib_glsl[]; extern char datatoc_bsdf_sampling_lib_glsl[]; extern char datatoc_octahedron_lib_glsl[]; extern char datatoc_effect_temporal_aa_glsl[]; @@ -133,12 +135,21 @@ extern char datatoc_effect_dof_frag_glsl[]; extern char datatoc_effect_downsample_frag_glsl[]; extern char datatoc_effect_downsample_cube_frag_glsl[]; extern char datatoc_effect_gtao_frag_glsl[]; +extern char datatoc_irradiance_lib_glsl[]; +extern char datatoc_lamps_lib_glsl[]; extern char datatoc_lightprobe_lib_glsl[]; extern char datatoc_lightprobe_vert_glsl[]; extern char datatoc_lightprobe_geom_glsl[]; extern char datatoc_raytrace_lib_glsl[]; extern char datatoc_tonemap_frag_glsl[]; extern char datatoc_volumetric_frag_glsl[]; +extern char datatoc_volumetric_geom_glsl[]; +extern char datatoc_volumetric_vert_glsl[]; +extern char datatoc_volumetric_resolve_frag_glsl[]; +extern char datatoc_volumetric_scatter_frag_glsl[]; +extern char datatoc_volumetric_integration_frag_glsl[]; +extern char datatoc_volumetric_lib_glsl[]; +extern char datatoc_gpu_shader_fullscreen_vert_glsl[]; static void eevee_motion_blur_camera_get_matrix_at_time( const bContext *C, Scene *scene, ARegion *ar, RegionView3D *rv3d, View3D *v3d, Object *camera, float time, float r_mat[4][4]) @@ -273,7 +284,36 @@ void EEVEE_effects_init(EEVEE_SceneLayerData *sldata, EEVEE_Data *vedata) datatoc_lightprobe_geom_glsl, datatoc_effect_downsample_cube_frag_glsl, NULL); - e_data.volumetric_upsample_sh = DRW_shader_create_fullscreen(datatoc_volumetric_frag_glsl, "#define STEP_UPSAMPLE\n"); + ds_frag = BLI_dynstr_new(); + BLI_dynstr_append(ds_frag, datatoc_bsdf_common_lib_glsl); + BLI_dynstr_append(ds_frag, datatoc_volumetric_lib_glsl); + e_data.volumetric_common_lib = BLI_dynstr_get_cstring(ds_frag); + BLI_dynstr_free(ds_frag); + + ds_frag = BLI_dynstr_new(); + BLI_dynstr_append(ds_frag, datatoc_bsdf_common_lib_glsl); + BLI_dynstr_append(ds_frag, datatoc_bsdf_direct_lib_glsl); + BLI_dynstr_append(ds_frag, datatoc_irradiance_lib_glsl); + BLI_dynstr_append(ds_frag, datatoc_octahedron_lib_glsl); + BLI_dynstr_append(ds_frag, datatoc_lamps_lib_glsl); + BLI_dynstr_append(ds_frag, datatoc_volumetric_lib_glsl); + e_data.volumetric_common_lamps_lib = BLI_dynstr_get_cstring(ds_frag); + BLI_dynstr_free(ds_frag); + + e_data.volumetric_scatter_sh = DRW_shader_create_with_lib(datatoc_volumetric_vert_glsl, + datatoc_volumetric_geom_glsl, + datatoc_volumetric_scatter_frag_glsl, + e_data.volumetric_common_lamps_lib, + SHADER_DEFINES + "#define VOLUMETRICS\n" + "#define VOLUME_SHADOW\n"); + e_data.volumetric_integration_sh = DRW_shader_create_with_lib(datatoc_volumetric_vert_glsl, + datatoc_volumetric_geom_glsl, + datatoc_volumetric_integration_frag_glsl, + e_data.volumetric_common_lib, NULL); + e_data.volumetric_resolve_sh = DRW_shader_create_with_lib(datatoc_gpu_shader_fullscreen_vert_glsl, NULL, + datatoc_volumetric_resolve_frag_glsl, + e_data.volumetric_common_lib, NULL); e_data.minz_downlevel_sh = DRW_shader_create_fullscreen(datatoc_effect_minmaxz_frag_glsl, "#define MIN_PASS\n"); e_data.maxz_downlevel_sh = DRW_shader_create_fullscreen(datatoc_effect_minmaxz_frag_glsl, "#define MAX_PASS\n"); @@ -594,9 +634,182 @@ void EEVEE_effects_init(EEVEE_SceneLayerData *sldata, EEVEE_Data *vedata) DRW_FRAMEBUFFER_FREE_SAFE(fbl->depth_double_buffer_fb); } - effects->enabled_effects = enabled_effects; + if (BKE_collection_engine_property_value_get_bool(props, "volumetric_enable")) { + + effects->enabled_effects |= EFFECT_VOLUMETRIC; + + if (sldata->volumetrics == NULL) { + sldata->volumetrics = MEM_callocN(sizeof(EEVEE_VolumetricsInfo), "EEVEE_VolumetricsInfo"); + } + + EEVEE_VolumetricsInfo *volumetrics = sldata->volumetrics; + + int tile_size = BKE_collection_engine_property_value_get_int(props, "volumetric_tile_size"); + + /* Find Froxel Texture resolution. */ + int froxel_tex_size[3]; + + froxel_tex_size[0] = (int)ceilf(fmaxf(1.0f, viewport_size[0] / (float)tile_size)); + froxel_tex_size[1] = (int)ceilf(fmaxf(1.0f, viewport_size[1] / (float)tile_size)); + froxel_tex_size[2] = max_ii(BKE_collection_engine_property_value_get_int(props, "volumetric_samples"), 1); + + volumetrics->volume_coord_scale[0] = viewport_size[0] / (float)(tile_size * froxel_tex_size[0]); + volumetrics->volume_coord_scale[1] = viewport_size[1] / (float)(tile_size * froxel_tex_size[1]); + + /* TODO compute snap to maxZBuffer for clustered rendering */ + + if ((volumetrics->froxel_tex_size[0] != froxel_tex_size[0]) || + (volumetrics->froxel_tex_size[1] != froxel_tex_size[1]) || + (volumetrics->froxel_tex_size[2] != froxel_tex_size[2])) + { + DRW_TEXTURE_FREE_SAFE(txl->volume_prop_scattering); + DRW_TEXTURE_FREE_SAFE(txl->volume_prop_extinction); + DRW_TEXTURE_FREE_SAFE(txl->volume_prop_emission); + DRW_TEXTURE_FREE_SAFE(txl->volume_prop_phase); + DRW_TEXTURE_FREE_SAFE(txl->volume_scatter); + DRW_TEXTURE_FREE_SAFE(txl->volume_transmittance); + DRW_TEXTURE_FREE_SAFE(txl->volume_scatter_history); + DRW_TEXTURE_FREE_SAFE(txl->volume_transmittance_history); + DRW_FRAMEBUFFER_FREE_SAFE(fbl->volumetric_fb); + DRW_FRAMEBUFFER_FREE_SAFE(fbl->volumetric_scat_fb); + DRW_FRAMEBUFFER_FREE_SAFE(fbl->volumetric_integ_fb); + volumetrics->froxel_tex_size[0] = froxel_tex_size[0]; + volumetrics->froxel_tex_size[1] = froxel_tex_size[1]; + volumetrics->froxel_tex_size[2] = froxel_tex_size[2]; + + volumetrics->inv_tex_size[0] = 1.0f / (float)(volumetrics->froxel_tex_size[0]); + volumetrics->inv_tex_size[1] = 1.0f / (float)(volumetrics->froxel_tex_size[1]); + volumetrics->inv_tex_size[2] = 1.0f / (float)(volumetrics->froxel_tex_size[2]); + } + + /* Like frostbite's paper, 5% blend of the new frame. */ + volumetrics->history_alpha = (txl->volume_prop_scattering == NULL) ? 0.0f : 0.95f; + + if (txl->volume_prop_scattering == NULL) { + /* Volume properties: We evaluate all volumetric objects + * and store their final properties into each froxel */ + txl->volume_prop_scattering = DRW_texture_create_3D(froxel_tex_size[0], froxel_tex_size[1], froxel_tex_size[2], DRW_TEX_RGB_11_11_10, DRW_TEX_FILTER, NULL); + txl->volume_prop_extinction = DRW_texture_create_3D(froxel_tex_size[0], froxel_tex_size[1], froxel_tex_size[2], DRW_TEX_RGB_11_11_10, DRW_TEX_FILTER, NULL); + txl->volume_prop_emission = DRW_texture_create_3D(froxel_tex_size[0], froxel_tex_size[1], froxel_tex_size[2], DRW_TEX_RGB_11_11_10, DRW_TEX_FILTER, NULL); + txl->volume_prop_phase = DRW_texture_create_3D(froxel_tex_size[0], froxel_tex_size[1], froxel_tex_size[2], DRW_TEX_RG_16, DRW_TEX_FILTER, NULL); + + /* Volume scattering: We compute for each froxel the + * Scattered light towards the view. We also resolve temporal + * super sampling during this stage. */ + txl->volume_scatter = DRW_texture_create_3D(froxel_tex_size[0], froxel_tex_size[1], froxel_tex_size[2], DRW_TEX_RGB_11_11_10, DRW_TEX_FILTER, NULL); + txl->volume_transmittance = DRW_texture_create_3D(froxel_tex_size[0], froxel_tex_size[1], froxel_tex_size[2], DRW_TEX_RGB_11_11_10, DRW_TEX_FILTER, NULL); + + /* Final integration: We compute for each froxel the + * amount of scattered light and extinction coef at this + * given depth. We use theses textures as double buffer + * for the volumetric history. */ + txl->volume_scatter_history = DRW_texture_create_3D(froxel_tex_size[0], froxel_tex_size[1], froxel_tex_size[2], DRW_TEX_RGB_11_11_10, DRW_TEX_FILTER, NULL); + txl->volume_transmittance_history = DRW_texture_create_3D(froxel_tex_size[0], froxel_tex_size[1], froxel_tex_size[2], DRW_TEX_RGB_11_11_10, DRW_TEX_FILTER, NULL); + } + + /* Temporal Super sampling jitter */ + double ht_point[3]; + double ht_offset[3] = {0.0, 0.0}; + unsigned int ht_primes[3] = {3, 7, 2}; + static unsigned int current_sample = 0; + struct wmWindowManager *wm = CTX_wm_manager(draw_ctx->evil_C); + + if (((effects->enabled_effects & EFFECT_TAA) != 0) && (ED_screen_animation_no_scrub(wm) == NULL)) { + /* If TAA is in use do not use the history buffer. */ + volumetrics->history_alpha = 0.0f; + current_sample = effects->taa_current_sample - 1; + } + else { + current_sample = (current_sample + 1) % (ht_primes[0] * ht_primes[1] * ht_primes[2]); + } + BLI_halton_3D(ht_primes, ht_offset, current_sample, ht_point); + + volumetrics->jitter[0] = (float)ht_point[0]; + volumetrics->jitter[1] = (float)ht_point[1]; + volumetrics->jitter[2] = (float)ht_point[2]; + + /* Framebuffer setup */ + DRWFboTexture tex_vol[4] = {{&txl->volume_prop_scattering, DRW_TEX_RGB_11_11_10, DRW_TEX_FILTER}, + {&txl->volume_prop_extinction, DRW_TEX_RGB_11_11_10, DRW_TEX_FILTER}, + {&txl->volume_prop_emission, DRW_TEX_RGB_11_11_10, DRW_TEX_FILTER}, + {&txl->volume_prop_phase, DRW_TEX_RG_16, DRW_TEX_FILTER}}; + + DRW_framebuffer_init(&fbl->volumetric_fb, &draw_engine_eevee_type, + (int)froxel_tex_size[0], (int)froxel_tex_size[1], + tex_vol, 4); + + DRWFboTexture tex_vol_scat[2] = {{&txl->volume_scatter, DRW_TEX_RGB_11_11_10, DRW_TEX_FILTER}, + {&txl->volume_transmittance, DRW_TEX_RGB_11_11_10, DRW_TEX_FILTER}}; + + DRW_framebuffer_init(&fbl->volumetric_scat_fb, &draw_engine_eevee_type, + (int)froxel_tex_size[0], (int)froxel_tex_size[1], + tex_vol_scat, 2); + + DRWFboTexture tex_vol_integ[2] = {{&txl->volume_scatter_history, DRW_TEX_RGB_11_11_10, DRW_TEX_FILTER}, + {&txl->volume_transmittance_history, DRW_TEX_RGB_11_11_10, DRW_TEX_FILTER}}; + + DRW_framebuffer_init(&fbl->volumetric_integ_fb, &draw_engine_eevee_type, + (int)froxel_tex_size[0], (int)froxel_tex_size[1], + tex_vol_integ, 2); + + volumetrics->integration_start = BKE_collection_engine_property_value_get_float(props, "volumetric_start"); + volumetrics->integration_end = BKE_collection_engine_property_value_get_float(props, "volumetric_end"); + volumetrics->sample_distribution = 4.0f * (1.00001f - BKE_collection_engine_property_value_get_float(props, "volumetric_sample_distribution")); + volumetrics->use_volume_shadows = BKE_collection_engine_property_value_get_bool(props, "volumetric_shadows"); + volumetrics->light_clamp = BKE_collection_engine_property_value_get_float(props, "volumetric_light_clamp"); + + if (volumetrics->use_volume_shadows) { + volumetrics->shadow_step_count = (float)BKE_collection_engine_property_value_get_int(props, "volumetric_shadow_samples"); + } + else { + volumetrics->shadow_step_count = 0; + } + + if (DRW_viewport_is_persp_get()) { + const float clip_start = stl->g_data->viewvecs[0][2]; + /* Negate */ + float near = volumetrics->integration_start = min_ff(-volumetrics->integration_start, clip_start - 1e-4f); + float far = volumetrics->integration_end = min_ff(-volumetrics->integration_end, near - 1e-4f); + + volumetrics->depth_param[0] = (far - near * exp2(1.0f / volumetrics->sample_distribution)) / (far - near); + volumetrics->depth_param[1] = (1.0f - volumetrics->depth_param[0]) / near; + volumetrics->depth_param[2] = volumetrics->sample_distribution; + } + else { + const float clip_start = stl->g_data->viewvecs[0][2]; + const float clip_end = stl->g_data->viewvecs[1][2]; + volumetrics->integration_start = min_ff(volumetrics->integration_end, clip_start); + volumetrics->integration_end = max_ff(-volumetrics->integration_end, clip_end); + + volumetrics->depth_param[0] = volumetrics->integration_start; + volumetrics->depth_param[1] = volumetrics->integration_end; + volumetrics->depth_param[2] = 1.0f / (volumetrics->integration_end - volumetrics->integration_start); + } + + /* Disable clamp if equal to 0. */ + if (volumetrics->light_clamp == 0.0) { + volumetrics->light_clamp = FLT_MAX; + } + + volumetrics->use_lights = BKE_collection_engine_property_value_get_bool(props, "volumetric_lights"); + } + else { + /* Cleanup */ + DRW_TEXTURE_FREE_SAFE(txl->volume_prop_scattering); + DRW_TEXTURE_FREE_SAFE(txl->volume_prop_extinction); + DRW_TEXTURE_FREE_SAFE(txl->volume_prop_emission); + DRW_TEXTURE_FREE_SAFE(txl->volume_prop_phase); + DRW_TEXTURE_FREE_SAFE(txl->volume_scatter); + DRW_TEXTURE_FREE_SAFE(txl->volume_transmittance); + DRW_TEXTURE_FREE_SAFE(txl->volume_scatter_history); + DRW_TEXTURE_FREE_SAFE(txl->volume_transmittance_history); + DRW_FRAMEBUFFER_FREE_SAFE(fbl->volumetric_fb); + DRW_FRAMEBUFFER_FREE_SAFE(fbl->volumetric_scat_fb); + DRW_FRAMEBUFFER_FREE_SAFE(fbl->volumetric_integ_fb); + } + /* Only allocate if at least one effect is activated */ if (effects->enabled_effects != 0) { /* Ping Pong buffer */ @@ -693,78 +906,6 @@ void EEVEE_effects_init(EEVEE_SceneLayerData *sldata, EEVEE_Data *vedata) stl->g_data->mip_ratio[i][1] = viewport_size[1] / (mip_size[1] * powf(2.0f, floorf(log2f(floorf(viewport_size[1] / mip_size[1]))))); } - if (BKE_collection_engine_property_value_get_bool(props, "volumetric_enable")) { - World *wo = scene->world; - - /* TODO: this will not be the case if we support object volumetrics */ - if ((wo != NULL) && (wo->use_nodes) && (wo->nodetree != NULL)) { - effects->enabled_effects |= EFFECT_VOLUMETRIC; - - if (sldata->volumetrics == NULL) { - sldata->volumetrics = MEM_callocN(sizeof(EEVEE_VolumetricsInfo), "EEVEE_VolumetricsInfo"); - } - - EEVEE_VolumetricsInfo *volumetrics = sldata->volumetrics; - bool last_use_colored_transmit = volumetrics->use_colored_transmit; /* Save to compare */ - - volumetrics->integration_start = BKE_collection_engine_property_value_get_float(props, "volumetric_start"); - volumetrics->integration_end = BKE_collection_engine_property_value_get_float(props, "volumetric_end"); - - if (DRW_viewport_is_persp_get()) { - /* Negate */ - volumetrics->integration_start = -volumetrics->integration_start; - volumetrics->integration_end = -volumetrics->integration_end; - } - else { - const float clip_start = stl->g_data->viewvecs[0][2]; - const float clip_end = stl->g_data->viewvecs[1][2]; - volumetrics->integration_start = min_ff(volumetrics->integration_end, clip_start); - volumetrics->integration_end = max_ff(-volumetrics->integration_end, clip_end); - } - - volumetrics->sample_distribution = BKE_collection_engine_property_value_get_float(props, "volumetric_sample_distribution"); - volumetrics->integration_step_count = (float)BKE_collection_engine_property_value_get_int(props, "volumetric_samples"); - volumetrics->shadow_step_count = (float)BKE_collection_engine_property_value_get_int(props, "volumetric_shadow_samples"); - volumetrics->light_clamp = BKE_collection_engine_property_value_get_float(props, "volumetric_light_clamp"); - - /* Disable clamp if equal to 0. */ - if (volumetrics->light_clamp == 0.0) { - volumetrics->light_clamp = FLT_MAX; - } - - volumetrics->use_lights = BKE_collection_engine_property_value_get_bool(props, "volumetric_lights"); - volumetrics->use_volume_shadows = BKE_collection_engine_property_value_get_bool(props, "volumetric_shadows"); - volumetrics->use_colored_transmit = BKE_collection_engine_property_value_get_bool(props, "volumetric_colored_transmittance"); - - if (last_use_colored_transmit != volumetrics->use_colored_transmit) { - if (fbl->volumetric_fb != NULL) { - DRW_framebuffer_free(fbl->volumetric_fb); - fbl->volumetric_fb = NULL; - } - } - - /* Integration result buffer(s) */ - if (volumetrics->use_colored_transmit == false) { - /* Monocromatic transmittance in alpha */ - 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); - } - else { - /* Transmittance is separated, No need for alpha and DRW_TEX_RGB_11_11_10 gives the same vram usage */ - /* Hint ! Could reuse this for transparency! */ - DRWFboTexture tex_vol[2] = {{&stl->g_data->volumetric, DRW_TEX_RGB_11_11_10, DRW_TEX_MIPMAP | DRW_TEX_FILTER | DRW_TEX_TEMP}, - {&stl->g_data->volumetric_transmit, DRW_TEX_RGB_11_11_10, 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, 2); - } - } - } - /* Compute pixel size, (shared with contact shadows) */ copy_v2_v2(effects->ssr_pixelsize, viewport_size); invert_v2(effects->ssr_pixelsize); @@ -910,57 +1051,67 @@ void EEVEE_effects_cache_init(EEVEE_SceneLayerData *sldata, EEVEE_Data *vedata) Scene *scene = draw_ctx->scene; struct World *wo = scene->world; /* Already checked non NULL */ EEVEE_VolumetricsInfo *volumetrics = sldata->volumetrics; + static int zero = 0; + DRWShadingGroup *grp; + + /* World pass is not additive as it also clear the buffer. */ + psl->volumetric_ps = DRW_pass_create("Volumetric Properties", DRW_STATE_WRITE_COLOR); + + /* World Volumetric */ + struct GPUMaterial *mat = EEVEE_material_world_volume_get(scene, wo); - struct GPUMaterial *mat = EEVEE_material_world_volume_get( - scene, wo, volumetrics->use_lights, volumetrics->use_volume_shadows, - false, volumetrics->use_colored_transmit, sldata->lamps->shadow_method); - - psl->volumetric_integrate_ps = DRW_pass_create("Volumetric Integration", DRW_STATE_WRITE_COLOR); - DRWShadingGroup *grp = DRW_shgroup_material_create(mat, psl->volumetric_integrate_ps); - - if (grp != NULL) { - DRW_shgroup_uniform_buffer(grp, "depthFull", &e_data.depth_src); - DRW_shgroup_uniform_buffer(grp, "shadowTexture", &sldata->shadow_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()); + grp = DRW_shgroup_material_empty_tri_batch_create(mat, psl->volumetric_ps, volumetrics->froxel_tex_size[2]); + + if (grp) { DRW_shgroup_uniform_vec4(grp, "viewvecs[0]", (float *)stl->g_data->viewvecs, 2); - DRW_shgroup_uniform_vec2(grp, "volume_start_end", &sldata->volumetrics->integration_start, 1); - DRW_shgroup_uniform_vec4(grp, "volume_samples_clamp", &sldata->volumetrics->integration_step_count, 1); - DRW_shgroup_call_add(grp, quad, NULL); - - if (volumetrics->use_colored_transmit == false) { /* Monochromatic transmittance */ - 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_vec4(grp, "viewvecs[0]", (float *)stl->g_data->viewvecs, 2); - 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); - } - else { - psl->volumetric_resolve_transmit_ps = DRW_pass_create("Volumetric Transmittance Resolve", DRW_STATE_WRITE_COLOR | DRW_STATE_MULTIPLY); - grp = DRW_shgroup_create(e_data.volumetric_upsample_sh, psl->volumetric_resolve_transmit_ps); - DRW_shgroup_uniform_vec4(grp, "viewvecs[0]", (float *)stl->g_data->viewvecs, 2); - DRW_shgroup_uniform_buffer(grp, "depthFull", &e_data.depth_src); - DRW_shgroup_uniform_buffer(grp, "volumetricBuffer", &stl->g_data->volumetric_transmit); - DRW_shgroup_call_add(grp, quad, NULL); - - psl->volumetric_resolve_ps = DRW_pass_create("Volumetric Resolve", DRW_STATE_WRITE_COLOR | DRW_STATE_ADDITIVE); - grp = DRW_shgroup_create(e_data.volumetric_upsample_sh, psl->volumetric_resolve_ps); - DRW_shgroup_uniform_vec4(grp, "viewvecs[0]", (float *)stl->g_data->viewvecs, 2); - 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); - } - } - else { - /* Compilation failled */ - effects->enabled_effects &= ~EFFECT_VOLUMETRIC; + DRW_shgroup_uniform_ivec3(grp, "volumeTextureSize", (int *)volumetrics->froxel_tex_size, 1); + DRW_shgroup_uniform_vec2(grp, "volume_uv_ratio", (float *)volumetrics->volume_coord_scale, 1); + DRW_shgroup_uniform_vec3(grp, "volume_param", (float *)volumetrics->depth_param, 1); + DRW_shgroup_uniform_vec3(grp, "volume_jitter", (float *)volumetrics->jitter, 1); } + + psl->volumetric_scatter_ps = DRW_pass_create("Volumetric Scattering", DRW_STATE_WRITE_COLOR); + grp = DRW_shgroup_empty_tri_batch_create(e_data.volumetric_scatter_sh, psl->volumetric_scatter_ps, volumetrics->froxel_tex_size[2]); + DRW_shgroup_uniform_vec4(grp, "viewvecs[0]", (float *)stl->g_data->viewvecs, 2); + DRW_shgroup_uniform_vec2(grp, "volume_uv_ratio", (float *)volumetrics->volume_coord_scale, 1); + DRW_shgroup_uniform_vec3(grp, "volume_param", (float *)volumetrics->depth_param, 1); + DRW_shgroup_uniform_vec3(grp, "volume_jitter", (float *)volumetrics->jitter, 1); + DRW_shgroup_uniform_mat4(grp, "PastViewProjectionMatrix", (float *)stl->g_data->prev_persmat); + DRW_shgroup_uniform_block(grp, "light_block", sldata->light_ubo); + DRW_shgroup_uniform_block(grp, "shadow_block", sldata->shadow_ubo); + DRW_shgroup_uniform_block(grp, "grid_block", sldata->grid_ubo); + DRW_shgroup_uniform_int(grp, "light_count", (volumetrics->use_lights) ? &sldata->lamps->num_light : &zero, 1); + DRW_shgroup_uniform_int(grp, "grid_count", &sldata->probes->num_render_grid, 1); + DRW_shgroup_uniform_buffer(grp, "irradianceGrid", &sldata->irradiance_pool); + DRW_shgroup_uniform_buffer(grp, "shadowTexture", &sldata->shadow_pool); + DRW_shgroup_uniform_float(grp, "volume_light_clamp", &volumetrics->light_clamp, 1); + DRW_shgroup_uniform_float(grp, "volume_shadows_steps", &volumetrics->shadow_step_count, 1); + DRW_shgroup_uniform_float(grp, "volume_history_alpha", &volumetrics->history_alpha, 1); + DRW_shgroup_uniform_buffer(grp, "volumeScattering", &txl->volume_prop_scattering); + DRW_shgroup_uniform_buffer(grp, "volumeExtinction", &txl->volume_prop_extinction); + DRW_shgroup_uniform_buffer(grp, "volumeEmission", &txl->volume_prop_emission); + DRW_shgroup_uniform_buffer(grp, "volumePhase", &txl->volume_prop_phase); + DRW_shgroup_uniform_buffer(grp, "historyScattering", &txl->volume_scatter_history); + DRW_shgroup_uniform_buffer(grp, "historyTransmittance", &txl->volume_transmittance_history); + + psl->volumetric_integration_ps = DRW_pass_create("Volumetric Integration", DRW_STATE_WRITE_COLOR); + grp = DRW_shgroup_empty_tri_batch_create(e_data.volumetric_integration_sh, psl->volumetric_integration_ps, volumetrics->froxel_tex_size[2]); + DRW_shgroup_uniform_vec4(grp, "viewvecs[0]", (float *)stl->g_data->viewvecs, 2); + DRW_shgroup_uniform_vec2(grp, "volume_uv_ratio", (float *)volumetrics->volume_coord_scale, 1); + DRW_shgroup_uniform_vec3(grp, "volume_param", (float *)volumetrics->depth_param, 1); + DRW_shgroup_uniform_buffer(grp, "volumeScattering", &txl->volume_scatter); + DRW_shgroup_uniform_buffer(grp, "volumeExtinction", &txl->volume_transmittance); + + psl->volumetric_resolve_ps = DRW_pass_create("Volumetric Resolve", DRW_STATE_WRITE_COLOR); + grp = DRW_shgroup_create(e_data.volumetric_resolve_sh, psl->volumetric_resolve_ps); + DRW_shgroup_uniform_vec4(grp, "viewvecs[0]", (float *)stl->g_data->viewvecs, 2); + DRW_shgroup_uniform_vec2(grp, "volume_uv_ratio", (float *)volumetrics->volume_coord_scale, 1); + DRW_shgroup_uniform_vec3(grp, "volume_param", (float *)volumetrics->depth_param, 1); + DRW_shgroup_uniform_buffer(grp, "inScattering", &txl->volume_scatter_history); + DRW_shgroup_uniform_buffer(grp, "inTransmittance", &txl->volume_transmittance_history); + DRW_shgroup_uniform_buffer(grp, "inSceneColor", &e_data.color_src); + DRW_shgroup_uniform_buffer(grp, "inSceneDepth", &e_data.depth_src); + DRW_shgroup_call_add(grp, quad, NULL); } if ((effects->enabled_effects & EFFECT_SSR) != 0) { @@ -1320,9 +1471,10 @@ void EEVEE_downsample_cube_buffer(EEVEE_Data *vedata, struct GPUFrameBuffer *fb_ DRW_stats_group_end(); } -void EEVEE_effects_do_volumetrics(EEVEE_SceneLayerData *sldata, EEVEE_Data *vedata) +void EEVEE_effects_do_volumetrics(EEVEE_SceneLayerData *UNUSED(sldata), EEVEE_Data *vedata) { EEVEE_PassList *psl = vedata->psl; + EEVEE_TextureList *txl = vedata->txl; EEVEE_FramebufferList *fbl = vedata->fbl; EEVEE_StorageList *stl = vedata->stl; EEVEE_EffectsInfo *effects = stl->effects; @@ -1330,33 +1482,35 @@ void EEVEE_effects_do_volumetrics(EEVEE_SceneLayerData *sldata, EEVEE_Data *veda if ((effects->enabled_effects & EFFECT_VOLUMETRIC) != 0) { DefaultTextureList *dtxl = DRW_viewport_texture_list_get(); + e_data.color_src = txl->color; e_data.depth_src = dtxl->depth; - /* Compute volumetric integration at halfres. */ - DRW_framebuffer_texture_attach(fbl->volumetric_fb, stl->g_data->volumetric, 0, 0); - if (sldata->volumetrics->use_colored_transmit) { - DRW_framebuffer_texture_attach(fbl->volumetric_fb, stl->g_data->volumetric_transmit, 1, 0); - } + /* Step 1: Participating Media Properties */ DRW_framebuffer_bind(fbl->volumetric_fb); - DRW_draw_pass(psl->volumetric_integrate_ps); + DRW_draw_pass(psl->volumetric_ps); - /* Resolve at fullres */ - DRW_framebuffer_texture_detach(dtxl->depth); - DRW_framebuffer_bind(fbl->main); - if (sldata->volumetrics->use_colored_transmit) { - DRW_draw_pass(psl->volumetric_resolve_transmit_ps); - } + /* Step 2: Scatter Light */ + DRW_framebuffer_bind(fbl->volumetric_scat_fb); + DRW_draw_pass(psl->volumetric_scatter_ps); + + /* Step 3: Integration */ + DRW_framebuffer_bind(fbl->volumetric_integ_fb); + DRW_draw_pass(psl->volumetric_integration_ps); + + /* Step 4: Apply for opaque */ + DRW_framebuffer_bind(fbl->effect_fb); DRW_draw_pass(psl->volumetric_resolve_ps); - /* Restore */ - DRW_framebuffer_texture_attach(fbl->main, dtxl->depth, 0, 0); - DRW_framebuffer_texture_detach(stl->g_data->volumetric); - if (sldata->volumetrics->use_colored_transmit) { - DRW_framebuffer_texture_detach(stl->g_data->volumetric_transmit); - } + /* Swap volume history buffers */ + SWAP(struct GPUFrameBuffer *, fbl->volumetric_scat_fb, fbl->volumetric_integ_fb); + SWAP(GPUTexture *, txl->volume_scatter, txl->volume_scatter_history); + SWAP(GPUTexture *, txl->volume_transmittance, txl->volume_transmittance_history); - /* Rebind main buffer after attach/detach operations */ - DRW_framebuffer_bind(fbl->main); + /* Swap the buffers and rebind depth to the current buffer */ + DRW_framebuffer_texture_detach(dtxl->depth); + SWAP(struct GPUFrameBuffer *, fbl->main, fbl->effect_fb); + SWAP(GPUTexture *, txl->color, txl->color_post); + DRW_framebuffer_texture_attach(fbl->main, dtxl->depth, 0, 0); } } @@ -1681,6 +1835,9 @@ void EEVEE_draw_effects(EEVEE_Data *vedata) void EEVEE_effects_free(void) { + MEM_SAFE_FREE(e_data.volumetric_common_lib); + MEM_SAFE_FREE(e_data.volumetric_common_lamps_lib); + for (int i = 0; i < SSR_MAX_SHADER; ++i) { DRW_SHADER_FREE_SAFE(e_data.ssr_sh[i]); } @@ -1692,7 +1849,9 @@ void EEVEE_effects_free(void) DRW_SHADER_FREE_SAFE(e_data.gtao_sh); DRW_SHADER_FREE_SAFE(e_data.gtao_debug_sh); - DRW_SHADER_FREE_SAFE(e_data.volumetric_upsample_sh); + DRW_SHADER_FREE_SAFE(e_data.volumetric_scatter_sh); + DRW_SHADER_FREE_SAFE(e_data.volumetric_integration_sh); + DRW_SHADER_FREE_SAFE(e_data.volumetric_resolve_sh); DRW_SHADER_FREE_SAFE(e_data.minz_downlevel_sh); DRW_SHADER_FREE_SAFE(e_data.maxz_downlevel_sh); diff --git a/source/blender/draw/engines/eevee/eevee_engine.c b/source/blender/draw/engines/eevee/eevee_engine.c index 4408e93afd4..4617d44ef45 100644 --- a/source/blender/draw/engines/eevee/eevee_engine.c +++ b/source/blender/draw/engines/eevee/eevee_engine.c @@ -253,17 +253,17 @@ static void EEVEE_draw_scene(void *vedata) DRW_draw_pass(psl->refract_pass); DRW_stats_group_end(); + /* Volumetrics */ + DRW_stats_group_start("Volumetrics"); + EEVEE_effects_do_volumetrics(sldata, vedata); + DRW_stats_group_end(); + /* Transparent */ DRW_pass_sort_shgroup_z(psl->transparent_pass); DRW_stats_group_start("Transparent"); DRW_draw_pass(psl->transparent_pass); DRW_stats_group_end(); - /* Volumetrics */ - DRW_stats_group_start("Volumetrics"); - EEVEE_effects_do_volumetrics(sldata, vedata); - DRW_stats_group_end(); - /* Post Process */ DRW_stats_group_start("Post FX"); EEVEE_draw_effects(vedata); @@ -329,6 +329,7 @@ static void EEVEE_scene_layer_settings_create(RenderEngine *UNUSED(engine), IDPr BKE_collection_engine_property_add_bool(props, "volumetric_enable", false); BKE_collection_engine_property_add_float(props, "volumetric_start", 0.1f); BKE_collection_engine_property_add_float(props, "volumetric_end", 100.0f); + BKE_collection_engine_property_add_int(props, "volumetric_tile_size", 8); BKE_collection_engine_property_add_int(props, "volumetric_samples", 64); BKE_collection_engine_property_add_float(props, "volumetric_sample_distribution", 0.8f); BKE_collection_engine_property_add_bool(props, "volumetric_lights", true); diff --git a/source/blender/draw/engines/eevee/eevee_materials.c b/source/blender/draw/engines/eevee/eevee_materials.c index 98dbef8e445..b555761d5df 100644 --- a/source/blender/draw/engines/eevee/eevee_materials.c +++ b/source/blender/draw/engines/eevee/eevee_materials.c @@ -43,26 +43,6 @@ #include "eevee_lut.h" #include "eevee_private.h" -#if defined(IRRADIANCE_SH_L2) -#define SHADER_IRRADIANCE "#define IRRADIANCE_SH_L2\n" -#elif defined(IRRADIANCE_CUBEMAP) -#define SHADER_IRRADIANCE "#define IRRADIANCE_CUBEMAP\n" -#elif defined(IRRADIANCE_HL2) -#define SHADER_IRRADIANCE "#define IRRADIANCE_HL2\n" -#endif - -#define SHADER_DEFINES \ - "#define EEVEE_ENGINE\n" \ - "#define MAX_PROBE " STRINGIFY(MAX_PROBE) "\n" \ - "#define MAX_GRID " STRINGIFY(MAX_GRID) "\n" \ - "#define MAX_PLANAR " STRINGIFY(MAX_PLANAR) "\n" \ - "#define MAX_LIGHT " STRINGIFY(MAX_LIGHT) "\n" \ - "#define MAX_SHADOW " STRINGIFY(MAX_SHADOW) "\n" \ - "#define MAX_SHADOW_CUBE " STRINGIFY(MAX_SHADOW_CUBE) "\n" \ - "#define MAX_SHADOW_CASCADE " STRINGIFY(MAX_SHADOW_CASCADE) "\n" \ - "#define MAX_CASCADE_NUM " STRINGIFY(MAX_CASCADE_NUM) "\n" \ - SHADER_IRRADIANCE - /* *********** STATIC *********** */ static struct { char *frag_shader_lib; @@ -105,7 +85,10 @@ extern char datatoc_shadow_geom_glsl[]; extern char datatoc_lightprobe_geom_glsl[]; extern char datatoc_lightprobe_vert_glsl[]; extern char datatoc_background_vert_glsl[]; +extern char datatoc_volumetric_vert_glsl[]; +extern char datatoc_volumetric_geom_glsl[]; extern char datatoc_volumetric_frag_glsl[]; +extern char datatoc_volumetric_lib_glsl[]; extern Material defmaterial; extern GlobalsUboStorage ts; @@ -332,7 +315,7 @@ static char *eevee_get_defines(int options) return str; } -static char *eevee_get_volume_defines(int options) +static char *eevee_get_volume_defines(int UNUSED(options)) { char *str = NULL; @@ -340,19 +323,6 @@ static char *eevee_get_volume_defines(int options) BLI_dynstr_appendf(ds, SHADER_DEFINES); BLI_dynstr_appendf(ds, "#define VOLUMETRICS\n"); - if ((options & VAR_VOLUME_SHADOW) != 0) { - BLI_dynstr_appendf(ds, "#define VOLUME_SHADOW\n"); - } - if ((options & VAR_VOLUME_HOMO) != 0) { - BLI_dynstr_appendf(ds, "#define VOLUME_HOMOGENEOUS\n"); - } - if ((options & VAR_VOLUME_LIGHT) != 0) { - BLI_dynstr_appendf(ds, "#define VOLUME_LIGHTING\n"); - } - if ((options & VAR_VOLUME_COLOR) != 0) { - BLI_dynstr_appendf(ds, "#define COLOR_TRANSMITTANCE\n"); - } - str = BLI_dynstr_get_cstring(ds); BLI_dynstr_free(ds); @@ -516,6 +486,7 @@ void EEVEE_materials_init(EEVEE_StorageList *stl) BLI_dynstr_append(ds_frag, datatoc_ltc_lib_glsl); BLI_dynstr_append(ds_frag, datatoc_bsdf_direct_lib_glsl); BLI_dynstr_append(ds_frag, datatoc_lamps_lib_glsl); + BLI_dynstr_append(ds_frag, datatoc_volumetric_lib_glsl); BLI_dynstr_append(ds_frag, datatoc_volumetric_frag_glsl); e_data.volume_shader_lib = BLI_dynstr_get_cstring(ds_frag); BLI_dynstr_free(ds_frag); @@ -617,20 +588,11 @@ struct GPUMaterial *EEVEE_material_world_background_get(struct Scene *scene, Wor SHADER_DEFINES "#define WORLD_BACKGROUND\n"); } -struct GPUMaterial *EEVEE_material_world_volume_get( - struct Scene *scene, World *wo, - bool use_lights, bool use_volume_shadows, bool is_homogeneous, bool use_color_transmit, int shadow_method) +struct GPUMaterial *EEVEE_material_world_volume_get(struct Scene *scene, World *wo) { const void *engine = &DRW_engine_viewport_eevee_type; int options = VAR_WORLD_VOLUME; - if (use_lights) options |= VAR_VOLUME_LIGHT; - if (is_homogeneous) options |= VAR_VOLUME_HOMO; - if (use_volume_shadows) options |= VAR_VOLUME_SHADOW; - if (use_color_transmit) options |= VAR_VOLUME_COLOR; - - options |= eevee_material_shadow_option(shadow_method); - GPUMaterial *mat = GPU_material_from_nodetree_find(&wo->gpumaterial, engine, options); if (mat != NULL) { return mat; @@ -640,7 +602,7 @@ struct GPUMaterial *EEVEE_material_world_volume_get( mat = GPU_material_from_nodetree( scene, wo->nodetree, &wo->gpumaterial, engine, options, - datatoc_background_vert_glsl, NULL, e_data.volume_shader_lib, + datatoc_volumetric_vert_glsl, datatoc_volumetric_geom_glsl, e_data.volume_shader_lib, defines); MEM_freeN(defines); diff --git a/source/blender/draw/engines/eevee/eevee_private.h b/source/blender/draw/engines/eevee/eevee_private.h index 8bae904b0e1..4919189ff91 100644 --- a/source/blender/draw/engines/eevee/eevee_private.h +++ b/source/blender/draw/engines/eevee/eevee_private.h @@ -46,16 +46,31 @@ extern struct DrawEngineType draw_engine_eevee_type; // #define IRRADIANCE_CUBEMAP #define IRRADIANCE_HL2 +#if defined(IRRADIANCE_SH_L2) +#define SHADER_IRRADIANCE "#define IRRADIANCE_SH_L2\n" +#elif defined(IRRADIANCE_CUBEMAP) +#define SHADER_IRRADIANCE "#define IRRADIANCE_CUBEMAP\n" +#elif defined(IRRADIANCE_HL2) +#define SHADER_IRRADIANCE "#define IRRADIANCE_HL2\n" +#endif + +#define SHADER_DEFINES \ + "#define EEVEE_ENGINE\n" \ + "#define MAX_PROBE " STRINGIFY(MAX_PROBE) "\n" \ + "#define MAX_GRID " STRINGIFY(MAX_GRID) "\n" \ + "#define MAX_PLANAR " STRINGIFY(MAX_PLANAR) "\n" \ + "#define MAX_LIGHT " STRINGIFY(MAX_LIGHT) "\n" \ + "#define MAX_SHADOW " STRINGIFY(MAX_SHADOW) "\n" \ + "#define MAX_SHADOW_CUBE " STRINGIFY(MAX_SHADOW_CUBE) "\n" \ + "#define MAX_SHADOW_CASCADE " STRINGIFY(MAX_SHADOW_CASCADE) "\n" \ + "#define MAX_CASCADE_NUM " STRINGIFY(MAX_CASCADE_NUM) "\n" \ + SHADER_IRRADIANCE + /* World shader variations */ enum { VAR_WORLD_BACKGROUND = 0, VAR_WORLD_PROBE = 1, VAR_WORLD_VOLUME = 2, - - VAR_VOLUME_SHADOW = (1 << 2), - VAR_VOLUME_HOMO = (1 << 3), - VAR_VOLUME_LIGHT = (1 << 4), - VAR_VOLUME_COLOR = (1 << 5), }; /* Material shader variations */ @@ -117,9 +132,10 @@ typedef struct EEVEE_PassList { struct DRWPass *dof_down; struct DRWPass *dof_scatter; struct DRWPass *dof_resolve; - struct DRWPass *volumetric_integrate_ps; + struct DRWPass *volumetric_ps; + struct DRWPass *volumetric_scatter_ps; + struct DRWPass *volumetric_integration_ps; struct DRWPass *volumetric_resolve_ps; - struct DRWPass *volumetric_resolve_transmit_ps; struct DRWPass *ssr_raytrace; struct DRWPass *ssr_resolve; struct DRWPass *color_downsample_ps; @@ -164,6 +180,8 @@ typedef struct EEVEE_FramebufferList { struct GPUFrameBuffer *dof_scatter_far_fb; struct GPUFrameBuffer *dof_scatter_near_fb; struct GPUFrameBuffer *volumetric_fb; + struct GPUFrameBuffer *volumetric_scat_fb; + struct GPUFrameBuffer *volumetric_integ_fb; struct GPUFrameBuffer *screen_tracing_fb; struct GPUFrameBuffer *refract_fb; @@ -189,6 +207,15 @@ typedef struct EEVEE_TextureList { struct GPUTexture *ssr_specrough_input; struct GPUTexture *refract_color; + struct GPUTexture *volume_prop_scattering; + struct GPUTexture *volume_prop_extinction; + struct GPUTexture *volume_prop_emission; + struct GPUTexture *volume_prop_phase; + struct GPUTexture *volume_scatter; + struct GPUTexture *volume_transmittance; + struct GPUTexture *volume_scatter_history; + struct GPUTexture *volume_transmittance_history; + struct GPUTexture *planar_pool; struct GPUTexture *planar_depth; @@ -250,7 +277,12 @@ typedef struct EEVEE_ShadowRender { typedef struct EEVEE_VolumetricsInfo { float integration_step_count, shadow_step_count, sample_distribution, light_clamp; float integration_start, integration_end; - bool use_lights, use_volume_shadows, use_colored_transmit; + float depth_param[3], history_alpha; + bool use_lights, use_volume_shadows; + int froxel_tex_size[3]; + float inv_tex_size[3]; + float volume_coord_scale[2]; + float jitter[3]; } EEVEE_VolumetricsInfo; /* ************ LIGHT DATA ************* */ @@ -532,8 +564,6 @@ typedef struct EEVEE_PrivateData { struct GHash *hair_material_hash; struct GPUTexture *minzbuffer; struct GPUTexture *ssr_hit_output[4]; - struct GPUTexture *volumetric; - struct GPUTexture *volumetric_transmit; struct GPUTexture *gtao_horizons_debug; float background_alpha; /* TODO find a better place for this. */ float viewvecs[2][4]; @@ -561,9 +591,7 @@ void EEVEE_materials_cache_populate(EEVEE_Data *vedata, EEVEE_SceneLayerData *sl 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 GPUMaterial *EEVEE_material_world_volume_get( - struct Scene *scene, struct World *wo, bool use_lights, bool use_volume_shadows, bool is_homogeneous, bool use_color_transmit, - int shadow_method); +struct GPUMaterial *EEVEE_material_world_volume_get(struct Scene *scene, struct World *wo); struct GPUMaterial *EEVEE_material_mesh_get( struct Scene *scene, Material *ma, bool use_blend, bool use_multiply, bool use_refract, int shadow_method); struct GPUMaterial *EEVEE_material_mesh_depth_get(struct Scene *scene, Material *ma, bool use_hashed_alpha, bool is_shadow); 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 3e0e36cad24..c9aa6705b67 100644 --- a/source/blender/draw/engines/eevee/shaders/bsdf_common_lib.glsl +++ b/source/blender/draw/engines/eevee/shaders/bsdf_common_lib.glsl @@ -303,6 +303,17 @@ float get_view_z_from_depth(float depth) } } +float get_depth_from_view_z(float z) +{ + if (ProjectionMatrix[3][3] == 0.0) { + float d = (-ProjectionMatrix[3][2] / z) - ProjectionMatrix[2][2]; + return d * 0.5 + 0.5; + } + else { + return (z - viewvecs[0].z) / viewvecs[1].z; + } +} + vec2 get_uvs_from_view(vec3 view) { vec3 ndc = project_point(ProjectionMatrix, view); diff --git a/source/blender/draw/engines/eevee/shaders/bsdf_direct_lib.glsl b/source/blender/draw/engines/eevee/shaders/bsdf_direct_lib.glsl index d0a365f5a3e..ae03f22ac14 100644 --- a/source/blender/draw/engines/eevee/shaders/bsdf_direct_lib.glsl +++ b/source/blender/draw/engines/eevee/shaders/bsdf_direct_lib.glsl @@ -59,8 +59,7 @@ float direct_diffuse_sphere(LightData ld, vec3 N, vec4 l_vector) return bsdf; } -/* From Frostbite PBR Course - * http://www.frostbite.com/wp-content/uploads/2014/11/course_notes_moving_frostbite_to_pbr.pdf */ +#ifdef USE_LTC float direct_diffuse_rectangle(LightData ld, vec3 N, vec3 V, vec4 l_vector) { vec3 corners[4]; @@ -73,7 +72,7 @@ float direct_diffuse_rectangle(LightData ld, vec3 N, vec3 V, vec4 l_vector) bsdf *= M_1_2PI; return bsdf; } - +#endif #if 0 float direct_diffuse_unit_disc(vec3 N, vec3 L) @@ -104,6 +103,7 @@ vec3 direct_ggx_sun(LightData ld, vec3 N, vec3 V, float roughness, vec3 f0) return F_schlick(f0, VH) * bsdf; } +#ifdef USE_LTC vec3 direct_ggx_sphere(LightData ld, vec3 N, vec3 V, vec4 l_vector, float roughness, vec3 f0) { vec3 L = l_vector.xyz / l_vector.w; @@ -173,6 +173,7 @@ vec3 direct_ggx_rectangle(LightData ld, vec3 N, vec3 V, vec4 l_vector, float rou return spec; } +#endif #if 0 float direct_ggx_disc(vec3 N, vec3 L) diff --git a/source/blender/draw/engines/eevee/shaders/lamps_lib.glsl b/source/blender/draw/engines/eevee/shaders/lamps_lib.glsl index 553416fa3a5..5ca40cd06a2 100644 --- a/source/blender/draw/engines/eevee/shaders/lamps_lib.glsl +++ b/source/blender/draw/engines/eevee/shaders/lamps_lib.glsl @@ -1,6 +1,8 @@ uniform sampler2DArray shadowTexture; +#define LAMPS_LIB + layout(std140) uniform shadow_block { ShadowData shadows_data[MAX_SHADOW]; ShadowCubeData shadows_cube_data[MAX_SHADOW_CUBE]; @@ -244,7 +246,7 @@ float light_diffuse(LightData ld, vec3 N, vec3 V, vec4 l_vector) } #else if (ld.l_type == SUN) { - return direct_diffuse_sun(ld, N, V); + return direct_diffuse_sun(ld, N); } else { return direct_diffuse_point(N, l_vector); diff --git a/source/blender/draw/engines/eevee/shaders/volumetric_frag.glsl b/source/blender/draw/engines/eevee/shaders/volumetric_frag.glsl index 7f44dd53163..a3e2979a9b4 100644 --- a/source/blender/draw/engines/eevee/shaders/volumetric_frag.glsl +++ b/source/blender/draw/engines/eevee/shaders/volumetric_frag.glsl @@ -1,396 +1,38 @@ -#ifdef VOLUMETRICS +/* Based on Frosbite Unified Volumetric. + * https://www.ea.com/frostbite/news/physically-based-unified-volumetric-rendering-in-frostbite */ #define NODETREE_EXEC -#define VOLUMETRIC_INTEGRATION_MAX_STEP 256 -#define VOLUMETRIC_SHADOW_MAX_STEP 128 - -uniform int light_count; -uniform vec2 volume_start_end; -uniform vec4 volume_samples_clamp; - -#define volume_start volume_start_end.x -#define volume_end volume_start_end.y +uniform ivec3 volumeTextureSize; +uniform vec3 volume_jitter; -#define volume_integration_steps volume_samples_clamp.x -#define volume_shadows_steps volume_samples_clamp.y -#define volume_sample_distribution volume_samples_clamp.z -#define volume_light_clamp volume_samples_clamp.w - -#ifdef COLOR_TRANSMITTANCE -layout(location = 0) out vec4 outScattering; -layout(location = 1) out vec4 outTransmittance; -#else -out vec4 outScatteringTransmittance; -#endif +flat in int slice; /* Warning: theses are not attributes, theses are global vars. */ vec3 worldPosition = vec3(0.0); vec3 viewPosition = vec3(0.0); vec3 viewNormal = vec3(0.0); -uniform sampler2D depthFull; - -void participating_media_properties(vec3 wpos, out vec3 extinction, out vec3 scattering, out vec3 emission, out float anisotropy) -{ -#ifndef VOLUME_HOMOGENEOUS - worldPosition = wpos; - viewPosition = (ViewMatrix * vec4(wpos, 1.0)).xyz; /* warning, Perf. */ -#endif - - Closure cl = nodetree_exec(); - - scattering = cl.scatter; - emission = cl.emission; - anisotropy = cl.anisotropy; - extinction = max(vec3(1e-4), cl.absorption + cl.scatter); -} - -vec3 participating_media_extinction(vec3 wpos) -{ -#ifndef VOLUME_HOMOGENEOUS - worldPosition = wpos; - viewPosition = (ViewMatrix * vec4(wpos, 1.0)).xyz; /* warning, Perf. */ -#endif - - Closure cl = nodetree_exec(); +layout(location = 0) out vec4 volumeScattering; +layout(location = 1) out vec4 volumeExtinction; +layout(location = 2) out vec4 volumeEmissive; +layout(location = 3) out vec4 volumePhase; - return max(vec3(1e-4), cl.absorption + cl.scatter); -} +/* Store volumetric properties into the froxel textures. */ -float phase_function_isotropic() -{ - return 1.0 / (4.0 * M_PI); -} - -float phase_function(vec3 v, vec3 l, float g) -{ -#ifndef VOLUME_ISOTROPIC /* TODO Use this flag when only isotropic closures are used */ - /* Henyey-Greenstein */ - float cos_theta = dot(v, l); - g = clamp(g, -1.0 + 1e-3, 1.0 - 1e-3); - 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 -} - -float light_volume(LightData ld, vec4 l_vector) -{ - float power; - float dist = max(1e-4, abs(l_vector.w - ld.l_radius)); - /* TODO : Area lighting ? */ - /* Removing Area Power. */ - /* TODO : put this out of the shader. */ - if (ld.l_type == AREA) { - power = 0.0962 * (ld.l_sizex * ld.l_sizey * 4.0f * M_PI); - } - else { - power = 0.0248 * (4.0 * ld.l_radius * ld.l_radius * M_PI * M_PI); - } - return min(power / (l_vector.w * l_vector.w), volume_light_clamp); -} - -vec3 irradiance_volumetric(vec3 wpos) -{ - IrradianceData ir_data = load_irradiance_cell(0, vec3(1.0)); - vec3 irradiance = ir_data.cubesides[0] + ir_data.cubesides[1] + ir_data.cubesides[2]; - ir_data = load_irradiance_cell(0, vec3(-1.0)); - irradiance += ir_data.cubesides[0] + ir_data.cubesides[1] + ir_data.cubesides[2]; - irradiance *= 0.16666666; /* 1/6 */ - return irradiance; -} - -vec3 light_volume_shadow(LightData ld, vec3 ray_wpos, vec4 l_vector, vec3 s_extinction) -{ -#ifdef VOLUME_SHADOW - -#ifdef VOLUME_HOMOGENEOUS - /* Simple extinction */ - return exp(-s_extinction * l_vector.w); -#else - /* Heterogeneous volume shadows */ - float dd = l_vector.w / volume_shadows_steps; - vec3 L = l_vector.xyz * l_vector.w; - vec3 shadow = vec3(1.0); - for (float s = 0.5; s < VOLUMETRIC_SHADOW_MAX_STEP && s < (volume_shadows_steps - 0.1); s += 1.0) { - vec3 pos = ray_wpos + L * (s / volume_shadows_steps); - vec3 s_extinction = participating_media_extinction(pos); - shadow *= exp(-s_extinction * dd); - } - return shadow; -#endif /* VOLUME_HOMOGENEOUS */ - -#else - return vec3(1.0); -#endif /* VOLUME_SHADOW */ -} - -float find_next_step(float iter, float noise) -{ - float progress = (iter + noise) / volume_integration_steps; - - float linear_split = mix(volume_start, volume_end, progress); - - if (ProjectionMatrix[3][3] == 0.0) { - float exp_split = volume_start * pow(volume_end / volume_start, progress); - return mix(linear_split, exp_split, volume_sample_distribution); - } - else { - return linear_split; - } -} - -/* 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; - -#ifdef VOLUME_HOMOGENEOUS - /* Put it out of the loop for homogeneous media. */ - vec3 s_extinction, s_scattering, s_emission; - float s_anisotropy; - participating_media_properties(vec3(0.0), s_extinction, s_scattering, s_emission, s_anisotropy); -#endif - - /* 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 dist = volume_start; - for (float i = 0.5; i < VOLUMETRIC_INTEGRATION_MAX_STEP && i < (volume_integration_steps - 0.1); ++i) { - float new_dist = max(max_z, find_next_step(rand, i)); - float step = dist - new_dist; /* Marching step */ - dist = new_dist; - - vec3 ray_wpos = ray_origin + wdir_proj * dist; - -#ifndef VOLUME_HOMOGENEOUS - vec3 s_extinction, s_scattering, s_emission; - float s_anisotropy; - participating_media_properties(ray_wpos, s_extinction, s_scattering, s_emission, s_anisotropy); -#endif - - /* Evaluate each light */ - vec3 Lscat = s_emission; - -#ifdef VOLUME_LIGHTING /* Lights */ - for (int i = 0; i < MAX_LIGHT && i < light_count; ++i) { - LightData ld = lights_data[i]; + ivec3 volume_cell = ivec3(gl_FragCoord.xy, slice); + vec3 ndc_cell = volume_to_ndc((vec3(volume_cell) + volume_jitter) / volumeTextureSize); - vec4 l_vector; - l_vector.xyz = ld.l_position - ray_wpos; - l_vector.w = length(l_vector.xyz); + viewPosition = get_view_space_from_depth(ndc_cell.xy, ndc_cell.z); + worldPosition = transform_point(ViewMatrixInverse, viewPosition); - float Vis = light_visibility(ld, ray_wpos, l_vector); - - vec3 Li = ld.l_color * light_volume(ld, l_vector) * light_volume_shadow(ld, ray_wpos, l_vector, s_extinction); - - Lscat += Li * Vis * s_scattering * phase_function(-wdir, l_vector.xyz / l_vector.w, s_anisotropy); - } -#endif - - /* Environment : Average color. */ - Lscat += irradiance_volumetric(wpos) * 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; - } - -#ifdef COLOR_TRANSMITTANCE - outScattering = vec4(scattering, 1.0); - outTransmittance = vec4(transmittance, 1.0); -#else - float mono_transmittance = dot(transmittance, vec3(1.0)) / 3.0; - - outScatteringTransmittance = vec4(scattering, mono_transmittance); -#endif -} - -#else /* STEP_UPSAMPLE */ - -out vec4 FragColor; - -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 + Closure cl = nodetree_exec(); - FragColor = integration_result / weight_sum; + volumeScattering = vec4(cl.scatter, 1.0); + volumeExtinction = vec4(max(vec3(1e-4), cl.absorption + cl.scatter), 1.0); + volumeEmissive = vec4(cl.emission, 1.0); + volumePhase = vec4(cl.anisotropy, vec3(1.0)); } -#endif diff --git a/source/blender/draw/engines/eevee/shaders/volumetric_geom.glsl b/source/blender/draw/engines/eevee/shaders/volumetric_geom.glsl new file mode 100644 index 00000000000..f944184058d --- /dev/null +++ b/source/blender/draw/engines/eevee/shaders/volumetric_geom.glsl @@ -0,0 +1,25 @@ + +layout(triangles) in; +layout(triangle_strip, max_vertices=3) out; + +in vec4 vPos[]; + +flat out int slice; + +/* This is just a pass-through geometry shader that send the geometry + * to the layer corresponding to it's depth. */ + +void main() { + gl_Layer = slice = int(vPos[0].z); + + gl_Position = vPos[0].xyww; + EmitVertex(); + + gl_Position = vPos[1].xyww; + EmitVertex(); + + gl_Position = vPos[2].xyww; + EmitVertex(); + + EndPrimitive(); +}
\ No newline at end of file diff --git a/source/blender/draw/engines/eevee/shaders/volumetric_integration_frag.glsl b/source/blender/draw/engines/eevee/shaders/volumetric_integration_frag.glsl new file mode 100644 index 00000000000..15e9696a4c5 --- /dev/null +++ b/source/blender/draw/engines/eevee/shaders/volumetric_integration_frag.glsl @@ -0,0 +1,63 @@ + +/* Based on Frosbite Unified Volumetric. + * https://www.ea.com/frostbite/news/physically-based-unified-volumetric-rendering-in-frostbite */ + +/* Step 3 : Integrate for each froxel the final amount of light + * scattered back to the viewer and the amout of transmittance. */ + +uniform sampler3D volumeScattering; /* Result of the scatter step */ +uniform sampler3D volumeExtinction; + +flat in int slice; + +layout(location = 0) out vec4 finalScattering; +layout(location = 1) out vec4 finalTransmittance; + +void main() +{ + /* Start with full transmittance and no scattered light. */ + finalScattering = vec4(0.0); + finalTransmittance = vec4(1.0); + + vec3 tex_size = vec3(textureSize(volumeScattering, 0).xyz); + + /* Compute view ray. */ + vec2 uvs = gl_FragCoord.xy / tex_size.xy; + vec3 ndc_cell = volume_to_ndc(vec3(uvs, 1e-5)); + vec3 view_cell = get_view_space_from_depth(ndc_cell.xy, ndc_cell.z); + + /* Ortho */ + float prev_ray_len = view_cell.z; + float orig_ray_len = 1.0; + + /* Persp */ + if (ProjectionMatrix[3][3] == 0.0) { + prev_ray_len = length(view_cell); + orig_ray_len = prev_ray_len / view_cell.z; + } + + /* Without compute shader and arbitrary write we need to + * accumulate from the beginning of the ray for each cell. */ + float integration_end = float(slice); + for (int i = 0; i < slice; ++i) { + ivec3 volume_cell = ivec3(gl_FragCoord.xy, i); + + vec4 Lscat = texelFetch(volumeScattering, volume_cell, 0); + vec4 s_extinction = texelFetch(volumeExtinction, volume_cell, 0); + + float cell_depth = volume_z_to_view_z((float(i) + 1.0) / tex_size.z); + float ray_len = orig_ray_len * cell_depth; + + /* Evaluate Scattering */ + float s_len = abs(ray_len - prev_ray_len); + prev_ray_len = ray_len; + vec4 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 */ + finalScattering += finalTransmittance * Lscat; + + finalTransmittance *= Tr; + } +}
\ No newline at end of file diff --git a/source/blender/draw/engines/eevee/shaders/volumetric_lib.glsl b/source/blender/draw/engines/eevee/shaders/volumetric_lib.glsl new file mode 100644 index 00000000000..fd2630f54f9 --- /dev/null +++ b/source/blender/draw/engines/eevee/shaders/volumetric_lib.glsl @@ -0,0 +1,134 @@ + +/* Based on Frosbite Unified Volumetric. + * https://www.ea.com/frostbite/news/physically-based-unified-volumetric-rendering-in-frostbite */ + +uniform float volume_light_clamp; + +uniform vec3 volume_param; /* Parameters to the volume Z equation */ + +uniform vec2 volume_uv_ratio; /* To convert volume uvs to screen uvs */ + +/* Volume slice to view space depth. */ +float volume_z_to_view_z(float z) +{ + if (ProjectionMatrix[3][3] == 0.0) { + /* Exponential distribution */ + return (exp2(z / volume_param.z) - volume_param.x) / volume_param.y; + } + else { + /* Linear distribution */ + return mix(volume_param.x, volume_param.y, z); + } +} + +float view_z_to_volume_z(float depth) +{ + if (ProjectionMatrix[3][3] == 0.0) { + /* Exponential distribution */ + return volume_param.z * log2(depth * volume_param.y + volume_param.x); + } + else { + /* Linear distribution */ + return (depth - volume_param.x) * volume_param.z; + } +} + +/* Volume texture normalized coordinates to NDC (special range [0, 1]). */ +vec3 volume_to_ndc(vec3 cos) +{ + cos.z = volume_z_to_view_z(cos.z); + cos.z = get_depth_from_view_z(cos.z); + cos.xy /= volume_uv_ratio; + return cos; +} + +vec3 ndc_to_volume(vec3 cos) +{ + cos.z = get_view_z_from_depth(cos.z); + cos.z = view_z_to_volume_z(cos.z); + cos.xy *= volume_uv_ratio; + return cos; +} + +float phase_function_isotropic() +{ + return 1.0 / (4.0 * M_PI); +} + +float phase_function(vec3 v, vec3 l, float g) +{ + /* Henyey-Greenstein */ + float cos_theta = dot(v, l); + g = clamp(g, -1.0 + 1e-3, 1.0 - 1e-3); + 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)); +} + +#ifdef LAMPS_LIB +vec3 light_volume(LightData ld, vec4 l_vector) +{ + float power; + float dist = max(1e-4, abs(l_vector.w - ld.l_radius)); + /* TODO : Area lighting ? */ + /* XXX : Removing Area Power. */ + /* TODO : put this out of the shader. */ + if (ld.l_type == AREA) { + power = 0.0962 * (ld.l_sizex * ld.l_sizey * 4.0f * M_PI); + } + else { + power = 0.0248 * (4.0 * ld.l_radius * ld.l_radius * M_PI * M_PI); + } + + /* OPTI: find a better way than calculating this on the fly */ + float lum = dot(ld.l_color, vec3(0.3, 0.6, 0.1)); /* luminance approx. */ + vec3 tint = (lum > 0.0) ? ld.l_color / lum : vec3(1.0); /* normalize lum. to isolate hue+sat */ + + lum = min(lum * power / (l_vector.w * l_vector.w), volume_light_clamp); + + return tint * lum; +} + +#define VOLUMETRIC_SHADOW_MAX_STEP 32.0 + +uniform float volume_shadows_steps; + +vec3 participating_media_extinction(vec3 wpos, sampler3D volume_extinction) +{ + /* Waiting for proper volume shadowmaps and out of frustum shadow map. */ + vec3 ndc = project_point(ViewProjectionMatrix, wpos); + vec3 volume_co = ndc_to_volume(ndc * 0.5 + 0.5); + + /* Let the texture be clamped to edge. This reduce visual glitches. */ + return texture(volume_extinction, volume_co).rgb; +} + +vec3 light_volume_shadow(LightData ld, vec3 ray_wpos, vec4 l_vector, sampler3D volume_extinction) +{ +#if defined(VOLUME_SHADOW) + /* Heterogeneous volume shadows */ + float dd = l_vector.w / volume_shadows_steps; + vec3 L = l_vector.xyz * l_vector.w; + vec3 shadow = vec3(1.0); + for (float s = 0.5; s < VOLUMETRIC_SHADOW_MAX_STEP && s < (volume_shadows_steps - 0.1); s += 1.0) { + vec3 pos = ray_wpos + L * (s / volume_shadows_steps); + vec3 s_extinction = participating_media_extinction(pos, volume_extinction); + shadow *= exp(-s_extinction * dd); + } + return shadow; +#else + return vec3(1.0); +#endif /* VOLUME_SHADOW */ +} +#endif + +#ifdef IRRADIANCE_LIB +vec3 irradiance_volumetric(vec3 wpos) +{ + IrradianceData ir_data = load_irradiance_cell(0, vec3(1.0)); + vec3 irradiance = ir_data.cubesides[0] + ir_data.cubesides[1] + ir_data.cubesides[2]; + ir_data = load_irradiance_cell(0, vec3(-1.0)); + irradiance += ir_data.cubesides[0] + ir_data.cubesides[1] + ir_data.cubesides[2]; + irradiance *= 0.16666666; /* 1/6 */ + return irradiance; +} +#endif diff --git a/source/blender/draw/engines/eevee/shaders/volumetric_resolve_frag.glsl b/source/blender/draw/engines/eevee/shaders/volumetric_resolve_frag.glsl new file mode 100644 index 00000000000..3e678bbc83f --- /dev/null +++ b/source/blender/draw/engines/eevee/shaders/volumetric_resolve_frag.glsl @@ -0,0 +1,27 @@ + +/* Based on Frosbite Unified Volumetric. + * https://www.ea.com/frostbite/news/physically-based-unified-volumetric-rendering-in-frostbite */ + +/* Step 4 : Apply final integration on top of the scene color. + * Note that we do the blending ourself instead of relying + * on hardware blending which would require 2 pass. */ + +uniform sampler3D inScattering; +uniform sampler3D inTransmittance; + +uniform sampler2D inSceneColor; +uniform sampler2D inSceneDepth; + +out vec4 FragColor; + +void main() +{ + vec2 uvs = gl_FragCoord.xy / vec2(textureSize(inSceneDepth, 0)); + vec3 volume_cos = ndc_to_volume(vec3(uvs, texture(inSceneDepth, uvs).r)); + + vec3 scene_color = texture(inSceneColor, uvs).rgb; + vec3 scattering = texture(inScattering, volume_cos).rgb; + vec3 transmittance = texture(inTransmittance, volume_cos).rgb; + + FragColor = vec4(scene_color * transmittance + scattering, 1.0); +} diff --git a/source/blender/draw/engines/eevee/shaders/volumetric_scatter_frag.glsl b/source/blender/draw/engines/eevee/shaders/volumetric_scatter_frag.glsl new file mode 100644 index 00000000000..ec695b2f0ac --- /dev/null +++ b/source/blender/draw/engines/eevee/shaders/volumetric_scatter_frag.glsl @@ -0,0 +1,83 @@ + +/* Based on Frosbite Unified Volumetric. + * https://www.ea.com/frostbite/news/physically-based-unified-volumetric-rendering-in-frostbite */ + +/* Step 2 : Evaluate all light scattering for each froxels. + * Also do the temporal reprojection to fight aliasing artifacts. */ + +uniform sampler3D volumeScattering; +uniform sampler3D volumeExtinction; +uniform sampler3D volumeEmission; +uniform sampler3D volumePhase; + +uniform sampler3D historyScattering; +uniform sampler3D historyTransmittance; + +uniform vec3 volume_jitter; +uniform float volume_history_alpha; +uniform int light_count; +uniform mat4 PastViewProjectionMatrix; + +flat in int slice; + +layout(location = 0) out vec4 outScattering; +layout(location = 1) out vec4 outTransmittance; + +#define VOLUME_LIGHTING + +void main() +{ + vec3 volume_tex_size = vec3(textureSize(volumeScattering, 0)); + ivec3 volume_cell = ivec3(gl_FragCoord.xy, slice); + + /* Emission */ + outScattering = texelFetch(volumeEmission, volume_cell, 0); + outTransmittance = texelFetch(volumeExtinction, volume_cell, 0); + vec3 s_scattering = texelFetch(volumeScattering, volume_cell, 0).rgb; + vec3 volume_ndc = volume_to_ndc((vec3(volume_cell) + volume_jitter) / volume_tex_size); + vec3 worldPosition = get_world_space_from_depth(volume_ndc.xy, volume_ndc.z); + vec3 wdir = cameraVec; + + vec2 phase = texelFetch(volumePhase, volume_cell, 0).rg; + float s_anisotropy = phase.x / phase.y; + + /* Environment : Average color. */ + outScattering.rgb += irradiance_volumetric(worldPosition) * s_scattering * phase_function_isotropic(); + +#ifdef VOLUME_LIGHTING /* 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 - worldPosition; + l_vector.w = length(l_vector.xyz); + + float Vis = light_visibility(ld, worldPosition, l_vector); + + vec3 Li = light_volume(ld, l_vector) * light_volume_shadow(ld, worldPosition, l_vector, volumeExtinction); + + outScattering.rgb += Li * Vis * s_scattering * phase_function(-wdir, l_vector.xyz / l_vector.w, s_anisotropy); + } +#endif + + /* Temporal supersampling */ + /* Note : this uses the cell non-jittered position (texel center). */ + vec3 curr_ndc = volume_to_ndc(vec3(gl_FragCoord.xy, float(slice) + 0.5) / volume_tex_size); + vec3 wpos = get_world_space_from_depth(curr_ndc.xy, curr_ndc.z); + vec3 prev_ndc = project_point(PastViewProjectionMatrix, wpos); + vec3 prev_volume = ndc_to_volume(prev_ndc * 0.5 + 0.5); + + if ((volume_history_alpha > 0.0) && all(greaterThan(prev_volume, vec3(0.0))) && all(lessThan(prev_volume, vec3(1.0)))) { + vec4 h_Scattering = texture(historyScattering, prev_volume); + vec4 h_Transmittance = texture(historyTransmittance, prev_volume); + outScattering = mix(outScattering, h_Scattering, volume_history_alpha); + outTransmittance = mix(outTransmittance, h_Transmittance, volume_history_alpha); + } + + /* Catch NaNs */ + if (any(isnan(outScattering)) || any(isnan(outTransmittance))) { + outScattering = vec4(0.0); + outTransmittance = vec4(0.0); + } +} diff --git a/source/blender/draw/engines/eevee/shaders/volumetric_vert.glsl b/source/blender/draw/engines/eevee/shaders/volumetric_vert.glsl new file mode 100644 index 00000000000..a99acd41fbd --- /dev/null +++ b/source/blender/draw/engines/eevee/shaders/volumetric_vert.glsl @@ -0,0 +1,27 @@ + +out vec4 vPos; + +void main() +{ + /* Generate Triangle : less memory fetches from a VBO */ + int v_id = gl_VertexID % 3; /* Vertex Id */ + int t_id = gl_VertexID / 3; /* Triangle Id */ + + /* Crappy diagram + * ex 1 + * | \ + * | \ + * 1 | \ + * | \ + * | \ + * 0 | \ + * | \ + * | \ + * -1 0 --------------- 2 + * -1 0 1 ex + **/ + vPos.x = float(v_id / 2) * 4.0 - 1.0; /* int divisor round down */ + vPos.y = float(v_id % 2) * 4.0 - 1.0; + vPos.z = float(t_id); + vPos.w = 1.0; +} |