forked from bartvdbraak/blender
Subsurface scattering:
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/
This commit is contained in:
parent
4ec6f1b4e4
commit
3a8c6c81d8
@ -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);
|
||||
|
||||
|
@ -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]= {
|
||||
|
@ -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;
|
||||
|
@ -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
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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
|
||||
|
||||
|
||||
|
||||
|
@ -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);
|
||||
|
65
source/blender/render/intern/include/sss.h
Normal file
65
source/blender/render/intern/include/sss.h
Normal file
@ -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*/
|
||||
|
@ -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 *);
|
||||
|
@ -103,6 +103,7 @@
|
||||
#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())
|
||||
|
@ -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 */
|
||||
|
@ -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);
|
||||
|
@ -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 */
|
||||
|
@ -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;
|
||||
|
||||
}
|
||||
}
|
||||
|
@ -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) {
|
||||
|
@ -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 */
|
||||
|
984
source/blender/render/intern/source/sss.c
Normal file
984
source/blender/render/intern/source/sss.c
Normal file
@ -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));
|
||||
}
|
||||
|
@ -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 {
|
||||
|
@ -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 ];
|
||||
|
@ -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");
|
||||
|
Loading…
Reference in New Issue
Block a user