diff options
author | Matt Ebb <matt@mke3.net> | 2007-09-07 07:48:50 +0400 |
---|---|---|
committer | Matt Ebb <matt@mke3.net> | 2007-09-07 07:48:50 +0400 |
commit | 0ba5295404dd1c2ee04a9e7823eafc664562f4eb (patch) | |
tree | 816891a80322ecc06a5016cfb1e8ef4abba9f0ba | |
parent | f85cd06873a991fec91766ea8cd5efe3eda17594 (diff) |
* QMC Raytracing
This introduces QMC sampling for use in glossy reflections/refractions, soft raytraced shadows, and ambient occlusion.
This work includes many new features and speed-ups, so check out the nice docs here:
Glossy Reflection/Refraction
http://www.blender.org/development/current-projects/changes-since-244/glossy-reflectionrefraction/
Raytraced Soft Shadows
http://www.blender.org/development/current-projects/changes-since-244/raytraced-soft-shadows/
QMC Sampling
http://www.blender.org/development/current-projects/changes-since-244/qmc-sampling/
Many thanks to Brecht van Lommel for some initial code snippets and for reviewing the patch, and especially to Alfredo de Greef who gave me a lot of guidance and help along the way!
-rw-r--r-- | source/blender/blenkernel/intern/material.c | 6 | ||||
-rw-r--r-- | source/blender/blenkernel/intern/object.c | 2 | ||||
-rw-r--r-- | source/blender/blenkernel/intern/world.c | 4 | ||||
-rw-r--r-- | source/blender/blenlib/BLI_arithb.h | 2 | ||||
-rw-r--r-- | source/blender/blenlib/intern/arithb.c | 24 | ||||
-rw-r--r-- | source/blender/blenloader/intern/readfile.c | 34 | ||||
-rw-r--r-- | source/blender/include/BIF_butspace.h | 32 | ||||
-rw-r--r-- | source/blender/makesdna/DNA_lamp_types.h | 12 | ||||
-rw-r--r-- | source/blender/makesdna/DNA_material_types.h | 12 | ||||
-rw-r--r-- | source/blender/makesdna/DNA_world_types.h | 11 | ||||
-rw-r--r-- | source/blender/render/intern/include/render_types.h | 21 | ||||
-rw-r--r-- | source/blender/render/intern/include/rendercore.h | 4 | ||||
-rw-r--r-- | source/blender/render/intern/source/convertblender.c | 46 | ||||
-rw-r--r-- | source/blender/render/intern/source/rayshade.c | 968 | ||||
-rw-r--r-- | source/blender/src/buttons_shading.c | 197 |
15 files changed, 1188 insertions, 187 deletions
diff --git a/source/blender/blenkernel/intern/material.c b/source/blender/blenkernel/intern/material.c index f5f726b1745..d70cd683237 100644 --- a/source/blender/blenkernel/intern/material.c +++ b/source/blender/blenkernel/intern/material.c @@ -140,6 +140,12 @@ void init_material(Material *ma) ma->tx_falloff= 1.0; ma->shad_alpha= 1.0f; + ma->gloss_mir = ma->gloss_tra= 1.0; + ma->samp_gloss_mir = ma->samp_gloss_tra= 18; + ma->adapt_thresh_mir = ma->adapt_thresh_tra = 0.005; + ma->dist_mir = 0.0; + ma->fadeto_mir = MA_RAYMIR_FADETOSKY; + ma->rampfac_col= 1.0; ma->rampfac_spec= 1.0; ma->pr_lamp= 3; /* two lamps, is bits */ diff --git a/source/blender/blenkernel/intern/object.c b/source/blender/blenkernel/intern/object.c index 349ccda8126..9f68706716a 100644 --- a/source/blender/blenkernel/intern/object.c +++ b/source/blender/blenkernel/intern/object.c @@ -598,6 +598,8 @@ void *add_lamp(char *name) la->area_size=la->area_sizey=la->area_sizez= 1.0; la->buffers= 1; la->buftype= LA_SHADBUF_HALFWAY; + la->ray_samp_method = LA_SAMP_HALTON; + la->adapt_thresh = 0.001; la->preview=NULL; return la; } diff --git a/source/blender/blenkernel/intern/world.c b/source/blender/blenkernel/intern/world.c index ce41720cb9d..625ca57dbf3 100644 --- a/source/blender/blenkernel/intern/world.c +++ b/source/blender/blenkernel/intern/world.c @@ -98,10 +98,12 @@ World *add_world(char *name) wrld->exp= 0.0f; wrld->exposure=wrld->range= 1.0f; - wrld->aodist= 10.0; + wrld->aodist= 5.0; wrld->aosamp= 5; wrld->aoenergy= 1.0; wrld->aobias= 0.05; + wrld->ao_samp_method = WO_AOSAMP_HAMMERSLEY; + wrld->physicsEngine= WOPHY_BULLET;//WOPHY_SUMO; Bullet by default wrld->preview = NULL; diff --git a/source/blender/blenlib/BLI_arithb.h b/source/blender/blenlib/BLI_arithb.h index b2043bfcf93..fc132250fc6 100644 --- a/source/blender/blenlib/BLI_arithb.h +++ b/source/blender/blenlib/BLI_arithb.h @@ -244,6 +244,8 @@ void VecSubf(float *v, float *v1, float *v2); void VecLerpf(float *target, float *a, float *b, float t); void VecMidf(float *v, float *v1, float *v2); +void VecOrthoBasisf(float *v, float *v1, float *v2); + float Vec2Lenf(float *v1, float *v2); void Vec2Mulf(float *v1, float f); void Vec2Addf(float *v, float *v1, float *v2); diff --git a/source/blender/blenlib/intern/arithb.c b/source/blender/blenlib/intern/arithb.c index d93f4b9c009..f95d102763a 100644 --- a/source/blender/blenlib/intern/arithb.c +++ b/source/blender/blenlib/intern/arithb.c @@ -2093,6 +2093,30 @@ void VecMulf(float *v1, float f) v1[2]*= f; } +void VecOrthoBasisf(float *v, float *v1, float *v2) +{ + if (v[0] == 0.0f && v[1] == 0.0f) + { + // degenerate case + v1[0] = 0.0f; v1[1] = 1.0f; v1[2] = 0.0f; + if (v[2] > 0.0f) { + v2[0] = 1.0f; v2[1] = v2[2] = 0.0f; + } + else { + v2[0] = -1.0f; v2[1] = v2[2] = 0.0f; + } + } + else + { + float f = 1.0f/sqrt(v[0]*v[0] + v[1]*v[1]); + v1[0] = v[1]*f; + v1[1] = -v[0]*f; + v1[2] = 0.0f; + + Crossf(v2, v, v1); + } +} + int VecLenCompare(float *v1, float *v2, float limit) { float x,y,z; diff --git a/source/blender/blenloader/intern/readfile.c b/source/blender/blenloader/intern/readfile.c index 1f1dd84f49c..6c7750b5741 100644 --- a/source/blender/blenloader/intern/readfile.c +++ b/source/blender/blenloader/intern/readfile.c @@ -6562,9 +6562,12 @@ static void do_versions(FileData *fd, Library *lib, Main *main) } if(main->versionfile <= 244) { Scene *sce; + Material *ma; bScreen *sc; Object *ob; - + Lamp *la; + World *wrld; + if(main->versionfile != 244 || main->subversionfile < 2) { Mesh *me; @@ -6588,7 +6591,7 @@ static void do_versions(FileData *fd, Library *lib, Main *main) } } } - + /* correct older action editors - incorrect scrolling */ for(sc= main->screen.first; sc; sc= sc->id.next) { ScrArea *sa; @@ -6692,6 +6695,33 @@ static void do_versions(FileData *fd, Library *lib, Main *main) } } } + + for(ma=main->mat.first; ma; ma= ma->id.next) { + ma->gloss_mir = ma->gloss_tra= 1.0; + ma->aniso_gloss_mir = 1.0; + ma->samp_gloss_mir = ma->samp_gloss_tra= 18; + ma->adapt_thresh_mir = ma->adapt_thresh_tra = 0.005; + ma->dist_mir = 0.0; + ma->fadeto_mir = MA_RAYMIR_FADETOSKY; + } + + for(wrld=main->world.first; wrld; wrld= wrld->id.next) { + if (wrld->mode & WO_AMB_OCC) + wrld->ao_samp_method = WO_AOSAMP_CONSTANT; + else + wrld->ao_samp_method = WO_AOSAMP_HAMMERSLEY; + + wrld->ao_adapt_thresh = 0.005; + } + + for(la=main->lamp.first; la; la= la->id.next) { + if (la->type == LA_AREA) + la->ray_samp_method = LA_SAMP_CONSTANT; + else + la->ray_samp_method = LA_SAMP_HALTON; + + la->adapt_thresh = 0.001; + } } } diff --git a/source/blender/include/BIF_butspace.h b/source/blender/include/BIF_butspace.h index f87e9797cb3..519f3f18a0c 100644 --- a/source/blender/include/BIF_butspace.h +++ b/source/blender/include/BIF_butspace.h @@ -102,6 +102,38 @@ extern void validate_editbonebutton_cb(void *bonev, void *namev); #define BUTS_ACT_LINK 256 +/* buttons grid */ +#define PANELX 320 +#define PANELY 0 +#define PANELW 318 +#define PANELH 204 + +#define BUTW1 300 +#define BUTW2 145 +#define BUTW3 93 +#define BUTW4 67 +#define ICONBUTW 20 +#define BUTH 22 + +#define YSPACE 6 +#define XSPACE 10 +#define PANEL_YMAX 210 +#define PANEL_XMAX 310 + +#define X1CLM 10 + +#define X2CLM1 X1CLM +#define X2CLM2 165 + +#define X3CLM1 X1CLM +#define X3CLM2 113 +#define X3CLM3 217 + +#define X4CLM1 X1CLM +#define X4CLM2 77 +#define X4CLM3 165 +#define X4CLM4 232 + #endif diff --git a/source/blender/makesdna/DNA_lamp_types.h b/source/blender/makesdna/DNA_lamp_types.h index 5d998ccca6e..e1d4e4c1cc1 100644 --- a/source/blender/makesdna/DNA_lamp_types.h +++ b/source/blender/makesdna/DNA_lamp_types.h @@ -61,9 +61,13 @@ typedef struct Lamp { short bufsize, samp, buffers, filtertype; char bufflag, buftype; - short ray_samp, ray_sampy, ray_sampz, ray_samp_type; + short ray_samp, ray_sampy, ray_sampz; + short ray_samp_type; short area_shape; float area_size, area_sizey, area_sizez; + float adapt_thresh; + short ray_samp_method; + short pad1; /* texact is for buttons */ short texact, shadhalostep; @@ -135,6 +139,12 @@ typedef struct Lamp { #define LA_AREA_CUBE 2 #define LA_AREA_BOX 3 +/* ray_samp_method */ +#define LA_SAMP_CONSTANT 0 +#define LA_SAMP_HALTON 1 +#define LA_SAMP_HAMMERSLEY 2 + + /* ray_samp_type */ #define LA_SAMP_ROUND 1 #define LA_SAMP_UMBRA 2 diff --git a/source/blender/makesdna/DNA_material_types.h b/source/blender/makesdna/DNA_material_types.h index b6bc475fb28..252af0ebdb1 100644 --- a/source/blender/makesdna/DNA_material_types.h +++ b/source/blender/makesdna/DNA_material_types.h @@ -73,6 +73,14 @@ typedef struct Material { short har; char seed1, seed2; + float gloss_mir, gloss_tra; + short samp_gloss_mir, samp_gloss_tra; + float adapt_thresh_mir, adapt_thresh_tra; + float aniso_gloss_mir; + float dist_mir; + short fadeto_mir; + short pad1; + int mode, mode_l; /* mode_l is the or-ed result of all layer modes */ short flarec, starc, linec, ringc; float hasize, flaresize, subsize, flareboost; @@ -183,6 +191,10 @@ typedef struct Material { #define MA_MODE_MASK 0x1fffffff /* all valid mode bits */ +/* ray mirror fadeout */ +#define MA_RAYMIR_FADETOSKY 0 +#define MA_RAYMIR_FADETOMAT 1 + /* diff_shader */ #define MA_DIFF_LAMBERT 0 #define MA_DIFF_ORENNAYAR 1 diff --git a/source/blender/makesdna/DNA_world_types.h b/source/blender/makesdna/DNA_world_types.h index 7349e37f3b7..769d11ecebe 100644 --- a/source/blender/makesdna/DNA_world_types.h +++ b/source/blender/makesdna/DNA_world_types.h @@ -103,8 +103,14 @@ typedef struct World { /* ambient occlusion */ float aodist, aodistfac, aoenergy, aobias; short aomode, aosamp, aomix, aocolor; + float ao_adapt_thresh; + float pad2[3]; + short ao_samp_method; + short pad1[3]; + float *aosphere, *aotables; + struct Ipo *ipo; struct MTex *mtex[10]; @@ -137,6 +143,11 @@ typedef struct World { #define WO_AOSUB 1 #define WO_AOADDSUB 2 +/* ao_samp_method - methods for sampling the AO hemi */ +#define WO_AOSAMP_CONSTANT 0 +#define WO_AOSAMP_HALTON 1 +#define WO_AOSAMP_HAMMERSLEY 2 + /* aomode (use distances & random sampling modes) */ #define WO_AODIST 1 #define WO_AORNDSMP 2 diff --git a/source/blender/render/intern/include/render_types.h b/source/blender/render/intern/include/render_types.h index 5c2e788ce44..b6d1024a081 100644 --- a/source/blender/render/intern/include/render_types.h +++ b/source/blender/render/intern/include/render_types.h @@ -59,6 +59,18 @@ typedef struct SampleTables } SampleTables; +typedef struct QMCSampler +{ + int type; + int tot; + double *samp2d; + double offs[BLENDER_MAX_THREADS][2]; +} QMCSampler; + +#define SAMP_TYPE_JITTERED 0 +#define SAMP_TYPE_HALTON 1 +#define SAMP_TYPE_HAMMERSLEY 2 + /* this is handed over to threaded hiding/passes/shading engine */ typedef struct RenderPart { @@ -130,6 +142,7 @@ struct Render /* samples */ SampleTables *samples; float jit[32][2]; + QMCSampler *qsa; /* scene, and its full copy of renderdata and world */ Scene *scene; @@ -335,12 +348,14 @@ typedef struct LampRen { /** A small depth offset to prevent self-shadowing. */ float bias; - short ray_samp, ray_sampy, ray_sampz, ray_samp_type, area_shape, ray_totsamp; + short ray_samp, ray_sampy, ray_sampz, ray_samp_method, ray_samp_type, area_shape, ray_totsamp; short xold[BLENDER_MAX_THREADS], yold[BLENDER_MAX_THREADS]; /* last jitter table for area lights */ float area_size, area_sizey, area_sizez; - + float adapt_thresh; + struct ShadBuf *shb; float *jitter; + QMCSampler *qsa; float imat[3][3]; float spottexfac; @@ -351,7 +366,7 @@ typedef struct LampRen { /* passes & node shader support: all shadow info for a pixel */ LampShadowSample *shadsamp; - + /* yafray: photonlight params */ int YF_numphotons, YF_numsearch; short YF_phdepth, YF_useqmc, YF_bufsize; diff --git a/source/blender/render/intern/include/rendercore.h b/source/blender/render/intern/include/rendercore.h index 3e3c49e87d0..0d807d56180 100644 --- a/source/blender/render/intern/include/rendercore.h +++ b/source/blender/render/intern/include/rendercore.h @@ -99,6 +99,10 @@ extern void ray_trace(ShadeInput *, ShadeResult *); extern void ray_ao(ShadeInput *, float *); extern void init_jitter_plane(LampRen *lar); extern void init_ao_sphere(struct World *wrld); +extern void init_lamp_hammersley(LampRen *lar); +extern void free_lamp_qmcsampler(LampRen *lar); +extern void init_render_hammersley(Render *re); +extern void free_render_qmcsampler(Render *re); #endif /* RENDER_EXT_H */ diff --git a/source/blender/render/intern/source/convertblender.c b/source/blender/render/intern/source/convertblender.c index f57fa74f618..7d95a5e6340 100644 --- a/source/blender/render/intern/source/convertblender.c +++ b/source/blender/render/intern/source/convertblender.c @@ -2243,15 +2243,23 @@ static GroupObject *add_render_lamp(Render *re, Object *ob) lar->ray_samp= la->ray_samp; lar->ray_sampy= la->ray_sampy; lar->ray_sampz= la->ray_sampz; - + lar->area_size= la->area_size; lar->area_sizey= la->area_sizey; lar->area_sizez= la->area_sizez; lar->area_shape= la->area_shape; + lar->ray_samp_method= la->ray_samp_method; lar->ray_samp_type= la->ray_samp_type; - - if(lar->type==LA_AREA) { + + lar->adapt_thresh= la->adapt_thresh; + + if( ELEM3(lar->type, LA_SPOT, LA_SUN, LA_LOCAL)) { + lar->ray_totsamp= lar->ray_samp*lar->ray_samp; + lar->area_shape = LA_AREA_SQUARE; + lar->area_sizey= lar->area_size; + } + else if(lar->type==LA_AREA) { switch(lar->area_shape) { case LA_AREA_SQUARE: lar->ray_totsamp= lar->ray_samp*lar->ray_samp; @@ -2369,15 +2377,20 @@ static GroupObject *add_render_lamp(Render *re, Object *ob) if(re->r.mode & R_SHADOW) { - if (la->type==LA_SPOT && (lar->mode & LA_SHAD_BUF) ) { + + if ((lar->mode & LA_SHAD_RAY) && (lar->ray_samp_method == LA_SAMP_HAMMERSLEY)) { + init_lamp_hammersley(lar); + } + if(la->type==LA_AREA && (lar->mode & LA_SHAD_RAY) && (lar->ray_samp_method == LA_SAMP_CONSTANT)) { + init_jitter_plane(lar); + } + else if (la->type==LA_SPOT && (lar->mode & LA_SHAD_BUF) ) { /* Per lamp, one shadow buffer is made. */ lar->bufflag= la->bufflag; Mat4CpyMat4(mat, ob->obmat); initshadowbuf(re, lar, mat); // mat is altered } - else if(la->type==LA_AREA && (lar->mode & LA_SHAD_RAY) ) { - init_jitter_plane(lar); - } + /* this is the way used all over to check for shadow */ if(lar->shb || (lar->mode & LA_SHAD_RAY)) { @@ -2951,6 +2964,7 @@ void RE_Database_Free(Render *re) freeshadowbuf(lar); if(lar->jitter) MEM_freeN(lar->jitter); if(lar->shadsamp) MEM_freeN(lar->shadsamp); + if(lar->qsa) free_lamp_qmcsampler(lar); } BLI_freelistN(&re->lampren); @@ -2987,6 +3001,9 @@ void RE_Database_Free(Render *re) re->wrld.aotables= NULL; re->scene->world->aotables= NULL; } + if((re->r.mode & R_RAYTRACE) && (re->wrld.mode & WO_AMB_OCC) && + (re->wrld.ao_samp_method == WO_AOSAMP_HAMMERSLEY) && (re->qsa)) + free_render_qmcsampler(re); if(re->r.mode & R_RAYTRACE) freeraytree(re); @@ -3024,8 +3041,11 @@ static void set_fullsample_flag(Render *re) if(vlr->mat->mode & MA_FULL_OSA) vlr->flag |= R_FULL_OSA; else if(trace) { if(vlr->mat->mode & MA_SHLESS); - else if(vlr->mat->mode & (MA_RAYTRANSP|MA_RAYMIRROR|MA_SHADOW)) - vlr->flag |= R_FULL_OSA; + else if(vlr->mat->mode & (MA_RAYTRANSP|MA_RAYMIRROR)) + /* for blurry reflect/refract, better to take more samples + * inside the raytrace than as OSA samples */ + if ((vlr->mat->gloss_mir == 1.0) && (vlr->mat->gloss_tra == 1.0)) + vlr->flag |= R_FULL_OSA; } } } @@ -3278,8 +3298,12 @@ void RE_Database_FromScene(Render *re, Scene *scene, int use_camera_view) } init_render_world(re); /* do first, because of ambient. also requires re->osa set correct */ - if(re->wrld.mode & WO_AMB_OCC) - init_ao_sphere(&re->wrld); + if(re->wrld.mode & WO_AMB_OCC) { + if (re->wrld.ao_samp_method == WO_AOSAMP_HAMMERSLEY) + init_render_hammersley(re); + else if (re->wrld.ao_samp_method == WO_AOSAMP_CONSTANT) + init_ao_sphere(&re->wrld); + } /* still bad... doing all */ init_render_textures(re); diff --git a/source/blender/render/intern/source/rayshade.c b/source/blender/render/intern/source/rayshade.c index f65a386ebf4..0e4c0bbcc45 100644 --- a/source/blender/render/intern/source/rayshade.c +++ b/source/blender/render/intern/source/rayshade.c @@ -345,19 +345,51 @@ static float shade_by_transmission(Isect *is, ShadeInput *shi, ShadeResult *shr) return d; } +static void ray_fadeout_endcolor(float *col, ShadeInput *origshi, ShadeInput *shi, ShadeResult *shr, Isect *isec, float *vec) +{ + /* un-intersected rays get either rendered material colour or sky colour */ + if (origshi->mat->fadeto_mir == MA_RAYMIR_FADETOMAT) { + VECCOPY(col, shr->combined); + } else if (origshi->mat->fadeto_mir == MA_RAYMIR_FADETOSKY) { + VECCOPY(shi->view, vec); + Normalize(shi->view); + + shadeSkyView(col, isec->start, shi->view, NULL); + } +} + +static void ray_fadeout(Isect *is, ShadeInput *shi, float *col, float *blendcol, float dist_mir) +{ + /* if fading out, linear blend against fade colour */ + float blendfac; + + blendfac = 1.0 - VecLenf(shi->co, is->start)/dist_mir; + + col[0] = col[0]*blendfac + (1.0 - blendfac)*blendcol[0]; + col[1] = col[1]*blendfac + (1.0 - blendfac)*blendcol[1]; + col[2] = col[2]*blendfac + (1.0 - blendfac)*blendcol[2]; +} + /* the main recursive tracer itself */ -static void traceray(ShadeInput *origshi, short depth, float *start, float *vec, float *col, VlakRen *vlr, int traflag) +static void traceray(ShadeInput *origshi, ShadeResult *origshr, short depth, float *start, float *vec, float *col, VlakRen *vlr, int traflag) { ShadeInput shi; ShadeResult shr; Isect isec; float f, f1, fr, fg, fb; - float ref[3], maxsize= RE_ray_tree_max_size(R.raytree); + float ref[3], maxsize=RE_ray_tree_max_size(R.raytree); + float dist_mir = origshi->mat->dist_mir; VECCOPY(isec.start, start); - isec.end[0]= start[0]+maxsize*vec[0]; - isec.end[1]= start[1]+maxsize*vec[1]; - isec.end[2]= start[2]+maxsize*vec[2]; + if (dist_mir > 0.0) { + isec.end[0]= start[0]+dist_mir*vec[0]; + isec.end[1]= start[1]+dist_mir*vec[1]; + isec.end[2]= start[2]+dist_mir*vec[2]; + } else { + isec.end[0]= start[0]+maxsize*vec[0]; + isec.end[1]= start[1]+maxsize*vec[1]; + isec.end[2]= start[2]+maxsize*vec[2]; + } isec.mode= RE_RAY_MIRROR; isec.faceorig= (RayFace*)vlr; @@ -409,10 +441,10 @@ static void traceray(ShadeInput *origshi, short depth, float *start, float *vec, reflection(refract, shi.vn, shi.view, shi.vn); } traflag |= RAY_TRA; - traceray(origshi, depth-1, shi.co, refract, tracol, shi.vlr, traflag ^ RAY_TRAFLIP); + traceray(origshi, origshr, depth-1, shi.co, refract, tracol, shi.vlr, traflag ^ RAY_TRAFLIP); } else - traceray(origshi, depth-1, shi.co, shi.view, tracol, shi.vlr, 0); + traceray(origshi, origshr, depth-1, shi.co, shi.view, tracol, shi.vlr, 0); f= shr.alpha; f1= 1.0f-f; nf= d * shi.mat->filter; @@ -442,7 +474,7 @@ static void traceray(ShadeInput *origshi, short depth, float *start, float *vec, float mircol[4]; reflection(ref, shi.vn, shi.view, NULL); - traceray(origshi, depth-1, shi.co, ref, mircol, shi.vlr, 0); + traceray(origshi, origshr, depth-1, shi.co, ref, mircol, shi.vlr, 0); f1= 1.0f-f; @@ -465,6 +497,15 @@ static void traceray(ShadeInput *origshi, short depth, float *start, float *vec, col[1]= shr.diff[1] + shr.spec[1]; col[2]= shr.diff[2] + shr.spec[2]; } + + if (dist_mir > 0.0) { + float blendcol[3]; + + /* max ray distance set, but found an intersection, so fade this colour + * out towards the sky/material colour for a smooth transition */ + ray_fadeout_endcolor(blendcol, origshi, &shi, origshr, &isec, vec); + ray_fadeout(&isec, &shi, col, blendcol, dist_mir); + } } else { col[0]= shr.diff[0] + shr.spec[0]; @@ -473,11 +514,8 @@ static void traceray(ShadeInput *origshi, short depth, float *start, float *vec, } } - else { /* sky */ - VECCOPY(shi.view, vec); - Normalize(shi.view); - - shadeSkyView(col, isec.start, shi.view, NULL); + else { + ray_fadeout_endcolor(col, origshi, &shi, origshr, &isec, vec); } } @@ -593,14 +631,455 @@ static float *give_jitter_plane(LampRen *lar, int thread, int xs, int ys) } +/* **************** QMC sampling *************** */ + +static void halton_sample(double *ht_invprimes, double *ht_nums, double *v) +{ + // incremental halton sequence generator, from: + // "Instant Radiosity", Keller A. + unsigned int i; + + for (i = 0; i < 2; i++) + { + double r = (1.0 - ht_nums[i]) - 1e-10; + + if (ht_invprimes[i] >= r) + { + double lasth; + double h = ht_invprimes[i]; + + do { + lasth = h; + h *= ht_invprimes[i]; + } while (h >= r); + + ht_nums[i] += ((lasth + h) - 1.0); + } + else + ht_nums[i] += ht_invprimes[i]; + + v[i] = (float)ht_nums[i]; + } +} + +/* Generate Hammersley points in [0,1)^2 + * From Lucille renderer */ +static void hammersley_create(double *out, int n) +{ + double p, t; + int k, kk; + + for (k = 0; k < n; k++) { + t = 0; + for (p = 0.5, kk = k; kk; p *= 0.5, kk >>= 1) { + if (kk & 1) { /* kk mod 2 = 1 */ + t += p; + } + } + + out[2 * k + 0] = (double)k / (double)n; + out[2 * k + 1] = t; + } +} + +struct QMCSampler *QMC_initSampler(int type, int tot) +{ + QMCSampler *qsa = MEM_mallocN(sizeof(QMCSampler), "qmc sampler"); + qsa->samp2d = MEM_mallocN(2*sizeof(double)*tot, "qmc sample table"); + + qsa->tot = tot; + qsa->type = type; + + if (qsa->type==SAMP_TYPE_HAMMERSLEY) + hammersley_create(qsa->samp2d, qsa->tot); + + return qsa; +} + +static void QMC_initPixel(QMCSampler *qsa, int thread) +{ + if (qsa->type==SAMP_TYPE_HAMMERSLEY) + { + /* hammersley sequence is fixed, already created in QMCSampler init. + * per pixel, gets a random offset. We create separate offsets per thread, for write-safety */ + qsa->offs[thread][0] = 0.5 * BLI_thread_frand(thread); + qsa->offs[thread][1] = 0.5 * BLI_thread_frand(thread); + } + else { /* SAMP_TYPE_HALTON */ + + /* generate a new randomised halton sequence per pixel + * to alleviate qmc artifacts and make it reproducable + * between threads/frames */ + double ht_invprimes[2], ht_nums[2]; + double r[2]; + int i; + + ht_nums[0] = BLI_thread_frand(thread); + ht_nums[1] = BLI_thread_frand(thread); + ht_invprimes[0] = 0.5; + ht_invprimes[1] = 1.0/3.0; + + for (i=0; i< qsa->tot; i++) { + halton_sample(ht_invprimes, ht_nums, r); + qsa->samp2d[2*i+0] = r[0]; + qsa->samp2d[2*i+1] = r[1]; + } + } +} + +static void QMC_freeSampler(QMCSampler *qsa) +{ + MEM_freeN(qsa->samp2d); + MEM_freeN(qsa); +} + +static void QMC_getSample(double *s, QMCSampler *qsa, int thread, int num) +{ + if (qsa->type == SAMP_TYPE_HAMMERSLEY) { + s[0] = fmodf(qsa->samp2d[2*num+0] + qsa->offs[thread][0], 1.0f); + s[1] = fmodf(qsa->samp2d[2*num+1] + qsa->offs[thread][1], 1.0f); + } + else { /* SAMP_TYPE_HALTON */ + s[0] = qsa->samp2d[2*num+0]; + s[1] = qsa->samp2d[2*num+1]; + } +} + +/* phong weighted disc using 'blur' for exponent, centred on 0,0 */ +static void QMC_samplePhong(float *vec, QMCSampler *qsa, int thread, int num, float blur) +{ + double s[2]; + float phi, pz, sqr; + + QMC_getSample(s, qsa, thread, num); + + phi = s[0]*2*M_PI; + pz = pow(s[1], blur); + sqr = sqrt(1.0f-pz*pz); + + vec[0] = cos(phi)*sqr; + vec[1] = sin(phi)*sqr; + vec[2] = 0.0f; +} + +/* rect of edge lengths sizex, sizey, centred on 0.0,0.0 i.e. ranging from -sizex/2 to +sizey/2 */ +static void QMC_sampleRect(float *vec, QMCSampler *qsa, int thread, int num, float sizex, float sizey) +{ + double s[2]; + + QMC_getSample(s, qsa, thread, num); + + vec[0] = (s[0] - 0.5) * sizex; + vec[1] = (s[1] - 0.5) * sizey; + vec[2] = 0.0f; +} + +/* disc of radius 'radius', centred on 0,0 */ +static void QMC_sampleDisc(float *vec, QMCSampler *qsa, int thread, int num, float radius) +{ + double s[2]; + float phi, sqr; + + QMC_getSample(s, qsa, thread, num); + + phi = s[0]*2*M_PI; + sqr = sqrt(s[1]); + + vec[0] = cos(phi)*sqr* radius/2.0; + vec[1] = sin(phi)*sqr* radius/2.0; + vec[2] = 0.0f; +} + +/* uniform hemisphere sampling */ +static void QMC_sampleHemi(float *vec, QMCSampler *qsa, int thread, int num) +{ + double s[2]; + float phi, sqr; + + QMC_getSample(s, qsa, thread, num); + + phi = s[0]*2.f*M_PI; + sqr = sqrt(s[1]); + + vec[0] = cos(phi)*sqr; + vec[1] = sin(phi)*sqr; + vec[2] = 1.f - s[1]*s[1]; +} + +/* called from convertBlenderScene.c */ +/* samples don't change per pixel, so build the samples in advance for efficiency */ +void init_lamp_hammersley(LampRen *lar) +{ + lar->qsa = QMC_initSampler(SAMP_TYPE_HAMMERSLEY, lar->ray_totsamp); +} + +void init_render_hammersley(Render *re) +{ + re->qsa = QMC_initSampler(SAMP_TYPE_HAMMERSLEY, R.wrld.aosamp*R.wrld.aosamp); +} + +void free_lamp_qmcsampler(LampRen *lar) +{ + QMC_freeSampler(lar->qsa); +} + +void free_render_qmcsampler(Render *re) +{ + QMC_freeSampler(re->qsa); +} + +static int adaptive_sample_variance(int samples, float *col, float *colsq, float thresh) +{ + float var[3], mean[3]; + + /* scale threshold just to give a bit more precision in input rather than dealing with + * tiny tiny numbers in the UI */ + thresh /= 2; + + mean[0] = col[0] / (float)samples; + mean[1] = col[1] / (float)samples; + mean[2] = col[2] / (float)samples; + + var[0] = (colsq[0] / (float)samples) - (mean[0]*mean[0]); + var[1] = (colsq[1] / (float)samples) - (mean[1]*mean[1]); + var[2] = (colsq[2] / (float)samples) - (mean[2]*mean[2]); + + if ((var[0] * 0.4 < thresh) && (var[1] * 0.3 < thresh) && (var[2] * 0.6 < thresh)) + return 1; + else + return 0; +} + +static int adaptive_sample_contrast(int samples, float *prevcol, float *curcol, float thresh) +{ + /* if the last sample's contribution to the total colour was below a small threshold + * (i.e. the samples taken are very similar), then taking more samples that are probably + * going to be the same is wasting effort */ + if ( (fabs( prevcol[0]/(float)(samples-1) - curcol[0]/(float)(samples) ) < thresh) && + (fabs( prevcol[1]/(float)(samples-1) - curcol[1]/(float)(samples) ) < thresh) && + (fabs( prevcol[2]/(float)(samples-1) - curcol[2]/(float)(samples) ) < thresh) ) + { + return 1; + } + else + return 0; +} + +static int adaptive_sample_contrast_val(int samples, float prev, float val, float thresh) +{ + /* if the last sample's contribution to the total value was below a small threshold + * (i.e. the samples taken are very similar), then taking more samples that are probably + * going to be the same is wasting effort */ + if (fabs( prev/(float)(samples-1) - val/(float)samples ) < thresh) { + return 1; + } else + return 0; +} + /* ***************** main calls ************** */ +static void trace_refract(float *col, ShadeInput *shi, ShadeResult *shr) +{ + QMCSampler *qsa=NULL; + int samp_type; + + float samp3d[3], orthx[3], orthy[3]; + float v_refract[3], v_refract_new[3]; + float sampcol[4], colsq[4]; + + float blur = pow(1.0 - shi->mat->gloss_tra, 3); + short max_samples = shi->mat->samp_gloss_tra; + float adapt_thresh = shi->mat->adapt_thresh_tra; + + int samples=0; + + colsq[0] = colsq[1] = colsq[2] = 0.0; + col[0] = col[1] = col[2] = 0.0; + col[3]= shr->alpha; + + if (blur > 0.0) { + if (adapt_thresh != 0.0) samp_type = SAMP_TYPE_HALTON; + else samp_type = SAMP_TYPE_HAMMERSLEY; + + /* all samples are generated per pixel */ + qsa = QMC_initSampler(samp_type, max_samples); + QMC_initPixel(qsa, shi->thread); + } else + max_samples = 1; + + + while (samples < max_samples) { + refraction(v_refract, shi->vn, shi->view, shi->ang); + + if (max_samples > 1) { + /* get a quasi-random vector from a phong-weighted disc */ + QMC_samplePhong(samp3d, qsa, shi->thread, samples, blur); + + VecOrthoBasisf(v_refract, orthx, orthy); + VecMulf(orthx, samp3d[0]); + VecMulf(orthy, samp3d[1]); + + /* and perturb the refraction vector in it */ + VecAddf(v_refract_new, v_refract, orthx); + VecAddf(v_refract_new, v_refract_new, orthy); + + Normalize(v_refract_new); + } else { + /* no blurriness, use the original normal */ + VECCOPY(v_refract_new, v_refract); + } + + traceray(shi, shr, shi->mat->ray_depth_tra, shi->co, v_refract_new, sampcol, shi->vlr, RAY_TRA|RAY_TRAFLIP); + + col[0] += sampcol[0]; + col[1] += sampcol[1]; + col[2] += sampcol[2]; + col[3] += sampcol[3]; + + /* for variance calc */ + colsq[0] += sampcol[0]*sampcol[0]; + colsq[1] += sampcol[1]*sampcol[1]; + colsq[2] += sampcol[2]*sampcol[2]; + + samples++; + + /* adaptive sampling */ + if (adapt_thresh < 1.0 && samples > max_samples/2) + { + if (adaptive_sample_variance(samples, col, colsq, adapt_thresh)) + break; + + /* if the pixel so far is very dark, we can get away with less samples */ + if ( (col[0] + col[1] + col[2])/3.0/(float)samples < 0.01 ) + max_samples--; + } + } + + col[0] /= (float)samples; + col[1] /= (float)samples; + col[2] /= (float)samples; + col[3] /= (float)samples; + + if (qsa) QMC_freeSampler(qsa); +} + +static void trace_reflect(float *col, ShadeInput *shi, ShadeResult *shr, float fresnelfac) +{ + QMCSampler *qsa=NULL; + int samp_type; + + float samp3d[3], orthx[3], orthy[3]; + float v_nor_new[3], v_facenor_new[3], v_reflect[3]; + float sampcol[4], colsq[4]; + + float blur = pow(1.0 - shi->mat->gloss_mir, 3); + short max_samples = shi->mat->samp_gloss_mir; + float adapt_thresh = shi->mat->adapt_thresh_mir; + float aniso = 1.0 - shi->mat->aniso_gloss_mir; + + int samples=0; + + col[0] = col[1] = col[2] = 0.0; + colsq[0] = colsq[1] = colsq[2] = 0.0; + + if (blur > 0.0) { + if (adapt_thresh != 0.0) samp_type = SAMP_TYPE_HALTON; + else samp_type = SAMP_TYPE_HAMMERSLEY; + + /* all samples are generated per pixel */ + qsa = QMC_initSampler(samp_type, max_samples); + QMC_initPixel(qsa, shi->thread); + } else + max_samples = 1; + + VECCOPY(v_facenor_new, shi->facenor); + + while (samples < max_samples) { + + if (max_samples > 1) { + /* get a quasi-random vector from a phong-weighted disc */ + QMC_samplePhong(samp3d, qsa, shi->thread, samples, blur); + + /* find the normal's perpendicular plane, blurring along tangents + * if tangent shading enabled */ + if (shi->mat->mode & (MA_TANGENT_V)) { + Crossf(orthx, shi->vn, shi->tang); // bitangent + VECCOPY(orthy, shi->tang); + VecMulf(orthx, samp3d[0]); + VecMulf(orthy, samp3d[1]*aniso); + } else { + VecOrthoBasisf(shi->vn, orthx, orthy); + VecMulf(orthx, samp3d[0]); + VecMulf(orthy, samp3d[1]); + } + + /* and perturb the normal in it */ + VecAddf(v_nor_new, shi->vn, orthx); + VecAddf(v_nor_new, v_nor_new, orthy); + VecAddf(v_facenor_new, shi->facenor, orthx); + VecAddf(v_facenor_new, v_facenor_new, orthy); + Normalize(v_nor_new); + Normalize(v_facenor_new); + } else { + /* no blurriness, use the original normal */ + VECCOPY(v_nor_new, shi->vn); + } + + if((shi->vlr->flag & R_SMOOTH)) + reflection(v_reflect, v_nor_new, shi->view, v_facenor_new); + else + reflection(v_reflect, v_nor_new, shi->view, NULL); + + traceray(shi, shr, shi->mat->ray_depth, shi->co, v_reflect, sampcol, shi->vlr, 0); + + + col[0] += sampcol[0]; + col[1] += sampcol[1]; + col[2] += sampcol[2]; + + /* for variance calc */ + colsq[0] += sampcol[0]*sampcol[0]; + colsq[1] += sampcol[1]*sampcol[1]; + colsq[2] += sampcol[2]*sampcol[2]; + + samples++; + + /* adaptive sampling */ + if (adapt_thresh > 0.0 && samples > max_samples/3) + { + if (adaptive_sample_variance(samples, col, colsq, adapt_thresh)) + break; + + /* if the pixel so far is very dark, we can get away with less samples */ + if ( (col[0] + col[1] + col[2])/3.0/(float)samples < 0.01 ) + max_samples--; + + /* reduce samples when reflection is dim due to low ray mirror blend value or fresnel factor + * and when reflection is blurry */ + if (fresnelfac < 0.1 * (blur+1)) { + max_samples--; + + /* even more for very dim */ + if (fresnelfac < 0.05 * (blur+1)) + max_samples--; + } + } + } + + col[0] /= (float)samples; + col[1] /= (float)samples; + col[2] /= (float)samples; + + if (qsa) QMC_freeSampler(qsa); +} + /* extern call from render loop */ void ray_trace(ShadeInput *shi, ShadeResult *shr) { VlakRen *vlr; - float i, f, f1, fr, fg, fb, vec[3], mircol[4], tracol[4]; + float i, f, f1, fr, fg, fb; + float mircol[4], tracol[4]; float diff[3]; int do_tra, do_mir; @@ -615,13 +1094,9 @@ void ray_trace(ShadeInput *shi, ShadeResult *shr) VECCOPY(diff, shr->combined); if(do_tra) { - float refract[3]; float olddiff[3]; - tracol[3]= shr->alpha; - - refraction(refract, shi->vn, shi->view, shi->ang); - traceray(shi, shi->mat->ray_depth_tra, shi->co, refract, tracol, shi->vlr, RAY_TRA|RAY_TRAFLIP); + trace_refract(tracol, shi, shr); f= shr->alpha; f1= 1.0f-f; fr= 1.0f+ shi->mat->filter*(shi->r-1.0f); @@ -648,18 +1123,13 @@ void ray_trace(ShadeInput *shi, ShadeResult *shr) i= shi->ray_mirror*fresnel_fac(shi->view, shi->vn, shi->mat->fresnel_mir_i, shi->mat->fresnel_mir); if(i!=0.0f) { + + trace_reflect(mircol, shi, shr, i); fr= i*shi->mirr; fg= i*shi->mirg; fb= i*shi->mirb; - if(vlr->flag & R_SMOOTH) - reflection(vec, shi->vn, shi->view, shi->facenor); - else - reflection(vec, shi->vn, shi->view, NULL); - - traceray(shi, shi->mat->ray_depth, shi->co, vec, mircol, shi->vlr, 0); - if(shi->passflag & SCE_PASS_REFLECT) { /* mirror pass is not blocked out with spec */ shr->refl[0]= fr*mircol[0] - fr*diff[0]; @@ -942,21 +1412,149 @@ static float *sphere_sampler(int type, int resol, int thread, int xs, int ys) } } +void ray_ao_qmc(ShadeInput *shi, float *shadfac) +{ + Isect isec; + QMCSampler *qsa=NULL; + float samp3d[3]; + float up[3], side[3], dir[3], nrm[3]; + + float maxdist = R.wrld.aodist; + float fac=0.0f, prev=0.0f; + float adapt_thresh = G.scene->world->ao_adapt_thresh; + float bias = G.scene->world->aobias; + + int samples=0; + int max_samples = R.wrld.aosamp*R.wrld.aosamp; + + float dxyview[3], skyadded=0, div; + int aocolor; + + isec.faceorig= (RayFace*)shi->vlr; + isec.face_last= NULL; + isec.mode= (R.wrld.aomode & WO_AODIST)?RE_RAY_SHADOW_TRA:RE_RAY_SHADOW; + isec.lay= -1; + VECCOPY(isec.start, shi->co); + + shadfac[0]= shadfac[1]= shadfac[2]= 0.0f; + + /* prevent sky colors to be added for only shadow (shadow becomes alpha) */ + aocolor= R.wrld.aocolor; + if(shi->mat->mode & MA_ONLYSHADOW) + aocolor= WO_AOPLAIN; + + if(aocolor == WO_AOSKYTEX) { + dxyview[0]= 1.0f/(float)R.wrld.aosamp; + dxyview[1]= 1.0f/(float)R.wrld.aosamp; + dxyview[2]= 0.0f; + } + + /* bias prevents smoothed faces to appear flat */ + if(shi->vlr->flag & R_SMOOTH) { + bias= G.scene->world->aobias; + VECCOPY(nrm, shi->vn); + } + else { + bias= 0.0f; + VECCOPY(nrm, shi->facenor); + } + + VecOrthoBasisf(nrm, up, side); + + /* sampling init */ + if (R.wrld.ao_samp_method==WO_AOSAMP_HALTON) + qsa = QMC_initSampler(SAMP_TYPE_HALTON, max_samples); + else if (R.wrld.ao_samp_method==WO_AOSAMP_HAMMERSLEY) + qsa = R.qsa; + + QMC_initPixel(qsa, shi->thread); + + while (samples < max_samples) { + + /* sampling, returns quasi-random vector in unit hemisphere */ + QMC_sampleHemi(samp3d, qsa, shi->thread, samples); + + dir[0] = (samp3d[0]*up[0] + samp3d[1]*side[0] + samp3d[2]*nrm[0]); + dir[1] = (samp3d[0]*up[1] + samp3d[1]*side[1] + samp3d[2]*nrm[1]); + dir[2] = (samp3d[0]*up[2] + samp3d[1]*side[2] + samp3d[2]*nrm[2]); + + Normalize(dir); + + isec.end[0] = shi->co[0] - maxdist*dir[0]; + isec.end[1] = shi->co[1] - maxdist*dir[1]; + isec.end[2] = shi->co[2] - maxdist*dir[2]; + + prev = fac; + + if(RE_ray_tree_intersect(R.raytree, &isec)) { + if (R.wrld.aomode & WO_AODIST) fac+= exp(-isec.labda*R.wrld.aodistfac); + else fac+= 1.0f; + } + else if(aocolor!=WO_AOPLAIN) { + float skycol[4]; + float skyfac, view[3]; + + view[0]= -dir[0]; + view[1]= -dir[1]; + view[2]= -dir[2]; + Normalize(view); + + if(aocolor==WO_AOSKYCOL) { + skyfac= 0.5*(1.0f+view[0]*R.grvec[0]+ view[1]*R.grvec[1]+ view[2]*R.grvec[2]); + shadfac[0]+= (1.0f-skyfac)*R.wrld.horr + skyfac*R.wrld.zenr; + shadfac[1]+= (1.0f-skyfac)*R.wrld.horg + skyfac*R.wrld.zeng; + shadfac[2]+= (1.0f-skyfac)*R.wrld.horb + skyfac*R.wrld.zenb; + } + else { /* WO_AOSKYTEX */ + shadeSkyView(skycol, isec.start, view, dxyview); + shadfac[0]+= skycol[0]; + shadfac[1]+= skycol[1]; + shadfac[2]+= skycol[2]; + } + skyadded++; + } + + samples++; + + if (qsa->type == SAMP_TYPE_HALTON) { + /* adaptive sampling - consider samples below threshold as in shadow (or vice versa) and exit early */ + if (adapt_thresh > 0.0 && (samples > max_samples/2) ) { + + if (adaptive_sample_contrast_val(samples, prev, fac, adapt_thresh)) { + break; + } + } + } + } + + if(aocolor!=WO_AOPLAIN && skyadded) { + div= (1.0f - fac/(float)samples)/((float)skyadded); + + shadfac[0]*= div; // average color times distances/hits formula + shadfac[1]*= div; // average color times distances/hits formula + shadfac[2]*= div; // average color times distances/hits formula + } else { + shadfac[0]= shadfac[1]= shadfac[2]= 1.0f - fac/(float)samples; + } + + if ((qsa) && (qsa->type == SAMP_TYPE_HALTON)) QMC_freeSampler(qsa); +} /* extern call from shade_lamp_loop, ambient occlusion calculus */ -void ray_ao(ShadeInput *shi, float *shadfac) +void ray_ao_spheresamp(ShadeInput *shi, float *shadfac) { Isect isec; float *vec, *nrm, div, bias, sh=0.0f; float maxdist = R.wrld.aodist; float dxyview[3]; int j= -1, tot, actual=0, skyadded=0, aocolor; - + isec.faceorig= (RayFace*)shi->vlr; isec.face_last= NULL; isec.mode= (R.wrld.aomode & WO_AODIST)?RE_RAY_SHADOW_TRA:RE_RAY_SHADOW; isec.lay= -1; + shadfac[0]= shadfac[1]= shadfac[2]= 0.0f; /* bias prevents smoothed faces to appear flat */ @@ -1000,7 +1598,7 @@ void ray_ao(ShadeInput *shi, float *shadfac) actual++; - /* always set start/end, 3dda clips it */ + /* always set start/end, RE_ray_tree_intersect clips it */ VECCOPY(isec.start, shi->co); isec.end[0] = shi->co[0] - maxdist*vec[0]; isec.end[1] = shi->co[1] - maxdist*vec[1]; @@ -1054,8 +1652,223 @@ void ray_ao(ShadeInput *shi, float *shadfac) } } +void ray_ao(ShadeInput *shi, float *shadfac) +{ + /* Unfortunately, the unusual way that the sphere sampler calculates roughly twice as many + * samples as are actually traced, and skips them based on bias and OSA settings makes it very difficult + * to reuse code between these two functions. This is the easiest way I can think of to do it + * --broken */ + if (ELEM(R.wrld.ao_samp_method, WO_AOSAMP_HAMMERSLEY, WO_AOSAMP_HALTON)) + ray_ao_qmc(shi, shadfac); + else if (R.wrld.ao_samp_method == WO_AOSAMP_CONSTANT) + ray_ao_spheresamp(shi, shadfac); +} + + +static void ray_shadow_qmc(ShadeInput *shi, LampRen *lar, float *lampco, float *shadfac, Isect *isec) +{ + QMCSampler *qsa=NULL; + QMCSampler *qsa_jit=NULL; + int samples=0; + float samp3d[3], jit[3]; + + float fac=0.0f, vec[3]; + float adapt_thresh = lar->adapt_thresh; + int max_samples = lar->ray_totsamp; + float pos[3]; + int do_soft=1; + + if(isec->mode==RE_RAY_SHADOW_TRA) { + shadfac[0]= shadfac[1]= shadfac[2]= shadfac[3]= 0.0f; + } else + shadfac[3]= 1.0f; + + if (lar->ray_totsamp < 2) do_soft = 0; + if (do_soft) max_samples = lar->ray_totsamp; + else max_samples = (R.osa > 4)?R.osa:5; + + /* sampling init */ + if (lar->ray_samp_method==LA_SAMP_HALTON) { + qsa = QMC_initSampler(SAMP_TYPE_HALTON, max_samples); + qsa_jit = QMC_initSampler(SAMP_TYPE_HALTON, max_samples); + } else if (lar->ray_samp_method==LA_SAMP_HAMMERSLEY) { + qsa = lar->qsa; + qsa_jit = QMC_initSampler(SAMP_TYPE_HAMMERSLEY, max_samples); + } + + QMC_initPixel(qsa, shi->thread); + QMC_initPixel(qsa_jit, shi->thread); + + VECCOPY(vec, lampco); + + + while (samples < max_samples) { + isec->faceorig= (RayFace*)shi->vlr; + + /* manually jitter the start shading co-ord per sample + * based on the pre-generated OSA texture sampling offsets, + * for anti-aliasing sharp shadow edges. */ + VECCOPY(pos, shi->co); + if (shi->vlr && ((shi->vlr->flag & R_FULL_OSA) == 0)) { + QMC_sampleRect(jit, qsa_jit, shi->thread, samples, 1.0, 1.0); + + pos[0] += shi->dxco[0]*jit[0] + shi->dyco[0]*jit[1]; + pos[1] += shi->dxco[1]*jit[0] + shi->dyco[1]*jit[1]; + pos[2] += shi->dxco[2]*jit[0] + shi->dyco[2]*jit[1]; + } + + if (do_soft) { + /* sphere shadow source */ + if (lar->type == LA_LOCAL) { + float ru[3], rv[3], v[3], s[3]; + + /* calc tangent plane vectors */ + v[0] = pos[0] - lampco[0]; + v[1] = pos[1] - lampco[1]; + v[2] = pos[2] - lampco[2]; + Normalize(v); + VecOrthoBasisf(v, ru, rv); + + /* sampling, returns quasi-random vector in area_size disc */ + QMC_sampleDisc(samp3d, qsa, shi->thread, samples,lar->area_size); + + /* distribute disc samples across the tangent plane */ + s[0] = samp3d[0]*ru[0] + samp3d[1]*rv[0]; + s[1] = samp3d[0]*ru[1] + samp3d[1]*rv[1]; + s[2] = samp3d[0]*ru[2] + samp3d[1]*rv[2]; + + VECCOPY(samp3d, s); + } + else { + /* sampling, returns quasi-random vector in [sizex,sizey]^2 plane */ + QMC_sampleRect(samp3d, qsa, shi->thread, samples, lar->area_size, lar->area_sizey); + + /* align samples to lamp vector */ + Mat3MulVecfl(lar->mat, samp3d); + } + isec->end[0]= vec[0]+samp3d[0]; + isec->end[1]= vec[1]+samp3d[1]; + isec->end[2]= vec[2]+samp3d[2]; + } else { + VECCOPY(isec->end, vec); + } + VECCOPY(isec->start, pos); + + + /* trace the ray */ + if(isec->mode==RE_RAY_SHADOW_TRA) { + isec->col[0]= isec->col[1]= isec->col[2]= 1.0f; + isec->col[3]= 1.0f; + + ray_trace_shadow_tra(isec, DEPTH_SHADOW_TRA, 0); + shadfac[0] += isec->col[0]; + shadfac[1] += isec->col[1]; + shadfac[2] += isec->col[2]; + shadfac[3] += isec->col[3]; + } + else { + if( RE_ray_tree_intersect(R.raytree, isec) ) fac+= 1.0f; + } + + samples++; + + if ((lar->ray_samp_method == LA_SAMP_HALTON)) { + /* adaptive sampling - consider samples below threshold as in shadow (or vice versa) and exit early */ + if ((do_soft) && (adapt_thresh > 0.0)) { + if ( (samples > max_samples/3) && ((fac / samples > (1.0-adapt_thresh)) || (fac / samples < adapt_thresh)) ) break; + } + } + } + + if(isec->mode==RE_RAY_SHADOW_TRA) { + shadfac[0] /= samples; + shadfac[1] /= samples; + shadfac[2] /= samples; + shadfac[3] /= samples; + } else + shadfac[3]= 1.0f-fac/samples; + + if (qsa_jit) QMC_freeSampler(qsa_jit); + if ((qsa) && (qsa->type == SAMP_TYPE_HALTON)) QMC_freeSampler(qsa); +} +static void ray_shadow_jitter(ShadeInput *shi, LampRen *lar, float *lampco, float *shadfac, Isect *isec) +{ + /* area soft shadow */ + float *jitlamp; + float fac=0.0f, div=0.0f, vec[3]; + int a, j= -1, mask; + + if(isec->mode==RE_RAY_SHADOW_TRA) { + shadfac[0]= shadfac[1]= shadfac[2]= shadfac[3]= 0.0f; + } + else shadfac[3]= 1.0f; + + fac= 0.0f; + jitlamp= give_jitter_plane(lar, shi->thread, shi->xs, shi->ys); + a= lar->ray_totsamp; + + /* this correction to make sure we always take at least 1 sample */ + mask= shi->mask; + if(a==4) mask |= (mask>>4)|(mask>>8); + else if(a==9) mask |= (mask>>9); + + while(a--) { + + if(R.r.mode & R_OSA) { + j++; + if(j>=R.osa) j= 0; + if(!(mask & (1<<j))) { + jitlamp+= 2; + continue; + } + } + + isec->faceorig= (RayFace*)shi->vlr; + + vec[0]= jitlamp[0]; + vec[1]= jitlamp[1]; + vec[2]= 0.0f; + Mat3MulVecfl(lar->mat, vec); + + /* set start and end, RE_ray_tree_intersect clips it */ + VECCOPY(isec->start, shi->co); + isec->end[0]= lampco[0]+vec[0]; + isec->end[1]= lampco[1]+vec[1]; + isec->end[2]= lampco[2]+vec[2]; + + if(isec->mode==RE_RAY_SHADOW_TRA) { + /* isec.col is like shadfac, so defines amount of light (0.0 is full shadow) */ + isec->col[0]= isec->col[1]= isec->col[2]= 1.0f; + isec->col[3]= 1.0f; + + ray_trace_shadow_tra(isec, DEPTH_SHADOW_TRA, 0); + shadfac[0] += isec->col[0]; + shadfac[1] += isec->col[1]; + shadfac[2] += isec->col[2]; + shadfac[3] += isec->col[3]; + } + else if( RE_ray_tree_intersect(R.raytree, isec) ) fac+= 1.0f; + + div+= 1.0f; + jitlamp+= 2; + } + + if(isec->mode==RE_RAY_SHADOW_TRA) { + shadfac[0] /= div; + shadfac[1] /= div; + shadfac[2] /= div; + shadfac[3] /= div; + } + else { + // sqrt makes nice umbra effect + if(lar->ray_samp_type & LA_SAMP_UMBRA) + shadfac[3]= sqrt(1.0f-fac/div); + else + shadfac[3]= 1.0f-fac/div; + } +} /* extern call from shade_lamp_loop */ void ray_shadow(ShadeInput *shi, LampRen *lar, float *shadfac) { @@ -1074,7 +1887,6 @@ void ray_shadow(ShadeInput *shi, LampRen *lar, float *shadfac) else isec.face_last= NULL; - if(lar->type==LA_SUN || lar->type==LA_HEMI) { maxsize= RE_ray_tree_max_size(R.raytree); lampco[0]= shi->co[0] - maxsize*lar->vec[0]; @@ -1085,103 +1897,35 @@ void ray_shadow(ShadeInput *shi, LampRen *lar, float *shadfac) VECCOPY(lampco, lar->co); } - if(lar->ray_totsamp<2) { - - isec.faceorig= (RayFace*)shi->vlr; - shadfac[3]= 1.0f; // 1.0=full light + if (ELEM(lar->ray_samp_method, LA_SAMP_HALTON, LA_SAMP_HAMMERSLEY)) { - /* set up isec vec */ - VECCOPY(isec.start, shi->co); - VECCOPY(isec.end, lampco); - - if(isec.mode==RE_RAY_SHADOW_TRA) { - /* isec.col is like shadfac, so defines amount of light (0.0 is full shadow) */ - isec.col[0]= isec.col[1]= isec.col[2]= 1.0f; - isec.col[3]= 1.0f; - - ray_trace_shadow_tra(&isec, DEPTH_SHADOW_TRA, 0); - QUATCOPY(shadfac, isec.col); - //printf("shadfac %f %f %f %f\n", shadfac[0], shadfac[1], shadfac[2], shadfac[3]); - } - else if( RE_ray_tree_intersect(R.raytree, &isec)) shadfac[3]= 0.0f; - } - else { - /* area soft shadow */ - float *jitlamp; - float fac=0.0f, div=0.0f, vec[3]; - int a, j= -1, mask; + ray_shadow_qmc(shi, lar, lampco, shadfac, &isec); - if(isec.mode==RE_RAY_SHADOW_TRA) { - shadfac[0]= shadfac[1]= shadfac[2]= shadfac[3]= 0.0f; - } - else shadfac[3]= 1.0f; // 1.0=full light - - fac= 0.0f; - jitlamp= give_jitter_plane(lar, shi->thread, shi->xs, shi->ys); - - a= lar->ray_totsamp; - - /* this correction to make sure we always take at least 1 sample */ - mask= shi->mask; - if(a==4) mask |= (mask>>4)|(mask>>8); - else if(a==9) mask |= (mask>>9); - - while(a--) { - - if(R.r.mode & R_OSA) { - j++; - if(j>=R.osa) j= 0; - if(!(mask & (1<<j))) { - jitlamp+= 2; - continue; - } - } + } else { + if(lar->ray_totsamp<2) { - isec.faceorig= (RayFace*)shi->vlr; // ray_trace_shadow_tra changes it + isec.faceorig= (RayFace*)shi->vlr; + shadfac[3]= 1.0f; // 1.0=full light - vec[0]= jitlamp[0]; - vec[1]= jitlamp[1]; - vec[2]= 0.0f; - Mat3MulVecfl(lar->mat, vec); - - /* set start and end, RE_ray_tree_intersect clips it */ + /* set up isec vec */ VECCOPY(isec.start, shi->co); - isec.end[0]= lampco[0]+vec[0]; - isec.end[1]= lampco[1]+vec[1]; - isec.end[2]= lampco[2]+vec[2]; - + VECCOPY(isec.end, lampco); + if(isec.mode==RE_RAY_SHADOW_TRA) { /* isec.col is like shadfac, so defines amount of light (0.0 is full shadow) */ isec.col[0]= isec.col[1]= isec.col[2]= 1.0f; isec.col[3]= 1.0f; - + ray_trace_shadow_tra(&isec, DEPTH_SHADOW_TRA, 0); - shadfac[0] += isec.col[0]; - shadfac[1] += isec.col[1]; - shadfac[2] += isec.col[2]; - shadfac[3] += isec.col[3]; + QUATCOPY(shadfac, isec.col); } - else if( RE_ray_tree_intersect(R.raytree, &isec) ) fac+= 1.0f; - - div+= 1.0f; - jitlamp+= 2; - } - - if(isec.mode==RE_RAY_SHADOW_TRA) { - shadfac[0] /= div; - shadfac[1] /= div; - shadfac[2] /= div; - shadfac[3] /= div; + else if(RE_ray_tree_intersect(R.raytree, &isec)) shadfac[3]= 0.0f; } else { - // sqrt makes nice umbra effect - if(lar->ray_samp_type & LA_SAMP_UMBRA) - shadfac[3]= sqrt(1.0f-fac/div); - else - shadfac[3]= 1.0f-fac/div; + ray_shadow_jitter(shi, lar, lampco, shadfac, &isec); } } - + /* for first hit optim, set last interesected shadow face */ if(shi->depth==0) lar->vlr_last[shi->thread]= (VlakRen*)isec.face_last; diff --git a/source/blender/src/buttons_shading.c b/source/blender/src/buttons_shading.c index ab0c1cb3ad2..3eaf3ad8582 100644 --- a/source/blender/src/buttons_shading.c +++ b/source/blender/src/buttons_shading.c @@ -2135,13 +2135,17 @@ static void world_panel_amb_occ(World *wrld) if(wrld->mode & WO_AMB_OCC) { /* aolight: samples */ - uiBlockBeginAlign(block); - uiDefButS(block, NUM, B_REDR, "Samples:", 10, 120, 150, 19, &wrld->aosamp, 1.0, 16.0, 100, 0, "Sets the number of samples used for AO (actual number: squared)"); - /* enable/disable total random sampling */ - uiDefButBitS(block, TOG, WO_AORNDSMP, 0, "Random Sampling", 160, 120, 150, 19, &wrld->aomode, 0, 0, 0, 0, "When enabled, total random sampling will be used for an even noisier effect"); - uiBlockEndAlign(block); - - uiDefButF(block, NUM, B_REDR, "Dist:", 10, 95, 150, 19, &wrld->aodist, 0.001, 5000.0, 100, 0, "Sets length of AO rays, defines how far away other faces give occlusion effect"); + uiDefButS(block, MENU, B_REDR, "Constant QMC %x2|Adaptive QMC %x1|Constant Jittered %x0", + 10, 120, 145, 19, &wrld->ao_samp_method, 0, 0, 0, 0, "Method for generating shadow samples: Constant QMC: best quality, Adaptive QMC: fast in high contrast areas"); + uiDefButS(block, NUM, B_REDR, "Samples:", + 165, 120, 145, 19, &wrld->aosamp, 1.0, 32.0, 100, 0, "Sets the number of samples used for AO (actual number: squared)"); + + if (wrld->ao_samp_method == WO_AOSAMP_HALTON) { + uiDefButF(block, NUM, B_REDR, "Threshold:", + 10, 95, 145, 19, &wrld->ao_adapt_thresh, 0.0, 1.0, 100, 0, "Samples below this threshold will be considered fully shadowed/unshadowed and skipped"); + } + uiDefButF(block, NUM, B_REDR, "Dist:", + 165, 95, 145, 19, &wrld->aodist, 0.001, 5000.0, 100, 0, "Sets length of AO rays, defines how far away other faces give occlusion effect"); uiBlockBeginAlign(block); uiDefButBitS(block, TOG, WO_AODIST, B_AO_DISTANCES, "Use Distances", 10, 70, 150, 19, &wrld->aomode, 0, 0, 0, 0, "When enabled, distances to objects will be used to attenuate shadows. Only for Plain AO."); @@ -2163,7 +2167,8 @@ static void world_panel_amb_occ(World *wrld) uiBlockBeginAlign(block); uiDefButF(block, NUMSLI, B_REDR, "Energy:", 10, 0, 150, 19, &wrld->aoenergy, 0.01, 3.0, 100, 0, "Sets global energy scale for AO"); - uiDefButF(block, NUMSLI, B_REDR, "Bias:", 160, 0, 150, 19, &wrld->aobias, 0.0, 0.5, 10, 0, "Sets bias to prevent smoothed faces to show banding (in radians)"); + if (wrld->ao_samp_method == WO_AOSAMP_CONSTANT) + uiDefButF(block, NUMSLI, B_REDR, "Bias:", 160, 0, 150, 19, &wrld->aobias, 0.0, 0.5, 10, 0, "Sets bias to prevent smoothed faces to show banding (in radians)"); } } @@ -2483,7 +2488,6 @@ static void lamp_panel_spot(Object *ob, Lamp *la) uiDefButBitS(block, TOG, LA_SQUARE, B_LAMPREDRAW,"Square", 10,60,80,19,&la->mode, 0, 0, 0, 0, "Sets square spotbundles"); uiDefButBitS(block, TOG, LA_HALO, B_LAMPREDRAW,"Halo", 10,40,80,19,&la->mode, 0, 0, 0, 0, "Renders spotlight with a volumetric halo"); - uiBlockSetCol(block, TH_AUTO); uiBlockBeginAlign(block); uiDefButF(block, NUMSLI,B_LAMPREDRAW,"SpotSi ", 100,180,200,19,&la->spotsize, 1.0, 180.0, 0, 0, "Sets the angle of the spotlight beam in degrees"); uiDefButF(block, NUMSLI,B_LAMPREDRAW,"SpotBl ", 100,160,200,19,&la->spotblend, 0.0, 1.0, 0, 0, "Sets the softness of the spotlight edge"); @@ -2528,25 +2532,46 @@ static void lamp_panel_spot(Object *ob, Lamp *la) } } - else if(la->type==LA_AREA && (la->mode & LA_SHAD_RAY)) { - uiBlockBeginAlign(block); - uiBlockSetCol(block, TH_AUTO); - if(la->area_shape==LA_AREA_SQUARE) - uiDefButS(block, NUM,0,"Samples:", 100,180,200,19, &la->ray_samp, 1.0, 16.0, 100, 0, "Sets the amount of samples taken extra (samp x samp)"); - if(la->area_shape==LA_AREA_CUBE) - uiDefButS(block, NUM,0,"Samples:", 100,160,200,19, &la->ray_samp, 1.0, 16.0, 100, 0, "Sets the amount of samples taken extra (samp x samp x samp)"); - - if (ELEM(la->area_shape, LA_AREA_RECT, LA_AREA_BOX)) { - uiDefButS(block, NUM,0,"SamplesX:", 100,180,200,19, &la->ray_samp, 1.0, 16.0, 100, 0, "Sets the amount of X samples taken extra"); - uiDefButS(block, NUM,0,"SamplesY:", 100,160,200,19, &la->ray_sampy, 1.0, 16.0, 100, 0, "Sets the amount of Y samples taken extra"); - if(la->area_shape==LA_AREA_BOX) - uiDefButS(block, NUM,0,"SamplesZ:", 100,140,200,19, &la->ray_sampz, 1.0, 8.0, 100, 0, "Sets the amount of Z samples taken extra"); + if(ELEM4(la->type, LA_AREA, LA_SPOT, LA_SUN, LA_LOCAL) && (la->mode & LA_SHAD_RAY)) { + + if (ELEM3(la->type, LA_SPOT, LA_SUN, LA_LOCAL)) { + if (la->ray_samp_method == LA_SAMP_CONSTANT) la->ray_samp_method = LA_SAMP_HALTON; + + uiDefButS(block, MENU, B_REDR, "Adaptive QMC %x1|Constant QMC %x2", + 100,110,200,19, &la->ray_samp_method, 0, 0, 0, 0, "Method for generating shadow samples: Adaptive QMC is fastest, Constant QMC is less noisy but slower"); + + uiDefButF(block, NUM,B_LAMPREDRAW,"Soft Size", 100,80,200,19, &la->area_size, 0.01, 100.0, 10, 0, "Area light size, doesn't affect energy amount"); + + uiDefButS(block, NUM,0,"Samples:", 100,60,200,19, &la->ray_samp, 1.0, 16.0, 100, 0, "Sets the amount of samples taken extra (samp x samp)"); + uiDefButF(block, NUM,0,"Threshold:", 100,40,200,19, &la->adapt_thresh, 0.0, 1.0, 100, 0, "Threshold for adaptive sampling, to control what level is considered already in shadow"); } + else if (la->type == LA_AREA) { + uiDefButS(block, MENU, B_REDR, "Adaptive QMC %x1|Constant QMC %x2|Constant Jittered %x0", + 100,180,200,19, &la->ray_samp_method, 0, 0, 0, 0, "Method for generating shadow samples: Adaptive QMC is fastest"); + + if(la->area_shape==LA_AREA_SQUARE) + uiDefButS(block, NUM,0,"Samples:", 100,150,200,19, &la->ray_samp, 1.0, 16.0, 100, 0, "Sets the amount of samples taken extra (samp x samp)"); + else if(la->area_shape==LA_AREA_CUBE) + uiDefButS(block, NUM,0,"Samples:", 100,130,200,19, &la->ray_samp, 1.0, 16.0, 100, 0, "Sets the amount of samples taken extra (samp x samp x samp)"); + + if (ELEM(la->area_shape, LA_AREA_RECT, LA_AREA_BOX)) { + uiDefButS(block, NUM,0,"SamplesX:", 100,150,200,19, &la->ray_samp, 1.0, 16.0, 100, 0, "Sets the amount of X samples taken extra"); + uiDefButS(block, NUM,0,"SamplesY:", 100,130,200,19, &la->ray_sampy, 1.0, 16.0, 100, 0, "Sets the amount of Y samples taken extra"); + if(la->area_shape==LA_AREA_BOX) + uiDefButS(block, NUM,0,"SamplesZ:", 100,110,200,19, &la->ray_sampz, 1.0, 8.0, 100, 0, "Sets the amount of Z samples taken extra"); + } + + if (la->ray_samp_method == LA_SAMP_CONSTANT) { + uiBlockBeginAlign(block); + uiDefButBitS(block, TOG, LA_SAMP_UMBRA, 0,"Umbra", 100,90,200,19,&la->ray_samp_type, 0, 0, 0, 0, "Emphasis parts that are fully shadowed"); + uiDefButBitS(block, TOG, LA_SAMP_DITHER, 0,"Dither", 100,70,100,19,&la->ray_samp_type, 0, 0, 0, 0, "Use 2x2 dithering for sampling"); + uiDefButBitS(block, TOG, LA_SAMP_JITTER, 0,"Noise", 200,70,100,19,&la->ray_samp_type, 0, 0, 0, 0, "Use noise for sampling"); + } else if (la->ray_samp_method == LA_SAMP_HALTON) { + uiDefButF(block, NUM,0,"Threshold:", 100,90,200,19, &la->adapt_thresh, 0.0, 1.0, 100, 0, "Threshold for adaptive sampling, to control what level is considered already in shadow"); + } + } + - uiBlockBeginAlign(block); - uiDefButBitS(block, TOG, LA_SAMP_UMBRA, 0,"Umbra", 100,110,200,19,&la->ray_samp_type, 0, 0, 0, 0, "Emphasis parts that are fully shadowed"); - uiDefButBitS(block, TOG, LA_SAMP_DITHER, 0,"Dither", 100,90,100,19,&la->ray_samp_type, 0, 0, 0, 0, "Use 2x2 dithering for sampling"); - uiDefButBitS(block, TOG, LA_SAMP_JITTER, 0,"Noise", 200,90,100,19,&la->ray_samp_type, 0, 0, 0, 0, "Use noise for sampling"); } else uiDefBut(block, LABEL,0," ", 100,180,200,19,NULL, 0, 0, 0, 0, ""); @@ -3237,44 +3262,102 @@ static void material_panel_texture(Material *ma) static void material_panel_tramir(Material *ma) { - uiBlock *block; - - block= uiNewBlock(&curarea->uiblocks, "material_panel_tramir", UI_EMBOSS, UI_HELV, curarea->win); - uiNewPanelTabbed("Shaders", "Material"); - if(uiNewPanel(curarea, block, "Mirror Transp", "Material", 640, 0, 318, 204)==0) return; - - uiSetButLock(ma->id.lib!=NULL, ERROR_LIBDATA_MESSAGE); + uiBlock *block; + short yco=PANEL_YMAX; + + block= uiNewBlock(&curarea->uiblocks, "material_panel_tramir", UI_EMBOSS, UI_HELV, curarea->win); + uiNewPanelTabbed("Shaders", "Material"); + if(uiNewPanel(curarea, block, "Mirror Transp", "Material", PANELX, PANELY, PANELW, PANELH+80)==0) return; + + uiSetButLock(ma->id.lib!=NULL, ERROR_LIBDATA_MESSAGE); + + uiDefButBitI(block, TOG, MA_RAYMIRROR, B_MATPRV, "Ray Mirror", + X2CLM1, yco-=BUTH, BUTW2, BUTH, &(ma->mode), 0, 0, 0, 0, "Enables raytracing for mirror reflection rendering"); + + yco -= YSPACE; + + uiBlockBeginAlign(block); + uiDefButF(block, NUMSLI, B_MATPRV, "RayMir: ", + X2CLM1, yco-=BUTH, BUTW2, BUTH, &(ma->ray_mirror), 0.0, 1.0, 100, 2, "Sets the amount mirror reflection for raytrace"); + uiDefButF(block, NUMSLI, B_MATPRV, "Fresnel: ", + X2CLM1, yco-=BUTH, BUTW2, BUTH, &(ma->fresnel_mir), 0.0, 5.0, 10, 2, "Power of Fresnel for mirror reflection"); + uiDefButF(block, NUMSLI, B_MATPRV, "Fac: ", + X2CLM1, yco-=BUTH, BUTW2, BUTH, &(ma->fresnel_mir_i), 1.0, 5.0, 10, 2, "Blending factor for Fresnel"); + uiBlockEndAlign(block); + + yco -= YSPACE; - uiDefButBitI(block, TOG, MA_RAYMIRROR, B_MATPRV,"Ray Mirror",210,180,100,20, &(ma->mode), 0, 0, 0, 0, "Enables raytracing for mirror reflection rendering"); - uiBlockBeginAlign(block); - uiDefButF(block, NUMSLI, B_MATPRV, "RayMir ", 10,160,200,20, &(ma->ray_mirror), 0.0, 1.0, 100, 2, "Sets the amount mirror reflection for raytrace"); - uiDefButS(block, NUM, B_MATPRV, "Depth:", 210,160,100,20, &(ma->ray_depth), 0.0, 10.0, 100, 0, "Amount of inter-reflections calculated maximal "); - - uiDefButF(block, NUMSLI, B_MATPRV, "Fresnel ", 10,140,160,20, &(ma->fresnel_mir), 0.0, 5.0, 10, 2, "Power of Fresnel for mirror reflection"); - uiDefButF(block, NUMSLI, B_MATPRV, "Fac ", 170,140,140,20, &(ma->fresnel_mir_i), 1.0, 5.0, 10, 2, "Blending factor for Fresnel"); + uiDefButF(block, NUMSLI, B_MATPRV, "Gloss: ", + X2CLM1, yco-=BUTH, BUTW2, BUTH, &(ma->gloss_mir), 0.0, 1.0, 100, 0, "The shininess of the reflection. Values < 1.0 give diffuse, blurry reflections "); + uiDefButF(block, NUMSLI, B_MATPRV, "Aniso: ", + X2CLM1, yco-=BUTH, BUTW2, BUTH, &(ma->aniso_gloss_mir), 0.0, 1.0, 100, 0, "The shape of the reflection, from 0. (circular) to 1.0 (fully stretched along the tangent)"); + uiDefButS(block, NUM, B_MATPRV, "Samples:", + X2CLM1, yco-=BUTH, BUTW2, BUTH, &(ma->samp_gloss_mir), 0.0, 1024.0, 100, 0, "Number of cone samples averaged for blurry reflections"); + uiDefButF(block, NUM, B_MATPRV, "Thresh: ", + X2CLM1, yco-=BUTH, BUTW2, BUTH, &(ma->adapt_thresh_mir), 0.0, 1.0, 100, 0, "Threshold for adaptive sampling. If a sample contributes less than this amount (as a percentage), sampling is stopped"); + uiBlockEndAlign(block); + + yco -= YSPACE; + uiDefButS(block, NUM, B_MATPRV, "Depth:", + X2CLM1, yco-=BUTH, BUTW2, BUTH, &(ma->ray_depth), 0.0, 10.0, 100, 0, "Maximum allowed number of light inter-reflections"); + + yco -= YSPACE; + uiBlockBeginAlign(block); + uiDefButF(block, NUM, B_MATPRV, "Max Dist:", + X2CLM1, yco-=BUTH, BUTW2, BUTH, &(ma->dist_mir), 0.0, 100.0, 100, 0, "Maximum distance of reflected rays. Reflections further than this range fade to sky color"); + uiDefButS(block, MENU, B_MATPRV, "Ray end fade-out: %t|Fade to Sky Color %x0|Fade to Material Color %x1", + X2CLM1, yco-=BUTH, BUTW2, BUTH, &(ma->fadeto_mir), 0, 0, 0, 0, "The color that rays with no intersection within the Max Distance take. Material color can be best for indoor scenes, sky color for outdoor."); + uiBlockEndAlign(block); + + yco=PANEL_YMAX; + uiDefButBitI(block, TOG, MA_RAYTRANSP, B_MATRAYTRANSP,"Ray Transp", + X2CLM2, yco-=BUTH, BUTW2, BUTH, &(ma->mode), 0, 0, 0, 0, "Enables raytracing for transparent refraction rendering"); + + yco -= YSPACE; + uiBlockBeginAlign(block); - uiDefButF(block, NUM, B_MATPRV, "Filt:", 10,110,150,20, &(ma->filter), 0.0, 1.0, 10, 0, "Amount of filtering for transparent raytrace"); - uiDefButBitI(block, TOG, MA_RAYTRANSP, B_MATRAYTRANSP,"Ray Transp",160,110,150,20, &(ma->mode), 0, 0, 0, 0, "Enables raytracing for transparency rendering"); - - /* uiBlockBeginAlign(block); */ - uiDefButF(block, NUMSLI, B_MATPRV, "IOR ", 10,90,200,20, &(ma->ang), 1.0, 3.0, 100, 2, "Sets the angular index of refraction for raytrace"); - uiDefButS(block, NUM, B_MATPRV, "Depth:", 210,90,100,20, &(ma->ray_depth_tra), 0.0, 10.0, 100, 0, "Amount of refractions calculated maximal "); - - uiDefButF(block, NUMSLI, B_MATPRV, "Limit ", 10,70,160,20, &(ma->tx_limit), 0.0, 100.0, 10, 2, "Depth limit for transmissivity (0.0 is disabled)"); - uiDefButF(block, NUMSLI, B_MATPRV, "Falloff ", 170,70,140,20, &(ma->tx_falloff), 0.1, 10.0, 10, 2, "Falloff power for transmissivity (1.0 is linear)"); - - uiDefButF(block, NUMSLI, B_MATPRV, "Fresnel ", 10,50,160,20, &(ma->fresnel_tra), 0.0, 5.0, 10, 2, "Power of Fresnel for transparency"); - uiDefButF(block, NUMSLI, B_MATPRV, "Fac ", 170,50,140,20, &(ma->fresnel_tra_i), 1.0, 5.0, 10, 2, "Blending factor for Fresnel"); - + uiDefButF(block, NUMSLI, B_MATPRV, "IOR: ", + X2CLM2, yco-=BUTH, BUTW2, BUTH, &(ma->ang), 1.0, 3.0, 100, 2, "Sets angular index of refraction for raytraced refraction"); + uiDefButF(block, NUMSLI, B_MATPRV, "Fresnel: ", + X2CLM2, yco-=BUTH, BUTW2, BUTH, &(ma->fresnel_tra), 0.0, 5.0, 10, 2, "Power of Fresnel for mirror reflection"); + uiDefButF(block, NUMSLI, B_MATPRV, "Fac: ", + X2CLM2, yco-=BUTH, BUTW2, BUTH, &(ma->fresnel_tra_i), 1.0, 5.0, 10, 2, "Blending factor for Fresnel"); + uiBlockEndAlign(block); + + yco -= YSPACE; + uiBlockBeginAlign(block); - uiDefButF(block, NUMSLI, B_MATPRV, "SpecTra ", 10,20,150,20, &(ma->spectra), 0.0, 1.0, 0, 0, "Makes specular areas opaque on transparent materials"); -// uiDefButF(block, NUMSLI, B_MATPRV, "Add ", 160,20,150,20, &(ma->add), 0.0, 1.0, 0, 0, "Uses additive blending for Z-transparant materials"); - + uiDefButF(block, NUMSLI, B_MATPRV, "Gloss: ", + X2CLM2, yco-=BUTH, BUTW2, BUTH, &(ma->gloss_tra), 0.0, 1.0, 100, 0, "The clarity of the refraction. Values < 1.0 give diffuse, blurry reflections "); + uiDefButS(block, NUM, B_MATPRV, "Samples:", + X2CLM2, yco-=BUTH, BUTW2, BUTH, &(ma->samp_gloss_tra), 0.0, 1024.0, 100, 0, "Number of cone samples averaged for blurry refractions"); + uiDefButF(block, NUM, B_MATPRV, "Thresh: ", + X2CLM2, yco-=BUTH, BUTW2, BUTH, &(ma->adapt_thresh_tra), 0.0, 1.0, 100, 0, "Threshold for adaptive sampling. If a sample contributes less than this amount (as a percentage), sampling is stopped"); + uiBlockEndAlign(block); + + yco -= YSPACE; + + uiDefButS(block, NUM, B_MATPRV, "Depth:", + X2CLM2, yco-=BUTH, BUTW2, BUTH, &(ma->ray_depth_tra), 0.0, 10.0, 100, 0, "Maximum allowed number of light inter-refractions"); + + yco -= YSPACE; + + uiBlockBeginAlign(block); + uiDefButF(block, NUM, B_MATPRV, "Filter:", + X2CLM2, yco-=BUTH, BUTW2, BUTH, &(ma->filter), 0.0, 1.0, 10, 0, "Amount to blend in the material's diffuse colour in raytraced transparency (simulating absorption)"); + uiDefButF(block, NUMSLI, B_MATPRV, "Limit: ", + X2CLM2, yco-=BUTH, BUTW2, BUTH, &(ma->tx_limit), 0.0, 100.0, 10, 2, "Maximum depth for light to travel through the transparent material before becoming fully filtered (0.0 is disabled)"); + uiDefButF(block, NUMSLI, B_MATPRV, "Falloff: ", + X2CLM2, yco-=BUTH, BUTW2, BUTH, &(ma->tx_falloff), 0.1, 10.0, 10, 2, "Falloff power for transmissivity filter effect (1.0 is linear)"); uiBlockEndAlign(block); -} + yco -= YSPACE; + + uiDefButF(block, NUMSLI, B_MATPRV, "SpecTra: ", + X2CLM2, yco-=BUTH, BUTW2, BUTH, &(ma->spectra), 0.0, 1.0, 0, 0, "Makes specular areas opaque on transparent materials"); +} /* yafray: adapted version of Blender's tramir panel. * Only removed the buttons not needed, so only the ones that are important for yafray are left. |