* Volume rendering
authorMatt Ebb <matt@mke3.net>
Mon, 22 Sep 2008 01:51:24 +0000 (01:51 +0000)
committerMatt Ebb <matt@mke3.net>
Mon, 22 Sep 2008 01:51:24 +0000 (01:51 +0000)
This is an initial commit to get it in SVN and make it easier to work on.

Don't expect it to work perfectly, it's still in development and there's
plenty of work still needing to be done. And so no I'm not very interested
in hearing bug reports or feature requests at this stage :)

There's some info on this, and a todo list at:
http://mke3.net/weblog/volume-rendering/

Right now I'm trying to focus on getting shading working correctly (there's
currently a problem in which 'surfaces' of the volume facing towards or away
from light sources are getting shaded differently to how they should be),
then I'll work on integration issues, like taking materials behind the volume
into account, blending with alpha, etc. You can do simple testing though,
mapping textures to density or emission on a cube with volume material.

15 files changed:
source/blender/blenkernel/intern/material.c
source/blender/blenlib/BLI_arithb.h
source/blender/blenlib/intern/arithb.c
source/blender/blenloader/intern/readfile.c
source/blender/makesdna/DNA_material_types.h
source/blender/render/intern/include/shading.h
source/blender/render/intern/include/texture.h
source/blender/render/intern/include/volumetric.h [new file with mode: 0644]
source/blender/render/intern/source/convertblender.c
source/blender/render/intern/source/shadeinput.c
source/blender/render/intern/source/shadeoutput.c
source/blender/render/intern/source/texture.c
source/blender/render/intern/source/volumetric.c [new file with mode: 0644]
source/blender/src/buttons_shading.c
source/blender/src/previewrender.c

index f05b84f6e90ddc8727c9af9467fbbd015feb17a5..5c20858ec63763c4854fa3dc449842943d9a56f2 100644 (file)
@@ -166,6 +166,11 @@ void init_material(Material *ma)
        ma->sss_texfac= 0.0f;
        ma->sss_front= 1.0f;
        ma->sss_back= 1.0f;
+       
+       ma->vol_stepsize = 0.2f;
+       ma->vol_shade_stepsize = 0.2f;
+       ma->vol_absorption = 1.0f;
+       ma->vol_scattering = 1.0f;
 
        ma->mode= MA_TRACEBLE|MA_SHADBUF|MA_SHADOW|MA_RADIO|MA_RAYBIAS|MA_TANGENT_STR;
 
index 6e54fae58d0eace41f87af0e4fea48dbbc3a84b8..0a9abc3460810e142b7c6e1a805a63bb2ea67d02 100644 (file)
@@ -250,6 +250,7 @@ void printvec4f(char *str, float v[4]);
 
 void VecAddf(float *v, float *v1, float *v2);
 void VecSubf(float *v, float *v1, float *v2);
+void VecMulVecf(float *v, float *v1, float *v2);
 void VecLerpf(float *target, float *a, float *b, float t);
 void VecMidf(float *v, float *v1, float *v2);
 
index f89f90f70455f5dccbfd05e56bb825871524f63c..f311c30e0c70dd8712933226369a3ff692e5909a 100644 (file)
@@ -2126,6 +2126,13 @@ void VecSubf(float *v, float *v1, float *v2)
        v[2]= v1[2]- v2[2];
 }
 
+void VecMulVecf(float *v, float *v1, float *v2)
+{
+       v[0] = v1[0] * v2[0];
+       v[1] = v1[1] * v2[1];
+       v[2] = v1[2] * v2[2];
+}
+
 void VecLerpf(float *target, float *a, float *b, float t)
 {
        float s = 1.0f-t;
@@ -3269,6 +3276,12 @@ float Normalize2(float *n)
        return d;
 }
 
+float rgb_to_luminance(float r, float g, float b)
+{
+       /* Rec. 709 HDTV */
+       return (0.2126*r + 0.7152*g + 0.0722*b);
+}
+
 void hsv_to_rgb(float h, float s, float v, float *r, float *g, float *b)
 {
        int i;
index f3f1a99cdbff5dae200b7c77073557200dc8d9ae..a1d50b618d12816d64f1dd9bc0f1c1775c6d3bfd 100644 (file)
@@ -7681,6 +7681,7 @@ static void do_versions(FileData *fd, Library *lib, Main *main)
        if ((main->versionfile < 245) || (main->versionfile == 245 && main->subversionfile < 11)) {
                Object *ob;
                bActionStrip *strip;
+
                
                /* nla-strips - scale */                
                for (ob= main->object.first; ob; ob= ob->id.next) {
@@ -7704,12 +7705,14 @@ static void do_versions(FileData *fd, Library *lib, Main *main)
                                ob->soft->shearstiff = 1.0f; 
                        }
                }
+               
        }
 
        if ((main->versionfile < 245) || (main->versionfile == 245 && main->subversionfile < 14)) {
                Scene *sce= main->scene.first;
                Sequence *seq;
                Editing *ed;
+               Material *ma;
                
                while(sce) {
                        ed= sce->ed;
@@ -7724,6 +7727,13 @@ static void do_versions(FileData *fd, Library *lib, Main *main)
                        
                        sce= sce->id.next;
                }
+               
+               for(ma=main->mat.first; ma; ma= ma->id.next) {
+                       if (ma->vol_shade_stepsize < 0.001f) ma->vol_shade_stepsize = 0.2f;
+                       if (ma->vol_stepsize < 0.001f) ma->vol_stepsize = 0.2f;
+                       if (ma->vol_absorption < 0.001f) ma->vol_absorption = 1.0f;
+                       if (ma->vol_scattering < 0.001f) ma->vol_scattering = 1.0f;
+               }
        }
        
        /*fix broken group lengths in id properties*/
