|
Blender
V2.59
|
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