Eevee: Volumetrics: Add Volume object support.

This is quite basic as it only support boundbing boxes.
But the material can refine the volume shape in anyway the user like.

To overcome this limitation, a voxelisation should be done on the mesh (generating a SDF maybe?) and tested against every volumetric cell.
This commit is contained in:
Clément Foucault 2017-10-27 16:20:33 +02:00
parent 18ba7e26ad
commit 4f7665c844
8 changed files with 182 additions and 16 deletions

View File

@ -36,6 +36,7 @@
#include "BKE_global.h" /* for G.debug_value */
#include "BKE_camera.h"
#include "BKE_mesh.h"
#include "BKE_object.h"
#include "BKE_animsys.h"
#include "BKE_screen.h"
@ -93,6 +94,7 @@ static struct {
struct GPUShader *dof_resolve_sh;
/* Volumetric */
struct GPUShader *volumetric_clear_sh;
struct GPUShader *volumetric_scatter_sh;
struct GPUShader *volumetric_integration_sh;
struct GPUShader *volumetric_resolve_sh;
@ -300,6 +302,12 @@ void EEVEE_effects_init(EEVEE_SceneLayerData *sldata, EEVEE_Data *vedata)
e_data.volumetric_common_lamps_lib = BLI_dynstr_get_cstring(ds_frag);
BLI_dynstr_free(ds_frag);
e_data.volumetric_clear_sh = DRW_shader_create_with_lib(datatoc_volumetric_vert_glsl,
datatoc_volumetric_geom_glsl,
datatoc_volumetric_frag_glsl,
e_data.volumetric_common_lib,
"#define VOLUMETRICS\n"
"#define CLEAR\n");
e_data.volumetric_scatter_sh = DRW_shader_create_with_lib(datatoc_volumetric_vert_glsl,
datatoc_volumetric_geom_glsl,
datatoc_volumetric_scatter_frag_glsl,
@ -1031,6 +1039,38 @@ static DRWShadingGroup *eevee_create_bloom_pass(const char *name, EEVEE_EffectsI
return grp;
}
void EEVEE_effects_cache_volume_object_add(EEVEE_SceneLayerData *sldata, EEVEE_Data *vedata, Scene *scene, Object *ob)
{
float *texcoloc = NULL;
float *texcosize = NULL;
EEVEE_VolumetricsInfo *volumetrics = sldata->volumetrics;
Material *ma = give_current_material(ob, 1);
if (ma == NULL) {
return;
}
struct GPUMaterial *mat = EEVEE_material_mesh_volume_get(scene, ma);
DRWShadingGroup *grp = DRW_shgroup_material_empty_tri_batch_create(mat, vedata->psl->volumetric_objects_ps, volumetrics->froxel_tex_size[2]);
/* Making sure it's updated. */
invert_m4_m4(ob->imat, ob->obmat);
BKE_mesh_texspace_get_reference((struct Mesh *)ob->data, NULL, &texcoloc, NULL, &texcosize);
if (grp) {
DRW_shgroup_uniform_mat4(grp, "volumeObjectMatrix", (float *)ob->imat);
DRW_shgroup_uniform_vec3(grp, "volumeOrcoLoc", texcoloc, 1);
DRW_shgroup_uniform_vec3(grp, "volumeOrcoSize", texcosize, 1);
DRW_shgroup_uniform_vec4(grp, "viewvecs[0]", (float *)vedata->stl->g_data->viewvecs, 2);
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);
}
}
void EEVEE_effects_cache_init(EEVEE_SceneLayerData *sldata, EEVEE_Data *vedata)
{
EEVEE_PassList *psl = vedata->psl;
@ -1054,26 +1094,36 @@ void EEVEE_effects_cache_init(EEVEE_SceneLayerData *sldata, EEVEE_Data *vedata)
if ((effects->enabled_effects & EFFECT_VOLUMETRIC) != 0) {
const DRWContextState *draw_ctx = DRW_context_state_get();
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);
psl->volumetric_world_ps = DRW_pass_create("Volumetric World", DRW_STATE_WRITE_COLOR);
/* World Volumetric */
struct GPUMaterial *mat = EEVEE_material_world_volume_get(scene, wo);
struct World *wo = scene->world;
if (wo != NULL && wo->use_nodes && wo->nodetree) {
struct GPUMaterial *mat = EEVEE_material_world_volume_get(scene, wo);
grp = DRW_shgroup_material_empty_tri_batch_create(mat, psl->volumetric_ps, volumetrics->froxel_tex_size[2]);
grp = DRW_shgroup_material_empty_tri_batch_create(mat, psl->volumetric_world_ps, volumetrics->froxel_tex_size[2]);
if (grp) {
DRW_shgroup_uniform_vec4(grp, "viewvecs[0]", (float *)stl->g_data->viewvecs, 2);
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);
if (grp) {
DRW_shgroup_uniform_vec4(grp, "viewvecs[0]", (float *)stl->g_data->viewvecs, 2);
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);
}
}
else {
grp = DRW_shgroup_empty_tri_batch_create(e_data.volumetric_clear_sh, psl->volumetric_world_ps, volumetrics->froxel_tex_size[2]);
DRW_shgroup_uniform_ivec3(grp, "volumeTextureSize", (int *)volumetrics->froxel_tex_size, 1);
}
/* Volumetric Objects */
psl->volumetric_objects_ps = DRW_pass_create("Volumetric Properties", DRW_STATE_WRITE_COLOR | DRW_STATE_ADDITIVE);
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]);
@ -1490,7 +1540,8 @@ void EEVEE_effects_do_volumetrics(EEVEE_SceneLayerData *UNUSED(sldata), EEVEE_Da
/* Step 1: Participating Media Properties */
DRW_framebuffer_bind(fbl->volumetric_fb);
DRW_draw_pass(psl->volumetric_ps);
DRW_draw_pass(psl->volumetric_world_ps);
DRW_draw_pass(psl->volumetric_objects_ps);
/* Step 2: Scatter Light */
DRW_framebuffer_bind(fbl->volumetric_scat_fb);
@ -1852,6 +1903,7 @@ 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_clear_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);