index c92a33bbcbbdd04b935675147c731d2ee1d33d9d..940f57c1ebcca5bfe6ba9f6c666ee97c3db9a11e 100644 (file)
@@ -62,6 +62,16 @@ typedef struct Material {
        float translucency;
        /* end synced with render_types.h */
        
+       short material_type; /* solid, halo, volumetric */
+       short pad5[3];
+       
+       /* volumetrics */
+       float vol_alphathresh;
+       float vol_stepsize, vol_shade_stepsize;
+       float vol_absorption, vol_scattering;
+       short vol_shadeflag;
+       short vpad;
+       
        float fresnel_mir, fresnel_mir_i;
        float fresnel_tra, fresnel_tra_i;
        float filter;           /* filter added, for raytrace transparency and transmissivity */
@@ -154,6 +164,12 @@ typedef struct Material {
                /* for render */
 #define MA_IS_USED             1
 
+/* material_type */
+#define MA_SOLID               0
+#define MA_PTHALO              1
+#define MA_VOLUME              2
+#define MA_VOLUMESOLID 3
+
 /* mode (is int) */
 #define MA_TRACEBLE            1
 #define MA_SHADOW              2
@@ -327,5 +343,10 @@ typedef struct Material {
 /* sss_flag */
 #define MA_DIFF_SSS            1
 
+/* vol_shadeflag */
+#define MA_VOL_SHADED          1
+#define MA_VOL_ATTENUATED      2
+#define MA_VOL_SHADOWED                4
+
 #endif
 
index 6f1cb8dd7a923cab9932a15e00feed0d26a44afb..9229e612337c5d8f85c6bd222523538c4cc5c750 100644 (file)
@@ -52,6 +52,7 @@ typedef struct ShadeSample {
 
        /* also the node shader callback */
 void shade_material_loop(struct ShadeInput *shi, struct ShadeResult *shr);
+void shade_volume_loop(struct ShadeInput *shi, struct ShadeResult *shr);
 
 void shade_input_set_triangle_i(struct ShadeInput *shi, struct ObjectInstanceRen *obi, struct VlakRen *vlr, short i1, short i2, short i3);
 void shade_input_set_triangle(struct ShadeInput *shi, volatile int obi, volatile int facenr, int normal_flip);
@@ -85,6 +86,7 @@ void shade_color(struct ShadeInput *shi, ShadeResult *shr);
 void ambient_occlusion_to_diffuse(struct ShadeInput *shi, float *diff);
 void ambient_occlusion(struct ShadeInput *shi);
 
+ListBase *get_lights(struct ShadeInput *shi);
 float lamp_get_visibility(struct LampRen *lar, float *co, float *lv, float *dist);
 void lamp_get_shadow(struct LampRen *lar, ShadeInput *shi, float inp, float *shadfac, int do_real);
 
index 8e56c4a852fe54142c323902cc7623d23d03ba6c..08c4ee5df98140b067cb9c523ba29c547e3e577a 100644 (file)
@@ -56,6 +56,7 @@ void do_halo_tex(struct HaloRen *har, float xn, float yn, float *colf);
 void do_sky_tex(float *rco, float *lo, float *dxyview, float *hor, float *zen, float *blend, int skyflag);
 void do_material_tex(struct ShadeInput *shi);
 void do_lamp_tex(LampRen *la, float *lavec, struct ShadeInput *shi, float *colf);
+void do_volume_tex(ShadeInput *shi, float *xyz, float *col, float *alpha, float *emit);
 
 void init_render_textures(Render *re);
 
diff --git a/source/blender/render/intern/include/volumetric.h b/source/blender/render/intern/include/volumetric.h
new file mode 100644 (file)
index 0000000..8db3fa6
--- /dev/null
@@ -0,0 +1,29 @@
+/**
+ *
+ * ***** BEGIN GPL LICENSE BLOCK *****
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+ *
+ * The Original Code is Copyright (C) 2001-2002 by NaN Holding BV.
+ * All rights reserved.
+ *
+ * The Original Code is: all of this file.
+ *
+ * Contributor(s): Farsthary (Raul FHernandez), Matt Ebb.
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ */
+
+void volume_trace(struct ShadeInput *shi, struct ShadeResult *shr);
\ No newline at end of file
index 322d2066a6cc5fea220af68447692a16e703aa0a..48855246e87127b592b4ba1114a3f9cfe9a1991e 100644 (file)
@@ -1029,6 +1029,8 @@ static Material *give_render_material(Render *re, Object *ob, int nr)
        if(ma->nodetree && ma->use_nodes)
                flag_render_node_material(re, ma->nodetree);
        
+       if (ma->material_type == MA_VOLUME) re->r.mode |= R_RAYTRACE;
+       
        return ma;
 }
 
index 7397d623264b7d06a2c338412179d1225f37bfab..cd5ecc413dff8b21845b946bb7bf1470b3ed4251 100644 (file)
@@ -135,6 +135,12 @@ void shade_material_loop(ShadeInput *shi, ShadeResult *shr)
        }       
 }
 
+/* delivers a fully filled in ShadeResult, for all passes */
+void shade_volume_loop(ShadeInput *shi, ShadeResult *shr)
+{
+       if(R.r.mode & R_RAYTRACE) volume_trace(shi, shr);
+}
+
 
 /* do a shade, finish up some passes, apply mist */
 void shade_input_do_shade(ShadeInput *shi, ShadeResult *shr)
@@ -151,7 +157,8 @@ void shade_input_do_shade(ShadeInput *shi, ShadeResult *shr)
                memcpy(&shi->r, &shi->mat->r, 23*sizeof(float));
                shi->har= shi->mat->har;
                
-               shade_material_loop(shi, shr);
+               if (shi->mat->material_type == MA_SOLID) shade_material_loop(shi, shr);
+               else if (shi->mat->material_type == MA_VOLUME) shade_volume_loop(shi, shr);
        }
        
        /* copy additional passes */
