/* splitbsdf.c : implementation of a bsdf consisting of one brdf and one bsdf. Either of the components may be NULL */ #include "bsdf.h" #include "pools.h" #include "vector.h" #include "error.h" #include "splitbsdf.h" #include "spherical.h" #ifdef NOPOOLS #define NEWSPLITBSDF() (SPLIT_BSDF *)Alloc(sizeof(SPLIT_BSDF)) #define DISPOSESPLITBSDF(ptr) Free((char *)ptr, sizeof(SPLIT_BSDF)) #else static POOL *bsdfPool = (POOL *)NULL; #define NEWSPLITBSDF() (SPLIT_BSDF *)NewPoolCell(sizeof(BSDF), 0, "split bsdfs", &bsdfPool) #define DISPOSESPLITBSDF(ptr) Dispose((char *)ptr, &bsdfPool) #endif /* Creation (->bsdf.h) */ void SplitBsdfPrint(FILE *out, SPLIT_BSDF *bsdf) { fprintf(out, "Split Bsdf :\n "); BrdfPrint(out, bsdf->brdf); fprintf(out, " "); BtdfPrint(out, bsdf->btdf); fprintf(out, " "); PrintTexture(out, bsdf->texture); } SPLIT_BSDF *SplitBSDFCreate(BRDF *brdf, BTDF *btdf, TEXTURE *texture) { SPLIT_BSDF *bsdf = NEWSPLITBSDF(); bsdf->brdf = brdf; bsdf->btdf = btdf; bsdf->texture = texture; return bsdf; } static SPLIT_BSDF *SplitBsdfDuplicate(SPLIT_BSDF *bsdf) { SPLIT_BSDF *newBsdf = NEWSPLITBSDF(); *newBsdf = *bsdf; return newBsdf; } static void SplitBsdfDestroy(SPLIT_BSDF *bsdf) { DISPOSESPLITBSDF(bsdf); } static COLOR SplitBsdfEvalTexture(TEXTURE* texture, HITREC *hit) { VECTOR texCoord; COLOR col; COLORCLEAR(col); if (!texture) return col; if (!hit || !HitTexCoord(hit, &texCoord)) { Warning("SplitBsdfEvalTexture", "Couldn't get texture coordinates"); return col; } return EvalTextureColor(texture, texCoord.x, texCoord.y); } /* texture modulates diffuse reflection */ #define TEXTURED_COMPONENT BRDF_DIFFUSE_COMPONENT static double TexturedScattererEval(VECTOR *in, VECTOR *out, VECTOR *normal) { return (1. / M_PI); } /* albedo is assumed to be 1 */ static VECTOR TexturedScattererSample(VECTOR *in, VECTOR *normal, double x_1, double x_2, double*pdf) { COORDSYS coord; VectorCoordSys(normal, &coord); return SampleHemisphereCosTheta(&coord, x_1, x_2, pdf); } static void TexturedScattererEvalPdf(VECTOR *in, VECTOR* out, VECTOR* normal, double *pdf) { *pdf = VECTORDOTPRODUCT(*normal, *out) / M_PI; } static COLOR SplitBsdfScatteredPower(SPLIT_BSDF *bsdf, HITREC *hit, VECTOR *in, BSDFFLAGS flags) { COLOR albedo; COLORCLEAR(albedo); if (bsdf->texture && (flags & TEXTURED_COMPONENT)) { COLOR textureColor = SplitBsdfEvalTexture(bsdf->texture, hit); COLORADD(albedo, textureColor, albedo); flags &= ~TEXTURED_COMPONENT; /* avoid taking it into account again */ } if (bsdf->brdf) { COLOR refl = BrdfReflectance(bsdf->brdf, GETBRDFFLAGS(flags)); COLORADD(albedo, refl, albedo); } if (bsdf->btdf) { COLOR trans = BtdfTransmittance(bsdf->btdf, GETBTDFFLAGS(flags)); COLORADD(albedo, trans, albedo); } return albedo; } static void SplitBsdfIndexOfRefraction(SPLIT_BSDF *bsdf, REFRACTIONINDEX *index) { BtdfIndexOfRefraction(bsdf->btdf, index); } static COLOR SplitBsdfEval(SPLIT_BSDF *bsdf, HITREC *hit, BSDF *inBsdf, BSDF *outBsdf, VECTOR *in, VECTOR *out, BSDFFLAGS flags) { COLOR result; VECTOR normal; COLORCLEAR(result); if (!HitShadingNormal(hit, &normal)) { Warning("SplitBsdfEval", "Couldn't determine shading normal"); return result; } if (bsdf->texture && (flags & TEXTURED_COMPONENT)) { double textureBsdf = TexturedScattererEval(in, out, &normal); COLOR textureCol = SplitBsdfEvalTexture(bsdf->texture, hit); COLORADDSCALED(result, textureBsdf, textureCol, result); flags &= ~TEXTURED_COMPONENT; } /* Just add brdf and btdf contributions, the eval routines handle the direction of out. Note that out * normal is computed more than once :-( */ if (bsdf->brdf) { COLOR reflectionCol = BrdfEval(bsdf->brdf, in, out, &normal, GETBRDFFLAGS(flags)); COLORADD(result, reflectionCol, result); } if (bsdf->btdf) { REFRACTIONINDEX inIndex, outIndex; COLOR refractionCol; BsdfIndexOfRefraction(inBsdf, &inIndex); BsdfIndexOfRefraction(outBsdf, &outIndex); refractionCol = BtdfEval(bsdf->btdf, inIndex, outIndex, in, out, &normal, GETBTDFFLAGS(flags)); COLORADD(result, refractionCol, result); } return result; } /* Sample a split bsdf. If no sample was taken (RR/absorption) the pdf will be 0 upon return */ /* Computes probabilities for sampling the texture, reflection minus texture, * or transmission. Also determines b[r|t]dfFlags taking into * account potential texturing. */ static void SplitBsdfProbabilities(SPLIT_BSDF *bsdf, HITREC *hit, BSDFFLAGS flags, double *Ptexture, double *Preflection, double *Ptransmission, XXDFFLAGS *brdfFlags, XXDFFLAGS *btdfFlags) { COLOR textureColor, reflectance, transmittance; *Ptexture = 0.; if (bsdf->texture && (flags & TEXTURED_COMPONENT)) { /* bsdf has a texture for diffuse reflection and diffuse reflection * needs to be sampled */ textureColor = SplitBsdfEvalTexture(bsdf->texture, hit); *Ptexture = COLORAVERAGE(textureColor); flags &= ~TEXTURED_COMPONENT; } *brdfFlags = GETBRDFFLAGS(flags); *btdfFlags = GETBTDFFLAGS(flags); reflectance = BrdfReflectance(bsdf->brdf, *brdfFlags); *Preflection = COLORAVERAGE(reflectance); transmittance = BtdfTransmittance(bsdf->btdf, *btdfFlags); *Ptransmission = COLORAVERAGE(transmittance); } /* Selects sampling mode according to given probabilities and random number x_1. * If the selected mode is not absorption, x_1 is rescaled to the interval [0,1) * again. */ typedef enum SAMPLING_MODE {SAMPLE_TEXTURE, SAMPLE_REFLECTION, SAMPLE_TRANSMISSION, SAMPLE_ABSORPTION} SAMPLING_MODE; static SAMPLING_MODE SplitBsdfSamplingMode(double Ptexture, double Preflection, double Ptransmission, double *x_1) { SAMPLING_MODE mode = SAMPLE_ABSORPTION; if (*x_1 < Ptexture) { mode = SAMPLE_TEXTURE; *x_1 /= Ptexture; /* rescale into [0,1) interval again */ } else { *x_1 -= Ptexture; if (*x_1 < Preflection) { mode = SAMPLE_REFLECTION; *x_1 /= Preflection; } else { *x_1 -= Preflection; if (*x_1 < Ptransmission) { mode = SAMPLE_TRANSMISSION; *x_1 /= Ptransmission; } } } return mode; } static VECTOR SplitBsdfSample(SPLIT_BSDF *bsdf, HITREC *hit, BSDF *inBsdf, BSDF *outBsdf, VECTOR *in, int doRussianRoulette, BSDFFLAGS flags, double x_1, double x_2, double *pdf) { double Ptexture, Preflection, Ptransmission, Pscattering, p, pRR; REFRACTIONINDEX inIndex, outIndex; XXDFFLAGS brdfFlags, btdfFlags; VECTOR normal; SAMPLING_MODE mode; VECTOR out; *pdf = 0; /* so we can return safely */ if (!HitShadingNormal(hit, &normal)) { Warning("SplitBsdfSample", "Couldn't determine shading normal"); VECTORSET(out, 0., 0., 1.); return out; } /* Calculate probabilities for sampling the texture, reflection minus texture, * and transmission. Also fills in correct b[r|t]dfFlags. */ SplitBsdfProbabilities(bsdf, hit, flags, &Ptexture, &Preflection, &Ptransmission, &brdfFlags, &btdfFlags); Pscattering = Ptexture + Preflection + Ptransmission; if (Pscattering < EPSILON) return out; /* Decide whether to sample the texture reflectance, the reflectance * modes not in the texture, transmission or absorption */ if (!doRussianRoulette) { /* normalize: no absorption sampling */ Ptexture /= Pscattering; Preflection /= Pscattering; Ptransmission /= Pscattering; } mode = SplitBsdfSamplingMode(Ptexture, Preflection, Ptransmission, &x_1); BsdfIndexOfRefraction(inBsdf, &inIndex); BsdfIndexOfRefraction(outBsdf, &outIndex); /* sample according to the selected mode */ switch (mode) { case SAMPLE_TEXTURE: out = TexturedScattererSample(in, &normal, x_1, x_2, &p); if (p < EPSILON) return out; /* don't care */ *pdf = Ptexture * p; /* other components will be added later */ break; case SAMPLE_REFLECTION: out = BrdfSample(bsdf->brdf, in, &normal, FALSE, brdfFlags, x_1, x_2, &p); if (p < EPSILON) return out; *pdf = Preflection * p; break; case SAMPLE_TRANSMISSION: out = BtdfSample(bsdf->btdf, inIndex, outIndex, in, &normal, FALSE, btdfFlags, x_1, x_2, &p); if (p < EPSILON) return out; *pdf = Ptransmission * p; break; case SAMPLE_ABSORPTION: *pdf = 0; return out; break; default: Fatal(-1, "SplitBsdfSample", "Impossible sampling mode %d", mode); } /* add probability of sampling the same direction in other than the * selected scattering mode (e.g. internal reflection) */ if (mode != SAMPLE_TEXTURE) { TexturedScattererEvalPdf(in, &out, &normal, &p); *pdf += Ptexture * p; } if (mode != SAMPLE_REFLECTION) { BrdfEvalPdf(bsdf->brdf, in, &out, &normal, brdfFlags, &p, &pRR); *pdf += Preflection * p; } if (mode != SAMPLE_TRANSMISSION) { BtdfEvalPdf(bsdf->btdf, inIndex, outIndex, in, &out, &normal, btdfFlags, &p, &pRR); *pdf += Ptransmission * p; } return out; } /* Sample a split bsdf. If no sample was taken (RR/absorption) the pdf will be 0 upon return */ static void SplitBsdfEvalPdf(SPLIT_BSDF *bsdf, HITREC *hit, BSDF *inBsdf, BSDF *outBsdf, VECTOR *in, VECTOR *out, BSDFFLAGS flags, double *pdf, double *pdfRR) { double Ptexture, Preflection, Ptransmission, Pscattering, p, pRR; REFRACTIONINDEX inIndex, outIndex; XXDFFLAGS brdfFlags, btdfFlags; VECTOR normal; *pdf = *pdfRR = 0.; /* so we can return safely */ if (!HitShadingNormal(hit, &normal)) { Warning("SplitBsdfEvalPdf", "Couldn't determine shading normal"); return; } /* Calculate probabilities for sampling the texture, reflection minus texture, * and transmission. Also fills in correct b[r|t]dfFlags. */ SplitBsdfProbabilities(bsdf, hit, flags, &Ptexture, &Preflection, &Ptransmission, &brdfFlags, &btdfFlags); Pscattering = Ptexture + Preflection + Ptransmission; if (Pscattering < EPSILON) return; /* survival probability */ *pdfRR = Pscattering; /* probability of sampling the outgoing direction, after survival decision */ BsdfIndexOfRefraction(inBsdf, &inIndex); BsdfIndexOfRefraction(outBsdf, &outIndex); TexturedScattererEvalPdf(in, out, &normal, &p); *pdf = Ptexture * p; BrdfEvalPdf(bsdf->brdf, in, out, &normal, brdfFlags, &p, &pRR); *pdf += Preflection * p; BtdfEvalPdf(bsdf->btdf, inIndex, outIndex, in, out, &normal, btdfFlags, &p, &pRR); *pdf += Ptransmission * p; *pdf /= Pscattering; } static int SplitBsdfIsTextured(SPLIT_BSDF *bsdf) { return bsdf->texture != NULL; } BSDF_METHODS SplitBsdfMethods = { (COLOR (*)(void *data, HITREC *hit, VECTOR *in, BSDFFLAGS flags))SplitBsdfScatteredPower, (int (*)(void *))SplitBsdfIsTextured, (void (*)(void *data, REFRACTIONINDEX *index))SplitBsdfIndexOfRefraction, (COLOR (*)(void *data, HITREC *hit, void *inBsdf, void *outBsdf, VECTOR *in, VECTOR *out, BSDFFLAGS flags))SplitBsdfEval, (VECTOR (*)(void *data, HITREC *hit, void *inBsdf, void *outBsdf, VECTOR *in, int doRussianRoulette, BSDFFLAGS flags, double x_1, double x_2, double *pdf))SplitBsdfSample, (void (*)(void *data, HITREC *hit, void *inBsdf, void *outBsdf, VECTOR *in, VECTOR *out, BSDFFLAGS flags, double *pdf, double *pdfRR))SplitBsdfEvalPdf, (int (*)(void *data, HITREC *hit, VECTOR *X, VECTOR *Y, VECTOR *Z))NULL, (void (*)(FILE *out, void *data))SplitBsdfPrint, (void *(*)(void *data))SplitBsdfDuplicate, (void *(*)(void *parent, void *data))NULL, (void (*)(void *data))SplitBsdfDestroy };