Subsurface scattering:
authorBrecht Van Lommel <brechtvanlommel@pandora.be>
Thu, 3 May 2007 21:37:52 +0000 (21:37 +0000)
committerBrecht Van Lommel <brechtvanlommel@pandora.be>
Thu, 3 May 2007 21:37:52 +0000 (21:37 +0000)
Documentation on the settings, known limitations and implementation
info can be found here:
http://www.blender.org/development/current-projects/changes-since-243/subsurface-scattering/

20 files changed:
source/blender/blenkernel/BKE_material.h
source/blender/blenkernel/intern/material.c
source/blender/blenloader/intern/readfile.c
source/blender/makesdna/DNA_material_types.h
source/blender/render/extern/include/RE_pipeline.h
source/blender/render/intern/include/render_types.h
source/blender/render/intern/include/rendercore.h
source/blender/render/intern/include/sss.h [new file with mode: 0644]
source/blender/render/intern/include/zbuf.h
source/blender/render/intern/source/convertblender.c
source/blender/render/intern/source/envmap.c
source/blender/render/intern/source/pipeline.c
source/blender/render/intern/source/rendercore.c
source/blender/render/intern/source/renderdatabase.c
source/blender/render/intern/source/shadeinput.c
source/blender/render/intern/source/shadeoutput.c
source/blender/render/intern/source/sss.c [new file with mode: 0644]
source/blender/render/intern/source/zbuf.c
source/blender/src/buttons_shading.c
source/blender/src/previewrender.c

index 93260bac73df05185cb1939c2ed887a9123313e3..2add4b9508006aa301f60f1adcc9e78e685ef6e0 100644 (file)
@@ -62,6 +62,8 @@ void init_render_materials(int, float *);
 void end_render_material(struct Material *);
 void end_render_materials(void);
 
+int material_in_material(struct Material *parmat, struct Material *mat);
+
 void automatname(struct Material *);
 void delete_material_index(void);            
 
index d33cc7bd1c4bed3cab65f542a036b10c38ced874..56b8307020aeb1856571b2d26a7a6e93b88fa28d 100644 (file)
@@ -144,6 +144,20 @@ void init_material(Material *ma)
        ma->pr_lamp= 3;                 /* two lamps, is bits */
        ma->pr_type= MA_SPHERE;
 
+       ma->sss_radius[0]= 1.0f;
+       ma->sss_radius[1]= 1.0f;
+       ma->sss_radius[2]= 1.0f;
+       ma->sss_col[0]= 0.8f;
+       ma->sss_col[1]= 0.8f;
+       ma->sss_col[2]= 0.8f;
+       ma->sss_error= 0.05f;
+       ma->sss_scale= 0.1f;
+       ma->sss_ior= 1.3f;
+       ma->sss_colfac= 1.0f;
+       ma->sss_texfac= 0.0f;
+       ma->sss_front= 1.0f;
+       ma->sss_back= 1.0f;
+
        ma->mode= MA_TRACEBLE|MA_SHADBUF|MA_SHADOW|MA_RADIO|MA_RAYBIAS|MA_TANGENT_STR;
 }
 
@@ -702,6 +716,33 @@ void end_render_materials(void)
                        end_render_material(ma);
 }
 