index 5a80173d1f1118d2fa19adc88d2e591d2d7df6a8..4f7c26614e9501a14c407c97ac761845dbb0e075 100644 (file)
@@ -58,7 +58,7 @@
 extern struct Render R;
 /* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
 
-static ListBase *get_lights(ShadeInput *shi)
+ListBase *get_lights(ShadeInput *shi)
 {
        
        if(shi->light_override)
index 27628d914653024ac64a3c6d43e830bf524a13dd..37778fafeb3558dedc01231ad71352dca44b4cc5 100644 (file)
@@ -1451,6 +1451,141 @@ float texture_value_blend(float tex, float out, float fact, float facg, int blen
        return in;
 }
 
+void do_volume_tex(ShadeInput *shi, float *xyz, float *col, float *alpha, float *emit)
+{
+       MTex *mtex;
+       Tex *tex;
+       TexResult texres= {0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0, NULL};
+       int tex_nr, rgbnor= 0;
+       float co[3], texvec[3];
+       float fact, stencilTin=1.0;
+       
+       if (R.r.scemode & R_NO_TEX) return;
+       /* here: test flag if there's a tex (todo) */
+       
+       for(tex_nr=0; tex_nr<MAX_MTEX; tex_nr++) {
+               /* separate tex switching */
+               if(shi->mat->septex & (1<<tex_nr)) continue;
+               
+               if(shi->mat->mtex[tex_nr]) {
+                       mtex= shi->mat->mtex[tex_nr];
+                       tex= mtex->tex;
+                       if(tex==0) continue;
+                       
+                       /* which coords */
+                       if(mtex->texco==TEXCO_OBJECT) { 
+                               Object *ob= mtex->object;
+                               ob= mtex->object;
+                               if(ob) {                                                
+                                       VECCOPY(co, xyz);       
+                                       if(mtex->texflag & MTEX_OB_DUPLI_ORIG) {
+                                               if(shi->obi && shi->obi->duplitexmat)
+                                                       MTC_Mat4MulVecfl(shi->obi->duplitexmat, co);                                    
+                                       } 
+                                       MTC_Mat4MulVecfl(ob->imat, co);
+                               }
+                       }
+                       else if(mtex->texco==TEXCO_GLOB) {                                                      
+                          VECCOPY(co, xyz);            
+                       }
+                       else continue;  // can happen when texco defines disappear and it renders old files
+
+                       texres.nor= NULL;
+                       
+                       if(tex->type==TEX_IMAGE) {
+                               continue;       /* not supported yet */
+                       }
+                       else {
+                               /* placement */
+                               if(mtex->projx) texvec[0]= mtex->size[0]*(co[mtex->projx-1]+mtex->ofs[0]);
+                               else texvec[0]= mtex->size[0]*(mtex->ofs[0]);
+
+                               if(mtex->projy) texvec[1]= mtex->size[1]*(co[mtex->projy-1]+mtex->ofs[1]);
+                               else texvec[1]= mtex->size[1]*(mtex->ofs[1]);
+
+                               if(mtex->projz) texvec[2]= mtex->size[2]*(co[mtex->projz-1]+mtex->ofs[2]);
+                               else texvec[2]= mtex->size[2]*(mtex->ofs[2]);
+                       }
+                       
+
+                       rgbnor= multitex(tex, co, NULL, NULL, 0, &texres);      /* NULL = dxt/dyt, 0 = shi->osatex - not supported */
+                       
+                       /* texture output */
+
+                       if( (rgbnor & TEX_RGB) && (mtex->texflag & MTEX_RGBTOINT)) {
+                               texres.tin= (0.35*texres.tr+0.45*texres.tg+0.2*texres.tb);
+                               rgbnor-= TEX_RGB;
+                       }
+                       if(mtex->texflag & MTEX_NEGATIVE) {
+                               if(rgbnor & TEX_RGB) {
+                                       texres.tr= 1.0-texres.tr;
+                                       texres.tg= 1.0-texres.tg;
+                                       texres.tb= 1.0-texres.tb;
+                               }
+                               texres.tin= 1.0-texres.tin;
+                       }
+                       if(mtex->texflag & MTEX_STENCIL) {
+                               if(rgbnor & TEX_RGB) {
+                                       fact= texres.ta;
+                                       texres.ta*= stencilTin;
+                                       stencilTin*= fact;
+                               }
+                               else {
+                                       fact= texres.tin;
+                                       texres.tin*= stencilTin;
+                                       stencilTin*= fact;
+                               }
+                       }
+                       
+                       
+                       if(mtex->mapto & (MAP_COL)) {
+                               float tcol[3], colfac;
+                               
+                               /* stencil maps on the texture control slider, not texture intensity value */
+                               colfac= mtex->colfac*stencilTin;
+                               
+                               tcol[0]=texres.tr; tcol[1]=texres.tg; tcol[2]=texres.tb;
+                               
+                               if((rgbnor & TEX_RGB)==0) {
+                                       tcol[0]= mtex->r;
+                                       tcol[1]= mtex->g;
+                                       tcol[2]= mtex->b;
+                               }
+                               else if(mtex->mapto & MAP_ALPHA) {
+                                       texres.tin= stencilTin;
+                               }
+                               else texres.tin= texres.ta;
+                               
+                               if(mtex->mapto & MAP_COL) {
+                                       texture_rgb_blend(col, tcol, col, texres.tin, colfac, mtex->blendtype);
+                               }
+                       }
+                       
+                       if(mtex->mapto & MAP_VARS) {
+                               /* stencil maps on the texture control slider, not texture intensity value */
+                               float varfac= mtex->varfac*stencilTin;
+                               
+                               if(rgbnor & TEX_RGB) {
+                                       if(texres.talpha) texres.tin= texres.ta;
+                                       else texres.tin= (0.35*texres.tr+0.45*texres.tg+0.2*texres.tb);
+                               }
+                               
+                               if(mtex->mapto & MAP_EMIT) {
+                                       int flip= mtex->maptoneg & MAP_EMIT;
+
+                                       *emit = texture_value_blend(mtex->def_var, *emit, texres.tin, varfac, mtex->blendtype, flip);
+                                       if(*emit<0.0) *emit= 0.0;
+                               }
+                               if(mtex->mapto & MAP_ALPHA) {
+                                       int flip= mtex->maptoneg & MAP_ALPHA;
+
+                                       *alpha = texture_value_blend(mtex->def_var, *alpha, texres.tin, varfac, mtex->blendtype, flip);
+                                       CLAMP(*alpha, 0.0, 1.0);
+                               }
+                       }
+               }
+       }
+}
 
 void do_material_tex(ShadeInput *shi)
 {
diff --git a/source/blender/render/intern/source/volumetric.c b/source/blender/render/intern/source/volumetric.c
new file mode 100644 (file)
index 0000000..3ca8c0a
--- /dev/null
@@ -0,0 +1,400 @@
+/**
+ *
+ * ***** BEGIN GPL LICENSE BLOCK *****
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+ *
+ * The Original Code is Copyright (C) 2001-2002 by NaN Holding BV.
+ * All rights reserved.
+ *
+ * The Original Code is: all of this file.
+ *
+ * Contributor(s): Farsthary (Raul FHernandez), Matt Ebb.
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ */
+
+#include <math.h>
+#include <string.h>
+#include <stdlib.h>
+#include <float.h>
+
+#include "BLI_blenlib.h"
+#include "BLI_arithb.h"
+#include "BLI_rand.h"
+
+#include "RE_shader_ext.h"
+#include "RE_raytrace.h"
+
+#include "DNA_material_types.h"
+#include "DNA_group_types.h"
+#include "DNA_lamp_types.h"
+
+#include "render_types.h"
+#include "shading.h"
+
+/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
+/* defined in pipeline.c, is hardcopy of active dynamic allocated Render */
+/* only to be used here in this file, it's for speed */
+extern struct Render R;
+/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
+
+static int vol_backface_intersect_check(Isect *is, int ob, RayFace *face)
+{
+       VlakRen *vlr = (VlakRen *)face;
+       
+       /* only consider faces away, so overlapping layers
+        * of foward facing geometry don't cause the ray to stop */
+       return (INPR(is->vec, vlr->n) < 0.0f);
+}
+
+#define VOL_IS_SAMEOBJECT              1
+#define VOL_IS_SAMEMATERIAL            2
+
+int vol_get_bounds(ShadeInput *shi, float *co, float *vec, float *hitco)
+{
+       /* TODO: Box or sphere intersection types could speed things up */
+
+       /* raytrace method */
+       Isect isect;
+       float maxsize = RE_ray_tree_max_size(R.raytree);
+
+       /* TODO: use object's bounding box to calculate max size */
+       VECCOPY(isect.start, co);
+       isect.end[0] = co[0] + vec[0] * maxsize;
+       isect.end[1] = co[1] + vec[1] * maxsize;
+       isect.end[2] = co[2] + vec[2] * maxsize;
+       
+       isect.mode= RE_RAY_MIRROR;
+       isect.faceorig= (RayFace*)shi->vlr;
+       isect.oborig= RAY_OBJECT_SET(&R, shi->obi);
+       isect.face_last= NULL;
+       isect.ob_last= 0;
+       isect.lay= -1;
+       
+       if(RE_ray_tree_intersect(R.raytree, &isect))
+       {
+               float isvec[3];
+
+               VECCOPY(isvec, isect.vec);
+               hitco[0] = isect.start[0] + isect.labda*isvec[0];
+               hitco[1] = isect.start[1] + isect.labda*isvec[1];
+               hitco[2] = isect.start[2] + isect.labda*isvec[2];
+               
+               return 1;
+       } else {
+               return 0;
+       }
+}
+
+float vol_get_density(struct ShadeInput *shi, float *co)
+{
+       float density = shi->mat->alpha;
+       float emit_fac=0.0f;
+       float col[3] = {0.0, 0.0, 0.0};
+       
+       /* do any density gain stuff here */
+       
+       do_volume_tex(shi, co, col, &density, &emit_fac);
+       
+       return density;
+}
+
+
+/* compute emission component, amount of radiance to add per segment
+ * can be textured with 'emit' */
+void vol_get_emission(ShadeInput *shi, float *em, float *co, float *endco, float density)
+{
+       float emission = shi->mat->emit;
+       float col[3] = {0.0, 0.0, 0.0};
+       float dens_dummy = 1.0f;
+       
+       do_volume_tex(shi, co, col, &dens_dummy, &emission);
+       
+       em[0] = em[1] = em[2] = emission;
+}
+
+
+/* Compute attenuation, otherwise known as 'optical thickness', extinction, or tau.
+ * Used in the relationship Transmittance = e^(-attenuation)
+ * can be textured with 'alpha' */
+void vol_get_attenuation(ShadeInput *shi, float *tau, float *co, float *endco, float density, float stepsize)
+{
+       /* input density = density at co */
+
+       float dist;
+       float absorption = shi->mat->vol_absorption;
+       int s, nsteps;
+       float step_vec[3], step_sta[3], step_end[3];
+
+       dist = VecLenf(co, endco);
+
+       nsteps = (int)ceil(dist / stepsize);
+       
+       if (nsteps == 1) {
+               /* homogenous volume within the sampled distance */
+               tau[0] = tau[1] = tau[2] = dist * density;
+               VecMulf(tau, absorption);
+               return;
+       } else {
+               tau[0] = tau[1] = tau[2] = 0.0;
+       }
+       
+       VecSubf(step_vec, endco, co);
+       VecMulf(step_vec, 1.0f / nsteps);
+       
+       VECCOPY(step_sta, co);
+       VecAddf(step_end, step_sta, step_vec);
+       
+       for (s = 0;  s < nsteps; s++) {
+               
+               if (s > 0) density = vol_get_density(shi, step_sta);
+               
+               tau[0] += stepsize * density;
+               tau[1] += stepsize * density;
+               tau[2] += stepsize * density;
+               
+               if (s < nsteps-1) {
+                       VECCOPY(step_sta, step_end);
+                       VecAddf(step_end, step_end, step_vec);
+               }
+       }
+       VecMulf(tau, absorption);       
+}
+
+void vol_shade_one_lamp(struct ShadeInput *shi, float *co, LampRen *lar, float *col, float stepsize, float density)
+{
+       float visifac, lv[3], lampdist;
+       float lacol[3];
+       float tau[3], tr[3]={1.0,1.0,1.0};
+       float hitco[3], *atten_co;
+       
+       if (lar->mode & LA_LAYER) if((lar->lay & shi->obi->lay)==0) return;
+       if ((lar->lay & shi->lay)==0) return;
+       if (lar->energy == 0.0) return;
+       
+       visifac= lamp_get_visibility(lar, co, lv, &lampdist);
+       if(visifac==0.0f) return;
+
+       lacol[0] = lar->r;
+       lacol[1] = lar->g;
+       lacol[2] = lar->b;
+       
+       if(lar->mode & LA_TEXTURE) {
+               shi->osatex= 0;
+               do_lamp_tex(lar, lv, shi, lacol);
+       }
+       
+       VecMulf(lacol, visifac*lar->energy);
+
+       if (shi->mat->vol_shadeflag & MA_VOL_ATTENUATED) {
+               /* find minimum of volume bounds, or lamp coord */
+               
+               if (ELEM(lar->type, LA_SUN, LA_HEMI))
+                       VECCOPY(lv, lar->vec);
+               
+               VecMulf(lv, -1.0f);
+               
+               if (vol_get_bounds(shi, co, lv, hitco)) {
+                       if (ELEM(lar->type, LA_SUN, LA_HEMI))
+                               atten_co = hitco;
+                       else if ( lampdist < VecLenf(co, hitco) )
+                               atten_co = lar->co;
+                       else
+                               atten_co = hitco;
+
+                       atten_co = lar->co;
+
+                       vol_get_attenuation(shi, tau, co, atten_co, density, shi->mat->vol_shade_stepsize);
+                       tr[0] = exp(-tau[0]);
+                       tr[1] = exp(-tau[1]);
+                       tr[2] = exp(-tau[2]);
+                       
+                       VecMulVecf(lacol, lacol, tr);
+               }
+               else {
+                       /* point is on the outside edge of the volume,
+                        * therefore no attenuation, full transmission
+                        * radiance from lamp remains unchanged */
+               }
+       }
+       
+       VecAddf(col, col, lacol);
+}              
+
+/* shadows -> trace a ray to find blocker geometry
+   - if blocker is outside the volume, use standard shadow functions
+   - if blocker is inside the volume, use raytracing
+    -- (deep shadow maps could potentially slot in here too I suppose)
+   - attenuate from current point, to blocked point or volume bounds
+*/
+
+/* single scattering only for now */
+void vol_get_scattering(ShadeInput *shi, float *scatter, float *co, float *endco, float stepsize, float density)
+{
+       GroupObject *go;
+       ListBase *lights;
+       LampRen *lar;
+       float col[3] = {0.f, 0.f, 0.f};
+       
+       lights= get_lights(shi);
+       for(go=lights->first; go; go= go->next)
+       {
+               float lacol[3] = {0.f, 0.f, 0.f};
+       
+               lar= go->lampren;
+               if (lar==NULL) continue;
+               
+               vol_shade_one_lamp(shi, co, lar, lacol, stepsize, density);
+               
+               /* isotropic phase function */
+               VecMulf(lacol, 1.0f / (4.f * M_PI));
+       
+               VecMulf(lacol, density);
+               
+               VecAddf(col, col, lacol);
+       }
+       
+       
+               
+       VECCOPY(scatter, col);
+}
+
+
+
+static void volumeintegrate(struct ShadeInput *shi, float *col, float *co, float *endco)
+{
+       float total_tau;
+       float total_tr[3];
+       float tr[3] = {1.f, 1.f, 1.f};                  /* total transmittance */
+       float radiance[3] = {0.f, 0.f, 0.f}, d_radiance[3] = {0.f, 0.f, 0.f};
+       float stepsize = shi->mat->vol_stepsize;
+       int nsteps;
+       float vec[3], stepvec[3] = {0.0, 0.0, 0.0};
+       float step_tau[3], step_emit[3], step_scatter[3] = {0.0, 0.0, 0.0};
+       int s;
+       float step_sta[3], step_end[3], step_offs[3] = {0.0, 0.0, 0.0};
+       float alpha_fac, emit_fac=0.0f, tex_col[3];
+       
+       /* multiply col_behind with beam transmittance over entire distance */
+/*
+       // get col_behind
+       
+       // get total transmittance
+       vol_get_attenuation(shi, total_tau, start, dist, stepsize);
+       total_tr[0] = exp(-total_tau[0]);
+       total_tr[1] = exp(-total_tau[1]);
+       total_tr[2] = exp(-total_tau[2]);
+       VecMulVecf(radiance, total_tr, col_behind);
+*/     
+       
+       /* ray marching */
+       nsteps = (int)ceil(VecLenf(co, endco) / stepsize);
+       
+       VecSubf(vec, endco, co);
+       VECCOPY(stepvec, vec);
+       VecMulf(stepvec, 1.0f / nsteps);
+       
+       VECCOPY(step_sta, co);
+       VecAddf(step_end, step_sta, stepvec);
+       
+       
+       /* get radiance from all points along the ray due to participating media */
+       for (s = 0; s < nsteps; s++) {
+               float density = vol_get_density(shi, step_sta);
+               
+               /* *** transmittance and emission *** */
+               
+               /* transmittance component (alpha) */
+               vol_get_attenuation(shi, step_tau, step_sta, step_end, density, stepsize);
+               tr[0] *= exp(-step_tau[0]);
+               tr[1] *= exp(-step_tau[1]);
+               tr[2] *= exp(-step_tau[2]);
+               
+               /* Terminate raymarching if transmittance is small */
+               //if (rgb_to_luminance(tr[0], tr[1], tr[2]) < 1e-3) break;
+               
+               /* incoming light via emission or scattering (additive) */
+               vol_get_emission(shi, step_emit, step_sta, step_end, density);
+               vol_get_scattering(shi, step_scatter, step_end, step_end, stepsize, density);
+               
+               VecAddf(d_radiance, step_emit, step_scatter);
+               
+               /*   Lv += Tr * (Lve() + Ld) */
+               VecMulVecf(d_radiance, tr, d_radiance);
+               VecAddf(radiance, radiance, d_radiance);        
+
+               if (s < nsteps-1) {
+                       VECCOPY(step_sta, step_end);
+                       VecAddf(step_end, step_end, stepvec);
+               }
+       }
+       
+       VecMulf(radiance, stepsize);
+       VECCOPY(col, radiance);
+       
+       /*
+       Incoming radiance = 
+                 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)
+               return exp(-tau)
+               
+       -- To find radiance from segments along the way:
+               find radiance for one step:
+                 - loop over lights and weight by phase function
+
+                 - single scattering
+                   : integrate over sphere
+               
+               then multiply each step for final exit radiance
+               */
+}
+
+void volume_trace(struct ShadeInput *shi, struct ShadeResult *shr)
+{
+       Isect isect;
+       float hitco[3], col[3];
+
+       memset(shr, 0, sizeof(ShadeResult));
+
+       if (vol_get_bounds(shi, shi->co, shi->view, hitco)) {
+               
+               volumeintegrate(shi, col, shi->co, hitco);
+               
+               /* hit */
+               shr->alpha = 1.0f;
+               shr->combined[0] = col[0];
+               shr->combined[1] = col[1];
+               shr->combined[2] = col[2];
+               
+               QUATCOPY(shr->diff, shr->combined);
+       }
+       else {
+               /* no hit */
+               shr->combined[0] = 0.0f;
+               shr->combined[1] = 0.0f;
+               shr->combined[2] = 0.0f;
+               shr->combined[3] = shr->alpha =  0.0f;
+       }
+}
\ No newline at end of file
index 6d5e1a62ad4967c9ffba2f51bcfb68d503b48192..bdb2fe5a3f37ffcababb5d095aae98e22825920c 100644 (file)
@@ -3383,6 +3383,10 @@ static void material_panel_map_to(Object *ob, Material *ma, int from_nodes)
                uiDefButBitS(block, TOG, 1, B_MATPRV, "PAttr",          250,160,60,19, &pattr, 0, 0, 0, 0, "Display settings for particle attributes");
                uiBlockSetCol(block, TH_AUTO);
        }
