/* For ease of use, I've also introduced a 'reflection' and 'reflection color' parameter, which isn't * physically correct. This works as an RGB tint/gain on out-scattered light, but doesn't affect the light * that is transmitted through the volume. While having wavelength dependent absorption/scattering is more correct, * it also makes it harder to control the overall look of the volume since coloring the outscattered light results * in the inverse color being transmitted through the rest of the volume. */ static void volumeintegrate(struct ShadeInput *shi, float *col, float *co, float *endco) { float radiance[3] = {0.f, 0.f, 0.f}; float tr[3] = {1.f, 1.f, 1.f}; float p[3] = {co[0], co[1], co[2]}; float step_vec[3] = {endco[0] - co[0], endco[1] - co[1], endco[2] - co[2]}; const float stepsize = shi->mat->vol.stepsize; float t0 = 0.f; float pt0 = t0; float t1 = normalize_v3(step_vec); /* returns vector length */ t0 += stepsize * ((shi->mat->vol.stepsize_type == MA_VOL_STEP_CONSTANT) ? 0.5f : BLI_thread_frand(shi->thread)); p[0] += t0 * step_vec[0]; p[1] += t0 * step_vec[1]; p[2] += t0 * step_vec[2]; mul_v3_fl(step_vec, stepsize); for (; t0 < t1; pt0 = t0, t0 += stepsize) { const float density = vol_get_density(shi, p); if (density > 0.00001f) { float scatter_col[3] = {0.f, 0.f, 0.f}, emit_col[3]; const float stepd = (t0 - pt0) * density; /* transmittance component (alpha) */ vol_get_transmittance_seg(shi, tr, stepsize, co, density); if (t0 > t1 * 0.25f) { /* only use depth cutoff after we've traced a little way into the volume */ if (luminance(tr) < shi->mat->vol.depth_cutoff) break; } vol_get_emission(shi, emit_col, p); if (shi->obi->volume_precache) { float p2[3]; p2[0] = p[0] + (step_vec[0] * 0.5f); p2[1] = p[1] + (step_vec[1] * 0.5f); p2[2] = p[2] + (step_vec[2] * 0.5f); vol_get_precached_scattering(&R, shi, scatter_col, p2); } else vol_get_scattering(shi, scatter_col, p); radiance[0] += stepd * tr[0] * (emit_col[0] + scatter_col[0]); radiance[1] += stepd * tr[1] * (emit_col[1] + scatter_col[1]); radiance[2] += stepd * tr[2] * (emit_col[2] + scatter_col[2]); } add_v3_v3(p, step_vec); } /* multiply original color (from behind volume) with transmittance over entire distance */ mul_v3_v3v3(col, tr, col); add_v3_v3(col, radiance); /* alpha <-- transmission luminance */ col[3] = 1.0f - luminance(tr); }
/* 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; }