* Volume rendering - z transparency
authorMatt Ebb <matt@mke3.net>
Sun, 23 Aug 2009 02:54:30 +0000 (02:54 +0000)
committerMatt Ebb <matt@mke3.net>
Sun, 23 Aug 2009 02:54:30 +0000 (02:54 +0000)
This solves one of the last remaining hurdles for
volume rendering. Previously it always used ray
tracing to shade other objects inside or behind the
volume. This meant that said objects would look
aliased, unless you used Full OSA on the volume
(which is slow!). As well as this, it meant that you didn't
get a good alpha channel out of the volume to use for
compositing, similar to ray refracting materials.

This commit enables z transparency for volume
materials. Although it can be potentially less
physically correct, in most situations there's no
difference, and you get the benefit of nice sampling for
other objects and an alpha channel for compositing too.

release/ui/buttons_material.py
source/blender/makesdna/DNA_material_types.h
source/blender/makesrna/intern/rna_material.c
source/blender/render/intern/source/convertblender.c
source/blender/render/intern/source/shadeinput.c
source/blender/render/intern/source/volumetric.c

index 67ba4d326ba0cffe6e9ed29f9cb79014c3cfa32c..3aec2517bf1dd751506dc34482584b2db7bc1b3d 100644 (file)
@@ -568,6 +568,27 @@ class MATERIAL_PT_volume_scattering(MaterialButtonsPanel):
                col.itemR(vol, "phase_function", text="")
                if vol.phase_function in ('SCHLICK', 'HENYEY-GREENSTEIN'):
                        col.itemR(vol, "asymmetry")
+
+class MATERIAL_PT_volume_transp(MaterialButtonsPanel):
+       __label__= "Transparency"
+       COMPAT_ENGINES = set(['BLENDER_RENDER'])
+               
+       def poll(self, context):
+               mat = context.material
+               return mat and (mat.type == 'VOLUME') and (context.scene.render_data.engine in self.COMPAT_ENGINES)
+
+       def draw_header(self, context):
+               layout = self.layout
+
+       def draw(self, context):
+               layout = self.layout
+               
+               mat = context.material
+               rayt = context.material.raytrace_transparency
+               
+               row= layout.row()
+               row.itemR(mat, "transparency_method", expand=True)
+               row.active = mat.transparency and (not mat.shadeless)
                
 class MATERIAL_PT_volume_integration(MaterialButtonsPanel):
        __label__ = "Integration"
@@ -660,6 +681,7 @@ bpy.types.register(MATERIAL_PT_mirror)
 bpy.types.register(MATERIAL_PT_sss)
 bpy.types.register(MATERIAL_PT_volume_shading)
 bpy.types.register(MATERIAL_PT_volume_scattering)
+bpy.types.register(MATERIAL_PT_volume_transp)
 bpy.types.register(MATERIAL_PT_volume_integration)
 bpy.types.register(MATERIAL_PT_halo)
 bpy.types.register(MATERIAL_PT_physics)
index b4fdb07b52040155dba2ad29a24e8d53e1d7ea1a..f268c11706539fbfeae196e7f4275b9b34bb7321 100644 (file)
@@ -377,7 +377,6 @@ typedef struct Material {
 #define MA_VOL_SHADED          1
 #define MA_VOL_RECVSHADOW      4
 #define MA_VOL_PRECACHESHADING 8
-#define MA_VOL_USEALPHA                16
 
 /* vol_shading_type */
 #define MA_VOL_SHADE_NONE                                      0
index 0c5fc70fabddcda0b937aa45be2b753e3761962c..cde65f46e5ce9b2268e6c233fc8ea94957c0f1fa 100644 (file)
@@ -1017,11 +1017,6 @@ static void rna_def_material_volume(BlenderRNA *brna)
        RNA_def_property_ui_text(prop, "Emission Color", "");
        RNA_def_property_update(prop, NC_MATERIAL|ND_SHADING_DRAW, NULL);
        
-       prop= RNA_def_property(srna, "use_alpha", PROP_BOOLEAN, PROP_NONE);
-       RNA_def_property_boolean_sdna(prop, NULL, "shadeflag", MA_VOL_USEALPHA); /* use bitflags */
-       RNA_def_property_ui_text(prop, "Use Alpha", "Temp method for getting a usable alpha channel");
-       RNA_def_property_update(prop, NC_MATERIAL|ND_SHADING, NULL);
-       
        prop= RNA_def_property(srna, "phase_function", PROP_ENUM, PROP_NONE);
        RNA_def_property_enum_sdna(prop, NULL, "phasefunc_type");
        RNA_def_property_enum_items(prop, prop_phasefunction_items);
index f91e48ea2879ecc84a40676e85f3a39cf160b97f..e486daf25851616b9499268e08bc3a270d03a9c2 100644 (file)
@@ -920,6 +920,7 @@ static Material *give_render_material(Render *re, Object *ob, int nr)
        
        if(re->r.mode & R_SPEED) ma->texco |= NEED_UV;
        
+       if(ma->material_type == MA_TYPE_VOLUME) ma->mode |= MA_TRANSP;
        if((ma->mode & MA_TRANSP) && (ma->mode & MA_ZTRANSP))
                re->flag |= R_ZTRA;
        
@@ -3005,13 +3006,12 @@ static void init_camera_inside_volumes(Render *re)
                }
        }
        