+       else if (ma->material_type == MA_VOLUME) {
+               uiDefButBitS(block, TOG3, MAP_ALPHA, B_MATPRV, "Density",               10,180,60,19, &(mtex->mapto), 0, 0, 0, 0, "Causes the texture to affect the alpha value");
+               uiDefButBitS(block, TOG3, MAP_EMIT, B_MATPRV, "Emit",           70,180,45,19, &(mtex->mapto), 0, 0, 0, 0, "Causes the texture to affect the emit value");
+       }
        else {
                uiDefButBitS(block, TOG, MAP_COL, B_MATPRV, "Col",              10,180,40,19, &(mtex->mapto), 0, 0, 0, 0, "Causes the texture to affect basic color of the material");
                uiDefButBitS(block, TOG3, MAP_NORM, B_MATPRV, "Nor",            50,180,40,19, &(mtex->mapto), 0, 0, 0, 0, "Causes the texture to affect the rendered normal");
@@ -3499,37 +3503,43 @@ static void material_panel_map_input(Object *ob, Material *ma)
        
        /* TEXCO */
        uiBlockBeginAlign(block);
-       uiDefButS(block, ROW, B_MATPRV, "Glob",                 630,180,45,18, &(mtex->texco), 4.0, (float)TEXCO_GLOB, 0, 0, "Uses global coordinates for the texture coordinates");
-       uiDefButS(block, ROW, B_MATPRV, "Object",               675,180,75,18, &(mtex->texco), 4.0, (float)TEXCO_OBJECT, 0, 0, "Uses linked object's coordinates for texture coordinates");
-       if(mtex->texco == TEXCO_UV && !(mtex->texflag & MTEX_DUPLI_MAPTO)) {
-               if(!verify_valid_uv_name(mtex->uvname))
-            uiBlockSetCol(block, TH_REDALERT);
-               but=uiDefBut(block, TEX, B_MATPRV, "UV:", 750,180,158,18, mtex->uvname, 0, 31, 0, 0, "Set name of UV layer to use, default is active UV layer");
-               uiButSetCompleteFunc(but, autocomplete_uv, NULL);
-               uiBlockSetCol(block, TH_AUTO);
-       }
-       else
-               uiDefIDPoinBut(block, test_obpoin_but, ID_OB, B_MATPRV, "Ob:",750,180,158,18, &(mtex->object), "");
-       
-       uiDefButS(block, ROW, B_MATPRV, "UV",                   630,160,40,18, &(mtex->texco), 4.0, (float)TEXCO_UV, 0, 0, "Uses UV coordinates for texture coordinates");
-       uiDefButS(block, ROW, B_MATPRV, "Orco",                 670,160,55,18, &(mtex->texco), 4.0, (float)TEXCO_ORCO, 0, 0, "Uses the original undeformed coordinates of the object");
-       if( ob->particlesystem.first )
-               uiDefButS(block, ROW, B_MATPRV, "Strand",       725,160,50,18, &(mtex->texco), 4.0, (float)TEXCO_STRAND, 0, 0, "Uses normalized strand texture coordinate (1D)");
-       else
-               uiDefButS(block, ROW, B_MATPRV, "Stick",        725,160,50,18, &(mtex->texco), 4.0, (float)TEXCO_STICKY, 0, 0, "Uses mesh's sticky coordinates for the texture coordinates");
-       uiDefButS(block, ROW, B_MATPRV, "Win",                  775,160,45,18, &(mtex->texco), 4.0, (float)TEXCO_WINDOW, 0, 0, "Uses screen coordinates as texture coordinates");
-       uiDefButS(block, ROW, B_MATPRV, "Nor",                  820,160,44,18, &(mtex->texco), 4.0, (float)TEXCO_NORM, 0, 0, "Uses normal vector as texture coordinates");
-       uiDefButS(block, ROW, B_MATPRV, "Refl",                 864,160,44,18, &(mtex->texco), 4.0, (float)TEXCO_REFL, 0, 0, "Uses reflection vector as texture coordinates");
        
-       uiDefButS(block, ROW, B_MATPRV, "Stress",               630,140,70,18, &(mtex->texco), 4.0, (float)TEXCO_STRESS, 0, 0, "Uses the difference of edge lengths compared to original coordinates of the mesh");
-       uiDefButS(block, ROW, B_MATPRV, "Tangent",              700,140,70,18, &(mtex->texco), 4.0, (float)TEXCO_TANGENT, 0, 0, "Uses the optional tangent vector as texture coordinates");
-       uiBlockEndAlign(block);
-
-       if(ELEM(mtex->texco, TEXCO_UV, TEXCO_ORCO))
-               uiDefButBitS(block, TOG, MTEX_DUPLI_MAPTO, B_MATPRV, "From Dupli",      820,140,88,18, &(mtex->texflag), 0, 0, 0, 0, "Dupli's instanced from verts, faces or particles, inherit texture coordinate from their parent");
-       else if(mtex->texco == TEXCO_OBJECT)
-               uiDefButBitS(block, TOG, MTEX_OB_DUPLI_ORIG, B_MATPRV, "From Original", 820,140,88,18, &(mtex->texflag), 0, 0, 0, 0, "Dupli's derive their object coordinates from the original objects transformation");
+       if (ma->material_type == MA_VOLUME) {
+               uiDefButS(block, ROW, B_MATPRV, "Glob",                 630,180,45,18, &(mtex->texco), 4.0, (float)TEXCO_GLOB, 0, 0, "Uses global coordinates for the texture coordinates");
+               uiDefButS(block, ROW, B_MATPRV, "Object",               675,180,75,18, &(mtex->texco), 4.0, (float)TEXCO_OBJECT, 0, 0, "Uses linked object's coordinates for texture coordinates");
+               uiDefIDPoinBut(block, test_obpoin_but, ID_OB, B_MATPRV, "Ob:",750,180,158,18, &(mtex->object), "");
+       } else {
+               uiDefButS(block, ROW, B_MATPRV, "Glob",                 630,180,45,18, &(mtex->texco), 4.0, (float)TEXCO_GLOB, 0, 0, "Uses global coordinates for the texture coordinates");
+               uiDefButS(block, ROW, B_MATPRV, "Object",               675,180,75,18, &(mtex->texco), 4.0, (float)TEXCO_OBJECT, 0, 0, "Uses linked object's coordinates for texture coordinates");
+               if(mtex->texco == TEXCO_UV && !(mtex->texflag & MTEX_DUPLI_MAPTO)) {
+                       if(!verify_valid_uv_name(mtex->uvname))
+                               uiBlockSetCol(block, TH_REDALERT);
+                       but=uiDefBut(block, TEX, B_MATPRV, "UV:", 750,180,158,18, mtex->uvname, 0, 31, 0, 0, "Set name of UV layer to use, default is active UV layer");
+                       uiButSetCompleteFunc(but, autocomplete_uv, NULL);
+                       uiBlockSetCol(block, TH_AUTO);
+               }
+               else
+                       uiDefIDPoinBut(block, test_obpoin_but, ID_OB, B_MATPRV, "Ob:",750,180,158,18, &(mtex->object), "");
+               
+               uiDefButS(block, ROW, B_MATPRV, "UV",                   630,160,40,18, &(mtex->texco), 4.0, (float)TEXCO_UV, 0, 0, "Uses UV coordinates for texture coordinates");
+               uiDefButS(block, ROW, B_MATPRV, "Orco",                 670,160,55,18, &(mtex->texco), 4.0, (float)TEXCO_ORCO, 0, 0, "Uses the original undeformed coordinates of the object");
+               if( ob->particlesystem.first )
+                       uiDefButS(block, ROW, B_MATPRV, "Strand",       725,160,50,18, &(mtex->texco), 4.0, (float)TEXCO_STRAND, 0, 0, "Uses normalized strand texture coordinate (1D)");
+               else
+                       uiDefButS(block, ROW, B_MATPRV, "Stick",        725,160,50,18, &(mtex->texco), 4.0, (float)TEXCO_STICKY, 0, 0, "Uses mesh's sticky coordinates for the texture coordinates");
+               uiDefButS(block, ROW, B_MATPRV, "Win",                  775,160,45,18, &(mtex->texco), 4.0, (float)TEXCO_WINDOW, 0, 0, "Uses screen coordinates as texture coordinates");
+               uiDefButS(block, ROW, B_MATPRV, "Nor",                  820,160,44,18, &(mtex->texco), 4.0, (float)TEXCO_NORM, 0, 0, "Uses normal vector as texture coordinates");
+               uiDefButS(block, ROW, B_MATPRV, "Refl",                 864,160,44,18, &(mtex->texco), 4.0, (float)TEXCO_REFL, 0, 0, "Uses reflection vector as texture coordinates");
+               
+               uiDefButS(block, ROW, B_MATPRV, "Stress",               630,140,70,18, &(mtex->texco), 4.0, (float)TEXCO_STRESS, 0, 0, "Uses the difference of edge lengths compared to original coordinates of the mesh");
+               uiDefButS(block, ROW, B_MATPRV, "Tangent",              700,140,70,18, &(mtex->texco), 4.0, (float)TEXCO_TANGENT, 0, 0, "Uses the optional tangent vector as texture coordinates");
+               uiBlockEndAlign(block);
 
+               if(ELEM(mtex->texco, TEXCO_UV, TEXCO_ORCO))
+                       uiDefButBitS(block, TOG, MTEX_DUPLI_MAPTO, B_MATPRV, "From Dupli",      820,140,88,18, &(mtex->texflag), 0, 0, 0, 0, "Dupli's instanced from verts, faces or particles, inherit texture coordinate from their parent");
+               else if(mtex->texco == TEXCO_OBJECT)
+                       uiDefButBitS(block, TOG, MTEX_OB_DUPLI_ORIG, B_MATPRV, "From Original", 820,140,88,18, &(mtex->texflag), 0, 0, 0, 0, "Dupli's derive their object coordinates from the original objects transformation");
+       }
 
        /* COORDS */
        uiBlockBeginAlign(block);
@@ -4111,7 +4121,7 @@ static uiBlock *strand_menu(void *mat_v)
 }
 
 
