diff options
author | Matt Ebb <matt@mke3.net> | 2008-11-15 07:16:46 +0300 |
---|---|---|
committer | Matt Ebb <matt@mke3.net> | 2008-11-15 07:16:46 +0300 |
commit | e6a903c06e24ce9afd7b6e3de70e36fa450171d6 (patch) | |
tree | 9e44710f9651309b7bd3281d4630e3fc759997b9 /source/blender/render | |
parent | 8d5c14b20d61d990691ccaecefc3b5cbce449ef5 (diff) |
cleaned some code and split volume precaching into a new file
Diffstat (limited to 'source/blender/render')
-rw-r--r-- | source/blender/render/intern/include/volume_precache.h | 30 | ||||
-rw-r--r-- | source/blender/render/intern/include/volumetric.h | 17 | ||||
-rw-r--r-- | source/blender/render/intern/source/convertblender.c | 1 | ||||
-rw-r--r-- | source/blender/render/intern/source/volume_precache.c | 356 | ||||
-rw-r--r-- | source/blender/render/intern/source/volumetric.c | 566 |
5 files changed, 509 insertions, 461 deletions
diff --git a/source/blender/render/intern/include/volume_precache.h b/source/blender/render/intern/include/volume_precache.h new file mode 100644 index 00000000000..7a719840f48 --- /dev/null +++ b/source/blender/render/intern/include/volume_precache.h @@ -0,0 +1,30 @@ +/** + * + * ***** 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): Matt Ebb. + * + * ***** END GPL LICENSE BLOCK ***** + */ + +void volume_precache(Render *re); +void free_volume_precache(Render *re);
\ No newline at end of file diff --git a/source/blender/render/intern/include/volumetric.h b/source/blender/render/intern/include/volumetric.h index fb87035145f..a69238f54b3 100644 --- a/source/blender/render/intern/include/volumetric.h +++ b/source/blender/render/intern/include/volumetric.h @@ -21,12 +21,23 @@ * * The Original Code is: all of this file. * - * Contributor(s): Farsthary (Raul FHernandez), Matt Ebb. + * Contributor(s): Matt Ebb. * * ***** END GPL LICENSE BLOCK ***** */ +float vol_get_stepsize(struct ShadeInput *shi, int context); +float vol_get_density(struct ShadeInput *shi, float *co); +void vol_get_scattering(ShadeInput *shi, float *scatter, float *co, float stepsize, float density); + void volume_trace(struct ShadeInput *shi, struct ShadeResult *shr); void volume_trace_shadow(struct ShadeInput *shi, struct ShadeResult *shr, struct Isect *last_is); -void volume_precache(Render *re); -void free_volume_precache(Render *re);
\ No newline at end of file + +#define STEPSIZE_VIEW 0 +#define STEPSIZE_SHADE 1 + +#define VOL_IS_BACKFACE 1 +#define VOL_IS_SAMEMATERIAL 2 + +#define VOL_BOUNDS_DEPTH 0 +#define VOL_BOUNDS_SS 1
\ No newline at end of file diff --git a/source/blender/render/intern/source/convertblender.c b/source/blender/render/intern/source/convertblender.c index 45e934e5273..b9b151ab9fd 100644 --- a/source/blender/render/intern/source/convertblender.c +++ b/source/blender/render/intern/source/convertblender.c @@ -113,6 +113,7 @@ #include "shading.h" #include "strand.h" #include "texture.h" +#include "volume_precache.h" #include "sss.h" #include "strand.h" #include "zbuf.h" diff --git a/source/blender/render/intern/source/volume_precache.c b/source/blender/render/intern/source/volume_precache.c new file mode 100644 index 00000000000..5b43aa8f935 --- /dev/null +++ b/source/blender/render/intern/source/volume_precache.c @@ -0,0 +1,356 @@ +/** + * + * ***** 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): Matt Ebb. + * + * ***** END GPL LICENSE BLOCK ***** + */ + +#include <math.h> +#include <stdlib.h> +#include <string.h> +#include <float.h> + +#include "MEM_guardedalloc.h" + +#include "BLI_blenlib.h" +#include "BLI_arithb.h" + +#include "PIL_time.h" + +#include "RE_shader_ext.h" +#include "RE_raytrace.h" + +#include "DNA_material_types.h" + +#include "render_types.h" +#include "renderdatabase.h" +#include "volumetric.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; +/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ + + +/* Recursive test for intersections, from a point inside the mesh, to outside + * Number of intersections (depth) determine if a point is inside or outside the mesh */ +int intersect_outside_volume(RayTree *tree, Isect *isect, float *offset, int limit, int depth) +{ + if (limit == 0) return depth; + + if (RE_ray_tree_intersect(tree, isect)) { + float hitco[3]; + + hitco[0] = isect->start[0] + isect->labda*isect->vec[0]; + hitco[1] = isect->start[1] + isect->labda*isect->vec[1]; + hitco[2] = isect->start[2] + isect->labda*isect->vec[2]; + VecAddf(isect->start, hitco, offset); + + return intersect_outside_volume(tree, isect, offset, limit-1, depth+1); + } else { + return depth; + } +} + +/* Uses ray tracing to check if a point is inside or outside an ObjectInstanceRen */ +int point_inside_obi(RayTree *tree, ObjectInstanceRen *obi, float *co) +{ + float maxsize = RE_ray_tree_max_size(tree); + Isect isect; + float vec[3] = {0.0f,0.0f,1.0f}; + int final_depth=0, depth=0, limit=20; + + /* set up the isect */ + memset(&isect, 0, sizeof(isect)); + 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; + + /* and give it a little offset to prevent self-intersections */ + VecMulf(vec, 1e-5); + VecAddf(isect.start, isect.start, vec); + + isect.mode= RE_RAY_MIRROR; + isect.face_last= NULL; + isect.lay= -1; + + final_depth = intersect_outside_volume(tree, &isect, vec, limit, depth); + + /* even number of intersections: point is outside + * odd number: point is inside */ + if (final_depth % 2 == 0) return 0; + else return 1; +} + +static int inside_check_func(Isect *is, int ob, RayFace *face) +{ + return 1; +} +static void vlr_face_coords(RayFace *face, float **v1, float **v2, float **v3, float **v4) +{ + VlakRen *vlr= (VlakRen*)face; + + *v1 = (vlr->v1)? vlr->v1->co: NULL; + *v2 = (vlr->v2)? vlr->v2->co: NULL; + *v3 = (vlr->v3)? vlr->v3->co: NULL; + *v4 = (vlr->v4)? vlr->v4->co: NULL; +} + +RayTree *create_raytree_obi(ObjectInstanceRen *obi, float *bbmin, float *bbmax) +{ + int v; + VlakRen *vlr= NULL; + + /* create empty raytree */ + RayTree *tree = RE_ray_tree_create(64, obi->obr->totvlak, bbmin, bbmax, + vlr_face_coords, inside_check_func, NULL, NULL); + + /* fill it with faces */ + for(v=0; v<obi->obr->totvlak; v++) { + if((v & 255)==0) + vlr= obi->obr->vlaknodes[v>>8].vlak; + else + vlr++; + + RE_ray_tree_add_face(tree, 0, vlr); + } + + RE_ray_tree_done(tree); + + return tree; +} + +static float get_avg_surrounds(float *cache, int res, int res_2, int res_3, int rgb, int xx, int yy, int zz) +{ + int x, y, z, x_, y_, z_; + int added=0; + float tot=0.0f; + int i; + + for (x=-1; x <= 1; x++) { + x_ = xx+x; + if (x_ >= 0 && x_ <= res-1) { + + for (y=-1; y <= 1; y++) { + y_ = yy+y; + if (y_ >= 0 && y_ <= res-1) { + + for (z=-1; z <= 1; z++) { + z_ = zz+z; + if (z_ >= 0 && z_ <= res-1) { + + i = rgb*res_3 + x_*res_2 + y_*res + z_; + if (cache[i] > 0.0f) { + tot += cache[i]; + added++; + } + + } + } + } + } + } + } + + tot /= added; + + return ((added>0)?tot:0.0f); +} + +/* function to filter the edges of the light cache, where there was no volume originally. + * For each voxel which was originally external to the mesh, it finds the average values of + * the surrounding internal voxels and sets the original external voxel to that average amount. + * Works almost a bit like a 'dilate' filter */ +static void lightcache_filter(float *cache, int res) +{ + int x, y, z, rgb; + int res_2, res_3; + int i; + + res_2 = res*res; + res_3 = res*res*res; + + for (x=0; x < res; x++) { + for (y=0; y < res; y++) { + for (z=0; z < res; z++) { + for (rgb=0; rgb < 3; rgb++) { + i = rgb*res_3 + x*res_2 + y*res + z; + + /* trigger for outside mesh */ + if (cache[i] < 0.5f) cache[i] = get_avg_surrounds(cache, res, res_2, res_3, rgb, x, y, z); + } + } + } + } +} + +/* Precache a volume into a 3D voxel grid. + * The voxel grid is stored in the ObjectInstanceRen, + * in camera space, aligned with the ObjectRen's bounding box. + * Resolution is defined by the user. + */ +void vol_precache_objectinstance(Render *re, ObjectInstanceRen *obi, Material *ma, float *bbmin, float *bbmax) +{ + int x, y, z; + + float co[3], voxel[3], scatter_col[3]; + ShadeInput shi; + float view[3] = {0.0,0.0,-1.0}; + float density; + float stepsize; + + float resf, res_3f; + int res_2, res_3; + + float i = 1.0f; + double time, lasttime= PIL_check_seconds_timer(); + const int res = ma->vol_precache_resolution; + RayTree *tree; + + R = *re; + + /* create a raytree with just the faces of the instanced ObjectRen, + * used for checking if the cached point is inside or outside. */ + tree = create_raytree_obi(obi, bbmin, bbmax); + if (!tree) return; + + /* Need a shadeinput to calculate scattering */ + memset(&shi, 0, sizeof(ShadeInput)); + shi.depth= 1; + shi.mask= 1; + shi.mat = ma; + shi.vlr = NULL; + memcpy(&shi.r, &shi.mat->r, 23*sizeof(float)); // note, keep this synced with render_types.h + shi.har= shi.mat->har; + shi.obi= obi; + shi.obr= obi->obr; + shi.lay = re->scene->lay; + VECCOPY(shi.view, view); + + stepsize = vol_get_stepsize(&shi, STEPSIZE_VIEW); + + resf = (float)res; + res_2 = res*res; + res_3 = res*res*res; + res_3f = (float)res_3; + + VecSubf(voxel, bbmax, bbmin); + if ((voxel[0] < FLT_EPSILON) || (voxel[1] < FLT_EPSILON) || (voxel[2] < FLT_EPSILON)) + return; + VecMulf(voxel, 1.0f/res); + + obi->volume_precache = MEM_callocN(sizeof(float)*res_3*3, "volume light cache"); + + /* Iterate over the 3d voxel grid, and fill the voxels with scattering information + * + * It's stored in memory as 3 big float grids next to each other, one for each RGB channel. + * I'm guessing the memory alignment may work out better this way for the purposes + * of doing linear interpolation, but I haven't actually tested this theory! :) + */ + for (x=0; x < res; x++) { + co[0] = bbmin[0] + (voxel[0] * x); + + for (y=0; y < res; y++) { + co[1] = bbmin[1] + (voxel[1] * y); + + for (z=0; z < res; z++) { + co[2] = bbmin[2] + (voxel[2] * z); + + time= PIL_check_seconds_timer(); + i++; + + /* display progress every second */ + if(re->test_break()) { + if(tree) { + RE_ray_tree_free(tree); + tree= NULL; + } + return; + } + if(time-lasttime>1.0f) { + char str[64]; + sprintf(str, "Precaching volume: %d%%", (int)(100.0f * (i / res_3f))); + re->i.infostr= str; + re->stats_draw(&re->i); + re->i.infostr= NULL; + lasttime= time; + } + + /* don't bother if the point is not inside the volume mesh */ + if (!point_inside_obi(tree, obi, co)) { + obi->volume_precache[0*res_3 + x*res_2 + y*res + z] = -1.0f; + obi->volume_precache[1*res_3 + x*res_2 + y*res + z] = -1.0f; + obi->volume_precache[2*res_3 + x*res_2 + y*res + z] = -1.0f; + continue; + } + density = vol_get_density(&shi, co); + vol_get_scattering(&shi, scatter_col, co, stepsize, density); + + obi->volume_precache[0*res_3 + x*res_2 + y*res + z] = scatter_col[0]; + obi->volume_precache[1*res_3 + x*res_2 + y*res + z] = scatter_col[1]; + obi->volume_precache[2*res_3 + x*res_2 + y*res + z] = scatter_col[2]; + } + } + } + + if(tree) { + RE_ray_tree_free(tree); + tree= NULL; + } + + lightcache_filter(obi->volume_precache, res); + +} + +/* loop through all objects (and their associated materials) + * marked for pre-caching in convertblender.c, and pre-cache them */ +void volume_precache(Render *re) +{ + ObjectInstanceRen *obi; + VolPrecache *vp; + + for(vp= re->vol_precache_obs.first; vp; vp= vp->next) { + for(obi= re->instancetable.first; obi; obi= obi->next) { + if (obi->obr == vp->obr) + vol_precache_objectinstance(re, obi, vp->ma, obi->obr->boundbox[0], obi->obr->boundbox[1]); + } + } + + re->i.infostr= NULL; + re->stats_draw(&re->i); +} + +void free_volume_precache(Render *re) +{ + ObjectInstanceRen *obi; + + for(obi= re->instancetable.first; obi; obi= obi->next) { + if (obi->volume_precache) + MEM_freeN(obi->volume_precache); + } + + BLI_freelistN(&re->vol_precache_obs); +} diff --git a/source/blender/render/intern/source/volumetric.c b/source/blender/render/intern/source/volumetric.c index 83c25ad68c6..86705232b1e 100644 --- a/source/blender/render/intern/source/volumetric.c +++ b/source/blender/render/intern/source/volumetric.c @@ -27,8 +27,8 @@ */ #include <math.h> -#include <string.h> #include <stdlib.h> +#include <string.h> #include <float.h> #include "MEM_guardedalloc.h" @@ -36,9 +36,6 @@ #include "BLI_blenlib.h" #include "BLI_arithb.h" #include "BLI_rand.h" -#include "BLI_kdtree.h" - -#include "PIL_time.h" #include "RE_shader_ext.h" #include "RE_raytrace.h" @@ -48,13 +45,12 @@ #include "DNA_lamp_types.h" #include "BKE_global.h" -#include "BKE_main.h" #include "render_types.h" -#include "renderdatabase.h" #include "pixelshading.h" #include "shading.h" #include "texture.h" +#include "volumetric.h" #if defined( _MSC_VER ) && !defined( __cplusplus ) # define inline __inline @@ -75,29 +71,6 @@ static int vol_backface_intersect_check(Isect *is, int ob, RayFace *face) return (INPR(is->vec, vlr->n) < 0.0f); } -#if 0 -static int vol_frontface_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); -} - -static int vol_always_intersect_check(Isect *is, int ob, RayFace *face) -{ - return 1; -} -#endif - -#define VOL_IS_BACKFACE 1 -#define VOL_IS_SAMEMATERIAL 2 - - -#define VOL_BOUNDS_DEPTH 0 -#define VOL_BOUNDS_SS 1 - /* TODO: Box or sphere intersection types could speed things up */ static int vol_get_bounds(ShadeInput *shi, float *co, float *vec, float *hitco, Isect *isect, int intersect_type, int checkfunc) { @@ -139,8 +112,6 @@ static int vol_get_bounds(ShadeInput *shi, float *co, float *vec, float *hitco, } } -#define STEPSIZE_VIEW 0 -#define STEPSIZE_SHADE 1 float vol_get_stepsize(struct ShadeInput *shi, int context) { if (shi->mat->vol_stepsize_type == MA_VOL_STEP_RANDOMIZED) { @@ -152,8 +123,7 @@ float vol_get_stepsize(struct ShadeInput *shi, int context) else if (context == STEPSIZE_SHADE) return shi->mat->vol_shade_stepsize * rnd; } - else { - // MA_VOL_STEP_CONSTANT + else { // MA_VOL_STEP_CONSTANT if (context == STEPSIZE_VIEW) return shi->mat->vol_stepsize; @@ -164,6 +134,81 @@ float vol_get_stepsize(struct ShadeInput *shi, int context) return shi->mat->vol_stepsize; } +/* SHADING */ + +static float D(ShadeInput *shi, int rgb, int x, int y, int z) +{ + const int res = shi->mat->vol_precache_resolution; + CLAMP(x, 0, res-1); + CLAMP(y, 0, res-1); + CLAMP(z, 0, res-1); + return shi->obi->volume_precache[rgb*res*res*res + x*res*res + y*res + z]; +} + +inline float lerp(float t, float v1, float v2) { + return (1.f - t) * v1 + t * v2; +} + +/* trilinear interpolation */ +static void vol_get_precached_scattering(ShadeInput *shi, float *scatter_col, float *co) +{ + const int res = shi->mat->vol_precache_resolution; + float voxx, voxy, voxz; + int vx, vy, vz; + float dx, dy, dz; + float d00, d10, d01, d11, d0, d1, d_final; + float bbmin[3], bbmax[3], dim[3]; + int rgb; + + if (!shi->obi->volume_precache) return; + + VECCOPY(bbmin, shi->obi->obr->boundbox[0]); + VECCOPY(bbmax, shi->obi->obr->boundbox[1]); + VecSubf(dim, bbmax, bbmin); + + voxx = ((co[0] - bbmin[0]) / dim[0]) * res - 0.5f; + voxy = ((co[1] - bbmin[1]) / dim[1]) * res - 0.5f; + voxz = ((co[2] - bbmin[2]) / dim[2]) * res - 0.5f; + + vx = (int)voxx; vy = (int)voxy; vz = (int)voxz; + + dx = voxx - vx; dy = voxy - vy; dz = voxz - vz; + + for (rgb=0; rgb < 3; rgb++) { + d00 = lerp(dx, D(shi, rgb, vx, vy, vz), D(shi, rgb, vx+1, vy, vz)); + d10 = lerp(dx, D(shi, rgb, vx, vy+1, vz), D(shi, rgb, vx+1, vy+1, vz)); + d01 = lerp(dx, D(shi, rgb, vx, vy, vz+1), D(shi, rgb, vx+1, vy, vz+1)); + d11 = lerp(dx, D(shi, rgb, vx, vy+1, vz+1), D(shi, rgb, vx+1, vy+1, vz+1)); + d0 = lerp(dy, d00, d10); + d1 = lerp(dy, d01, d11); + d_final = lerp(dz, d0, d1); + + scatter_col[rgb] = d_final; + } +} + +/* no interpolation */ +static void vol_get_precached_scattering_nearest(ShadeInput *shi, float *scatter_col, float *co) +{ + const int res = shi->mat->vol_precache_resolution; + int x,y,z; + float bbmin[3], bbmax[3], dim[3]; + + if (!shi->obi->volume_precache) return; + + VECCOPY(bbmin, shi->obi->obr->boundbox[0]); + VECCOPY(bbmax, shi->obi->obr->boundbox[1]); + VecSubf(dim, bbmax, bbmin); + + x = (int)(((co[0] - bbmin[0]) / dim[0]) * res); + y = (int)(((co[1] - bbmin[1]) / dim[1]) * res); + z = (int)(((co[2] - bbmin[2]) / dim[2]) * res); + + scatter_col[0] = shi->obi->volume_precache[0*res*res*res + x*res*res + y*res + z]; + scatter_col[1] = shi->obi->volume_precache[1*res*res*res + x*res*res + y*res + z]; + scatter_col[2] = shi->obi->volume_precache[2*res*res*res + x*res*res + y*res + z]; +} + float vol_get_density(struct ShadeInput *shi, float *co) { float density = shi->mat->alpha; @@ -245,82 +290,6 @@ void vol_get_absorption(ShadeInput *shi, float *absorb_col, float *co) absorb_col[2] = (1.0f - absorb_col[2]) * absorption; } - -static float D(ShadeInput *shi, int rgb, int x, int y, int z) -{ - const int res = shi->mat->vol_precache_resolution; - CLAMP(x, 0, res-1); - CLAMP(y, 0, res-1); - CLAMP(z, 0, res-1); - return shi->obi->volume_precache[rgb*res*res*res + x*res*res + y*res + z]; -} - -inline float lerp(float t, float v1, float v2) { - return (1.f - t) * v1 + t * v2; -} - -/* trilinear interpolation */ -static void vol_get_precached_scattering(ShadeInput *shi, float *scatter_col, float *co) -{ - const int res = shi->mat->vol_precache_resolution; - float voxx, voxy, voxz; - int vx, vy, vz; - float dx, dy, dz; - float d00, d10, d01, d11, d0, d1, d_final; - float bbmin[3], bbmax[3], dim[3]; - int rgb; - - if (!shi->obi->volume_precache) return; - - VECCOPY(bbmin, shi->obi->obr->boundbox[0]); - VECCOPY(bbmax, shi->obi->obr->boundbox[1]); - VecSubf(dim, bbmax, bbmin); - - voxx = ((co[0] - bbmin[0]) / dim[0]) * res - 0.5f; - voxy = ((co[1] - bbmin[1]) / dim[1]) * res - 0.5f; - voxz = ((co[2] - bbmin[2]) / dim[2]) * res - 0.5f; - - vx = (int)voxx; vy = (int)voxy; vz = (int)voxz; - - dx = voxx - vx; dy = voxy - vy; dz = voxz - vz; - - for (rgb=0; rgb < 3; rgb++) { - d00 = lerp(dx, D(shi, rgb, vx, vy, vz), D(shi, rgb, vx+1, vy, vz)); - d10 = lerp(dx, D(shi, rgb, vx, vy+1, vz), D(shi, rgb, vx+1, vy+1, vz)); - d01 = lerp(dx, D(shi, rgb, vx, vy, vz+1), D(shi, rgb, vx+1, vy, vz+1)); - d11 = lerp(dx, D(shi, rgb, vx, vy+1, vz+1), D(shi, rgb, vx+1, vy+1, vz+1)); - d0 = lerp(dy, d00, d10); - d1 = lerp(dy, d01, d11); - d_final = lerp(dz, d0, d1); - - scatter_col[rgb] = d_final; - } -} - - -/* no interpolation */ -static void vol_get_precached_scattering_nearest(ShadeInput *shi, float *scatter_col, float *co) -{ - const int res = shi->mat->vol_precache_resolution; - int x,y,z; - float bbmin[3], bbmax[3], dim[3]; - - if (!shi->obi->volume_precache) return; - - VECCOPY(bbmin, shi->obi->obr->boundbox[0]); - VECCOPY(bbmax, shi->obi->obr->boundbox[1]); - VecSubf(dim, bbmax, bbmin); - - x = (int)(((co[0] - bbmin[0]) / dim[0]) * res); - y = (int)(((co[1] - bbmin[1]) / dim[1]) * res); - z = (int)(((co[2] - bbmin[2]) / dim[2]) * res); - - scatter_col[0] = shi->obi->volume_precache[0*res*res*res + x*res*res + y*res + z]; - scatter_col[1] = shi->obi->volume_precache[1*res*res*res + x*res*res + y*res + z]; - scatter_col[2] = shi->obi->volume_precache[2*res*res*res + x*res*res + y*res + z]; -} - - /* Compute attenuation, otherwise known as 'optical thickness', extinction, or tau. * Used in the relationship Transmittance = e^(-attenuation) */ @@ -353,7 +322,7 @@ void vol_get_attenuation(ShadeInput *shi, float *tau, float *co, float *endco, f VecSubf(step_vec, endco, co); VecMulf(step_vec, 1.0f / nsteps); - VECCOPY(step_sta, co); + VecCopyf(step_sta, co); VecAddf(step_end, step_sta, step_vec); for (s = 0; s < nsteps; s++) { @@ -469,17 +438,33 @@ void vol_get_scattering(ShadeInput *shi, float *scatter, float *co, float stepsi VECCOPY(scatter, col); } + +/* +The main volumetric integrator, using an emission/absorption/scattering model. + +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) + transmittance = exp(-tau) + +-- To find radiance from segments along the way: + find radiance for one step: + - loop over lights and weight by phase function +*/ static void volumeintegrate(struct ShadeInput *shi, float *col, float *co, float *endco) { - float tr[3] = {1.0f, 1.0f, 1.0f}; /* total transmittance */ + float tr[3] = {1.0f, 1.0f, 1.0f}; float radiance[3] = {0.f, 0.f, 0.f}, d_radiance[3] = {0.f, 0.f, 0.f}; float stepsize = vol_get_stepsize(shi, STEPSIZE_VIEW); - int nsteps; - float vec[3], stepvec[3] = {0.0, 0.0, 0.0}; + int nsteps, s; float tau[3], emit_col[3], scatter_col[3] = {0.0, 0.0, 0.0}; - int s; - float step_sta[3], step_end[3], step_mid[3]; - float alpha; + float stepvec[3], step_sta[3], step_end[3], step_mid[3]; float density = vol_get_density(shi, co); /* multiply col_behind with beam transmittance over entire distance */ @@ -493,11 +478,9 @@ static void volumeintegrate(struct ShadeInput *shi, float *col, float *co, float /* ray marching */ nsteps = (int)((VecLenf(co, endco) / stepsize) + 0.5); - VecSubf(vec, endco, co); - VECCOPY(stepvec, vec); + VecSubf(stepvec, endco, co); VecMulf(stepvec, 1.0f / nsteps); - - VECCOPY(step_sta, co); + VecCopyf(step_sta, co); VecAddf(step_end, step_sta, stepvec); /* get radiance from all points along the ray due to participating media */ @@ -505,8 +488,7 @@ static void volumeintegrate(struct ShadeInput *shi, float *col, float *co, float if (s > 0) density = vol_get_density(shi, step_sta); - /* there's only any use in shading here - * if there's actually some density to shade! */ + /* there's only any use in shading here if there's actually some density to shade! */ if (density > 0.01f) { /* transmittance component (alpha) */ @@ -524,10 +506,7 @@ static void volumeintegrate(struct ShadeInput *shi, float *col, float *co, float if ((shi->mat->vol_shadeflag & MA_VOL_PRECACHESHADING) && (shi->mat->vol_shadeflag & MA_VOL_ATTENUATED)) { - if (G.rt==100) - vol_get_precached_scattering_nearest(shi, scatter_col, step_mid); - else - vol_get_precached_scattering(shi, scatter_col, step_mid); + vol_get_precached_scattering(shi, scatter_col, step_mid); } else vol_get_scattering(shi, scatter_col, step_mid, stepsize, density); @@ -541,36 +520,12 @@ static void volumeintegrate(struct ShadeInput *shi, float *col, float *co, float VecAddf(radiance, radiance, d_radiance); } - VECCOPY(step_sta, step_end); + VecCopyf(step_sta, step_end); VecAddf(step_end, step_end, stepvec); } - col[0] = radiance[0]; - col[1] = radiance[1]; - col[2] = radiance[2]; - - alpha = 1.0f -(tr[0] + tr[1] + tr[2]) * 0.333f; - col[3] = alpha; - - /* - 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 - */ + VecCopyf(col, radiance); + col[3] = 1.0f -(tr[0] + tr[1] + tr[2]) * 0.333f; } static void shade_intersection(ShadeInput *shi, float *col, Isect *is) @@ -759,308 +714,3 @@ void volume_trace_shadow(struct ShadeInput *shi, struct ShadeResult *shr, struct } } - -/* Recursive test for intersections, from a point inside the mesh, to outside - * Number of intersections (depth) determine if a point is inside or outside the mesh */ -int intersect_outside_volume(RayTree *tree, Isect *isect, float *offset, int limit, int depth) -{ - if (limit == 0) return depth; - - if (RE_ray_tree_intersect(tree, isect)) { - float hitco[3]; - - hitco[0] = isect->start[0] + isect->labda*isect->vec[0]; - hitco[1] = isect->start[1] + isect->labda*isect->vec[1]; - hitco[2] = isect->start[2] + isect->labda*isect->vec[2]; - VecAddf(isect->start, hitco, offset); - - return intersect_outside_volume(tree, isect, offset, limit-1, depth+1); - } else { - return depth; - } -} - -/* Uses ray tracing to check if a point is inside or outside an ObjectInstanceRen */ -int point_inside_obi(RayTree *tree, ObjectInstanceRen *obi, float *co) -{ - float maxsize = RE_ray_tree_max_size(tree); - Isect isect; - float vec[3] = {0.0f,0.0f,1.0f}; - int final_depth=0, depth=0, limit=20; - - /* set up the isect */ - memset(&isect, 0, sizeof(isect)); - 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; - - /* and give it a little offset to prevent self-intersections */ - VecMulf(vec, 1e-5); - VecAddf(isect.start, isect.start, vec); - - isect.mode= RE_RAY_MIRROR; - isect.face_last= NULL; - isect.lay= -1; - - final_depth = intersect_outside_volume(tree, &isect, vec, limit, depth); - - /* even number of intersections: point is outside - * odd number: point is inside */ - if (final_depth % 2 == 0) return 0; - else return 1; -} - -static int inside_check_func(Isect *is, int ob, RayFace *face) -{ - return 1; -} -static void vlr_face_coords(RayFace *face, float **v1, float **v2, float **v3, float **v4) -{ - VlakRen *vlr= (VlakRen*)face; - - *v1 = (vlr->v1)? vlr->v1->co: NULL; - *v2 = (vlr->v2)? vlr->v2->co: NULL; - *v3 = (vlr->v3)? vlr->v3->co: NULL; - *v4 = (vlr->v4)? vlr->v4->co: NULL; -} - -RayTree *create_raytree_obi(ObjectInstanceRen *obi, float *bbmin, float *bbmax) -{ - int v; - VlakRen *vlr= NULL; - - /* create empty raytree */ - RayTree *tree = RE_ray_tree_create(64, obi->obr->totvlak, bbmin, bbmax, - vlr_face_coords, inside_check_func, NULL, NULL); - - /* fill it with faces */ - for(v=0; v<obi->obr->totvlak; v++) { - if((v & 255)==0) - vlr= obi->obr->vlaknodes[v>>8].vlak; - else - vlr++; - - RE_ray_tree_add_face(tree, 0, vlr); - } - - RE_ray_tree_done(tree); - - return tree; -} - -static float get_avg_surrounds(float *cache, int res, int res_2, int res_3, int rgb, int xx, int yy, int zz) -{ - int x, y, z, x_, y_, z_; - int added=0; - float tot=0.0f; - int i; - - for (x=-1; x <= 1; x++) { - x_ = xx+x; - if (x_ >= 0 && x_ <= res-1) { - - for (y=-1; y <= 1; y++) { - y_ = yy+y; - if (y_ >= 0 && y_ <= res-1) { - - for (z=-1; z <= 1; z++) { - z_ = zz+z; - if (z_ >= 0 && z_ <= res-1) { - - i = rgb*res_3 + x_*res_2 + y_*res + z_; - if (cache[i] > 0.0f) { - tot += cache[i]; - added++; - } - - } - } - } - } - } - } - - tot /= added; - - return ((added>0)?tot:0.0f); -} - -/* function to filter the edges of the light cache, where there was no volume originally. - * For each voxel which was originally external to the mesh, it finds the average values of - * the surrounding internal voxels and sets the original external voxel to that average amount. - * Works almost a bit like a 'dilate' filter */ -static void lightcache_filter(float *cache, int res) -{ - int x, y, z, rgb; - int res_2, res_3; - int i; - - res_2 = res*res; - res_3 = res*res*res; - - for (x=0; x < res; x++) { - for (y=0; y < res; y++) { - for (z=0; z < res; z++) { - for (rgb=0; rgb < 3; rgb++) { - i = rgb*res_3 + x*res_2 + y*res + z; - - /* trigger for outside mesh */ - if (cache[i] < 0.5f) cache[i] = get_avg_surrounds(cache, res, res_2, res_3, rgb, x, y, z); - } - } - } - } -} - -/* Precache a volume into a 3D voxel grid. - * The voxel grid is stored in the ObjectInstanceRen, - * in camera space, aligned with the ObjectRen's bounding box. - * Resolution is defined by the user. - */ -void vol_precache_objectinstance(Render *re, ObjectInstanceRen *obi, Material *ma, float *bbmin, float *bbmax) -{ - int x, y, z; - - float co[3], voxel[3], scatter_col[3]; - ShadeInput shi; - float view[3] = {0.0,0.0,-1.0}; - float density; - float stepsize; - - float resf, res_3f; - int res_2, res_3; - - float i = 1.0f; - double time, lasttime= PIL_check_seconds_timer(); - const int res = ma->vol_precache_resolution; - RayTree *tree; - - R = *re; - - /* create a raytree with just the faces of the instanced ObjectRen, - * used for checking if the cached point is inside or outside. */ - tree = create_raytree_obi(obi, bbmin, bbmax); - if (!tree) return; - - /* Need a shadeinput to calculate scattering */ - memset(&shi, 0, sizeof(ShadeInput)); - shi.depth= 1; - shi.mask= 1; - shi.mat = ma; - shi.vlr = NULL; - memcpy(&shi.r, &shi.mat->r, 23*sizeof(float)); // note, keep this synced with render_types.h - shi.har= shi.mat->har; - shi.obi= obi; - shi.obr= obi->obr; - shi.lay = re->scene->lay; - VECCOPY(shi.view, view); - - stepsize = vol_get_stepsize(&shi, STEPSIZE_VIEW); - - resf = (float)res; - res_2 = res*res; - res_3 = res*res*res; - res_3f = (float)res_3; - - VecSubf(voxel, bbmax, bbmin); - if ((voxel[0] < FLT_EPSILON) || (voxel[1] < FLT_EPSILON) || (voxel[2] < FLT_EPSILON)) - return; - VecMulf(voxel, 1.0f/res); - - obi->volume_precache = MEM_callocN(sizeof(float)*res_3*3, "volume light cache"); - - /* Iterate over the 3d voxel grid, and fill the voxels with scattering information - * - * It's stored in memory as 3 big float grids next to each other, one for each RGB channel. - * I'm guessing the memory alignment may work out better this way for the purposes - * of doing linear interpolation, but I haven't actually tested this theory! :) - */ - for (x=0; x < res; x++) { - co[0] = bbmin[0] + (voxel[0] * x); - - for (y=0; y < res; y++) { - co[1] = bbmin[1] + (voxel[1] * y); - - for (z=0; z < res; z++) { - co[2] = bbmin[2] + (voxel[2] * z); - - time= PIL_check_seconds_timer(); - i++; - - /* display progress every second */ - if(re->test_break()) { - if(tree) { - RE_ray_tree_free(tree); - tree= NULL; - } - return; - } - if(time-lasttime>1.0f) { - char str[64]; - sprintf(str, "Precaching volume: %d%%", (int)(100.0f * (i / res_3f))); - re->i.infostr= str; - re->stats_draw(&re->i); - re->i.infostr= NULL; - lasttime= time; - } - - /* don't bother if the point is not inside the volume mesh */ - if (!point_inside_obi(tree, obi, co)) { - obi->volume_precache[0*res_3 + x*res_2 + y*res + z] = -1.0f; - obi->volume_precache[1*res_3 + x*res_2 + y*res + z] = -1.0f; - obi->volume_precache[2*res_3 + x*res_2 + y*res + z] = -1.0f; - continue; - } - - VECCOPY(shi.view, co); - Normalize(shi.view); - density = vol_get_density(&shi, co); - vol_get_scattering(&shi, scatter_col, co, stepsize, density); - - obi->volume_precache[0*res_3 + x*res_2 + y*res + z] = scatter_col[0]; - obi->volume_precache[1*res_3 + x*res_2 + y*res + z] = scatter_col[1]; - obi->volume_precache[2*res_3 + x*res_2 + y*res + z] = scatter_col[2]; - } - } - } - - if(tree) { - RE_ray_tree_free(tree); - tree= NULL; - } - - lightcache_filter(obi->volume_precache, res); - -} - -/* loop through all objects (and their associated materials) - * marked for pre-caching in convertblender.c, and pre-cache them */ -void volume_precache(Render *re) -{ - ObjectInstanceRen *obi; - VolPrecache *vp; - - for(vp= re->vol_precache_obs.first; vp; vp= vp->next) { - for(obi= re->instancetable.first; obi; obi= obi->next) { - if (obi->obr == vp->obr) - vol_precache_objectinstance(re, obi, vp->ma, obi->obr->boundbox[0], obi->obr->boundbox[1]); - } - } - - re->i.infostr= NULL; - re->stats_draw(&re->i); -} - -void free_volume_precache(Render *re) -{ - ObjectInstanceRen *obi; - - for(obi= re->instancetable.first; obi; obi= obi->next) { - if (obi->volume_precache) - MEM_freeN(obi->volume_precache); - } - - BLI_freelistN(&re->vol_precache_obs); -} - |