static float get_avg_surrounds(float *cache, int *res, int xx, int yy, int zz) { int x, y, z, x_, y_, z_; int added=0; float tot=0.0f; for (z=-1; z <= 1; z++) { z_ = zz+z; if (z_ >= 0 && z_ <= res[2]-1) { for (y=-1; y <= 1; y++) { y_ = yy+y; if (y_ >= 0 && y_ <= res[1]-1) { for (x=-1; x <= 1; x++) { x_ = xx+x; if (x_ >= 0 && x_ <= res[0]-1) { if (cache[ V_I(x_, y_, z_, res) ] > 0.0f) { tot += cache[ V_I(x_, y_, z_, res) ]; added++; } } } } } } } if (added > 0) tot /= added; return tot; }
vec_t mat_vmult(const mat_t *a, vec_t b){ vec_t r; int i = 4; while(i--){ V_I(r,i) = vec_wdot(mat_get_row(i,a),b); } return r; }
static void load_frame_image_sequence(VoxelData *vd, Tex *tex) { ImBuf *ibuf; Image *ima = tex->ima; ImageUser *tiuser = &tex->iuser; ImageUser iuser = *(tiuser); int x=0, y=0, z=0; float *rf; if (!ima || !tiuser) return; if (iuser.frames == 0) return; ima->source = IMA_SRC_SEQUENCE; iuser.framenr = 1 + iuser.offset; /* find the first valid ibuf and use it to initialise the resolution of the data set */ /* need to do this in advance so we know how much memory to allocate */ ibuf= BKE_image_get_ibuf(ima, &iuser); while (!ibuf && (iuser.framenr < iuser.frames)) { iuser.framenr++; ibuf= BKE_image_get_ibuf(ima, &iuser); } if (!ibuf) return; if (!ibuf->rect_float) IMB_float_from_rect(ibuf); vd->flag |= TEX_VD_STILL; vd->resol[0] = ibuf->x; vd->resol[1] = ibuf->y; vd->resol[2] = iuser.frames; vd->dataset = MEM_mapallocN(sizeof(float)*vd_resol_size(vd), "voxel dataset"); for (z=0; z < iuser.frames; z++) { /* get a new ibuf for each frame */ if (z > 0) { iuser.framenr++; ibuf= BKE_image_get_ibuf(ima, &iuser); if (!ibuf) break; if (!ibuf->rect_float) IMB_float_from_rect(ibuf); } rf = ibuf->rect_float; for (y=0; y < ibuf->y; y++) { for (x=0; x < ibuf->x; x++) { /* currently averaged to monchrome */ vd->dataset[ V_I(x, y, z, vd->resol) ] = (rf[0] + rf[1] + rf[2])*0.333f; rf +=4; } } BKE_image_free_anim_ibufs(ima, iuser.framenr); } vd->ok = 1; return; }
/* get the total amount of light energy in the light cache. used to normalise after multiple scattering */ static float total_ss_energy(VolumePrecache *vp) { int x, y, z; int *res = vp->res; float energy=0.f; for (z=0; z < res[2]; z++) { for (y=0; y < res[1]; y++) { for (x=0; x < res[0]; x++) { if (vp->data_r[ V_I(x, y, z, res) ] > 0.f) energy += vp->data_r[ V_I(x, y, z, res) ]; if (vp->data_g[ V_I(x, y, z, res) ] > 0.f) energy += vp->data_g[ V_I(x, y, z, res) ]; if (vp->data_b[ V_I(x, y, z, res) ] > 0.f) energy += vp->data_b[ V_I(x, y, z, res) ]; } } } return energy; }
/* 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(VolumePrecache *vp) { int x, y, z; for (z=0; z < vp->res[2]; z++) { for (y=0; y < vp->res[1]; y++) { for (x=0; x < vp->res[0]; x++) { /* trigger for outside mesh */ if (vp->data_r[ V_I(x, y, z, vp->res) ] < -0.f) vp->data_r[ V_I(x, y, z, vp->res) ] = get_avg_surrounds(vp->data_r, vp->res, x, y, z); if (vp->data_g[ V_I(x, y, z, vp->res) ] < -0.f) vp->data_g[ V_I(x, y, z, vp->res) ] = get_avg_surrounds(vp->data_g, vp->res, x, y, z); if (vp->data_b[ V_I(x, y, z, vp->res) ] < -0.f) vp->data_b[ V_I(x, y, z, vp->res) ] = get_avg_surrounds(vp->data_b, vp->res, x, y, z); } } } }
/* 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! :) */ static void *vol_precache_part(void *data) { VolPrecachePart *pa = (VolPrecachePart *)data; ObjectInstanceRen *obi = pa->obi; RayObject *tree = pa->tree; ShadeInput *shi = pa->shi; float scatter_col[3] = {0.f, 0.f, 0.f}; float co[3]; int x, y, z; const int res[3]= {pa->res[0], pa->res[1], pa->res[2]}; for (z= pa->minz; z < pa->maxz; z++) { co[2] = pa->bbmin[2] + (pa->voxel[2] * (z + 0.5f)); for (y= pa->miny; y < pa->maxy; y++) { co[1] = pa->bbmin[1] + (pa->voxel[1] * (y + 0.5f)); for (x=pa->minx; x < pa->maxx; x++) { co[0] = pa->bbmin[0] + (pa->voxel[0] * (x + 0.5f)); // don't bother if the point is not inside the volume mesh if (!point_inside_obi(tree, obi, co)) { obi->volume_precache->data_r[ V_I(x, y, z, res) ] = -1.0f; obi->volume_precache->data_g[ V_I(x, y, z, res) ] = -1.0f; obi->volume_precache->data_b[ V_I(x, y, z, res) ] = -1.0f; continue; } VecCopyf(shi->view, co); Normalize(shi->view); vol_get_scattering(shi, scatter_col, co); obi->volume_precache->data_r[ V_I(x, y, z, res) ] = scatter_col[0]; obi->volume_precache->data_g[ V_I(x, y, z, res) ] = scatter_col[1]; obi->volume_precache->data_b[ V_I(x, y, z, res) ] = scatter_col[2]; } } } pa->done = 1; return 0; }
static void lightcache_filter2(VolumePrecache *vp) { int x, y, z; float *new_r, *new_g, *new_b; int field_size = vp->res[0]*vp->res[1]*vp->res[2]*sizeof(float); new_r = MEM_mallocN(field_size, "temp buffer for light cache filter r channel"); new_g = MEM_mallocN(field_size, "temp buffer for light cache filter g channel"); new_b = MEM_mallocN(field_size, "temp buffer for light cache filter b channel"); memcpy(new_r, vp->data_r, field_size); memcpy(new_g, vp->data_g, field_size); memcpy(new_b, vp->data_b, field_size); for (z=0; z < vp->res[2]; z++) { for (y=0; y < vp->res[1]; y++) { for (x=0; x < vp->res[0]; x++) { /* trigger for outside mesh */ if (vp->data_r[ V_I(x, y, z, vp->res) ] < -0.f) new_r[ V_I(x, y, z, vp->res) ] = get_avg_surrounds(vp->data_r, vp->res, x, y, z); if (vp->data_g[ V_I(x, y, z, vp->res) ] < -0.f) new_g[ V_I(x, y, z, vp->res) ] = get_avg_surrounds(vp->data_g, vp->res, x, y, z); if (vp->data_b[ V_I(x, y, z, vp->res) ] < -0.f) new_b[ V_I(x, y, z, vp->res) ] = get_avg_surrounds(vp->data_b, vp->res, x, y, z); } } } SWAP(float *, vp->data_r, new_r); SWAP(float *, vp->data_g, new_g); SWAP(float *, vp->data_b, new_b); if (new_r) { MEM_freeN(new_r); new_r=NULL; } if (new_g) { MEM_freeN(new_g); new_g=NULL; } if (new_b) { MEM_freeN(new_b); new_b=NULL; } }
void multiple_scattering_diffusion(Render *re, VolumePrecache *vp, Material *ma) { const float diff = ma->vol.ms_diff * 0.001f; /* compensate for scaling for a nicer UI range */ const float simframes = ma->vol.ms_steps; const int shade_type = ma->vol.shade_type; float fac = ma->vol.ms_intensity; int x, y, z, m; int *n = vp->res; const int size = (n[0]+2)*(n[1]+2)*(n[2]+2); double time, lasttime= PIL_check_seconds_timer(); float total; float c=1.0f; int i; float origf; /* factor for blending in original light cache */ float energy_ss, energy_ms; float *sr0=(float *)MEM_callocN(size*sizeof(float), "temporary multiple scattering buffer"); float *sr=(float *)MEM_callocN(size*sizeof(float), "temporary multiple scattering buffer"); float *sg0=(float *)MEM_callocN(size*sizeof(float), "temporary multiple scattering buffer"); float *sg=(float *)MEM_callocN(size*sizeof(float), "temporary multiple scattering buffer"); float *sb0=(float *)MEM_callocN(size*sizeof(float), "temporary multiple scattering buffer"); float *sb=(float *)MEM_callocN(size*sizeof(float), "temporary multiple scattering buffer"); total = (float)(n[0]*n[1]*n[2]*simframes); energy_ss = total_ss_energy(vp); /* Scattering as diffusion pass */ for (m=0; m<simframes; m++) { /* add sources */ for (z=1; z<=n[2]; z++) { for (y=1; y<=n[1]; y++) { for (x=1; x<=n[0]; x++) { i = V_I((x-1), (y-1), (z-1), n); time= PIL_check_seconds_timer(); c++; if (vp->data_r[i] > 0.f) sr[ms_I(x,y,z,n)] += vp->data_r[i]; if (vp->data_g[i] > 0.f) sg[ms_I(x,y,z,n)] += vp->data_g[i]; if (vp->data_b[i] > 0.f) sb[ms_I(x,y,z,n)] += vp->data_b[i]; /* Displays progress every second */ if(time-lasttime>1.0f) { char str[64]; sprintf(str, "Simulating multiple scattering: %d%%", (int) (100.0f * (c / total))); re->i.infostr= str; re->stats_draw(re->sdh, &re->i); re->i.infostr= NULL; lasttime= time; } } } } SWAP(float *, sr, sr0); SWAP(float *, sg, sg0); SWAP(float *, sb, sb0); /* main diffusion simulation */ ms_diffuse(0, sr0, sr, diff, n); ms_diffuse(0, sg0, sg, diff, n); ms_diffuse(0, sb0, sb, diff, n); if (re->test_break(re->tbh)) break; } /* normalisation factor to conserve energy */ energy_ms = total_ms_energy(sr, sg, sb, n); fac *= (energy_ss / energy_ms); /* blend multiple scattering back in the light cache */ if (shade_type == MA_VOL_SHADE_SHADEDPLUSMULTIPLE) { /* conserve energy - half single, half multiple */ origf = 0.5f; fac *= 0.5f; } else { origf = 0.0f; } for (z=1;z<=n[2];z++) { for (y=1;y<=n[1];y++) { for (x=1;x<=n[0];x++) { int index=(x-1)*n[1]*n[2] + (y-1)*n[2] + z-1; vp->data_r[index] = origf * vp->data_r[index] + fac * sr[ms_I(x,y,z,n)]; vp->data_g[index] = origf * vp->data_g[index] + fac * sg[ms_I(x,y,z,n)]; vp->data_b[index] = origf * vp->data_b[index] + fac * sb[ms_I(x,y,z,n)]; } } } MEM_freeN(sr0); MEM_freeN(sr); MEM_freeN(sg0); MEM_freeN(sg); MEM_freeN(sb0); MEM_freeN(sb); }