Index: source/blender/render/intern/source/occlusion.c =================================================================== --- source/blender/render/intern/source/occlusion.c (revision 13667) +++ source/blender/render/intern/source/occlusion.c (working copy) @@ -1348,11 +1348,18 @@ skycol[1]= (1.0f-fac)*re->wrld.horg + fac*re->wrld.zeng; skycol[2]= (1.0f-fac)*re->wrld.horb + fac*re->wrld.zenb; } - else { /* WO_AOSKYTEX */ + else if (aocolor == WO_AOSKYTEX) { dxyview[0]= 1.0f; dxyview[1]= 1.0f; dxyview[2]= 0.0f; shadeSkyView(skycol, co, bn, dxyview); + } else { /* WO_AODIFFUSESH */ + float bentnor[3]; + + VECCOPY(bentnor, bn); + Mat4Mul3Vecfl(re->viewinv, bentnor); + + IBL_diffusesh_getcolor(skycol, (float *)re->ibl_sh_coeffs, bentnor); } VecMulf(skycol, occlusion); Index: source/blender/render/intern/source/rayshade.c =================================================================== --- source/blender/render/intern/source/rayshade.c (revision 13667) +++ source/blender/render/intern/source/rayshade.c (working copy) @@ -814,15 +814,56 @@ } /* disc of radius 'radius', centred on 0,0 */ -static void QMC_sampleDisc(float *vec, QMCSampler *qsa, int thread, int num, float radius) +static void QMC_sampleDiscConcentric(float *vec, QMCSampler *qsa, int thread, int num, float radius) { double s[2]; + double phi, r; + double a, b; + + QMC_getSample(s, qsa, thread, num); + + a = 2*s[0] - 1; /* (a,b) is now on [-1,1]^2 */ + b = 2*s[1] - 1; + + if (a > -b) { /* region 1 or 2 */ + if (a > b) { /* region 1, also |a| > |b| */ + r = a; + phi = (M_PI/4 ) * (b/a); + } + else { /* region 2, also |b| > |a| */ + r = b; + phi = (M_PI/4) * (2 - (a/b)); + } + } + else { /* region 3 or 4 */ + if (a < b) { /* region 3, also |a| >= |b|, a != 0 */ + r = -a; + phi = (M_PI/4) * (4 + (b/a)); + } + else { /* region 4, |b| >= |a|, but a==0 and b==0 could occur. */ + r = -b; + if (b != 0) + phi = (M_PI/4) * (6 - (a/b)); + else + phi = 0; + } + } + + vec[0] = r * cos(phi)* radius/2.0; + vec[1] = r * sin(phi)* radius/2.0; + 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 blur, float radius) +{ + double s[2]; float phi, sqr; QMC_getSample(s, qsa, thread, num); phi = s[0]*2*M_PI; - sqr = sqrt(s[1]); + sqr = pow(s[1], blur*5 - 0.5); vec[0] = cos(phi)*sqr* radius/2.0; vec[1] = sin(phi)*sqr* radius/2.0; @@ -845,7 +886,6 @@ vec[2] = 1.f - s[1]*s[1]; } -#if 0 /* currently not used */ /* cosine weighted hemisphere sampling */ static void QMC_sampleHemiCosine(float *vec, QMCSampler *qsa, int thread, int num) { @@ -862,7 +902,6 @@ vec[2] = 1.f - s[1]*s[1]; } -#endif /* called from convertBlenderScene.c */ /* samples don't change per pixel, so build the samples in advance for efficiency */ @@ -1499,6 +1538,9 @@ float dxyview[3], skyadded=0, div; int aocolor; + float bentnor[3]; + int noradded=0; + isec.faceorig= (RayFace*)shi->vlr; isec.oborig= RAY_OBJECT_SET(&R, shi->obi); isec.face_last= NULL; @@ -1508,6 +1550,7 @@ VECCOPY(isec.start, shi->co); shadfac[0]= shadfac[1]= shadfac[2]= 0.0f; + bentnor[0]= bentnor[1]= bentnor[2]= 0.0f; /* prevent sky colors to be added for only shadow (shadow becomes alpha) */ aocolor= R.wrld.aocolor; @@ -1550,7 +1593,8 @@ while (samples < max_samples) { /* sampling, returns quasi-random vector in unit hemisphere */ - QMC_sampleHemi(samp3d, qsa, shi->thread, samples); + if (G.rt > 1) QMC_sampleHemiCosine(samp3d, qsa, shi->thread, samples); + else 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]); @@ -1567,29 +1611,35 @@ 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]; + } else { - 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; + + 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); + + VecAddf(bentnor, bentnor, view); + noradded++; + + 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 if (aocolor==WO_AOSKYTEX) { + shadeSkyView(skycol, isec.start, view, dxyview); + shadfac[0]+= skycol[0]; + shadfac[1]+= skycol[1]; + shadfac[2]+= skycol[2]; + } + skyadded++; } - else { /* WO_AOSKYTEX */ - shadeSkyView(skycol, isec.start, view, dxyview); - shadfac[0]+= skycol[0]; - shadfac[1]+= skycol[1]; - shadfac[2]+= skycol[2]; - } - skyadded++; } samples++; @@ -1605,9 +1655,22 @@ } } - if(aocolor!=WO_AOPLAIN && skyadded) { - div= (1.0f - fac/(float)samples)/((float)skyadded); + if (aocolor == WO_AODIFFUSESH && G.rt!=1 && noradded > 0) { + VecMulf(bentnor, (1.0f / (float)noradded)); + Mat4Mul3Vecfl(R.viewinv, bentnor); + + IBL_diffusesh_getcolor(shadfac, (float *)R.ibl_sh_coeffs, bentnor); + + /* multiply by inverse proportion of geom hits, so rays that hit more other faces are darker */ + div = (float)noradded / (float)samples * exp(R.wrld.aodistfac); + + VecMulf(shadfac, div); + + } + else if(aocolor!=WO_AOPLAIN && skyadded) { + div= (1.0f - fac/(float)noradded)/(float)samples; + 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 @@ -1820,7 +1883,8 @@ VecOrthoBasisf(v, ru, rv); /* sampling, returns quasi-random vector in area_size disc */ - QMC_sampleDisc(samp3d, qsa, shi->thread, samples,lar->area_size); + if (G.rt==0) QMC_sampleDisc(samp3d, qsa, shi->thread, samples, 0.0, lar->area_size); + else QMC_sampleDiscConcentric(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]; @@ -1829,7 +1893,14 @@ VECCOPY(samp3d, s); } - else { + else if ((lar->type == LA_SPOT) && !(lar->mode & LA_SQUARE)) { + + /* sampling, returns quasi-random vector in area_size disc */ + if (G.rt==0) QMC_sampleDisc(samp3d, qsa, shi->thread, samples, lar->spotbl, lar->area_size); + else QMC_sampleDiscConcentric(samp3d, qsa, shi->thread, samples,lar->area_size); + + Mat3MulVecfl(lar->mat, samp3d); + } else { /* sampling, returns quasi-random vector in [sizex,sizey]^2 plane */ QMC_sampleRect(samp3d, qsa, shi->thread, samples, lar->area_size, lar->area_sizey); Index: source/blender/render/intern/source/shadeinput.c =================================================================== --- source/blender/render/intern/source/shadeinput.c (revision 13667) +++ source/blender/render/intern/source/shadeinput.c (working copy) @@ -773,6 +773,11 @@ else VECCOPY(shi->vn, shi->facenor); + /* normal in worldspace, used in Diffuse SH IBL, and nodes */ + VECCOPY(shi->worldnor, shi->vn); + Mat4Mul3Vecfl(R.viewinv, shi->worldnor); + VecMulf(shi->worldnor, -1.0f); + /* used in nodes */ VECCOPY(shi->vno, shi->vn); Index: source/blender/render/intern/source/shadeoutput.c =================================================================== --- source/blender/render/intern/source/shadeoutput.c (revision 13667) +++ source/blender/render/intern/source/shadeoutput.c (working copy) @@ -1584,6 +1584,10 @@ } } + /* ibl, adds results in diff and shad pass */ + if(R.wrld.mode & WO_IMAGE_BASED_LIGHTING) + image_based_lighting(shi, shr); + /* lighting pass */ if(passflag & (SCE_PASS_COMBINED|SCE_PASS_DIFFUSE|SCE_PASS_SPEC|SCE_PASS_SHADOW)) { GroupObject *go; Index: source/blender/render/intern/source/ibl.c =================================================================== --- source/blender/render/intern/source/ibl.c (revision 0) +++ source/blender/render/intern/source/ibl.c (revision 0) @@ -0,0 +1,467 @@ +/** +* $Id: + * + * ***** BEGIN GPL LICENSE BLOCK ***** + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * Contributors: Matt Ebb + * + * ***** END GPL LICENSE BLOCK ***** + */ + +#include +#include +#include + +#include "BLI_arithb.h" +#include "BLI_rand.h" + +#include "IMB_imbuf_types.h" + +#include "DNA_image_types.h" +#include "DNA_texture_types.h" +#include "DNA_world_types.h" + +#include "BKE_image.h" +#include "BKE_global.h" + +/* local include */ +#include "render_types.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; +/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ + +float sinc(float x) { + if (fabs(x) < 1.0e-4) return 1.0 ; + else return(sin(x)/x) ; +} + + +/* Converts UV coordinates from an environment map to the equivalent 3D vector + * Incoming UV coordinates are considered to be in [-1,1] + * domega = the solid angle that pixel represents in the unit sphere + * returns 1 if the pixel actually represents anything in the env map + * eg. pixels within the unit circle of an angmap */ +static int uv_to_xyz(int mapping, float *uv, float *xyz, float dtheta_dphi, float *domega) +{ + float phi, theta, sinphi, sintheta; + float radius_sq=1.0f; + + float u = uv[0]; + float v = uv[1]; + + switch(mapping) + { + case WO_IBL_MAP_SPHERE: + phi = (v*0.5f+0.5f)*M_PI; // pi < phi < 0 + theta = -u*M_PI; // pi < theta < -pi + + /* transform u,v to cartesian components */ + sinphi = sin(phi); + xyz[0] = -sinphi * sin(theta); + xyz[1] = sinphi * cos(theta); + xyz[2] = -cos(phi); + + /* solid angle */ + if (domega) *domega = sinphi*dtheta_dphi; + break; + + case WO_IBL_MAP_ANGMAP: + default: + /* for angmap, only consider points inside the unit circle */ + radius_sq = u*u + v*v; + if(radius_sq>1.0f) return 0; + + theta = M_PI*sqrt(radius_sq); + phi = atan2(v,u); + + /* transform u,v to cartesian components */ + sintheta = sin(theta); + xyz[0] = sintheta*cos(phi); + xyz[1] = cos(theta); + xyz[2] = sintheta*sin(phi); + + /* solid angle */ + if (domega) *domega = sinc(theta)*dtheta_dphi; + break; + } + + return 1; +} + +typedef struct ImpHistogram +{ + float dir[3]; + float col[3]; + double sumintensity; + float domega; +} ImpHistogram; + +/* RGB -> YCbCr(use Y only) */ +static double rgb_to_intensity(float r, float g, float b) +{ + return (0.2989 * r + 0.5866 * g + 0.1145 * b); +} + +void free_ibl_importance(Render *re) +{ + if (re->wrld.importance_col) MEM_freeN(re->wrld.importance_col); + if (re->wrld.importance_vec) MEM_freeN(re->wrld.importance_vec); +} + +static void importance_get_sample(float *vec, float *col, double *importance_vec, float *importance_col, int num) +{ + vec[0] = (float)importance_vec[3*num+0]; + vec[1] = (float)importance_vec[3*num+1]; + vec[2] = (float)importance_vec[3*num+2]; + + col[0] = importance_col[3*num+0]; + col[1] = importance_col[3*num+1]; + col[2] = importance_col[3*num+2]; +} + +static void importance_print_samples(Render *re) +{ + int i; + + float vec[3], col[3]; + + for (i=0; i < re->wrld.ibl_importance_samples; i++) { + importance_get_sample(vec, col, re->wrld.importance_vec, re->wrld.importance_col, i); + + printf("col %d - [%f,%f,%f] , ", i, col[0], col[1], col[2]); + printf("vec %d - [%f,%f,%f] , ", i, vec[0], vec[1], vec[2]); + } +} + + +void init_ibl_importance(Render *re) +{ + ImBuf *ibuf; + Image *ima; + ImpHistogram *hist; + float *rect_float; + float col[4], rnd; + int w, h, i, j; + double sumintensity=0.0, maxintensity, intensity, highest_intensity=0.0; + int index; + float dtheta_dphi, domega=0.f; + + + double *vec_table; + float *col_table; + + float uv[2], xyz[3]; + + float falloff; + + int nsamples = re->wrld.ibl_importance_samples; + + ima = re->wrld.ibl_image; + if (!ima) return; + ibuf = BKE_image_get_ibuf(ima, NULL); + + w = ibuf->x; + h = ibuf->y; + rect_float = ibuf->rect_float; + if (!rect_float) return; + + dtheta_dphi= (M_PI/(float)h) * (M_PI_2/(float)w); + if (re->wrld.ibl_mapping == WO_IBL_MAP_SPHERE) dtheta_dphi /= 2.0f; + + hist = (ImpHistogram *)MEM_mallocN(sizeof(ImpHistogram)*w*h, "importance sampling histogram"); + re->wrld.importance_vec = MEM_mallocN(3*sizeof(double)*nsamples, "importance sampling vectors"); + re->wrld.importance_col = MEM_mallocN(3*sizeof(float)*nsamples, "importance sampling colours"); + + vec_table = re->wrld.importance_vec; + col_table = re->wrld.importance_col; + + re->i.infostr= "Calculating Importance Histogram"; + + /* build a histogram with summed pixel intensities */ + for(j=0;jwrld.ibl_mapping, uv, hist[index].dir, dtheta_dphi, &domega); + + falloff = cos(M_PI * ((float)j/(float)h)); + + hist[index].col[0] = rect_float[4 * index + 0]; + hist[index].col[1] = rect_float[4 * index + 1]; + hist[index].col[2] = rect_float[4 * index + 2]; + + hist[index].domega = domega; + + // printf("col - [%f,%f,%f] , ", j, hist[i].col[0], hist[i].col[1], hist[i].col[2]); + + intensity = rgb_to_intensity(hist[index].col[0], hist[index].col[1], hist[index].col[2]); + sumintensity += intensity; + hist[index].sumintensity = sumintensity; + + } + } + +/* for (j = 0; j < h; j++) { + for (i = 0; i < w; i++) { + + if (!inunitsphere(i, j, w, h)) continue; + + index = j * w + i; + + xy_to_vec(hist[index].dir, i, j, w, h); + + hist[index].col[0] = rect_float[4 * index + 0]; + hist[index].col[1] = rect_float[4 * index + 1]; + hist[index].col[2] = rect_float[4 * index + 2]; + hist[index].col[3] = 1.0; + + hist[index].xy[0] = i; + hist[index].xy[1] = j; + intensity = rgb_to_intensity(hist[index].col[0], hist[index].col[1], hist[index].col[2]); + + sumintensity += intensity; + hist[index].sumintensity = sumintensity; + } + } +*/ + + printf("IBL:importance: sumintensity = %f\n", sumintensity); + + maxintensity = sumintensity; + + R.i.infostr= "Generating Importance Sampling Points"; + printf("colours ["); + for (j = 0; j < nsamples; j++) { + // printf("col %d - [%f,%f,%f] , ", j, hist[i].col[0], hist[i].col[1], hist[i].col[2]); + // printf("dir %d - [%f,%f,%f] , ", j, hist[i].dir[0], hist[i].dir[1], hist[i].dir[2]); + + /* generate random number in [0, maxintensity) */ + rnd = BLI_frand() * maxintensity; + + /* + if (samp_type == SAMP_TYPE_IMPORTANCE) { + rnd = BLI_frand() * maxintensity; // no thread_frand, since we're calculating in a non-threaded prepass + } else if (samp_type == SAMP_TYPE_IMPORTANCESTRATIFIED) { + // stratified + // printf("stratified \n"); + rnd = (((double)j + BLI_frand()) / (double)nsamples) * maxintensity; + } + */ + + for (i = 0; i < w*h; i++) { + if (rnd < hist[i].sumintensity) { + break; + } + } + + vec_table[3*j + 0] = hist[i].dir[0]; + vec_table[3*j + 1] = hist[i].dir[1]; + vec_table[3*j + 2] = hist[i].dir[2]; + + col_table[3*j + 0] = hist[i].col[0]; + col_table[3*j + 1] = hist[i].col[1]; + col_table[3*j + 2] = hist[i].col[2]; + + printf("[%f,%f, %f] , ", col_table[3*j + 0], col_table[3*j + 1], col_table[3*j + 2]); + } + printf("]\n"); + MEM_freeN(hist); + + importance_print_samples(re); +} + + + +/* expects normal in world space */ +void IBL_getcolor_importance(float *col, Render *re, float *n) +{ + int s, nsamples = re->wrld.ibl_importance_samples; + float vec[3], dot, sampcol[3], accumcol[3]; + int samplesadded=0; + + for (s=0; swrld.importance_vec, re->wrld.importance_col, s); + + /* only use samples in same hemisphere */ + dot = Inpf(n, vec); + if (dot > 0.0f) { + + VecMulf(sampcol, dot); + VecAddf(accumcol, accumcol, sampcol); + + } + samplesadded++; + } + + VecMulf(accumcol, 1.0f/(float)samplesadded); + VECCOPY(col, accumcol); +} + + + +/* *** Diffuse irradiance: Spherical Harmonics *** */ +/* +* Based on the paper and sample code of: +* "An Efficient Representation for Irradiance Environment Maps" +* by Ravi Ramamoorthi and Pat Hanrahan +* http://graphics.stanford.edu/papers/envmap/ +* +* Also thanks to some example code by Francesco Banterle +* http://www.banterle.com/francesco/ +* +* This calculates RGB values for lighting coefficients L_{lm} with +* 0 <= l <= 2 and -l <= m <= l. There are 9 coefficients in all. +*/ + + +void init_ibl_diffusesh(struct Render *re) +{ + ImBuf *ibuf; + Image *ima; + float uv[2], xyz[3], x, y, z; + float dtheta_dphi, c, domega=0.f; + int w, h, i, j, k; + float *rect_float, pix[3]; + int index, col; + + ima = re->wrld.ibl_image; + if (!ima) return; + ibuf = BKE_image_get_ibuf(ima, NULL); + + w = ibuf->x; + h = ibuf->y; + rect_float = ibuf->rect_float; + if (!rect_float) return; + + dtheta_dphi= (M_PI/(float)h) * (M_PI_2/(float)w); + if (re->wrld.ibl_mapping == WO_IBL_MAP_SPHERE) dtheta_dphi /= 2.0f; + + memset(re->ibl_sh_coeffs, 0, sizeof(re->ibl_sh_coeffs)); + + for(j=0;jwrld.ibl_mapping, uv, xyz, dtheta_dphi, &domega); + + x = xyz[0]; y = xyz[1]; z = xyz[2]; + index = j*w+i; + + pix[0] = rect_float[4 * index + 0]; + pix[1] = rect_float[4 * index + 1]; + pix[2] = rect_float[4 * index + 2]; + + /* add the light per pixel to the coefficients per term, + * based on a constant, the differential solid angle and + * cartesian components of surface normal x,y,z */ + for (col = 0 ; col < 3 ; col++) { + + /* L_{00}. Note that Y_{00} = 0.282095 */ + c=0.282095f*domega; + re->ibl_sh_coeffs[0][col] += pix[col]*c; + + /* L_{1m}. -1 <= m <= 1. The linear terms */ + c=0.488603f*domega; + re->ibl_sh_coeffs[1][col]+=pix[col]*c*y; + re->ibl_sh_coeffs[2][col]+=pix[col]*c*z; + re->ibl_sh_coeffs[3][col]+=pix[col]*c*x; + + /* The Quadratic terms, L_{2m} -2 <= m <= 2 */ + /* First, L_{2-2}, L_{2-1}, L_{21} corresponding to xy,yz,xz */ + c=1.092548f*domega; + re->ibl_sh_coeffs[4][col]+=pix[col]*c*x*y; + re->ibl_sh_coeffs[5][col]+=pix[col]*c*y*z; + re->ibl_sh_coeffs[7][col]+=pix[col]*c*x*z; + + /* L_{20}. Note that Y_{20} = 0.315392 (3z^2 - 1) */ + c=0.315392f*domega*(3.0f*z*z-1.0f); + re->ibl_sh_coeffs[6][col]+=pix[col]*c; + + c=0.546274f*domega*(x*x-y*y); + re->ibl_sh_coeffs[8][col]+=pix[col]*c; + } + } + } +}; + + +/* expects normal in world space */ +void IBL_diffusesh_getcolor( float *col, float *coeffs, float *n) +{ + float x, y, z; + static const float c1 = 0.429043, c2 = 0.511664, c3 = 0.743125, c4 = 0.886227, c5 = 0.247708; + int rgb; + float *L00, *L1_1, *L10, *L11, *L2_2, *L2_1, *L20, *L21, *L22; + + L00 = coeffs + 0*3; + L1_1 = coeffs + 1*3; + L10 = coeffs + 2*3; + L11 = coeffs + 3*3; + L2_2 = coeffs + 4*3; + L2_1 = coeffs + 5*3; + L20 = coeffs + 6*3; + L21 = coeffs + 7*3; + L22 = coeffs + 8*3; + + x = n[0] ; y = n[1] ; z = n[2]; + + for (rgb = 0; rgb < 3; rgb++) { + col[rgb] = c1*L22[rgb]*(x*x - y*y); + col[rgb] += c3*L20[rgb]*z*z; + col[rgb] += c4*L00[rgb]; + col[rgb] += -c5*L20[rgb]; + col[rgb] += 2.0f*c1*(L2_2[rgb]*x*y + L21[rgb]*x*z + L2_1[rgb]*y*z); + col[rgb] += 2.0f*c2*(L11[rgb]*x + L1_1[rgb]*y + L10[rgb]*z); + } +} + + +void image_based_lighting(ShadeInput *shi, ShadeResult *shr) +{ + float iblcol[3], nor[3]; + + VECCOPY(nor, shi->worldnor); + + if (R.wrld.ibl_method == WO_IBL_DIFFUSESH) { + + IBL_diffusesh_getcolor(iblcol, (float *)R.ibl_sh_coeffs, nor); + + } + else if (R.wrld.ibl_method == WO_IBL_IMPORTANCE) { + IBL_getcolor_importance(iblcol, &R, nor); + //importance_print_samples(&R); + } + + VecMulf(iblcol, R.wrld.ibl_multiplier); + + VecAddf(shr->diff, shr->diff, iblcol); + VecAddf(shr->shad, shr->shad, iblcol); +} Property changes on: source/blender/render/intern/source/ibl.c ___________________________________________________________________ Name: svn:executable + * Index: source/blender/render/intern/source/convertblender.c =================================================================== --- source/blender/render/intern/source/convertblender.c (revision 13667) +++ source/blender/render/intern/source/convertblender.c (working copy) @@ -4196,10 +4196,16 @@ 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) && (re->wrld.mode & WO_AMB_OCC)) { + if ((re->wrld.ao_samp_method == WO_AOSAMP_HAMMERSLEY) && (re->qsa)) + free_render_qmcsampler(re); + } + if (re->wrld.mode & WO_IMAGE_BASED_LIGHTING) { + if (re->wrld.ibl_method == WO_IBL_IMPORTANCE) + free_ibl_importance(re); + } + if(re->r.mode & R_RAYTRACE) freeraytree(re); free_sss(re); @@ -4487,8 +4493,19 @@ init_render_hammersley(re); else if (re->wrld.ao_samp_method == WO_AOSAMP_CONSTANT) init_ao_sphere(&re->wrld); + + if (re->wrld.aocolor == WO_AODIFFUSESH) + init_ibl_diffusesh(re); } + if(re->wrld.mode & WO_IMAGE_BASED_LIGHTING) { + if (re->wrld.ibl_method == WO_IBL_DIFFUSESH) { + init_ibl_diffusesh(re); + } else if (re->wrld.ibl_method == WO_IBL_IMPORTANCE) { + init_ibl_importance(re); + } + } + /* still bad... doing all */ init_render_textures(re); init_render_materials(re->r.mode, &re->wrld.ambr); Index: source/blender/render/intern/include/render_types.h =================================================================== --- source/blender/render/intern/include/render_types.h (revision 13667) +++ source/blender/render/intern/include/render_types.h (working copy) @@ -33,6 +33,7 @@ /* ------------------------------------------------------------------------- */ #include "DNA_color_types.h" +#include "DNA_image_types.h" #include "DNA_scene_types.h" #include "DNA_world_types.h" #include "DNA_object_types.h" @@ -193,6 +194,14 @@ struct Object *excludeob; + /* Image Based Lighting */ + /* spherical harmonics coefficients diffuse prefiltered */ + float ibl_sh_coeffs[9][3]; + /* importance sampling directions and colours */ + float *importance_col; + double *importance_vec; + + /* arena for allocating data for use during render, for * example dynamic TFaces to go in the VlakRen structure. */ Index: source/blender/render/intern/include/rendercore.h =================================================================== --- source/blender/render/intern/include/rendercore.h (revision 13667) +++ source/blender/render/intern/include/rendercore.h (working copy) @@ -109,5 +109,13 @@ extern void init_render_hammersley(Render *re); extern void free_render_qmcsampler(Render *re); +/* -------- ibl.c ------- */ + +void init_ibl_diffusesh(Render *re); +void init_ibl_importance(Render *re); +void free_ibl_importance(Render *re); + +void image_based_lighting(struct ShadeInput *shi, struct ShadeResult *shr); +void IBL_diffusesh_getcolor( float *col, float *coeffs, float *n); #endif /* RENDER_EXT_H */ Index: source/blender/render/extern/include/RE_shader_ext.h =================================================================== --- source/blender/render/extern/include/RE_shader_ext.h (revision 13667) +++ source/blender/render/extern/include/RE_shader_ext.h (working copy) @@ -136,6 +136,7 @@ float refcol[4], displace[3]; float strandco, tang[3], stress, winspeed[4]; float duplilo[3], dupliuv[3]; + float worldnor[3]; ShadeInputUV uv[8]; /* 8 = MAX_MTFACE */ ShadeInputCol col[8]; /* 8 = MAX_MCOL */ Index: source/blender/blenkernel/intern/world.c =================================================================== --- source/blender/blenkernel/intern/world.c (revision 13667) +++ source/blender/blenkernel/intern/world.c (working copy) @@ -37,6 +37,7 @@ #include #include "MEM_guardedalloc.h" +#include "DNA_image_types.h" #include "DNA_world_types.h" #include "DNA_texture_types.h" #include "DNA_scriptlink_types.h" @@ -86,6 +87,7 @@ World *add_world(char *name) { World *wrld; + ImageUser *iuser; wrld= alloc_libblock(&G.main->world, ID_WO, name); @@ -107,6 +109,14 @@ wrld->physicsEngine= WOPHY_BULLET;//WOPHY_SUMO; Bullet by default wrld->preview = NULL; + + iuser = &(wrld->ibl_iuser); + + iuser->sfra= 1; + iuser->fie_ima= 2; + iuser->ok= 1; + + wrld->ibl_multiplier = 1.0; return wrld; } Index: source/blender/python/api2_2x/World.h =================================================================== --- source/blender/python/api2_2x/World.h (revision 13667) +++ source/blender/python/api2_2x/World.h (working copy) @@ -1,5 +1,5 @@ /* - * $Id: World.h 10269 2007-03-15 01:09:14Z campbellbarton $ + * $Id$ * * ***** BEGIN GPL/BL DUAL LICENSE BLOCK ***** * @@ -34,6 +34,7 @@ #define EXPP_WORLD_H #include +#include "DNA_image_types.h" #include "DNA_world_types.h" #define BPy_World_Check(v) ((v)->ob_type==&World_Type) Index: source/blender/makesdna/DNA_world_types.h =================================================================== --- source/blender/makesdna/DNA_world_types.h (revision 13667) +++ source/blender/makesdna/DNA_world_types.h (working copy) @@ -39,12 +39,18 @@ struct Ipo; struct MTex; +struct Image; +struct ImageUser; #ifndef MAX_MTEX #define MAX_MTEX 10 #endif +typedef struct Ibl { + float dsh_coeffs[9][3]; +} Ibl; + /** * World defines general modeling data such as a background fill, * gravity, color model, stars, etc. It mixes game-data, rendering @@ -109,13 +115,21 @@ float *aosphere, *aotables; + short ibl_method, ibl_mapping; + short ibl_importance_samples, pad5[3]; + float ibl_multiplier; + struct Image *ibl_image; + struct ImageUser ibl_iuser; + float *importance_col; + double *importance_vec; + struct Ipo *ipo; struct MTex *mtex[10]; /* previews */ struct PreviewImage *preview; - + ScriptLink scriptlink; } World; @@ -136,6 +150,7 @@ #define WO_DOF 4 #define WO_ACTIVITY_CULLING 8 #define WO_AMB_OCC 16 +#define WO_IMAGE_BASED_LIGHTING 32 /* aomix */ #define WO_AOADD 0 @@ -156,7 +171,16 @@ #define WO_AOPLAIN 0 #define WO_AOSKYCOL 1 #define WO_AOSKYTEX 2 +#define WO_AODIFFUSESH 3 +/* ibl_method */ +#define WO_IBL_DIFFUSESH 0 +#define WO_IBL_IMPORTANCE 1 + +/* ibl_mapping */ +#define WO_IBL_MAP_ANGMAP 0 +#define WO_IBL_MAP_SPHERE 1 + /* ao_gather_method */ #define WO_AOGATHER_RAYTRACE 0 #define WO_AOGATHER_APPROX 1 Index: source/blender/src/buttons_shading.c =================================================================== --- source/blender/src/buttons_shading.c (revision 13667) +++ source/blender/src/buttons_shading.c (working copy) @@ -2137,13 +2137,95 @@ } +static void world_panel_ibl(World *wrld) +{ + uiBlock *block; + short yco=PANEL_YMAX; + char *strp; + struct ImageUser *iuser= &(wrld->ibl_iuser); + uiBut *but; + + block= uiNewBlock(&curarea->uiblocks, "world_panel_ibl", UI_EMBOSS, UI_HELV, curarea->win); + uiNewPanelTabbed("Amb Occ", "World"); + if(uiNewPanel(curarea, block, "Image Based Lighting", "World", PANELX, PANELY, PANELW, PANELH)==0) return; + + uiDefButBitS(block, TOG, WO_IMAGE_BASED_LIGHTING, B_REDR, "Image Based Lighting", + X2CLM1, yco-=BUTH, BUTW1, BUTH, &wrld->mode, 0, 0, 0, 0, "Enables lighting from an environment image (light probe)"); + + yco -= YSPACE; + + uiDefButS(block, MENU, B_REDR, "Calculation Method %t|Diffuse Prefiltered %x0|Importance Sampled %x1", + X2CLM1, yco-=BUTH, BUTW2, BUTH, &wrld->ibl_method, 0, 0, 0, 0, ""); + + if (wrld->ibl_method == WO_IBL_IMPORTANCE) { + uiDefButS(block, NUM, B_REDR, "Samples:", + X2CLM2, yco, BUTW2, BUTH, &wrld->ibl_importance_samples, 0.0, 512.0, 1.0, 0, "Number of times to sample the environment map"); + } + + yco -= YSPACE; + + /* Browse */ + IMAnames_to_pupstring(&strp, NULL, NULL, &(G.main->image), NULL, &iuser->menunr); + + uiBlockBeginAlign(block); + but= uiDefButS(block, MENU, B_REDR, strp, + X2CLM1, yco-=BUTH, ICONBUTW, BUTH, &iuser->menunr, 0, 0, 0, 0, "Selects an existing Image or Movie"); + uiButSetFunc(but, image_browse_cb, &(wrld->ibl_image), iuser); + + MEM_freeN(strp); + + if (wrld->ibl_image != NULL) { + char str[128]; + + if (wrld->ibl_image->source != IMA_SRC_FILE) { + uiDefBut(block, LABEL, 0, "Still images only", + X2CLM1, yco-=BUTH, BUTW2, BUTH, 0, 0, 0, 0, 0, ""); /* for align in panel */ + return; + } + uiSetButLock(wrld->ibl_image->id.lib!=NULL, ERROR_LIBDATA_MESSAGE); + but= uiDefIconBut(block, BUT, B_REDR, ICON_FILESEL, + X2CLM1+ICONBUTW, yco, X2CLM1+ICONBUTW, BUTH, 0, 0, 0, 0, 0, "Open Fileselect to load new Image"); + uiButSetFunc(but, image_load_fs_cb, &(wrld->ibl_image), iuser); + + but= uiDefBut(block, TEX, B_IDNAME, "IM:", + X2CLM1+2*ICONBUTW, yco, PANEL_XMAX-4*ICONBUTW-BUTW4, BUTH, wrld->ibl_image->id.name+2, 0.0, 21.0, 0, 0, "Current Image Datablock name."); + uiButSetFunc(but, test_idbutton_cb, wrld->ibl_image->id.name, NULL); + + but= uiDefBut(block, BUT, B_REDR, "Reload", + PANEL_XMAX-2*ICONBUTW-BUTW4, yco, BUTW4, BUTH, NULL, 0, 0, 0, 0, "Reloads Image or Movie"); + uiButSetFunc(but, image_reload_cb, &(wrld->ibl_image), iuser); + + but= uiDefIconBut(block, BUT, B_REDR, ICON_X, + PANEL_XMAX-2*ICONBUTW, yco, ICONBUTW, BUTH, 0, 0, 0, 0, 0, "Unlink Image block"); + uiButSetFunc(but, image_unlink_cb, &(wrld->ibl_image), NULL); + + sprintf(str, "%d", wrld->ibl_image->id.us); + uiDefBut(block, BUT, B_NOP, str, + PANEL_XMAX-ICONBUTW, yco, ICONBUTW, BUTH, 0, 0, 0, 0, 0, "Only displays number of users of Image block"); + + } else { + but= uiDefBut(block, BUT, B_REDR, "Load", + X2CLM1+ICONBUTW, yco, BUTW2-ICONBUTW, BUTH, NULL, 0, 0, 0, 0, "Load new Image"); + uiButSetFunc(but, image_load_fs_cb, &(wrld->ibl_image), iuser); + } + + uiBlockEndAlign(block); + + yco-= YSPACE; + + uiDefButF(block, NUM, B_REDR, "Multiplier:", + X2CLM1, yco-=BUTH, BUTW2, BUTH, &wrld->ibl_multiplier, 0.0, 100.0, 1.0, 0, "Multiply the final result to brighten or darken the lighting"); + + uiDefButS(block, MENU, B_REDR, "Image Format %t|Angular Map %x0|Sphere (LatLong) %x1", + X2CLM2, yco, BUTW2, BUTH, &wrld->ibl_mapping, 0, 0, 0, 0, ""); +} + static void world_panel_amb_occ(World *wrld) { uiBlock *block; short yco=PANEL_YMAX; block= uiNewBlock(&curarea->uiblocks, "world_panel_amb_oc", UI_EMBOSS, UI_HELV, curarea->win); - uiNewPanelTabbed("Mist / Stars / Physics", "World"); if(uiNewPanel(curarea, block, "Amb Occ", "World", PANELX, PANELY, PANELW, PANELH)==0) return; uiSetButLock(wrld->id.lib!=0, ERROR_LIBDATA_MESSAGE); @@ -2236,18 +2318,14 @@ /* color treatment */ uiBlockBeginAlign(block); - uiDefButS(block, ROW, B_REDR, "Plain", - X3CLM1, yco-=BUTH, BUTW3, BUTH, &wrld->aocolor, 2.0, (float)WO_AOPLAIN, 0, 0, "Plain diffuse energy (white)"); - uiDefButS(block, ROW, B_REDR, "Sky Color", - X3CLM2, yco, BUTW3, BUTH, &wrld->aocolor, 2.0, (float)WO_AOSKYCOL, 0, 0, "Use horizon and zenith color for diffuse energy"); - uiDefButS(block, ROW, B_REDR, "Sky Texture", - X3CLM3, yco, BUTW3, BUTH, &wrld->aocolor, 2.0, (float)WO_AOSKYTEX, 0, 0, "Does full Sky texture render for diffuse energy"); + uiDefButS(block, MENU, B_REDR, "Plain %x0|Sky Color %x1|Sky Texture %x2|IBL: Diffuse Prefiltered %x3", + X3CLM1, yco-=BUTH, BUTW2, BUTH, &wrld->aocolor, 0.0, (float)WO_AOPLAIN, 0, 0, ""); uiBlockEndAlign(block); yco -= YSPACE; - uiDefButF(block, NUMSLI, B_REDR, "Energy:", - X2CLM1, yco-=BUTH, BUTW2, BUTH, &wrld->aoenergy, 0.01, 3.0, 100, 0, "Sets global energy scale for AO"); + uiDefButF(block, NUM, B_REDR, "Energy:", + X2CLM1, yco-=BUTH, BUTW2, BUTH, &wrld->aoenergy, 0.01, 10.0, 100, 0, "Sets global energy scale for AO"); } static void world_panel_world(World *wrld) @@ -4276,6 +4354,7 @@ if(wrld) { world_panel_mistaph(wrld); world_panel_amb_occ(wrld); + world_panel_ibl(wrld); world_panel_texture(wrld); world_panel_mapto(wrld); } Index: source/blender/blenloader/intern/readfile.c =================================================================== --- source/blender/blenloader/intern/readfile.c (revision 13667) +++ source/blender/blenloader/intern/readfile.c (working copy) @@ -2120,6 +2120,8 @@ } } + wrld->ibl_image= newlibadr_us(fd, wrld->id.lib, wrld->ibl_image); + lib_link_scriptlink(fd, &wrld->id, &wrld->scriptlink); wrld->id.flag -= LIB_NEEDLINK; @@ -2137,6 +2139,9 @@ for(a=0; amtex[a]= newdataadr(fd, wrld->mtex[a]); } + + wrld->ibl_iuser.ok= 1; + wrld->preview = direct_link_preview_image(fd, wrld->preview); } @@ -7402,6 +7407,8 @@ if ((main->versionfile < 245) || (main->versionfile == 245 && main->subversionfile < 11)) { Object *ob; bActionStrip *strip; + World *wrld; + ImageUser *iuser; /* nla-strips - scale */ for (ob= main->object.first; ob; ob= ob->id.next) { @@ -7425,6 +7432,16 @@ ob->soft->shearstiff = 1.0f; } } + + for(wrld=main->world.first; wrld; wrld= wrld->id.next) { + iuser = &(wrld->ibl_iuser); + + iuser->sfra= 1; + iuser->fie_ima= 2; + iuser->ok= 1; + wrld->ibl_multiplier = 1.0; + } + } if ((main->versionfile < 245) || (main->versionfile == 245 && main->subversionfile < 14)) { Index: source/blender/nodes/intern/SHD_util.h =================================================================== --- source/blender/nodes/intern/SHD_util.h (revision 13667) +++ source/blender/nodes/intern/SHD_util.h (working copy) @@ -109,6 +109,7 @@ #define GEOM_OUT_NORMAL 5 #define GEOM_OUT_VCOL 6 #define GEOM_OUT_FRONTBACK 7 +#define GEOM_OUT_WORLDNORMAL 8 /* input socket defines */ Index: source/blender/nodes/intern/SHD_nodes/SHD_geom.c =================================================================== --- source/blender/nodes/intern/SHD_nodes/SHD_geom.c (revision 13667) +++ source/blender/nodes/intern/SHD_nodes/SHD_geom.c (working copy) @@ -39,9 +39,10 @@ { SOCK_VECTOR, 0, "View", 0.0f, 0.0f, 0.0f, 1.0f, -1.0f, 1.0f}, { SOCK_VECTOR, 0, "Orco", 0.0f, 0.0f, 0.0f, 1.0f, -1.0f, 1.0f}, { SOCK_VECTOR, 0, "UV", 0.0f, 0.0f, 0.0f, 1.0f, -1.0f, 1.0f}, - { SOCK_VECTOR, 0, "Normal", 0.0f, 0.0f, 0.0f, 1.0f, -1.0f, 1.0f}, + { SOCK_VECTOR, 0, "View Normal", 0.0f, 0.0f, 0.0f, 1.0f, -1.0f, 1.0f}, { SOCK_RGBA, 0, "Vertex Color", 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f}, { SOCK_VALUE, 0, "Front/Back", 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f}, + { SOCK_VECTOR, 0, "World Normal", 0.0f, 0.0f, 0.0f, 1.0f, -1.0f, 1.0f}, { -1, 0, "" } }; @@ -73,6 +74,7 @@ VECCOPY(out[GEOM_OUT_ORCO]->vec, shi->lo); VECCOPY(out[GEOM_OUT_UV]->vec, suv->uv); VECCOPY(out[GEOM_OUT_NORMAL]->vec, shi->vno); + VECCOPY(out[GEOM_OUT_WORLDNORMAL]->vec, shi->worldnor); if (shi->totcol) { /* find vertex color layer by name */