/* * ***** 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * * The Original Code is Copyright (C) 2001-2002 by NaN Holding BV. * All rights reserved. * * Contributors: Hos, Robert Wenzlaff. * Contributors: 2004/2005/2006 Blender Foundation, full recode * * ***** END GPL LICENSE BLOCK ***** */ /** \file blender/render/intern/source/rendercore.c * \ingroup render */ /* system includes */ #include #include #include #include #include /* External modules: */ #include "MEM_guardedalloc.h" #include "BLI_math.h" #include "BLI_blenlib.h" #include "BLI_rand.h" #include "BLI_threads.h" #include "BLI_utildefines.h" #include "DNA_image_types.h" #include "DNA_lamp_types.h" #include "DNA_material_types.h" #include "DNA_group_types.h" /* local include */ #include "renderpipeline.h" #include "render_result.h" #include "render_types.h" #include "renderdatabase.h" #include "occlusion.h" #include "pixelblending.h" #include "pixelshading.h" #include "shadbuf.h" #include "shading.h" #include "sss.h" #include "zbuf.h" /* own include */ #include "rendercore.h" /* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ /* defined in pipeline.c, is hardcopy of active dynamic allocated Render */ /* only to be used here in this file, it's for speed */ extern struct Render R; /* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ /* x and y are current pixels in rect to be rendered */ /* do not normalize! */ void calc_view_vector(float view[3], float x, float y) { view[2]= -ABS(R.clipsta); if (R.r.mode & R_ORTHO) { view[0]= view[1]= 0.0f; } else { if (R.r.mode & R_PANORAMA) { x-= R.panodxp; } /* move x and y to real viewplane coords */ x = (x / (float)R.winx); view[0] = R.viewplane.xmin + x * BLI_rctf_size_x(&R.viewplane); y = (y / (float)R.winy); view[1] = R.viewplane.ymin + y * BLI_rctf_size_y(&R.viewplane); // if (R.flag & R_SEC_FIELD) { // if (R.r.mode & R_ODDFIELD) view[1]= (y+R.ystart)*R.ycor; // else view[1]= (y+R.ystart+1.0)*R.ycor; // } // else view[1]= (y+R.ystart+R.bluroffsy+0.5)*R.ycor; if (R.r.mode & R_PANORAMA) { float u= view[0] + R.panodxv; float v= view[2]; view[0]= R.panoco*u + R.panosi*v; view[2]= -R.panosi*u + R.panoco*v; } } } void calc_renderco_ortho(float co[3], float x, float y, int z) { /* x and y 3d coordinate can be derived from pixel coord and winmat */ float fx= 2.0f/(R.winx*R.winmat[0][0]); float fy= 2.0f/(R.winy*R.winmat[1][1]); float zco; co[0]= (x - 0.5f*R.winx)*fx - R.winmat[3][0]/R.winmat[0][0]; co[1]= (y - 0.5f*R.winy)*fy - R.winmat[3][1]/R.winmat[1][1]; zco= ((float)z)/2147483647.0f; co[2]= R.winmat[3][2]/( R.winmat[2][3]*zco - R.winmat[2][2] ); } void calc_renderco_zbuf(float co[3], const float view[3], int z) { float fac, zco; /* inverse of zbuf calc: zbuf = MAXZ*hoco_z/hoco_w */ zco= ((float)z)/2147483647.0f; co[2]= R.winmat[3][2]/( R.winmat[2][3]*zco - R.winmat[2][2] ); fac= co[2]/view[2]; co[0]= fac*view[0]; co[1]= fac*view[1]; } /* also used in zbuf.c and shadbuf.c */ int count_mask(unsigned short mask) { if (R.samples) return (R.samples->cmask[mask & 255]+R.samples->cmask[mask>>8]); return 0; } static int calchalo_z(HaloRen *har, int zz) { if (har->type & HA_ONLYSKY) { if (zz < 0x7FFFFFF0) zz= - 0x7FFFFF; /* edge render messes zvalues */ } else { zz= (zz>>8); } return zz; } static void halo_pixelstruct(HaloRen *har, RenderLayer **rlpp, int totsample, int od, float dist, float xn, float yn, PixStr *ps) { float col[4], accol[4], fac; int amount, amountm, zz, flarec, sample, fullsample, mask=0; fullsample= (totsample > 1); amount= 0; accol[0] = accol[1] = accol[2] = accol[3]= 0.0f; col[0] = col[1] = col[2] = col[3]= 0.0f; flarec= har->flarec; while (ps) { amountm= count_mask(ps->mask); amount+= amountm; zz= calchalo_z(har, ps->z); if ((zz> har->zs) || (har->mat && (har->mat->mode & MA_HALO_SOFT))) { if (shadeHaloFloat(har, col, zz, dist, xn, yn, flarec)) { flarec= 0; if (fullsample) { for (sample=0; samplemask & (1 << sample)) { float *pass = RE_RenderLayerGetPass(rlpp[sample], RE_PASSNAME_COMBINED, R.viewname); addalphaAddfacFloat(pass + od*4, col, har->add); } } } else { fac= ((float)amountm)/(float)R.osa; accol[0]+= fac*col[0]; accol[1]+= fac*col[1]; accol[2]+= fac*col[2]; accol[3]+= fac*col[3]; } } } mask |= ps->mask; ps= ps->next; } /* now do the sky sub-pixels */ amount= R.osa-amount; if (amount) { if (shadeHaloFloat(har, col, 0x7FFFFF, dist, xn, yn, flarec)) { if (!fullsample) { fac= ((float)amount)/(float)R.osa; accol[0]+= fac*col[0]; accol[1]+= fac*col[1]; accol[2]+= fac*col[2]; accol[3]+= fac*col[3]; } } } if (fullsample) { for (sample=0; sampleadd); } } } else { col[0]= accol[0]; col[1]= accol[1]; col[2]= accol[2]; col[3]= accol[3]; for (sample=0; sampleadd); } } } static void halo_tile(RenderPart *pa, RenderLayer *rl) { RenderLayer *rlpp[RE_MAX_OSA]; HaloRen *har; rcti disprect= pa->disprect, testrect= pa->disprect; float dist, xsq, ysq, xn, yn; float col[4]; intptr_t *rd= NULL; int a, *rz, zz, y, sample, totsample, od; short minx, maxx, miny, maxy, x; unsigned int lay= rl->lay; /* we don't render halos in the cropped area, gives errors in flare counter */ if (pa->crop) { testrect.xmin+= pa->crop; testrect.xmax-= pa->crop; testrect.ymin+= pa->crop; testrect.ymax-= pa->crop; } totsample= get_sample_layers(pa, rl, rlpp); for (a=0; alay & lay) == 0) { /* pass */ } else if (testrect.ymin > har->maxy) { /* pass */ } else if (testrect.ymax < har->miny) { /* pass */ } else { minx= floor(har->xs-har->rad); maxx= ceil(har->xs+har->rad); if (testrect.xmin > maxx) { /* pass */ } else if (testrect.xmax < minx) { /* pass */ } else { minx = max_ii(minx, testrect.xmin); maxx = min_ii(maxx, testrect.xmax); miny = max_ii(har->miny, testrect.ymin); maxy = min_ii(har->maxy, testrect.ymax); for (y=miny; yrectx + (minx - disprect.xmin); rz= pa->rectz + rectofs; od= rectofs; if (pa->rectdaps) rd= pa->rectdaps + rectofs; yn= (y-har->ys)*R.ycor; ysq= yn*yn; for (x=minx; xxs; xsq= xn*xn; dist= xsq+ysq; if (distradsq) { if (rd && *rd) { halo_pixelstruct(har, rlpp, totsample, od, dist, xn, yn, (PixStr *)*rd); } else { zz= calchalo_z(har, *rz); if ((zz> har->zs) || (har->mat && (har->mat->mode & MA_HALO_SOFT))) { if (shadeHaloFloat(har, col, zz, dist, xn, yn, har->flarec)) { for (sample=0; sampleadd); } } } } } if (rd) rd++; } } } } if (R.test_break(R.tbh) ) break; } } static void lamphalo_tile(RenderPart *pa, RenderLayer *rl) { RenderLayer *rlpp[RE_MAX_OSA]; ShadeInput shi; float *pass; float fac, col[4]; intptr_t *rd= pa->rectdaps; const int *rz= pa->rectz; int x, y, sample, totsample, fullsample, od; totsample= get_sample_layers(pa, rl, rlpp); fullsample= (totsample > 1); shade_input_initialize(&shi, pa, rl, 0); /* this zero's ShadeInput for us */ for (od=0, y=pa->disprect.ymin; ydisprect.ymax; y++) { for (x=pa->disprect.xmin; xdisprect.xmax; x++, rz++, od++) { calc_view_vector(shi.view, x, y); if (rd && *rd) { PixStr *ps= (PixStr *)*rd; int count, totsamp= 0, mask= 0; while (ps) { if (R.r.mode & R_ORTHO) calc_renderco_ortho(shi.co, (float)x, (float)y, ps->z); else calc_renderco_zbuf(shi.co, shi.view, ps->z); totsamp+= count= count_mask(ps->mask); mask |= ps->mask; col[0]= col[1]= col[2]= col[3]= 0.0f; renderspothalo(&shi, col, 1.0f); if (fullsample) { for (sample=0; samplemask & (1 << sample)) { pass = RE_RenderLayerGetPass(rlpp[sample], RE_PASSNAME_COMBINED, R.viewname); pass += od * 4; pass[0]+= col[0]; pass[1]+= col[1]; pass[2]+= col[2]; pass[3]+= col[3]; if (pass[3]>1.0f) pass[3]= 1.0f; } } } else { fac= ((float)count)/(float)R.osa; pass = RE_RenderLayerGetPass(rl, RE_PASSNAME_COMBINED, R.viewname); pass += od * 4; pass[0]+= fac*col[0]; pass[1]+= fac*col[1]; pass[2]+= fac*col[2]; pass[3]+= fac*col[3]; if (pass[3]>1.0f) pass[3]= 1.0f; } ps= ps->next; } if (totsamp1.0f) pass[3]= 1.0f; } } } else { fac= ((float)R.osa-totsamp)/(float)R.osa; pass = RE_RenderLayerGetPass(rl, RE_PASSNAME_COMBINED, R.viewname); pass += od * 4; pass[0]+= fac*col[0]; pass[1]+= fac*col[1]; pass[2]+= fac*col[2]; pass[3]+= fac*col[3]; if (pass[3]>1.0f) pass[3]= 1.0f; } } } else { if (R.r.mode & R_ORTHO) calc_renderco_ortho(shi.co, (float)x, (float)y, *rz); else calc_renderco_zbuf(shi.co, shi.view, *rz); col[0]= col[1]= col[2]= col[3]= 0.0f; renderspothalo(&shi, col, 1.0f); for (sample=0; sample1.0f) pass[3]= 1.0f; } } if (rd) rd++; } if (y&1) if (R.test_break(R.tbh)) break; } } /* ********************* MAINLOOPS ******************** */ /* osa version */ static void add_filt_passes(RenderLayer *rl, int curmask, int rectx, int offset, ShadeInput *shi, ShadeResult *shr) { RenderPass *rpass; for (rpass= rl->passes.first; rpass; rpass= rpass->next) { float *fp, *col= NULL; int pixsize= 3; if (STREQ(rpass->name, RE_PASSNAME_COMBINED)) { add_filt_fmask(curmask, shr->combined, rpass->rect + 4*offset, rectx); } else if (STREQ(rpass->name, RE_PASSNAME_Z)) { fp = rpass->rect + offset; *fp = shr->z; } else if (STREQ(rpass->name, RE_PASSNAME_RGBA)) { col = shr->col; pixsize = 4; } else if (STREQ(rpass->name, RE_PASSNAME_EMIT)) { col = shr->emit; } else if (STREQ(rpass->name, RE_PASSNAME_DIFFUSE)) { col = shr->diff; } else if (STREQ(rpass->name, RE_PASSNAME_SPEC)) { col = shr->spec; } else if (STREQ(rpass->name, RE_PASSNAME_SHADOW)) { col = shr->shad; } else if (STREQ(rpass->name, RE_PASSNAME_AO)) { col = shr->ao; } else if (STREQ(rpass->name, RE_PASSNAME_ENVIRONMENT)) { col = shr->env; } else if (STREQ(rpass->name, RE_PASSNAME_INDIRECT)) { col = shr->indirect; } else if (STREQ(rpass->name, RE_PASSNAME_REFLECT)) { col = shr->refl; } else if (STREQ(rpass->name, RE_PASSNAME_REFRACT)) { col = shr->refr; } else if (STREQ(rpass->name, RE_PASSNAME_NORMAL)) { col = shr->nor; } else if (STREQ(rpass->name, RE_PASSNAME_UV)) { /* box filter only, gauss will screwup UV too much */ if (shi->totuv) { float mult = (float)count_mask(curmask)/(float)R.osa; fp = rpass->rect + 3*offset; fp[0]+= mult*(0.5f + 0.5f*shi->uv[shi->actuv].uv[0]); fp[1]+= mult*(0.5f + 0.5f*shi->uv[shi->actuv].uv[1]); fp[2]+= mult; } } else if (STREQ(rpass->name, RE_PASSNAME_INDEXOB)) { /* no filter */ if (shi->vlr) { fp = rpass->rect + offset; if (*fp==0.0f) *fp = (float)shi->obr->ob->index; } } else if (STREQ(rpass->name, RE_PASSNAME_INDEXMA)) { /* no filter */ if (shi->vlr) { fp = rpass->rect + offset; if (*fp==0.0f) *fp = (float)shi->mat->index; } } else if (STREQ(rpass->name, RE_PASSNAME_MIST)) { /* */ col = &shr->mist; pixsize = 1; } else if (STREQ(rpass->name, RE_PASSNAME_VECTOR)) { /* add minimum speed in pixel, no filter */ fp = rpass->rect + 4*offset; if ( (ABS(shr->winspeed[0]) + ABS(shr->winspeed[1]))< (ABS(fp[0]) + ABS(fp[1])) ) { fp[0] = shr->winspeed[0]; fp[1] = shr->winspeed[1]; } if ( (ABS(shr->winspeed[2]) + ABS(shr->winspeed[3]))< (ABS(fp[2]) + ABS(fp[3])) ) { fp[2] = shr->winspeed[2]; fp[3] = shr->winspeed[3]; } } else if (STREQ(rpass->name, RE_PASSNAME_RAYHITS)) { /* */ col = shr->rayhits; pixsize= 4; } if (col) { fp= rpass->rect + pixsize*offset; add_filt_fmask_pixsize(curmask, col, fp, rectx, pixsize); } } } /* non-osa version */ static void add_passes(RenderLayer *rl, int offset, ShadeInput *shi, ShadeResult *shr) { RenderPass *rpass; float *fp; for (rpass= rl->passes.first; rpass; rpass= rpass->next) { float *col= NULL, uvcol[3]; int a, pixsize= 3; if (STREQ(rpass->name, RE_PASSNAME_COMBINED)) { /* copy combined to use for preview */ copy_v4_v4(rpass->rect + 4*offset, shr->combined); } else if (STREQ(rpass->name, RE_PASSNAME_Z)) { fp = rpass->rect + offset; *fp = shr->z; } else if (STREQ(rpass->name, RE_PASSNAME_RGBA)) { col = shr->col; pixsize = 4; } else if (STREQ(rpass->name, RE_PASSNAME_EMIT)) { col = shr->emit; } else if (STREQ(rpass->name, RE_PASSNAME_DIFFUSE)) { col = shr->diff; } else if (STREQ(rpass->name, RE_PASSNAME_SPEC)) { col = shr->spec; } else if (STREQ(rpass->name, RE_PASSNAME_SHADOW)) { col = shr->shad; } else if (STREQ(rpass->name, RE_PASSNAME_AO)) { col = shr->ao; } else if (STREQ(rpass->name, RE_PASSNAME_ENVIRONMENT)) { col = shr->env; } else if (STREQ(rpass->name, RE_PASSNAME_INDIRECT)) { col = shr->indirect; } else if (STREQ(rpass->name, RE_PASSNAME_REFLECT)) { col = shr->refl; } else if (STREQ(rpass->name, RE_PASSNAME_REFRACT)) { col = shr->refr; } else if (STREQ(rpass->name, RE_PASSNAME_NORMAL)) { col = shr->nor; } else if (STREQ(rpass->name, RE_PASSNAME_UV)) { if (shi->totuv) { uvcol[0] = 0.5f + 0.5f*shi->uv[shi->actuv].uv[0]; uvcol[1] = 0.5f + 0.5f*shi->uv[shi->actuv].uv[1]; uvcol[2] = 1.0f; col = uvcol; } } else if (STREQ(rpass->name, RE_PASSNAME_VECTOR)) { col = shr->winspeed; pixsize = 4; } else if (STREQ(rpass->name, RE_PASSNAME_INDEXOB)) { if (shi->vlr) { fp = rpass->rect + offset; *fp = (float)shi->obr->ob->index; } } else if (STREQ(rpass->name, RE_PASSNAME_INDEXMA)) { if (shi->vlr) { fp = rpass->rect + offset; *fp = (float)shi->mat->index; } } else if (STREQ(rpass->name, RE_PASSNAME_MIST)) { fp = rpass->rect + offset; *fp = shr->mist; } else if (STREQ(rpass->name, RE_PASSNAME_RAYHITS)) { col = shr->rayhits; pixsize = 4; } if (col) { fp = rpass->rect + pixsize*offset; for (a=0; afullresult.first) { int sample, nr= BLI_findindex(&pa->result->layers, rl); for (sample=0; samplefullresult, sample); rlpp[sample]= BLI_findlink(&rr->layers, nr); } return R.osa; } else { rlpp[0]= rl; return 1; } } /* only do sky, is default in the solid layer (shade_tile) btw */ static void sky_tile(RenderPart *pa, RenderLayer *rl) { RenderLayer *rlpp[RE_MAX_OSA]; int x, y, od=0, totsample; if (R.r.alphamode!=R_ADDSKY) return; totsample= get_sample_layers(pa, rl, rlpp); for (y=pa->disprect.ymin; ydisprect.ymax; y++) { for (x=pa->disprect.xmin; xdisprect.xmax; x++, od+=4) { float col[4]; int sample; bool done = false; for (sample= 0; samplethread); done = true; } if (pass[3]==0.0f) { copy_v4_v4(pass, col); pass[3] = 1.0f; } else { addAlphaUnderFloat(pass, col); pass[3] = 1.0f; } } } } if (y&1) if (R.test_break(R.tbh)) break; } } static void atm_tile(RenderPart *pa, RenderLayer *rl) { RenderPass *zpass; GroupObject *go; LampRen *lar; RenderLayer *rlpp[RE_MAX_OSA]; int totsample; int x, y, od= 0; totsample= get_sample_layers(pa, rl, rlpp); /* check that z pass is enabled */ if (pa->rectz==NULL) return; for (zpass= rl->passes.first; zpass; zpass= zpass->next) if (STREQ(zpass->name, RE_PASSNAME_Z)) break; if (zpass==NULL) return; /* check for at least one sun lamp that its atmosphere flag is enabled */ for (go=R.lights.first; go; go= go->next) { lar= go->lampren; if (lar->type==LA_SUN && lar->sunsky && (lar->sunsky->effect_type & LA_SUN_EFFECT_AP)) break; } /* do nothign and return if there is no sun lamp */ if (go==NULL) return; /* for each x,y and each sample, and each sun lamp*/ for (y=pa->disprect.ymin; ydisprect.ymax; y++) { for (x=pa->disprect.xmin; xdisprect.xmax; x++, od++) { int sample; for (sample=0; samplenext) { lar= go->lampren; if (lar->type==LA_SUN && lar->sunsky) { /* if it's sky continue and don't apply atmosphere effect on it */ if (*zrect >= 9.9e10f || rgbrect[3]==0.0f) { continue; } if ((lar->sunsky->effect_type & LA_SUN_EFFECT_AP)) { float tmp_rgb[3]; /* skip if worldspace lamp vector is below horizon */ if (go->ob->obmat[2][2] < 0.f) { continue; } copy_v3_v3(tmp_rgb, rgbrect); if (rgbrect[3]!=1.0f) { /* de-premul */ mul_v3_fl(tmp_rgb, 1.0f/rgbrect[3]); } shadeAtmPixel(lar->sunsky, tmp_rgb, x, y, *zrect); if (rgbrect[3]!=1.0f) { /* premul */ mul_v3_fl(tmp_rgb, rgbrect[3]); } if (done==0) { copy_v3_v3(rgb, tmp_rgb); done = true; } else { rgb[0] = 0.5f*rgb[0] + 0.5f*tmp_rgb[0]; rgb[1] = 0.5f*rgb[1] + 0.5f*tmp_rgb[1]; rgb[2] = 0.5f*rgb[2] + 0.5f*tmp_rgb[2]; } } } } /* if at least for one sun lamp aerial perspective was applied*/ if (done) { copy_v3_v3(rgbrect, rgb); } } } } } static void shadeDA_tile(RenderPart *pa, RenderLayer *rl) { RenderResult *rr= pa->result; ShadeSample ssamp; intptr_t *rd, *rectdaps= pa->rectdaps; int samp; int x, y, seed, crop=0, offs=0, od; if (R.test_break(R.tbh)) return; /* irregular shadowb buffer creation */ if (R.r.mode & R_SHADOW) ISB_create(pa, NULL); /* we set per pixel a fixed seed, for random AO and shadow samples */ seed= pa->rectx*pa->disprect.ymin; /* general shader info, passes */ shade_sample_initialize(&ssamp, pa, rl); /* occlusion caching */ if (R.occlusiontree) cache_occ_samples(&R, pa, &ssamp); /* filtered render, for now we assume only 1 filter size */ if (pa->crop) { crop= 1; rectdaps+= pa->rectx + 1; offs= pa->rectx + 1; } /* scanline updates have to be 2 lines behind */ rr->renrect.ymin = 0; rr->renrect.ymax = -2*crop; rr->renlay= rl; for (y=pa->disprect.ymin+crop; ydisprect.ymax-crop; y++, rr->renrect.ymax++) { rd= rectdaps; od= offs; for (x=pa->disprect.xmin+crop; xdisprect.xmax-crop; x++, rd++, od++) { BLI_thread_srandom(pa->thread, seed++); if (*rd) { if (shade_samples(&ssamp, (PixStr *)(*rd), x, y)) { /* multisample buffers or filtered mask filling? */ if (pa->fullresult.first) { int a; for (samp=0; samprectx, od, &ssamp.shi[samp], &ssamp.shr[samp]); } } } } rectdaps+= pa->rectx; offs+= pa->rectx; if (y&1) if (R.test_break(R.tbh)) break; } /* disable scanline updating */ rr->renlay= NULL; if (R.r.mode & R_SHADOW) ISB_free(pa); if (R.occlusiontree) free_occ_samples(&R, pa); } /* ************* pixel struct ******** */ static PixStrMain *addpsmain(ListBase *lb) { PixStrMain *psm; psm= (PixStrMain *)MEM_mallocN(sizeof(PixStrMain), "pixstrMain"); BLI_addtail(lb, psm); psm->ps= (PixStr *)MEM_mallocN(4096*sizeof(PixStr), "pixstr"); psm->counter= 0; return psm; } static void freeps(ListBase *lb) { PixStrMain *psm, *psmnext; for (psm= lb->first; psm; psm= psmnext) { psmnext= psm->next; if (psm->ps) MEM_freeN(psm->ps); MEM_freeN(psm); } BLI_listbase_clear(lb); } static void addps(ListBase *lb, intptr_t *rd, int obi, int facenr, int z, int maskz, unsigned short mask) { PixStrMain *psm; PixStr *ps, *last= NULL; if (*rd) { ps= (PixStr *)(*rd); while (ps) { if ( ps->obi == obi && ps->facenr == facenr ) { ps->mask |= mask; return; } last= ps; ps= ps->next; } } /* make new PS (pixel struct) */ psm= lb->last; if (psm->counter==4095) psm= addpsmain(lb); ps= psm->ps + psm->counter++; if (last) last->next= ps; else *rd= (intptr_t)ps; ps->next= NULL; ps->obi= obi; ps->facenr= facenr; ps->z= z; ps->maskz= maskz; ps->mask = mask; ps->shadfac= 0; } static void edge_enhance_add(RenderPart *pa, float *rectf, float *arect) { float addcol[4]; int pix; if (arect==NULL) return; for (pix= pa->rectx*pa->recty; pix>0; pix--, arect++, rectf+=4) { if (*arect != 0.0f) { addcol[0]= *arect * R.r.edgeR; addcol[1]= *arect * R.r.edgeG; addcol[2]= *arect * R.r.edgeB; addcol[3]= *arect; addAlphaOverFloat(rectf, addcol); } } } /* clamp alpha and RGB to 0..1 and 0..inf, can go outside due to filter */ static void clamp_alpha_rgb_range(RenderPart *pa, RenderLayer *rl) { RenderLayer *rlpp[RE_MAX_OSA]; int y, sample, totsample; totsample= get_sample_layers(pa, rl, rlpp); /* not for full sample, there we clamp after compositing */ if (totsample > 1) return; for (sample= 0; samplerectx*pa->recty; y>0; y--, rectf+=4) { rectf[0] = MAX2(rectf[0], 0.0f); rectf[1] = MAX2(rectf[1], 0.0f); rectf[2] = MAX2(rectf[2], 0.0f); CLAMP(rectf[3], 0.0f, 1.0f); } } } /* adds only alpha values */ static void edge_enhance_tile(RenderPart *pa, float *rectf, int *rectz) { /* use zbuffer to define edges, add it to the image */ int y, x, col, *rz, *rz1, *rz2, *rz3; int zval1, zval2, zval3; float *rf; /* shift values in zbuffer 4 to the right (anti overflows), for filter we need multiplying with 12 max */ rz= rectz; if (rz==NULL) return; for (y=0; yrecty; y++) for (x=0; xrectx; x++, rz++) (*rz)>>= 4; rz1= rectz; rz2= rz1+pa->rectx; rz3= rz2+pa->rectx; rf= rectf+pa->rectx+1; for (y=0; yrecty-2; y++) { for (x=0; xrectx-2; x++, rz1++, rz2++, rz3++, rf++) { /* prevent overflow with sky z values */ zval1= rz1[0] + 2*rz1[1] + rz1[2]; zval2= 2*rz2[0] + 2*rz2[2]; zval3= rz3[0] + 2*rz3[1] + rz3[2]; col= ( 4*rz2[1] - (zval1 + zval2 + zval3)/3 ); if (col<0) col= -col; col >>= 5; if (col > (1<<16)) col= (1<<16); else col= (R.r.edgeint*col)>>8; if (col>0) { float fcol; if (col>255) fcol= 1.0f; else fcol= (float)col/255.0f; if (R.osa) *rf+= fcol/(float)R.osa; else *rf= fcol; } } rz1+= 2; rz2+= 2; rz3+= 2; rf+= 2; } /* shift back zbuf values, we might need it still */ rz= rectz; for (y=0; yrecty; y++) for (x=0; xrectx; x++, rz++) (*rz)<<= 4; } static void reset_sky_speed(RenderPart *pa, RenderLayer *rl) { /* for all pixels with max speed, set to zero */ RenderLayer *rlpp[RE_MAX_OSA]; float *fp; int a, sample, totsample; totsample= get_sample_layers(pa, rl, rlpp); for (sample= 0; samplerectx*pa->recty - 1; a>=0; a--) if (fp[a] == PASS_VECTOR_MAX) fp[a]= 0.0f; } } static unsigned short *make_solid_mask(RenderPart *pa) { intptr_t *rd= pa->rectdaps; unsigned short *solidmask, *sp; int x; if (rd==NULL) return NULL; sp=solidmask= MEM_mallocN(sizeof(short)*pa->rectx*pa->recty, "solidmask"); for (x=pa->rectx*pa->recty; x>0; x--, rd++, sp++) { if (*rd) { PixStr *ps= (PixStr *)*rd; *sp= ps->mask; for (ps= ps->next; ps; ps= ps->next) *sp |= ps->mask; } else *sp= 0; } return solidmask; } static void addAlphaOverFloatMask(float *dest, float *source, unsigned short dmask, unsigned short smask) { unsigned short shared= dmask & smask; float mul= 1.0f - source[3]; if (shared) { /* overlapping masks */ /* masks differ, we make a mixture of 'add' and 'over' */ if (shared!=dmask) { float shared_bits= (float)count_mask(shared); /* alpha over */ float tot_bits= (float)count_mask(smask|dmask); /* alpha add */ float add= (tot_bits - shared_bits)/tot_bits; /* add level */ mul= add + (1.0f-add)*mul; } } else if (dmask && smask) { /* works for premul only, of course */ dest[0]+= source[0]; dest[1]+= source[1]; dest[2]+= source[2]; dest[3]+= source[3]; return; } dest[0]= (mul*dest[0]) + source[0]; dest[1]= (mul*dest[1]) + source[1]; dest[2]= (mul*dest[2]) + source[2]; dest[3]= (mul*dest[3]) + source[3]; } typedef struct ZbufSolidData { RenderLayer *rl; ListBase *psmlist; float *edgerect; } ZbufSolidData; static void make_pixelstructs(RenderPart *pa, ZSpan *zspan, int sample, void *data) { ZbufSolidData *sdata = (ZbufSolidData *)data; ListBase *lb= sdata->psmlist; intptr_t *rd= pa->rectdaps; const int *ro= zspan->recto; const int *rp= zspan->rectp; const int *rz= zspan->rectz; const int *rm= zspan->rectmask; int x, y; int mask= 1<recty; y++) { for (x=0; xrectx; x++, rd++, rp++, ro++, rz++, rm++) { if (*rp) { addps(lb, rd, *ro, *rp, *rz, (zspan->rectmask)? *rm: 0, mask); } } } if (sdata->rl->layflag & SCE_LAY_EDGE) if (R.r.mode & R_EDGE) edge_enhance_tile(pa, sdata->edgerect, zspan->rectz); } /* main call for shading Delta Accum, for OSA */ /* supposed to be fully threadable! */ void zbufshadeDA_tile(RenderPart *pa) { RenderResult *rr= pa->result; RenderLayer *rl; ListBase psmlist= {NULL, NULL}; float *edgerect= NULL; /* allocate the necessary buffers */ /* zbuffer inits these rects */ pa->recto= MEM_mallocN(sizeof(int)*pa->rectx*pa->recty, "recto"); pa->rectp= MEM_mallocN(sizeof(int)*pa->rectx*pa->recty, "rectp"); pa->rectz= MEM_mallocN(sizeof(int)*pa->rectx*pa->recty, "rectz"); for (rl= rr->layers.first; rl; rl= rl->next) { float *rect = RE_RenderLayerGetPass(rl, RE_PASSNAME_COMBINED, R.viewname); if ((rl->layflag & SCE_LAY_ZMASK) && (rl->layflag & SCE_LAY_NEG_ZMASK)) pa->rectmask= MEM_mallocN(sizeof(int)*pa->rectx*pa->recty, "rectmask"); /* initialize pixelstructs and edge buffer */ addpsmain(&psmlist); pa->rectdaps= MEM_callocN(sizeof(intptr_t)*pa->rectx*pa->recty+4, "zbufDArectd"); if (rl->layflag & SCE_LAY_EDGE) if (R.r.mode & R_EDGE) edgerect= MEM_callocN(sizeof(float)*pa->rectx*pa->recty, "rectedge"); /* always fill visibility */ for (pa->sample=0; pa->samplesample+=4) { ZbufSolidData sdata; sdata.rl= rl; sdata.psmlist= &psmlist; sdata.edgerect= edgerect; zbuffer_solid(pa, rl, make_pixelstructs, &sdata); if (R.test_break(R.tbh)) break; } /* shades solid */ if (rl->layflag & SCE_LAY_SOLID) shadeDA_tile(pa, rl); /* lamphalo after solid, before ztra, looks nicest because ztra does own halo */ if (R.flag & R_LAMPHALO) if (rl->layflag & SCE_LAY_HALO) lamphalo_tile(pa, rl); /* halo before ztra, because ztra fills in zbuffer now */ if (R.flag & R_HALO) if (rl->layflag & SCE_LAY_HALO) halo_tile(pa, rl); /* transp layer */ if (R.flag & R_ZTRA || R.totstrand) { if (rl->layflag & (SCE_LAY_ZTRA|SCE_LAY_STRAND)) { if (pa->fullresult.first) { zbuffer_transp_shade(pa, rl, rect, &psmlist); } else { unsigned short *ztramask, *solidmask= NULL; /* 16 bits, MAX_OSA */ /* allocate, but not free here, for asynchronous display of this rect in main thread */ rl->acolrect= MEM_callocN(4*sizeof(float)*pa->rectx*pa->recty, "alpha layer"); /* swap for live updates, and it is used in zbuf.c!!! */ SWAP(float *, rl->acolrect, rect); ztramask = zbuffer_transp_shade(pa, rl, rect, &psmlist); SWAP(float *, rl->acolrect, rect); /* zbuffer transp only returns ztramask if there's solid rendered */ if (ztramask) solidmask= make_solid_mask(pa); if (ztramask && solidmask) { unsigned short *sps= solidmask, *spz= ztramask; unsigned short fullmask= (1<acolrect; int x; for (x=pa->rectx*pa->recty; x>0; x--, acol+=4, fcol+=4, sps++, spz++) { if (*sps == fullmask) addAlphaOverFloat(fcol, acol); else addAlphaOverFloatMask(fcol, acol, *sps, *spz); } } else { float *fcol= rect; float *acol= rl->acolrect; int x; for (x=pa->rectx*pa->recty; x>0; x--, acol+=4, fcol+=4) { addAlphaOverFloat(fcol, acol); } } if (solidmask) MEM_freeN(solidmask); if (ztramask) MEM_freeN(ztramask); } } } /* sun/sky */ if (rl->layflag & SCE_LAY_SKY) atm_tile(pa, rl); /* sky before edge */ if (rl->layflag & SCE_LAY_SKY) sky_tile(pa, rl); /* extra layers */ if (rl->layflag & SCE_LAY_EDGE) if (R.r.mode & R_EDGE) edge_enhance_add(pa, rect, edgerect); if (rl->passflag & SCE_PASS_VECTOR) reset_sky_speed(pa, rl); /* clamp alpha to 0..1 range, can go outside due to filter */ clamp_alpha_rgb_range(pa, rl); /* free stuff within loop! */ MEM_freeN(pa->rectdaps); pa->rectdaps= NULL; freeps(&psmlist); if (edgerect) MEM_freeN(edgerect); edgerect= NULL; if (pa->rectmask) { MEM_freeN(pa->rectmask); pa->rectmask= NULL; } } /* free all */ MEM_freeN(pa->recto); pa->recto= NULL; MEM_freeN(pa->rectp); pa->rectp= NULL; MEM_freeN(pa->rectz); pa->rectz= NULL; /* display active layer */ rr->renrect.ymin=rr->renrect.ymax = 0; rr->renlay= render_get_active_layer(&R, rr); } /* ------------------------------------------------------------------------ */ /* non OSA case, full tile render */ /* supposed to be fully threadable! */ void zbufshade_tile(RenderPart *pa) { ShadeSample ssamp; RenderResult *rr= pa->result; RenderLayer *rl; PixStr ps; float *edgerect= NULL; /* fake pixel struct, to comply to osa render */ ps.next= NULL; ps.mask= 0xFFFF; /* zbuffer code clears/inits rects */ pa->recto= MEM_mallocN(sizeof(int)*pa->rectx*pa->recty, "recto"); pa->rectp= MEM_mallocN(sizeof(int)*pa->rectx*pa->recty, "rectp"); pa->rectz= MEM_mallocN(sizeof(int)*pa->rectx*pa->recty, "rectz"); for (rl= rr->layers.first; rl; rl= rl->next) { float *rect= RE_RenderLayerGetPass(rl, RE_PASSNAME_COMBINED, R.viewname); if ((rl->layflag & SCE_LAY_ZMASK) && (rl->layflag & SCE_LAY_NEG_ZMASK)) pa->rectmask= MEM_mallocN(sizeof(int)*pa->rectx*pa->recty, "rectmask"); /* general shader info, passes */ shade_sample_initialize(&ssamp, pa, rl); zbuffer_solid(pa, rl, NULL, NULL); if (!R.test_break(R.tbh)) { /* NOTE: this if () is not consistent */ /* edges only for solid part, ztransp doesn't support it yet anti-aliased */ if (rl->layflag & SCE_LAY_EDGE) { if (R.r.mode & R_EDGE) { edgerect= MEM_callocN(sizeof(float)*pa->rectx*pa->recty, "rectedge"); edge_enhance_tile(pa, edgerect, pa->rectz); } } /* initialize scanline updates for main thread */ rr->renrect.ymin = 0; rr->renlay= rl; if (rl->layflag & SCE_LAY_SOLID) { const float *fcol = rect; const int *ro= pa->recto, *rp= pa->rectp, *rz= pa->rectz; int x, y, offs=0, seed; /* we set per pixel a fixed seed, for random AO and shadow samples */ seed= pa->rectx*pa->disprect.ymin; /* irregular shadowb buffer creation */ if (R.r.mode & R_SHADOW) ISB_create(pa, NULL); if (R.occlusiontree) cache_occ_samples(&R, pa, &ssamp); for (y=pa->disprect.ymin; ydisprect.ymax; y++, rr->renrect.ymax++) { for (x=pa->disprect.xmin; xdisprect.xmax; x++, ro++, rz++, rp++, fcol+=4, offs++) { /* per pixel fixed seed */ BLI_thread_srandom(pa->thread, seed++); if (*rp) { ps.obi= *ro; ps.facenr= *rp; ps.z= *rz; if (shade_samples(&ssamp, &ps, x, y)) { /* combined and passes */ add_passes(rl, offs, ssamp.shi, ssamp.shr); } } } if (y&1) if (R.test_break(R.tbh)) break; } if (R.occlusiontree) free_occ_samples(&R, pa); if (R.r.mode & R_SHADOW) ISB_free(pa); } /* disable scanline updating */ rr->renlay= NULL; } /* lamphalo after solid, before ztra, looks nicest because ztra does own halo */ if (R.flag & R_LAMPHALO) if (rl->layflag & SCE_LAY_HALO) lamphalo_tile(pa, rl); /* halo before ztra, because ztra fills in zbuffer now */ if (R.flag & R_HALO) if (rl->layflag & SCE_LAY_HALO) halo_tile(pa, rl); if (R.flag & R_ZTRA || R.totstrand) { if (rl->layflag & (SCE_LAY_ZTRA|SCE_LAY_STRAND)) { float *fcol, *acol; int x; /* allocate, but not free here, for asynchronous display of this rect in main thread */ rl->acolrect= MEM_callocN(4*sizeof(float)*pa->rectx*pa->recty, "alpha layer"); /* swap for live updates */ SWAP(float *, rl->acolrect, rect); zbuffer_transp_shade(pa, rl, rect, NULL); SWAP(float *, rl->acolrect, rect); fcol= rect; acol= rl->acolrect; for (x=pa->rectx*pa->recty; x>0; x--, acol+=4, fcol+=4) { addAlphaOverFloat(fcol, acol); } } } /* sun/sky */ if (rl->layflag & SCE_LAY_SKY) atm_tile(pa, rl); /* sky before edge */ if (rl->layflag & SCE_LAY_SKY) sky_tile(pa, rl); if (!R.test_break(R.tbh)) { if (rl->layflag & SCE_LAY_EDGE) if (R.r.mode & R_EDGE) edge_enhance_add(pa, rect, edgerect); } if (rl->passflag & SCE_PASS_VECTOR) reset_sky_speed(pa, rl); if (edgerect) MEM_freeN(edgerect); edgerect= NULL; if (pa->rectmask) { MEM_freeN(pa->rectmask); pa->rectmask= NULL; } } /* display active layer */ rr->renrect.ymin=rr->renrect.ymax = 0; rr->renlay= render_get_active_layer(&R, rr); MEM_freeN(pa->recto); pa->recto= NULL; MEM_freeN(pa->rectp); pa->rectp= NULL; MEM_freeN(pa->rectz); pa->rectz= 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 obi, int facenr, int x, int y, int z) { ZBufSSSHandle *handle = cb_handle; RenderPart *pa= handle->pa; /* extra border for filter gives double samples on part edges, * don't use those */ if (xcrop || x>=pa->rectx-pa->crop) return; if (ycrop || y>=pa->recty-pa->crop) return; if (pa->rectall) { intptr_t *rs= pa->rectall + pa->rectx*y + x; addps(&handle->psmlist, rs, obi, facenr, z, 0, 0); handle->totps++; } if (pa->rectz) { int *rz= pa->rectz + pa->rectx*y + x; int *rp= pa->rectp + pa->rectx*y + x; int *ro= pa->recto + pa->rectx*y + x; if (z < *rz) { if (*rp == 0) handle->totps++; *rz= z; *rp= facenr; *ro= obi; } } if (pa->rectbackz) { int *rz= pa->rectbackz + pa->rectx*y + x; int *rp= pa->rectbackp + pa->rectx*y + x; int *ro= pa->rectbacko + pa->rectx*y + x; if (z >= *rz) { if (*rp == 0) handle->totps++; *rz= z; *rp= facenr; *ro= obi; } } } static void shade_sample_sss(ShadeSample *ssamp, Material *mat, ObjectInstanceRen *obi, VlakRen *vlr, int quad, float x, float y, float z, float *co, float color[3], float *area) { ShadeInput *shi= ssamp->shi; ShadeResult shr; float /* texfac,*/ /* UNUSED */ orthoarea, nor[3], alpha, sx, sy; /* cache for shadow */ shi->samplenr= R.shadowsamplenr[shi->thread]++; if (quad) shade_input_set_triangle_i(shi, obi, vlr, 0, 2, 3); else shade_input_set_triangle_i(shi, obi, vlr, 0, 1, 2); /* center pixel */ sx = x + 0.5f; sy = y + 0.5f; /* 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; copy_v3_v3(nor, shi->facenor); calc_view_vector(shi->facenor, sx, sy); normalize_v3(shi->facenor); shade_input_set_viewco(shi, x, y, sx, sy, z); orthoarea= len_v3(shi->dxco)*len_v3(shi->dyco); copy_v3_v3(shi->facenor, nor); shade_input_set_viewco(shi, x, y, sx, sy, z); *area = min_ff(len_v3(shi->dxco) * len_v3(shi->dyco), 2.0f * orthoarea); shade_input_set_uv(shi); shade_input_set_normals(shi); /* we don't want flipped normals, they screw up back scattering */ if (shi->flippednor) shade_input_flip_normals(shi); /* not a pretty solution, but fixes common cases */ if (shi->obr->ob && shi->obr->ob->transflag & OB_NEG_SCALE) { negate_v3(shi->vn); negate_v3(shi->vno); negate_v3(shi->nmapnorm); } /* 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 */ shade_input_init_material(shi); /* render */ shade_input_set_shade_texco(shi); shade_samples_do_AO(ssamp); shade_material_loop(shi, &shr); copy_v3_v3(co, shi->co); copy_v3_v3(color, shr.combined); /* texture blending */ /* texfac= shi->mat->sss_texfac; */ /* UNUSED */ alpha= shr.combined[3]; *area *= alpha; } static void zbufshade_sss_free(RenderPart *pa) { #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->recto); pa->recto= NULL; MEM_freeN(pa->rectbackz); pa->rectbackz= NULL; MEM_freeN(pa->rectbackp); pa->rectbackp= NULL; MEM_freeN(pa->rectbacko); pa->rectbacko= NULL; #endif } void zbufshade_sss_tile(RenderPart *pa) { Render *re= &R; ShadeSample ssamp; ZBufSSSHandle handle; RenderResult *rr= pa->result; RenderLayer *rl; VlakRen *vlr; Material *mat= re->sss_mat; float (*co)[3], (*color)[3], *area, *fcol; int x, y, seed, quad, totpoint; const bool display = (re->r.scemode & (R_BUTS_PREVIEW | R_VIEWPORT_PREVIEW)) == 0; int *ro, *rz, *rp, *rbo, *rbz, *rbp, lay; #if 0 PixStr *ps; intptr_t *rs; int z; #endif /* 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(intptr_t)*pa->rectx*pa->recty+4, "rectall"); #else pa->recto= MEM_mallocN(sizeof(int)*pa->rectx*pa->recty, "recto"); pa->rectp= MEM_mallocN(sizeof(int)*pa->rectx*pa->recty, "rectp"); pa->rectz= MEM_mallocN(sizeof(int)*pa->rectx*pa->recty, "rectz"); pa->rectbacko= MEM_mallocN(sizeof(int)*pa->rectx*pa->recty, "rectbacko"); pa->rectbackp= MEM_mallocN(sizeof(int)*pa->rectx*pa->recty, "rectbackp"); pa->rectbackz= MEM_mallocN(sizeof(int)*pa->rectx*pa->recty, "rectbackz"); #endif /* setup shade sample with correct passes */ memset(&ssamp, 0, sizeof(ssamp)); shade_sample_initialize(&ssamp, pa, rr->layers.first); ssamp.tot= 1; for (rl=rr->layers.first; rl; rl=rl->next) { ssamp.shi[0].lay |= rl->lay; ssamp.shi[0].layflag |= rl->layflag; ssamp.shi[0].passflag |= rl->passflag; ssamp.shi[0].combinedflag |= ~rl->pass_xor; } rl= rr->layers.first; ssamp.shi[0].passflag |= SCE_PASS_RGBA|SCE_PASS_COMBINED; ssamp.shi[0].combinedflag &= ~(SCE_PASS_SPEC); ssamp.shi[0].mat_override= NULL; ssamp.shi[0].light_override= NULL; lay= ssamp.shi[0].lay; /* create the pixelstrs to be used later */ zbuffer_sss(pa, lay, &handle, addps_sss); if (handle.totps==0) { zbufshade_sss_free(pa); return; } fcol= RE_RenderLayerGetPass(rl, RE_PASSNAME_COMBINED, R.viewname); 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 (re->r.mode & R_SHADOW) ISB_create(pa, NULL); #endif if (display) { /* 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; ro= pa->recto; rbz= pa->rectbackz; rbp= pa->rectbackp; rbo= pa->rectbacko; #endif totpoint= 0; for (y=pa->disprect.ymin; ydisprect.ymax; y++, rr->renrect.ymax++) { for (x=pa->disprect.xmin; xdisprect.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) { ObjectInstanceRen *obi= &re->objectinstance[ps->obi]; ObjectRen *obr= obi->obr; vlr= RE_findOrAddVlak(obr, (ps->facenr-1) & RE_QUAD_MASK); quad= (ps->facenr & RE_QUAD_OFFS); z= ps->z; shade_sample_sss(&ssamp, mat, obi, vlr, quad, x, y, z, co[totpoint], color[totpoint], &area[totpoint]); totpoint++; add_v3_v3(fcol, color); fcol[3]= 1.0f; } rs++; } #else if (rp) { if (*rp != 0) { ObjectInstanceRen *obi= &re->objectinstance[*ro]; ObjectRen *obr= obi->obr; /* shade front */ vlr= RE_findOrAddVlak(obr, (*rp-1) & RE_QUAD_MASK); quad= ((*rp) & RE_QUAD_OFFS); shade_sample_sss(&ssamp, mat, obi, vlr, quad, x, y, *rz, co[totpoint], color[totpoint], &area[totpoint]); add_v3_v3(fcol, color[totpoint]); fcol[3]= 1.0f; totpoint++; } rp++; rz++; ro++; } if (rbp) { if (*rbp != 0 && !(*rbp == *(rp-1) && *rbo == *(ro-1))) { ObjectInstanceRen *obi= &re->objectinstance[*rbo]; ObjectRen *obr= obi->obr; /* shade back */ vlr= RE_findOrAddVlak(obr, (*rbp-1) & RE_QUAD_MASK); quad= ((*rbp) & RE_QUAD_OFFS); shade_sample_sss(&ssamp, mat, obi, vlr, quad, x, y, *rbz, co[totpoint], color[totpoint], &area[totpoint]); /* to indicate this is a back sample */ area[totpoint]= -area[totpoint]; add_v3_v3(fcol, color[totpoint]); fcol[3]= 1.0f; totpoint++; } rbz++; rbp++; rbo++; } #endif } if (y&1) if (re->test_break(re->tbh)) break; } /* note: after adding we do not free these arrays, sss keeps them */ if (totpoint > 0) { sss_add_points(re, co, color, area, totpoint); } else { MEM_freeN(co); MEM_freeN(color); MEM_freeN(area); } #if 0 if (re->r.mode & R_SHADOW) ISB_free(pa); #endif if (display) { /* display active layer */ rr->renrect.ymin=rr->renrect.ymax = 0; rr->renlay= render_get_active_layer(&R, rr); } zbufshade_sss_free(pa); } /* ------------------------------------------------------------------------ */ static void renderhalo_post(RenderResult *rr, float *rectf, HaloRen *har) /* postprocess version */ { float dist, xsq, ysq, xn, yn, colf[4], *rectft, *rtf; float haloxs, haloys; int minx, maxx, miny, maxy, x, y; /* calculate the disprect mapped coordinate for halo. note: rectx is disprect corrected */ haloxs= har->xs - R.disprect.xmin; haloys= har->ys - R.disprect.ymin; har->miny= miny= haloys - har->rad/R.ycor; har->maxy= maxy= haloys + har->rad/R.ycor; if (maxy < 0) { /* pass */ } else if (rr->recty < miny) { /* pass */ } else { minx = floor(haloxs - har->rad); maxx = ceil(haloxs + har->rad); if (maxx < 0) { /* pass */ } else if (rr->rectx < minx) { /* pass */ } else { if (minx<0) minx= 0; if (maxx>=rr->rectx) maxx= rr->rectx-1; if (miny<0) miny= 0; if (maxy>rr->recty) maxy= rr->recty; rectft= rectf+ 4*rr->rectx*miny; for (y=miny; yradsq) { if (shadeHaloFloat(har, colf, 0x7FFFFF, dist, xn, yn, har->flarec)) addalphaAddfacFloat(rtf, colf, har->add); } rtf+=4; } rectft+= 4*rr->rectx; if (R.test_break(R.tbh)) break; } } } } /* ------------------------------------------------------------------------ */ static void renderflare(RenderResult *rr, float *rectf, HaloRen *har) { extern const float hashvectf[]; HaloRen fla; Material *ma; const float *rc; float rad, alfa, visifac, vec[3]; int b, type; fla= *har; fla.linec= fla.ringc= fla.flarec= 0; rad= har->rad; alfa= har->alfa; visifac= R.ycor*(har->pixels); /* all radials added / r^3 == 1.0f! */ visifac /= (har->rad*har->rad*har->rad); visifac*= visifac; ma= har->mat; /* first halo: just do */ har->rad= rad*ma->flaresize*visifac; har->radsq= har->rad*har->rad; har->zs= fla.zs= 0; har->alfa= alfa*visifac; renderhalo_post(rr, rectf, har); /* next halo's: the flares */ rc= hashvectf + ma->seed2; for (b=1; bflarec; b++) { fla.r = fabsf(rc[0]); fla.g = fabsf(rc[1]); fla.b = fabsf(rc[2]); fla.alfa= ma->flareboost*fabsf(alfa*visifac*rc[3]); fla.hard= 20.0f + fabsf(70.0f*rc[7]); fla.tex= 0; type= (int)(fabsf(3.9f*rc[6])); fla.rad = ma->subsize * sqrtf(fabsf(2.0f * har->rad * rc[4])); if (type==3) { fla.rad*= 3.0f; fla.rad+= R.rectx/10; } fla.radsq= fla.rad*fla.rad; vec[0]= 1.4f*rc[5]*(har->xs-R.winx/2); vec[1]= 1.4f*rc[5]*(har->ys-R.winy/2); vec[2]= 32.0f*sqrtf(vec[0]*vec[0] + vec[1]*vec[1] + 1.0f); fla.xs= R.winx/2 + vec[0] + (1.2f+rc[8])*R.rectx*vec[0]/vec[2]; fla.ys= R.winy/2 + vec[1] + (1.2f+rc[8])*R.rectx*vec[1]/vec[2]; if (R.flag & R_SEC_FIELD) { if (R.r.mode & R_ODDFIELD) fla.ys += 0.5f; else fla.ys -= 0.5f; } if (type & 1) fla.type= HA_FLARECIRC; else fla.type= 0; renderhalo_post(rr, rectf, &fla); fla.alfa*= 0.5f; if (type & 2) fla.type= HA_FLARECIRC; else fla.type= 0; renderhalo_post(rr, rectf, &fla); rc+= 7; } } /* needs recode... integrate this better! */ void add_halo_flare(Render *re) { RenderResult *rr= re->result; RenderLayer *rl; HaloRen *har; int a, mode; float *rect; /* for now, we get the first renderlayer in list with halos set */ for (rl= rr->layers.first; rl; rl= rl->next) { bool do_draw = false; if ((rl->layflag & SCE_LAY_HALO) == 0) continue; rect = RE_RenderLayerGetPass(rl, RE_PASSNAME_COMBINED, re->viewname); if (rect==NULL) continue; mode= R.r.mode; R.r.mode &= ~R_PANORAMA; project_renderdata(&R, projectverto, 0, 0, 0); for (a=0; aflarec && (har->lay & rl->lay)) { do_draw = true; renderflare(rr, rect, har); } } if (do_draw) { /* weak... the display callback wants an active renderlayer pointer... */ rr->renlay= rl; re->display_update(re->duh, rr, NULL); } R.r.mode= mode; } } void render_internal_update_passes(RenderEngine *engine, Scene *scene, SceneRenderLayer *srl) { int type; RE_engine_register_pass(engine, scene, srl, RE_PASSNAME_COMBINED, 4, "RGBA", SOCK_RGBA); #define CHECK_PASS(name, channels, chanid) \ if (srl->passflag & (SCE_PASS_ ## name)) { \ if (channels == 4) type = SOCK_RGBA; \ else if (channels == 3) type = SOCK_VECTOR; \ else type = SOCK_FLOAT; \ RE_engine_register_pass(engine, scene, srl, RE_PASSNAME_ ## name, channels, chanid, type); \ } CHECK_PASS(Z, 1, "Z"); CHECK_PASS(VECTOR, 4, "XYZW"); CHECK_PASS(NORMAL, 3, "XYZ"); CHECK_PASS(UV, 3, "UVA"); CHECK_PASS(RGBA, 4, "RGBA"); CHECK_PASS(EMIT, 3, "RGB"); CHECK_PASS(DIFFUSE, 3, "RGB"); CHECK_PASS(SPEC, 3, "RGB"); CHECK_PASS(AO, 3, "RGB"); CHECK_PASS(ENVIRONMENT, 3, "RGB"); CHECK_PASS(INDIRECT, 3, "RGB"); CHECK_PASS(SHADOW, 3, "RGB"); CHECK_PASS(REFLECT, 3, "RGB"); CHECK_PASS(REFRACT, 3, "RGB"); CHECK_PASS(INDEXOB, 1, "X"); CHECK_PASS(INDEXMA, 1, "X"); CHECK_PASS(MIST, 1, "Z"); #undef CHECK_PASS }