[Bf-blender-cvs] SVN commit: /data/svn/bf-blender [15204] branches/soc-2008-unclezeiv/source /blender/render/intern/source/lightcuts.c: Long story short: added material error bound and statistics.
Davide Vercelli
davide.vercelli at gmail.com
Thu Jun 12 01:53:47 CEST 2008
Revision: 15204
http://projects.blender.org/plugins/scmsvn/viewcvs.php?view=rev&root=bf-blender&revision=15204
Author: unclezeiv
Date: 2008-06-12 01:53:42 +0200 (Thu, 12 Jun 2008)
Log Message:
-----------
Long story short: added material error bound and statistics.
In detail:
- bugfix: don't consider negative light contributions (my own version of shade_one_light was missing the check); this led to artifacts every now and then
- finally added calculation for material error bound: this lowers the number of lights taken into account and thus the rendering times, but currently introduces more visible error w.r.t. a "vanilla" rendering; this could be a bug or could have other causes, I plan to commit more on this soon and to discuss it in detail on my blog. Please also note that this still works only with lambertian diffuse shading.
- don't shoot rays if the computed error bound is 0: this potentially cuts entire subtrees of lights laying behind the shaded sample
- don't put leaf nodes into the cut queue (minor optimization)
- prints statistics during deallocation (average cut size is particularly interesting)
- added some functions not strictly related to Lightcuts:
- function to obtain world-to-tangent-space matrix from ShadeInput's normal and position; unlike other shading-related cases, here we need also the translation part
- function to transform AABBs (from a few quick greps it looks like it wasn't there already, at least not cleanly exposed)
- function to compute maximum possible cosine between z+ and all vectors contained in a particular AABB
- updated/changed/clarified comments
Modified Paths:
--------------
branches/soc-2008-unclezeiv/source/blender/render/intern/source/lightcuts.c
Modified: branches/soc-2008-unclezeiv/source/blender/render/intern/source/lightcuts.c
===================================================================
--- branches/soc-2008-unclezeiv/source/blender/render/intern/source/lightcuts.c 2008-06-11 22:53:52 UTC (rev 15203)
+++ branches/soc-2008-unclezeiv/source/blender/render/intern/source/lightcuts.c 2008-06-11 23:53:42 UTC (rev 15204)
@@ -105,6 +105,9 @@
short dbg_first_pixel;
short dbg_jolly;
#endif
+ int stat_samples;
+ int stat_cut_size;
+ int stat_rays_shot;
} LightcutsData;
#define VEC_LEN_SQ(v) (v[0]*v[0] + v[1]*v[1] + v[2]*v[2])
@@ -112,6 +115,7 @@
#define LC_LUMINOSITY(c) (0.299*(c)[0] + 0.587*(c)[1] + 0.114*(c)[2])
/* #define LC_LUMINOSITY(c) (0.316*c[0] + 0.460*c[1] + 0.224*c[2]) */
#define VECCOPYMUL(v1,v2,aS) {*(v1)= *(v2)*aS; *(v1+1)= *(v2+1)*aS; *(v1+2)= *(v2+2)*aS;}
+#define IS_LEAF(node) (!(node)->child1 && !(node)->child2)
static float lightcuts_compute_metric(LightcutsCluster * one, LightcutsCluster * two)
{
@@ -333,7 +337,7 @@
dbg_check_tree(lcd->array_local, lcd->root_local);
/* dbg_print_tree(lcd->array_local, lcd->root_local, 0); */
#endif
- printf("Lightcuts tree built: %d\n", lcd->root_local);
+ printf("Lightcuts tree built. %d nodes used.\n", lcd->root_local);
MEM_freeN(pair_array);
}
@@ -493,8 +497,81 @@
lcd->cut_nodes= MEM_callocN(sizeof(CutNode) * lcd->cut_nodes_size * re->r.threads, "cut_nodes");
}
+/* Adapted from "Transforming Axis-Aligned Bounding Boxes" by Jim Arvo, "Graphics Gems", Academic Press, 1990 */
+static void transform_aabb(float tsm[4][4], float min[3], float max[3], float rmin[3], float rmax[3])
+{
+ int i, j;
+ float a, b;
+
+ for (i = 0; i < 3; i++) {
+ /* translation */
+ rmin[i]= rmax[i]= tsm[i][3];
+
+ for (j = 0; j < 3; j++) {
+ a= tsm[i][j] * min[j];
+ b= tsm[i][j] * max[j];
+ if (a < b) {
+ rmin[i]+= a;
+ rmax[i]+= b;
+ }
+ else {
+ rmin[i]+= b;
+ rmax[i]+= a;
+ }
+ }
+ }
+}
+
+/* compute the maximum possible cosine between vector z+ and any point in the axis aligned bounding box */
+static float compute_cosine_bound(float *min, float *max)
+{
+ float xsq, ysq;
+ if (max[2] > 0.0) {
+ /* min(x^2): if it includes 0.0, is 0.0 */
+ xsq= (min[0]*max[0] < 0.0) ? 0.0 : MIN2(min[0]*min[0], max[0]*max[0]);
+ ysq= (min[1]*max[1] < 0.0) ? 0.0 : MIN2(min[1]*min[1], max[1]*max[1]);
+ }
+ else {
+ xsq= MAX2(min[0]*min[0], max[0]*max[0]);
+ ysq= MAX2(min[1]*min[1], max[1]*max[1]);
+ }
+ return max[2] / sqrt(xsq + ysq + max[2]*max[2]);
+}
+
+/* fill in tangent space matrix tsm given normal and position; please note, it's also a translation matrix */
+static void get_tangent_space_matrix(float tsm[4][4], float *nor, float *pos)
+{
+ float normat[4][4];
+
+ /* warning: we fill it transposed as it's easier */
+ /* XXX: shi->vn seems to point inside the mesh, investigate; works inverted like this */
+ normat[2][0] = -nor[0];
+ normat[2][1] = -nor[1];
+ normat[2][2] = -nor[2];
+
+ VecOrthoBasisf(normat[2], normat[0], normat[1]);
+
+ VECCOPY(normat[3], pos);
+
+ normat[0][3]= normat[1][3]= normat[2][3]= 0.0f;
+ normat[3][3]= 1.0f;
+
+ Mat4Transp(normat);
+ /* now we have a proper tangent-to-world matrix: let's invert it */
+ Mat4Invert(tsm, normat);
+}
+
+/* error bound: material term */
+/* TODO: currently supporting only diffuse */
+static float calc_material_eb(float tsm[4][4], LightcutsCluster *clus)
+{
+ float tmin[3], tmax[3];
+ transform_aabb(tsm, clus->min, clus->max, tmin, tmax);
+ return MAX2(compute_cosine_bound(tmin, tmax), 0.0);
+}
+
/* error bound: geometric term */
-/* TODO: right now is working only for point lights */
+/* TODO: right now is working only for omnidirectional (LA_LOCAL) lights */
static float calc_geometric_eb(LightcutsData *lcd, int id, float *pos)
{
/* find the nearest point in the bounding box */
@@ -529,6 +606,7 @@
int it= 0;
int used= 0;
float i, i_noshad;
+ float tsm[4][4]; /* tangent space matrix */
/* this heap maintaines the current cut */
/* TODO: its maximum size is known in advance, this allows to
@@ -545,6 +623,8 @@
float dbg_totlum = 0.0f;
#endif
+
+ get_tangent_space_matrix(tsm, shi->vn, shi->co);
cut_nodes= lcd->cut_nodes + shi->thread * lcd->cut_nodes_size;
@@ -553,29 +633,36 @@
LightcutsCluster *clus= &lcd->array_local[lcd->root_local];
root->id= lcd->root_local;
- root->error_bound= calc_geometric_eb(lcd, lcd->root_local, shi->co);
+ root->error_bound= calc_geometric_eb(lcd, lcd->root_local, shi->co) * calc_material_eb(tsm, clus) * shi->refl;
#ifdef LIGHTCUTS_DEBUG
dbg_convert[lcd->root_local]= 0;
#endif
+ if (root->error_bound > FLT_EPSILON) {
+ get_contrib(clus->lar, shi, &i, &i_noshad);
+ lcd->stat_rays_shot++;
+ root->contr_factor= MAX2(i, 0.0f);
+ root->f_clus= root->contr_factor * clus->intensity;
+ VECADDFAC(totest, totest, clus->col, root->f_clus);
- get_contrib(clus->lar, shi, &i, &i_noshad);
- root->contr_factor= i;
- root->f_clus= i * clus->intensity;
- VECADDFAC(totest, totest, clus->col, root->f_clus);
-
- if(shi->passflag & (SCE_PASS_DIFFUSE|SCE_PASS_SHADOW)) {
- root->contr_factor_noshad= i_noshad;
- root->f_clus_noshad= i_noshad * clus->intensity;
- VECADDFAC(totest_noshad, totest_noshad, clus->col, root->f_clus_noshad);
- }
+ if(shi->passflag & (SCE_PASS_DIFFUSE|SCE_PASS_SHADOW)) {
+ root->contr_factor_noshad= MAX2(i_noshad, 0.0f);
+ root->f_clus_noshad= root->contr_factor_noshad * clus->intensity;
+ VECADDFAC(totest_noshad, totest_noshad, clus->col, root->f_clus_noshad);
+ }
#ifdef LIGHTCUTS_DEBUG
- dbg_totlum= root->contr_factor * clus->luminance;
+ dbg_totlum= root->contr_factor * clus->luminance;
#endif
- BLI_heap_insert(cut, -root->error_bound * clus->luminance, root);
+ if (!IS_LEAF(clus))
+ BLI_heap_insert(cut, -root->error_bound * clus->luminance, root);
+ else
+ used++;
+ }
+ else
+ used++;
}
- /* at each iteration the heap grows by one, but we have a maximum size */
+ /* at each iteration the heap may grow by one, but we have a maximum size */
while (BLI_heap_size(cut) < (lcd->max_lights - used) && BLI_heap_size(cut) > 0) {
LightcutsCluster *hinode;
CutNode *cn_hinode;
@@ -583,22 +670,27 @@
it++;
cn_hinode= BLI_heap_popmin(cut);
+#ifdef LIGHTCUTS_DEBUG
if (cn_hinode < cut_nodes || cn_hinode >= (cut_nodes + lcd->cut_nodes_size)) {
printf("tricky node! at %d, %d (it=%d)\n", shi->xs, shi->ys, it);
break;
}
- if (cn_hinode->error_bound < cn_hinode->contr_factor) {
- printf("troublesome node! at %d, %d (it=%d): eb %7.5f < cf %7.5f\n",
- shi->xs, shi->ys, it, cn_hinode->error_bound, cn_hinode->contr_factor);
+#endif
+ hinode= &lcd->array_local[cn_hinode->id];
+#ifdef LIGHTCUTS_DEBUG
+ if (cn_hinode->error_bound + FLT_EPSILON < cn_hinode->contr_factor) {
+ printf("troublesome node! at %d, %d (it=%d): eb %7.5f < cf %7.5f, (%10.6f * 10^6)\n",
+ shi->xs, shi->ys, it, cn_hinode->error_bound, cn_hinode->contr_factor, 1000000.0f * fabs(cn_hinode->error_bound - cn_hinode->contr_factor));
break;
}
- hinode= &lcd->array_local[cn_hinode->id];
- if (hinode->child1==0 && hinode->child2==0) {
+ if (IS_LEAF(hinode)) {
/* can't go further down */
used++;
- continue;
+ printf("Leaf node in queue: this should never happen\n");
+ break;
}
+#endif
if (LC_LUMINOSITY(totest) * lcd->error_rate > cn_hinode->error_bound * hinode->luminance) {
break;
@@ -634,28 +726,45 @@
/* for the reprsented light we can reuse most calculations */
cn_rep->id= rep->id;
- cn_rep->error_bound= calc_geometric_eb(lcd, rep->id, shi->co);
- cn_rep->contr_factor= cn_hinode->contr_factor;
- cn_rep->f_clus= cn_hinode->contr_factor * rep->intensity;
- VECADDFAC(totest, totest, rep->col, cn_rep->f_clus);
+ cn_rep->error_bound= calc_geometric_eb(lcd, rep->id, shi->co) * calc_material_eb(tsm, rep) * shi->refl;
+
+ if (cn_rep->error_bound > FLT_EPSILON) {
+ cn_rep->contr_factor= cn_hinode->contr_factor;
+ cn_rep->f_clus= cn_hinode->contr_factor * rep->intensity;
+ VECADDFAC(totest, totest, rep->col, cn_rep->f_clus);
#ifdef LIGHTCUTS_DEBUG
- dbg_totlum+= rep->luminance * cn_rep->contr_factor;
+ dbg_totlum+= rep->luminance * cn_rep->contr_factor;
#endif
-
- BLI_heap_insert(cut, -cn_rep->error_bound * rep->luminance, cn_rep);
+
+ if (!IS_LEAF(rep))
+ BLI_heap_insert(cut, -cn_rep->error_bound * rep->luminance, cn_rep);
+ else
+ used++;
+ }
+ else
+ used++;
/* for the "unrepresented" light we have to compute stuff from scratch */
cn_unrep->id= unrep->id;
- cn_unrep->error_bound= calc_geometric_eb(lcd, unrep->id, shi->co);
- get_contrib(unrep->lar, shi, &i, &i_noshad);
- cn_unrep->contr_factor= i;
- cn_unrep->f_clus= i * unrep->intensity;
- VECADDFAC(totest, totest, unrep->col, cn_unrep->f_clus);
+ cn_unrep->error_bound= calc_geometric_eb(lcd, unrep->id, shi->co) * calc_material_eb(tsm, unrep) * shi->refl;
+
+ if (cn_unrep->error_bound > FLT_EPSILON) {
+ get_contrib(unrep->lar, shi, &i, &i_noshad);
+ lcd->stat_rays_shot++;
+ cn_unrep->contr_factor= MAX2(i, 0.0f);
+ cn_unrep->f_clus= cn_unrep->contr_factor * unrep->intensity;
+ VECADDFAC(totest, totest, unrep->col, cn_unrep->f_clus);
#ifdef LIGHTCUTS_DEBUG
- dbg_totlum+= unrep->luminance * cn_unrep->contr_factor;
+ dbg_totlum+= unrep->luminance * cn_unrep->contr_factor;
#endif
- BLI_heap_insert(cut, -cn_unrep->error_bound * unrep->luminance, cn_unrep);
+ if (!IS_LEAF(unrep))
+ BLI_heap_insert(cut, -cn_unrep->error_bound * unrep->luminance, cn_unrep);
+ else
+ used++;
+ }
+ else
+ used++;
if(shi->passflag & (SCE_PASS_DIFFUSE|SCE_PASS_SHADOW)) {
VECADDFAC(totest_noshad, totest_noshad, rep->col, -cn_hinode->f_clus_noshad);
@@ -664,8 +773,8 @@
cn_rep->f_clus_noshad= cn_hinode->contr_factor_noshad * rep->intensity;
VECADDFAC(totest_noshad, totest_noshad, rep->col, cn_rep->f_clus_noshad);
- cn_unrep->contr_factor_noshad= i_noshad;
- cn_unrep->f_clus_noshad= i_noshad * unrep->intensity;
+ cn_unrep->contr_factor_noshad= MAX2(i_noshad, 0.0f);
+ cn_unrep->f_clus_noshad= cn_unrep->contr_factor_noshad * unrep->intensity;
VECADDFAC(totest_noshad, totest_noshad, unrep->col, cn_unrep->f_clus_noshad);
}
}
@@ -695,13 +804,33 @@
shr->faux[3]= (float)((used + BLI_heap_size(cut)) > 0.0);
}
@@ Diff output truncated at 10240 characters. @@
More information about the Bf-blender-cvs
mailing list