Welcome to mirror list, hosted at ThFree Co, Russian Federation.

git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
path: root/source
diff options
context:
space:
mode:
authorClément Foucault <foucault.clem@gmail.com>2017-10-24 15:49:00 +0300
committerClément Foucault <foucault.clem@gmail.com>2017-10-27 23:49:15 +0300
commit66d8f82b832b58cba3273c0a4196fae6db0e1efd (patch)
tree682a3aba8fbd23ccd72a4e4f2a02fb36ad0a0d01 /source
parent1c0c63ce5b3914be2d0828260e5ac777a7596d36 (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')
-rw-r--r--source/blender/draw/CMakeLists.txt6
-rw-r--r--source/blender/draw/engines/eevee/eevee_effects.c461
-rw-r--r--source/blender/draw/engines/eevee/eevee_engine.c11
-rw-r--r--source/blender/draw/engines/eevee/eevee_materials.c52
-rw-r--r--source/blender/draw/engines/eevee/eevee_private.h54
-rw-r--r--source/blender/draw/engines/eevee/shaders/bsdf_common_lib.glsl11
-rw-r--r--source/blender/draw/engines/eevee/shaders/bsdf_direct_lib.glsl7
-rw-r--r--source/blender/draw/engines/eevee/shaders/lamps_lib.glsl4
-rw-r--r--source/blender/draw/engines/eevee/shaders/volumetric_frag.glsl396
-rw-r--r--source/blender/draw/engines/eevee/shaders/volumetric_geom.glsl25
-rw-r--r--source/blender/draw/engines/eevee/shaders/volumetric_integration_frag.glsl63
-rw-r--r--source/blender/draw/engines/eevee/shaders/volumetric_lib.glsl134
-rw-r--r--source/blender/draw/engines/eevee/shaders/volumetric_resolve_frag.glsl27
-rw-r--r--source/blender/draw/engines/eevee/shaders/volumetric_scatter_frag.glsl83
-rw-r--r--source/blender/draw/engines/eevee/shaders/volumetric_vert.glsl27
-rw-r--r--source/blender/draw/intern/DRW_render.h1
-rw-r--r--source/blender/draw/intern/draw_manager.c14
-rw-r--r--source/blender/makesrna/intern/rna_layer.c17
18 files changed, 798 insertions, 595 deletions
diff --git a/source/blender/draw/CMakeLists.txt b/source/blender/draw/CMakeLists.txt
index 64edd76c257..265993e8114 100644
--- a/source/blender/draw/CMakeLists.txt
+++ b/source/blender/draw/CMakeLists.txt
@@ -168,7 +168,13 @@ data_to_c_simple(engines/eevee/shaders/bsdf_sampling_lib.glsl SRC)
data_to_c_simple(engines/eevee/shaders/raytrace_lib.glsl SRC)
data_to_c_simple(engines/eevee/shaders/ltc_lib.glsl SRC)
data_to_c_simple(engines/eevee/shaders/ssr_lib.glsl SRC)
+data_to_c_simple(engines/eevee/shaders/volumetric_lib.glsl SRC)
data_to_c_simple(engines/eevee/shaders/volumetric_frag.glsl SRC)
+data_to_c_simple(engines/eevee/shaders/volumetric_geom.glsl SRC)
+data_to_c_simple(engines/eevee/shaders/volumetric_vert.glsl SRC)
+data_to_c_simple(engines/eevee/shaders/volumetric_resolve_frag.glsl SRC)
+data_to_c_simple(engines/eevee/shaders/volumetric_scatter_frag.glsl SRC)
+data_to_c_simple(engines/eevee/shaders/volumetric_integration_frag.glsl SRC)
data_to_c_simple(modes/shaders/common_globals_lib.glsl SRC)
data_to_c_simple(modes/shaders/common_fxaa_lib.glsl SRC)
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;
+}
diff --git a/source/blender/draw/intern/DRW_render.h b/source/blender/draw/intern/DRW_render.h
index 92213a0bee0..599656ae163 100644
--- a/source/blender/draw/intern/DRW_render.h
+++ b/source/blender/draw/intern/DRW_render.h
@@ -302,6 +302,7 @@ DRWShadingGroup *DRW_shgroup_create(struct GPUShader *shader, DRWPass *pass);
DRWShadingGroup *DRW_shgroup_material_create(struct GPUMaterial *material, DRWPass *pass);
DRWShadingGroup *DRW_shgroup_material_instance_create(
struct GPUMaterial *material, DRWPass *pass, struct Gwn_Batch *geom, struct Object *ob);
+DRWShadingGroup *DRW_shgroup_material_empty_tri_batch_create(struct GPUMaterial *material, DRWPass *pass, int size);
DRWShadingGroup *DRW_shgroup_instance_create(struct GPUShader *shader, DRWPass *pass, struct Gwn_Batch *geom);
DRWShadingGroup *DRW_shgroup_point_batch_create(struct GPUShader *shader, DRWPass *pass);
DRWShadingGroup *DRW_shgroup_line_batch_create(struct GPUShader *shader, DRWPass *pass);
diff --git a/source/blender/draw/intern/draw_manager.c b/source/blender/draw/intern/draw_manager.c
index 0329b3c3121..63910321f39 100644
--- a/source/blender/draw/intern/draw_manager.c
+++ b/source/blender/draw/intern/draw_manager.c
@@ -878,6 +878,20 @@ DRWShadingGroup *DRW_shgroup_material_instance_create(
return shgroup;
}
+DRWShadingGroup *DRW_shgroup_material_empty_tri_batch_create(
+ struct GPUMaterial *material, DRWPass *pass, int size)
+{
+ DRWShadingGroup *shgroup = DRW_shgroup_material_create(material, pass);
+
+ if (shgroup) {
+ shgroup->type = DRW_SHG_TRIANGLE_BATCH;
+ shgroup->interface->instance_count = size * 3;
+ DRW_interface_attrib(shgroup, "dummy", DRW_ATTRIB_FLOAT, 1, true);
+ }
+
+ return shgroup;
+}
+
DRWShadingGroup *DRW_shgroup_instance_create(struct GPUShader *shader, DRWPass *pass, Gwn_Batch *geom)
{
DRWShadingGroup *shgroup = DRW_shgroup_create(shader, pass);
diff --git a/source/blender/makesrna/intern/rna_layer.c b/source/blender/makesrna/intern/rna_layer.c
index 49c2da6973f..ca629f2963e 100644
--- a/source/blender/makesrna/intern/rna_layer.c
+++ b/source/blender/makesrna/intern/rna_layer.c
@@ -359,6 +359,7 @@ RNA_LAYER_ENGINE_EEVEE_GET_SET_FLOAT(motion_blur_shutter)
RNA_LAYER_ENGINE_EEVEE_GET_SET_BOOL(volumetric_enable)
RNA_LAYER_ENGINE_EEVEE_GET_SET_FLOAT(volumetric_start)
RNA_LAYER_ENGINE_EEVEE_GET_SET_FLOAT(volumetric_end)
+RNA_LAYER_ENGINE_EEVEE_GET_SET_INT(volumetric_tile_size)
RNA_LAYER_ENGINE_EEVEE_GET_SET_INT(volumetric_samples)
RNA_LAYER_ENGINE_EEVEE_GET_SET_FLOAT(volumetric_sample_distribution)
RNA_LAYER_ENGINE_EEVEE_GET_SET_BOOL(volumetric_lights)
@@ -1142,6 +1143,14 @@ static void rna_def_scene_layer_engine_settings_eevee(BlenderRNA *brna)
{0, NULL, 0, NULL, NULL}
};
+ static const EnumPropertyItem eevee_volumetric_tile_size_items[] = {
+ {2, "2", 0, "2px", ""},
+ {4, "4", 0, "4px", ""},
+ {8, "8", 0, "8px", ""},
+ {16, "16", 0, "16px", ""},
+ {0, NULL, 0, NULL, NULL}
+ };
+
srna = RNA_def_struct(brna, "SceneLayerEngineSettingsEevee", "SceneLayerSettings");
RNA_def_struct_ui_text(srna, "Eevee Scene Layer Settings", "Eevee Engine settings");
@@ -1273,6 +1282,14 @@ static void rna_def_scene_layer_engine_settings_eevee(BlenderRNA *brna)
RNA_def_property_flag(prop, PROP_CONTEXT_UPDATE);
RNA_def_property_update(prop, NC_SCENE | ND_LAYER_CONTENT, "rna_SceneLayerEngineSettings_update");
+ prop = RNA_def_property(srna, "volumetric_tile_size", PROP_ENUM, PROP_NONE);
+ RNA_def_property_enum_funcs(prop, "rna_LayerEngineSettings_Eevee_volumetric_tile_size_get",
+ "rna_LayerEngineSettings_Eevee_volumetric_tile_size_set", NULL);
+ RNA_def_property_enum_items(prop, eevee_volumetric_tile_size_items);
+ RNA_def_property_ui_text(prop, "Tile Size", "Number of samples to compute volumetric effects");
+ RNA_def_property_flag(prop, PROP_CONTEXT_UPDATE);
+ RNA_def_property_update(prop, NC_SCENE | ND_LAYER_CONTENT, "rna_SceneLayerEngineSettings_update");
+
prop = RNA_def_property(srna, "volumetric_samples", PROP_INT, PROP_NONE);
RNA_def_property_int_funcs(prop, "rna_LayerEngineSettings_Eevee_volumetric_samples_get",
"rna_LayerEngineSettings_Eevee_volumetric_samples_set", NULL);