-static void material_panel_material(Material *ma)
+static void material_panel_material_solid(Material *ma)
 {
        uiBlock *block;
        float *colpoin = NULL;
@@ -4199,6 +4209,43 @@ static void material_panel_material(Material *ma)
 
 }
 
+static void material_panel_material_volume(Material *ma)
+{
+       uiBlock *block;
+       short yco=PANEL_YMAX;
+       
+       block= uiNewBlock(&curarea->uiblocks, "material_panel_material_volume", UI_EMBOSS, UI_HELV, curarea->win);
+       if(uiNewPanel(curarea, block, "Volume", "Material", PANELX, PANELY, PANELW, PANELH)==0) return;
+       
+       uiSetButLock(ma->id.lib!=NULL, ERROR_LIBDATA_MESSAGE);
+       
+       uiBlockBeginAlign(block);
+       uiDefButF(block, NUM, B_MATPRV, "Step Size: ",
+               X2CLM1, yco-=BUTH, BUTW2, BUTH, &(ma->vol_stepsize), 0.001, 100.0, 10, 2, "Ray marching step size");
+       uiDefButF(block, NUMSLI, B_MATPRV, "Density: ",
+               X2CLM1, yco-=BUTH, BUTW2, BUTH, &(ma->alpha), 0.0, 1.0, 0, 0, "Base opacity value");
+       uiDefButF(block, NUM, B_MATPRV, "Absorption: ",
+               X2CLM1, yco-=BUTH, BUTW2, BUTH, &(ma->vol_absorption), 0.0, 5.0, 0, 0, "Multiplier for absorption");
+       uiBlockEndAlign(block);
+       
+       yco -= YSPACE;
+       
+       uiDefButF(block, NUMSLI, B_MATPRV, "Emit: ",
+               X2CLM1, yco-=BUTH, BUTW2, BUTH, &(ma->emit), 0.0, 2.0, 0, 0, "Emission component");
+       
+       yco -= YSPACE;
+               
+       uiBlockBeginAlign(block);
+       uiDefButBitS(block, TOG, MA_VOL_ATTENUATED, B_MATPRV, "Shading",
+               X2CLM1, yco-=BUTH, BUTW2, BUTH, &(ma->vol_shadeflag), 0, 0, 0, 0, "Uses absorption for light attenuation");
+       uiDefButF(block, NUM, B_MATPRV, "Step Size: ",
+               X2CLM1, yco-=BUTH, BUTW2, BUTH, &(ma->vol_shade_stepsize), 0.001, 100.0, 10, 2, "Step");
+       uiBlockEndAlign(block);
+
+       uiDefBut(block, LABEL, B_DIFF, "",
+               X2CLM2, yco, BUTW2, BUTH, 0, 0, 0, 0, 0, "");
+}
+
 static void material_panel_nodes(Material *ma)
 {
        bNode *node;
@@ -4237,7 +4284,7 @@ static void material_panel_links(Object *ob, Material *ma)
        
        block= uiNewBlock(&curarea->uiblocks, "material_panel_links", UI_EMBOSS, UI_HELV, curarea->win);
        /* 310 makes sorting code to put it right after preview panel */
-       if(uiNewPanel(curarea, block, "Links and Pipeline", "Material", 310, 0, 318, 204)==0) return;
+       if(uiNewPanel(curarea, block, "Links and Pipeline", "Material", 310, 0, 338, 204)==0) return;
 
        /* Links from object to material/nodes */
        uiDefBut(block, ROUNDBOX, 0, "",                                        5, 90, 310, 110, NULL, 7.0, 0.0, 15 , 20, ""); 
@@ -4330,7 +4377,10 @@ static void material_panel_links(Object *ob, Material *ma)
        uiDefButBitI(block, TOG, MA_ONLYCAST, B_MATPRV,"OnlyCast",              85,10,75,19, &(ma->mode), 0, 0, 0, 0, "Makes faces cast shadows only, not rendered");
        uiDefButBitI(block, TOG, MA_TRACEBLE, B_NOP,"Traceable",        160,10,75,19, &(ma->mode), 0, 0, 0, 0, "Makes material detectable by ray tracing");
        uiDefButBitI(block, TOG, MA_SHADBUF, B_MATPRV,  "Shadbuf",              235,10,75,19, &(ma->mode), 0, 0, 0, 0, "Makes material cast shadows from shadow buffer lamps");
-                                 
+       uiBlockEndAlign(block);
+       
+       uiDefButS(block, MENU, B_MATPRV, "Material Type %t|Solid %x0|Halo %x1|Volume %x2",
+               10, -15, 300, 20, &(ma->material_type), 0.0, 0.0, 0, 0, "");
        
 }
 
