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