-       {
+       /* debug {
        MatInside *m;
        for (m=re->render_volumes_inside.first; m; m=m->next) {
                printf("matinside: ma: %s \n", m->ma->id.name+2);
        }
-       
-       }
+       }*/
 }
 
 static void add_volume(Render *re, ObjectRen *obr, Material *ma)
@@ -3862,6 +3862,7 @@ static void set_fullsample_flag(Render *re, ObjectRen *obr)
                        vlr->flag |= R_FULL_OSA;
                else if(trace) {
                        if(mode & MA_SHLESS);
+                       else if(vlr->mat->material_type == MA_TYPE_VOLUME);
                        else if((mode & MA_RAYMIRROR) || ((mode & MA_TRANSP) && (mode & MA_RAYTRANSP)))
                                /* for blurry reflect/refract, better to take more samples 
                                 * inside the raytrace than as OSA samples */
index 1a97440cec304febdde89fe826d97ca22a301a2a..7887392f1d2d9439ddf4e28c476017cb2c0c2ec7 100644 (file)
@@ -216,15 +216,17 @@ void shade_input_do_shade(ShadeInput *shi, ShadeResult *shr)
        }
        else alpha= 1.0f;
        
+       
        /* add mist and premul color */
        if(shr->alpha!=1.0f || alpha!=1.0f) {
                float fac= alpha*(shr->alpha);
                shr->combined[3]= fac;
-               shr->combined[0]*= fac;
-               shr->combined[1]*= fac;
-               shr->combined[2]*= fac;
+               
+               if (shi->mat->material_type!= MA_TYPE_VOLUME)
+                       VecMulf(shr->combined, fac);
        }
-       else shr->combined[3]= 1.0f;
+       else
+               shr->combined[3]= 1.0f;
        
        /* add z */
        shr->z= -shi->co[2];
index 7943d9ae526f059988012e9d9311860ce08ab667..cf66d580944db8bd8416275d8913146eaa66ecff 100644 (file)
@@ -65,12 +65,20 @@ extern struct Render R;
 /* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
 
 
-/* TODO: Box or sphere intersection types could speed things up */
+/* tracing */
+
 static int vol_get_bounds(ShadeInput *shi, float *co, float *vec, float *hitco, Isect *isect, int intersect_type)
 {
        float maxsize = RE_ray_tree_max_size(R.raytree);
-
-       /* TODO: use object's bounding box to calculate max size */
+       
+       /* XXX TODO - get raytrace max distance from object instance's bounding box */
+       /* need to account for scaling only, but keep coords in camera space...
+        * below code is WIP and doesn't work!
+       VecSubf(bb_dim, shi->obi->obr->boundbox[1], shi->obi->obr->boundbox[2]);
+       Mat3MulVecfl(shi->obi->nmat, bb_dim);
+       maxsize = VecLength(bb_dim);
+       */
+       
        VECCOPY(isect->start, co);
        isect->end[0] = co[0] + vec[0] * maxsize;
        isect->end[1] = co[1] + vec[1] * maxsize;
@@ -96,6 +104,68 @@ static int vol_get_bounds(ShadeInput *shi, float *co, float *vec, float *hitco,
        }
 }
 