@@ -4388,21 +4438,26 @@ void material_panels()
                
                ma= editnode_get_active_material(ma);
                if(ma) {
-                       material_panel_material(ma);
-                       material_panel_ramps(ma);
-                       material_panel_shading(ma);
-                       
-                       if (G.scene->r.renderer==R_INTERN)
-                               material_panel_tramir(ma);
-                       else {
-                               if(ma->YF_ar==0.f) {
-                                       ma->YF_ar = ma->YF_ag = ma->YF_ab = 1;
-                                       ma->YF_dscale = 1;
+                       if (ma->material_type == MA_SOLID) {
+                               material_panel_material_solid(ma);
+                               material_panel_ramps(ma);
+                               material_panel_shading(ma);
+                               
+                               if (G.scene->r.renderer==R_INTERN)
+                                       material_panel_tramir(ma);
+                               else {
+                                       if(ma->YF_ar==0.f) {
+                                               ma->YF_ar = ma->YF_ag = ma->YF_ab = 1;
+                                               ma->YF_dscale = 1;
+                                       }
+                                       material_panel_tramir_yafray(ma);
                                }
-                               material_panel_tramir_yafray(ma);
-                       }
 
-                       material_panel_sss(ma);
+                               material_panel_sss(ma);
+                               
+                       } else if (ma->material_type == MA_VOLUME) {
+                               material_panel_material_volume(ma);
+                       }
                        material_panel_texture(ob, ma);
                        
                        mtex= ma->mtex[ ma->texact ];
index 1730bb890bcc25cffffd03cb39ac7bba2ae344c2..dacb26b99a4a7e0ff64624dacc471d0eb02e1c95 100644 (file)
@@ -315,6 +315,8 @@ static Scene *preview_prepare_scene(RenderInfo *ri, int id_type, ID *id, int pr_
                                /* turn on raytracing if needed */
                                if(mat->mode_l & (MA_RAYTRANSP|MA_RAYMIRROR))
                                        sce->r.mode |= R_RAYTRACE;
+                               if(mat->material_type == MA_VOLUME)
+                                       sce->r.mode |= R_RAYTRACE;
                                if(mat->sss_flag & MA_DIFF_SSS)
                                        sce->r.mode |= R_SSS;