Eevee: Volumetrics: Add Volume object support.
authorClément Foucault <foucault.clem@gmail.com>
Fri, 27 Oct 2017 14:20:33 +0000 (16:20 +0200)
committerClément Foucault <foucault.clem@gmail.com>
Fri, 27 Oct 2017 20:49:15 +0000 (22:49 +0200)
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.

source/blender/draw/engines/eevee/eevee_effects.c
source/blender/draw/engines/eevee/eevee_materials.c
source/blender/draw/engines/eevee/eevee_private.h
source/blender/draw/engines/eevee/shaders/bsdf_common_lib.glsl
source/blender/draw/engines/eevee/shaders/volumetric_frag.glsl
source/blender/draw/engines/eevee/shaders/volumetric_geom.glsl
source/blender/gpu/shaders/gpu_shader_material.glsl
source/blender/nodes/shader/nodes/node_shader_tex_coord.c

index c6dcf78d4011d11eb8f9b6d5da733fb197f0c34f..d3c22ed29f4d8ef7fdef38f4252e0af8631b8d90 100644 (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,27 +1094,37 @@ 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);
-
-               grp = DRW_shgroup_material_empty_tri_batch_create(mat, psl->volumetric_ps, volumetrics->froxel_tex_size[2]);
+               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_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);
+                       }
+               }
+               else {
+                       grp = DRW_shgroup_empty_tri_batch_create(e_data.volumetric_clear_sh, 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);
                }
 
+               /* 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]);
                DRW_shgroup_uniform_vec4(grp, "viewvecs[0]", (float *)stl->g_data->viewvecs, 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);
index f5d3898d1bc4a5edb2494be8bff1ffa218ffc3db..1d551796a60cdacee01291307573c11ed224d020 100644 (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) {
index ced44fad0c947a826efb21edd0bd310d362bdeac..20353fdc5124de28bbebbeb4d4aab470b8cc0313 100644 (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);
index eb0b93d5ac4407b25ab67a2732389717cc83cb61..b2c72126c40aa3887d23859c38a0934196161be7 100644 (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;
index a3e2979a9b40a4649a7d2d13f0c9df75f48a88a4..00e01e753f9fedf3da1a1032504c8c34bb15b7fc 100644 (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));
+       }
 }
index f944184058d58d42e18a1539d409f5cf0c1942f5..208e81656892c049b256d6d40c9d87e452697adc 100644 (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();
-}
\ No newline at end of file
+}
+
+#endif
index fb89c554775d9caa091f519db08846651227a2a9..0dd8e6643b283625fb75ad480303a5bb69dd9ceb 100644 (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;
index 99ab4ad2c206524a88c7a734648742026b572257..0cab9379364419eb53eaba9b705399c76bc6bfc5 100644 (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,