double BSDF::ShadingNormalCorrectionFactor( BSDFRecord& record, Intersection& isect ) { // Prevent light leak // In some cases wi and wo are same side according to the shading normal but // opposite side according to the geometry normal. auto worldWi = isect.shadingToWorld * record.wi; auto worldWo = isect.shadingToWorld * record.wo; double wiDotNg = Math::Dot(worldWi, isect.gn); double woDotNg = Math::Dot(worldWo, isect.gn); if (wiDotNg * CosTheta(record.wi) <= 0 || woDotNg * CosTheta(record.wo) <= 0) { return 0.0; } // Special handling for adjoint case // Be careful of the difference of the notation between Veach's thesis; // in the framework, wo is always the propagating direction. if (record.adjoint) { // |w_i, N_s| * |w_o, N_g| / |w_i, N_g| / |w_o, N_s| return CosTheta(record.wi) * woDotNg / (CosTheta(record.wo) * wiDotNg); } return 1.0; }
Spectrum FresnelSpecular::Sample_f(const Vector3f &wo, Vector3f *wi, const Point2f &u, Float *pdf, BxDFType *sampledType) const { Float F = FrDielectric(CosTheta(wo), etaA, etaB); if (u[0] < F) { // Compute specular reflection for _FresnelSpecular_ // Compute perfect specular reflection direction *wi = Vector3f(-wo.x, -wo.y, wo.z); if (sampledType) *sampledType = BxDFType(BSDF_SPECULAR | BSDF_REFLECTION); *pdf = F; return F * R / AbsCosTheta(*wi); } else { // Compute specular transmission for _FresnelSpecular_ // Figure out which $\eta$ is incident and which is transmitted bool entering = CosTheta(wo) > 0; Float etaI = entering ? etaA : etaB; Float etaT = entering ? etaB : etaA; // Compute ray direction for specular transmission if (!Refract(wo, Faceforward(Normal3f(0, 0, 1), wo), etaI / etaT, wi)) return 0; Spectrum ft = T * (1 - F); // Account for non-symmetry with transmission to different medium if (mode == TransportMode::Radiance) ft *= (etaI * etaI) / (etaT * etaT); if (sampledType) *sampledType = BxDFType(BSDF_SPECULAR | BSDF_TRANSMISSION); *pdf = 1 - F; return ft / AbsCosTheta(*wi); } }
Spectrum SpecularReflection::Sample_f(const Vector &wo, Vector *wi, float u1, float u2, float *pdf) const { // Compute perfect specular reflection direction *wi = Vector(-wo.x, -wo.y, wo.z); *pdf = 1.f; return fresnel->Evaluate(CosTheta(wo)) * R / fabsf(CosTheta(*wi)); }
double DiffuseBSDF::Pdf( BSDFRecord& record ) { if ((record.type & BSDFType::DiffuseReflection) == 0 || CosTheta(record.wi) <= 0 || CosTheta(record.wo) <= 0) { return 0.0; } return CosTheta(record.wo) * InvPi; }
Spectrum Microfacet::f(const Vector &wo, const Vector &wi) const { float cosThetaO = fabsf(CosTheta(wo)); float cosThetaI = fabsf(CosTheta(wi)); Vector wh = Normalize(wi + wo); float cosThetaH = Dot(wi, wh); Spectrum F = fresnel->Evaluate(cosThetaH); return R * distribution->D(wh) * G(wo, wi, wh) * F / (4.f * cosThetaI * cosThetaO); }
Spectrum FourierBSDF::f(const Vector3f &wo, const Vector3f &wi) const { // Find the zenith angle cosines and azimuth difference angle Float muI = CosTheta(-wi), muO = CosTheta(wo); Float cosPhi = CosDPhi(-wi, wo); // Compute Fourier coefficients $a_k$ for $(\mui, \muo)$ // Determine offsets and weights for $\mui$ and $\muo$ int offsetI, offsetO; Float weightsI[4], weightsO[4]; if (!bsdfTable.GetWeightsAndOffset(muI, &offsetI, weightsI) || !bsdfTable.GetWeightsAndOffset(muO, &offsetO, weightsO)) return Spectrum(0.f); // Allocate storage to accumulate _ak_ coefficients Float *ak = ALLOCA(Float, bsdfTable.mMax * bsdfTable.nChannels); memset(ak, 0, bsdfTable.mMax * bsdfTable.nChannels * sizeof(Float)); // Accumulate weighted sums of nearby $a_k$ coefficients int mMax = 0; for (int b = 0; b < 4; ++b) { for (int a = 0; a < 4; ++a) { // Add contribution of _(a, b)_ to $a_k$ values Float weight = weightsI[a] * weightsO[b]; if (weight != 0) { int m; const Float *ap = bsdfTable.GetAk(offsetI + a, offsetO + b, &m); mMax = std::max(mMax, m); for (int c = 0; c < bsdfTable.nChannels; ++c) for (int k = 0; k < m; ++k) ak[c * bsdfTable.mMax + k] += weight * ap[c * m + k]; } } } // Evaluate Fourier expansion for angle $\phi$ Float Y = std::max((Float)0, Fourier(ak, mMax, cosPhi)); Float scale = muI != 0 ? (1 / std::abs(muI)) : (Float)0; // Update _scale_ to account for adjoint light transport if (mode == TransportMode::Radiance && muI * muO > 0) { float eta = muI > 0 ? 1 / bsdfTable.eta : bsdfTable.eta; scale *= eta * eta; } if (bsdfTable.nChannels == 1) return Spectrum(Y * scale); else { // Compute and return RGB colors for tabulated BSDF Float R = Fourier(ak + 1 * bsdfTable.mMax, mMax, cosPhi); Float B = Fourier(ak + 2 * bsdfTable.mMax, mMax, cosPhi); Float G = 1.39829f * Y - 0.100913f * B - 0.297375f * R; Float rgb[3] = {R * scale, G * scale, B * scale}; return Spectrum::FromRGB(rgb).Clamp(); } }
Spectrum FresnelBlend::f(const Vector &wo, const Vector &wi) const { Spectrum diffuse = (28.f/(23.f*M_PI)) * Rd * (Spectrum(1.) - Rs) * (1 - powf(1 - .5f * fabsf(CosTheta(wi)), 5)) * (1 - powf(1 - .5f * fabsf(CosTheta(wo)), 5)); Vector H = Normalize(wi + wo); Spectrum specular = distribution->D(H) / (8.f * M_PI * AbsDot(wi, H) * max(fabsf(CosTheta(wi)), fabsf(CosTheta(wo)))) * SchlickFresnel(Dot(wi, H)); return diffuse + specular; }
float GGXForHeitz::Pdf(const Vector &wo, const Vector &wi) const { // We allow cos(theta_o) == 0 because it doesn't break our computation Assert(CosTheta(wo) >= 0.f); Vector wh = Normalize(wo + wi); float dotHO = Dot(wo, wh); if (dotHO < sSmallValue) { return 0.f; } return D(wh) * CosTheta(wh) / (4.f * dotHO); }
Vec3d DiffuseBSDF::SampleAndEvaluate( BSDFRecord& record, BSDFSample& sample, double& pdf, Intersection& isect ) { if ((record.type & BSDFType::DiffuseReflection) == 0 || CosTheta(record.wi) <= 0) { return Vec3d(); } // Sample direction (cosine weighted) record.wo = RenderUtils::CosineSampleHemisphere(sample.u); record.sampledType = BSDFType::DiffuseReflection; // Pdf pdf = Pdf(record); if (pdf == 0.0) { return Vec3d(); } // Correction factor for shading normal double sf = ShadingNormalCorrectionFactor(record, isect); if (sf == 0.0) { return Vec3d(); } // f(wi, wo) * cos(theta) / p(wo) // = R * invPi * cos(theta) / (cos(theta) * invPi) = R return R->Evaluate(isect.uv) * sf; }
Vec3d DiffuseBSDF::Evaluate( BSDFRecord& record, Intersection& isect ) { if ((record.type & BSDFType::DiffuseReflection) == 0 || CosTheta(record.wi) <= 0 || CosTheta(record.wo) <= 0) { return Vec3d(); } // Correction factor double sf = ShadingNormalCorrectionFactor(record, isect); if (sf == 0.0) { return Vec3d(); } return R->Evaluate(isect.uv) * InvPi * CosTheta(record.wo) * sf; }
// wo.z >= 0 is guaranteed // Note the sampled wi ~ D(wh)*cos(thetah)/(4*dot(wo,wh)) // and pdf = 0 when dot(wo, wh) < 0 (make sure Pdf() does the same thing) void GGXForHeitz::Sample_f(const Vector &wo, Vector *wi, float u1, float u2, float *pdf) const { // We allow cos(theta_o) == 0 because it doesn't break our computation Assert(CosTheta(wo) >= 0.f); // From [Walter et al., 2007] float thetah = atanf(mAlpha * sqrt(u1) / sqrt(1.f-u1)), costhetah = cosf(thetah), sinthetah = sinf(thetah), phih = u2 * 2.f * M_PI; Vector wh = SphericalDirection(sinthetah, costhetah, phih); // Compute incident direction by reflecting about $\wh$ float dotHO = Dot(wo, wh); if (dotHO < sSmallValue) { *pdf = 0.f; } else { *wi = -wo + 2.f * dotHO * wh; // TODO: test //if (wi->z < 0) { // fprintf(stderr, "wi.z == %f\n", wi->z); //} // TODO: can be optimized *pdf = D(wh) * costhetah / (4.f * dotHO); } }
Spectrum KajiyaKay::f(const Vector3f &wo, const Vector3f &wi) const { Spectrum diffuse(0.f), specular(0.f); if (!Ks.IsBlack()) { // Compute specular Kajiya-Kay term Vector3f wh = wi + wo; if (!(wh.x == 0 && wh.y == 0 && wh.z == 0)) { wh = Normalize(wh); #if 0 Float cosThetaH = Dot(wo, wh); Float sinThetaH = std::sqrt(std::max((Float)0, (Float)1 - cosThetaH * cosThetaH)); Float cosThetaO = CosTheta(wo), sinThetaO = SinTheta(wo); Float spec = std::pow(cosThetao * cosThetah + sinThetaO * sinThetaH, exponent); #else Float tdoth = wh.x; Float spec = std::pow( std::sqrt(std::max((Float)0, (Float)1 - tdoth * tdoth)), exponent); #endif specular = spec * Ks; } } // Compute diffuse Kajiya-Kay term diffuse = Kd * std::sqrt(std::max((Float)0., (Float)1. - wi.x * wi.x)); return (InvPi / AbsCosTheta(wi)) * (diffuse + specular); }
void SpotLight::Pdf_Le(const Ray &ray, const Normal3f &, Float *pdfPos, Float *pdfDir) const { *pdfPos = 0; *pdfDir = (CosTheta(WorldToLight(ray.d)) >= cosTotalWidth) ? UniformConePdf(cosTotalWidth) : 0; }
struct Spectrum SpecularReflection_cc_Sample_f(struct const_Vector wo, struct Vector *wi, float u1, float u2, float *pdf) { // Compute perfect specular reflection direction *wi = make_Vector(-wo.x, -wo.y, wo.z); *pdf = 1.f; struct Spectrum tmp1 = multiply(fresnel_arrow_Evaluate(CosTheta(wo)), make_Spectrum(R)); return div(tmp1, make_Spectrum(AbsCosTheta(*wi))); }
struct Spectrum SpecularReflection_cc_Sample_f(const_Vector wo, Vector *wi, float u1, float u2, float *pdf) { // return 0;// mgr // return 1;//mgr // Compute perfect specular reflection direction *wi = make_Vector(-wo.x, -wo.y, wo.z); *pdf = 1.f; return make_Spectrum(fresnel__Evaluate(CosTheta(wo)) * R / AbsCosTheta(*wi)); }
Spectrum SpecularTransmission::Sample_f(const Vector3f &wo, Vector3f *wi, const Point2f &sample, Float *pdf, BxDFType *sampledType) const { // Figure out which $\eta$ is incident and which is transmitted bool entering = CosTheta(wo) > 0; Float etaI = entering ? etaA : etaB; Float etaT = entering ? etaB : etaA; // Compute ray direction for specular transmission if (!Refract(wo, Faceforward(Normal3f(0, 0, 1), wo), etaI / etaT, wi)) return 0; *pdf = 1; Spectrum ft = T * (Spectrum(1.) - fresnel.Evaluate(CosTheta(*wi))); // Account for non-symmetry with transmission to different medium if (mode == TransportMode::Radiance) ft *= (etaI * etaI) / (etaT * etaT); return ft / AbsCosTheta(*wi); }
void ProjectionLight::Pdf_Le(const Ray &ray, const Normal3f &, Float *pdfPos, Float *pdfDir) const { ProfilePhase _(Prof::LightPdf); *pdfPos = 0.f; *pdfDir = (CosTheta(WorldToLight(ray.d)) >= cosTotalWidth) ? UniformConePdf(cosTotalWidth) : 0; }
Spectrum SpecularReflection::Sample_f(const Vector3f &wo, Vector3f *wi, const Point2f &sample, Float *pdf, BxDFType *sampledType) const { // Compute perfect specular reflection direction *wi = Vector3f(-wo.x, -wo.y, wo.z); *pdf = 1; return fresnel->Evaluate(CosTheta(*wi)) * R / AbsCosTheta(*wi); }
Spectrum Heitz::f(const Vector &woInput, const Vector &wiInput) const { // In PBRT's implementation, woInput and wiInput is not guaranteed to be at the same side of the shading normal // We inverse them if woInput is on the other side. // Note: because BSDF::f() determines BSDF_REFLECTION and BSDF_TRANSMISSION using ng (geometric normal) instead // of nn (shading normal), it's possible that woInput and wiInput has opposite sign of z-axis. Therefore, // we determine if the shading normal is at the opposite side using woInput (viewing direction) only: we // always make the viewing direction from the +Z side Vector wo = woInput, wi = wiInput; if (wo.z < 0.f) { // Reverse side wo = -wo; wi = -wi; } float cosThetaO = CosTheta(wo), absCosThetaI = AbsCosTheta(wi); Assert(cosThetaO >= 0.f); if (cosThetaO < sSmallValue || absCosThetaI < sSmallValue) { return Spectrum(0.f); } Vector wh = wi + wo; if (wh.x == 0. && wh.y == 0. && wh.z == 0.) { return Spectrum(0.f); } wh = Normalize(wh); float i_dot_h = Dot(wi, wh); Spectrum F = fresnel->Evaluate(i_dot_h); // Specular/glossy component float spec = mDistribution.D(wh) * mDistribution.G(wo, wi, wh) / (4.f * cosThetaO * absCosThetaI); // Diffuse component Spectrum Fdiff = Spectrum(1.f) - fresnel->Evaluate(cosThetaO); float diff = wi.z > 0.f ? INV_PI : 0.f; // TODO: test //F = Spectrum(1.f); // Note wi is possible to be at lower hemisphere, so we need to use // AbsCosTheta() //return R * (F * spec + (Spectrum(1.f) - F) * diff); return R * (F * spec + Fdiff * diff); // TODO: test /*float Dval = mDistribution.D(wh), Gval = mDistribution.G(wo, wi, wh); Spectrum val = R * Dval * Gval * F / (4.f * cosThetaO * absCosThetaI); if (isinf(val.y()) || val.y() < -sSmallValue) { fprintf(stderr, "invalid value\n"); } return val;*/ }
Spectrum MicrofacetTransmission::Sample_f(const Vector3f &wo, Vector3f *wi, const Point2f &u, Float *pdf, BxDFType *sampledType) const { Vector3f wh = distribution->Sample_wh(wo, u); Float eta = CosTheta(wo) > 0 ? (etaA / etaB) : (etaB / etaA); if (!Refract(wo, (Normal3f)wh, eta, wi)) return 0; *pdf = Pdf(wo, *wi); return f(wo, *wi); }
Float FourierBSDF::Pdf(const Vector3f &wo, const Vector3f &wi) const { // Find the zenith angle cosines and azimuth difference angle Float muI = CosTheta(-wi), muO = CosTheta(wo); Float cosPhi = CosDPhi(-wi, wo); // Compute luminance Fourier coefficients $a_k$ for $(\mui, \muo)$ int offsetI, offsetO; Float weightsI[4], weightsO[4]; if (!bsdfTable.GetWeightsAndOffset(muI, &offsetI, weightsI) || !bsdfTable.GetWeightsAndOffset(muO, &offsetO, weightsO)) return 0; Float *ak = ALLOCA(Float, bsdfTable.mMax * bsdfTable.nChannels); memset(ak, 0, bsdfTable.mMax * bsdfTable.nChannels * sizeof(Float)); int mMax = 0; for (int o = 0; o < 4; ++o) { for (int i = 0; i < 4; ++i) { Float weight = weightsI[i] * weightsO[o]; if (weight == 0) continue; int order; const Float *coeffs = bsdfTable.GetAk(offsetI + i, offsetO + o, &order); mMax = std::max(mMax, order); for (int k = 0; k < order; ++k) ak[k] += *coeffs++ * weight; } } // Evaluate probability of sampling _wi_ Float rho = 0; for (int o = 0; o < 4; ++o) { if (weightsO[o] == 0) continue; rho += weightsO[o] * bsdfTable.cdf[(offsetO + o) * bsdfTable.nMu + bsdfTable.nMu - 1] * (2 * Pi); } Float Y = Fourier(ak, mMax, cosPhi); return (rho > 0 && Y > 0) ? (Y / rho) : 0; }
// commented, dpl 10 august 2005 Spectrum OrenNayar::f(const Vector &wo, const Vector &wi) const { float sinthetai = SinTheta(wi); float sinthetao = SinTheta(wo); // Compute cosine term of Oren--Nayar model float sinphii = SinPhi(wi), cosphii = CosPhi(wi); float sinphio = SinPhi(wo), cosphio = CosPhi(wo); float dcos = cosphii * cosphio + sinphii * sinphio; float maxcos = max(0.f, dcos); // Compute sine and tangent terms of Oren--Nayar model float sinalpha, tanbeta; if (fabsf(CosTheta(wi)) > fabsf(CosTheta(wo))) { sinalpha = sinthetao; tanbeta = sinthetai / fabsf(CosTheta(wi)); } else { sinalpha = sinthetai; tanbeta = sinthetao / fabsf(CosTheta(wo)); } return R * INV_PI * (A + B * maxcos * sinalpha * tanbeta); }
RGB SpecularTransmission::Sample_f(const Vector& wo, Vector* wi, float u1, float u2, float *pdf) const { bool entering = CosTheta(wo) > 0.; float ei = mEtaI, et = mEtaT; if (!entering) //判断wo是从外面射入还是从内部射出 swap(ei, et); //根据Snell's law 计算折射方向 float sini2 = SinTheta2(wo); float eta = ei / et; float sint2 = eta * eta * sini2; if (sint2 >= 1.) return 0.; //所有的光线全部反射,所以没有折射 float cost = sqrtf(max(0.f, 1.f - sint2)); if (entering) cost = -cost; //设置符号 float sintOverSini = eta; *wi = Vector(sintOverSini * -wo.x, sintOverSini * -wo.y, cost); *pdf = 1.f; RGB F = mFresnel.Evaluate(CosTheta(wo)); //计算反射系数 return (ei * ei) / (et * et) * (RGB(1.0f) - F) * mScale / AbsCosTheta(*wi); }
Float MicrofacetTransmission::Pdf(const Vector3f &wo, const Vector3f &wi) const { if (SameHemisphere(wo, wi)) return 0; // Compute $\wh$ from $\wo$ and $\wi$ for microfacet transmission Float eta = CosTheta(wo) > 0 ? (etaB / etaA) : (etaA / etaB); Vector3f wh = Normalize(wo + wi * eta); // Compute change of variables _dwh\_dwi_ for microfacet transmission Float sqrtDenom = Dot(wo, wh) + eta * Dot(wi, wh); Float dwh_dwi = std::abs((eta * eta * Dot(wi, wh)) / (sqrtDenom * sqrtDenom)); return distribution->Pdf(wo, wh) * dwh_dwi; }
Spectrum MicrofacetTransmission::f(const Vector3f &wo, const Vector3f &wi) const { if (SameHemisphere(wo, wi)) return 0; // transmission only Float cosThetaO = CosTheta(wo); Float cosThetaI = CosTheta(wi); if (cosThetaI == 0 || cosThetaO == 0) return Spectrum(0); // Compute $\wh$ from $\wo$ and $\wi$ for microfacet transmission Float eta = CosTheta(wo) > 0 ? (etaB / etaA) : (etaA / etaB); Vector3f wh = Normalize(wo + wi * eta); if (wh.z < 0) wh = -wh; Spectrum F = fresnel.Evaluate(Dot(wo, wh)); Float sqrtDenom = Dot(wo, wh) + eta * Dot(wi, wh); Float factor = (mode == TransportMode::Radiance) ? (1 / eta) : 1; return (Spectrum(1.f) - F) * T * std::abs(distribution->D(wh) * distribution->G(wo, wi) * eta * eta * AbsDot(wi, wh) * AbsDot(wo, wh) * factor * factor / (cosThetaI * cosThetaO * sqrtDenom * sqrtDenom)); }
// commented, dpl 10 august 2005 Spectrum SpecularTransmission::Sample_f(const Vector &wo, Vector *wi, float u1, float u2, float *pdf) const { // Figure out which $\eta$ is incident and which is transmitted bool entering = CosTheta(wo) > 0.; float ei = etai, et = etat; if (!entering) swap(ei, et); // Compute transmitted ray direction float sini2 = SinTheta2(wo); float eta = ei / et; float sint2 = eta * eta * sini2; // Handle total internal reflection for transmission if (sint2 > 1.) return 0.; float cost = sqrtf(max(0.f, 1.f - sint2)); if (entering) cost = -cost; float sintOverSini = eta; *wi = Vector(sintOverSini * -wo.x, sintOverSini * -wo.y, cost); *pdf = 1.f; Spectrum F = fresnel.Evaluate(CosTheta(wo)); return (ei*ei)/(et*et) * (Spectrum(1.)-F) * T / fabsf(CosTheta(*wi)); }
float GGXForHeitz::D(const Vector &wh) const { float costhetah = CosTheta(wh); if (costhetah < sSmallValue) { return 0.f; } else { Assert(costhetah > 0.f && costhetah <= 1.f); float thetah = acosf(costhetah), costhetah2 = costhetah * costhetah, costhetah4 = costhetah2 * costhetah2, tanthetah = tanf(thetah), alphaSq = mAlpha * mAlpha, tanOverAlpha2 = (tanthetah * tanthetah) / alphaSq, factor = 1.f + tanOverAlpha2; return 1.f / (M_PI * alphaSq * costhetah4 * factor * factor); } }
float GGXForHeitz::G(const Vector &wo, const Vector &/*wi*/, const Vector &wh) const { float costhetao = CosTheta(wo); if (costhetao < sSmallValue) { return 0.f; } // TODO: currently, we only compute G1(wo, wh) float dotHO = Dot(wo, wh); if (dotHO < 0.f) { return 0.f; } float thetao = acosf(costhetao); if (thetao < sSmallValue) { return 1.f; } float a = 1.f / (mAlpha * tanf(thetao)), lambda = (-1.f + sqrtf(1.f + 1.f / (a*a))) / 2; return 1.f / (1.f + lambda); }
static Vector3f TrowbridgeReitzSample(const Vector3f &wi, Float alpha_x, Float alpha_y, Float U1, Float U2) { // 1. stretch wi Vector3f wiStretched = Normalize(Vector3f(alpha_x * wi.x, alpha_y * wi.y, wi.z)); // 2. simulate P22_{wi}(x_slope, y_slope, 1, 1) Float slope_x, slope_y; TrowbridgeReitzSample11(CosTheta(wiStretched), U1, U2, &slope_x, &slope_y); // 3. rotate Float tmp = CosPhi(wiStretched) * slope_x - SinPhi(wiStretched) * slope_y; slope_y = SinPhi(wiStretched) * slope_x + CosPhi(wiStretched) * slope_y; slope_x = tmp; // 4. unstretch slope_x = alpha_x * slope_x; slope_y = alpha_y * slope_y; // 5. compute normal return Normalize(Vector3f(-slope_x, -slope_y, 1.)); }
RGB SpecularReflection::Sample_f(const Vector& wo, Vector* wi, float u1, float u2, float *pdf) const { *wi = Vector(-wo.x, -wo.y, wo.z); //反射向量 *pdf = 1.f; //概率分布为1 return mFresnel->Evaluate(CosTheta(wo)) * mScale / AbsCosTheta(*wi); //镜面反射的brdf公式 }