View File

@ -318,7 +318,7 @@ static char *eevee_get_defines(int options)
return str;
}
static char *eevee_get_volume_defines(int UNUSED(options))
static char *eevee_get_volume_defines(int options)
{
char *str = NULL;
@ -326,6 +326,10 @@ static char *eevee_get_volume_defines(int UNUSED(options))
BLI_dynstr_appendf(ds, SHADER_DEFINES);
BLI_dynstr_appendf(ds, "#define VOLUMETRICS\n");
if ((options & VAR_MAT_VOLUME) != 0) {
BLI_dynstr_appendf(ds, "#define MESH_SHADER\n");
}
str = BLI_dynstr_get_cstring(ds);
BLI_dynstr_free(ds);
@ -656,6 +660,28 @@ struct GPUMaterial *EEVEE_material_mesh_get(
return mat;
}
struct GPUMaterial *EEVEE_material_mesh_volume_get(struct Scene *scene, Material *ma)
{
const void *engine = &DRW_engine_viewport_eevee_type;
int options = VAR_MAT_VOLUME;
GPUMaterial *mat = GPU_material_from_nodetree_find(&ma->gpumaterial, engine, options);
if (mat != NULL) {
return mat;
}
char *defines = eevee_get_volume_defines(options);
mat = GPU_material_from_nodetree(
scene, ma->nodetree, &ma->gpumaterial, engine, options,
datatoc_volumetric_vert_glsl, datatoc_volumetric_geom_glsl, e_data.volume_shader_lib,
defines);
MEM_freeN(defines);
return mat;
}
struct GPUMaterial *EEVEE_material_mesh_depth_get(
struct Scene *scene, Material *ma,
bool use_hashed_alpha, bool is_shadow)
@ -1200,6 +1226,12 @@ void EEVEE_materials_cache_populate(EEVEE_Data *vedata, EEVEE_SceneLayerData *sl
DRW_cache_mesh_sculpt_coords_ensure(ob);
}
/* Only support single volume material for now. */
/* XXX We rely on the previously compiled surface shader
* to know if the material has a "volume nodetree".
*/
bool use_volume_material = (gpumat_array[0] && GPU_material_use_domain_volume(gpumat_array[0]));
/* Get per-material split surface */
struct Gwn_Batch **mat_geom = DRW_cache_object_surface_material_get(ob, gpumat_array, materials_len);
if (mat_geom) {
@ -1209,6 +1241,14 @@ void EEVEE_materials_cache_populate(EEVEE_Data *vedata, EEVEE_SceneLayerData *sl
if (ma == NULL)
ma = &defmaterial;
/* Do not render surface if we are rendering a volume object
* and do not have a surface closure. */
if (use_volume_material &&
(gpumat_array[i] && !GPU_material_use_domain_surface(gpumat_array[i])))
{
continue;
}
/* Shading pass */
ADD_SHGROUP_CALL(shgrp_array[i], ob, mat_geom[i]);
@ -1241,6 +1281,11 @@ void EEVEE_materials_cache_populate(EEVEE_Data *vedata, EEVEE_SceneLayerData *sl
}
}
}
/* Volumetrics */
if (vedata->stl->effects->use_volumetrics && use_volume_material) {
EEVEE_effects_cache_volume_object_add(sldata, vedata, scene, ob);
}
}
if (ob->type == OB_MESH) {

View File

@ -133,7 +133,8 @@ typedef struct EEVEE_PassList {
struct DRWPass *dof_down;
struct DRWPass *dof_scatter;
struct DRWPass *dof_resolve;
struct DRWPass *volumetric_ps;
struct DRWPass *volumetric_world_ps;
struct DRWPass *volumetric_objects_ps;
struct DRWPass *volumetric_scatter_ps;
struct DRWPass *volumetric_integration_ps;
struct DRWPass *volumetric_resolve_ps;
@ -600,6 +601,7 @@ struct GPUMaterial *EEVEE_material_world_volume_get(struct Scene *scene, struct
struct GPUMaterial *EEVEE_material_mesh_get(
struct Scene *scene, Material *ma, EEVEE_Data *vedata,
bool use_blend, bool use_multiply, bool use_refract, int shadow_method);
struct GPUMaterial *EEVEE_material_mesh_volume_get(struct Scene *scene, Material *ma);
struct GPUMaterial *EEVEE_material_mesh_depth_get(struct Scene *scene, Material *ma, bool use_hashed_alpha, bool is_shadow);
struct GPUMaterial *EEVEE_material_hair_get(struct Scene *scene, Material *ma, int shadow_method);
void EEVEE_materials_free(void);
@ -632,6 +634,7 @@ void EEVEE_lightprobes_free(void);
/* eevee_effects.c */
void EEVEE_effects_init(EEVEE_SceneLayerData *sldata, EEVEE_Data *vedata);
void EEVEE_effects_cache_init(EEVEE_SceneLayerData *sldata, EEVEE_Data *vedata);
void EEVEE_effects_cache_volume_object_add(EEVEE_SceneLayerData *sldata, EEVEE_Data *vedata, struct Scene *scene, Object *ob);
void EEVEE_create_minmax_buffer(EEVEE_Data *vedata, struct GPUTexture *depth_src, int layer);
void EEVEE_downsample_buffer(EEVEE_Data *vedata, struct GPUFrameBuffer *fb_src, struct GPUTexture *texture_src, int level);
void EEVEE_downsample_cube_buffer(EEVEE_Data *vedata, struct GPUFrameBuffer *fb_src, struct GPUTexture *texture_src, int level);

View File

@ -574,7 +574,8 @@ Closure closure_add(Closure cl1, Closure cl2)
cl.anisotropy = (cl1.anisotropy + cl2.anisotropy) / 2.0; /* Average phase (no multi lobe) */
return cl;
}
#else
#else /* VOLUMETRICS */
struct Closure {
vec3 radiance;

View File

@ -7,12 +7,21 @@
uniform ivec3 volumeTextureSize;
uniform vec3 volume_jitter;
#ifdef MESH_SHADER
uniform mat4 volumeObjectMatrix;
uniform vec3 volumeOrcoLoc;
uniform vec3 volumeOrcoSize;
#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);
#ifdef MESH_SHADER
vec3 volumeObjectLocalCoord = vec3(0.0);
#endif
layout(location = 0) out vec4 volumeScattering;
layout(location = 1) out vec4 volumeExtinction;
@ -28,11 +37,30 @@ void main()
viewPosition = get_view_space_from_depth(ndc_cell.xy, ndc_cell.z);
worldPosition = transform_point(ViewMatrixInverse, viewPosition);
#ifdef MESH_SHADER
volumeObjectLocalCoord = transform_point(volumeObjectMatrix, worldPosition);
volumeObjectLocalCoord = (volumeObjectLocalCoord - volumeOrcoLoc + volumeOrcoSize) / (volumeOrcoSize * 2.0);
if (any(lessThan(volumeObjectLocalCoord, vec3(0.0))) ||
any(greaterThan(volumeObjectLocalCoord, vec3(1.0))))
discard;
#endif
#ifdef CLEAR
Closure cl = CLOSURE_DEFAULT;
#else
Closure cl = nodetree_exec();
#endif
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));
/* Do not add phase weight if no scattering. */
if (all(equal(cl.scatter, vec3(0.0)))) {
volumePhase = vec4(0.0);
}
else {
volumePhase = vec4(cl.anisotropy, vec3(1.0));
}
}

