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.
This commit is contained in:
Clément Foucault 2017-10-24 14:49:00 +02:00
parent 1c0c63ce5b
commit 66d8f82b83
20 changed files with 798 additions and 593 deletions

View File

@ -747,6 +747,7 @@ class RENDER_PT_eevee_volumetric(RenderButtonsPanel, Panel):
col = layout.column()
col.prop(props, "volumetric_start")
col.prop(props, "volumetric_end")
col.prop(props, "volumetric_tile_size")
col.prop(props, "volumetric_samples")
col.prop(props, "volumetric_sample_distribution")
col.prop(props, "volumetric_lights")

View File

@ -308,6 +308,7 @@ class RENDERLAYER_PT_eevee_volumetric(RenderLayerButtonsPanel, Panel):
col = layout.column()
col.template_override_property(layer_props, scene_props, "volumetric_start")
col.template_override_property(layer_props, scene_props, "volumetric_end")
col.template_override_property(layer_props, scene_props, "volumetric_tile_size")
col.template_override_property(layer_props, scene_props, "volumetric_samples")
col.template_override_property(layer_props, scene_props, "volumetric_sample_distribution")
col.template_override_property(layer_props, scene_props, "volumetric_lights")

View File

@ -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)

View File

@ -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;
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);
/* World pass is not additive as it also clear the buffer. */
psl->volumetric_ps = DRW_pass_create("Volumetric Properties", DRW_STATE_WRITE_COLOR);
psl->volumetric_integrate_ps = DRW_pass_create("Volumetric Integration", DRW_STATE_WRITE_COLOR);
DRWShadingGroup *grp = DRW_shgroup_material_create(mat, psl->volumetric_integrate_ps);
/* World Volumetric */
struct GPUMaterial *mat = EEVEE_material_world_volume_get(scene, wo);
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);

View File

@ -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);

View File

@ -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);

View File

@ -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);

View File

@ -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);

View File

@ -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)

View File

@ -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);

View File

