Vec3f TraceBase::attenuatedEmission(PathSampleGenerator &sampler, const Primitive &light, const Medium *medium, float expectedDist, IntersectionTemporary &data, IntersectionInfo &info, int bounce, bool startsOnSurface, Ray &ray, Vec3f *transmittance) { CONSTEXPR float fudgeFactor = 1.0f + 1e-3f; if (light.isDirac()) { ray.setFarT(expectedDist); } else { if (!light.intersect(ray, data) || ray.farT()*fudgeFactor < expectedDist) return Vec3f(0.0f); } info.p = ray.pos() + ray.dir()*ray.farT(); info.w = ray.dir(); light.intersectionInfo(data, info); Vec3f shadow = generalizedShadowRay(sampler, ray, medium, &light, startsOnSurface, true, bounce); if (transmittance) *transmittance = shadow; if (shadow == 0.0f) return Vec3f(0.0f); return shadow*light.evalDirect(data, info); }
bool Cube::intersect(Ray &ray, IntersectionTemporary &data) const { Vec3f p = _invRot*(ray.pos() - _pos); Vec3f d = _invRot*ray.dir(); Vec3f invD = 1.0f/d; Vec3f relMin((-_scale - p)); Vec3f relMax(( _scale - p)); float ttMin = ray.nearT(), ttMax = ray.farT(); for (int i = 0; i < 3; ++i) { if (invD[i] >= 0.0f) { ttMin = max(ttMin, relMin[i]*invD[i]); ttMax = min(ttMax, relMax[i]*invD[i]); } else { ttMax = min(ttMax, relMin[i]*invD[i]); ttMin = max(ttMin, relMax[i]*invD[i]); } } if (ttMin <= ttMax) { if (ttMin > ray.nearT() && ttMin < ray.farT()) { data.primitive = this; ray.setFarT(ttMin); data.as<CubeIntersection>()->backSide = false; } else if (ttMax > ray.nearT() && ttMax < ray.farT()) { data.primitive = this; ray.setFarT(ttMax); data.as<CubeIntersection>()->backSide = true; } return true; } return false; }
bool Sphere::intersect(Ray &ray, IntersectionTemporary &data) const { Vec3f p = ray.pos() - _pos; float B = p.dot(ray.dir()); float C = p.lengthSq() - _radius*_radius; float detSq = B*B - C; if (detSq >= 0.0f) { float det = std::sqrt(detSq); float t = -B - det; if (t < ray.farT() && t > ray.nearT()) { ray.setFarT(t); data.primitive = this; data.as<SphereIntersection>()->backSide = false; return true; } t = -B + det; if (t < ray.farT() && t > ray.nearT()) { ray.setFarT(t); data.primitive = this; data.as<SphereIntersection>()->backSide = true; return true; } } return false; }
Vec3f HomogeneousMedium::transmittance(PathSampleGenerator &/*sampler*/, const Ray &ray) const { if (ray.farT() == Ray::infinity()) return Vec3f(0.0f); else { return std::exp(-_sigmaT*ray.farT()); } }
Vec3f ExponentialMedium::transmittance(PathSampleGenerator &/*sampler*/, const Ray &ray) const { float x = _falloffScale*(ray.pos() - _unitPoint).dot(_unitFalloffDirection); float dx = _falloffScale*ray.dir().dot(_unitFalloffDirection); if (ray.farT() == Ray::infinity() && dx <= 0.0f) return Vec3f(0.0f); else return std::exp(-_sigmaT*densityIntegral(x, dx, ray.farT())); }
float HomogeneousMedium::pdf(PathSampleGenerator &/*sampler*/, const Ray &ray, bool onSurface) const { if (_absorptionOnly) { return 1.0f; } else { if (onSurface) return std::exp(-ray.farT()*_sigmaT).avg(); else return (_sigmaT*std::exp(-ray.farT()*_sigmaT)).avg(); } }
float ExponentialMedium::pdf(PathSampleGenerator &/*sampler*/, const Ray &ray, bool onSurface) const { if (_absorptionOnly) { return 1.0f; } else { float x = _falloffScale*(ray.pos() - _unitPoint).dot(_unitFalloffDirection); float dx = _falloffScale*ray.dir().dot(_unitFalloffDirection); Vec3f transmittance = std::exp(-_sigmaT*densityIntegral(x, dx, ray.farT())); if (onSurface) { return transmittance.avg(); } else { return (density(x, dx, ray.farT())*_sigmaT*transmittance).avg(); } } }
Vec3f HomogeneousMedium::transmittanceAndPdfs(PathSampleGenerator &/*sampler*/, const Ray &ray, bool startOnSurface, bool endOnSurface, float &pdfForward, float &pdfBackward) const { if (ray.farT() == Ray::infinity()) { pdfForward = pdfBackward = 0.0f; return Vec3f(0.0f); } else if (_absorptionOnly) { pdfForward = pdfBackward = 1.0f; return std::exp(-_sigmaT*ray.farT()); } else { Vec3f weight = std::exp(-_sigmaT*ray.farT()); pdfForward = endOnSurface ? weight.avg() : (_sigmaT*weight).avg(); pdfBackward = startOnSurface ? weight.avg() : (_sigmaT*weight).avg(); return weight; } }
bool Quad::intersect(Ray &ray, IntersectionTemporary &data) const { float nDotW = ray.dir().dot(_frame.normal); if (std::abs(nDotW) < 1e-6f) return false; float t = _frame.normal.dot(_base - ray.pos())/nDotW; if (t < ray.nearT() || t > ray.farT()) return false; Vec3f q = ray.pos() + t*ray.dir(); Vec3f v = q - _base; float l0 = v.dot(_edge0)*_invUvSq.x(); float l1 = v.dot(_edge1)*_invUvSq.y(); if (l0 < 0.0f || l0 > 1.0f || l1 < 0.0f || l1 > 1.0f) return false; ray.setFarT(t); QuadIntersection *isect = data.as<QuadIntersection>(); isect->p = q; isect->u = l0; isect->v = 1.0f - l1; isect->backSide = nDotW >= 0.0f; data.primitive = this; return true; }
Vec3f AtmosphericMedium::transmittance(PathSampleGenerator &/*sampler*/, const Ray &ray) const { Vec3f p = (ray.pos() - _center); float t0 = p.dot(ray.dir()); float t1 = ray.farT() + t0; float h = (p - t0*ray.dir()).length(); return std::exp(-_sigmaT*densityIntegral(h, t0, t1)); }
bool Sphere::occluded(const Ray &ray) const { Vec3f p = ray.pos() - _pos; float B = p.dot(ray.dir()); float C = p.lengthSq() - _radius*_radius; float detSq = B*B - C; if (detSq >= 0.0f) { float det = std::sqrt(detSq); float t = -B - det; if (t < ray.farT() && t > ray.nearT()) return true; t = -B + det; if (t < ray.farT() && t > ray.nearT()) return true; } return false; }
bool MultiQuadLight::intersect(Ray &ray, IntersectionTemporary &data) const { QuadLightIntersection *isect = data.as<QuadLightIntersection>(); isect->wasPrimary = ray.isPrimaryRay(); float farT = ray.farT(); _bvh->trace(ray, [&](Ray &ray, uint32 idx, float /*tMin*/) { _geometry.intersect(ray, idx, isect->isect); }); if (ray.farT() < farT) { data.primitive = this; return true; } return false; }
bool ExponentialMedium::sampleDistance(PathSampleGenerator &sampler, const Ray &ray, MediumState &state, MediumSample &sample) const { if (state.bounce > _maxBounce) return false; float x = _falloffScale*(ray.pos() - _unitPoint).dot(_unitFalloffDirection); float dx = _falloffScale*ray.dir().dot(_unitFalloffDirection); float maxT = ray.farT(); if (_absorptionOnly) { if (maxT == Ray::infinity() && dx <= 0.0f) return false; sample.t = maxT; sample.weight = std::exp(-_sigmaT*densityIntegral(x, dx, ray.farT())); sample.pdf = 1.0f; sample.exited = true; } else { int component = sampler.nextDiscrete(3); float sigmaTc = _sigmaT[component]; float xi = 1.0f - sampler.next1D(); float logXi = std::log(xi); float t = inverseOpticalDepth(x, dx, sigmaTc, logXi); sample.t = min(t, maxT); sample.weight = std::exp(-_sigmaT*densityIntegral(x, dx, sample.t)); sample.exited = (t >= maxT); if (sample.exited) { sample.pdf = sample.weight.avg(); } else { float rho = density(x, dx, sample.t); sample.pdf = (rho*_sigmaT*sample.weight).avg(); sample.weight *= rho*_sigmaS; } sample.weight /= sample.pdf; state.advance(); } sample.p = ray.pos() + sample.t*ray.dir(); sample.phase = _phaseFunction.get(); return true; }
bool AtmosphericMedium::sampleDistance(PathSampleGenerator &sampler, const Ray &ray, MediumState &state, MediumSample &sample) const { if (state.bounce > _maxBounce) return false; Vec3f p = (ray.pos() - _center); float t0 = p.dot(ray.dir()); float h = (p - t0*ray.dir()).length(); float maxT = ray.farT() + t0; if (_absorptionOnly) { sample.t = ray.farT(); sample.weight = std::exp(-_sigmaT*densityIntegral(h, t0, maxT)); sample.pdf = 1.0f; sample.exited = true; } else { int component = sampler.nextDiscrete(3); float sigmaTc = _sigmaT[component]; float xi = 1.0f - sampler.next1D(); float t = inverseOpticalDepth(h, t0, sigmaTc, xi); sample.t = min(t, maxT); sample.weight = std::exp(-_sigmaT*densityIntegral(h, t0, sample.t)); sample.exited = (t >= maxT); if (sample.exited) { sample.pdf = sample.weight.avg(); } else { float rho = density(h, sample.t); sample.pdf = (rho*_sigmaT*sample.weight).avg(); sample.weight *= rho*_sigmaS; } sample.weight /= sample.pdf; sample.t -= t0; state.advance(); } sample.p = ray.pos() + sample.t*ray.dir(); sample.phase = _phaseFunction.get(); return true; }
Vec3f ExponentialMedium::transmittanceAndPdfs(PathSampleGenerator &/*sampler*/, const Ray &ray, bool startOnSurface, bool endOnSurface, float &pdfForward, float &pdfBackward) const { float x = _falloffScale*(ray.pos() - _unitPoint).dot(_unitFalloffDirection); float dx = _falloffScale*ray.dir().dot(_unitFalloffDirection); if (ray.farT() == Ray::infinity() && dx <= 0.0f) { pdfForward = pdfBackward = 0.0f; return Vec3f(0.0f); } Vec3f transmittance = std::exp(-_sigmaT*densityIntegral(x, dx, ray.farT())); if (_absorptionOnly) { pdfForward = pdfBackward = 1.0f; } else { pdfForward = endOnSurface ? transmittance.avg() : (density(x, dx, ray.farT())*_sigmaT*transmittance).avg(); pdfBackward = startOnSurface ? transmittance.avg() : (density(x, dx, 0.0f)*_sigmaT*transmittance).avg(); } return transmittance; }
inline RTCRay convert(const Ray &r) { RTCRay ray; ray.org[0] = r.pos().x(); ray.org[1] = r.pos().y(); ray.org[2] = r.pos().z(); ray.dir[0] = r.dir().x(); ray.dir[1] = r.dir().y(); ray.dir[2] = r.dir().z(); ray.tnear = r.nearT(); ray.tfar = r.farT(); ray.geomID = RTC_INVALID_GEOMETRY_ID; ray.primID = RTC_INVALID_GEOMETRY_ID; return ray; }
bool Quad::occluded(const Ray &ray) const { float nDotW = ray.dir().dot(_frame.normal); float t = _frame.normal.dot(_base - ray.pos())/nDotW; if (t < ray.nearT() || t > ray.farT()) return false; Vec3f q = ray.pos() + t*ray.dir(); Vec3f v = q - _base; float l0 = v.dot(_edge0)*_invUvSq.x(); float l1 = v.dot(_edge1)*_invUvSq.y(); if (l0 < 0.0f || l0 > 1.0f || l1 < 0.0f || l1 > 1.0f) return false; return true; }
float AtmosphericMedium::pdf(PathSampleGenerator &/*sampler*/, const Ray &ray, bool onSurface) const { if (_absorptionOnly) { return 1.0f; } else { Vec3f p = (ray.pos() - _center); float t0 = p.dot(ray.dir()); float t1 = ray.farT() + t0; float h = (p - t0*ray.dir()).length(); Vec3f transmittance = std::exp(-_sigmaT*densityIntegral(h, t0, t1)); if (onSurface) { return transmittance.avg(); } else { return (density(h, t0)*_sigmaT*transmittance).avg(); } } }
Vec3f AtmosphericMedium::transmittanceAndPdfs(PathSampleGenerator &/*sampler*/, const Ray &ray, bool startOnSurface, bool endOnSurface, float &pdfForward, float &pdfBackward) const { Vec3f p = (ray.pos() - _center); float t0 = p.dot(ray.dir()); float t1 = ray.farT() + t0; float h = (p - t0*ray.dir()).length(); Vec3f transmittance = std::exp(-_sigmaT*densityIntegral(h, t0, t1)); if (_absorptionOnly) { pdfForward = pdfBackward = 1.0f; } else { pdfForward = endOnSurface ? transmittance.avg() : (density(h, t1)*_sigmaT*transmittance).avg(); pdfBackward = startOnSurface ? transmittance.avg() : (density(h, t0)*_sigmaT*transmittance).avg(); } return transmittance; }
bool Cube::occluded(const Ray &ray) const { Vec3f p = _invRot*(ray.pos() - _pos); Vec3f d = _invRot*ray.dir(); Vec3f invD = 1.0f/d; Vec3f relMin((-_scale - p)); Vec3f relMax(( _scale - p)); float ttMin = ray.nearT(), ttMax = ray.farT(); for (int i = 0; i < 3; ++i) { if (invD[i] >= 0.0f) { ttMin = max(ttMin, relMin[i]*invD[i]); ttMax = min(ttMax, relMax[i]*invD[i]); } else { ttMax = min(ttMax, relMin[i]*invD[i]); ttMin = max(ttMin, relMax[i]*invD[i]); } } return ttMin <= ttMax; }
bool HomogeneousMedium::sampleDistance(PathSampleGenerator &sampler, const Ray &ray, MediumState &state, MediumSample &sample) const { if (state.bounce > _maxBounce) return false; float maxT = ray.farT(); if (_absorptionOnly) { if (maxT == Ray::infinity()) return false; sample.t = maxT; sample.weight = std::exp(-_sigmaT*maxT); sample.pdf = 1.0f; sample.exited = true; } else { int component = sampler.nextDiscrete(3); float sigmaTc = _sigmaT[component]; float t = -std::log(1.0f - sampler.next1D())/sigmaTc; sample.t = min(t, maxT); sample.weight = std::exp(-sample.t*_sigmaT); sample.exited = (t >= maxT); if (sample.exited) { sample.pdf = sample.weight.avg(); } else { sample.pdf = (_sigmaT*sample.weight).avg(); sample.weight *= _sigmaS; } sample.weight /= sample.pdf; state.advance(); } sample.p = ray.pos() + sample.t*ray.dir(); sample.phase = _phaseFunction.get(); return true; }
inline Vec3f TraceBase::generalizedShadowRayImpl(PathSampleGenerator &sampler, Ray &ray, const Medium *medium, const Primitive *endCap, int bounce, bool startsOnSurface, bool endsOnSurface, float &pdfForward, float &pdfBackward) const { IntersectionTemporary data; IntersectionInfo info; float initialFarT = ray.farT(); Vec3f throughput(1.0f); do { bool didHit = _scene->intersect(ray, data, info) && info.primitive != endCap; if (didHit) { if (!info.bsdf->lobes().hasForward()) return Vec3f(0.0f); SurfaceScatterEvent event = makeLocalScatterEvent(data, info, ray, nullptr); // For forward events, the transport direction does not matter (since wi = -wo) Vec3f transparency = info.bsdf->eval(event.makeForwardEvent(), false); if (transparency == 0.0f) return Vec3f(0.0f); if (ComputePdfs) { float transparencyScalar = transparency.avg(); pdfForward *= transparencyScalar; pdfBackward *= transparencyScalar; } throughput *= transparency; bounce++; if (bounce >= _settings.maxBounces) return Vec3f(0.0f); } if (medium) { if (ComputePdfs) { float forward, backward; throughput *= medium->transmittanceAndPdfs(sampler, ray, startsOnSurface, didHit || endsOnSurface, forward, backward); pdfForward *= forward; pdfBackward *= backward; } else { throughput *= medium->transmittance(sampler, ray, startsOnSurface, endsOnSurface); } } if (info.primitive == nullptr || info.primitive == endCap) return bounce >= _settings.minBounces ? throughput : Vec3f(0.0f); medium = info.primitive->selectMedium(medium, !info.primitive->hitBackside(data)); startsOnSurface = true; ray.setPos(ray.hitpoint()); initialFarT -= ray.farT(); ray.setNearT(info.epsilon); ray.setFarT(initialFarT); } while(true); return Vec3f(0.0f); }