Workbench: Add cubic filtering for smoke simulation
authorClément Foucault <foucault.clem@gmail.com>
Wed, 7 Nov 2018 12:22:22 +0000 (13:22 +0100)
committerClément Foucault <foucault.clem@gmail.com>
Wed, 7 Nov 2018 12:25:28 +0000 (13:25 +0100)
The option is per domain and only affects the solid / xray / wireframe view.

Eevee is not yet supported.

release/scripts/startup/bl_ui/properties_physics_smoke.py
source/blender/blenkernel/intern/smoke.c
source/blender/draw/engines/workbench/shaders/workbench_volume_frag.glsl
source/blender/draw/engines/workbench/workbench_volume.c
source/blender/makesdna/DNA_smoke_types.h
source/blender/makesrna/intern/rna_smoke.c

index 9896d3afc521c18c1f7f3fd467823b630ea0bb25..2e698ffb58d395ed3c970ae04bc94e33faa0df89 100644 (file)
@@ -596,9 +596,11 @@ class PHYSICS_PT_smoke_viewport_display(PhysicButtonsPanel, Panel):
         sub.prop(domain, "slice_axis")
         sub.prop(domain, "slice_depth")
 
-        col = col.row()
-        col.enabled = do_full_slicing or not do_axis_slicing
-        col.prop(domain, "slice_per_voxel")
+        row = col.row()
+        row.enabled = do_full_slicing or not do_axis_slicing
+        row.prop(domain, "slice_per_voxel")
+
+        col.prop(domain, "display_interpolation")
 
 
 class PHYSICS_PT_smoke_viewport_display_color(PhysicButtonsPanel, Panel):
index be3a025a96ff86d49eb038aa05f949fbb50a3dd6..ced6cf0fe7424cb14e1e5b738ca9e83fb2ab899b 100644 (file)
@@ -670,6 +670,7 @@ void smokeModifier_copy(const struct SmokeModifierData *smd, struct SmokeModifie
                tsds->slice_per_voxel = sds->slice_per_voxel;
                tsds->slice_depth = sds->slice_depth;
                tsds->slice_axis = sds->slice_axis;
+               tsds->interp_method = sds->interp_method;
                tsds->draw_velocity = sds->draw_velocity;
                tsds->vector_draw_type = sds->vector_draw_type;
                tsds->vector_scale = sds->vector_scale;
index 023ebb8e1114394daa1389944b4fa1ad8f325540..f14078a71eb57d3965c5d2a196cd45e3b69bc3db 100644 (file)
@@ -67,20 +67,73 @@ float line_unit_box_intersect_dist(vec3 lineorigin, vec3 linedirection)
        return max_v3(furthestplane);
 }
 