@ -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 ivec3 volumeTextureSize;
uniform vec3 volume_jitter;
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
#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;
layout(location = 0) out vec4 volumeScattering;
layout(location = 1) out vec4 volumeExtinction;
layout(location = 2) out vec4 volumeEmissive;
layout(location = 3) out vec4 volumePhase;
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();
return max(vec3(1e-4), cl.absorption + cl.scatter);
}
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];
vec4 l_vector;
l_vector.xyz = ld.l_position - ray_wpos;
l_vector.w = length(l_vector.xyz);
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]);
}
/* Store volumetric properties into the froxel textures. */
void main()
{
#if 0 /* 2 x 2 with bilinear */
ivec3 volume_cell = ivec3(gl_FragCoord.xy, slice);
vec3 ndc_cell = volume_to_ndc((vec3(volume_cell) + volume_jitter) / volumeTextureSize);
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 )
);
viewPosition = get_view_space_from_depth(ndc_cell.xy, ndc_cell.z);
worldPosition = transform_point(ViewMatrixInverse, viewPosition);
/* Depth aware upsampling */
vec4 depths;
ivec2 texel_co = ivec2(gl_FragCoord.xy * 0.5) * 2;
Closure cl = nodetree_exec();
/* TODO use textureGather on glsl 4.0 */
depths.x = texelFetchOffset(depthFull, texel_co, 0, ivec2(0, 0)).r;
depths.y = texelFetchOffset(depthFull, texel_co, 0, ivec2(2, 0)).r;
depths.z = texelFetchOffset(depthFull, texel_co, 0, ivec2(0, 2)).r;
depths.w = texelFetchOffset(depthFull, texel_co, 0, ivec2(2, 2)).r;
vec4 target_depth = texelFetch(depthFull, ivec2(gl_FragCoord.xy), 0).rrrr;
depths = get_view_z_from_depth(depths);
target_depth = get_view_z_from_depth(target_depth);
vec4 weights = 1.0 - step(0.05, abs(depths - target_depth));
/* Index in range [0-3] */
int pix_id = int(dot(mod(ivec2(gl_FragCoord.xy), 2), ivec2(1, 2)));
weights *= bilinear_weights[pix_id];
float weight_sum = dot(weights, vec4(1.0));
if (weight_sum == 0.0) {
weights.x = 1.0;
weight_sum = 1.0;
}
texel_co = ivec2(gl_FragCoord.xy * 0.5);
vec4 integration_result;
integration_result = texelFetchOffset(volumetricBuffer, texel_co, 0, ivec2(0, 0)) * weights.x;
integration_result += texelFetchOffset(volumetricBuffer, texel_co, 0, ivec2(1, 0)) * weights.y;
integration_result += texelFetchOffset(volumetricBuffer, texel_co, 0, ivec2(0, 1)) * weights.z;
integration_result += texelFetchOffset(volumetricBuffer, texel_co, 0, ivec2(1, 1)) * weights.w;
#else /* 4 x 4 */
/* Depth aware upsampling */
vec4 depths[4];
ivec2 texel_co = ivec2(gl_FragCoord.xy * 0.5) * 2;
/* TODO use textureGather on glsl 4.0 */
texel_co += ivec2(-2, -2);
depths[0].x = texelFetchOffset(depthFull, texel_co, 0, ivec2(0, 0)).r;
depths[0].y = texelFetchOffset(depthFull, texel_co, 0, ivec2(2, 0)).r;
depths[0].z = texelFetchOffset(depthFull, texel_co, 0, ivec2(0, 2)).r;
depths[0].w = texelFetchOffset(depthFull, texel_co, 0, ivec2(2, 2)).r;
texel_co += ivec2(4, 0);
depths[1].x = texelFetchOffset(depthFull, texel_co, 0, ivec2(0, 0)).r;
depths[1].y = texelFetchOffset(depthFull, texel_co, 0, ivec2(2, 0)).r;
depths[1].z = texelFetchOffset(depthFull, texel_co, 0, ivec2(0, 2)).r;
depths[1].w = texelFetchOffset(depthFull, texel_co, 0, ivec2(2, 2)).r;
texel_co += ivec2(-4, 4);
depths[2].x = texelFetchOffset(depthFull, texel_co, 0, ivec2(0, 0)).r;
depths[2].y = texelFetchOffset(depthFull, texel_co, 0, ivec2(2, 0)).r;
depths[2].z = texelFetchOffset(depthFull, texel_co, 0, ivec2(0, 2)).r;
depths[2].w = texelFetchOffset(depthFull, texel_co, 0, ivec2(2, 2)).r;
texel_co += ivec2(4, 0);
depths[3].x = texelFetchOffset(depthFull, texel_co, 0, ivec2(0, 0)).r;
depths[3].y = texelFetchOffset(depthFull, texel_co, 0, ivec2(2, 0)).r;
depths[3].z = texelFetchOffset(depthFull, texel_co, 0, ivec2(0, 2)).r;
depths[3].w = texelFetchOffset(depthFull, texel_co, 0, ivec2(2, 2)).r;
vec4 target_depth = texelFetch(depthFull, ivec2(gl_FragCoord.xy), 0).rrrr;
depths[0] = get_view_z_from_depth(depths[0]);
depths[1] = get_view_z_from_depth(depths[1]);
depths[2] = get_view_z_from_depth(depths[2]);
depths[3] = get_view_z_from_depth(depths[3]);
target_depth = get_view_z_from_depth(target_depth);
vec4 weights[4];
weights[0] = 1.0 - step(0.05, abs(depths[0] - target_depth));
weights[1] = 1.0 - step(0.05, abs(depths[1] - target_depth));
weights[2] = 1.0 - step(0.05, abs(depths[2] - target_depth));
weights[3] = 1.0 - step(0.05, abs(depths[3] - target_depth));
float weight_sum;
weight_sum = dot(weights[0], vec4(1.0));
weight_sum += dot(weights[1], vec4(1.0));
weight_sum += dot(weights[2], vec4(1.0));
weight_sum += dot(weights[3], vec4(1.0));
if (weight_sum == 0.0) {
weights[0].x = 1.0;
weight_sum = 1.0;
}
texel_co = ivec2(gl_FragCoord.xy * 0.5);
vec4 integration_result;
texel_co += ivec2(-1, -1);
integration_result = texelFetchOffset(volumetricBuffer, texel_co, 0, ivec2(0, 0)) * weights[0].x;
integration_result += texelFetchOffset(volumetricBuffer, texel_co, 0, ivec2(1, 0)) * weights[0].y;
integration_result += texelFetchOffset(volumetricBuffer, texel_co, 0, ivec2(0, 1)) * weights[0].z;
integration_result += texelFetchOffset(volumetricBuffer, texel_co, 0, ivec2(1, 1)) * weights[0].w;
texel_co += ivec2(2, 0);
integration_result += texelFetchOffset(volumetricBuffer, texel_co, 0, ivec2(0, 0)) * weights[1].x;
integration_result += texelFetchOffset(volumetricBuffer, texel_co, 0, ivec2(1, 0)) * weights[1].y;
integration_result += texelFetchOffset(volumetricBuffer, texel_co, 0, ivec2(0, 1)) * weights[1].z;
integration_result += texelFetchOffset(volumetricBuffer, texel_co, 0, ivec2(1, 1)) * weights[1].w;
texel_co += ivec2(-2, 2);
integration_result += texelFetchOffset(volumetricBuffer, texel_co, 0, ivec2(0, 0)) * weights[2].x;
integration_result += texelFetchOffset(volumetricBuffer, texel_co, 0, ivec2(1, 0)) * weights[2].y;
integration_result += texelFetchOffset(volumetricBuffer, texel_co, 0, ivec2(0, 1)) * weights[2].z;
integration_result += texelFetchOffset(volumetricBuffer, texel_co, 0, ivec2(1, 1)) * weights[2].w;
texel_co += ivec2(2, 0);
integration_result += texelFetchOffset(volumetricBuffer, texel_co, 0, ivec2(0, 0)) * weights[3].x;
integration_result += texelFetchOffset(volumetricBuffer, texel_co, 0, ivec2(1, 0)) * weights[3].y;
integration_result += texelFetchOffset(volumetricBuffer, texel_co, 0, ivec2(0, 1)) * weights[3].z;
integration_result += texelFetchOffset(volumetricBuffer, texel_co, 0, ivec2(1, 1)) * weights[3].w;
#endif
FragColor = integration_result / weight_sum;
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

View File

@ -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();
}

View File

@ -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;
}
}

View File

@ -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

View File

@ -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);
}

View File

@ -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);
}
}

View File

@ -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;
}

View File

@ -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);

View File

@ -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);

View File

@ -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);