+static int material_in_nodetree(bNodeTree *ntree, Material *mat)
+{
+       bNode *node;
+
+       for(node=ntree->nodes.first; node; node= node->next) {
+               if(node->id && GS(node->id->name)==ID_MA) {
+                       if(node->id==(ID*)mat)
+                               return 1;
+               }
+               else if(node->type==NODE_GROUP)
+                       if(material_in_nodetree((bNodeTree*)node->id, mat))
+                               return 1;
+       }
+
+       return 0;
+}
+
+int material_in_material(Material *parmat, Material *mat)
+{
+       if(parmat==mat)
+               return 1;
+       else if(parmat->nodetree && parmat->use_nodes)
+               return material_in_nodetree(parmat->nodetree, mat);
+       else
+               return 0;
+}
+       
 /* ****************** */
 
 char colname_array[125][20]= {
index ace436cf04c65d50e6230b660441176433eea2d2..c283e5c9fcfebface232541f50cede42097c5329 100644 (file)
@@ -6387,10 +6387,36 @@ static void do_versions(FileData *fd, Library *lib, Main *main)
        if(main->versionfile <= 243) {
                Object *ob= main->object.first;
                Camera *cam = main->camera.first;
+               Material *ma;
                
                for(; cam; cam= cam->id.next) {
                        cam->angle= 360.0f * atan(16.0f/cam->lens) / M_PI;
                }
+
+               for(ma=main->mat.first; ma; ma= ma->id.next) {
+                       if(ma->sss_scale==0.0f) {
+                               ma->sss_radius[0]= 1.0f;
+                               ma->sss_radius[1]= 1.0f;
+                               ma->sss_radius[2]= 1.0f;
+                               ma->sss_col[0]= 0.8f;
+                               ma->sss_col[1]= 0.8f;
+                               ma->sss_col[2]= 0.8f;
+                               ma->sss_error= 0.05f;
+                               ma->sss_scale= 0.1f;
+                               ma->sss_ior= 1.3f;
+                               ma->sss_colfac= 1.0f;
+                               ma->sss_texfac= 0.0f;
+                       }
+                       if(ma->sss_front==0 && ma->sss_back==0) {
+                               ma->sss_front= 1.0f;
+                               ma->sss_back= 1.0f;
+                       }
+                       if(ma->sss_col[0]==0 && ma->sss_col[1]==0 && ma->sss_col[2]==0) {
+                               ma->sss_col[0]= ma->r;
+                               ma->sss_col[1]= ma->g;
+                               ma->sss_col[2]= ma->b;
+                       }
+               }
                
                for(; ob; ob= ob->id.next) {
                        bDeformGroup *curdef;
index 7b948b93b1022c0afca908268c11deffc3c13c70..0fad110d64b3dca4c2eb743e65eb22cf9ca1a7b3 100644 (file)
@@ -111,6 +111,12 @@ typedef struct Material {
        float fhdist, xyfrict;
        short dynamode, pad2;
 
+       float sss_radius[3], sss_col[3];
+       float sss_error, sss_scale, sss_ior;
+       float sss_colfac, sss_texfac;
+       float sss_front, sss_back;
+       short sss_flag, sss_preset;
+
        /* yafray: absorption color, dispersion parameters and material preset menu */
        float YF_ar, YF_ag, YF_ab, YF_dscale, YF_dpwr;
        int YF_dsmp, YF_preset, YF_djit;
@@ -268,5 +274,8 @@ typedef struct Material {
 /* pr_back */
 #define MA_DARK                        1
 
+/* sss_flag */
+#define MA_DIFF_SSS            1
+
 #endif
 
index bd210501330acc0ef57fd80ef51f6d27ef6288c7..824050d802f0b1160ca33507a9a178242d9cdfcd 100644 (file)
@@ -173,7 +173,7 @@ void RE_DataBase_ApplyWindow(struct Render *re);
 void RE_set_max_threads(int threads);
 
 /* the main processor, assumes all was set OK! */
-void RE_TileProcessor(struct Render *re, int firsttile);
+void RE_TileProcessor(struct Render *re, int firsttile, int threaded);
 
 /* only RE_NewRender() needed, main Blender render calls */
 void RE_BlenderFrame(struct Render *re, struct Scene *scene, int frame);
index 609c391d599d0ad1d10a02c446c62ed290b4eaeb..45aff8e8b6061bd5f8a3d214808c91b266d0fb2b 100644 (file)
@@ -68,9 +68,12 @@ typedef struct RenderPart
        /* result of part rendering */
        RenderResult *result;
        
-       int *rectp;                     /* polygon index table */
+       int *rectp;                                             /* polygon index table */
        int *rectz;                                             /* zbuffer */
        long *rectdaps;                                 /* delta acum buffer for pixel structs */
+       int *rectbackp;                                 /* polygon index table for backside sss */
+       int *rectbackz;                                 /* zbuffer for backside sss */
+       long *rectall;                                  /* buffer for all faces for sss */
        
        rcti disprect;                                  /* part coordinates within total picture */
        int rectx, recty;                               /* the size */
@@ -169,6 +172,10 @@ struct Render
        
        struct GHash *orco_hash;
 
+       struct GHash *sss_hash;
+       ListBase *sss_points;
+       struct Material *sss_mat;
+
        ListBase customdata_names;
 
        /* arena for allocating data for use during render, for
@@ -262,7 +269,7 @@ typedef struct VlakRen {
        unsigned int lay;
        float n[3];
        struct Material *mat;
-       char snproj, puno;
+       char noflag, puno;
        char flag, ec;
        RadFace *radface;
        Object *ob;
@@ -393,7 +400,11 @@ typedef struct LampRen {
 /* vertex normals are tangent or view-corrected vector, for hair strands */
 #define R_TANGENT              128             
 
-
+/* vlakren->noflag (char) */
+#define R_SNPROJ_X             1
+#define R_SNPROJ_Y             2
+#define R_SNPROJ_Z             4
+#define R_FLIPPED_NO   8
 
 
 
index 9f22480f0cbfec6e0dbaf5caeb72ca32d1540655..651fd423e5d85ee98343dffe048435eec79b2950 100644 (file)
@@ -87,6 +87,8 @@ void zbufshadeDA(void);       /* Delta Accum Pixel Struct */
 void zbufshade_tile(struct RenderPart *pa);
 void zbufshadeDA_tile(struct RenderPart *pa);
 
+void zbufshade_sss_tile(struct RenderPart *pa);
+
 /* -------- ray.c ------- */
 
 extern void freeoctree(Render *re);
diff --git a/source/blender/render/intern/include/sss.h b/source/blender/render/intern/include/sss.h
new file mode 100644 (file)
index 0000000..660fb54
--- /dev/null
@@ -0,0 +1,65 @@
+/**
+ * $Id$
+ *
+ * ***** BEGIN GPL LICENSE BLOCK *****
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version. 
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+ *
+ * The Original Code is Copyright (C) 2007 Blender Foundation.
+ * All rights reserved.
+ *
+ * The Original Code is: all of this file.
+ *
+ * Contributor(s): none yet.
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ */
+
+#ifndef SSS_H
+#define SSS_H
+
+/* Generic multiple scattering API */
+
+struct ScatterSettings;
+typedef struct ScatterSettings ScatterSettings;
+
+struct ScatterTree;
+typedef struct ScatterTree ScatterTree;
+
+ScatterSettings *scatter_settings_new(float refl, float radius, float ior,
+       float reflfac, float frontweight, float backweight);
+void scatter_settings_free(ScatterSettings *ss);
+
+ScatterTree *scatter_tree_new(ScatterSettings *ss[3], float scale, float error,
+       float (*co)[3], float (*color)[3], float *area, int totpoint);
+void scatter_tree_build(ScatterTree *tree);
+void scatter_tree_sample(ScatterTree *tree, float *co, float *color);
+void scatter_tree_free(ScatterTree *tree);
+
+/* Internal renderer API */
+
+struct Render;
+struct Material;
+struct VlakRen;
+
+void make_sss_tree(struct Render *re);
+void sss_add_points(Render *re, float (*co)[3], float (*color)[3], float *area, int totpoint);
+void free_sss(struct Render *re);
+
+int sample_sss(struct Render *re, struct Material *mat, float *co, float *col);
+int has_sss_tree(struct Render *re, struct Material *mat);
+
+#endif /*SSS_H*/
+
index c308ae25bceb70082bae07c07ea93b2950b4602a..ed3f93adfdd0f3885032a31d0187ca70a0699d05 100644 (file)
@@ -51,6 +51,7 @@ void zbuffer_shadow(struct Render *re, struct LampRen *lar, int *rectz, int size
 void zbuffer_solid(struct RenderPart *pa, unsigned int layer, short layflag);
 unsigned short *zbuffer_transp_shade(struct RenderPart *pa, struct RenderLayer *rl, float *pass);
 void convert_zbuf_to_distbuf(struct RenderPart *pa, struct RenderLayer *rl);
+void zbuffer_sss(RenderPart *pa, unsigned int lay, void *handle, void (*func)(void *, int, int, int, int));
 
 typedef struct APixstr {
     unsigned short mask[4]; /* jitter mask */
@@ -85,6 +86,9 @@ typedef struct ZSpan {
        int polygon_offset;                                             /* offset in Z */
        float shad_alpha;                                               /* copy from material, used by irregular shadbuf */
        int mask, apsmcounter;                                  /* in use by apixbuf */
+
+       void *sss_handle;                                               /* used by sss */
+       void (*sss_func)(void *, int, int, int, int);
        
        void (*zbuffunc)(struct ZSpan *, int, float *, float *, float *, float *);
        void (*zbuflinefunc)(struct ZSpan *, int, float *, float *);
index e93a509efd1160ef1f60dc0b75a14d04a8231db0..afa9aa0b2e431bb65df2f825228c0f390f534f0e 100644 (file)
 #include "shadbuf.h"
 #include "shading.h"
 #include "texture.h"
+#include "sss.h"
 #include "zbuf.h"
 
 #ifndef DISABLE_YAFRAY /* disable yafray */
@@ -2990,6 +2991,8 @@ void RE_Database_Free(Render *re)
        }
        
        if(re->r.mode & R_RAYTRACE) freeoctree(re);
+
+       free_sss(re);
        
        re->totvlak=re->totvert=re->totlamp=re->tothalo= 0;
        re->i.convertdone= 0;
@@ -3480,6 +3483,11 @@ void RE_Database_FromScene(Render *re, Scene *scene, int use_camera_view)
                
                if(!re->test_break())
                        project_renderdata(re, projectverto, re->r.mode & R_PANORAMA, 0);
+
+               /* SSS */
+               if(!re->test_break())
+                       if (re->r.renderer==R_INTERN)
+                               make_sss_tree(re);
        }
        
        if(re->test_break())
index c1f756894fe2a0ae3e34717280a6c9d80fd781bd..ec55bc0a5e2852300ceb2697c22e8eedabb21112 100644 (file)
@@ -410,7 +410,7 @@ static void render_envmap(Render *re, EnvMap *env)
                env_set_imats(envre);
                                
                if(re->test_break()==0) {
-                       RE_TileProcessor(envre, 0);
+                       RE_TileProcessor(envre, 0, 0);
                }
                
                /* rotate back */
index d93ee448f6231129080daa8689999fc86ecce963..93c80749bca234c295d5236e503f94b2d054bac1 100644 (file)
@@ -1163,16 +1163,17 @@ static void *do_part_thread(void *pa_v)
        
        /* need to return nicely all parts on esc */
        if(R.test_break()==0) {
-               
                pa->result= new_render_result(&R, &pa->disprect, pa->crop, RR_USEMEM);
-               
-               if(R.osa)
+
+               if(R.sss_points)
+                       zbufshade_sss_tile(pa);
+               else if(R.osa)
                        zbufshadeDA_tile(pa);
                else
                        zbufshade_tile(pa);
                
                /* merge too on break! */
-               if(R.result->exrhandle)
+               if(!R.sss_points && R.result->exrhandle)
                        save_render_result_tile(&R, pa);
                else
                        merge_render_result(R.result, pa->result);
@@ -1464,7 +1465,7 @@ static void threaded_tile_processor(Render *re)
 }
 
 /* currently only called by preview renders and envmap */
-void RE_TileProcessor(Render *re, int firsttile)
+void RE_TileProcessor(Render *re, int firsttile, int threaded)
 {
        /* the partsdone variable has to be reset to firsttile, to survive esc before it was set to zero */
        
@@ -1472,8 +1473,10 @@ void RE_TileProcessor(Render *re, int firsttile)
 
        re->i.starttime= PIL_check_seconds_timer();
 
-       //      threaded_tile_processor(re);
-       render_tile_processor(re, firsttile);
+       if(threaded)
+               threaded_tile_processor(re);
+       else
+               render_tile_processor(re, firsttile);
                
        re->i.lastframetime= PIL_check_seconds_timer()- re->i.starttime;
        re->stats_draw(&re->i);
index 10201087f2d27a0dc72021e29d5b1c6a2fbbfcec..1c5f56362bc4957e6b5f6ac7048664c953fd4068 100644 (file)
@@ -64,6 +64,7 @@
 #include "pixelshading.h"
 #include "shadbuf.h"
 #include "shading.h"
+#include "sss.h"
 #include "zbuf.h"
 
 #include "PIL_time.h"
@@ -1113,6 +1114,298 @@ void zbufshade_tile(RenderPart *pa)
        MEM_freeN(pa->clipflag); pa->clipflag= NULL;
 }
 
+/* SSS preprocess tile render, fully threadable */
+typedef struct ZBufSSSHandle {
+       RenderPart *pa;
+       ListBase psmlist;
+       int totps;
+} ZBufSSSHandle;
+
+static void addps_sss(void *cb_handle, int facenr, int x, int y, int z)
+{
+       ZBufSSSHandle *handle = cb_handle;
+       RenderPart *pa= handle->pa;
+
+       if (pa->rectall) {
+               long *rs= pa->rectall + pa->rectx*y + x;
+
+               addps(&handle->psmlist, rs, facenr, z, 0);
+               handle->totps++;
+       }
+       if (pa->rectz) {
+               int *rz= pa->rectz + pa->rectx*y + x;
+               int *rp= pa->rectp + pa->rectx*y + x;
+
+               if (z < *rz) {
+                       if(*rp == 0)
+                               handle->totps++;
+                       *rz= z;
+                       *rp= facenr;
+               }
+       }
+       if (pa->rectbackz) {
+               int *rz= pa->rectbackz + pa->rectx*y + x;
+               int *rp= pa->rectbackp + pa->rectx*y + x;
+
+               if (z >= *rz) {
+                       if(*rp == 0)
+                               handle->totps++;
+                       *rz= z;
+                       *rp= facenr;
+               }
+       }
+}
+
+static void shade_sample_sss(ShadeSample *ssamp, Material *mat, VlakRen *vlr, int quad, int x, int y, float z, float *co, float *color, float *area)
+{
+       ShadeInput *shi= ssamp->shi;
+       ShadeResult shr;
+       float texfac, orthoarea, nor[3];
+
+       /* normal flipping must be disabled to make back scattering work, so that
+          backside faces actually face any lighting from the back */
+       shi->puno= 0;
+       
+       /* cache for shadow */
+       shi->samplenr++;
+       
+       if(quad) 
+               shade_input_set_triangle_i(shi, vlr, 0, 2, 3);
+       else
+               shade_input_set_triangle_i(shi, vlr, 0, 1, 2);
+
+       /* we don't want flipped normals, they screw up back scattering */
+       if(vlr->noflag & R_FLIPPED_NO) {
+               shi->facenor[0]= -shi->facenor[0];
+               shi->facenor[1]= -shi->facenor[1];
+               shi->facenor[2]= -shi->facenor[2];
+       }
+
+       /* we estimate the area here using shi->dxco and shi->dyco. we need to
+          enabled shi->osatex these are filled. we compute two areas, one with
+          the normal pointed at the camera and one with the original normal, and
+          then clamp to avoid a too large contribution from a single pixel */
+       shi->osatex= 1;
+
+       VECCOPY(nor, shi->facenor);
+       calc_view_vector(shi->facenor, x, y);
+       Normalize(shi->facenor);
+       shade_input_set_viewco(shi, x, y, z);
+       orthoarea= VecLength(shi->dxco)*VecLength(shi->dyco);
+
+       VECCOPY(shi->facenor, nor);
+       shade_input_set_viewco(shi, x, y, z);
+       *area= VecLength(shi->dxco)*VecLength(shi->dyco);
+       *area= MIN2(*area, 2.0f*orthoarea);
+
+       shi->osatex= 0;
+
+       shade_input_set_uv(shi);
+       shade_input_set_normals(shi);
+
+       /* if nodetree, use the material that we are currently preprocessing
+          instead of the node material */
+       if(shi->mat->nodetree && shi->mat->use_nodes)
+               shi->mat= mat;
+
+       /* init material vars */
+       // note, keep this synced with render_types.h
+       memcpy(&shi->r, &shi->mat->r, 23*sizeof(float));
+       shi->har= shi->mat->har;
+       
+       /* render */
+       shade_input_set_shade_texco(shi);
+       
+       shade_samples_do_AO(ssamp);
+       shade_material_loop(shi, &shr);
+       
+       VECCOPY(co, shi->co);
+       VECCOPY(color, shr.combined);
+
+       /* texture blending */
+       texfac= shi->mat->sss_texfac;
+
+       if(texfac == 0.0f) {
+               if(shr.col[0]!=0.0f) color[0] /= shr.col[0];
+               if(shr.col[1]!=0.0f) color[1] /= shr.col[1];
+               if(shr.col[2]!=0.0f) color[2] /= shr.col[2];
+       }
+       else if(texfac != 1.0f) {
+               if(shr.col[0]!=0.0f) color[0] *= pow(shr.col[0], texfac)/shr.col[0];
+               if(shr.col[1]!=0.0f) color[1] *= pow(shr.col[1], texfac)/shr.col[1];
+               if(shr.col[2]!=0.0f) color[2] *= pow(shr.col[2], texfac)/shr.col[2];
+       }
+}
+
+void zbufshade_sss_tile(RenderPart *pa)
+{
+       ShadeSample ssamp;
+       ZBufSSSHandle handle;
+       RenderResult *rr= pa->result;
+       RenderLayer *rl= rr->layers.first;
+       VlakRen *vlr;
+       Material *mat= R.sss_mat;
+       float (*co)[3], (*color)[3], *area, *fcol= rl->rectf;
+       int x, y, seed, quad, totpoint;
+#if 0
+       PixStr *ps;
+       long *rs;
+       int z;
+#else
+       int *rz, *rp, *rbz, *rbp;
+#endif
+
+       set_part_zbuf_clipflag(pa);
+
+       /* setup pixelstr list and buffer for zbuffering */
+       handle.pa= pa;
+       handle.totps= 0;
+
+#if 0
+       handle.psmlist.first= handle.psmlist.last= NULL;
+       addpsmain(&handle.psmlist);
+
+       pa->rectall= MEM_callocN(sizeof(long)*pa->rectx*pa->recty+4, "rectall");
+#else
+       pa->rectp= MEM_mallocN(sizeof(int)*pa->rectx*pa->recty, "rectp");
+       pa->rectz= MEM_mallocN(sizeof(int)*pa->rectx*pa->recty, "rectz");
+       pa->rectbackp= MEM_mallocN(sizeof(int)*pa->rectx*pa->recty, "rectbackp");
+       pa->rectbackz= MEM_mallocN(sizeof(int)*pa->rectx*pa->recty, "rectbackz");
+#endif
+
+       /* create the pixelstrs to be used later */
+       zbuffer_sss(pa, rl->lay, &handle, addps_sss);
+
+       co= MEM_mallocN(sizeof(float)*3*handle.totps, "SSSCo");
+       color= MEM_mallocN(sizeof(float)*3*handle.totps, "SSSColor");
+       area= MEM_mallocN(sizeof(float)*handle.totps, "SSSArea");
+
+#if 0
+       /* create ISB (does not work currently!) */
+       if(R.r.mode & R_SHADOW)
+               ISB_create(pa, NULL);
+#endif
+
+       /* setup shade sample with correct passes */
+       shade_sample_initialize(&ssamp, pa, rl);
+       ssamp.shi[0].passflag= SCE_PASS_DIFFUSE|SCE_PASS_AO|SCE_PASS_RADIO;
+       ssamp.shi[0].passflag |= SCE_PASS_RGBA;
+       ssamp.shi[0].combinedflag= ~(SCE_PASS_SPEC);
+
+       /* initialize scanline updates for main thread */
+       rr->renrect.ymin= 0;
+       rr->renlay= rl;
+       
+       seed= pa->rectx*pa->disprect.ymin;
+#if 0
+       rs= pa->rectall;
+#else
+       rz= pa->rectz;
+       rp= pa->rectp;
+       rbz= pa->rectbackz;
+       rbp= pa->rectbackp;
+#endif
+       totpoint= 0;
+
+       for(y=pa->disprect.ymin; y<pa->disprect.ymax; y++, rr->renrect.ymax++) {
+               for(x=pa->disprect.xmin; x<pa->disprect.xmax; x++, fcol+=4) {
+                       /* per pixel fixed seed */
+                       BLI_thread_srandom(pa->thread, seed++);
+                       
+#if 0
+                       if(rs) {
+                               /* for each sample in this pixel, shade it */
+                               for(ps=(PixStr*)*rs; ps; ps=ps->next) {
+                                       vlr= RE_findOrAddVlak(&R, (ps->facenr-1) & RE_QUAD_MASK);
+                                       quad= (ps->facenr & RE_QUAD_OFFS);
+                                       z= ps->z;
+
+                                       shade_sample_sss(&ssamp, mat, vlr, quad, x, y, z,
+                                               co[totpoint], color[totpoint], &area[totpoint]);
+
+                                       totpoint++;
+
+                                       VECADD(fcol, fcol, color);
+                                       fcol[3]= 1.0f;
+                               }
+
+                               rs++;
+                       }
+#else
+                       if(rp) {
+                               if(*rp != 0) {
+                                       /* shade front */
+                                       vlr= RE_findOrAddVlak(&R, (*rp-1) & RE_QUAD_MASK);
+                                       quad= ((*rp) & RE_QUAD_OFFS);
+
+                                       shade_sample_sss(&ssamp, mat, vlr, quad, x, y, *rz,
+                                               co[totpoint], color[totpoint], &area[totpoint]);
+
+                                       VECADD(fcol, fcol, color[totpoint]);
+                                       fcol[3]= 1.0f;
+                                       totpoint++;
+                               }
+
+                               rp++; rz++;
+                       }
+
+                       if(rbp) {
+                               if(*rbp != 0 && *rbp != *(rp-1)) {
+                                       /* shade back */
+                                       vlr= RE_findOrAddVlak(&R, (*rbp-1) & RE_QUAD_MASK);
+                                       quad= ((*rbp) & RE_QUAD_OFFS);
+
+                                       shade_sample_sss(&ssamp, mat, vlr, quad, x, y, *rbz,
+                                               co[totpoint], color[totpoint], &area[totpoint]);
+                                       
+                                       /* to indicate this is a back sample */
+                                       area[totpoint]= -area[totpoint];
+
+                                       VECADD(fcol, fcol, color[totpoint]);
+                                       fcol[3]= 1.0f;
+                                       totpoint++;
+                               }
+
+                               rbz++; rbp++;
+                       }
+#endif
+               }
+
+               if(y&1)
+                       if(R.test_break()) break; 
+       }
+
+       /* note: after adding we do not free these arrays, sss keeps them */
+       if(totpoint > 0) {
+               sss_add_points(&R, co, color, area, totpoint);
+       }
+       else {
+               MEM_freeN(co);
+               MEM_freeN(color);
+               MEM_freeN(area);
+       }
+       
+#if 0
+       if(R.r.mode & R_SHADOW)
+               ISB_free(pa);
+#endif
+               
+       /* display active layer */
+       rr->renrect.ymin=rr->renrect.ymax= 0;
+       rr->renlay= render_get_active_layer(&R, rr);
+       
+       MEM_freeN(pa->clipflag); pa->clipflag= NULL;
+#if 0
+       MEM_freeN(pa->rectall); pa->rectall= NULL;
+       freeps(&handle.psmlist);
+#else
+       MEM_freeN(pa->rectz); pa->rectz= NULL;
+       MEM_freeN(pa->rectp); pa->rectp= NULL;
+       MEM_freeN(pa->rectbackz); pa->rectbackz= NULL;
+       MEM_freeN(pa->rectbackp); pa->rectbackp= NULL;
+#endif
+}
+
 /* ------------------------------------------------------------------------ */
 
 static void renderhalo_post(RenderResult *rr, float *rectf, HaloRen *har)      /* postprocess version */
index 57f0f689871b0414aec56a61f31f7324d35026c2..6d9f0e4eb01c4f4eef98c8b644624482ab09bade 100644 (file)
@@ -894,6 +894,8 @@ void set_normalflags(Render *re)
                if((a1 & 255)==0) vlr= re->vlaknodes[a1>>8].vlak;
                else vlr++;
                
+               vlr->noflag= 0;
+
                /* abuse of this flag... this is code that just sets face normal in direction of camera */
                /* that convention we should get rid of */
                if((vlr->flag & R_NOPUNOFLIP)==0) {
@@ -910,6 +912,7 @@ void set_normalflags(Render *re)
                                vlr->n[0]= -vlr->n[0];
                                vlr->n[1]= -vlr->n[1];
                                vlr->n[2]= -vlr->n[2];
+                               vlr->noflag |= R_FLIPPED_NO;
                        }
                }
                
@@ -924,9 +927,9 @@ void set_normalflags(Render *re)
                xn= fabs(vlr->n[0]);
                yn= fabs(vlr->n[1]);
                zn= fabs(vlr->n[2]);
-               if(zn>=xn && zn>=yn) vlr->snproj= 0;
-               else if(yn>=xn && yn>=zn) vlr->snproj= 1;
-               else vlr->snproj= 2;
+               if(zn>=xn && zn>=yn) vlr->noflag |= R_SNPROJ_X;
+               else if(yn>=xn && yn>=zn) vlr->noflag |= R_SNPROJ_Y;
+               else vlr->noflag |= R_SNPROJ_Z;
 
        }
 }
index d9126a3cdb2355b5cfb1abda13920c6be20456dc..51157cb83f44a216794f840be964739822dba4d2 100644 (file)
@@ -429,11 +429,11 @@ void shade_input_set_uv(ShadeInput *shi)
                        /* most of this could become re-used for faces */
                        float detsh, t00, t10, t01, t11;
                        
-                       if(vlr->snproj==0) {
+                       if(vlr->noflag & R_SNPROJ_X) {
                                t00= v3[0]-v1[0]; t01= v3[1]-v1[1];
                                t10= v3[0]-v2[0]; t11= v3[1]-v2[1];
                        }
-                       else if(vlr->snproj==1) {
+                       else if(vlr->noflag & R_SNPROJ_Y) {
                                t00= v3[0]-v1[0]; t01= v3[2]-v1[2];
                                t10= v3[0]-v2[0]; t11= v3[2]-v2[2];
                        }
@@ -446,7 +446,7 @@ void shade_input_set_uv(ShadeInput *shi)
                        t00*= detsh; t01*=detsh; 
                        t10*=detsh; t11*=detsh;
                        
-                       if(vlr->snproj==0) {
+                       if(vlr->noflag & R_SNPROJ_X) {
                                shi->u= (shi->co[0]-v3[0])*t11-(shi->co[1]-v3[1])*t10;
                                shi->v= (shi->co[1]-v3[1])*t00-(shi->co[0]-v3[0])*t01;
                                if(shi->osatex) {
@@ -456,7 +456,7 @@ void shade_input_set_uv(ShadeInput *shi)
                                        shi->dy_v=  shi->dyco[1]*t00- shi->dyco[0]*t01;
                                }
                        }
-                       else if(vlr->snproj==1) {
+                       else if(vlr->noflag & R_SNPROJ_Y) {
                                shi->u= (shi->co[0]-v3[0])*t11-(shi->co[2]-v3[2])*t10;
                                shi->v= (shi->co[2]-v3[2])*t00-(shi->co[0]-v3[0])*t01;
                                if(shi->osatex) {
index c2aeee6aab6445b95a5fbe6040a1f40e71c8b8c4..7dd7383c60c7c3a2450d0cb7c64b4dde18ff4f5d 100644 (file)
@@ -46,6 +46,7 @@
 #include "pixelblending.h"
 #include "rendercore.h"
 #include "shadbuf.h"
+#include "sss.h"
 #include "texture.h"
 
 /* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
@@ -1506,6 +1507,35 @@ void shade_lamp_loop(ShadeInput *shi, ShadeResult *shr)
                        /* accumulates in shr->diff and shr->spec and shr->shad (diffuse with shadow!) */
                        shade_one_light(lar, shi, shr, passflag);
                }
+
+               if(ma->sss_flag & MA_DIFF_SSS) {
+                       float sss[3], col[3], texfac= ma->sss_texfac;
+
+                       /* this will return false in the preprocess stage */
+                       if(sample_sss(&R, ma, shi->co, sss)) {
+                               if(texfac==0.0f) {
+                                       VECCOPY(col, shr->col);
+                               }
+                               else if(texfac==1.0f) {
+                                       col[0]= col[1]= col[2]= 1.0f;
+                               }
+                               else {
+                                       col[0]= pow(shr->col[0], 1.0f-texfac);
+                                       col[1]= pow(shr->col[1], 1.0f-texfac);
+                                       col[2]= pow(shr->col[2], 1.0f-texfac);
+                               }
+
+                               shr->diff[0]= sss[0]*col[0];
+                               shr->diff[1]= sss[1]*col[1];
+                               shr->diff[2]= sss[2]*col[2];
+
+                               if(shi->combinedflag & SCE_PASS_SHADOW) {
+                                       shr->shad[0]= sss[0]*col[0];
+                                       shr->shad[1]= sss[1]*col[1];
+                                       shr->shad[2]= sss[2]*col[2];
+                               }
+                       }
+               }
                
                if(shi->combinedflag & SCE_PASS_SHADOW) 
                        VECCOPY(shr->combined, shr->shad)       /* note, no ';' ! */
@@ -1544,23 +1574,26 @@ void shade_lamp_loop(ShadeInput *shi, ShadeResult *shr)
        shr->alpha= shi->alpha;
        
        /* from now stuff everything in shr->combined: ambient, AO, radio, ramps, exposure */
-       shr->combined[0]+= shi->ambr + shi->r*shi->amb*shi->rad[0];
-       shr->combined[1]+= shi->ambg + shi->g*shi->amb*shi->rad[1];
-       shr->combined[2]+= shi->ambb + shi->b*shi->amb*shi->rad[2];
-       
-       /* add AO in combined? */
-       if(R.wrld.mode & WO_AMB_OCC) {
-               if(shi->combinedflag & SCE_PASS_AO) {
-                       float aodiff[3];
-                       ambient_occlusion_to_diffuse(shi, aodiff);
-                       
-                       shr->combined[0] += shi->r*aodiff[0];
-                       shr->combined[1] += shi->g*aodiff[1];
-                       shr->combined[2] += shi->b*aodiff[2];
+       if(!(ma->sss_flag & MA_DIFF_SSS) || !has_sss_tree(&R, ma)) {
+               shr->combined[0]+= shi->ambr + shi->r*shi->amb*shi->rad[0];
+               shr->combined[1]+= shi->ambg + shi->g*shi->amb*shi->rad[1];
+               shr->combined[2]+= shi->ambb + shi->b*shi->amb*shi->rad[2];
+               
+               /* add AO in combined? */
+               if(R.wrld.mode & WO_AMB_OCC) {
+                       if(shi->combinedflag & SCE_PASS_AO) {
+                               float aodiff[3];
+                               ambient_occlusion_to_diffuse(shi, aodiff);
+                               
+                               shr->combined[0] += shi->r*aodiff[0];
+                               shr->combined[1] += shi->g*aodiff[1];
+                               shr->combined[2] += shi->b*aodiff[2];
+                       }
                }
+               
+               if(ma->mode & MA_RAMP_COL) ramp_diffuse_result(shr->combined, shi);
        }
-       
-       if(ma->mode & MA_RAMP_COL) ramp_diffuse_result(shr->combined, shi);
+
        if(ma->mode & MA_RAMP_SPEC) ramp_spec_result(shr->spec, shr->spec+1, shr->spec+2, shi);
        
        /* refcol is for envmap only */
diff --git a/source/blender/render/intern/source/sss.c b/source/blender/render/intern/source/sss.c
new file mode 100644 (file)
index 0000000..2683356
--- /dev/null
@@ -0,0 +1,984 @@
+/* 
+ * $Id$
+ *
+ * ***** BEGIN GPL LICENSE BLOCK *****
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+ *
+ * The Original Code is Copyright (C) 2007 Blender Foundation.
+ * All rights reserved.
+ *
+ * The Original Code is: all of this file.
+ *
+ * Contributor(s): none yet.
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ */
+
+/* Possible Improvements:
+   - add fresnel terms
+   - adapt Rd table to scale, now with small scale there are a lot of misses?
+   - possible interesting method: perform sss on all samples in the tree,
+     and then use those values interpolated somehow later. can also do this
+        filtering on demand for speed. since we are doing things in screen
+        space now there is an exact correspondence
+   - avoid duplicate shading (filtering points in advance, irradiance cache
+     like lookup?)
+   - lower resolution samples
+*/
+
+#include <math.h>
+#include <string.h>
+#include <stdio.h>
+#include <string.h>
+
+/* external modules: */
+#include "MEM_guardedalloc.h"
+
+#include "BLI_arithb.h"
+#include "BLI_blenlib.h"
+#include "BLI_ghash.h"
+#include "BLI_memarena.h"
+#include "PIL_time.h"
+
+#include "DNA_material_types.h"
+
+#include "BKE_global.h"
+#include "BKE_main.h"
+#include "BKE_material.h"
+#include "BKE_node.h"
+#include "BKE_utildefines.h"
+
+/* this module */
+#include "render_types.h"
+#include "rendercore.h"
+#include "renderdatabase.h" 
+#include "shading.h"
+#include "sss.h"
+#include "zbuf.h"
+
+extern Render R; // meh
+
+/* Generic Multiple Scattering API */
+
+/* Relevant papers:
+       [1] A Practical Model for Subsurface Light Transport
+       [2] A Rapid Hierarchical Rendering Technique for Translucent Materials
+       [3] Efficient Rendering of Local Subsurface Scattering
+       [4] Implementing a skin BSSRDF (or several...)
+*/
+
+/* Defines */
+
+#define RD_TABLE_RANGE                 100.0f
+#define RD_TABLE_RANGE_2       10000.0f
+#define RD_TABLE_SIZE          10000
+
+#define MAX_OCTREE_NODE_POINTS 8
+#define MAX_OCTREE_DEPTH               15
+
+/* Struct Definitions */
+
+struct ScatterSettings {
+       float eta;              /* index of refraction */
+       float sigma_a;  /* absorption coefficient */
+       float sigma_s_; /* reduced scattering coefficient */
+       float sigma_t_; /* reduced extinction coefficient */
+       float sigma;    /* effective extinction coefficient */
+       float Fdr;              /* diffuse fresnel reflectance */
+       float D;                /* diffusion constant */
+       float A;
+       float alpha_;   /* reduced albedo */
+       float zr;               /* distance of virtual lightsource above surface */
+       float zv;               /* distance of virtual lightsource below surface */
+       float ld;               /* mean free path */
+       float ro;               /* diffuse reflectance */
+       float color;
+       float invsigma_t_;
+       float frontweight;
+       float backweight;
+
+       float *tableRd;  /* lookup table to avoid computing Rd */
+       float *tableRd2; /* lookup table to avoid computing Rd for bigger values */
+};
+
+typedef struct ScatterPoint {
+       float co[3];
+       float radiance[3];
+       float area;
+       int back;
+} ScatterPoint;
+
+typedef struct ScatterNode {
+       float co[3];
+       float radiance[3];
+       float backradiance[3];
+       float area;
+       int back;
+
+       int totpoint;
+       ScatterPoint *points;
+
+       float split[3];
+       struct ScatterNode *child[8];
+} ScatterNode;
+
+struct ScatterTree {
+       MemArena *arena;
+
+       ScatterSettings *ss[3];
+       float error, scale;
+
+       ScatterNode *root;
+       ScatterPoint *points;
+       ScatterPoint **refpoints;
+       ScatterPoint **tmppoints;
+       int totpoint;
+       float min[3], max[3];
+};
+
+typedef struct ScatterResult {
+       float radiance[3];
+       float backradiance[3];
+       float rdsum[3];
+} ScatterResult;
+
+/* Functions for BSSRDF reparametrization in to more intuitive parameters,
+   see [2] section 4 for more info. */
+
+static float f_Rd(float alpha_, float A, float ro)
+{
+       float sq;
+
+       sq= sqrt(3.0f*(1.0f - alpha_));
+       return (alpha_/2.0f)*(1.0f + exp((-4.0f/3.0f)*A*sq))*exp(-sq) - ro;
+}
+
+static float compute_reduced_albedo(ScatterSettings *ss)
+{
+       const float tolerance= 1e-8;
+       const int max_iteration_count= 20;
+       float d, fsub, xn_1= 0.0f , xn= 1.0f, fxn, fxn_1;
+       int i;
+
+       /* use secant method to compute reduced albedo using Rd function inverse
+          with a given reflectance */
+       fxn= f_Rd(xn, ss->A, ss->ro);
+       fxn_1= f_Rd(xn_1, ss->A, ss->ro);
+
+       for(i= 0; i < max_iteration_count; i++) {
+               fsub= (fxn - fxn_1);
+               if(fabs(fsub) < tolerance)
+                       break;
+               d= ((xn - xn_1)/fsub)*fxn;
+               if(fabs(d) < tolerance)
+                       break;
+
+               xn_1= xn;
+               fxn_1= fxn;
+               xn= xn - d;
+
+               if(xn > 1.0f) xn= 1.0f;
+               if(xn_1 > 1.0f) xn_1= 1.0f;
+               
+               fxn= f_Rd(xn, ss->A, ss->ro);
+    }
+
+       /* avoid division by zero later */
+       if(xn <= 0.0f)
+               xn= 0.00001f;
+
+    return xn;
+}
+
+/* Exponential falloff functions */
+
+static float Rd_rsquare(ScatterSettings *ss, float rr)
+{
+       float sr, sv, Rdr, Rdv;
+
+       sr= sqrt(rr + ss->zr*ss->zr);
+       sv= sqrt(rr + ss->zv*ss->zv);
+
+       Rdr= ss->zr*(1.0f + ss->sigma*sr)*exp(-ss->sigma*sr)/(sr*sr*sr);
+       Rdv= ss->zv*(1.0f + ss->sigma*sv)*exp(-ss->sigma*sv)/(sv*sv*sv);
+
+       return /*ss->alpha_*/(1.0f/(4.0f*M_PI))*(Rdr + Rdv);
+}
+
+static float Rd(ScatterSettings *ss, float r)
+{
+       return Rd_rsquare(ss, r*r);
+}
+
+/* table lookups for Rd. this avoids expensive exp calls. we use two
+   separate tables as well for lower and higher numbers to improve
+   precision, since the number are poorly distributed because we do
+   a lookup with the squared distance for smaller distances, saving
+   another sqrt. */
+
+static void approximate_Rd_rgb(ScatterSettings **ss, float rr, float *rd)
+{
+       float indexf, t, idxf;
+       int index;
+
+       if(rr > (RD_TABLE_RANGE_2*RD_TABLE_RANGE_2)) {
+               rd[0]= Rd_rsquare(ss[0], rr);
+               rd[1]= Rd_rsquare(ss[1], rr);
+               rd[2]= Rd_rsquare(ss[2], rr);
+       }
+       else if(rr > RD_TABLE_RANGE) {
+               rr= sqrt(rr);
+               indexf= rr*(RD_TABLE_SIZE/RD_TABLE_RANGE_2);
+               index= (int)indexf;
+               idxf= (float)index;
+               t= indexf - idxf;
+
+               rd[0]= (ss[0]->tableRd2[index]*(1-t) + ss[0]->tableRd2[index+1]*t);
+               rd[1]= (ss[1]->tableRd2[index]*(1-t) + ss[1]->tableRd2[index+1]*t);
+               rd[2]= (ss[2]->tableRd2[index]*(1-t) + ss[2]->tableRd2[index+1]*t);
+       }
+       else {
+               indexf= rr*(RD_TABLE_SIZE/RD_TABLE_RANGE);
+               index= (int)indexf;
+               idxf= (float)index;
+               t= indexf - idxf;
+
+               rd[0]= (ss[0]->tableRd[index]*(1-t) + ss[0]->tableRd[index+1]*t);
+               rd[1]= (ss[1]->tableRd[index]*(1-t) + ss[1]->tableRd[index+1]*t);
+               rd[2]= (ss[2]->tableRd[index]*(1-t) + ss[2]->tableRd[index+1]*t);
+       }
+}
+
+static void build_Rd_table(ScatterSettings *ss)
+{
+       float r;
+       int i, size = RD_TABLE_SIZE+1;
+
+       ss->tableRd= MEM_mallocN(sizeof(float)*size, "scatterTableRd");
+       ss->tableRd2= MEM_mallocN(sizeof(float)*size, "scatterTableRd");
+
+       for(i= 0; i < size; i++) {
+               r= i*(RD_TABLE_RANGE/RD_TABLE_SIZE);
+               /*if(r < ss->invsigma_t_*ss->invsigma_t_)
+                       r= ss->invsigma_t_*ss->invsigma_t_;*/
+               ss->tableRd[i]= Rd(ss, sqrt(r));
+
+               r= i*(RD_TABLE_RANGE_2/RD_TABLE_SIZE);
+               /*if(r < ss->invsigma_t_)
+                       r= ss->invsigma_t_;*/
+               ss->tableRd2[i]= Rd(ss, r);
+       }
+}
+
+ScatterSettings *scatter_settings_new(float refl, float radius, float ior, float reflfac, float frontweight, float backweight)
+{
+       ScatterSettings *ss;
+       
+       ss= MEM_callocN(sizeof(ScatterSettings), "ScatterSettings");
+
+       /* see [1] and [3] for these formulas */
+       ss->eta= ior;
+       ss->Fdr= -1.440f/ior*ior + 0.710f/ior + 0.668f + 0.0636f*ior;
+       ss->A= (1.0f + ss->Fdr)/(1.0f - ss->Fdr);
+       ss->ld= radius;
+       ss->ro= MIN2(refl, 0.999f);
+       ss->color= ss->ro*reflfac + (1.0f-reflfac);
+
+       ss->alpha_= compute_reduced_albedo(ss);
+
+       ss->sigma= 1.0f/ss->ld;
+       ss->sigma_t_= ss->sigma/sqrt(3.0f*(1.0f - ss->alpha_));
+       ss->sigma_s_= ss->alpha_*ss->sigma_t_;
+       ss->sigma_a= ss->sigma_t_ - ss->sigma_s_;
+
+       ss->D= 1.0f/(3.0f*ss->sigma_t_);
+
+       ss->zr= 1.0f/ss->sigma_t_;
+       ss->zv= ss->zr + 4.0f*ss->A*ss->D;
+
+       ss->invsigma_t_= 1.0f/ss->sigma_t_;
+
+       ss->frontweight= frontweight;
+       ss->backweight= backweight;
+
+       /* precompute a table of Rd values for quick lookup */
+       build_Rd_table(ss);
+
+       return ss;
+}
+
+void scatter_settings_free(ScatterSettings *ss)
+{
+       MEM_freeN(ss->tableRd);
+       MEM_freeN(ss->tableRd2);
+       MEM_freeN(ss);
+}
+
+/* Hierarchical method as in [2]. */
+
+/* traversal */
+
+#define SUBNODE_INDEX(co, split) \
+       ((co[0]>=split[0]) + (co[1]>=split[1])*2 + (co[2]>=split[2])*4)
+       
+static void add_radiance(ScatterTree *tree, float *frontrad, float *backrad, float area, float rr, ScatterResult *result)
+{
+       float rd[3];
+
+#if 0
+       rd[0]= Rd_rsquare(tree->ss[0], rr);
+       rd[1]= Rd_rsquare(tree->ss[1], rr);
+       rd[2]= Rd_rsquare(tree->ss[2], rr);
+#else
+       approximate_Rd_rgb(tree->ss, rr, rd);
+#endif
+
+       rd[0] *= area;
+       rd[1] *= area;
+       rd[2] *= area;
+
+       if(frontrad) {
+               result->radiance[0] += frontrad[0]*rd[0];
+               result->radiance[1] += frontrad[1]*rd[1];
+               result->radiance[2] += frontrad[2]*rd[2];
+       }
+       if(backrad) {
+               result->backradiance[0] += backrad[0]*rd[0];
+               result->backradiance[1] += backrad[1]*rd[1];
+               result->backradiance[2] += backrad[2]*rd[2];
+       }
+
+       result->rdsum[0] += rd[0];
+       result->rdsum[1] += rd[1];
+       result->rdsum[2] += rd[2];
+}
+
+static void traverse_octree(ScatterTree *tree, ScatterNode *node, float *co, int self, ScatterResult *result)
+{
+       float sub[3], dist;
+       int i, index = 0;
+
+       if(node->totpoint > 0) {
+               /* leaf - add radiance from all samples */
+               for(i=0; i<node->totpoint; i++) {
+                       ScatterPoint *p= &node->points[i];
+
+                       VECSUB(sub, co, p->co);
+                       dist= INPR(sub, sub);
+
+                       if(p->back)
+                               add_radiance(tree, NULL, p->radiance, p->area, dist, result);
+                       else
+                               add_radiance(tree, p->radiance, NULL, p->area, dist, result);
+               }
+       }
+       else {
+               /* branch */
+               if (self)
+                       index = SUBNODE_INDEX(co, node->split);
+
+               for(i=0; i<8; i++) {
+                       if(node->child[i]) {
+                               ScatterNode *subnode= node->child[i];
+
+                               if(self && index == i) {
+                                       /* always traverse node containing the point */
+                                       traverse_octree(tree, subnode, co, 1, result);
+                               }
+                               else {
+                                       /* decide subnode traversal based on maximum solid angle */
+                                       VECSUB(sub, co, subnode->co);
+                                       dist= INPR(sub, sub);
+
+                                       /* actually area/dist > error, but this avoids division */
+                                       if(subnode->area>tree->error*dist) {
+                                               traverse_octree(tree, subnode, co, 0, result);
+                                       }
+                                       else {
+                                               add_radiance(tree, subnode->radiance,
+                                                       subnode->backradiance, subnode->area, dist, result);
+                                       }
+                               }
+                       }
+               }
+       }
+}
+
+static void compute_radiance(ScatterTree *tree, float *co, float *radiance)
+{
+       ScatterResult result;
+       float rdsum[3];
+
+       memset(&result, 0, sizeof(result));
+
+       traverse_octree(tree, tree->root, co, 1, &result);
+
+       VecMulf(result.radiance, tree->ss[0]->frontweight);
+       VecMulf(result.backradiance, tree->ss[0]->backweight);
+
+       VECCOPY(radiance, result.backradiance);
+       VECADD(radiance, radiance, result.radiance);
+
+       VECCOPY(rdsum, result.rdsum);
+
+       /* the original paper doesn't do this, but we normalize over the
+          sampled area and multiply with the reflectance. this is because
+          our point samples are incomplete, there are no samples on parts
+          of the mesh not visible from the camera. this can not only make
+          it darker, but also lead to ugly color shifts */
+       if(rdsum[0] > 0.0f) radiance[0]= tree->ss[0]->color*radiance[0]/rdsum[0];
+       if(rdsum[1] > 0.0f) radiance[1]= tree->ss[1]->color*radiance[1]/rdsum[1];
+       if(rdsum[2] > 0.0f) radiance[2]= tree->ss[2]->color*radiance[2]/rdsum[2];
+}
+
+/* building */
+
+static void sum_leaf_radiance(ScatterTree *tree, ScatterNode *node)
+{
+       ScatterPoint *p;
+       float radiance, totradiance= 0.0f, inv;
+       int i;
+
+       node->co[0]= node->co[1]= node->co[2]= 0.0;
+       node->radiance[0]= node->radiance[1]= node->radiance[2]= 0.0;
+       node->backradiance[0]= node->backradiance[1]= node->backradiance[2]= 0.0;
+
+       /* compute total radiance, radiance weighted average position,
+          and total area */
+       for(i=0; i<node->totpoint; i++) {
+               p= &node->points[i];
+
+               radiance= p->area*(p->radiance[0] + p->radiance[1] + p->radiance[2]);
+               totradiance += radiance;
+
+               node->co[0] += radiance*p->co[0];
+               node->co[1] += radiance*p->co[1];
+               node->co[2] += radiance*p->co[2];
+
+               if(p->back) {
+                       node->backradiance[0] += p->radiance[0]*p->area;
+                       node->backradiance[1] += p->radiance[1]*p->area;
+                       node->backradiance[2] += p->radiance[2]*p->area;
+               }
+               else {
+                       node->radiance[0] += p->radiance[0]*p->area;
+                       node->radiance[1] += p->radiance[1]*p->area;
+                       node->radiance[2] += p->radiance[2]*p->area;
+               }
+
+               node->area += p->area;
+       }
+
+       if(node->area > 0) {
+               inv= 1.0/node->area;
+               node->radiance[0] *= inv;
+               node->radiance[1] *= inv;
+               node->radiance[2] *= inv;
+               node->backradiance[0] *= inv;
+               node->backradiance[1] *= inv;
+               node->backradiance[2] *= inv;
+       }
+
+       if(totradiance > 0.0f) {
+               inv= 1.0/totradiance;
+               node->co[0] *= inv;
+               node->co[1] *= inv;
+               node->co[2] *= inv;
+       }
+       else {
+               /* make sure that if radiance is 0.0f, we still have these points in
+                  the tree at a good position, they count for rdsum too */
+               for(i=0; i<node->totpoint; i++) {
+                       p= &node->points[i];
+
+                       node->co[0] += p->co[0];
+                       node->co[1] += p->co[1];
+                       node->co[2] += p->co[2];
+               }
+
+               node->co[0] /= node->totpoint;
+               node->co[1] /= node->totpoint;
+               node->co[2] /= node->totpoint;
+       }
+}
+
+static void sum_branch_radiance(ScatterTree *tree, ScatterNode *node)
+{
+       ScatterNode *subnode;
+       float radiance, totradiance= 0.0f, inv;
+       int i, totnode;
+
+       node->co[0]= node->co[1]= node->co[2]= 0.0;
+       node->radiance[0]= node->radiance[1]= node->radiance[2]= 0.0;
+       node->backradiance[0]= node->backradiance[1]= node->backradiance[2]= 0.0;
+
+       /* compute total radiance, radiance weighted average position,
+          and total area */
+       for(i=0; i<8; i++) {
+               if(node->child[i] == NULL)
+                       continue;
+
+               subnode= node->child[i];
+
+               radiance= subnode->area*(subnode->radiance[0] + subnode->radiance[1] + subnode->radiance[2] + subnode->backradiance[0] + subnode->backradiance[1] + subnode->backradiance[2]);
+               totradiance += radiance;
+
+               node->co[0] += radiance*subnode->co[0];
+               node->co[1] += radiance*subnode->co[1];
+               node->co[2] += radiance*subnode->co[2];
+
+               node->radiance[0] += subnode->radiance[0]*subnode->area;
+               node->radiance[1] += subnode->radiance[1]*subnode->area;
+               node->radiance[2] += subnode->radiance[2]*subnode->area;
+
+               node->backradiance[0] += subnode->backradiance[0]*subnode->area;
+               node->backradiance[1] += subnode->backradiance[1]*subnode->area;
+               node->backradiance[2] += subnode->backradiance[2]*subnode->area;
+
+               node->area += subnode->area;
+       }
+
+       if(node->area > 0) {
+               inv= 1.0/node->area;
+               node->radiance[0] *= inv;
+               node->radiance[1] *= inv;
+               node->radiance[2] *= inv;
+               node->backradiance[0] *= inv;
+               node->backradiance[1] *= inv;
+               node->backradiance[2] *= inv;
+       }
+
+       if(totradiance > 0.0f) {
+               inv= 1.0/totradiance;
+               node->co[0] *= inv;
+               node->co[1] *= inv;
+               node->co[2] *= inv;
+       }
+       else {
+               /* make sure that if radiance is 0.0f, we still have these points in
+                  the tree at a good position, they count for rdsum too */
+               totnode= 0;
+
+               for(i=0; i<8; i++) {
+                       if(node->child[i]) {
+                               subnode= node->child[i];
+
+                               node->co[0] += subnode->co[0];
+                               node->co[1] += subnode->co[1];
+                               node->co[2] += subnode->co[2];
+
+                               totnode++;
+                       }
+               }
+
+               node->co[0] /= totnode;
+               node->co[1] /= totnode;
+               node->co[2] /= totnode;
+       }
+}
+
+static void sum_radiance(ScatterTree *tree, ScatterNode *node)
+{
+       if(node->totpoint > 0) {
+               sum_leaf_radiance(tree, node);
+       }
+       else {
+               int i;
+
+               for(i=0; i<8; i++)
+                       if(node->child[i])
+                               sum_radiance(tree, node->child[i]);
+
+               sum_branch_radiance(tree, node);
+       }
+}
+
+static void subnode_middle(int i, float *mid, float *subsize, float *submid)
+{
+       int x= i & 1, y= i & 2, z= i & 4;
+
+       submid[0]= mid[0] + ((x)? subsize[0]: -subsize[0]);
+       submid[1]= mid[1] + ((y)? subsize[1]: -subsize[1]);
+       submid[2]= mid[2] + ((z)? subsize[2]: -subsize[2]);
+}
+
+static void create_octree_node(ScatterTree *tree, ScatterNode *node, float *mid, float *size, ScatterPoint **refpoints, int depth)
+{
+       ScatterNode *subnode;
+       ScatterPoint **subrefpoints, **tmppoints= tree->tmppoints;
+       int index, nsize[8], noffset[8], i, subco, usednodes, usedi;
+       float submid[3], subsize[3];
+
+       /* stopping condition */
+       if(node->totpoint <= MAX_OCTREE_NODE_POINTS || depth == MAX_OCTREE_DEPTH) {
+               for(i=0; i<node->totpoint; i++)
+                       node->points[i]= *(refpoints[i]);
+
+               return;
+       }
+
+       subsize[0]= size[0]*0.5;
+       subsize[1]= size[1]*0.5;
+       subsize[2]= size[2]*0.5;
+
+       node->split[0]= mid[0];
+       node->split[1]= mid[1];
+       node->split[2]= mid[2];
+
+       memset(nsize, 0, sizeof(nsize));
+       memset(noffset, 0, sizeof(noffset));
+
+       /* count points in subnodes */
+       for(i=0; i<node->totpoint; i++) {
+               index= SUBNODE_INDEX(refpoints[i]->co, node->split);
+               tmppoints[i]= refpoints[i];
+               nsize[index]++;
+       }
+
+       /* here we check if only one subnode is used. if this is the case, we don't
+          create a new node, but rather call this function again, with different 
+          size and middle position for the same node. */
+       for(usedi=0, usednodes=0, i=0; i<8; i++) {
+               if(nsize[i]) {
+                       usednodes++;
+                       usedi = i;
+               }
+               if(i != 0)
+                       noffset[i]= noffset[i-1]+nsize[i-1];
+       }
+       
+       if(usednodes<=1) {
+               subnode_middle(usedi, mid, subsize, submid);
+               create_octree_node(tree, node, submid, subsize, refpoints, depth+1);
+               return;
+       }
+
+       /* reorder refpoints by subnode */
+       for(i=0; i<node->totpoint; i++) {
+               index= SUBNODE_INDEX(tmppoints[i]->co, node->split);
+               refpoints[noffset[index]]= tmppoints[i];
+               noffset[index]++;
+       }
+
+       /* create subnodes */
+       for(subco=0, i=0; i<8; subco+=nsize[i], i++) {
+               if(nsize[i] > 0) {
+                       subnode= BLI_memarena_alloc(tree->arena, sizeof(ScatterNode));
+                       node->child[i]= subnode;
+                       subnode->points= node->points + subco;
+                       subnode->totpoint= nsize[i];
+                       subrefpoints= refpoints + subco;
+
+                       subnode_middle(i, mid, subsize, submid);
+
+                       create_octree_node(tree, subnode, submid, subsize, subrefpoints,
+                               depth+1);
+               }
+               else
+                       node->child[i]= NULL;
+       }
+
+       node->points= NULL;
+       node->totpoint= 0;
+}
+
+/* public functions */
+
+ScatterTree *scatter_tree_new(ScatterSettings *ss[3], float scale, float error,
+       float (*co)[3], float (*color)[3], float *area, int totpoint)
+{
+       ScatterTree *tree;
+       ScatterPoint *points, **refpoints;
+       int i;
+
+       /* allocate tree */
+       tree= MEM_callocN(sizeof(ScatterTree), "ScatterTree");
+       tree->scale= scale;
+       tree->error= error;
+       tree->totpoint= totpoint;
+
+       tree->ss[0]= ss[0];
+       tree->ss[1]= ss[1];
+       tree->ss[2]= ss[2];
+
+       points= MEM_callocN(sizeof(ScatterPoint)*totpoint, "ScatterPoints");
+       refpoints= MEM_callocN(sizeof(ScatterPoint*)*totpoint, "ScatterRefPoints");
+
+       tree->points= points;
+       tree->refpoints= refpoints;
+
+       /* build points */
+       INIT_MINMAX(tree->min, tree->max);
+
+       for(i=0; i<totpoint; i++) {
+               VECCOPY(points[i].co, co[i]);
+               VECCOPY(points[i].radiance, color[i]);
+               points[i].area= fabs(area[i])/(tree->scale*tree->scale);
+               points[i].back= (area[i] < 0.0f);
+
+               VecMulf(points[i].co, 1.0f/tree->scale);
+               DO_MINMAX(points[i].co, tree->min, tree->max);
+
+               refpoints[i]= points + i;
+       }
+
+       return tree;
+}
+
+void scatter_tree_build(ScatterTree *tree)
+{
+       ScatterPoint *newpoints, **tmppoints;
+       float mid[3], size[3];
+       int totpoint= tree->totpoint;
+
+       newpoints= MEM_callocN(sizeof(ScatterPoint)*totpoint, "ScatterPoints");
+       tmppoints= MEM_callocN(sizeof(ScatterPoint*)*totpoint, "ScatterTmpPoints");
+       tree->tmppoints= tmppoints;
+
+       tree->arena= BLI_memarena_new(0x8000 * sizeof(ScatterNode));
+       BLI_memarena_use_calloc(tree->arena);
+
+       /* build tree */
+       tree->root= BLI_memarena_alloc(tree->arena, sizeof(ScatterNode));
+       tree->root->points= newpoints;
+       tree->root->totpoint= totpoint;
+
+       mid[0]= (tree->min[0]+tree->max[0])*0.5;
+       mid[1]= (tree->min[1]+tree->max[1])*0.5;
+       mid[2]= (tree->min[2]+tree->max[2])*0.5;
+
+       size[0]= (tree->max[0]-tree->min[0])*0.5;
+       size[1]= (tree->max[1]-tree->min[1])*0.5;
+       size[2]= (tree->max[2]-tree->min[2])*0.5;
+
+       create_octree_node(tree, tree->root, mid, size, tree->refpoints, 0);
+
+       MEM_freeN(tree->points);
+       MEM_freeN(tree->refpoints);
+       MEM_freeN(tree->tmppoints);
+       tree->refpoints= NULL;
+       tree->tmppoints= NULL;
+       tree->points= newpoints;
+       
+       /* sum radiance at nodes */
+       sum_radiance(tree, tree->root);
+}
+
+void scatter_tree_sample(ScatterTree *tree, float *co, float *color)
+{
+       float sco[3];
+
+       VECCOPY(sco, co);
+       VecMulf(sco, 1.0f/tree->scale);
+
+       compute_radiance(tree, sco, color);
+}
+
+void scatter_tree_free(ScatterTree *tree)
+{
+       if (tree->arena) BLI_memarena_free(tree->arena);
+       if (tree->points) MEM_freeN(tree->points);
+       if (tree->refpoints) MEM_freeN(tree->refpoints);
+               
+       MEM_freeN(tree);
+}
+
+/* Internal Renderer API */
+
+/* sss tree building */
+
+typedef struct SSSData {
+       ScatterTree *tree;
+       ScatterSettings *ss[3];
+} SSSData;
+
+typedef struct SSSPoints {
+       struct SSSPoints *next, *prev;
+
+       float (*co)[3];
+       float (*color)[3];
+       float *area;
+       int totpoint;
+} SSSPoints;
+
+static void sss_create_tree_mat(Render *re, Material *mat)
+{
+       SSSPoints *p;
+       ListBase layers, points;
+       float (*co)[3] = NULL, (*color)[3] = NULL, *area = NULL;
+       int totpoint = 0, osa;
+
+       if(re->test_break())
+               return;
+       
+       points.first= points.last= NULL;
+
+       /* do SSS preprocessing render */
+       layers= re->r.layers;
+       osa= re->osa;
+
+       re->r.layers.first= re->r.layers.last= NULL;
+       re->osa= 0;
+       re->sss_points= &points;
+       re->sss_mat= mat;
+
+       RE_TileProcessor(re, 0, 1);
+
+       re->sss_mat= NULL;
+       re->sss_points= NULL;
+       re->r.layers= layers;
+       re->osa= osa;
+
+       /* no points? no tree */
+       if(!points.first)
+               return;
+
+       /* merge points together into a single buffer */
+       if(!re->test_break()) {
+               for(totpoint=0, p=points.first; p; p=p->next)
+                       totpoint += p->totpoint;
+               
+               co= MEM_mallocN(sizeof(*co)*totpoint, "SSSCo");
+               color= MEM_mallocN(sizeof(*color)*totpoint, "SSSColor");
+               area= MEM_mallocN(sizeof(*area)*totpoint, "SSSArea");
+
+               for(totpoint=0, p=points.first; p; p=p->next) {
+                       memcpy(co+totpoint, p->co, sizeof(*co)*p->totpoint);
+                       memcpy(color+totpoint, p->color, sizeof(*color)*p->totpoint);
+                       memcpy(area+totpoint, p->area, sizeof(*area)*p->totpoint);
+                       totpoint += p->totpoint;
+               }
+       }
+
+       /* free points */
+       for(p=points.first; p; p=p->next) {
+               MEM_freeN(p->co);
+               MEM_freeN(p->color);
+               MEM_freeN(p->area);
+       }
+       BLI_freelistN(&points);
+
+       /* build tree */
+       if(!re->test_break()) {
+               SSSData *sss= MEM_callocN(sizeof(*sss), "SSSData");
+               float ior= mat->sss_ior, cfac= mat->sss_colfac;
+               float *col= mat->sss_col, *radius= mat->sss_radius;
+               float fw= mat->sss_front, bw= mat->sss_back;
+
+               sss->ss[0]= scatter_settings_new(col[0], radius[0], ior, cfac, fw, bw);
+               sss->ss[1]= scatter_settings_new(col[1], radius[1], ior, cfac, fw, bw);
+               sss->ss[2]= scatter_settings_new(col[2], radius[2], ior, cfac, fw, bw);
+               sss->tree= scatter_tree_new(sss->ss, mat->sss_scale, mat->sss_error,
+                       co, color, area, totpoint);
+
+               MEM_freeN(co);
+               MEM_freeN(color);
+               MEM_freeN(area);
+
+               scatter_tree_build(sss->tree);
+
+               BLI_ghash_insert(re->sss_hash, mat, sss);
+       }
+       else {
+               if (co) MEM_freeN(co);
+               if (color) MEM_freeN(color);
+               if (area) MEM_freeN(area);
+       }
+}
+
+void sss_add_points(Render *re, float (*co)[3], float (*color)[3], float *area, int totpoint)
+{
+       SSSPoints *p;
+       
+       if(totpoint > 0) {
+               p= MEM_callocN(sizeof(SSSPoints), "SSSPoints");
+
+               p->co= co;
+               p->color= color;
+               p->area= area;
+               p->totpoint= totpoint;
+
+               BLI_lock_thread(LOCK_CUSTOM1);
+               BLI_addtail(re->sss_points, p);
+               BLI_unlock_thread(LOCK_CUSTOM1);
+       }
+}
+
+static void sss_free_tree(SSSData *sss)
+{
+       scatter_tree_free(sss->tree);
+       scatter_settings_free(sss->ss[0]);
+       scatter_settings_free(sss->ss[1]);
+       scatter_settings_free(sss->ss[2]);
+       MEM_freeN(sss);
+}
+
+/* public functions */
+
+void make_sss_tree(Render *re)
+{
+       Material *mat;
+       
+       re->sss_hash= BLI_ghash_new(BLI_ghashutil_ptrhash, BLI_ghashutil_ptrcmp);
+
+       re->i.infostr= "SSS preprocessing";
+       re->stats_draw(&re->i);
+       
+       for(mat= G.main->mat.first; mat; mat= mat->id.next)
+               if(mat->id.us && (mat->sss_flag & MA_DIFF_SSS))
+                       sss_create_tree_mat(re, mat);
+}
+
+void free_sss(Render *re)
+{
+       if(re->sss_hash) {
+               GHashIterator *it= BLI_ghashIterator_new(re->sss_hash);
+
+               while(!BLI_ghashIterator_isDone(it)) {
+                       sss_free_tree(BLI_ghashIterator_getValue(it));
+                       BLI_ghashIterator_step(it);
+               }
+
+               BLI_ghashIterator_free(it);
+               BLI_ghash_free(re->sss_hash, NULL, NULL);
+               re->sss_hash= NULL;
+       }
+}
+
+int sample_sss(Render *re, Material *mat, float *co, float *color)
+{
+       if(re->sss_hash) {
+               SSSData *sss= BLI_ghash_lookup(re->sss_hash, mat);
+
+               if(sss) {
+                       scatter_tree_sample(sss->tree, co, color);
+                       return 1;
+               }
+               else {
+                       color[0]= 0.0f;
+                       color[1]= 0.0f;
+                       color[2]= 0.0f;
+               }
+       }
+
+       return 0;
+}
+
+int has_sss_tree(struct Render *re, struct Material *mat)
+{
+       return (re->sss_hash && BLI_ghash_lookup(re->sss_hash, mat));
+}
+
index ee24b15029c6129002c8b3ac39fa946a8a646094..f919aa11b982ba172b3739887d70ecc8914d488d 100644 (file)
@@ -51,6 +51,7 @@
 #include "DNA_meshdata_types.h"
 
 #include "BKE_global.h"
+#include "BKE_material.h"
 #include "BKE_utildefines.h"
 
 #include "radio_types.h"
@@ -67,6 +68,7 @@
 #include "rendercore.h"
 #include "shadbuf.h"
 #include "shading.h"
+#include "sss.h"
 
 /* own includes */
 #include "zbuf.h"
@@ -2012,6 +2014,164 @@ void zbuffer_shadow(Render *re, LampRen *lar, int *rectz, int size, float jitx,
        zbuf_free_span(&zspan);
 }
 
+static void zbuffill_sss(ZSpan *zspan, int zvlnr, float *v1, float *v2, float *v3, float *v4)
+{
+       double zxd, zyd, zy0, z;
+       float x0, y0, x1, y1, x2, y2, z0, z1, z2, xx1, *span1, *span2;
+       int x, y, sn1, sn2, rectx= zspan->rectx, my0, my2;
+       
+       /* init */
+       zbuf_init_span(zspan);
+       
+       /* set spans */
+       zbuf_add_to_span(zspan, v1, v2);
+       zbuf_add_to_span(zspan, v2, v3);
+       if(v4) {
+               zbuf_add_to_span(zspan, v3, v4);
+               zbuf_add_to_span(zspan, v4, v1);
+       }
+       else 
+               zbuf_add_to_span(zspan, v3, v1);
+       
+       /* clipped */
+       if(zspan->minp2==NULL || zspan->maxp2==NULL) return;
+       
+       if(zspan->miny1 < zspan->miny2) my0= zspan->miny2; else my0= zspan->miny1;
+       if(zspan->maxy1 > zspan->maxy2) my2= zspan->maxy2; else my2= zspan->maxy1;
+       
+       if(my2<my0) return;
+       
+       /* ZBUF DX DY, in floats still */
+       x1= v1[0]- v2[0];
+       x2= v2[0]- v3[0];
+       y1= v1[1]- v2[1];
+       y2= v2[1]- v3[1];
+       z1= v1[2]- v2[2];
+       z2= v2[2]- v3[2];
+       
+       x0= y1*z2-z1*y2;
+       y0= z1*x2-x1*z2;
+       z0= x1*y2-y1*x2;
+       
+       if(z0==0.0f) return;
+       
+       xx1= (x0*v1[0] + y0*v1[1])/z0 + v1[2];
+       zxd= -(double)x0/(double)z0;
+       zyd= -(double)y0/(double)z0;
+       zy0= ((double)my2)*zyd + (double)xx1;
+       
+       /* correct span */
+       sn1= (my0 + my2)/2;
+       if(zspan->span1[sn1] < zspan->span2[sn1]) {
+               span1= zspan->span1+my2;
+               span2= zspan->span2+my2;
+       }
+       else {
+               span1= zspan->span2+my2;
+               span2= zspan->span1+my2;
+       }
+       
+       for(y=my2; y>=my0; y--, span1--, span2--) {
+               sn1= floor(*span1);
+               sn2= floor(*span2);
+               sn1++; 
+               
+               if(sn2>=rectx) sn2= rectx-1;
+               if(sn1<0) sn1= 0;
+               
+               z= (double)sn1*zxd + zy0;
+               
+               for(x= sn1; x<=sn2; x++, z+=zxd)
+                       zspan->sss_func(zspan->sss_handle, zvlnr, x, y, z);
+               
+               zy0 -= zyd;
+       }
+}
+
+void zbuffer_sss(RenderPart *pa, unsigned int lay, void *handle, void (*func)(void *, int, int, int, int))
+{
+       ZSpan zspan;
+       VlakRen *vlr= NULL;
+       VertRen *v1, *v2, *v3, *v4;
+       Material *ma=0, *sss_ma= R.sss_mat;
+       short nofill=0, env=0, wire=0;
+       char *clipflag= pa->clipflag;
+       int v, zvlnr;
+       
+       zbuf_alloc_span(&zspan, pa->rectx, pa->recty);
+
+       zspan.sss_handle= handle;
+       zspan.sss_func= func;
+       
+       /* needed for transform from hoco to zbuffer co */
+       zspan.zmulx=  ((float)R.winx)/2.0;
+       zspan.zmuly=  ((float)R.winy)/2.0;
+       
+       /* -0.5f to center the sample position */
+       zspan.zofsx= -pa->disprect.xmin - 0.5f;
+       zspan.zofsy= -pa->disprect.ymin - 0.5f;
+       
+       /* filling methods */
+       zspan.zbuffunc= zbuffill_sss;
+
+       /* fill front and back zbuffer */
+       if(pa->rectz) {
+               fillrect(pa->rectp, pa->rectx, pa->recty, 0); 
+               fillrect(pa->rectz, pa->rectx, pa->recty, 0x7FFFFFFF);
+       }
+       if(pa->rectbackz) {
+               fillrect(pa->rectbackp, pa->rectx, pa->recty, 0); 
+               fillrect(pa->rectbackz, pa->rectx, pa->recty, -0x7FFFFFFF);
+       }
+
+       for(v=0; v<R.totvlak; v++) {
+               if((v & 255)==0) vlr= R.vlaknodes[v>>8].vlak;
+               else vlr++;
+               
+               if((vlr->flag & R_VISIBLE) && material_in_material(vlr->mat, sss_ma)) {
+                       /* three cases, visible for render, only z values and nothing */
+                       if(vlr->lay & lay) {
+                               if(vlr->mat!=ma) {
+                                       ma= vlr->mat;
+                                       nofill= ma->mode & MA_ONLYCAST;
+                                       env= (ma->mode & MA_ENV);
+                                       wire= (ma->mode & MA_WIRE);
+                               }
+                       }
+                       else {
+                               nofill= 1;
+                               ma= NULL;       /* otherwise nofill can hang */
+                       }
+                       
+                       if(nofill==0 && wire==0 && env==0) {
+                               unsigned short partclip;
+                               
+                               v1= vlr->v1;
+                               v2= vlr->v2;
+                               v3= vlr->v3;
+                               v4= vlr->v4;
+                               
+                               /* partclipping doesn't need viewplane clipping */
+                               partclip= clipflag[v1->index] & clipflag[v2->index] & clipflag[v3->index];
+                               if(v4)
+                                       partclip &= clipflag[v4->index];
+                               
+
+                               if(partclip==0) {
+                                       zvlnr= v+1;
+                                       zbufclip(&zspan, zvlnr, v1->ho, v2->ho, v3->ho, v1->clip, v2->clip, v3->clip);
+                                       if(v4) {
+                                               zvlnr+= RE_QUAD_OFFS;
+                                               zbufclip(&zspan, zvlnr, v1->ho, v3->ho, v4->ho, v1->clip, v3->clip, v4->clip);
+                                       }
+                               }
+                       }
+               }
+       }
+       
+       zbuf_free_span(&zspan);
+}
+
 /* ******************** VECBLUR ACCUM BUF ************************* */
 
 typedef struct DrawBufPixel {
index bd8dae311e8f35c7034829a016d481e25fdcaa81..b290857c23d6034b07838cec035896c42b51d608 100644 (file)
@@ -3269,6 +3269,118 @@ static void material_panel_tramir_yafray(Material *ma)
 
 }
 
+static void material_sss_preset_cb(void *material_v, void *unused_v)
+{
+       static const float presets[11][7] = {
+       {0.909578, 0.905931, 0.665691, 6.961082,   6.400181, 1.895899, 1.300000},
+       {0.429632, 0.210025, 0.167767, 11.605177,  3.883766, 1.754386, 1.300000},
+       {0.439300, 0.216000, 0.141027, 9.435642,   3.347647, 1.790287, 1.300000},
+       {0.986552, 0.942637, 0.827285, 15.027623,  4.663968, 2.541380, 1.300000},
+       {0.221636, 0.007505, 0.002154, 4.761743,   0.574827, 0.394116, 1.300000},
+       {0.925008, 0.905025, 0.884275, 8.509412,   5.566180, 3.951266, 1.500000},
+       {0.855344, 0.740311, 0.291994, 14.266395,  7.227615, 2.036157, 1.300000},
+       {0.889319, 0.888034, 0.795811, 18.424364, 10.443473, 3.501882, 1.300000},
+       {0.573652, 0.312750, 0.174289, 3.673294,   1.366534, 0.682693, 1.300000},
+       {0.748679, 0.570766, 0.467133, 4.821475,   1.693699, 1.089971, 1.300000},
+       {0.947235, 0.931028, 0.851872, 10.898815,  6.575351, 2.508417, 1.300000}};
+
+       Material *ma= (Material*)material_v;
+
+       if(ma->sss_preset==0) return;
+
+       ma->sss_col[0]= presets[ma->sss_preset][0];
+       ma->sss_col[1]= presets[ma->sss_preset][1];
+       ma->sss_col[2]= presets[ma->sss_preset][2];
+       ma->sss_radius[0]= presets[ma->sss_preset][3];
+       ma->sss_radius[1]= presets[ma->sss_preset][4];
+       ma->sss_radius[2]= presets[ma->sss_preset][5];
+       ma->sss_ior= presets[ma->sss_preset][6];
+}
+
+static void material_sss_custom_set_cb(void *material_v, void *unused_v)
+{
+       Material *ma= (Material*)material_v;
+
+       ma->sss_preset= 0;
+       allqueue(REDRAWNODE, 0);
+}
+
+static void material_panel_sss(Material *ma)
+{
+       uiBlock *block;
+       uiBut *bt;
+       
+       block= uiNewBlock(&curarea->uiblocks, "material_panel_sss", UI_EMBOSS, UI_HELV, curarea->win);
+       uiNewPanelTabbed("Shaders", "Material");
+       if(uiNewPanel(curarea, block, "SSS", "Material", 640, 0, 318, 204)==0) return;
+
+       uiSetButLock(ma->id.lib!=NULL, ERROR_LIBDATA_MESSAGE);
+       
+       uiDefButBitS(block, TOG, MA_DIFF_SSS, B_MATPRV,"Subsurface Scattering",10,180,180,20, &(ma->sss_flag), 0, 0, 0, 0, "Enables diffuse subsurface scattering");
+
+       bt=uiDefButS(block, MENU, B_MATPRV, "Apple %x1|Chicken %x2|Cream %x3|Ketchup %x4|Marble %x5|Potato %x6|Skim Milk %x7|Skin 1 %x8|Skin 2 %x9|Whole Milk %x10|Custom %x0",
+               200,180,110,20, &ma->sss_preset, 0, 0, 0, 0, "");
+       uiButSetFunc(bt, material_sss_preset_cb, ma, NULL);
+
+       uiBlockBeginAlign(block);
+       uiDefButF(block, NUM, B_MATPRV, "Scale:", 10,150,145,20,
+                         &ma->sss_scale, 0.001, 1000, 1, 3, "Object scale");
+       bt=uiDefButF(block, NUM, B_MATPRV, "Radius R", 10,130,145,20,
+                         &ma->sss_radius[0], 0.00001, 10000, 0, 0,
+                         "Mean red scattering path length");
+       uiButSetFunc(bt, material_sss_custom_set_cb, ma, NULL);
+       bt=uiDefButF(block, NUM, B_MATPRV, "Radius G", 10,110,145,20,
+                         &ma->sss_radius[1], 0.00001, 10000, 0, 0,
+                         "Mean green scattering path length");
+       uiButSetFunc(bt, material_sss_custom_set_cb, ma, NULL);
+       bt=uiDefButF(block, NUM, B_MATPRV, "Radius B", 10,90,145,20,
+                         &ma->sss_radius[2], 0.00001, 10000, 0, 0,
+                         "Mean blue scattering path length");
+       uiButSetFunc(bt, material_sss_custom_set_cb, ma, NULL);
+       uiBlockEndAlign(block);
+
+       bt=uiDefButF(block, NUM, B_MATPRV, "IOR:", 10,60,145,20,
+                         &ma->sss_ior, 0.1, 2, 1, 3, "Index of refraction");
+       uiButSetFunc(bt, material_sss_custom_set_cb, ma, NULL);
+
+       uiBlockBeginAlign(block);
+       uiDefButF(block, NUM, B_MATPRV, "Error:", 10,30,145,20,
+                         &ma->sss_error, 0.0001, 10, 1, 3, "Error");
+       uiBlockEndAlign(block);
+
+       uiBlockBeginAlign(block);
+       bt=uiDefButF(block, NUMSLI, B_MATPRV, "R ", 165,150,145,20,
+                         &ma->sss_col[0], 0.0, 1.0, 0, 0,
+                         "Red scattering color");
+       uiButSetFunc(bt, material_sss_custom_set_cb, ma, NULL);
+       bt=uiDefButF(block, NUMSLI, B_MATPRV, "G ", 165,130,145,20,
+                         &ma->sss_col[1], 0.0, 1.0, 0, 0,
+                         "Green scattering color");
+       uiButSetFunc(bt, material_sss_custom_set_cb, ma, NULL);
+       bt=uiDefButF(block, NUMSLI, B_MATPRV, "B ", 165,110,145,20,
+                         &ma->sss_col[2], 0.0, 1.0, 0, 0,
+                         "Blue scattering color");
+       uiButSetFunc(bt, material_sss_custom_set_cb, ma, NULL);
+       uiBlockEndAlign(block);
+
+       uiBlockBeginAlign(block);
+       uiDefButF(block, NUMSLI, B_MATPRV, "Col ", 165,80,145,20,
+                         &ma->sss_colfac, 0.0, 1.0, 0, 0,
+                         "Blend factor for SSS colors");
+       uiDefButF(block, NUMSLI, B_MATPRV, "Tex ", 165,60,145,20,
+                         &ma->sss_texfac, 0.0, 1.0, 0, 0,
+                         "Texture scattering factor");
+       uiBlockEndAlign(block);
+
+       uiBlockBeginAlign(block);
+       uiDefButF(block, NUMSLI, B_MATPRV, "Front ", 165,30,145,20,
+                         &ma->sss_front, 0.0, 2.0, 0, 0,
+                         "Front scattering weight");
+       uiDefButF(block, NUMSLI, B_MATPRV, "Back ", 165,10,145,20,
+                         &ma->sss_back, 0.0, 10.0, 0, 0,
+                         "Back scattering weight");
+       uiBlockEndAlign(block);
+}
 
 static void material_panel_shading(Material *ma)
 {
@@ -3731,7 +3843,8 @@ void material_panels()
                                }
                                material_panel_tramir_yafray(ma);
                        }
-                       
+
+                       material_panel_sss(ma);
                        material_panel_texture(ma);
                        
                        mtex= ma->mtex[ ma->texact ];
index d54336807f95a0a1414d888aa54678dbf53c42c5..1526de1b74d7a63da3a7cfa879e597d6957df23e 100644 (file)
@@ -467,7 +467,7 @@ void BIF_previewrender(struct ID *id, struct RenderInfo *ri, struct ScrArea *are
        /* entire cycle for render engine */
        RE_SetCamera(re, sce->camera);
        RE_Database_FromScene(re, sce, 1);
-       RE_TileProcessor(re, ri->curtile);      // actual render engine
+       RE_TileProcessor(re, ri->curtile, 0);   // actual render engine
        RE_Database_Free(re);
        
        /* handle results */
@@ -842,7 +842,7 @@ void BIF_view3d_previewrender(ScrArea *sa)
                /* OK, can we enter render code? */
                if(ri->status==(PR_DISPRECT|PR_DBASE|PR_PROJECTED|PR_ROTATED)) {
                        //printf("curtile %d tottile %d\n", ri->curtile, ri->tottile);
-                       RE_TileProcessor(ri->re, ri->curtile);
+                       RE_TileProcessor(ri->re, ri->curtile, 0);
        
                        if(ri->rect==NULL)
                                ri->rect= MEM_mallocN(sizeof(int)*ri->pr_rectx*ri->pr_recty, "preview view3d rect");