+#define sample_trilinear(ima, co) texture(ima, co)
+
+vec4 sample_tricubic(sampler3D ima, vec3 co)
+{
+       vec3 tex_size = vec3(textureSize(ima, 0).xyz);
+
+       co *= tex_size;
+       /* texel center */
+       vec3 tc = floor(co - 0.5) + 0.5;
+       vec3 f = co - tc;
+       vec3 f2 = f * f;
+       vec3 f3 = f2 * f;
+       /* Bspline coefs (optimized) */
+       vec3 w3 =  f3 / 6.0;
+       vec3 w0 = -w3       + f2 * 0.5 - f * 0.5 + 1.0 / 6.0;
+       vec3 w1 =  f3 * 0.5 - f2                 + 2.0 / 3.0;
+       vec3 w2 = 1.0 - w0 - w1 - w3;
+
+       vec3 s0 = w0 + w1;
+       vec3 s1 = w2 + w3;
+
+       vec3 f0 = w1 / (w0 + w1);
+       vec3 f1 = w3 / (w2 + w3);
+
+       vec2 final_z;
+       vec4 final_co;
+       final_co.xy = tc.xy - 1.0 + f0.xy;
+       final_co.zw = tc.xy + 1.0 + f1.xy;
+       final_z     = tc.zz + vec2(-1.0, 1.0) + vec2(f0.z, f1.z);
+
+       final_co /= tex_size.xyxy;
+       final_z  /= tex_size.zz;
+
+       vec4 color;
+       color  = texture(ima, vec3(final_co.xy, final_z.x)) * s0.x * s0.y * s0.z;
+       color += texture(ima, vec3(final_co.zy, final_z.x)) * s1.x * s0.y * s0.z;
+       color += texture(ima, vec3(final_co.xw, final_z.x)) * s0.x * s1.y * s0.z;
+       color += texture(ima, vec3(final_co.zw, final_z.x)) * s1.x * s1.y * s0.z;
+
+       color += texture(ima, vec3(final_co.xy, final_z.y)) * s0.x * s0.y * s1.z;
+       color += texture(ima, vec3(final_co.zy, final_z.y)) * s1.x * s0.y * s1.z;
+       color += texture(ima, vec3(final_co.xw, final_z.y)) * s0.x * s1.y * s1.z;
+       color += texture(ima, vec3(final_co.zw, final_z.y)) * s1.x * s1.y * s1.z;
+
+       return color;
+}
+
+#ifdef USE_TRICUBIC
+#  define sample_volume_texture sample_tricubic
+#else
+#  define sample_volume_texture sample_trilinear
+#endif
+
 void volume_properties(vec3 ls_pos, out vec3 scattering, out float extinction)
 {
        vec3 co = ls_pos * 0.5 + 0.5;
 #ifdef USE_COBA
-       float val = texture(densityTexture, co).r;
+       float val = sample_volume_texture(densityTexture, co).r;
        vec4 tval = texture(transferTexture, val) * densityScale;
        tval.rgb = pow(tval.rgb, vec3(2.2));
        scattering = tval.rgb * 1500.0;
        extinction = max(1e-4, tval.a * 50.0);
 #else
-       float flame = texture(flameTexture, co).r;
+       float flame = sample_volume_texture(flameTexture, co).r;
        vec4 emission = texture(flameColorTexture, flame);
-       float shadows = texture(shadowTexture, co).r;
-       vec4 density = texture(densityTexture, co); /* rgb: color, a: density */
+       float shadows = sample_volume_texture(shadowTexture, co).r;
+       vec4 density = sample_volume_texture(densityTexture, co); /* rgb: color, a: density */
 
        scattering = density.rgb * density.a * densityScale;
        extinction = max(1e-4, dot(scattering, vec3(0.33333)));
index 0f6b3e5954aa77808b7a635eb4824fa8c8c94159..e720bb6aa648edf19c2766409e36e0248418ea35 100644 (file)
@@ -28,6 +28,7 @@
 #include "BKE_modifier.h"
 
 #include "BLI_rand.h"
+#include "BLI_dynstr.h"
 
 #include "DNA_modifier_types.h"
 #include "DNA_object_force_types.h"
 
 #include "GPU_draw.h"
 
+enum {
+       VOLUME_SH_SLICE = 0,
+       VOLUME_SH_COBA,
+       VOLUME_SH_CUBIC,
+};
+
+#define VOLUME_SH_MAX (1 << (VOLUME_SH_CUBIC + 1))
+
 static struct {
-       struct GPUShader *volume_sh;
+       struct GPUShader *volume_sh[VOLUME_SH_MAX];
        struct GPUShader *volume_coba_sh;
        struct GPUShader *volume_slice_sh;
        struct GPUShader *volume_slice_coba_sh;
@@ -47,26 +56,43 @@ static struct {
 extern char datatoc_workbench_volume_vert_glsl[];
 extern char datatoc_workbench_volume_frag_glsl[];
 
-void workbench_volume_engine_init(void)
+static GPUShader *volume_shader_get(bool slice, bool coba, bool cubic)
 {
-       if (!e_data.volume_sh) {
-               e_data.volume_sh = DRW_shader_create(
-                       datatoc_workbench_volume_vert_glsl, NULL,
-                       datatoc_workbench_volume_frag_glsl, NULL);
-               e_data.volume_coba_sh = DRW_shader_create(
-                       datatoc_workbench_volume_vert_glsl, NULL,
-                       datatoc_workbench_volume_frag_glsl,
-                       "#define USE_COBA\n");
-               e_data.volume_slice_sh = DRW_shader_create(
-                       datatoc_workbench_volume_vert_glsl, NULL,
-                       datatoc_workbench_volume_frag_glsl,
-                       "#define VOLUME_SLICE\n");
-               e_data.volume_slice_coba_sh = DRW_shader_create(
+       int id = 0;
+       id += (slice) ? (1 << VOLUME_SH_SLICE) : 0;
+       id += (coba) ? (1 << VOLUME_SH_COBA) : 0;
+       id += (cubic) ? (1 << VOLUME_SH_CUBIC) : 0;
+
+       if (!e_data.volume_sh[id]) {
+               DynStr *ds = BLI_dynstr_new();
+
+               if (slice) {
+                       BLI_dynstr_append(ds, "#define VOLUME_SLICE\n");
+               }
+               if (coba) {
+                       BLI_dynstr_append(ds, "#define USE_COBA\n");
+               }
+               if (cubic) {
+                       BLI_dynstr_append(ds, "#define USE_TRICUBIC\n");
+               }
+
+               char *defines = BLI_dynstr_get_cstring(ds);
+               BLI_dynstr_free(ds);
+
+               e_data.volume_sh[id] = DRW_shader_create(
                        datatoc_workbench_volume_vert_glsl, NULL,
                        datatoc_workbench_volume_frag_glsl,
-                       "#define VOLUME_SLICE\n"
-                       "#define USE_COBA\n");
+                       defines);
 
+               MEM_freeN(defines);
+       }
+
+       return e_data.volume_sh[id];
+}
+
+void workbench_volume_engine_init(void)
+{
+       if (!e_data.dummy_tex) {
                float pixel[4] = {0.0f, 0.0f, 0.0f, 0.0f};
                e_data.dummy_tex = GPU_texture_create_3D(1, 1, 1, GPU_RGBA8, pixel, NULL);
                e_data.dummy_coba_tex = GPU_texture_create_1D(1, GPU_RGBA8, pixel, NULL);
@@ -75,10 +101,9 @@ void workbench_volume_engine_init(void)
 
 void workbench_volume_engine_free(void)
 {
-       DRW_SHADER_FREE_SAFE(e_data.volume_sh);
-       DRW_SHADER_FREE_SAFE(e_data.volume_coba_sh);
-       DRW_SHADER_FREE_SAFE(e_data.volume_slice_sh);
-       DRW_SHADER_FREE_SAFE(e_data.volume_slice_coba_sh);
+       for (int i = 0; i < VOLUME_SH_MAX; ++i) {
+               DRW_SHADER_FREE_SAFE(e_data.volume_sh[i]);
+       }
        DRW_TEXTURE_FREE_SAFE(e_data.dummy_tex);
        DRW_TEXTURE_FREE_SAFE(e_data.dummy_coba_tex);
 }
@@ -121,6 +146,8 @@ void workbench_volume_cache_populate(WORKBENCH_Data *vedata, Scene *scene, Objec
 
        const bool use_slice = (sds->slice_method == MOD_SMOKE_SLICE_AXIS_ALIGNED &&
                                sds->axis_slice_method == AXIS_SLICE_SINGLE);
+       const bool cubic_interp = (sds->interp_method == VOLUME_INTERP_CUBIC);
+       GPUShader *sh = volume_shader_get(use_slice, sds->use_coba, cubic_interp);
 
        if (use_slice) {
                float invviewmat[4][4];
@@ -130,7 +157,6 @@ void workbench_volume_cache_populate(WORKBENCH_Data *vedata, Scene *scene, Objec
                                  ? axis_dominant_v3_single(invviewmat[2])
                                  : sds->slice_axis - 1;
 
-               GPUShader *sh = (sds->use_coba) ? e_data.volume_slice_coba_sh : e_data.volume_slice_sh;
                grp = DRW_shgroup_create(sh, vedata->psl->volume_pass);
                DRW_shgroup_uniform_float_copy(grp, "slicePosition", sds->slice_depth);
                DRW_shgroup_uniform_int_copy(grp, "sliceAxis", axis);
@@ -140,7 +166,6 @@ void workbench_volume_cache_populate(WORKBENCH_Data *vedata, Scene *scene, Objec
                BLI_halton_1D(3, 0.0, effect_info->jitter_index, &noise_ofs);
                int max_slices = max_iii(sds->res[0], sds->res[1], sds->res[2]) * sds->slice_per_voxel;
 
-               GPUShader *sh = (sds->use_coba) ? e_data.volume_coba_sh : e_data.volume_sh;
                grp = DRW_shgroup_create(sh, vedata->psl->volume_pass);
                DRW_shgroup_uniform_vec4(grp, "viewvecs[0]", (float *)wpd->viewvecs, 3);
                DRW_shgroup_uniform_int_copy(grp, "samplesLen", max_slices);
index b8ac0de00902a65b70550526c71abed048e55381..97913e29ad454ae1056e5355cc3159a34346dbf4 100644 (file)
@@ -72,6 +72,12 @@ enum {
        SLICE_AXIS_Z    = 3,
 };
 
+/* axis aligned method */
+enum {
+       VOLUME_INTERP_LINEAR   = 0,
+       VOLUME_INTERP_CUBIC    = 1,
+};
+
 enum {
        VECTOR_DRAW_NEEDLE     = 0,
        VECTOR_DRAW_STREAMLINE = 1,
@@ -222,7 +228,7 @@ typedef struct SmokeDomainSettings {
        char vector_draw_type;
        char use_coba;
        char coba_field;  /* simulation field used for the color mapping */
-       char pad2;
+       char interp_method;
 
        float clipping;
        float pad3;
index 5eb25985fa4cca45dfbe64c7b8f521c71e5f6a84..591798d508a56d177ea593c6435722b37a9254b5 100644 (file)
@@ -514,6 +514,12 @@ static void rna_def_smoke_domain_settings(BlenderRNA *brna)
            {0, NULL, 0, NULL, NULL}
        };
 
+       static const EnumPropertyItem interp_method_item[] = {
+           {VOLUME_INTERP_LINEAR, "LINEAR", 0, "Linear", "Good smoothness and speed"},
+           {VOLUME_INTERP_CUBIC, "CUBIC", 0, "Cubic", "Smoothed high quality interpolation, but slower"},
+           {0, NULL, 0, NULL, NULL}
+       };
+
        static const EnumPropertyItem axis_slice_position_items[] = {
            {SLICE_AXIS_AUTO, "AUTO", 0, "Auto", "Adjust slice direction according to the view direction"},
            {SLICE_AXIS_X, "X", 0, "X", "Slice along the X axis"},
@@ -856,6 +862,12 @@ static void rna_def_smoke_domain_settings(BlenderRNA *brna)
        RNA_def_property_ui_text(prop, "Thickness", "Thickness of smoke drawing in the viewport");
        RNA_def_property_update(prop, NC_OBJECT | ND_MODIFIER, NULL);
 
+       prop = RNA_def_property(srna, "display_interpolation", PROP_ENUM, PROP_NONE);
+       RNA_def_property_enum_sdna(prop, NULL, "interp_method");
+       RNA_def_property_enum_items(prop, interp_method_item);
+       RNA_def_property_ui_text(prop, "Interpolation", "Interpolation method to use for smoke/fire volumes in solid mode");
+       RNA_def_property_update(prop, NC_OBJECT | ND_DRAW, NULL);
+
        prop = RNA_def_property(srna, "display_velocity", PROP_BOOLEAN, PROP_NONE);
        RNA_def_property_boolean_sdna(prop, NULL, "draw_velocity", 0);
        RNA_def_property_ui_text(prop, "Display Velocity", "Toggle visualization of the velocity field as needles");