diff options
author | Matt Ebb <matt@mke3.net> | 2009-08-23 06:54:30 +0400 |
---|---|---|
committer | Matt Ebb <matt@mke3.net> | 2009-08-23 06:54:30 +0400 |
commit | 46aac7b4fc5456daf8faabd7e6f2e65832c96268 (patch) | |
tree | 556d6b1af238350da1f2ca237bda09aab045cc7e /source/blender/render | |
parent | 8df1d6d13af4509fbf1378e26e400c282c8a0fde (diff) |
* Volume rendering - z transparency
This solves one of the last remaining hurdles for
volume rendering. Previously it always used ray
tracing to shade other objects inside or behind the
volume. This meant that said objects would look
aliased, unless you used Full OSA on the volume
(which is slow!). As well as this, it meant that you didn't
get a good alpha channel out of the volume to use for
compositing, similar to ray refracting materials.
This commit enables z transparency for volume
materials. Although it can be potentially less
physically correct, in most situations there's no
difference, and you get the benefit of nice sampling for
other objects and an alpha channel for compositing too.
Diffstat (limited to 'source/blender/render')
-rw-r--r-- | source/blender/render/intern/source/convertblender.c | 7 | ||||
-rw-r--r-- | source/blender/render/intern/source/shadeinput.c | 10 | ||||
-rw-r--r-- | source/blender/render/intern/source/volumetric.c | 314 |
3 files changed, 172 insertions, 159 deletions
diff --git a/source/blender/render/intern/source/convertblender.c b/source/blender/render/intern/source/convertblender.c index f91e48ea287..e486daf2585 100644 --- a/source/blender/render/intern/source/convertblender.c +++ b/source/blender/render/intern/source/convertblender.c @@ -920,6 +920,7 @@ static Material *give_render_material(Render *re, Object *ob, int nr) if(re->r.mode & R_SPEED) ma->texco |= NEED_UV; + if(ma->material_type == MA_TYPE_VOLUME) ma->mode |= MA_TRANSP; if((ma->mode & MA_TRANSP) && (ma->mode & MA_ZTRANSP)) re->flag |= R_ZTRA; @@ -3005,13 +3006,12 @@ static void init_camera_inside_volumes(Render *re) } } - { + /* debug { MatInside *m; for (m=re->render_volumes_inside.first; m; m=m->next) { printf("matinside: ma: %s \n", m->ma->id.name+2); } - - } + }*/ } static void add_volume(Render *re, ObjectRen *obr, Material *ma) @@ -3862,6 +3862,7 @@ static void set_fullsample_flag(Render *re, ObjectRen *obr) vlr->flag |= R_FULL_OSA; else if(trace) { if(mode & MA_SHLESS); + else if(vlr->mat->material_type == MA_TYPE_VOLUME); else if((mode & MA_RAYMIRROR) || ((mode & MA_TRANSP) && (mode & MA_RAYTRANSP))) /* for blurry reflect/refract, better to take more samples * inside the raytrace than as OSA samples */ diff --git a/source/blender/render/intern/source/shadeinput.c b/source/blender/render/intern/source/shadeinput.c index 1a97440cec3..7887392f1d2 100644 --- a/source/blender/render/intern/source/shadeinput.c +++ b/source/blender/render/intern/source/shadeinput.c @@ -216,15 +216,17 @@ void shade_input_do_shade(ShadeInput *shi, ShadeResult *shr) } else alpha= 1.0f; + /* add mist and premul color */ if(shr->alpha!=1.0f || alpha!=1.0f) { float fac= alpha*(shr->alpha); shr->combined[3]= fac; - shr->combined[0]*= fac; - shr->combined[1]*= fac; - shr->combined[2]*= fac; + + if (shi->mat->material_type!= MA_TYPE_VOLUME) + VecMulf(shr->combined, fac); } - else shr->combined[3]= 1.0f; + else + shr->combined[3]= 1.0f; /* add z */ shr->z= -shi->co[2]; diff --git a/source/blender/render/intern/source/volumetric.c b/source/blender/render/intern/source/volumetric.c index 7943d9ae526..cf66d580944 100644 --- a/source/blender/render/intern/source/volumetric.c +++ b/source/blender/render/intern/source/volumetric.c @@ -65,12 +65,20 @@ extern struct Render R; /* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ -/* TODO: Box or sphere intersection types could speed things up */ +/* tracing */ + static int vol_get_bounds(ShadeInput *shi, float *co, float *vec, float *hitco, Isect *isect, int intersect_type) { float maxsize = RE_ray_tree_max_size(R.raytree); - - /* TODO: use object's bounding box to calculate max size */ + + /* XXX TODO - get raytrace max distance from object instance's bounding box */ + /* need to account for scaling only, but keep coords in camera space... + * below code is WIP and doesn't work! + VecSubf(bb_dim, shi->obi->obr->boundbox[1], shi->obi->obr->boundbox[2]); + Mat3MulVecfl(shi->obi->nmat, bb_dim); + maxsize = VecLength(bb_dim); + */ + VECCOPY(isect->start, co); isect->end[0] = co[0] + vec[0] * maxsize; isect->end[1] = co[1] + vec[1] * maxsize; @@ -96,6 +104,68 @@ static int vol_get_bounds(ShadeInput *shi, float *co, float *vec, float *hitco, } } +static void shade_intersection(ShadeInput *shi, float *col, Isect *is) +{ + ShadeInput shi_new; + ShadeResult shr_new; + + memset(&shi_new, 0, sizeof(ShadeInput)); + + shi_new.mask= shi->mask; + shi_new.osatex= shi->osatex; + shi_new.thread= shi->thread; + shi_new.depth = shi->depth + 1; + shi_new.volume_depth= shi->volume_depth + 1; + shi_new.xs= shi->xs; + shi_new.ys= shi->ys; + shi_new.lay= shi->lay; + shi_new.passflag= SCE_PASS_COMBINED; /* result of tracing needs no pass info */ + shi_new.combinedflag= 0xFFFFFF; /* ray trace does all options */ + shi_new.light_override= shi->light_override; + shi_new.mat_override= shi->mat_override; + + VECCOPY(shi_new.camera_co, is->start); + + memset(&shr_new, 0, sizeof(ShadeResult)); + + /* hardcoded limit of 100 for now - prevents problems in weird geometry */ + if (shi->volume_depth < 100) { + shade_ray(is, &shi_new, &shr_new); + } + + VecCopyf(col, shr_new.combined); + col[3] = shr_new.alpha; +} + +static void vol_trace_behind(ShadeInput *shi, VlakRen *vlr, float *co, float *col) +{ + Isect isect; + float maxsize = RE_ray_tree_max_size(R.raytree); + + VECCOPY(isect.start, co); + isect.end[0] = isect.start[0] + shi->view[0] * maxsize; + isect.end[1] = isect.start[1] + shi->view[1] * maxsize; + isect.end[2] = isect.start[2] + shi->view[2] * maxsize; + + isect.faceorig= (RayFace *)vlr; + + isect.mode= RE_RAY_MIRROR; + isect.oborig= RAY_OBJECT_SET(&R, shi->obi); + isect.face_last= NULL; + isect.ob_last= 0; + isect.lay= -1; + + /* check to see if there's anything behind the volume, otherwise shade the sky */ + if(RE_ray_tree_intersect(R.raytree, &isect)) { + shade_intersection(shi, col, &isect); + } else { + shadeSkyView(col, co, shi->view, NULL, shi->thread); + shadeSunView(col, shi->view); + } +} + +/* input shader data */ + float vol_get_stepsize(struct ShadeInput *shi, int context) { if (shi->mat->vol.stepsize_type == MA_VOL_STEP_RANDOMIZED) { @@ -280,7 +350,7 @@ void vol_get_attenuation(ShadeInput *shi, float *transmission, float *co, float tau[2] += stepsize * density; if (s < nsteps-1) { - VECCOPY(step_sta, step_end); + VecCopyf(step_sta, step_end); VecAddf(step_end, step_end, step_vec); } } @@ -319,9 +389,6 @@ void vol_shade_one_lamp(struct ShadeInput *shi, float *co, LampRen *lar, float * VECCOPY(lv, lar->vec); VecMulf(lv, -1.0f); - p = vol_get_phasefunc(shi, shi->mat->vol.phasefunc_type, shi->mat->vol.phasefunc_g, shi->view, lv); - VecMulf(lacol, p); - if (shi->mat->vol.shade_type != MA_VOL_SHADE_NONE) { Isect is; @@ -337,6 +404,7 @@ void vol_shade_one_lamp(struct ShadeInput *shi, float *co, LampRen *lar, float * } if (ELEM(lar->type, LA_SUN, LA_HEMI)) + /* infinite lights, can never be inside volume */ atten_co = hitco; else if ( lampdist < dist ) { atten_co = lar->co; @@ -354,6 +422,9 @@ void vol_shade_one_lamp(struct ShadeInput *shi, float *co, LampRen *lar, float * } } + p = vol_get_phasefunc(shi, shi->mat->vol.phasefunc_type, shi->mat->vol.phasefunc_g, shi->view, lv); + VecMulf(lacol, p); + scatter_fac = vol_get_scattering_fac(shi, co); VecMulf(lacol, scatter_fac); } @@ -390,14 +461,6 @@ 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) { @@ -462,150 +525,102 @@ static void volumeintegrate(struct ShadeInput *shi, float *col, float *co, float VecMulVecf(col, tr, col); VecAddf(col, col, radiance); - /* alpha - transmission */ - col[3] = 1.0f -(tr[0] + tr[1] + tr[2]) * 0.333f; -} - -static void shade_intersection(ShadeInput *shi, float *col, Isect *is) -{ - ShadeInput shi_new; - ShadeResult shr_new; - - memset(&shi_new, 0, sizeof(ShadeInput)); - - shi_new.mask= shi->mask; - shi_new.osatex= shi->osatex; - shi_new.thread= shi->thread; - shi_new.depth = shi->depth + 1; - shi_new.volume_depth= shi->volume_depth + 1; - shi_new.xs= shi->xs; - shi_new.ys= shi->ys; - shi_new.lay= shi->lay; - shi_new.passflag= SCE_PASS_COMBINED; /* result of tracing needs no pass info */ - shi_new.combinedflag= 0xFFFFFF; /* ray trace does all options */ - shi_new.light_override= shi->light_override; - shi_new.mat_override= shi->mat_override; - - VECCOPY(shi_new.camera_co, is->start); - - memset(&shr_new, 0, sizeof(ShadeResult)); - - /* hardcoded limit of 100 for now - prevents problems in weird geometry */ - if (shi->volume_depth < 100) { - shade_ray(is, &shi_new, &shr_new); - } - - col[0] = shr_new.combined[0]; - col[1] = shr_new.combined[1]; - col[2] = shr_new.combined[2]; - col[3] = shr_new.alpha; -} - -static void vol_trace_behind(ShadeInput *shi, VlakRen *vlr, float *co, float *col) -{ - Isect isect; - float maxsize = RE_ray_tree_max_size(R.raytree); - - VECCOPY(isect.start, co); - isect.end[0] = isect.start[0] + shi->view[0] * maxsize; - isect.end[1] = isect.start[1] + shi->view[1] * maxsize; - isect.end[2] = isect.start[2] + shi->view[2] * maxsize; - - isect.faceorig= (RayFace *)vlr; - - isect.mode= RE_RAY_MIRROR; - isect.oborig= RAY_OBJECT_SET(&R, shi->obi); - isect.face_last= NULL; - isect.ob_last= 0; - isect.lay= -1; - - /* check to see if there's anything behind the volume, otherwise shade the sky */ - if(RE_ray_tree_intersect(R.raytree, &isect)) { - shade_intersection(shi, col, &isect); - } else { - shadeSkyView(col, co, shi->view, NULL, shi->thread); - shadeSunView(col, shi->view); - } + /* alpha <-- transmission luminance */ + col[3] = 1.0f -(0.2126*tr[0] + 0.7152*tr[1] + 0.0722*tr[2]); } /* the main entry point for volume shading */ static void volume_trace(struct ShadeInput *shi, struct ShadeResult *shr, int inside_volume) { float hitco[3], col[4] = {0.f,0.f,0.f,0.f}; + float *startco, *endco; int trace_behind = 1; + const int ztransp= ((shi->depth==0) && (shi->mat->mode & MA_TRANSP) && (shi->mat->mode & MA_ZTRANSP)); Isect is; /* check for shading an internal face a volume object directly */ - if (inside_volume == VOL_SHADE_INSIDE) { + if (inside_volume == VOL_SHADE_INSIDE) trace_behind = 0; - } - if (inside_volume == VOL_SHADE_OUTSIDE) { + else if (inside_volume == VOL_SHADE_OUTSIDE) { if (shi->flippednor) inside_volume = VOL_SHADE_INSIDE; } - - if (inside_volume == VOL_SHADE_INSIDE) { - - if (trace_behind) { - /* trace behind the volume object */ - vol_trace_behind(shi, shi->vlr, shi->co, col); - } else { - /* we're tracing through the volume between the camera - * and a solid surface, so use that pre-shaded radiance */ - QUATCOPY(col, shr->combined); - } + + if (ztransp && inside_volume == VOL_SHADE_INSIDE) { + MatInside *mi; + int render_this=0; - /* shade volume from 'camera' to 1st hit point */ - volumeintegrate(shi, col, shi->camera_co, shi->co); + /* don't render the backfaces of ztransp volume materials. + + * volume shading renders the internal volume from between the + * near view intersection of the solid volume to the + * intersection on the other side, as part of the shading of + * the front face. + + * Because ztransp renders both front and back faces independently + * this will double up, so here we prevent rendering the backface as well, + * which would otherwise render the volume in between the camera and the backface + * --matt */ - VecCopyf(shr->combined, col); + for (mi=R.render_volumes_inside.first; mi; mi=mi->next) { + /* weak... */ + if (mi->ma == shi->mat) render_this=1; + } + if (!render_this) return; + } + + + if (inside_volume == VOL_SHADE_INSIDE) + { + startco = shi->camera_co; + endco = shi->co; - if (shi->mat->vol.shadeflag & MA_VOL_USEALPHA) { - if (col[3] > 1.0f) - col[3] = 1.0f; + if (!ztransp) { + if (trace_behind) { + /* trace behind the volume object */ + vol_trace_behind(shi, shi->vlr, endco, col); + } else { + /* we're tracing through the volume between the camera + * and a solid surface, so use that pre-shaded radiance */ + QUATCOPY(col, shr->combined); + } } - else - col[3] = 1.0f; - shr->combined[3] = col[3]; - shr->alpha = col[3]; - VECCOPY(shr->diff, shr->combined); + /* shade volume from 'camera' to 1st hit point */ + volumeintegrate(shi, col, startco, endco); } /* trace to find a backface, the other side bounds of the volume */ /* (ray intersect ignores front faces here) */ - else if (vol_get_bounds(shi, shi->co, shi->view, hitco, &is, VOL_BOUNDS_DEPTH)) { + else if (vol_get_bounds(shi, shi->co, shi->view, hitco, &is, VOL_BOUNDS_DEPTH)) + { VlakRen *vlr = (VlakRen *)is.face; - /* if it's another face in the same material */ - if (vlr->mat == shi->mat) { - /* trace behind the 2nd (raytrace) hit point */ - vol_trace_behind(shi, (VlakRen *)is.face, hitco, col); - } else { - shade_intersection(shi, col, &is); - } - - /* shade volume from 1st hit point to 2nd hit point */ - volumeintegrate(shi, col, shi->co, hitco); - - VecCopyf(shr->combined, col); + startco = shi->co; + endco = hitco; - if (shi->mat->vol.shadeflag & MA_VOL_USEALPHA) { - if (col[3] > 1.0f) - col[3] = 1.0f; + if (!ztransp) { + /* if it's another face in the same material */ + if (vlr->mat == shi->mat) { + /* trace behind the 2nd (raytrace) hit point */ + vol_trace_behind(shi, (VlakRen *)is.face, endco, col); + } else { + shade_intersection(shi, col, &is); + } } - else - col[3] = 1.0f; - shr->combined[3] = col[3]; - shr->alpha = col[3]; - VECCOPY(shr->diff, shr->combined); - } - else { - shr->combined[0] = 0.0f; - shr->combined[1] = 0.0f; - shr->combined[2] = 0.0f; - shr->combined[3] = shr->alpha = 1.0f; + /* shade volume from 1st hit point to 2nd hit point */ + volumeintegrate(shi, col, startco, endco); } + + if (ztransp) + col[3] = col[3]>1.f?1.f:col[3]; + else + col[3] = 1.f; + + VecCopyf(shr->combined, col); + shr->alpha = col[3]; + + VECCOPY(shr->diff, shr->combined); } /* Traces a shadow through the object, @@ -614,41 +629,37 @@ void shade_volume_shadow(struct ShadeInput *shi, struct ShadeResult *shr, struct { float hitco[3]; float tr[3] = {1.0,1.0,1.0}; - float tau[3] = {0.0,0.0,0.0}; Isect is; float shade_stepsize = vol_get_stepsize(shi, STEPSIZE_SHADE); + float *startco, *endco; + float density=0.f; memset(shr, 0, sizeof(ShadeResult)); /* if 1st hit normal is facing away from the camera, * then we're inside the volume already. */ if (shi->flippednor) { - - vol_get_attenuation(shi, tr, last_is->start, shi->co, -1.0f, shade_stepsize); - - VecCopyf(shr->combined, tr); - - shr->combined[3] = 1.0f -(tr[0] + tr[1] + tr[2]) * 0.333f; - shr->alpha = shr->combined[3]; + startco = last_is->start; + endco = shi->co; } /* trace to find a backface, the other side bounds of the volume */ /* (ray intersect ignores front faces here) */ else if (vol_get_bounds(shi, shi->co, shi->view, hitco, &is, VOL_BOUNDS_DEPTH)) { - - vol_get_attenuation(shi, tr, shi->co, hitco, -1.0f, shade_stepsize); - - VecCopyf(shr->combined, tr); - - shr->combined[3] = 1.0f -(tr[0] + tr[1] + tr[2]) * 0.333f; - shr->alpha = shr->combined[3]; - + startco = shi->co; + endco = hitco; } else { - shr->combined[0] = 0.0f; - shr->combined[1] = 0.0f; - shr->combined[2] = 0.0f; - shr->combined[3] = shr->alpha = 0.0f; + shr->combined[0] = shr->combined[1] = shr->combined[2] = 0.f; + shr->alpha = shr->combined[3] = 1.f; + return; } + + density = vol_get_density(shi, startco); + vol_get_attenuation(shi, tr, startco, endco, density, shade_stepsize); + + VecCopyf(shr->combined, tr); + shr->combined[3] = 1.0f -(0.2126*tr[0] + 0.7152*tr[1] + 0.0722*tr[2]); + shr->alpha = shr->combined[3]; } @@ -656,7 +667,6 @@ void shade_volume_shadow(struct ShadeInput *shi, struct ShadeResult *shr, struct void shade_volume_outside(ShadeInput *shi, ShadeResult *shr) { memset(shr, 0, sizeof(ShadeResult)); - volume_trace(shi, shr, VOL_SHADE_OUTSIDE); } |