View File

@ -1,11 +1,36 @@
#ifdef MESH_SHADER
/* TODO tight slices */
layout(triangles) in;
layout(triangle_strip, max_vertices=3) out;
#else /* World */
layout(triangles) in;
layout(triangle_strip, max_vertices=3) out;
#endif
in vec4 vPos[];
flat out int slice;
#ifdef MESH_SHADER
/* TODO tight slices */
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();
}
#else /* World */
/* This is just a pass-through geometry shader that send the geometry
* to the layer corresponding to it's depth. */
@ -22,4 +47,6 @@ void main() {
EmitVertex();
EndPrimitive();
}
}
#endif

View File

@ -2470,7 +2470,11 @@ float hypot(float x, float y)
void generated_from_orco(vec3 orco, out vec3 generated)
{
#ifdef VOLUMETRICS
#ifdef MESH_SHADER
generated = volumeObjectLocalCoord;
#else
generated = worldPosition;
#endif
#else
generated = orco;
#endif
@ -4053,7 +4057,11 @@ void node_bump(float strength, float dist, float height, vec3 N, vec3 surf_pos,
void node_output_material(Closure surface, Closure volume, float displacement, out Closure result)
{
#ifdef VOLUMETRICS
result = volume;
#else
result = surface;
#endif
}
uniform float backgroundAlpha;

View File

@ -47,6 +47,8 @@ static int node_shader_gpu_tex_coord(GPUMaterial *mat, bNode *node, bNodeExecDat
GPUNodeLink *orco = GPU_attribute(CD_ORCO, "");
GPUNodeLink *mtface = GPU_attribute(CD_MTFACE, "");
GPUMatType type = GPU_Material_get_type(mat);
GPU_link(mat, "generated_from_orco", orco, &orco);
if (type == GPU_MATERIAL_TYPE_WORLD) {
return GPU_stack_link(mat, node, "node_tex_coord_background", in, out,