Blender  V2.59
sss.c
Go to the documentation of this file.
00001 /* 
00002  * $Id: sss.c 35233 2011-02-27 19:31:27Z jesterking $
00003  *
00004  * ***** BEGIN GPL LICENSE BLOCK *****
00005  *
00006  * This program is free software; you can redistribute it and/or
00007  * modify it under the terms of the GNU General Public License
00008  * as published by the Free Software Foundation; either version 2
00009  * of the License, or (at your option) any later version.
00010  *
00011  * This program is distributed in the hope that it will be useful,
00012  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00013  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00014  * GNU General Public License for more details.
00015  *
00016  * You should have received a copy of the GNU General Public License
00017  * along with this program; if not, write to the Free Software Foundation,
00018  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
00019  *
00020  * The Original Code is Copyright (C) 2007 Blender Foundation.
00021  * All rights reserved.
00022  *
00023  * The Original Code is: all of this file.
00024  *
00025  * Contributor(s): none yet.
00026  *
00027  * ***** END GPL LICENSE BLOCK *****
00028  */
00029 
00035 /* Possible Improvements:
00036    - add fresnel terms
00037    - adapt Rd table to scale, now with small scale there are a lot of misses?
00038    - possible interesting method: perform sss on all samples in the tree,
00039          and then use those values interpolated somehow later. can also do this
00040          filtering on demand for speed. since we are doing things in screen
00041          space now there is an exact correspondence
00042    - avoid duplicate shading (filtering points in advance, irradiance cache
00043          like lookup?)
00044    - lower resolution samples
00045 */
00046 
00047 #include <math.h>
00048 #include <string.h>
00049 #include <stdio.h>
00050 #include <string.h>
00051 
00052 /* external modules: */
00053 #include "MEM_guardedalloc.h"
00054 
00055 #include "BLI_math.h"
00056 #include "BLI_blenlib.h"
00057 #include "BLI_utildefines.h"
00058 #include "BLI_ghash.h"
00059 #include "BLI_memarena.h"
00060 
00061 #include "PIL_time.h"
00062 
00063 #include "DNA_material_types.h"
00064 
00065 #include "BKE_colortools.h"
00066 #include "BKE_global.h"
00067 #include "BKE_main.h"
00068 #include "BKE_material.h"
00069 #include "BKE_node.h"
00070 #include "BKE_scene.h"
00071 
00072 
00073 /* this module */
00074 #include "render_types.h"
00075 #include "rendercore.h"
00076 #include "renderdatabase.h" 
00077 #include "shading.h"
00078 #include "sss.h"
00079 #include "zbuf.h"
00080 
00081 extern Render R; // meh
00082 
00083 /* Generic Multiple Scattering API */
00084 
00085 /* Relevant papers:
00086         [1] A Practical Model for Subsurface Light Transport
00087         [2] A Rapid Hierarchical Rendering Technique for Translucent Materials
00088         [3] Efficient Rendering of Local Subsurface Scattering
00089         [4] Implementing a skin BSSRDF (or several...)
00090 */
00091 
00092 /* Defines */
00093 
00094 #define RD_TABLE_RANGE          100.0f
00095 #define RD_TABLE_RANGE_2        10000.0f
00096 #define RD_TABLE_SIZE           10000
00097 
00098 #define MAX_OCTREE_NODE_POINTS  8
00099 #define MAX_OCTREE_DEPTH                15
00100 
00101 /* Struct Definitions */
00102 
00103 struct ScatterSettings {
00104         float eta;              /* index of refraction */
00105         float sigma_a;  /* absorption coefficient */
00106         float sigma_s_; /* reduced scattering coefficient */
00107         float sigma_t_; /* reduced extinction coefficient */
00108         float sigma;    /* effective extinction coefficient */
00109         float Fdr;              /* diffuse fresnel reflectance */
00110         float D;                /* diffusion constant */
00111         float A;
00112         float alpha_;   /* reduced albedo */
00113         float zr;               /* distance of virtual lightsource above surface */
00114         float zv;               /* distance of virtual lightsource below surface */
00115         float ld;               /* mean free path */
00116         float ro;               /* diffuse reflectance */
00117         float color;
00118         float invsigma_t_;
00119         float frontweight;
00120         float backweight;
00121 
00122         float *tableRd;  /* lookup table to avoid computing Rd */
00123         float *tableRd2; /* lookup table to avoid computing Rd for bigger values */
00124 };
00125 
00126 typedef struct ScatterPoint {
00127         float co[3];
00128         float rad[3];
00129         float area;
00130         int back;
00131 } ScatterPoint;
00132 
00133 typedef struct ScatterNode {
00134         float co[3];
00135         float rad[3];
00136         float backrad[3];
00137         float area, backarea;
00138 
00139         int totpoint;
00140         ScatterPoint *points;
00141 
00142         float split[3];
00143         struct ScatterNode *child[8];
00144 } ScatterNode;
00145 
00146 struct ScatterTree {
00147         MemArena *arena;
00148 
00149         ScatterSettings *ss[3];
00150         float error, scale;
00151 
00152         ScatterNode *root;
00153         ScatterPoint *points;
00154         ScatterPoint **refpoints;
00155         ScatterPoint **tmppoints;
00156         int totpoint;
00157         float min[3], max[3];
00158 };
00159 
00160 typedef struct ScatterResult {
00161         float rad[3];
00162         float backrad[3];
00163         float rdsum[3];
00164         float backrdsum[3];
00165 } ScatterResult;
00166 
00167 /* Functions for BSSRDF reparametrization in to more intuitive parameters,
00168    see [2] section 4 for more info. */
00169 
00170 static float f_Rd(float alpha_, float A, float ro)
00171 {
00172         float sq;
00173 
00174         sq= sqrt(3.0f*(1.0f - alpha_));
00175         return (alpha_/2.0f)*(1.0f + exp((-4.0f/3.0f)*A*sq))*exp(-sq) - ro;
00176 }
00177 
00178 static float compute_reduced_albedo(ScatterSettings *ss)
00179 {
00180         const float tolerance= 1e-8;
00181         const int max_iteration_count= 20;
00182         float d, fsub, xn_1= 0.0f , xn= 1.0f, fxn, fxn_1;
00183         int i;
00184 
00185         /* use secant method to compute reduced albedo using Rd function inverse
00186            with a given reflectance */
00187         fxn= f_Rd(xn, ss->A, ss->ro);
00188         fxn_1= f_Rd(xn_1, ss->A, ss->ro);
00189 
00190         for(i= 0; i < max_iteration_count; i++) {
00191                 fsub= (fxn - fxn_1);
00192                 if(fabs(fsub) < tolerance)
00193                         break;
00194                 d= ((xn - xn_1)/fsub)*fxn;
00195                 if(fabs(d) < tolerance)
00196                         break;
00197 
00198                 xn_1= xn;
00199                 fxn_1= fxn;
00200                 xn= xn - d;
00201 
00202                 if(xn > 1.0f) xn= 1.0f;
00203                 if(xn_1 > 1.0f) xn_1= 1.0f;
00204                 
00205                 fxn= f_Rd(xn, ss->A, ss->ro);
00206         }
00207 
00208         /* avoid division by zero later */
00209         if(xn <= 0.0f)
00210                 xn= 0.00001f;
00211 
00212         return xn;
00213 }
00214 
00215 /* Exponential falloff functions */
00216 
00217 static float Rd_rsquare(ScatterSettings *ss, float rr)
00218 {
00219         float sr, sv, Rdr, Rdv;
00220 
00221         sr= sqrt(rr + ss->zr*ss->zr);
00222         sv= sqrt(rr + ss->zv*ss->zv);
00223 
00224         Rdr= ss->zr*(1.0f + ss->sigma*sr)*exp(-ss->sigma*sr)/(sr*sr*sr);
00225         Rdv= ss->zv*(1.0f + ss->sigma*sv)*exp(-ss->sigma*sv)/(sv*sv*sv);
00226 
00227         return /*ss->alpha_*/(1.0f/(4.0f*M_PI))*(Rdr + Rdv);
00228 }
00229 
00230 static float Rd(ScatterSettings *ss, float r)
00231 {
00232         return Rd_rsquare(ss, r*r);
00233 }
00234 
00235 /* table lookups for Rd. this avoids expensive exp calls. we use two
00236    separate tables as well for lower and higher numbers to improve
00237    precision, since the number are poorly distributed because we do
00238    a lookup with the squared distance for smaller distances, saving
00239    another sqrt. */
00240 
00241 static void approximate_Rd_rgb(ScatterSettings **ss, float rr, float *rd)
00242 {
00243         float indexf, t, idxf;
00244         int index;
00245 
00246         if(rr > (RD_TABLE_RANGE_2*RD_TABLE_RANGE_2));
00247         else if(rr > RD_TABLE_RANGE) {
00248                 rr= sqrt(rr);
00249                 indexf= rr*(RD_TABLE_SIZE/RD_TABLE_RANGE_2);
00250                 index= (int)indexf;
00251                 idxf= (float)index;
00252                 t= indexf - idxf;
00253 
00254                 if(index >= 0 && index < RD_TABLE_SIZE) {
00255                         rd[0]= (ss[0]->tableRd2[index]*(1-t) + ss[0]->tableRd2[index+1]*t);
00256                         rd[1]= (ss[1]->tableRd2[index]*(1-t) + ss[1]->tableRd2[index+1]*t);
00257                         rd[2]= (ss[2]->tableRd2[index]*(1-t) + ss[2]->tableRd2[index+1]*t);
00258                         return;
00259                 }
00260         }
00261         else {
00262                 indexf= rr*(RD_TABLE_SIZE/RD_TABLE_RANGE);
00263                 index= (int)indexf;
00264                 idxf= (float)index;
00265                 t= indexf - idxf;
00266 
00267                 if(index >= 0 && index < RD_TABLE_SIZE) {
00268                         rd[0]= (ss[0]->tableRd[index]*(1-t) + ss[0]->tableRd[index+1]*t);
00269                         rd[1]= (ss[1]->tableRd[index]*(1-t) + ss[1]->tableRd[index+1]*t);
00270                         rd[2]= (ss[2]->tableRd[index]*(1-t) + ss[2]->tableRd[index+1]*t);
00271                         return;
00272                 }
00273         }
00274 
00275         /* fallback to slow Rd computation */
00276         rd[0]= Rd_rsquare(ss[0], rr);
00277         rd[1]= Rd_rsquare(ss[1], rr);
00278         rd[2]= Rd_rsquare(ss[2], rr);
00279 }
00280 
00281 static void build_Rd_table(ScatterSettings *ss)
00282 {
00283         float r;
00284         int i, size = RD_TABLE_SIZE+1;
00285 
00286         ss->tableRd= MEM_mallocN(sizeof(float)*size, "scatterTableRd");
00287         ss->tableRd2= MEM_mallocN(sizeof(float)*size, "scatterTableRd");
00288 
00289         for(i= 0; i < size; i++) {
00290                 r= i*(RD_TABLE_RANGE/RD_TABLE_SIZE);
00291                 /*if(r < ss->invsigma_t_*ss->invsigma_t_)
00292                         r= ss->invsigma_t_*ss->invsigma_t_;*/
00293                 ss->tableRd[i]= Rd(ss, sqrt(r));
00294 
00295                 r= i*(RD_TABLE_RANGE_2/RD_TABLE_SIZE);
00296                 /*if(r < ss->invsigma_t_)
00297                         r= ss->invsigma_t_;*/
00298                 ss->tableRd2[i]= Rd(ss, r);
00299         }
00300 }
00301 
00302 ScatterSettings *scatter_settings_new(float refl, float radius, float ior, float reflfac, float frontweight, float backweight)
00303 {
00304         ScatterSettings *ss;
00305         
00306         ss= MEM_callocN(sizeof(ScatterSettings), "ScatterSettings");
00307 
00308         /* see [1] and [3] for these formulas */
00309         ss->eta= ior;
00310         ss->Fdr= -1.440f/ior*ior + 0.710f/ior + 0.668f + 0.0636f*ior;
00311         ss->A= (1.0f + ss->Fdr)/(1.0f - ss->Fdr);
00312         ss->ld= radius;
00313         ss->ro= MIN2(refl, 0.999f);
00314         ss->color= ss->ro*reflfac + (1.0f-reflfac);
00315 
00316         ss->alpha_= compute_reduced_albedo(ss);
00317 
00318         ss->sigma= 1.0f/ss->ld;
00319         ss->sigma_t_= ss->sigma/sqrt(3.0f*(1.0f - ss->alpha_));
00320         ss->sigma_s_= ss->alpha_*ss->sigma_t_;
00321         ss->sigma_a= ss->sigma_t_ - ss->sigma_s_;
00322 
00323         ss->D= 1.0f/(3.0f*ss->sigma_t_);
00324 
00325         ss->zr= 1.0f/ss->sigma_t_;
00326         ss->zv= ss->zr + 4.0f*ss->A*ss->D;
00327 
00328         ss->invsigma_t_= 1.0f/ss->sigma_t_;
00329 
00330         ss->frontweight= frontweight;
00331         ss->backweight= backweight;
00332 
00333         /* precompute a table of Rd values for quick lookup */
00334         build_Rd_table(ss);
00335 
00336         return ss;
00337 }
00338 
00339 void scatter_settings_free(ScatterSettings *ss)
00340 {
00341         MEM_freeN(ss->tableRd);
00342         MEM_freeN(ss->tableRd2);
00343         MEM_freeN(ss);
00344 }
00345 
00346 /* Hierarchical method as in [2]. */
00347 
00348 /* traversal */
00349 
00350 #define SUBNODE_INDEX(co, split) \
00351         ((co[0]>=split[0]) + (co[1]>=split[1])*2 + (co[2]>=split[2])*4)
00352         
00353 static void add_radiance(ScatterTree *tree, float *frontrad, float *backrad, float area, float backarea, float rr, ScatterResult *result)
00354 {
00355         float rd[3], frontrd[3], backrd[3];
00356 
00357         approximate_Rd_rgb(tree->ss, rr, rd);
00358 
00359         if(frontrad && area) {
00360                 frontrd[0] = rd[0]*area;
00361                 frontrd[1] = rd[1]*area;
00362                 frontrd[2] = rd[2]*area;
00363 
00364                 result->rad[0] += frontrad[0]*frontrd[0];
00365                 result->rad[1] += frontrad[1]*frontrd[1];
00366                 result->rad[2] += frontrad[2]*frontrd[2];
00367 
00368                 result->rdsum[0] += frontrd[0];
00369                 result->rdsum[1] += frontrd[1];
00370                 result->rdsum[2] += frontrd[2];
00371         }
00372         if(backrad && backarea) {
00373                 backrd[0] = rd[0]*backarea;
00374                 backrd[1] = rd[1]*backarea;
00375                 backrd[2] = rd[2]*backarea;
00376 
00377                 result->backrad[0] += backrad[0]*backrd[0];
00378                 result->backrad[1] += backrad[1]*backrd[1];
00379                 result->backrad[2] += backrad[2]*backrd[2];
00380 
00381                 result->backrdsum[0] += backrd[0];
00382                 result->backrdsum[1] += backrd[1];
00383                 result->backrdsum[2] += backrd[2];
00384         }
00385 }
00386 
00387 static void traverse_octree(ScatterTree *tree, ScatterNode *node, float *co, int self, ScatterResult *result)
00388 {
00389         float sub[3], dist;
00390         int i, index = 0;
00391 
00392         if(node->totpoint > 0) {
00393                 /* leaf - add radiance from all samples */
00394                 for(i=0; i<node->totpoint; i++) {
00395                         ScatterPoint *p= &node->points[i];
00396 
00397                         VECSUB(sub, co, p->co);
00398                         dist= INPR(sub, sub);
00399 
00400                         if(p->back)
00401                                 add_radiance(tree, NULL, p->rad, 0.0f, p->area, dist, result);
00402                         else
00403                                 add_radiance(tree, p->rad, NULL, p->area, 0.0f, dist, result);
00404                 }
00405         }
00406         else {
00407                 /* branch */
00408                 if (self)
00409                         index = SUBNODE_INDEX(co, node->split);
00410 
00411                 for(i=0; i<8; i++) {
00412                         if(node->child[i]) {
00413                                 ScatterNode *subnode= node->child[i];
00414 
00415                                 if(self && index == i) {
00416                                         /* always traverse node containing the point */
00417                                         traverse_octree(tree, subnode, co, 1, result);
00418                                 }
00419                                 else {
00420                                         /* decide subnode traversal based on maximum solid angle */
00421                                         VECSUB(sub, co, subnode->co);
00422                                         dist= INPR(sub, sub);
00423 
00424                                         /* actually area/dist > error, but this avoids division */
00425                                         if(subnode->area+subnode->backarea>tree->error*dist) {
00426                                                 traverse_octree(tree, subnode, co, 0, result);
00427                                         }
00428                                         else {
00429                                                 add_radiance(tree, subnode->rad, subnode->backrad,
00430                                                         subnode->area, subnode->backarea, dist, result);
00431                                         }
00432                                 }
00433                         }
00434                 }
00435         }
00436 }
00437 
00438 static void compute_radiance(ScatterTree *tree, float *co, float *rad)
00439 {
00440         ScatterResult result;
00441         float rdsum[3], backrad[3], backrdsum[3];
00442 
00443         memset(&result, 0, sizeof(result));
00444 
00445         traverse_octree(tree, tree->root, co, 1, &result);
00446 
00447         /* the original paper doesn't do this, but we normalize over the
00448            sampled area and multiply with the reflectance. this is because
00449            our point samples are incomplete, there are no samples on parts
00450            of the mesh not visible from the camera. this can not only make
00451            it darker, but also lead to ugly color shifts */
00452 
00453         mul_v3_fl(result.rad, tree->ss[0]->frontweight);
00454         mul_v3_fl(result.backrad, tree->ss[0]->backweight);
00455 
00456         VECCOPY(rad, result.rad);
00457         VECADD(backrad, result.rad, result.backrad);
00458 
00459         VECCOPY(rdsum, result.rdsum);
00460         VECADD(backrdsum, result.rdsum, result.backrdsum);
00461 
00462         if(rdsum[0] > 1e-16f) rad[0]= tree->ss[0]->color*rad[0]/rdsum[0];
00463         if(rdsum[1] > 1e-16f) rad[1]= tree->ss[1]->color*rad[1]/rdsum[1];
00464         if(rdsum[2] > 1e-16f) rad[2]= tree->ss[2]->color*rad[2]/rdsum[2];
00465 
00466         if(backrdsum[0] > 1e-16f) backrad[0]= tree->ss[0]->color*backrad[0]/backrdsum[0];
00467         if(backrdsum[1] > 1e-16f) backrad[1]= tree->ss[1]->color*backrad[1]/backrdsum[1];
00468         if(backrdsum[2] > 1e-16f) backrad[2]= tree->ss[2]->color*backrad[2]/backrdsum[2];
00469 
00470         rad[0]= MAX2(rad[0], backrad[0]);
00471         rad[1]= MAX2(rad[1], backrad[1]);
00472         rad[2]= MAX2(rad[2], backrad[2]);
00473 }
00474 
00475 /* building */
00476 
00477 static void sum_leaf_radiance(ScatterTree *UNUSED(tree), ScatterNode *node)
00478 {
00479         ScatterPoint *p;
00480         float rad, totrad= 0.0f, inv;
00481         int i;
00482 
00483         node->co[0]= node->co[1]= node->co[2]= 0.0;
00484         node->rad[0]= node->rad[1]= node->rad[2]= 0.0;
00485         node->backrad[0]= node->backrad[1]= node->backrad[2]= 0.0;
00486 
00487         /* compute total rad, rad weighted average position,
00488            and total area */
00489         for(i=0; i<node->totpoint; i++) {
00490                 p= &node->points[i];
00491 
00492                 rad= p->area*fabs(p->rad[0] + p->rad[1] + p->rad[2]);
00493                 totrad += rad;
00494 
00495                 node->co[0] += rad*p->co[0];
00496                 node->co[1] += rad*p->co[1];
00497                 node->co[2] += rad*p->co[2];
00498 
00499                 if(p->back) {
00500                         node->backrad[0] += p->rad[0]*p->area;
00501                         node->backrad[1] += p->rad[1]*p->area;
00502                         node->backrad[2] += p->rad[2]*p->area;
00503 
00504                         node->backarea += p->area;
00505                 }
00506                 else {
00507                         node->rad[0] += p->rad[0]*p->area;
00508                         node->rad[1] += p->rad[1]*p->area;
00509                         node->rad[2] += p->rad[2]*p->area;
00510 
00511                         node->area += p->area;
00512                 }
00513         }
00514 
00515         if(node->area > 1e-16f) {
00516                 inv= 1.0/node->area;
00517                 node->rad[0] *= inv;
00518                 node->rad[1] *= inv;
00519                 node->rad[2] *= inv;
00520         }
00521         if(node->backarea > 1e-16f) {
00522                 inv= 1.0/node->backarea;
00523                 node->backrad[0] *= inv;
00524                 node->backrad[1] *= inv;
00525                 node->backrad[2] *= inv;
00526         }
00527 
00528         if(totrad > 1e-16f) {
00529                 inv= 1.0/totrad;
00530                 node->co[0] *= inv;
00531                 node->co[1] *= inv;
00532                 node->co[2] *= inv;
00533         }
00534         else {
00535                 /* make sure that if radiance is 0.0f, we still have these points in
00536                    the tree at a good position, they count for rdsum too */
00537                 for(i=0; i<node->totpoint; i++) {
00538                         p= &node->points[i];
00539 
00540                         node->co[0] += p->co[0];
00541                         node->co[1] += p->co[1];
00542                         node->co[2] += p->co[2];
00543                 }
00544 
00545                 node->co[0] /= node->totpoint;
00546                 node->co[1] /= node->totpoint;
00547                 node->co[2] /= node->totpoint;
00548         }
00549 }
00550 
00551 static void sum_branch_radiance(ScatterTree *UNUSED(tree), ScatterNode *node)
00552 {
00553         ScatterNode *subnode;
00554         float rad, totrad= 0.0f, inv;
00555         int i, totnode;
00556 
00557         node->co[0]= node->co[1]= node->co[2]= 0.0;
00558         node->rad[0]= node->rad[1]= node->rad[2]= 0.0;
00559         node->backrad[0]= node->backrad[1]= node->backrad[2]= 0.0;
00560 
00561         /* compute total rad, rad weighted average position,
00562            and total area */
00563         for(i=0; i<8; i++) {
00564                 if(node->child[i] == NULL)
00565                         continue;
00566 
00567                 subnode= node->child[i];
00568 
00569                 rad= subnode->area*fabs(subnode->rad[0] + subnode->rad[1] + subnode->rad[2]);
00570                 rad += subnode->backarea*fabs(subnode->backrad[0] + subnode->backrad[1] + subnode->backrad[2]);
00571                 totrad += rad;
00572 
00573                 node->co[0] += rad*subnode->co[0];
00574                 node->co[1] += rad*subnode->co[1];
00575                 node->co[2] += rad*subnode->co[2];
00576 
00577                 node->rad[0] += subnode->rad[0]*subnode->area;
00578                 node->rad[1] += subnode->rad[1]*subnode->area;
00579                 node->rad[2] += subnode->rad[2]*subnode->area;
00580 
00581                 node->backrad[0] += subnode->backrad[0]*subnode->backarea;
00582                 node->backrad[1] += subnode->backrad[1]*subnode->backarea;
00583                 node->backrad[2] += subnode->backrad[2]*subnode->backarea;
00584 
00585                 node->area += subnode->area;
00586                 node->backarea += subnode->backarea;
00587         }
00588 
00589         if(node->area > 1e-16f) {
00590                 inv= 1.0/node->area;
00591                 node->rad[0] *= inv;
00592                 node->rad[1] *= inv;
00593                 node->rad[2] *= inv;
00594         }
00595         if(node->backarea > 1e-16f) {
00596                 inv= 1.0/node->backarea;
00597                 node->backrad[0] *= inv;
00598                 node->backrad[1] *= inv;
00599                 node->backrad[2] *= inv;
00600         }
00601 
00602         if(totrad > 1e-16f) {
00603                 inv= 1.0/totrad;
00604                 node->co[0] *= inv;
00605                 node->co[1] *= inv;
00606                 node->co[2] *= inv;
00607         }
00608         else {
00609                 /* make sure that if radiance is 0.0f, we still have these points in
00610                    the tree at a good position, they count for rdsum too */
00611                 totnode= 0;
00612 
00613                 for(i=0; i<8; i++) {
00614                         if(node->child[i]) {
00615                                 subnode= node->child[i];
00616 
00617                                 node->co[0] += subnode->co[0];
00618                                 node->co[1] += subnode->co[1];
00619                                 node->co[2] += subnode->co[2];
00620 
00621                                 totnode++;
00622                         }
00623                 }
00624 
00625                 node->co[0] /= totnode;
00626                 node->co[1] /= totnode;
00627                 node->co[2] /= totnode;
00628         }
00629 }
00630 
00631 static void sum_radiance(ScatterTree *tree, ScatterNode *node)
00632 {
00633         if(node->totpoint > 0) {
00634                 sum_leaf_radiance(tree, node);
00635         }
00636         else {
00637                 int i;
00638 
00639                 for(i=0; i<8; i++)
00640                         if(node->child[i])
00641                                 sum_radiance(tree, node->child[i]);
00642 
00643                 sum_branch_radiance(tree, node);
00644         }
00645 }
00646 
00647 static void subnode_middle(int i, float *mid, float *subsize, float *submid)
00648 {
00649         int x= i & 1, y= i & 2, z= i & 4;
00650 
00651         submid[0]= mid[0] + ((x)? subsize[0]: -subsize[0]);
00652         submid[1]= mid[1] + ((y)? subsize[1]: -subsize[1]);
00653         submid[2]= mid[2] + ((z)? subsize[2]: -subsize[2]);
00654 }
00655 
00656 static void create_octree_node(ScatterTree *tree, ScatterNode *node, float *mid, float *size, ScatterPoint **refpoints, int depth)
00657 {
00658         ScatterNode *subnode;
00659         ScatterPoint **subrefpoints, **tmppoints= tree->tmppoints;
00660         int index, nsize[8], noffset[8], i, subco, usednodes, usedi;
00661         float submid[3], subsize[3];
00662 
00663         /* stopping condition */
00664         if(node->totpoint <= MAX_OCTREE_NODE_POINTS || depth == MAX_OCTREE_DEPTH) {
00665                 for(i=0; i<node->totpoint; i++)
00666                         node->points[i]= *(refpoints[i]);
00667 
00668                 return;
00669         }
00670 
00671         subsize[0]= size[0]*0.5;
00672         subsize[1]= size[1]*0.5;
00673         subsize[2]= size[2]*0.5;
00674 
00675         node->split[0]= mid[0];
00676         node->split[1]= mid[1];
00677         node->split[2]= mid[2];
00678 
00679         memset(nsize, 0, sizeof(nsize));
00680         memset(noffset, 0, sizeof(noffset));
00681 
00682         /* count points in subnodes */
00683         for(i=0; i<node->totpoint; i++) {
00684                 index= SUBNODE_INDEX(refpoints[i]->co, node->split);
00685                 tmppoints[i]= refpoints[i];
00686                 nsize[index]++;
00687         }
00688 
00689         /* here we check if only one subnode is used. if this is the case, we don't
00690            create a new node, but rather call this function again, with different 
00691            size and middle position for the same node. */
00692         for(usedi=0, usednodes=0, i=0; i<8; i++) {
00693                 if(nsize[i]) {
00694                         usednodes++;
00695                         usedi = i;
00696                 }
00697                 if(i != 0)
00698                         noffset[i]= noffset[i-1]+nsize[i-1];
00699         }
00700         
00701         if(usednodes<=1) {
00702                 subnode_middle(usedi, mid, subsize, submid);
00703                 create_octree_node(tree, node, submid, subsize, refpoints, depth+1);
00704                 return;
00705         }
00706 
00707         /* reorder refpoints by subnode */
00708         for(i=0; i<node->totpoint; i++) {
00709                 index= SUBNODE_INDEX(tmppoints[i]->co, node->split);
00710                 refpoints[noffset[index]]= tmppoints[i];
00711                 noffset[index]++;
00712         }
00713 
00714         /* create subnodes */
00715         for(subco=0, i=0; i<8; subco+=nsize[i], i++) {
00716                 if(nsize[i] > 0) {
00717                         subnode= BLI_memarena_alloc(tree->arena, sizeof(ScatterNode));
00718                         node->child[i]= subnode;
00719                         subnode->points= node->points + subco;
00720                         subnode->totpoint= nsize[i];
00721                         subrefpoints= refpoints + subco;
00722 
00723                         subnode_middle(i, mid, subsize, submid);
00724 
00725                         create_octree_node(tree, subnode, submid, subsize, subrefpoints,
00726                                 depth+1);
00727                 }
00728                 else
00729                         node->child[i]= NULL;
00730         }
00731 
00732         node->points= NULL;
00733         node->totpoint= 0;
00734 }
00735 
00736 /* public functions */
00737 
00738 ScatterTree *scatter_tree_new(ScatterSettings *ss[3], float scale, float error,
00739         float (*co)[3], float (*color)[3], float *area, int totpoint)
00740 {
00741         ScatterTree *tree;
00742         ScatterPoint *points, **refpoints;
00743         int i;
00744 
00745         /* allocate tree */
00746         tree= MEM_callocN(sizeof(ScatterTree), "ScatterTree");
00747         tree->scale= scale;
00748         tree->error= error;
00749         tree->totpoint= totpoint;
00750 
00751         tree->ss[0]= ss[0];
00752         tree->ss[1]= ss[1];
00753         tree->ss[2]= ss[2];
00754 
00755         points= MEM_callocN(sizeof(ScatterPoint)*totpoint, "ScatterPoints");
00756         refpoints= MEM_callocN(sizeof(ScatterPoint*)*totpoint, "ScatterRefPoints");
00757 
00758         tree->points= points;
00759         tree->refpoints= refpoints;
00760 
00761         /* build points */
00762         INIT_MINMAX(tree->min, tree->max);
00763 
00764         for(i=0; i<totpoint; i++) {
00765                 VECCOPY(points[i].co, co[i]);
00766                 VECCOPY(points[i].rad, color[i]);
00767                 points[i].area= fabs(area[i])/(tree->scale*tree->scale);
00768                 points[i].back= (area[i] < 0.0f);
00769 
00770                 mul_v3_fl(points[i].co, 1.0f/tree->scale);
00771                 DO_MINMAX(points[i].co, tree->min, tree->max);
00772 
00773                 refpoints[i]= points + i;
00774         }
00775 
00776         return tree;
00777 }
00778 
00779 void scatter_tree_build(ScatterTree *tree)
00780 {
00781         ScatterPoint *newpoints, **tmppoints;
00782         float mid[3], size[3];
00783         int totpoint= tree->totpoint;
00784 
00785         newpoints= MEM_callocN(sizeof(ScatterPoint)*totpoint, "ScatterPoints");
00786         tmppoints= MEM_callocN(sizeof(ScatterPoint*)*totpoint, "ScatterTmpPoints");
00787         tree->tmppoints= tmppoints;
00788 
00789         tree->arena= BLI_memarena_new(0x8000 * sizeof(ScatterNode), "sss tree arena");
00790         BLI_memarena_use_calloc(tree->arena);
00791 
00792         /* build tree */
00793         tree->root= BLI_memarena_alloc(tree->arena, sizeof(ScatterNode));
00794         tree->root->points= newpoints;
00795         tree->root->totpoint= totpoint;
00796 
00797         mid[0]= (tree->min[0]+tree->max[0])*0.5;
00798         mid[1]= (tree->min[1]+tree->max[1])*0.5;
00799         mid[2]= (tree->min[2]+tree->max[2])*0.5;
00800 
00801         size[0]= (tree->max[0]-tree->min[0])*0.5;
00802         size[1]= (tree->max[1]-tree->min[1])*0.5;
00803         size[2]= (tree->max[2]-tree->min[2])*0.5;
00804 
00805         create_octree_node(tree, tree->root, mid, size, tree->refpoints, 0);
00806 
00807         MEM_freeN(tree->points);
00808         MEM_freeN(tree->refpoints);
00809         MEM_freeN(tree->tmppoints);
00810         tree->refpoints= NULL;
00811         tree->tmppoints= NULL;
00812         tree->points= newpoints;
00813         
00814         /* sum radiance at nodes */
00815         sum_radiance(tree, tree->root);
00816 }
00817 
00818 void scatter_tree_sample(ScatterTree *tree, float *co, float *color)
00819 {
00820         float sco[3];
00821 
00822         VECCOPY(sco, co);
00823         mul_v3_fl(sco, 1.0f/tree->scale);
00824 
00825         compute_radiance(tree, sco, color);
00826 }
00827 
00828 void scatter_tree_free(ScatterTree *tree)
00829 {
00830         if (tree->arena) BLI_memarena_free(tree->arena);
00831         if (tree->points) MEM_freeN(tree->points);
00832         if (tree->refpoints) MEM_freeN(tree->refpoints);
00833                 
00834         MEM_freeN(tree);
00835 }
00836 
00837 /* Internal Renderer API */
00838 
00839 /* sss tree building */
00840 
00841 typedef struct SSSData {
00842         ScatterTree *tree;
00843         ScatterSettings *ss[3];
00844 } SSSData;
00845 
00846 typedef struct SSSPoints {
00847         struct SSSPoints *next, *prev;
00848 
00849         float (*co)[3];
00850         float (*color)[3];
00851         float *area;
00852         int totpoint;
00853 } SSSPoints;
00854 
00855 static void sss_create_tree_mat(Render *re, Material *mat)
00856 {
00857         SSSPoints *p;
00858         RenderResult *rr;
00859         ListBase points;
00860         float (*co)[3] = NULL, (*color)[3] = NULL, *area = NULL;
00861         int totpoint = 0, osa, osaflag, partsdone;
00862 
00863         if(re->test_break(re->tbh))
00864                 return;
00865         
00866         points.first= points.last= NULL;
00867 
00868         /* TODO: this is getting a bit ugly, copying all those variables and
00869            setting them back, maybe we need to create our own Render? */
00870 
00871         /* do SSS preprocessing render */
00872         BLI_rw_mutex_lock(&re->resultmutex, THREAD_LOCK_WRITE);
00873         rr= re->result;
00874         osa= re->osa;
00875         osaflag= re->r.mode & R_OSA;
00876         partsdone= re->i.partsdone;
00877 
00878         re->osa= 0;
00879         re->r.mode &= ~R_OSA;
00880         re->sss_points= &points;
00881         re->sss_mat= mat;
00882         re->i.partsdone= 0;
00883 
00884         if(!(re->r.scemode & R_PREVIEWBUTS))
00885                 re->result= NULL;
00886         BLI_rw_mutex_unlock(&re->resultmutex);
00887 
00888         RE_TileProcessor(re);
00889         
00890         BLI_rw_mutex_lock(&re->resultmutex, THREAD_LOCK_WRITE);
00891         if(!(re->r.scemode & R_PREVIEWBUTS)) {
00892                 RE_FreeRenderResult(re->result);
00893                 re->result= rr;
00894         }
00895         BLI_rw_mutex_unlock(&re->resultmutex);
00896 
00897         re->i.partsdone= partsdone;
00898         re->sss_mat= NULL;
00899         re->sss_points= NULL;
00900         re->osa= osa;
00901         if (osaflag) re->r.mode |= R_OSA;
00902 
00903         /* no points? no tree */
00904         if(!points.first)
00905                 return;
00906 
00907         /* merge points together into a single buffer */
00908         if(!re->test_break(re->tbh)) {
00909                 for(totpoint=0, p=points.first; p; p=p->next)
00910                         totpoint += p->totpoint;
00911                 
00912                 co= MEM_mallocN(sizeof(*co)*totpoint, "SSSCo");
00913                 color= MEM_mallocN(sizeof(*color)*totpoint, "SSSColor");
00914                 area= MEM_mallocN(sizeof(*area)*totpoint, "SSSArea");
00915 
00916                 for(totpoint=0, p=points.first; p; p=p->next) {
00917                         memcpy(co+totpoint, p->co, sizeof(*co)*p->totpoint);
00918                         memcpy(color+totpoint, p->color, sizeof(*color)*p->totpoint);
00919                         memcpy(area+totpoint, p->area, sizeof(*area)*p->totpoint);
00920                         totpoint += p->totpoint;
00921                 }
00922         }
00923 
00924         /* free points */
00925         for(p=points.first; p; p=p->next) {
00926                 MEM_freeN(p->co);
00927                 MEM_freeN(p->color);
00928                 MEM_freeN(p->area);
00929         }
00930         BLI_freelistN(&points);
00931 
00932         /* build tree */
00933         if(!re->test_break(re->tbh)) {
00934                 SSSData *sss= MEM_callocN(sizeof(*sss), "SSSData");
00935                 float ior= mat->sss_ior, cfac= mat->sss_colfac;
00936                 float *radius= mat->sss_radius;
00937                 float fw= mat->sss_front, bw= mat->sss_back;
00938                 float error = mat->sss_error;
00939 
00940                 error= get_render_aosss_error(&re->r, error);
00941                 if((re->r.scemode & R_PREVIEWBUTS) && error < 0.5f)
00942                         error= 0.5f;
00943                 
00944                 sss->ss[0]= scatter_settings_new(mat->sss_col[0], radius[0], ior, cfac, fw, bw);
00945                 sss->ss[1]= scatter_settings_new(mat->sss_col[1], radius[1], ior, cfac, fw, bw);
00946                 sss->ss[2]= scatter_settings_new(mat->sss_col[2], radius[2], ior, cfac, fw, bw);
00947                 sss->tree= scatter_tree_new(sss->ss, mat->sss_scale, error,
00948                         co, color, area, totpoint);
00949 
00950                 MEM_freeN(co);
00951                 MEM_freeN(color);
00952                 MEM_freeN(area);
00953 
00954                 scatter_tree_build(sss->tree);
00955 
00956                 BLI_ghash_insert(re->sss_hash, mat, sss);
00957         }
00958         else {
00959                 if (co) MEM_freeN(co);
00960                 if (color) MEM_freeN(color);
00961                 if (area) MEM_freeN(area);
00962         }
00963 }
00964 
00965 void sss_add_points(Render *re, float (*co)[3], float (*color)[3], float *area, int totpoint)
00966 {
00967         SSSPoints *p;
00968         
00969         if(totpoint > 0) {
00970                 p= MEM_callocN(sizeof(SSSPoints), "SSSPoints");
00971 
00972                 p->co= co;
00973                 p->color= color;
00974                 p->area= area;
00975                 p->totpoint= totpoint;
00976 
00977                 BLI_lock_thread(LOCK_CUSTOM1);
00978                 BLI_addtail(re->sss_points, p);
00979                 BLI_unlock_thread(LOCK_CUSTOM1);
00980         }
00981 }
00982 
00983 static void sss_free_tree(SSSData *sss)
00984 {
00985         scatter_tree_free(sss->tree);
00986         scatter_settings_free(sss->ss[0]);
00987         scatter_settings_free(sss->ss[1]);
00988         scatter_settings_free(sss->ss[2]);
00989         MEM_freeN(sss);
00990 }
00991 
00992 /* public functions */
00993 
00994 void make_sss_tree(Render *re)
00995 {
00996         Material *mat;
00997         
00998         re->sss_hash= BLI_ghash_new(BLI_ghashutil_ptrhash, BLI_ghashutil_ptrcmp, "make_sss_tree gh");
00999 
01000         re->i.infostr= "SSS preprocessing";
01001         re->stats_draw(re->sdh, &re->i);
01002         
01003         for(mat= re->main->mat.first; mat; mat= mat->id.next)
01004                 if(mat->id.us && (mat->flag & MA_IS_USED) && (mat->sss_flag & MA_DIFF_SSS))
01005                         sss_create_tree_mat(re, mat);
01006         
01007         /* XXX preview exception */
01008         /* localizing preview render data is not fun for node trees :( */
01009         if(re->main!=G.main) {
01010                 for(mat= G.main->mat.first; mat; mat= mat->id.next)
01011                         if(mat->id.us && (mat->flag & MA_IS_USED) && (mat->sss_flag & MA_DIFF_SSS))
01012                                 sss_create_tree_mat(re, mat);
01013         }
01014         
01015 }
01016 
01017 void free_sss(Render *re)
01018 {
01019         if(re->sss_hash) {
01020                 GHashIterator *it= BLI_ghashIterator_new(re->sss_hash);
01021 
01022                 while(!BLI_ghashIterator_isDone(it)) {
01023                         sss_free_tree(BLI_ghashIterator_getValue(it));
01024                         BLI_ghashIterator_step(it);
01025                 }
01026 
01027                 BLI_ghashIterator_free(it);
01028                 BLI_ghash_free(re->sss_hash, NULL, NULL);
01029                 re->sss_hash= NULL;
01030         }
01031 }
01032 
01033 int sample_sss(Render *re, Material *mat, float *co, float *color)
01034 {
01035         if(re->sss_hash) {
01036                 SSSData *sss= BLI_ghash_lookup(re->sss_hash, mat);
01037 
01038                 if(sss) {
01039                         scatter_tree_sample(sss->tree, co, color);
01040                         return 1;
01041                 }
01042                 else {
01043                         color[0]= 0.0f;
01044                         color[1]= 0.0f;
01045                         color[2]= 0.0f;
01046                 }
01047         }
01048 
01049         return 0;
01050 }
01051 
01052 int sss_pass_done(struct Render *re, struct Material *mat)
01053 {
01054         return ((re->flag & R_BAKING) || !(re->r.mode & R_SSS) || (re->sss_hash && BLI_ghash_lookup(re->sss_hash, mat)));
01055 }
01056