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