+static void shade_intersection(ShadeInput *shi, float *col, Isect *is)
+{
+       ShadeInput shi_new;
+       ShadeResult shr_new;
+       
+       memset(&shi_new, 0, sizeof(ShadeInput)); 
+       
+       shi_new.mask= shi->mask;
+       shi_new.osatex= shi->osatex;
+       shi_new.thread= shi->thread;
+       shi_new.depth = shi->depth + 1;
+       shi_new.volume_depth= shi->volume_depth + 1;
+       shi_new.xs= shi->xs;
+       shi_new.ys= shi->ys;
+       shi_new.lay= shi->lay;
+       shi_new.passflag= SCE_PASS_COMBINED; /* result of tracing needs no pass info */
+       shi_new.combinedflag= 0xFFFFFF;          /* ray trace does all options */
+       shi_new.light_override= shi->light_override;
+       shi_new.mat_override= shi->mat_override;
+       
+       VECCOPY(shi_new.camera_co, is->start);
+       
+       memset(&shr_new, 0, sizeof(ShadeResult));
+       
+       /* hardcoded limit of 100 for now - prevents problems in weird geometry */
+       if (shi->volume_depth < 100) {
+               shade_ray(is, &shi_new, &shr_new);
+       }
+       
+       VecCopyf(col, shr_new.combined);
+       col[3] = shr_new.alpha;
+}
+
+static void vol_trace_behind(ShadeInput *shi, VlakRen *vlr, float *co, float *col)
+{
+       Isect isect;
+       float maxsize = RE_ray_tree_max_size(R.raytree);
+       
+       VECCOPY(isect.start, co);
+       isect.end[0] = isect.start[0] + shi->view[0] * maxsize;
+       isect.end[1] = isect.start[1] + shi->view[1] * maxsize;
+       isect.end[2] = isect.start[2] + shi->view[2] * maxsize;
+       
+       isect.faceorig= (RayFace *)vlr;
+       
+       isect.mode= RE_RAY_MIRROR;
+       isect.oborig= RAY_OBJECT_SET(&R, shi->obi);
+       isect.face_last= NULL;
+       isect.ob_last= 0;
+       isect.lay= -1;
+       
+       /* check to see if there's anything behind the volume, otherwise shade the sky */
+       if(RE_ray_tree_intersect(R.raytree, &isect)) {
+               shade_intersection(shi, col, &isect);
+       } else {
+               shadeSkyView(col, co, shi->view, NULL, shi->thread);
+               shadeSunView(col, shi->view);
+       }
+}
+
+/* input shader data */
+
 float vol_get_stepsize(struct ShadeInput *shi, int context)
 {
        if (shi->mat->vol.stepsize_type == MA_VOL_STEP_RANDOMIZED) {
@@ -280,7 +350,7 @@ void vol_get_attenuation(ShadeInput *shi, float *transmission, float *co, float
                tau[2] += stepsize * density;
                
                if (s < nsteps-1) {
-                       VECCOPY(step_sta, step_end);
+                       VecCopyf(step_sta, step_end);
                        VecAddf(step_end, step_end, step_vec);
                }
        }
@@ -319,9 +389,6 @@ void vol_shade_one_lamp(struct ShadeInput *shi, float *co, LampRen *lar, float *
                VECCOPY(lv, lar->vec);
        VecMulf(lv, -1.0f);
        
-       p = vol_get_phasefunc(shi, shi->mat->vol.phasefunc_type, shi->mat->vol.phasefunc_g, shi->view, lv);
-       VecMulf(lacol, p);
-       
        if (shi->mat->vol.shade_type != MA_VOL_SHADE_NONE) {
                Isect is;
                
@@ -337,6 +404,7 @@ void vol_shade_one_lamp(struct ShadeInput *shi, float *co, LampRen *lar, float *
                        }
 
                        if (ELEM(lar->type, LA_SUN, LA_HEMI))
+                               /* infinite lights, can never be inside volume */
                                atten_co = hitco;
                        else if ( lampdist < dist ) {
                                atten_co = lar->co;
@@ -354,6 +422,9 @@ void vol_shade_one_lamp(struct ShadeInput *shi, float *co, LampRen *lar, float *
                }
        }
        
+       p = vol_get_phasefunc(shi, shi->mat->vol.phasefunc_type, shi->mat->vol.phasefunc_g, shi->view, lv);
+       VecMulf(lacol, p);
+       
        scatter_fac = vol_get_scattering_fac(shi, co);
        VecMulf(lacol, scatter_fac);
 }
@@ -390,14 +461,6 @@ outgoing radiance from behind surface * beam transmittance/attenuation
 + added radiance from all points along the ray due to participating media
        --> radiance for each segment = 
                (radiance added by scattering + radiance added by emission) * beam transmittance/attenuation
-
--- To find transmittance:
-       compute optical thickness with tau (perhaps involving monte carlo integration)
-       transmittance = exp(-tau)
-       
--- To find radiance from segments along the way:
-       find radiance for one step: 
-       - loop over lights and weight by phase function
 */
 static void volumeintegrate(struct ShadeInput *shi, float *col, float *co, float *endco)
 {
@@ -462,150 +525,102 @@ static void volumeintegrate(struct ShadeInput *shi, float *col, float *co, float
        VecMulVecf(col, tr, col);       
        VecAddf(col, col, radiance);
        
-       /* alpha - transmission */
-       col[3] = 1.0f -(tr[0] + tr[1] + tr[2]) * 0.333f;
-}
-
-static void shade_intersection(ShadeInput *shi, float *col, Isect *is)
-{
-       ShadeInput shi_new;
-       ShadeResult shr_new;
-       
-       memset(&shi_new, 0, sizeof(ShadeInput)); 
-       
-       shi_new.mask= shi->mask;
-       shi_new.osatex= shi->osatex;
-       shi_new.thread= shi->thread;
-       shi_new.depth = shi->depth + 1;
-       shi_new.volume_depth= shi->volume_depth + 1;
-       shi_new.xs= shi->xs;
-       shi_new.ys= shi->ys;
-       shi_new.lay= shi->lay;
-       shi_new.passflag= SCE_PASS_COMBINED; /* result of tracing needs no pass info */
-       shi_new.combinedflag= 0xFFFFFF;          /* ray trace does all options */
-       shi_new.light_override= shi->light_override;
-       shi_new.mat_override= shi->mat_override;
-       
-       VECCOPY(shi_new.camera_co, is->start);
-       
-       memset(&shr_new, 0, sizeof(ShadeResult));
-
-       /* hardcoded limit of 100 for now - prevents problems in weird geometry */
-       if (shi->volume_depth < 100) {
-               shade_ray(is, &shi_new, &shr_new);
-       }
-       
-       col[0] = shr_new.combined[0];
-       col[1] = shr_new.combined[1];
-       col[2] = shr_new.combined[2];
-       col[3] = shr_new.alpha;
-}
-
-static void vol_trace_behind(ShadeInput *shi, VlakRen *vlr, float *co, float *col)
-{
-       Isect isect;
-       float maxsize = RE_ray_tree_max_size(R.raytree);
-
-       VECCOPY(isect.start, co);
-       isect.end[0] = isect.start[0] + shi->view[0] * maxsize;
-       isect.end[1] = isect.start[1] + shi->view[1] * maxsize;
-       isect.end[2] = isect.start[2] + shi->view[2] * maxsize;
-
-       isect.faceorig= (RayFace *)vlr;
-       
-       isect.mode= RE_RAY_MIRROR;
-       isect.oborig= RAY_OBJECT_SET(&R, shi->obi);
-       isect.face_last= NULL;
-       isect.ob_last= 0;
-       isect.lay= -1;
-       
-       /* check to see if there's anything behind the volume, otherwise shade the sky */
-       if(RE_ray_tree_intersect(R.raytree, &isect)) {
-               shade_intersection(shi, col, &isect);
-       } else {
-               shadeSkyView(col, co, shi->view, NULL, shi->thread);
-               shadeSunView(col, shi->view);
-       }
+       /* alpha <-- transmission luminance */
+       col[3] = 1.0f -(0.2126*tr[0] + 0.7152*tr[1] + 0.0722*tr[2]);
 }
 
 /* the main entry point for volume shading */
 static void volume_trace(struct ShadeInput *shi, struct ShadeResult *shr, int inside_volume)
 {
        float hitco[3], col[4] = {0.f,0.f,0.f,0.f};
+       float *startco, *endco;
        int trace_behind = 1;
+       const int ztransp= ((shi->depth==0) && (shi->mat->mode & MA_TRANSP) && (shi->mat->mode & MA_ZTRANSP));
        Isect is;
 
        /* check for shading an internal face a volume object directly */
-       if (inside_volume == VOL_SHADE_INSIDE) {
+       if (inside_volume == VOL_SHADE_INSIDE)
                trace_behind = 0;
-       }
-       if (inside_volume == VOL_SHADE_OUTSIDE) {
+       else if (inside_volume == VOL_SHADE_OUTSIDE) {
                if (shi->flippednor)
                        inside_volume = VOL_SHADE_INSIDE;
        }
-
-       if (inside_volume == VOL_SHADE_INSIDE) {
-               
-               if (trace_behind) {
-                       /* trace behind the volume object */
-                       vol_trace_behind(shi, shi->vlr, shi->co, col);
-               } else {
-                       /* we're tracing through the volume between the camera 
-                        * and a solid surface, so use that pre-shaded radiance */
-                       QUATCOPY(col, shr->combined);
-               }
+       
+       if (ztransp && inside_volume == VOL_SHADE_INSIDE) {
+               MatInside *mi;
+               int render_this=0;
                
-               /* shade volume from 'camera' to 1st hit point */
-               volumeintegrate(shi, col, shi->camera_co, shi->co);
+               /* don't render the backfaces of ztransp volume materials.
+                
+                * volume shading renders the internal volume from between the
+                * near view intersection of the solid volume to the
+                * intersection on the other side, as part of the shading of
+                * the front face.
+                
+                * Because ztransp renders both front and back faces independently
+                * this will double up, so here we prevent rendering the backface as well, 
+                * which would otherwise render the volume in between the camera and the backface
+                * --matt */
                
-               VecCopyf(shr->combined, col);
+               for (mi=R.render_volumes_inside.first; mi; mi=mi->next) {
+                       /* weak... */
+                       if (mi->ma == shi->mat) render_this=1;
+               }
+               if (!render_this) return;
+       }
+       
+
+       if (inside_volume == VOL_SHADE_INSIDE)
+       {
+               startco = shi->camera_co;
+               endco = shi->co;
                
-               if (shi->mat->vol.shadeflag & MA_VOL_USEALPHA) {
-                       if (col[3] > 1.0f)
-                               col[3] = 1.0f;
+               if (!ztransp) {
+                       if (trace_behind) {
+                               /* trace behind the volume object */
+                               vol_trace_behind(shi, shi->vlr, endco, col);
+                       } else {
+                               /* we're tracing through the volume between the camera 
+                                * and a solid surface, so use that pre-shaded radiance */
+                               QUATCOPY(col, shr->combined);
+                       }
                }
-               else
-                       col[3] = 1.0f;
-               shr->combined[3] = col[3];
-               shr->alpha = col[3];
                
-               VECCOPY(shr->diff, shr->combined);
+               /* shade volume from 'camera' to 1st hit point */
+               volumeintegrate(shi, col, startco, endco);
        }
        /* trace to find a backface, the other side bounds of the volume */
        /* (ray intersect ignores front faces here) */
-       else if (vol_get_bounds(shi, shi->co, shi->view, hitco, &is, VOL_BOUNDS_DEPTH)) {
+       else if (vol_get_bounds(shi, shi->co, shi->view, hitco, &is, VOL_BOUNDS_DEPTH))
+       {
                VlakRen *vlr = (VlakRen *)is.face;
                
-               /* if it's another face in the same material */
-               if (vlr->mat == shi->mat) {
-                       /* trace behind the 2nd (raytrace) hit point */
-                       vol_trace_behind(shi, (VlakRen *)is.face, hitco, col);
-               } else {
-                       shade_intersection(shi, col, &is);
-               }
-       
-               /* shade volume from 1st hit point to 2nd hit point */
-               volumeintegrate(shi, col, shi->co, hitco);
-               
-               VecCopyf(shr->combined, col);
+               startco = shi->co;
+               endco = hitco;
                
-               if (shi->mat->vol.shadeflag & MA_VOL_USEALPHA) {
-                       if (col[3] > 1.0f)
-                               col[3] = 1.0f;
+               if (!ztransp) {
+                       /* if it's another face in the same material */
+                       if (vlr->mat == shi->mat) {
+                               /* trace behind the 2nd (raytrace) hit point */
+                               vol_trace_behind(shi, (VlakRen *)is.face, endco, col);
+                       } else {
+                               shade_intersection(shi, col, &is);
+                       }
                }
-               else
-                       col[3] = 1.0f;
-               shr->combined[3] = col[3];
-               shr->alpha = col[3];
                
-               VECCOPY(shr->diff, shr->combined);
-       }
-       else {
-               shr->combined[0] = 0.0f;
-               shr->combined[1] = 0.0f;
-               shr->combined[2] = 0.0f;
-               shr->combined[3] = shr->alpha =  1.0f;
+               /* shade volume from 1st hit point to 2nd hit point */
+               volumeintegrate(shi, col, startco, endco);
        }
+       
+       if (ztransp)
+               col[3] = col[3]>1.f?1.f:col[3];
+       else
+               col[3] = 1.f;
+       
+       VecCopyf(shr->combined, col);
+       shr->alpha = col[3];
+       
+       VECCOPY(shr->diff, shr->combined);
 }
 
 /* Traces a shadow through the object, 
@@ -614,41 +629,37 @@ void shade_volume_shadow(struct ShadeInput *shi, struct ShadeResult *shr, struct
 {
        float hitco[3];
        float tr[3] = {1.0,1.0,1.0};
-       float tau[3] = {0.0,0.0,0.0};
        Isect is;
        float shade_stepsize = vol_get_stepsize(shi, STEPSIZE_SHADE);
+       float *startco, *endco;
+       float density=0.f;
 
        memset(shr, 0, sizeof(ShadeResult));
        
        /* if 1st hit normal is facing away from the camera, 
         * then we're inside the volume already. */
        if (shi->flippednor) {
-       
-               vol_get_attenuation(shi, tr, last_is->start, shi->co, -1.0f, shade_stepsize);
-               
-               VecCopyf(shr->combined, tr);
-               
-               shr->combined[3] = 1.0f -(tr[0] + tr[1] + tr[2]) * 0.333f;
-               shr->alpha = shr->combined[3];
+               startco = last_is->start;
+               endco = shi->co;
        }
        /* trace to find a backface, the other side bounds of the volume */
        /* (ray intersect ignores front faces here) */
        else if (vol_get_bounds(shi, shi->co, shi->view, hitco, &is, VOL_BOUNDS_DEPTH)) {
-               
-               vol_get_attenuation(shi, tr, shi->co, hitco, -1.0f, shade_stepsize);
-               
-               VecCopyf(shr->combined, tr);
-               
-               shr->combined[3] = 1.0f -(tr[0] + tr[1] + tr[2]) * 0.333f;
-               shr->alpha = shr->combined[3];
-
+               startco = shi->co;
+               endco = hitco;
        }
        else {
-               shr->combined[0] = 0.0f;
-               shr->combined[1] = 0.0f;
-               shr->combined[2] = 0.0f;
-               shr->combined[3] = shr->alpha =  0.0f;
+               shr->combined[0] = shr->combined[1] = shr->combined[2] = 0.f;
+               shr->alpha = shr->combined[3] = 1.f;
+               return;
        }
+       
+       density = vol_get_density(shi, startco);
+       vol_get_attenuation(shi, tr, startco, endco, density, shade_stepsize);
+       
+       VecCopyf(shr->combined, tr);
+       shr->combined[3] = 1.0f -(0.2126*tr[0] + 0.7152*tr[1] + 0.0722*tr[2]);
+       shr->alpha = shr->combined[3];
 }
 
 
@@ -656,7 +667,6 @@ void shade_volume_shadow(struct ShadeInput *shi, struct ShadeResult *shr, struct
 void shade_volume_outside(ShadeInput *shi, ShadeResult *shr)
 {
        memset(shr, 0, sizeof(ShadeResult));
-       
        volume_trace(shi, shr, VOL_SHADE_OUTSIDE);
 }