// 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); } }
Vector3f TrowbridgeReitzDistribution::Sample_wh(const Vector3f &wo, const Point2f &u) const { Vector3f wh; if (!sampleVisibleArea) { Float cosTheta = 0, phi = (2 * Pi) * u[1]; if (alphax == alphay) { Float tanTheta2 = alphax * alphax * u[0] / (1.0f - u[0]); cosTheta = 1 / std::sqrt(1 + tanTheta2); } else { phi = std::atan(alphay / alphax * std::tan(2 * Pi * u[1] + .5f * Pi)); if (u[1] > .5f) phi += Pi; Float sinPhi = std::sin(phi), cosPhi = std::cos(phi); const Float alphax2 = alphax * alphax, alphay2 = alphay * alphay; const Float alpha2 = 1 / (cosPhi * cosPhi / alphax2 + sinPhi * sinPhi / alphay2); Float tanTheta2 = alpha2 * u[0] / (1 - u[0]); cosTheta = 1 / std::sqrt(1 + tanTheta2); } Float sinTheta = std::sqrt(std::max((Float)0., (Float)1. - cosTheta * cosTheta)); wh = SphericalDirection(sinTheta, cosTheta, phi); if (!SameHemisphere(wo, wh)) wh = -wh; } else { bool flip = wo.z < 0; wh = TrowbridgeReitzSample(flip ? -wo : wo, alphax, alphay, u[0], u[1]); if (flip) wh = -wh; } return wh; }
// commented, dpl 10 august 2005 Spectrum Lafortune::Sample_f(const Vector &wo, Vector *wi, float u1, float u2, float *pdf) const { u_int comp = RandomUInt() % (nLobes+1); if (comp == nLobes) { // Cosine-sample the hemisphere, flipping the direction if necessary *wi = CosineSampleHemisphere(u1, u2); if (wo.z < 0.) wi->z *= -1.f; } else { // Sample lobe _comp_ for Lafortune BRDF float xlum = x[comp].y(); float ylum = y[comp].y(); float zlum = z[comp].y(); float costheta = powf(u1, 1.f / (.8f * exponent[comp].y() + 1)); float sintheta = sqrtf(max(0.f, 1.f - costheta*costheta)); float phi = u2 * 2.f * M_PI; Vector lobeCenter = Normalize(Vector(xlum * wo.x, ylum * wo.y, zlum * wo.z)); Vector lobeX, lobeY; CoordinateSystem(lobeCenter, &lobeX, &lobeY); *wi = SphericalDirection(sintheta, costheta, phi, lobeX, lobeY, lobeCenter); } if (!SameHemisphere(wo, *wi)) return Spectrum(0.f); *pdf = Pdf(wo, *wi); return f(wo, *wi); }
// commented, dpl 10 august 2005 void Anisotropic::Sample_f(const Vector &wo, Vector *wi, float u1, float u2, float *pdf) const { // Sample from first quadrant and remap to hemisphere to sample \wh float phi, costheta; if (u1 < .25f) { sampleFirstQuadrant(4.f * u1, u2, &phi, &costheta); } else if (u1 < .5f) { u1 = 4.f * (.5f - u1); sampleFirstQuadrant(u1, u2, &phi, &costheta); phi = M_PI - phi; } else if (u1 < .75f) { u1 = 4.f * (u1 - .5f); sampleFirstQuadrant(u1, u2, &phi, &costheta); phi += M_PI; } else { u1 = 4.f * (1.f - u1); sampleFirstQuadrant(u1, u2, &phi, &costheta); phi = 2.f * M_PI - phi; } float sintheta = sqrtf(max(0.f, 1.f - costheta*costheta)); Vector H = SphericalDirection(sintheta, costheta, phi); if (Dot(wo, H) < 0.f) H = -H; // Compute incident direction by reflecting about $\wh$ *wi = -wo + 2.f * Dot(wo, H) * H; // Compute PDF for \wi from Anisotropic distribution float anisotropic_pdf = D(H) / (4.f * Dot(wo, H)); *pdf = anisotropic_pdf; }
Vector SampleHG(const Vector &w, float g, float u1, float u2) { float costheta; if (fabsf(g) < 1e-3) costheta = 1.f - 2.f * u1; else { float sqrTerm = (1.f - g * g) / (1.f - g + 2.f * g * u1); costheta = (1.f + g * g - sqrTerm * sqrTerm) / (2.f * g); } float sintheta = sqrtf(max(0.f, 1.f-costheta*costheta)); float phi = 2.f * M_PI * u2; Vector v1, v2; CoordinateSystem(w, &v1, &v2); return SphericalDirection(sintheta, costheta, phi, v1, v2, w); }
Vector SampleHG(const Vector &w, float g, const float u1, const float u2) { float costheta; if (fabsf(g) < 1e-3f) costheta = 1.f - 2.f * u1; else { // NOTE - lordcrc - Bugfix, pbrt tracker id 0000082: bug in SampleHG const float sqrTerm = (1.f - g * g) / (1.f - g + 2.f * g * u1); costheta = (1.f + g * g - sqrTerm * sqrTerm) / (2.f * g); } const float sintheta = sqrtf(Max(0.f, 1.f - costheta * costheta)); const float phi = 2.f * M_PI * u2; Vector v1, v2; CoordinateSystem(w, &v1, &v2); return SphericalDirection(sintheta, costheta, phi, v1, v2, w); }
void Blinn::Sample_f(const Vector &wo, Vector *wi, float u1, float u2, float *pdf) const { // Compute sampled half-angle vector $\wh$ for Blinn distribution float costheta = powf(u1, 1.f / (exponent+1)); float sintheta = sqrtf(max(0.f, 1.f - costheta*costheta)); float phi = u2 * 2.f * M_PI; Vector H = SphericalDirection(sintheta, costheta, phi); if (Dot(wo, H) < 0.f) H = -H; // Compute incident direction by reflecting about $\wh$ *wi = -wo + 2.f * Dot(wo, H) * H; // Compute PDF for \wi from Blinn distribution float blinn_pdf = ((exponent + 2.f) * powf(costheta, exponent)) / (2.f * M_PI * 4.f * Dot(wo, H)); *pdf = blinn_pdf; }
Interaction Sphere::Sample(const Interaction &ref, const Point2f &u) const { // Compute coordinate system for sphere sampling Point3f pCenter = (*ObjectToWorld)(Point3f(0, 0, 0)); Vector3f wc = Normalize(pCenter - ref.p); Vector3f wcX, wcY; CoordinateSystem(wc, &wcX, &wcY); // Sample uniformly on sphere if $\pt{}$ is inside it Point3f pOrigin = OffsetRayOrigin(ref.p, ref.pError, ref.n, pCenter - ref.p); if (DistanceSquared(pOrigin, pCenter) <= radius * radius) return Sample(u); // Sample sphere uniformly inside subtended cone // Compute $\theta$ and $\phi$ values for sample in cone Float sinThetaMax2 = radius * radius / DistanceSquared(ref.p, pCenter); Float cosThetaMax = std::sqrt(std::max((Float)0, 1 - sinThetaMax2)); Float cosTheta = (1 - u[0]) + u[0] * cosThetaMax; Float sinTheta = std::sqrt(1 - cosTheta * cosTheta); Float phi = u[1] * 2 * Pi; // Compute angle $\alpha$ from center of sphere to sampled point on surface Float dc = Distance(ref.p, pCenter); Float ds = dc * cosTheta - std::sqrt(std::max( (Float)0, radius * radius - dc * dc * sinTheta * sinTheta)); Float cosAlpha = (dc * dc + radius * radius - ds * ds) / (2 * dc * radius); Float sinAlpha = std::sqrt(std::max((Float)0, 1 - cosAlpha * cosAlpha)); // Compute surface normal and sampled point on sphere Vector3f nObj = SphericalDirection(sinAlpha, cosAlpha, phi, -wcX, -wcY, -wc); Point3f pObj = radius * Point3f(nObj.x, nObj.y, nObj.z); // Return _Interaction_ for sampled point on sphere Interaction it; // Reproject _pObj_ to sphere surface and compute _pObjError_ pObj *= radius / Distance(pObj, Point3f(0, 0, 0)); Vector3f pObjError = gamma(5) * Abs((Vector3f)pObj); it.p = (*ObjectToWorld)(pObj, pObjError, &it.pError); it.n = (*ObjectToWorld)(Normal3f(nObj)); if (reverseOrientation) it.n *= -1.f; return it; }
void SampleBlinn(const Vector &wo, Vector *wi, float u1, float u2, float *pdf, float exponent) { // Compute sampled half-angle vector $\wh$ for Blinn distribution float costheta = powf(u1, 1.f / (exponent+1)); float sintheta = sqrtf(max(0.f, 1.f - costheta*costheta)); float phi = u2 * 2.f * M_PI; Vector wh = SphericalDirection(sintheta, costheta, phi); if (!SameHemisphere(wo, wh)) wh = -wh; // Compute incident direction by reflecting about $\wh$ *wi = -wo + 2.f * Dot(wo, wh) * wh; // Compute PDF for $\wi$ from Blinn distribution float blinn_pdf = ((exponent + 1.f) * powf(costheta, exponent)) / (2.f * M_PI * 4.f * Dot(wo, wh)); if (Dot(wo, wh) < 0.f) blinn_pdf = 0.f; *pdf = blinn_pdf; }
// HenyeyGreenstein Method Definitions Float HenyeyGreenstein::Sample_p(const Vector3f &wo, Vector3f *wi, const Point2f &u) const { // Compute $\cos \theta$ for Henyey--Greenstein sample Float cosTheta; if (std::abs(g) < 1e-3) cosTheta = 1 - 2 * u[0]; else { Float sqrTerm = (1 - g * g) / (1 - g + 2 * g * u[0]); cosTheta = (1 + g * g - sqrTerm * sqrTerm) / (2 * g); } // Compute direction _wi_ for Henyey--Greenstein sample Float sinTheta = std::sqrt(std::max((Float)0, 1 - cosTheta * cosTheta)); Float phi = 2 * Pi * u[1]; Vector3f v1, v2; CoordinateSystem(wo, &v1, &v2); *wi = SphericalDirection(sinTheta, cosTheta, phi, v1, v2, -wo); return PhaseHG(-cosTheta, g); }
Vector3f BeckmannDistribution::Sample_wh(const Vector3f &wo, const Point2f &u) const { if (!sampleVisibleArea) { // Sample full distribution of normals for Beckmann distribution // Compute $\tan^2 \theta$ and $\phi$ for Beckmann distribution sample Float tan2Theta, phi; if (alphax == alphay) { Float logSample = std::log(u[0]); if (std::isinf(logSample)) logSample = 0; tan2Theta = -alphax * alphax * logSample; phi = u[1] * 2 * Pi; } else { // Compute _tan2Theta_ and _phi_ for anisotropic Beckmann // distribution Float logSample = std::log(u[0]); if (std::isinf(logSample)) logSample = 0; phi = std::atan(alphay / alphax * std::tan(2 * Pi * u[1] + 0.5f * Pi)); if (u[1] > 0.5f) phi += Pi; Float sinPhi = std::sin(phi), cosPhi = std::cos(phi); Float alphax2 = alphax * alphax, alphay2 = alphay * alphay; tan2Theta = -logSample / (cosPhi * cosPhi / alphax2 + sinPhi * sinPhi / alphay2); } // Map sampled Beckmann angles to normal direction _wh_ Float cosTheta = 1 / std::sqrt(1 + tan2Theta); Float sinTheta = std::sqrt(std::max((Float)0, 1 - cosTheta * cosTheta)); Vector3f wh = SphericalDirection(sinTheta, cosTheta, phi); if (!SameHemisphere(wo, wh)) wh = -wh; return wh; } else { // Sample visible area of normals with Beckmann distribution Vector3f wh; bool flip = wo.z < 0; wh = BeckmannSample(flip ? -wo : wo, alphax, alphay, u[0], u[1]); if (flip) wh = -wh; return wh; } }
//复制自PBRT void Anisotropic::Sample_f(const Vector3f &wo, Vector3f *wi, Float u1, Float u2, Float *pdf) const { Float phi, costheta; if (u1 < .25f) { sampleFirstQuadrant(4.f * u1, u2, &phi, &costheta); } else if (u1 < .5f) { u1 = 4.f * (.5f - u1); sampleFirstQuadrant(u1, u2, &phi, &costheta); phi = Pi - phi; } else if (u1 < .75f) { u1 = 4.f * (u1 - .5f); sampleFirstQuadrant(u1, u2, &phi, &costheta); phi += Pi; } else { u1 = 4.f * (1.f - u1); sampleFirstQuadrant(u1, u2, &phi, &costheta); phi = 2.f * Pi - phi; } Float sintheta = sqrtf(std::max(0.f, 1.f - costheta * costheta)); Vector3f wh = SphericalDirection(sintheta, costheta, phi); if (!SameHemisphere(wo, wh)) wh = -wh; // Compute incident direction by reflecting about $\wh$ *wi = -wo + 2.f * Dot(wo, wh) * wh; // Compute PDF for $\wi$ from anisotropic distribution Float costhetah = AbsCosTheta(wh); Float ds = 1.f - costhetah * costhetah; Float anisotropic_pdf = 0.f; if (ds > 0.f && Dot(wo, wh) > 0.f) { Float e = (ex * wh.x * wh.x + ey * wh.y * wh.y) / ds; Float d = sqrtf((ex + 1.f) * (ey + 1.f)) * InvTwoPi * powf(costhetah, e); anisotropic_pdf = d / (4.f * Dot(wo, wh)); } *pdf = anisotropic_pdf; }
MeasuredMaterial::MeasuredMaterial(string matName, const string &filename, Reference<Texture<float> > bump): Material(matName) { bumpMap = bump; const char *suffix = strrchr(filename.c_str(), '.'); regularHalfangleData = NULL; thetaPhiData = NULL; if (!suffix) Error("No suffix in measured BRDF filename \"%s\". " "Can't determine file type (.brdf / .merl)", filename.c_str()); else if (!strcmp(suffix, ".brdf") || !strcmp(suffix, ".BRDF")) { // Load $(\theta, \phi)$ measured BRDF data if (loadedThetaPhi.find(filename) != loadedThetaPhi.end()) { thetaPhiData = loadedThetaPhi[filename]; return; } vector<float> values; if (!ReadFloatFile(filename.c_str(), &values)) { Error("Unable to read BRDF data from file \"%s\"", filename.c_str()); return; } uint32_t pos = 0; int numWls = int(values[pos++]); if ((values.size() - 1 - numWls) % (4 + numWls) != 0) { Error("Excess or insufficient data in theta, phi BRDF file \"%s\"", filename.c_str()); return; } vector<float> wls; for (int i = 0; i < numWls; ++i) wls.push_back(values[pos++]); BBox bbox; vector<IrregIsotropicBRDFSample> samples; while (pos < values.size()) { float thetai = values[pos++]; float phii = values[pos++]; float thetao = values[pos++]; float phio = values[pos++]; Vector wo = SphericalDirection(sinf(thetao), cosf(thetao), phio); Vector wi = SphericalDirection(sinf(thetai), cosf(thetai), phii); Spectrum s = Spectrum::FromSampled(&wls[0], &values[pos], numWls); pos += numWls; pbrt::Point p = BRDFRemap(wo, wi); samples.push_back(IrregIsotropicBRDFSample(p, s)); bbox = Union(bbox, p); } loadedThetaPhi[filename] = thetaPhiData = new KdTree<IrregIsotropicBRDFSample>(samples); } else { // Load RegularHalfangle BRDF Data nThetaH = 90; nThetaD = 90; nPhiD = 180; if (loadedRegularHalfangle.find(filename) != loadedRegularHalfangle.end()) { regularHalfangleData = loadedRegularHalfangle[filename]; return; } FILE *f = fopen(filename.c_str(), "rb"); if (!f) { Error("Unable to open BRDF data file \"%s\"", filename.c_str()); return; } int dims[3]; if (fread(dims, sizeof(int), 3, f) != 3) { Error("Premature end-of-file in measured BRDF data file \"%s\"", filename.c_str()); fclose(f); return; } uint32_t n = dims[0] * dims[1] * dims[2]; if (n != nThetaH * nThetaD * nPhiD) { Error("Dimensions don't match\n"); fclose(f); return; } regularHalfangleData = new float[3*n]; const uint32_t chunkSize = 2*nPhiD; double *tmp = ALLOCA(double, chunkSize); uint32_t nChunks = n / chunkSize; Assert((n % chunkSize) == 0); float scales[3] = { 1.f/1500.f, 1.15f/1500.f, 1.66f/1500.f }; for (int c = 0; c < 3; ++c) { int offset = 0; for (uint32_t i = 0; i < nChunks; ++i) { if (fread(tmp, sizeof(double), chunkSize, f) != chunkSize) { Error("Premature end-of-file in measured BRDF data file \"%s\"", filename.c_str()); delete[] regularHalfangleData; regularHalfangleData = NULL; fclose(f); return; } for (uint32_t j = 0; j < chunkSize; ++j) regularHalfangleData[3 * offset++ + c] = max(0., tmp[j] * scales[c]); } } loadedRegularHalfangle[filename] = regularHalfangleData; fclose(f); } }