Colord PerfectSpecular::sample(const HitPoint& hitPoint, const Vector3d& out, Vector3d& in) const { double normalDotOut = hitPoint.normal() * out; in = -out + hitPoint.normal() * 2.0 * normalDotOut; double normalDotIn = hitPoint.normal() * out; return reflectionColor() * reflectionCoefficient() / normalDotIn; }
Colord PhongMaterial::shade(const Raytracer* raytracer, const Rayd& ray, const HitPoint& hitPoint, State& state) const { auto texColor = diffuseTexture() ? diffuseTexture()->evaluate(ray, hitPoint) : Colord::black(); Lambertian ambientBRDF(texColor, ambientCoefficient()); Lambertian diffuseBRDF(texColor, diffuseCoefficient()); Vector3d out = -ray.direction(); auto color = ambientBRDF.reflectance(hitPoint, out) * raytracer->scene()->ambient(); for (const auto& light : raytracer->scene()->lights()) { Vector3d in = light->direction(hitPoint.point()); if (raytracer->scene()->intersects(Rayd(hitPoint.point(), in).epsilonShifted(), state)) { state.shadowHit(this, "PhongMaterial"); } else { state.shadowMiss(this, "PhongMaterial"); double normalDotIn = hitPoint.normal() * in; if (normalDotIn > 0.0) { color += ( diffuseBRDF(hitPoint, out, in) + m_specularBRDF(hitPoint, out, in) ) * light->radiance() * normalDotIn; } } } return color; }
void HitPoints::AccumulateFlux(scheduling::Range *range) { for(unsigned i = range->begin(); i != range->end(); i = range->next()) { HitPoint *hp = &(*hitPoints)[i]; hp->DoRadiusReduction(renderer->sppmi->photonAlpha, GetPassCount(), renderer->sppmi->useproba); } }
void HitPoints::Init() { // Not using UpdateBBox() because hp->accumPhotonRadius2 is not yet set BBox hpBBox = BBox(); for (u_int i = 0; i < (*hitPoints).size(); ++i) { HitPoint *hp = &(*hitPoints)[i]; if (hp->IsSurface()) hpBBox = Union(hpBBox, hp->GetPosition()); } // Calculate initial radius Vector ssize = hpBBox.pMax - hpBBox.pMin; initialPhotonRadius = renderer->sppmi->photonStartRadiusScale * ((ssize.x + ssize.y + ssize.z) / 3.f) / sqrtf(nSamplePerPass) * 2.f; const float photonRadius2 = initialPhotonRadius * initialPhotonRadius; // Expand the bounding box by used radius hpBBox.Expand(initialPhotonRadius); // Update hit points information hitPointBBox = hpBBox; maxHitPointRadius2 = photonRadius2; LOG(LUX_DEBUG, LUX_NOERROR) << "Hit points bounding box: " << hitPointBBox; LOG(LUX_DEBUG, LUX_NOERROR) << "Hit points max. radius: " << sqrtf(maxHitPointRadius2); // Initialize hit points field for (u_int i = 0; i < (*hitPoints).size(); ++i) { HitPoint *hp = &(*hitPoints)[i]; hp->accumPhotonRadius2 = photonRadius2; } // Allocate hit points lookup accelerator switch (renderer->sppmi->lookupAccelType) { case HASH_GRID: lookUpAccel = new HashGrid(this); break; case KD_TREE: lookUpAccel = new KdTree(this); break; case HYBRID_HASH_GRID: lookUpAccel = new HybridHashGrid(this); break; case PARALLEL_HASH_GRID: lookUpAccel = new ParallelHashGrid(this, renderer->sppmi->parallelHashGridSpare); break; default: assert (false); } }
const Primitive* ConvexOperation::intersect(const Rayd& ray, HitPointInterval& hitPoints, State& state) const { if (!boundingBoxIntersects(ray)) { state.miss("ConvexOperation, bounding box miss"); return nullptr; } // unlike the intersection methods for box, sphere, etc, convexIntersect() // only returns a single (closest) hitpoint. So we need to shoot a ray in the // opposite direction as well. // calculate hitpoint in ray direction if (convexIntersect(ray, hitPoints)) { // if it's a hit, do it again in the opposite direction HitPointInterval opposite; // If we double the hit distance and take the longest possible length in // the bounding box, we should be beyond the other side of the object Rayd oppositeRay( ray.at(hitPoints.min().distance() * 2.0 + boundingBox().size().length()), -ray.direction() ); // fire! convexIntersect(oppositeRay, opposite); HitPoint oppositePoint = opposite.min(); // now, we have to project that point on the opposite side back to the // original ray to find the real distance value oppositePoint.setDistance(ray.projectedDistance(oppositePoint.point())); // add it to the interval hitPoints.add(oppositePoint); // and merge both points into a single interval. we can do that, since this // is by definition a convex object, so there can be only a single interval hitPoints = hitPoints.merged(); auto hitPoint = hitPoints.minWithPositiveDistance(); if (hitPoint.isUndefined()) { return nullptr; state.miss("ConvexOperation, ray miss"); } else { state.hit("ConvexOperation"); hitPoints.setPrimitive(this); return this; } } state.miss("ConvexOperation, ray miss"); return nullptr; }
const double HitPoints::GetPhotonHitEfficency() { u_int surfaceHitPointsCount = 0; u_int hitPointsUpdatedCount = 0; for (u_int i = 0; i < GetSize(); ++i) { HitPoint *hp = &(*hitPoints)[i]; if (hp->IsSurface()) { ++surfaceHitPointsCount; if (hp->GetPhotonCount() > 0) ++hitPointsUpdatedCount; } } return 100.0 * hitPointsUpdatedCount / surfaceHitPointsCount; }
void MixMaterial::Pdf(const HitPoint &hitPoint, const Vector &localLightDir, const Vector &localEyeDir, float *directPdfW, float *reversePdfW) const { const Frame frame(hitPoint.GetFrame()); const float weight2 = Clamp(mixFactor->GetFloatValue(hitPoint), 0.f, 1.f); const float weight1 = 1.f - weight2; float directPdfWMatA = 1.f; float reversePdfWMatA = 1.f; if (weight1 > 0.f) { HitPoint hitPointA(hitPoint); matA->Bump(&hitPointA); const Frame frameA(hitPointA.GetFrame()); const Vector lightDirA = frameA.ToLocal(frame.ToWorld(localLightDir)); const Vector eyeDirA = frameA.ToLocal(frame.ToWorld(localEyeDir)); matA->Pdf(hitPointA, lightDirA, eyeDirA, &directPdfWMatA, &reversePdfWMatA); } float directPdfWMatB = 1.f; float reversePdfWMatB = 1.f; if (weight2 > 0.f) { HitPoint hitPointB(hitPoint); matB->Bump(&hitPointB); const Frame frameB(hitPointB.GetFrame()); const Vector lightDirB = frameB.ToLocal(frame.ToWorld(localLightDir)); const Vector eyeDirB = frameB.ToLocal(frame.ToWorld(localEyeDir)); matB->Pdf(hitPointB, lightDirB, eyeDirB, &directPdfWMatB, &reversePdfWMatB); } if (directPdfW) *directPdfW = weight1 * directPdfWMatA + weight2 *directPdfWMatB; if (reversePdfW) *reversePdfW = weight1 * reversePdfWMatA + weight2 * reversePdfWMatB; }
Spectrum MixMaterial::Sample(const HitPoint &hitPoint, const Vector &localFixedDir, Vector *localSampledDir, const float u0, const float u1, const float passThroughEvent, float *pdfW, float *absCosSampledDir, BSDFEvent *event, const BSDFEvent requestedEvent) const { const Frame frame(hitPoint.GetFrame()); HitPoint hitPointA(hitPoint); matA->Bump(&hitPointA); const Frame frameA(hitPointA.GetFrame()); const Vector fixedDirA = frameA.ToLocal(frame.ToWorld(localFixedDir)); HitPoint hitPointB(hitPoint); matB->Bump(&hitPointB); const Frame frameB(hitPointB.GetFrame()); const Vector fixedDirB = frameB.ToLocal(frame.ToWorld(localFixedDir)); const float weight2 = Clamp(mixFactor->GetFloatValue(hitPoint), 0.f, 1.f); const float weight1 = 1.f - weight2; const bool sampleMatA = (passThroughEvent < weight1); const float weightFirst = sampleMatA ? weight1 : weight2; const float weightSecond = sampleMatA ? weight2 : weight1; const float passThroughEventFirst = sampleMatA ? (passThroughEvent / weight1) : (passThroughEvent - weight1) / weight2; // Sample the first material, evaluate the second const Material *matFirst = sampleMatA ? matA : matB; const Material *matSecond = sampleMatA ? matB : matA; HitPoint &hitPoint1 = sampleMatA ? hitPointA : hitPointB; HitPoint &hitPoint2 = sampleMatA ? hitPointB : hitPointA; const Frame &frame1 = sampleMatA ? frameA : frameB; const Frame &frame2 = sampleMatA ? frameB : frameA; const Vector &fixedDir1 = sampleMatA ? fixedDirA : fixedDirB; const Vector &fixedDir2 = sampleMatA ? fixedDirB : fixedDirA; // Sample the first material Spectrum result = matFirst->Sample(hitPoint1, fixedDir1, localSampledDir, u0, u1, passThroughEventFirst, pdfW, absCosSampledDir, event, requestedEvent); if (result.Black()) return Spectrum(); *localSampledDir = frame1.ToWorld(*localSampledDir); const Vector sampledDir2 = frame2.ToLocal(*localSampledDir); *localSampledDir = frame.ToLocal(*localSampledDir); *pdfW *= weightFirst; result *= *pdfW; // Evaluate the second material const Vector &localLightDir = (hitPoint2.fromLight) ? fixedDir2 : sampledDir2; const Vector &localEyeDir = (hitPoint2.fromLight) ? sampledDir2 : fixedDir2; BSDFEvent eventSecond; float pdfWSecond; Spectrum evalSecond = matSecond->Evaluate(hitPoint2, localLightDir, localEyeDir, &eventSecond, &pdfWSecond); if (!evalSecond.Black()) { result += weightSecond * evalSecond; *pdfW += weightSecond * pdfWSecond; } return result / *pdfW; }
HitPoints::HitPoints(SPPMRenderer *engine, RandomGenerator *rng) { renderer = engine; Scene *scene = renderer->scene; currentPass = 0; wavelengthSampleScramble = rng->uintValue(); timeSampleScramble = rng->uintValue(); wavelengthStratPasses = RoundUpPow2(renderer->sppmi->wavelengthStratification); if (wavelengthStratPasses > 0) { LOG(LUX_DEBUG, LUX_NOERROR) << "Non-random wavelength stratification for " << wavelengthStratPasses << " passes"; wavelengthSample = 0.5f; // non-randomly stratified in IncPass for first N passes } else wavelengthSample = Halton(0, wavelengthSampleScramble); timeSample = Halton(0, timeSampleScramble); // Get the count of hit points required int xstart, xend, ystart, yend; scene->camera()->film->GetSampleExtent(&xstart, &xend, &ystart, ¥d); // Set the sampler eyeSampler = new HaltonEyeSampler(xstart, xend, ystart, yend, renderer->sppmi->PixelSampler, renderer->sppmi->hitpointPerPass); // the eyeSampler may decide that it allocates less or more hitpointPerPass renderer->sppmi->hitpointPerPass = eyeSampler->GetTotalSamplePos(); nSamplePerPass = renderer->sppmi->hitpointPerPass; hitPoints = new std::vector<HitPoint>(nSamplePerPass); LOG(LUX_DEBUG, LUX_NOERROR) << "Hit points count: " << hitPoints->size(); // Initialize hit points field for (u_int i = 0; i < (*hitPoints).size(); ++i) { HitPoint *hp = &(*hitPoints)[i]; hp->InitStats(); } store_component = BxDFType(BSDF_DIFFUSE | BSDF_REFLECTION | BSDF_TRANSMISSION); bounce_component = BxDFType(BSDF_SPECULAR | BSDF_REFLECTION | BSDF_TRANSMISSION); if(engine->sppmi->storeGlossy) store_component = BxDFType(store_component | BSDF_GLOSSY); else bounce_component = BxDFType(bounce_component | BSDF_GLOSSY); }
Spectrum MixMaterial::Evaluate(const HitPoint &hitPoint, const Vector &localLightDir, const Vector &localEyeDir, BSDFEvent *event, float *directPdfW, float *reversePdfW) const { const Frame frame(hitPoint.GetFrame()); Spectrum result; const float weight2 = Clamp(mixFactor->GetFloatValue(hitPoint), 0.f, 1.f); const float weight1 = 1.f - weight2; if (directPdfW) *directPdfW = 0.f; if (reversePdfW) *reversePdfW = 0.f; BSDFEvent eventMatA = NONE; if (weight1 > 0.f) { HitPoint hitPointA(hitPoint); matA->Bump(&hitPointA); const Frame frameA(hitPointA.GetFrame()); const Vector lightDirA = frameA.ToLocal(frame.ToWorld(localLightDir)); const Vector eyeDirA = frameA.ToLocal(frame.ToWorld(localEyeDir)); float directPdfWMatA, reversePdfWMatA; const Spectrum matAResult = matA->Evaluate(hitPointA, lightDirA, eyeDirA, &eventMatA, &directPdfWMatA, &reversePdfWMatA); if (!matAResult.Black()) { result += weight1 * matAResult; if (directPdfW) *directPdfW += weight1 * directPdfWMatA; if (reversePdfW) *reversePdfW += weight1 * reversePdfWMatA; } } BSDFEvent eventMatB = NONE; if (weight2 > 0.f) { HitPoint hitPointB(hitPoint); matB->Bump(&hitPointB); const Frame frameB(hitPointB.GetFrame()); const Vector lightDirB = frameB.ToLocal(frame.ToWorld(localLightDir)); const Vector eyeDirB = frameB.ToLocal(frame.ToWorld(localEyeDir)); float directPdfWMatB, reversePdfWMatB; const Spectrum matBResult = matB->Evaluate(hitPointB, lightDirB, eyeDirB, &eventMatB, &directPdfWMatB, &reversePdfWMatB); if (!matBResult.Black()) { result += weight2 * matBResult; if (directPdfW) *directPdfW += weight2 * directPdfWMatB; if (reversePdfW) *reversePdfW += weight2 * reversePdfWMatB; } } *event = eventMatA | eventMatB; return result; }
bool RayTracer::calculateHitpoint() { HitPoint hp; double lastdist = 999999999; bool hitted = false; //get closest ray target std::vector<GameObject*> * gameObjects = Scene::getInstance()->getGameObjects(); for (std::vector<GameObject*>::iterator it = gameObjects->begin(); it != gameObjects->end(); it++) { if ((*it)->rayCast(ray, hp)) { //save closest hitpoint double dist = (ray.getPoint() - hp.getPoint()).magnitude(); //double dir = (hp.getPoint() - ray.getPoint()).dot(ray.getDirection()); && dir > 0 if (dist < lastdist) { hitpoint = hp; hittingObject = (*it); lastdist = dist; hitted = true; } } } return hitted; }
Colord TransparentMaterial::shade(const Raytracer* raytracer, const Rayd& ray, const HitPoint& hitPoint, State& state) const { Vector3d out = -ray.direction(); Vector3d in; Colord reflectedColor = m_reflectiveBRDF.sample(hitPoint, out, in); Rayd reflected(hitPoint.point(), in); if (m_specularBTDF.totalInternalReflection(ray, hitPoint)) { return raytracer->rayColor(reflected.epsilonShifted(), state); } else { auto color = PhongMaterial::shade(raytracer, ray, hitPoint, state); Vector3d trans; Colord transmittedColor = m_specularBTDF.sample(hitPoint, out, trans); Rayd transmitted(hitPoint.point(), trans); state.recordEvent("TransparentMaterial: Tracing reflection"); color += reflectedColor * raytracer->rayColor(reflected.epsilonShifted(), state) * fabs(hitPoint.normal() * in); state.recordEvent("TransparentMaterial: Tracing transmission"); color += transmittedColor * raytracer->rayColor(transmitted.epsilonShifted(), state) * fabs(hitPoint.normal() * trans); return color; } }
void HitPoints::UpdatePointsInformation() { // Calculate hit points bounding box BBox bbox; float maxr2, minr2, meanr2; u_int minp, maxp, meanp; u_int surfaceHits, constantHits, zeroHits; assert((*hitPoints).size() > 0); HitPoint *hp = &(*hitPoints)[0]; if (hp->IsSurface()) { surfaceHits = 1; constantHits = 0; u_int pc = hp->GetPhotonCount(); zeroHits = pc == 0 ? 1 : 0; bbox = hp->GetPosition(); maxr2 = minr2 = meanr2 = hp->accumPhotonRadius2; minp = maxp = meanp = pc; } else { constantHits = 1; surfaceHits = 0; zeroHits = 0; maxr2 = 0.f; minr2 = INFINITY; meanr2 = 0.f; minp = maxp = meanp = 0; } for (u_int i = 1; i < (*hitPoints).size(); ++i) { hp = &(*hitPoints)[i]; if (hp->IsSurface()) { u_int pc = hp->GetPhotonCount(); if(pc == 0) ++zeroHits; bbox = Union(bbox, hp->GetPosition()); maxr2 = max<float>(maxr2, hp->accumPhotonRadius2); minr2 = min<float>(minr2, hp->accumPhotonRadius2); meanr2 += hp->accumPhotonRadius2; maxp = max<float>(maxp, pc); minp = min<float>(minp, pc); meanp += pc; ++surfaceHits; } else ++constantHits; } LOG(LUX_DEBUG, LUX_NOERROR) << "Hit points stats:"; if (surfaceHits > 0) { LOG(LUX_DEBUG, LUX_NOERROR) << "\tbounding box: " << bbox; LOG(LUX_DEBUG, LUX_NOERROR) << "\tmin/max radius: " << sqrtf(minr2) << "/" << sqrtf(maxr2); LOG(LUX_DEBUG, LUX_NOERROR) << "\tmin/max photonCount: " << minp << "/" << maxp; LOG(LUX_DEBUG, LUX_NOERROR) << "\tmean radius/photonCount: " << sqrtf(meanr2 / surfaceHits) << "/" << meanp / surfaceHits; } LOG(LUX_DEBUG, LUX_NOERROR) << "\tconstant/zero hits: " << constantHits << "/" << zeroHits; hitPointBBox = bbox; maxHitPointRadius2 = maxr2; }