[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