Blender V2.61 - r43446

volumetric.c

Go to the documentation of this file.
00001 /*
00002  * ***** BEGIN GPL LICENSE BLOCK *****
00003  *
00004  * This program is free software; you can redistribute it and/or
00005  * modify it under the terms of the GNU General Public License
00006  * as published by the Free Software Foundation; either version 2
00007  * of the License, or (at your option) any later version.
00008  *
00009  * This program is distributed in the hope that it will be useful,
00010  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00011  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00012  * GNU General Public License for more details.
00013  *
00014  * You should have received a copy of the GNU General Public License
00015  * along with this program; if not, write to the Free Software Foundation,
00016  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
00017  *
00018  * The Original Code is Copyright (C) 2001-2002 by NaN Holding BV.
00019  * All rights reserved.
00020  *
00021  * The Original Code is: all of this file.
00022  *
00023  * Contributor(s): Matt Ebb, Raul Fernandez Hernandez (Farsthary)
00024  *
00025  * ***** END GPL LICENSE BLOCK *****
00026  */
00027 
00033 #include <math.h>
00034 #include <stdlib.h>
00035 #include <string.h>
00036 #include <float.h>
00037 
00038 #include "MEM_guardedalloc.h"
00039 
00040 #include "BLI_blenlib.h"
00041 #include "BLI_math.h"
00042 #include "BLI_rand.h"
00043 #include "BLI_voxel.h"
00044 #include "BLI_utildefines.h"
00045 
00046 #include "RE_shader_ext.h"
00047 
00048 #include "DNA_material_types.h"
00049 #include "DNA_group_types.h"
00050 #include "DNA_lamp_types.h"
00051 #include "DNA_meta_types.h"
00052 
00053 #include "BKE_global.h"
00054 
00055 #include "render_types.h"
00056 #include "pixelshading.h"
00057 #include "rayintersection.h"
00058 #include "rayobject.h"
00059 #include "shading.h"
00060 #include "shadbuf.h"
00061 #include "texture.h"
00062 #include "volumetric.h"
00063 #include "volume_precache.h"
00064 
00065 /* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
00066 /* defined in pipeline.c, is hardcopy of active dynamic allocated Render */
00067 /* only to be used here in this file, it's for speed */
00068 extern struct Render R;
00069 /* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
00070 
00071 /* luminance rec. 709 */
00072 BM_INLINE float luminance(const float col[3])
00073 {
00074     return (0.212671f*col[0] + 0.71516f*col[1] + 0.072169f*col[2]);
00075 }
00076 
00077 /* tracing */
00078 static float vol_get_shadow(ShadeInput *shi, LampRen *lar, const float co[3])
00079 {
00080     float visibility = 1.f;
00081     
00082     if(lar->shb) {
00083         float dxco[3]={0.f, 0.f, 0.f}, dyco[3]={0.f, 0.f, 0.f};
00084         
00085         visibility = testshadowbuf(&R, lar->shb, co, dxco, dyco, 1.0, 0.0);     
00086     } else if (lar->mode & LA_SHAD_RAY) {
00087         /* trace shadow manually, no good lamp api atm */
00088         Isect is;
00089         
00090         copy_v3_v3(is.start, co);
00091         if(lar->type==LA_SUN || lar->type==LA_HEMI) {
00092             is.dir[0] = -lar->vec[0];
00093             is.dir[1] = -lar->vec[1];
00094             is.dir[2] = -lar->vec[2];
00095             is.dist = R.maxdist;
00096         } else {
00097             sub_v3_v3v3(is.dir, lar->co, is.start);
00098             is.dist = normalize_v3( is.dir );
00099         }
00100 
00101         is.mode = RE_RAY_MIRROR;
00102         is.check = RE_CHECK_VLR_NON_SOLID_MATERIAL;
00103         is.skip = 0;
00104         
00105         if(lar->mode & (LA_LAYER|LA_LAYER_SHADOW))
00106             is.lay= lar->lay;   
00107         else
00108             is.lay= -1;
00109             
00110         is.orig.ob = NULL;
00111         is.orig.face = NULL;
00112         is.last_hit = lar->last_hit[shi->thread];
00113         
00114         if(RE_rayobject_raycast(R.raytree,&is)) {
00115             visibility = 0.f;
00116         }
00117         
00118         lar->last_hit[shi->thread]= is.last_hit;
00119     }
00120     return visibility;
00121 }
00122 
00123 static int vol_get_bounds(ShadeInput *shi, const float co[3], const float vec[3], float hitco[3], Isect *isect, int intersect_type)
00124 {
00125     
00126     copy_v3_v3(isect->start, co);
00127     copy_v3_v3(isect->dir, vec);
00128     isect->dist = FLT_MAX;
00129     isect->mode= RE_RAY_MIRROR;
00130     isect->last_hit = NULL;
00131     isect->lay= -1;
00132     isect->check= RE_CHECK_VLR_NONE;
00133     
00134     if (intersect_type == VOL_BOUNDS_DEPTH) {
00135         isect->skip = RE_SKIP_VLR_NEIGHBOUR;
00136         isect->orig.face = (void*)shi->vlr;
00137         isect->orig.ob = (void*)shi->obi;
00138     } else { // if (intersect_type == VOL_BOUNDS_SS) {
00139         isect->skip= 0;
00140         isect->orig.face= NULL;
00141         isect->orig.ob = NULL;
00142     }
00143     
00144     if(RE_rayobject_raycast(R.raytree, isect))
00145     {
00146         hitco[0] = isect->start[0] + isect->dist*isect->dir[0];
00147         hitco[1] = isect->start[1] + isect->dist*isect->dir[1];
00148         hitco[2] = isect->start[2] + isect->dist*isect->dir[2];
00149         return 1;
00150     } else {
00151         return 0;
00152     }
00153 }
00154 
00155 static void shade_intersection(ShadeInput *shi, float col_r[4], Isect *is)
00156 {
00157     ShadeInput shi_new;
00158     ShadeResult shr_new;
00159     
00160     memset(&shi_new, 0, sizeof(ShadeInput)); 
00161     
00162     shi_new.mask= shi->mask;
00163     shi_new.osatex= shi->osatex;
00164     shi_new.thread= shi->thread;
00165     shi_new.depth = shi->depth + 1;
00166     shi_new.volume_depth= shi->volume_depth + 1;
00167     shi_new.xs= shi->xs;
00168     shi_new.ys= shi->ys;
00169     shi_new.lay= shi->lay;
00170     shi_new.passflag= SCE_PASS_COMBINED; /* result of tracing needs no pass info */
00171     shi_new.combinedflag= 0xFFFFFF;      /* ray trace does all options */
00172     shi_new.light_override= shi->light_override;
00173     shi_new.mat_override= shi->mat_override;
00174     
00175     copy_v3_v3(shi_new.camera_co, is->start);
00176     
00177     memset(&shr_new, 0, sizeof(ShadeResult));
00178     
00179     /* hardcoded limit of 100 for now - prevents problems in weird geometry */
00180     if (shi->volume_depth < 100) {
00181         shade_ray(is, &shi_new, &shr_new);
00182     }
00183     
00184     copy_v3_v3(col_r, shr_new.combined);
00185     col_r[3] = shr_new.alpha;
00186 }
00187 
00188 static void vol_trace_behind(ShadeInput *shi, VlakRen *vlr, const float co[3], float col_r[4])
00189 {
00190     Isect isect;
00191     
00192     copy_v3_v3(isect.start, co);
00193     copy_v3_v3(isect.dir, shi->view);
00194     isect.dist = FLT_MAX;
00195     
00196     isect.mode= RE_RAY_MIRROR;
00197     isect.check = RE_CHECK_VLR_NONE;
00198     isect.skip = RE_SKIP_VLR_NEIGHBOUR;
00199     isect.orig.ob = (void*) shi->obi;
00200     isect.orig.face = (void*)vlr;
00201     isect.last_hit = NULL;
00202     isect.lay= -1;
00203     
00204     /* check to see if there's anything behind the volume, otherwise shade the sky */
00205     if(RE_rayobject_raycast(R.raytree, &isect)) {
00206         shade_intersection(shi, col_r, &isect);
00207     } else {
00208         shadeSkyView(col_r, co, shi->view, NULL, shi->thread);
00209         shadeSunView(col_r, shi->view);
00210     } 
00211 }
00212 
00213 
00214 /* trilinear interpolation */
00215 static void vol_get_precached_scattering(Render *re, ShadeInput *shi, float scatter_col[3], const float co[3])
00216 {
00217     VolumePrecache *vp = shi->obi->volume_precache;
00218     float bbmin[3], bbmax[3], dim[3];
00219     float world_co[3], sample_co[3];
00220     
00221     if (!vp) return;
00222     
00223     /* find sample point in global space bounding box 0.0-1.0 */
00224     global_bounds_obi(re, shi->obi, bbmin, bbmax);
00225     sub_v3_v3v3(dim, bbmax, bbmin);
00226     mul_v3_m4v3(world_co, re->viewinv, co); 
00227 
00228     /* sample_co in 0.0-1.0 */
00229     sample_co[0] = (world_co[0] - bbmin[0]) / dim[0];
00230     sample_co[1] = (world_co[1] - bbmin[1]) / dim[1];
00231     sample_co[2] = (world_co[2] - bbmin[2]) / dim[2];
00232 
00233     scatter_col[0] = voxel_sample_triquadratic(vp->data_r, vp->res, sample_co);
00234     scatter_col[1] = voxel_sample_triquadratic(vp->data_g, vp->res, sample_co);
00235     scatter_col[2] = voxel_sample_triquadratic(vp->data_b, vp->res, sample_co);
00236 }
00237 
00238 /* Meta object density, brute force for now 
00239  * (might be good enough anyway, don't need huge number of metaobs to model volumetric objects */
00240 static float metadensity(Object* ob, const float co[3])
00241 {
00242     float mat[4][4], imat[4][4], dens = 0.f;
00243     MetaBall* mb = (MetaBall*)ob->data;
00244     MetaElem* ml;
00245     
00246     /* transform co to meta-element */
00247     float tco[3] = {co[0], co[1], co[2]};
00248     mult_m4_m4m4(mat, R.viewmat, ob->obmat);
00249     invert_m4_m4(imat, mat);
00250     mul_m4_v3(imat, tco);
00251     
00252     for (ml = mb->elems.first; ml; ml=ml->next) {
00253         float bmat[3][3], dist2;
00254         
00255         /* element rotation transform */
00256         float tp[3] = {ml->x - tco[0], ml->y - tco[1], ml->z - tco[2]};
00257         quat_to_mat3( bmat,ml->quat);
00258         transpose_m3(bmat); // rot.only, so inverse == transpose
00259         mul_m3_v3(bmat, tp);
00260         
00261         /* MB_BALL default */
00262         switch (ml->type) {
00263             case MB_ELIPSOID:
00264                 tp[0] /= ml->expx, tp[1] /= ml->expy, tp[2] /= ml->expz;
00265                 break;
00266             case MB_CUBE:
00267                 tp[2] = (tp[2] > ml->expz) ? (tp[2] - ml->expz) : ((tp[2] < -ml->expz) ? (tp[2] + ml->expz) : 0.f);
00268                 // no break, xy as plane
00269             case MB_PLANE:
00270                 tp[1] = (tp[1] > ml->expy) ? (tp[1] - ml->expy) : ((tp[1] < -ml->expy) ? (tp[1] + ml->expy) : 0.f);
00271                 // no break, x as tube
00272             case MB_TUBE:
00273                 tp[0] = (tp[0] > ml->expx) ? (tp[0] - ml->expx) : ((tp[0] < -ml->expx) ? (tp[0] + ml->expx) : 0.f);
00274         }
00275         
00276         /* ml->rad2 is not set */
00277         dist2 = 1.f - ((tp[0]*tp[0] + tp[1]*tp[1] + tp[2]*tp[2]) / (ml->rad*ml->rad));
00278         if (dist2 > 0.f)
00279             dens += (ml->flag & MB_NEGATIVE) ? -ml->s*dist2*dist2*dist2 : ml->s*dist2*dist2*dist2;
00280     }
00281     
00282     dens -= mb->thresh;
00283     return (dens < 0.f) ? 0.f : dens;
00284 }
00285 
00286 float vol_get_density(struct ShadeInput *shi, const float co[3])
00287 {
00288     float density = shi->mat->vol.density;
00289     float density_scale = shi->mat->vol.density_scale;
00290         
00291     if (shi->mat->mapto_textured & MAP_DENSITY)
00292         do_volume_tex(shi, co, MAP_DENSITY, NULL, &density, &R);
00293     
00294     // if meta-object, modulate by metadensity without increasing it
00295     if (shi->obi->obr->ob->type == OB_MBALL) {
00296         const float md = metadensity(shi->obi->obr->ob, co);
00297         if (md < 1.f) density *= md;
00298      }
00299     
00300     return density * density_scale;
00301 }
00302 
00303 
00304 /* Color of light that gets scattered out by the volume */
00305 /* Uses same physically based scattering parameter as in transmission calculations, 
00306  * along with artificial reflection scale/reflection color tint */
00307 static void vol_get_reflection_color(ShadeInput *shi, float ref_col[3], const float co[3])
00308 {
00309     float scatter = shi->mat->vol.scattering;
00310     float reflection= shi->mat->vol.reflection;
00311     copy_v3_v3(ref_col, shi->mat->vol.reflection_col);
00312     
00313     if (shi->mat->mapto_textured & (MAP_SCATTERING+MAP_REFLECTION_COL))
00314         do_volume_tex(shi, co, MAP_SCATTERING+MAP_REFLECTION_COL, ref_col, &scatter, &R);
00315     
00316     /* only one single float parameter at a time... :s */
00317     if (shi->mat->mapto_textured & (MAP_REFLECTION))
00318         do_volume_tex(shi, co, MAP_REFLECTION, NULL, &reflection, &R);
00319     
00320     ref_col[0] = reflection * ref_col[0] * scatter;
00321     ref_col[1] = reflection * ref_col[1] * scatter;
00322     ref_col[2] = reflection * ref_col[2] * scatter;
00323 }
00324 
00325 /* compute emission component, amount of radiance to add per segment
00326  * can be textured with 'emit' */
00327 static void vol_get_emission(ShadeInput *shi, float emission_col[3], const float co[3])
00328 {
00329     float emission = shi->mat->vol.emission;
00330     copy_v3_v3(emission_col, shi->mat->vol.emission_col);
00331     
00332     if (shi->mat->mapto_textured & (MAP_EMISSION+MAP_EMISSION_COL))
00333         do_volume_tex(shi, co, MAP_EMISSION+MAP_EMISSION_COL, emission_col, &emission, &R);
00334     
00335     emission_col[0] = emission_col[0] * emission;
00336     emission_col[1] = emission_col[1] * emission;
00337     emission_col[2] = emission_col[2] * emission;
00338 }
00339 
00340 
00341 /* A combination of scattering and absorption -> known as sigma T.
00342  * This can possibly use a specific scattering color, 
00343  * and absorption multiplier factor too, but these parameters are left out for simplicity.
00344  * It's easy enough to get a good wide range of results with just these two parameters. */
00345 static void vol_get_sigma_t(ShadeInput *shi, float sigma_t[3], const float co[3])
00346 {
00347     /* technically absorption, but named transmission color 
00348      * since it describes the effect of the coloring *after* absorption */
00349     float transmission_col[3] = {shi->mat->vol.transmission_col[0], shi->mat->vol.transmission_col[1], shi->mat->vol.transmission_col[2]};
00350     float scattering = shi->mat->vol.scattering;
00351     
00352     if (shi->mat->mapto_textured & (MAP_SCATTERING+MAP_TRANSMISSION_COL))
00353         do_volume_tex(shi, co, MAP_SCATTERING+MAP_TRANSMISSION_COL, transmission_col, &scattering, &R);
00354     
00355     sigma_t[0] = (1.0f - transmission_col[0]) + scattering;
00356     sigma_t[1] = (1.0f - transmission_col[1]) + scattering;
00357     sigma_t[2] = (1.0f - transmission_col[2]) + scattering;
00358 }
00359 
00360 /* phase function - determines in which directions the light 
00361  * is scattered in the volume relative to incoming direction 
00362  * and view direction */
00363 static float vol_get_phasefunc(ShadeInput *UNUSED(shi), float g, const float w[3], const float wp[3])
00364 {
00365     const float normalize = 0.25f; // = 1.f/4.f = M_PI/(4.f*M_PI)
00366     
00367     /* normalization constant is 1/4 rather than 1/4pi, since
00368      * Blender's shading system doesn't normalise for
00369      * energy conservation - eg. multiplying by pdf ( 1/pi for a lambert brdf ).
00370      * This means that lambert surfaces in Blender are pi times brighter than they 'should be'
00371      * and therefore, with correct energy conservation, volumes will darker than other solid objects,
00372      * for the same lighting intensity.
00373      * To correct this, scale up the phase function values by pi
00374      * until Blender's shading system supports this better. --matt
00375      */
00376     
00377     if (g == 0.f) { /* isotropic */
00378         return normalize * 1.f;
00379     } else {        /* schlick */
00380         const float k = 1.55f * g - .55f * g * g * g;
00381         const float kcostheta = k * dot_v3v3(w, wp);
00382         return normalize * (1.f - k*k) / ((1.f - kcostheta) * (1.f - kcostheta));
00383     }
00384     
00385     /*
00386      * not used, but here for reference:
00387     switch (phasefunc_type) {
00388         case MA_VOL_PH_MIEHAZY:
00389             return normalize * (0.5f + 4.5f * powf(0.5 * (1.f + costheta), 8.f));
00390         case MA_VOL_PH_MIEMURKY:
00391             return normalize * (0.5f + 16.5f * powf(0.5 * (1.f + costheta), 32.f));
00392         case MA_VOL_PH_RAYLEIGH:
00393             return normalize * 3.f/4.f * (1 + costheta * costheta);
00394         case MA_VOL_PH_HG:
00395             return normalize * (1.f - g*g) / powf(1.f + g*g - 2.f * g * costheta, 1.5f));
00396         case MA_VOL_PH_SCHLICK:
00397         {
00398             const float k = 1.55f * g - .55f * g * g * g;
00399             const float kcostheta = k * costheta;
00400             return normalize * (1.f - k*k) / ((1.f - kcostheta) * (1.f - kcostheta));
00401         }
00402         case MA_VOL_PH_ISOTROPIC:
00403         default:
00404             return normalize * 1.f;
00405     }
00406     */
00407 }
00408 
00409 /* Compute transmittance = e^(-attenuation) */
00410 static void vol_get_transmittance_seg(ShadeInput *shi, float tr[3], float stepsize, const float co[3], float density)
00411 {
00412     /* input density = density at co */
00413     float tau[3] = {0.f, 0.f, 0.f};
00414     const float stepd = density * stepsize;
00415     float sigma_t[3];
00416     
00417     vol_get_sigma_t(shi, sigma_t, co);
00418     
00419     /* homogenous volume within the sampled distance */
00420     tau[0] += stepd * sigma_t[0];
00421     tau[1] += stepd * sigma_t[1];
00422     tau[2] += stepd * sigma_t[2];
00423     
00424     tr[0] *= expf(-tau[0]);
00425     tr[1] *= expf(-tau[1]);
00426     tr[2] *= expf(-tau[2]);
00427 }
00428 
00429 /* Compute transmittance = e^(-attenuation) */
00430 static void vol_get_transmittance(ShadeInput *shi, float tr[3], const float co[3], const float endco[3])
00431 {
00432     float p[3] = {co[0], co[1], co[2]};
00433     float step_vec[3] = {endco[0] - co[0], endco[1] - co[1], endco[2] - co[2]};
00434     float tau[3] = {0.f, 0.f, 0.f};
00435 
00436     float t0 = 0.f;
00437     float t1 = normalize_v3(step_vec);
00438     float pt0 = t0;
00439     
00440     t0 += shi->mat->vol.stepsize * ((shi->mat->vol.stepsize_type == MA_VOL_STEP_CONSTANT) ? 0.5f : BLI_thread_frand(shi->thread));
00441     p[0] += t0 * step_vec[0];
00442     p[1] += t0 * step_vec[1];
00443     p[2] += t0 * step_vec[2];
00444     mul_v3_fl(step_vec, shi->mat->vol.stepsize);
00445 
00446     for (; t0 < t1; pt0 = t0, t0 += shi->mat->vol.stepsize) {
00447         const float d = vol_get_density(shi, p);
00448         const float stepd = (t0 - pt0) * d;
00449         float sigma_t[3];
00450         
00451         vol_get_sigma_t(shi, sigma_t, p);
00452         
00453         tau[0] += stepd * sigma_t[0];
00454         tau[1] += stepd * sigma_t[1];
00455         tau[2] += stepd * sigma_t[2];
00456         
00457         add_v3_v3(p, step_vec);
00458     }
00459     
00460     /* return transmittance */
00461     tr[0] = expf(-tau[0]);
00462     tr[1] = expf(-tau[1]);
00463     tr[2] = expf(-tau[2]);
00464 }
00465 
00466 static void vol_shade_one_lamp(struct ShadeInput *shi, const float co[3], const float view[3], LampRen *lar, float lacol[3])
00467 {
00468     float visifac, lv[3], lampdist;
00469     float tr[3]={1.0,1.0,1.0};
00470     float hitco[3], *atten_co;
00471     float p, ref_col[3];
00472     
00473     if (lar->mode & LA_LAYER) if((lar->lay & shi->obi->lay)==0) return;
00474     if ((lar->lay & shi->lay)==0) return;
00475     if (lar->energy == 0.0f) return;
00476     
00477     if ((visifac= lamp_get_visibility(lar, co, lv, &lampdist)) == 0.f) return;
00478     
00479     copy_v3_v3(lacol, &lar->r);
00480     
00481     if(lar->mode & LA_TEXTURE) {
00482         shi->osatex= 0;
00483         do_lamp_tex(lar, lv, shi, lacol, LA_TEXTURE);
00484     }
00485 
00486     mul_v3_fl(lacol, visifac);
00487 
00488     if (ELEM(lar->type, LA_SUN, LA_HEMI))
00489         copy_v3_v3(lv, lar->vec);
00490     negate_v3(lv);
00491     
00492     if (shi->mat->vol.shade_type == MA_VOL_SHADE_SHADOWED) {
00493         mul_v3_fl(lacol, vol_get_shadow(shi, lar, co));
00494     }
00495     else if (ELEM3(shi->mat->vol.shade_type, MA_VOL_SHADE_SHADED, MA_VOL_SHADE_MULTIPLE, MA_VOL_SHADE_SHADEDPLUSMULTIPLE))
00496     {
00497         Isect is;
00498         
00499         if (shi->mat->vol.shadeflag & MA_VOL_RECV_EXT_SHADOW) {
00500             mul_v3_fl(lacol, vol_get_shadow(shi, lar, co));
00501             if (luminance(lacol) < 0.001f) return;
00502         }
00503         
00504         /* find minimum of volume bounds, or lamp coord */
00505         if (vol_get_bounds(shi, co, lv, hitco, &is, VOL_BOUNDS_SS)) {
00506             float dist = len_v3v3(co, hitco);
00507             VlakRen *vlr = (VlakRen *)is.hit.face;
00508             
00509             /* simple internal shadowing */
00510             if (vlr->mat->material_type == MA_TYPE_SURFACE) {
00511                 lacol[0] = lacol[1] = lacol[2] = 0.0f;
00512                 return;
00513             }
00514 
00515             if (ELEM(lar->type, LA_SUN, LA_HEMI))
00516                 /* infinite lights, can never be inside volume */
00517                 atten_co = hitco;
00518             else if ( lampdist < dist ) {
00519                 atten_co = lar->co;
00520             } else
00521                 atten_co = hitco;
00522             
00523             vol_get_transmittance(shi, tr, co, atten_co);
00524             
00525             mul_v3_v3v3(lacol, lacol, tr);
00526         }
00527         else {
00528             /* Point is on the outside edge of the volume,
00529              * therefore no attenuation, full transmission.
00530              * Radiance from lamp remains unchanged */
00531         }
00532     }
00533     
00534     if (luminance(lacol) < 0.001f) return;
00535     
00536     normalize_v3(lv);
00537     p = vol_get_phasefunc(shi, shi->mat->vol.asymmetry, view, lv);
00538     
00539     /* physically based scattering with non-physically based RGB gain */
00540     vol_get_reflection_color(shi, ref_col, co);
00541     
00542     lacol[0] *= p * ref_col[0];
00543     lacol[1] *= p * ref_col[1];
00544     lacol[2] *= p * ref_col[2];
00545 }
00546 
00547 /* single scattering only for now */
00548 void vol_get_scattering(ShadeInput *shi, float scatter_col[3], const float co[3], const float view[3])
00549 {
00550     ListBase *lights;
00551     GroupObject *go;
00552     LampRen *lar;
00553 
00554     zero_v3(scatter_col);
00555 
00556     lights= get_lights(shi);
00557     for(go=lights->first; go; go= go->next)
00558     {
00559         float lacol[3] = {0.f, 0.f, 0.f};
00560         lar= go->lampren;
00561         
00562         if (lar) {
00563             vol_shade_one_lamp(shi, co, view, lar, lacol);
00564             add_v3_v3(scatter_col, lacol);
00565         }
00566     }
00567 }
00568 
00569     
00570 /*
00571 The main volumetric integrator, using an emission/absorption/scattering model.
00572 
00573 Incoming radiance = 
00574 
00575 outgoing radiance from behind surface * beam transmittance/attenuation
00576 + added radiance from all points along the ray due to participating media
00577     --> radiance for each segment = 
00578         (radiance added by scattering + radiance added by emission) * beam transmittance/attenuation
00579 */
00580 
00581 /* For ease of use, I've also introduced a 'reflection' and 'reflection color' parameter, which isn't 
00582  * physically correct. This works as an RGB tint/gain on out-scattered light, but doesn't affect the light 
00583  * that is transmitted through the volume. While having wavelength dependent absorption/scattering is more correct,
00584  * it also makes it harder to control the overall look of the volume since coloring the outscattered light results
00585  * in the inverse color being transmitted through the rest of the volume.
00586  */
00587 static void volumeintegrate(struct ShadeInput *shi, float col[4], const float co[3], const float endco[3])
00588 {
00589     float radiance[3] = {0.f, 0.f, 0.f};
00590     float tr[3] = {1.f, 1.f, 1.f};
00591     float p[3] = {co[0], co[1], co[2]};
00592     float step_vec[3] = {endco[0] - co[0], endco[1] - co[1], endco[2] - co[2]};
00593     const float stepsize = shi->mat->vol.stepsize;
00594     
00595     float t0 = 0.f;
00596     float pt0 = t0;
00597     float t1 = normalize_v3(step_vec);  /* returns vector length */
00598     
00599     t0 += stepsize * ((shi->mat->vol.stepsize_type == MA_VOL_STEP_CONSTANT) ? 0.5f : BLI_thread_frand(shi->thread));
00600     p[0] += t0 * step_vec[0];
00601     p[1] += t0 * step_vec[1];
00602     p[2] += t0 * step_vec[2];
00603     mul_v3_fl(step_vec, stepsize);
00604     
00605     for (; t0 < t1; pt0 = t0, t0 += stepsize) {
00606         const float density = vol_get_density(shi, p);
00607         
00608         if (density > 0.00001f) {
00609             float scatter_col[3] = {0.f, 0.f, 0.f}, emit_col[3];
00610             const float stepd = (t0 - pt0) * density;
00611             
00612             /* transmittance component (alpha) */
00613             vol_get_transmittance_seg(shi, tr, stepsize, co, density);
00614             
00615             if (t0 > t1 * 0.25f) {
00616                 /* only use depth cutoff after we've traced a little way into the volume */
00617                 if (luminance(tr) < shi->mat->vol.depth_cutoff) break;
00618             }
00619             
00620             vol_get_emission(shi, emit_col, p);
00621             
00622             if (shi->obi->volume_precache) {
00623                 float p2[3];
00624                 
00625                 p2[0] = p[0] + (step_vec[0] * 0.5f);
00626                 p2[1] = p[1] + (step_vec[1] * 0.5f);
00627                 p2[2] = p[2] + (step_vec[2] * 0.5f);
00628                 
00629                 vol_get_precached_scattering(&R, shi, scatter_col, p2);
00630             } else
00631                 vol_get_scattering(shi, scatter_col, p, shi->view);
00632             
00633             radiance[0] += stepd * tr[0] * (emit_col[0] + scatter_col[0]);
00634             radiance[1] += stepd * tr[1] * (emit_col[1] + scatter_col[1]);
00635             radiance[2] += stepd * tr[2] * (emit_col[2] + scatter_col[2]);
00636         }
00637         add_v3_v3(p, step_vec);
00638     }
00639     
00640     /* multiply original color (from behind volume) with transmittance over entire distance */
00641     mul_v3_v3v3(col, tr, col);
00642     add_v3_v3(col, radiance);
00643     
00644     /* alpha <-- transmission luminance */
00645     col[3] = 1.0f - luminance(tr);
00646 }
00647 
00648 /* the main entry point for volume shading */
00649 static void volume_trace(struct ShadeInput *shi, struct ShadeResult *shr, int inside_volume)
00650 {
00651     float hitco[3], col[4] = {0.f,0.f,0.f,0.f};
00652     float *startco, *endco;
00653     int trace_behind = 1;
00654     const int ztransp= ((shi->depth==0) && (shi->mat->mode & MA_TRANSP) && (shi->mat->mode & MA_ZTRANSP));
00655     Isect is;
00656 
00657     /* check for shading an internal face a volume object directly */
00658     if (inside_volume == VOL_SHADE_INSIDE)
00659         trace_behind = 0;
00660     else if (inside_volume == VOL_SHADE_OUTSIDE) {
00661         if (shi->flippednor)
00662             inside_volume = VOL_SHADE_INSIDE;
00663     }
00664     
00665     if (ztransp && inside_volume == VOL_SHADE_INSIDE) {
00666         MatInside *mi;
00667         int render_this=0;
00668         
00669         /* don't render the backfaces of ztransp volume materials.
00670          
00671          * volume shading renders the internal volume from between the
00672          * ' view intersection of the solid volume to the
00673          * intersection on the other side, as part of the shading of
00674          * the front face.
00675          
00676          * Because ztransp renders both front and back faces independently
00677          * this will double up, so here we prevent rendering the backface as well, 
00678          * which would otherwise render the volume in between the camera and the backface
00679          * --matt */
00680         
00681         for (mi=R.render_volumes_inside.first; mi; mi=mi->next) {
00682             /* weak... */
00683             if (mi->ma == shi->mat) render_this=1;
00684         }
00685         if (!render_this) return;
00686     }
00687     
00688 
00689     if (inside_volume == VOL_SHADE_INSIDE)
00690     {
00691         startco = shi->camera_co;
00692         endco = shi->co;
00693         
00694         if (trace_behind) {
00695             if (!ztransp)
00696                 /* trace behind the volume object */
00697                 vol_trace_behind(shi, shi->vlr, endco, col);
00698         } else {
00699             /* we're tracing through the volume between the camera 
00700              * and a solid surface, so use that pre-shaded radiance */
00701             copy_v4_v4(col, shr->combined);
00702         }
00703         
00704         /* shade volume from 'camera' to 1st hit point */
00705         volumeintegrate(shi, col, startco, endco);
00706     }
00707     /* trace to find a backface, the other side bounds of the volume */
00708     /* (ray intersect ignores front faces here) */
00709     else if (vol_get_bounds(shi, shi->co, shi->view, hitco, &is, VOL_BOUNDS_DEPTH))
00710     {
00711         VlakRen *vlr = (VlakRen *)is.hit.face;
00712         
00713         startco = shi->co;
00714         endco = hitco;
00715         
00716         if (!ztransp) {
00717             /* if it's another face in the same material */
00718             if (vlr->mat == shi->mat) {
00719                 /* trace behind the 2nd (raytrace) hit point */
00720                 vol_trace_behind(shi, (VlakRen *)is.hit.face, endco, col);
00721             } else {
00722                 shade_intersection(shi, col, &is);
00723             }
00724         }
00725         
00726         /* shade volume from 1st hit point to 2nd hit point */
00727         volumeintegrate(shi, col, startco, endco);
00728     }
00729     
00730     if (ztransp)
00731         col[3] = col[3]>1.f?1.f:col[3];
00732     else
00733         col[3] = 1.f;
00734     
00735     copy_v3_v3(shr->combined, col);
00736     shr->alpha = col[3];
00737     
00738     copy_v3_v3(shr->diff, shr->combined);
00739 }
00740 
00741 /* Traces a shadow through the object, 
00742  * pretty much gets the transmission over a ray path */
00743 void shade_volume_shadow(struct ShadeInput *shi, struct ShadeResult *shr, struct Isect *last_is)
00744 {
00745     float hitco[3];
00746     float tr[3] = {1.0,1.0,1.0};
00747     Isect is= {{0}};
00748     float *startco, *endco;
00749 
00750     memset(shr, 0, sizeof(ShadeResult));
00751     
00752     /* if 1st hit normal is facing away from the camera, 
00753      * then we're inside the volume already. */
00754     if (shi->flippednor) {
00755         startco = last_is->start;
00756         endco = shi->co;
00757     }
00758     
00759     /* trace to find a backface, the other side bounds of the volume */
00760     /* (ray intersect ignores front faces here) */
00761     else if (vol_get_bounds(shi, shi->co, shi->view, hitco, &is, VOL_BOUNDS_DEPTH)) {
00762         startco = shi->co;
00763         endco = hitco;
00764     }
00765     else {
00766         shr->combined[0] = shr->combined[1] = shr->combined[2] = 0.f;
00767         shr->alpha = shr->combined[3] = 1.f;
00768         return;
00769     }
00770 
00771     vol_get_transmittance(shi, tr, startco, endco);
00772 
00773     
00774     /* if we hit another face in the same volume bounds */
00775     /* shift raytrace coordinates to the hit point, to avoid shading volume twice */
00776     /* due to idiosyncracy in ray_trace_shadow_tra() */
00777     if (is.hit.ob == shi->obi) {
00778         copy_v3_v3(shi->co, hitco);
00779         last_is->dist -= is.dist;
00780         shi->vlr = (VlakRen *)is.hit.face;
00781     }
00782 
00783     
00784     copy_v3_v3(shr->combined, tr);
00785     shr->combined[3] = 1.0f - luminance(tr);
00786     shr->alpha = shr->combined[3];
00787 }
00788 
00789 
00790 /* delivers a fully filled in ShadeResult, for all passes */
00791 void shade_volume_outside(ShadeInput *shi, ShadeResult *shr)
00792 {
00793     memset(shr, 0, sizeof(ShadeResult));
00794     volume_trace(shi, shr, VOL_SHADE_OUTSIDE);
00795 }
00796 
00797 
00798 void shade_volume_inside(ShadeInput *shi, ShadeResult *shr)
00799 {
00800     MatInside *m;
00801     Material *mat_backup;
00802     ObjectInstanceRen *obi_backup;
00803     float prev_alpha = shr->alpha;
00804 
00805     /* XXX: extend to multiple volumes perhaps later */
00806     mat_backup = shi->mat;
00807     obi_backup = shi->obi;
00808     
00809     m = R.render_volumes_inside.first;
00810     shi->mat = m->ma;
00811     shi->obi = m->obi;
00812     shi->obr = m->obi->obr;
00813     
00814     volume_trace(shi, shr, VOL_SHADE_INSIDE);
00815     
00816     shr->alpha = shr->alpha + prev_alpha;
00817     CLAMP(shr->alpha, 0.0f, 1.0f);
00818 
00819     shi->mat = mat_backup;
00820     shi->obi = obi_backup;
00821     shi->obr = obi_backup->obr;
00822 }
00823 
00824