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. @@

_______________________________________________
Bf-blender-cvs mailing list
[email protected]
http://lists.blender.org/mailman/listinfo/bf-blender-cvs

Reply via email to