/** * \brief Simple local illumination integration: * We cast a ray from the camera, intersects it with the first element * in the scene it goes through and from there we directly sample the * light's in the scene to compute direct lighting. */ Color3f Li(const Scene *scene, Sampler *sampler, const Ray3f &ray_) const { Ray3f ray(ray_); /* Find the surface that is visible in the requested direction */ Intersection its; if (!scene->rayIntersect(ray, its)) return Color3f(0.0f); const Mesh *mesh = its.mesh; const BSDF *bsdf = mesh->getBSDF(); /// TODO implement direct lighting using light sampling using // sampleLights(const Scene *, LuminaireQueryRecord &, const Point2d &) // which you also have to implement // Pour afficher les sources de lumiere (et eviter les intersections etranges) if(mesh->isLuminaire()) return mesh->getLuminaire()->getColor(); // Sampling des sources de lumiere LuminaireQueryRecord lRec(its.p); Color3f L_lum = sampleLights(scene, lRec, sampler->next2D()); // Calcul de la BRDF (il faut passer dans le repere local) Frame* its_frame = &(its.geoFrame); BSDFQueryRecord bRec(its_frame->toLocal(-ray.d), its_frame->toLocal(lRec.d), ESolidAngle); Color3f fr = bsdf->eval(bRec); // Calcul de cos(theta') float cosTheta = its_frame->cosTheta(its_frame->toLocal(lRec.d)); return fr*L_lum*cosTheta; }
int createGatherPoints(Scene *scene, const RayDifferential &ray, const Point2 &sample, Sampler *sampler, const Spectrum &weight, std::vector<GatherPoint> &gatherPoints, int depth) { int count = 0; if (depth >= m_maxDepth && m_maxDepth != -1) return 0; GatherPoint p; if (scene->rayIntersect(ray, p.its)) { const BSDF *bsdf = p.its.shape->getBSDF(); if (!bsdf) { p.radius = 0; p.sample = sample; gatherPoints.push_back(p); ++count; } else { if (bsdf->getType() & BSDF::ESmooth) { p.weight = weight; p.sample = sample; p.radius = m_initialRadius; p.depth = depth; if (p.its.isLuminaire()) p.emission = p.its.Le(-ray.d); gatherPoints.push_back(p); ++count; } if (bsdf->getType() & BSDF::EDelta) { int compCount = bsdf->getComponentCount(); for (int i=0; i<compCount; i++) { if ((bsdf->getType(i) & BSDF::EDelta) == 0) continue; /* Sample the BSDF and recurse */ BSDFQueryRecord bRec(p.its, sampler); bRec.component = i; Spectrum bsdfVal = bsdf->sample(bRec, Point2(0.0f)); if (bsdfVal.isZero()) continue; bsdfVal = bsdf->eval(bRec, EDiscrete); const Float rrProb = depth < 4 ? 1 : 0.8f; if (sampler->independent1D() < rrProb) { RayDifferential recursiveRay(p.its.p, p.its.toWorld(bRec.wo), ray.time); count += createGatherPoints(scene, recursiveRay, sample, sampler, weight * bsdfVal / rrProb, gatherPoints, depth+1); } } } } } else if (depth == 1) { /* Generate an invalid sample */ p.emission = scene->LeBackground(ray); p.radius = 0; p.sample = sample; gatherPoints.push_back(p); ++count; } return count; }
void CaptureParticleWorker::handleSurfaceInteraction(int depth, int nullInteractions, bool caustic, const Intersection &its, const Medium *medium, const Spectrum &weight) { if (its.isSensor()) { if (!m_bruteForce && !caustic) return; const Sensor *sensor = its.shape->getSensor(); if (sensor != m_sensor) return; Vector wi = its.toWorld(its.wi); Point2 uv; Spectrum value = sensor->eval(its, wi, uv) * weight; if (value.isZero()) return; m_workResult->put(uv, (Float *) &value[0]); return; } if (m_bruteForce || (depth >= m_maxPathDepth && m_maxPathDepth > 0)) return; int maxInteractions = m_maxPathDepth - depth - 1; DirectSamplingRecord dRec(its); Spectrum value = weight * m_scene->sampleAttenuatedSensorDirect( dRec, its, medium, maxInteractions, m_sampler->next2D(), m_sampler); if (value.isZero()) return; const BSDF *bsdf = its.getBSDF(); Vector wo = dRec.d; BSDFSamplingRecord bRec(its, its.toLocal(wo), EImportance); /* Prevent light leaks due to the use of shading normals -- [Veach, p. 158] */ Vector wi = its.toWorld(its.wi); Float wiDotGeoN = dot(its.geoFrame.n, wi), woDotGeoN = dot(its.geoFrame.n, wo); if (wiDotGeoN * Frame::cosTheta(bRec.wi) <= 0 || woDotGeoN * Frame::cosTheta(bRec.wo) <= 0) return; /* Adjoint BSDF for shading normals -- [Veach, p. 155] */ Float correction = std::abs( (Frame::cosTheta(bRec.wi) * woDotGeoN)/ (Frame::cosTheta(bRec.wo) * wiDotGeoN)); value *= bsdf->eval(bRec) * correction; /* Splat onto the accumulation buffer */ m_workResult->put(dRec.uv, (Float *) &value[0]); }
void CaptureParticleWorker::handleSurfaceInteraction(int depth, bool caustic, const Intersection &its, const Medium *medium, const Spectrum &weight) { const ProjectiveCamera *camera = static_cast<const ProjectiveCamera *>(m_camera.get()); Point2 screenSample; if (camera->positionToSample(its.p, screenSample)) { Point cameraPosition = camera->getPosition(screenSample); Float t = dot(camera->getImagePlaneNormal(), its.p-cameraPosition); if (t < camera->getNearClip() || t > camera->getFarClip()) return; if (its.isMediumTransition()) medium = its.getTargetMedium(cameraPosition - its.p); Spectrum transmittance = m_scene->getTransmittance(its.p, cameraPosition, its.time, medium); if (transmittance.isZero()) return; const BSDF *bsdf = its.shape->getBSDF(); Vector wo = cameraPosition - its.p; Float dist = wo.length(); wo /= dist; BSDFQueryRecord bRec(its, its.toLocal(wo)); bRec.quantity = EImportance; Float importance; if (m_isPerspectiveCamera) importance = ((const PerspectiveCamera *) camera)->importance(screenSample) / (dist * dist); else importance = 1/camera->areaDensity(screenSample); Vector wi = its.toWorld(its.wi); /* Prevent light leaks due to the use of shading normals -- [Veach, p. 158] */ Float wiDotGeoN = dot(its.geoFrame.n, wi), woDotGeoN = dot(its.geoFrame.n, wo); if (wiDotGeoN * Frame::cosTheta(bRec.wi) <= 0 || woDotGeoN * Frame::cosTheta(bRec.wo) <= 0) return; /* Adjoint BSDF for shading normals -- [Veach, p. 155] */ Float correction = std::abs( (Frame::cosTheta(bRec.wi) * woDotGeoN)/ (Frame::cosTheta(bRec.wo) * wiDotGeoN)); /* Splat onto the accumulation buffer */ Ray ray(its.p, wo, 0, dist, its.time); Spectrum sampleVal = weight * bsdf->fCos(bRec) * transmittance * (importance * correction); m_workResult->splat(screenSample, sampleVal, m_filter); } }
MTS_NAMESPACE_BEGIN void transmittanceIntegrand(const BSDF *bsdf, const Vector &wi, size_t nPts, const Float *in, Float *out) { Intersection its; for (size_t i=0; i<nPts; ++i) { BSDFSamplingRecord bRec(its, wi, Vector(), EImportance); bRec.typeMask = BSDF::ETransmission; out[i] = bsdf->sample(bRec, Point2(in[2*i], in[2*i+1]))[0]; } }
MTS_NAMESPACE_BEGIN void transmittanceIntegrand(const BSDF *bsdf, const Vector &wi, size_t nPts, const Float *in, Float *out) { Intersection its; #pragma omp parallel for for (int i=0; i<(int) nPts; ++i) { BSDFQueryRecord bRec(its, wi, Vector(), EImportance); bRec.typeMask = BSDF::ETransmission; Point2 sample(in[2*i], in[2*i+1]); if (sample.x == 1) sample.x = 1-Epsilon; if (sample.y == 1) sample.y = 1-Epsilon; out[i] = bsdf->sample(bRec, sample)[0]; if (mts_isnan(out[i])) SLog(EError, "%s\n\nNaN!", bRec.toString().c_str()); } }
void PreviewWorker::processIncoherent(const WorkUnit *workUnit, WorkResult *workResult, const bool &stop) { const RectangularWorkUnit *rect = static_cast<const RectangularWorkUnit *>(workUnit); ImageBlock *block = static_cast<ImageBlock *>(workResult); block->setOffset(rect->getOffset()); block->setSize(rect->getSize()); const int sx = rect->getOffset().x, sy = block->getOffset().y; const int ex = sx + rect->getSize().x, ey = sy + rect->getSize().y; /* Some local variables */ int pos = 0; Intersection its; Spectrum value, bsdfVal; Vector toVPL; Ray primary, secondary; int numRays = 0; float shutterOpen = m_scene->getCamera()->getShutterOpen(); for (int y=sy; y<ey; ++y) { for (int x=sx; x<ex; ++x) { /* Generate a camera ray without normalization */ primary = Ray(m_cameraO, m_cameraTL + m_cameraDx * (Float) x + m_cameraDy * (Float) y, shutterOpen); ++numRays; if (!m_kdtree->rayIntersect(primary, its)) { block->setPixel(pos++, m_scene->LeBackground(primary)*m_backgroundScale); continue; } if (its.shape->isLuminaire()) value = its.Le(-primary.d); else value = Spectrum(0.0f); toVPL = m_vpl.its.p - its.p; secondary = Ray(its.p, toVPL, ShadowEpsilon, 1-ShadowEpsilon, shutterOpen); ++numRays; if (m_kdtree->rayIntersect(secondary)) { block->setPixel(pos++, value); continue; } Float length = toVPL.length(); toVPL/=length; BSDFQueryRecord rr(its, its.toLocal(toVPL)); rr.wi = normalize(rr.wi); bsdfVal = its.shape->getBSDF()->eval(rr); length = std::max(length, m_minDist); if (m_vpl.type == ESurfaceVPL) { BSDFQueryRecord bRec(m_vpl.its, -m_vpl.its.toLocal(toVPL)); bRec.quantity = EImportance; value += m_vpl.P * bsdfVal * m_vpl.its.shape->getBSDF()->eval(bRec) / (length*length); } else { EmissionRecord eRec(m_vpl.luminaire, ShapeSamplingRecord(m_vpl.its.p, m_vpl.its.shFrame.n), -toVPL); eRec.type = EmissionRecord::EPreview; value += m_vpl.P * bsdfVal * m_vpl.luminaire->evalDirection(eRec) * ((m_vpl.luminaire->getType() & Luminaire::EOnSurface ? dot(m_vpl.its.shFrame.n, -toVPL) : (Float) 1) / (length*length)); } block->setPixel(pos++, value); } } block->setExtra(numRays); }
void PreviewWorker::processCoherent(const WorkUnit *workUnit, WorkResult *workResult, const bool &stop) { #if defined(MTS_HAS_COHERENT_RT) const RectangularWorkUnit *rect = static_cast<const RectangularWorkUnit *>(workUnit); ImageBlock *block = static_cast<ImageBlock *>(workResult); block->setOffset(rect->getOffset()); block->setSize(rect->getSize()); /* Some constants */ const int sx = rect->getOffset().x, sy = block->getOffset().y; const int ex = sx + rect->getSize().x, ey = sy + rect->getSize().y; const int width = rect->getSize().x; const SSEVector MM_ALIGN16 xOffset(0.0f, 1.0f, 0.0f, 1.0f); const SSEVector MM_ALIGN16 yOffset(0.0f, 0.0f, 1.0f, 1.0f); const int pixelOffset[] = {0, 1, width, width+1}; const __m128 clamping = _mm_set1_ps(1/(m_minDist*m_minDist)); uint8_t temp[MTS_KD_INTERSECTION_TEMP*4]; const __m128 camTL[3] = { _mm_set1_ps(m_cameraTL.x), _mm_set1_ps(m_cameraTL.y), _mm_set1_ps(m_cameraTL.z) }; const __m128 camDx[3] = { _mm_set1_ps(m_cameraDx.x), _mm_set1_ps(m_cameraDx.y), _mm_set1_ps(m_cameraDx.z) }; const __m128 camDy[3] = { _mm_set1_ps(m_cameraDy.x), _mm_set1_ps(m_cameraDy.y), _mm_set1_ps(m_cameraDy.z) }; const __m128 lumPos[3] = { _mm_set1_ps(m_vpl.its.p.x), _mm_set1_ps(m_vpl.its.p.y), _mm_set1_ps(m_vpl.its.p.z) }; const __m128 lumDir[3] = { _mm_set1_ps(m_vpl.its.shFrame.n.x), _mm_set1_ps(m_vpl.its.shFrame.n.y), _mm_set1_ps(m_vpl.its.shFrame.n.z) }; /* Some local variables */ int pos = 0; int numRays = 0; RayPacket4 MM_ALIGN16 primRay4, secRay4; Intersection4 MM_ALIGN16 its4, secIts4; RayInterval4 MM_ALIGN16 itv4, secItv4; SSEVector MM_ALIGN16 nSecD[3], cosThetaLight, invLengthSquared; Spectrum emitted[4], direct[4]; Intersection its; Vector wo, wi; its.hasUVPartials = false; bool diffuseVPL = false, vplOnSurface = false; Spectrum vplWeight; if (m_vpl.type == ESurfaceVPL && (m_diffuseSources || m_vpl.its.shape->getBSDF()->getType() == BSDF::EDiffuseReflection)) { diffuseVPL = true; vplOnSurface = true; vplWeight = m_vpl.its.shape->getBSDF()->getDiffuseReflectance(m_vpl.its) * m_vpl.P / M_PI; } else if (m_vpl.type == ELuminaireVPL) { vplOnSurface = m_vpl.luminaire->getType() & Luminaire::EOnSurface; diffuseVPL = m_vpl.luminaire->getType() & Luminaire::EDiffuseDirection; EmissionRecord eRec(m_vpl.luminaire, ShapeSamplingRecord(m_vpl.its.p, m_vpl.its.shFrame.n), m_vpl.its.shFrame.n); vplWeight = m_vpl.P * m_vpl.luminaire->evalDirection(eRec); } primRay4.o[0].ps = _mm_set1_ps(m_cameraO.x); primRay4.o[1].ps = _mm_set1_ps(m_cameraO.y); primRay4.o[2].ps = _mm_set1_ps(m_cameraO.z); secItv4.mint.ps = _mm_set1_ps(ShadowEpsilon); /* Work on 2x2 sub-blocks */ for (int y=sy; y<ey; y += 2, pos += width) { for (int x=sx; x<ex; x += 2, pos += 2) { /* Generate camera rays without normalization */ const __m128 xPixel = _mm_add_ps(xOffset.ps, _mm_set1_ps((float) x)), yPixel = _mm_add_ps(yOffset.ps, _mm_set1_ps((float) y)); primRay4.d[0].ps = _mm_add_ps(camTL[0], _mm_add_ps( _mm_mul_ps(xPixel, camDx[0]), _mm_mul_ps(yPixel, camDy[0]))); primRay4.d[1].ps = _mm_add_ps(camTL[1], _mm_add_ps( _mm_mul_ps(xPixel, camDx[1]), _mm_mul_ps(yPixel, camDy[1]))); primRay4.d[2].ps = _mm_add_ps(camTL[2], _mm_add_ps( _mm_mul_ps(xPixel, camDx[2]), _mm_mul_ps(yPixel, camDy[2]))); primRay4.dRcp[0].ps = _mm_div_ps(SSEConstants::one.ps, primRay4.d[0].ps); primRay4.dRcp[1].ps = _mm_div_ps(SSEConstants::one.ps, primRay4.d[1].ps); primRay4.dRcp[2].ps = _mm_div_ps(SSEConstants::one.ps, primRay4.d[2].ps); /* Ray coherence test */ const int primSignsX = _mm_movemask_ps(primRay4.d[0].ps); const int primSignsY = _mm_movemask_ps(primRay4.d[1].ps); const int primSignsZ = _mm_movemask_ps(primRay4.d[2].ps); const bool primCoherent = (primSignsX == 0 || primSignsX == 0xF) && (primSignsY == 0 || primSignsY == 0xF) && (primSignsZ == 0 || primSignsZ == 0xF); /* Trace the primary rays */ its4.t = SSEConstants::p_inf; if (EXPECT_TAKEN(primCoherent)) { primRay4.signs[0][0] = primSignsX ? 1 : 0; primRay4.signs[1][0] = primSignsY ? 1 : 0; primRay4.signs[2][0] = primSignsZ ? 1 : 0; m_kdtree->rayIntersectPacket(primRay4, itv4, its4, temp); } else { m_kdtree->rayIntersectPacketIncoherent(primRay4, itv4, its4, temp); } numRays += 4; /* Generate secondary rays */ secRay4.o[0].ps = _mm_add_ps(primRay4.o[0].ps, _mm_mul_ps(its4.t.ps, primRay4.d[0].ps)); secRay4.o[1].ps = _mm_add_ps(primRay4.o[1].ps, _mm_mul_ps(its4.t.ps, primRay4.d[1].ps)); secRay4.o[2].ps = _mm_add_ps(primRay4.o[2].ps, _mm_mul_ps(its4.t.ps, primRay4.d[2].ps)); secRay4.d[0].ps = _mm_sub_ps(lumPos[0], secRay4.o[0].ps); secRay4.d[1].ps = _mm_sub_ps(lumPos[1], secRay4.o[1].ps); secRay4.d[2].ps = _mm_sub_ps(lumPos[2], secRay4.o[2].ps); /* Normalization */ const __m128 lengthSquared = _mm_add_ps(_mm_add_ps( _mm_mul_ps(secRay4.d[0].ps, secRay4.d[0].ps), _mm_mul_ps(secRay4.d[1].ps, secRay4.d[1].ps)), _mm_mul_ps(secRay4.d[2].ps, secRay4.d[2].ps)), invLength = _mm_rsqrt_ps(lengthSquared); invLengthSquared.ps = _mm_min_ps(_mm_rcp_ps(lengthSquared), clamping); nSecD[0].ps = _mm_mul_ps(secRay4.d[0].ps, invLength); nSecD[1].ps = _mm_mul_ps(secRay4.d[1].ps, invLength); nSecD[2].ps = _mm_mul_ps(secRay4.d[2].ps, invLength); secRay4.dRcp[0].ps = _mm_div_ps(SSEConstants::one.ps, secRay4.d[0].ps); secRay4.dRcp[1].ps = _mm_div_ps(SSEConstants::one.ps, secRay4.d[1].ps); secRay4.dRcp[2].ps = _mm_div_ps(SSEConstants::one.ps, secRay4.d[2].ps); cosThetaLight.ps = _mm_sub_ps(_mm_setzero_ps(), _mm_add_ps(_mm_add_ps( _mm_mul_ps(nSecD[0].ps, lumDir[0]), _mm_mul_ps(nSecD[1].ps, lumDir[1])), _mm_mul_ps(nSecD[2].ps, lumDir[2]))); secItv4.maxt.ps = _mm_set1_ps(1-ShadowEpsilon); /* Shading (scalar) --- this is way too much work and should be rewritten to be smarter in special cases */ for (int idx=0; idx<4; ++idx) { if (EXPECT_NOT_TAKEN(its4.t.f[idx] == std::numeric_limits<float>::infinity())) { /* Don't trace a secondary ray */ secItv4.maxt.f[idx] = 0; emitted[idx] = m_scene->LeBackground(Ray( Point(primRay4.o[0].f[idx], primRay4.o[1].f[idx], primRay4.o[2].f[idx]), Vector(primRay4.d[0].f[idx], primRay4.d[1].f[idx], primRay4.d[2].f[idx]), 0.0f )) * m_backgroundScale; memset(&direct[idx], 0, sizeof(Spectrum)); continue; } const unsigned int primIndex = its4.primIndex.i[idx]; const Shape *shape = (*m_shapes)[its4.shapeIndex.i[idx]]; const BSDF *bsdf = shape->getBSDF(); if (EXPECT_NOT_TAKEN(!bsdf)) { memset(&emitted[idx], 0, sizeof(Spectrum)); memset(&direct[idx], 0, sizeof(Spectrum)); continue; } if (EXPECT_TAKEN(primIndex != KNoTriangleFlag)) { const TriMesh *mesh = static_cast<const TriMesh *>(shape); const Triangle &t = mesh->getTriangles()[primIndex]; const Normal *normals = mesh->getVertexNormals(); const Point2 *texcoords = mesh->getVertexTexcoords(); const Spectrum *colors = mesh->getVertexColors(); const TangentSpace * tangents = mesh->getVertexTangents(); const Float beta = its4.u.f[idx], gamma = its4.v.f[idx], alpha = 1.0f - beta - gamma; const uint32_t idx0 = t.idx[0], idx1 = t.idx[1], idx2 = t.idx[2]; if (EXPECT_TAKEN(normals)) { const Normal &n0 = normals[idx0], &n1 = normals[idx1], &n2 = normals[idx2]; its.shFrame.n = normalize(n0 * alpha + n1 * beta + n2 * gamma); } else { const Point *positions = mesh->getVertexPositions(); const Point &p0 = positions[idx0], &p1 = positions[idx1], &p2 = positions[idx2]; Vector sideA = p1 - p0, sideB = p2 - p0; Vector n = cross(sideA, sideB); Float nLengthSqr = n.lengthSquared(); if (nLengthSqr != 0) n /= std::sqrt(nLengthSqr); its.shFrame.n = Normal(n); } if (EXPECT_TAKEN(texcoords)) { const Point2 &t0 = texcoords[idx0], &t1 = texcoords[idx1], &t2 = texcoords[idx2]; its.uv = t0 * alpha + t1 * beta + t2 * gamma; } else { its.uv = Point2(0.0f); } if (EXPECT_NOT_TAKEN(colors)) { const Spectrum &c0 = colors[idx0], &c1 = colors[idx1], &c2 = colors[idx2]; its.color = c0 * alpha + c1 * beta + c2 * gamma; } if (EXPECT_NOT_TAKEN(tangents)) { const TangentSpace &t0 = tangents[idx0], &t1 = tangents[idx1], &t2 = tangents[idx2]; its.dpdu = t0.dpdu * alpha + t1.dpdu * beta + t2.dpdu * gamma; its.dpdv = t0.dpdv * alpha + t1.dpdv * beta + t2.dpdv * gamma; } } else { Ray ray( Point(primRay4.o[0].f[idx], primRay4.o[1].f[idx], primRay4.o[2].f[idx]), Vector(primRay4.d[0].f[idx], primRay4.d[1].f[idx], primRay4.d[2].f[idx]), 0.0f ); its.t = its4.t.f[idx]; shape->fillIntersectionRecord(ray, temp + idx * MTS_KD_INTERSECTION_TEMP + 8, its); bsdf = its.shape->getBSDF(); } wo.x = nSecD[0].f[idx]; wo.y = nSecD[1].f[idx]; wo.z = nSecD[2].f[idx]; if (EXPECT_TAKEN(!shape->isLuminaire())) { memset(&emitted[idx], 0, sizeof(Spectrum)); } else { Vector d(-primRay4.d[0].f[idx], -primRay4.d[1].f[idx], -primRay4.d[2].f[idx]); emitted[idx] = shape->getLuminaire()->Le(ShapeSamplingRecord(its.p, its.shFrame.n), d); } if (EXPECT_TAKEN(bsdf->getType() == BSDF::EDiffuseReflection && diffuseVPL)) { /* Fast path */ direct[idx] = (bsdf->getDiffuseReflectance(its) * vplWeight) * (std::max((Float) 0.0f, dot(wo, its.shFrame.n)) * (vplOnSurface ? (std::max(cosThetaLight.f[idx], (Float) 0.0f) * INV_PI) : INV_PI) * invLengthSquared.f[idx]); } else { wi.x = -primRay4.d[0].f[idx]; wi.y = -primRay4.d[1].f[idx]; wi.z = -primRay4.d[2].f[idx]; its.p.x = secRay4.o[0].f[idx]; its.p.y = secRay4.o[1].f[idx]; its.p.z = secRay4.o[2].f[idx]; if (EXPECT_NOT_TAKEN(bsdf->getType() & BSDF::EAnisotropic)) { its.shFrame.s = normalize(its.dpdu - its.shFrame.n * dot(its.shFrame.n, its.dpdu)); its.shFrame.t = cross(its.shFrame.n, its.shFrame.s); } else { coordinateSystem(its.shFrame.n, its.shFrame.s, its.shFrame.t); } const Float ctLight = cosThetaLight.f[idx]; wi = normalize(wi); its.wi = its.toLocal(wi); wo = its.toLocal(wo); if (!diffuseVPL) { if (m_vpl.type == ESurfaceVPL) { BSDFQueryRecord bRec(m_vpl.its, m_vpl.its.toLocal(wi)); bRec.quantity = EImportance; vplWeight = m_vpl.its.shape->getBSDF()->eval(bRec) * m_vpl.P; } else { EmissionRecord eRec(m_vpl.luminaire, ShapeSamplingRecord(m_vpl.its.p, m_vpl.its.shFrame.n), wi); eRec.type = EmissionRecord::EPreview; vplWeight = m_vpl.luminaire->evalDirection(eRec) * m_vpl.P; } } if (EXPECT_TAKEN(ctLight >= 0)) { direct[idx] = (bsdf->eval(BSDFQueryRecord(its, wo)) * vplWeight * ((vplOnSurface ? std::max(ctLight, (Float) 0.0f) : 1.0f) * invLengthSquared.f[idx])); } else { memset(&direct[idx], 0, sizeof(Spectrum)); } } ++numRays; } /* Shoot the secondary rays */ const int secSignsX = _mm_movemask_ps(secRay4.d[0].ps); const int secSignsY = _mm_movemask_ps(secRay4.d[1].ps); const int secSignsZ = _mm_movemask_ps(secRay4.d[2].ps); const bool secCoherent = (secSignsX == 0 || secSignsX == 0xF) && (secSignsY == 0 || secSignsY == 0xF) && (secSignsZ == 0 || secSignsZ == 0xF); /* Shoot the secondary rays */ secIts4.t = SSEConstants::p_inf; if (EXPECT_TAKEN(secCoherent)) { secRay4.signs[0][0] = secSignsX ? 1 : 0; secRay4.signs[1][0] = secSignsY ? 1 : 0; secRay4.signs[2][0] = secSignsZ ? 1 : 0; m_kdtree->rayIntersectPacket(secRay4, secItv4, secIts4, temp); } else { m_kdtree->rayIntersectPacketIncoherent(secRay4, secItv4, secIts4, temp); } for (int idx=0; idx<4; ++idx) { if (EXPECT_TAKEN(secIts4.t.f[idx] == std::numeric_limits<float>::infinity())) block->setPixel(pos+pixelOffset[idx], direct[idx]+emitted[idx]); else block->setPixel(pos+pixelOffset[idx], emitted[idx]); } } } block->setExtra(numRays); #else Log(EError, "Coherent raytracing support was not compiled into this binary!"); #endif }
void distributedRTPass(Scene *scene, std::vector<SerializableObject *> &samplers) { ref<Camera> camera = scene->getCamera(); bool needsLensSample = camera->needsLensSample(); bool needsTimeSample = camera->needsTimeSample(); ref<Film> film = camera->getFilm(); Vector2i cropSize = film->getCropSize(); Point2i cropOffset = film->getCropOffset(); /* Process the image in parallel using blocks for better memory locality */ Log(EInfo, "Creating %i gather points", cropSize.x*cropSize.y); #pragma omp parallel for schedule(dynamic) for (int i=-1; i<(int) m_gatherBlocks.size(); ++i) { std::vector<GatherPoint> &gatherPoints = m_gatherBlocks[i]; #if !defined(__OSX__) && defined(_OPENMP) Sampler *sampler = static_cast<Sampler *>(samplers[omp_get_thread_num()]); #else Sampler *sampler = static_cast<Sampler *>(samplers[0]); #endif int xofs = m_offset[i].x, yofs = m_offset[i].y; int index = 0; for (int yofsInt = 0; yofsInt < m_blockSize; ++yofsInt) { if (yofsInt + yofs - cropOffset.y >= cropSize.y) continue; for (int xofsInt = 0; xofsInt < m_blockSize; ++xofsInt) { if (xofsInt + xofs - cropOffset.x >= cropSize.x) continue; Point2 lensSample, sample; Float timeSample = 0.0f; GatherPoint &gatherPoint = gatherPoints[index++]; sampler->generate(); if (needsLensSample) lensSample = sampler->next2D(); if (needsTimeSample) timeSample = sampler->next1D(); gatherPoint.pos = Point2i(xofs + xofsInt, yofs + yofsInt); sample = sampler->next2D(); sample += Vector2((Float) gatherPoint.pos.x, (Float) gatherPoint.pos.y); RayDifferential ray; camera->generateRayDifferential(sample, lensSample, timeSample, ray); Spectrum weight(1.0f); int depth = 1; while (true) { if (depth > m_maxDepth) { gatherPoint.depth = -1; break; } if (scene->rayIntersect(ray, gatherPoint.its)) { const BSDF *bsdf = gatherPoint.its.shape->getBSDF(); /* Create hit point if this is a diffuse material or a glossy one, and there has been a previous interaction with a glossy material */ if (bsdf->getType() == BSDF::EDiffuseReflection || bsdf->getType() == BSDF::EDiffuseTransmission) { gatherPoint.weight = weight; gatherPoint.depth = depth; if (gatherPoint.its.isLuminaire()) gatherPoint.emission = gatherPoint.its.Le(-ray.d); else gatherPoint.emission = Spectrum(0.0f); break; } else { /* Recurse for dielectric materials and (specific to SPPM): recursive "final gathering" for glossy materials */ BSDFQueryRecord bRec(gatherPoint.its); weight *= bsdf->sampleCos(bRec, sampler->next2D()); if (weight.isZero()) { gatherPoint.depth = -1; break; } ray = RayDifferential(gatherPoint.its.p, gatherPoint.its.toWorld(bRec.wo), ray.time); ++depth; } } else { /* Generate an invalid sample */ gatherPoint.depth = -1; break; } } sampler->advance(); } } } }
bool PathEdge::pathConnect(const Scene *scene, const PathEdge *predEdge, const PathVertex *vs, Path &result, const PathVertex *vt, const PathEdge *succEdge, int maxInteractions, MemoryPool &pool) { BDAssert(result.edgeCount() == 0 && result.vertexCount() == 0); if (vs->isEmitterSupernode() || vt->isSensorSupernode()) { Float radianceTransport = vt->isSensorSupernode() ? 1.0f : 0.0f, importanceTransport = 1-radianceTransport; PathEdge *edge = pool.allocEdge(); edge->medium = NULL; edge->length = 0.0f; edge->d = Vector(0.0f); edge->pdf[ERadiance] = radianceTransport; edge->pdf[EImportance] = importanceTransport; edge->weight[ERadiance] = Spectrum(radianceTransport); edge->weight[EImportance] = Spectrum(importanceTransport); result.append(edge); } else { Point vsp = vs->getPosition(), vtp = vt->getPosition(); Vector d(vsp-vtp); Float remaining = d.length(); d /= remaining; if (remaining == 0) { #if defined(MTS_BD_DEBUG) SLog(EWarn, "Tried to connect %s and %s, which are located at exactly the same position!", vs->toString().c_str(), vt->toString().c_str()); #endif return false; } Float lengthFactor = vs->isOnSurface() ? (1-ShadowEpsilon) : 1; Ray ray(vtp, d, vt->isOnSurface() ? Epsilon : 0, remaining * lengthFactor, vs->getTime()); const Medium *medium = vt->getTargetMedium(succEdge, d); int interactions = 0; Intersection its; while (true) { bool surface = scene->rayIntersectAll(ray, its); if (surface && (interactions == maxInteractions || !(its.getBSDF()->getType() & BSDF::ENull))) { /* Encountered an occluder -- zero transmittance. */ result.release(pool); return false; } /* Construct an edge */ PathEdge *edge = pool.allocEdge(); result.append(edge); edge->length = std::min(its.t, remaining); edge->medium = medium; edge->d = d; if (medium) { MediumSamplingRecord mRec; medium->eval(Ray(ray, 0, edge->length), mRec); edge->pdf[ERadiance] = (surface || !vs->isMediumInteraction()) ? mRec.pdfFailure : mRec.pdfSuccess; edge->pdf[EImportance] = (interactions > 0 || !vt->isMediumInteraction()) ? mRec.pdfFailure : mRec.pdfSuccessRev; if (edge->pdf[ERadiance] == 0 || edge->pdf[EImportance] == 0 || mRec.transmittance.isZero()) { /* Zero transmittance */ result.release(pool); return false; } edge->weight[EImportance] = mRec.transmittance / edge->pdf[EImportance]; edge->weight[ERadiance] = mRec.transmittance / edge->pdf[ERadiance]; } else { edge->weight[ERadiance] = edge->weight[EImportance] = Spectrum(1.0f); edge->pdf[ERadiance] = edge->pdf[EImportance] = 1.0f; } if (!surface || remaining - its.t < 0) break; /* Advance the ray */ ray.o = ray(its.t); remaining -= its.t; ray.mint = Epsilon; ray.maxt = remaining * lengthFactor; const BSDF *bsdf = its.getBSDF(); /* Account for the ENull interaction */ Vector wo = its.toLocal(ray.d); BSDFSamplingRecord bRec(its, -wo, wo, ERadiance); bRec.component = BSDF::ENull; Float nullPdf = bsdf->pdf(bRec, EDiscrete); if (nullPdf == 0) { result.release(pool); return false; } PathVertex *vertex = pool.allocVertex(); vertex->type = PathVertex::ESurfaceInteraction; vertex->degenerate = !(bsdf->hasComponent(BSDF::ESmooth) || its.shape->isEmitter() || its.shape->isSensor()); vertex->measure = EDiscrete; vertex->componentType = BSDF::ENull; vertex->pdf[EImportance] = vertex->pdf[ERadiance] = nullPdf; vertex->weight[EImportance] = vertex->weight[ERadiance] = bsdf->eval(bRec, EDiscrete) / nullPdf; vertex->rrWeight = 1.0f; vertex->getIntersection() = its; result.append(vertex); if (its.isMediumTransition()) { const Medium *expected = its.getTargetMedium(-ray.d); if (medium != expected) { #if defined(MTS_BD_TRACE) SLog(EWarn, "PathEdge::pathConnect(): attempted two connect " "two vertices that disagree about the medium in between! " "Please check your scene for leaks."); #endif ++mediumInconsistencies; result.release(pool); return false; } medium = its.getTargetMedium(ray.d); } if (++interactions > 100) { /// Just a precaution.. SLog(EWarn, "pathConnect(): round-off error issues?"); result.release(pool); return false; } } if (medium != vs->getTargetMedium(predEdge, -d)) { #if defined(MTS_BD_TRACE) SLog(EWarn, "PathEdge::pathConnect(): attempted two connect " "two vertices that disagree about the medium in between! " "Please check your scene for leaks."); #endif ++mediumInconsistencies; result.release(pool); return false; } } result.reverse(); BDAssert(result.edgeCount() == result.vertexCount() + 1); BDAssert((int) result.vertexCount() <= maxInteractions || maxInteractions < 0); return true; }
Spectrum Li(const RayDifferential &ray, RadianceQueryRecord &rRec) const { Spectrum LiSurf(0.0f), LiMedium(0.0f), transmittance(1.0f); Intersection &its = rRec.its; const Scene *scene = rRec.scene; bool cacheQuery = (rRec.extra & RadianceQueryRecord::ECacheQuery); bool adaptiveQuery = (rRec.extra & RadianceQueryRecord::EAdaptiveQuery); /* Perform the first ray intersection (or ignore if the intersection has already been provided). */ rRec.rayIntersect(ray); if (rRec.medium) { Ray mediumRaySegment(ray, 0, its.t); transmittance = rRec.medium->evalTransmittance(mediumRaySegment); mediumRaySegment.mint = ray.mint; if (rRec.type & RadianceQueryRecord::EVolumeRadiance && (rRec.depth < m_maxDepth || m_maxDepth < 0) && m_bre.get() != NULL) LiMedium = m_bre->query(mediumRaySegment, rRec.medium); } if (!its.isValid()) { /* If no intersection could be found, possibly return attenuated radiance from a background luminaire */ if ((rRec.type & RadianceQueryRecord::EEmittedRadiance) && !m_hideEmitters) LiSurf = scene->evalEnvironment(ray); return LiSurf * transmittance + LiMedium; } /* Possibly include emitted radiance if requested */ if (its.isEmitter() && (rRec.type & RadianceQueryRecord::EEmittedRadiance) && !m_hideEmitters) LiSurf += its.Le(-ray.d); /* Include radiance from a subsurface scattering model if requested */ if (its.hasSubsurface() && (rRec.type & RadianceQueryRecord::ESubsurfaceRadiance)) LiSurf += its.LoSub(scene, rRec.sampler, -ray.d, rRec.depth); const BSDF *bsdf = its.getBSDF(ray); if (rRec.depth >= m_maxDepth && m_maxDepth > 0) return LiSurf * transmittance + LiMedium; unsigned int bsdfType = bsdf->getType() & BSDF::EAll; /* Irradiance cache query -> treat as diffuse */ bool isDiffuse = (bsdfType == BSDF::EDiffuseReflection) || cacheQuery; bool hasSpecular = bsdfType & BSDF::EDelta; /* Exhaustively recurse into all specular lobes? */ bool exhaustiveSpecular = rRec.depth < m_maxSpecularDepth && !cacheQuery; if (isDiffuse && (dot(its.shFrame.n, ray.d) < 0 || (bsdf->getType() & BSDF::EBackSide))) { /* 1. Diffuse indirect */ int maxDepth = m_maxDepth == -1 ? INT_MAX : (m_maxDepth-rRec.depth); if (rRec.type & RadianceQueryRecord::EIndirectSurfaceRadiance && m_globalPhotonMap.get()) LiSurf += m_globalPhotonMap->estimateIrradiance(its.p, its.shFrame.n, m_globalLookupRadius, maxDepth, m_globalLookupSize) * bsdf->getDiffuseReflectance(its) * INV_PI; if (rRec.type & RadianceQueryRecord::ECausticRadiance && m_causticPhotonMap.get()) LiSurf += m_causticPhotonMap->estimateIrradiance(its.p, its.shFrame.n, m_causticLookupRadius, maxDepth, m_causticLookupSize) * bsdf->getDiffuseReflectance(its) * INV_PI; } if (hasSpecular && exhaustiveSpecular && (rRec.type & RadianceQueryRecord::EIndirectSurfaceRadiance)) { /* 1. Specular indirect */ int compCount = bsdf->getComponentCount(); RadianceQueryRecord rRec2; for (int i=0; i<compCount; i++) { unsigned int type = bsdf->getType(i); if (!(type & BSDF::EDelta)) continue; /* Sample the BSDF and recurse */ BSDFSamplingRecord bRec(its, rRec.sampler, ERadiance); bRec.component = i; Spectrum bsdfVal = bsdf->sample(bRec, Point2(0.5f)); if (bsdfVal.isZero()) continue; rRec2.recursiveQuery(rRec, RadianceQueryRecord::ERadiance); RayDifferential bsdfRay(its.p, its.toWorld(bRec.wo), ray.time); if (its.isMediumTransition()) rRec2.medium = its.getTargetMedium(bsdfRay.d); LiSurf += bsdfVal * m_parentIntegrator->Li(bsdfRay, rRec2); } } /* Estimate the direct illumination if this is requested */ int numEmitterSamples = m_directSamples, numBSDFSamples; Float weightLum, weightBSDF; Point2 *sampleArray; Point2 sample; if (rRec.depth > 1 || cacheQuery || adaptiveQuery) { /* This integrator is used recursively by another integrator. Be less accurate as this sample will not directly be observed. */ numBSDFSamples = numEmitterSamples = 1; weightLum = weightBSDF = 1.0f; } else { if (isDiffuse) { numBSDFSamples = m_directSamples; weightBSDF = weightLum = m_invEmitterSamples; } else { numBSDFSamples = m_glossySamples; weightLum = m_invEmitterSamples; weightBSDF = m_invGlossySamples; } } if ((bsdfType & BSDF::ESmooth) && (rRec.type & RadianceQueryRecord::EDirectSurfaceRadiance)) { DirectSamplingRecord dRec(its); if (numEmitterSamples > 1) { sampleArray = rRec.sampler->next2DArray(m_directSamples); } else { sample = rRec.nextSample2D(); sampleArray = &sample; } for (int i=0; i<numEmitterSamples; ++i) { int interactions = m_maxDepth - rRec.depth - 1; Spectrum value = scene->sampleAttenuatedEmitterDirect( dRec, its, rRec.medium, interactions, sampleArray[i], rRec.sampler); /* Estimate the direct illumination if this is requested */ if (!value.isZero()) { const Emitter *emitter = static_cast<const Emitter *>(dRec.object); /* Allocate a record for querying the BSDF */ BSDFSamplingRecord bRec(its, its.toLocal(dRec.d)); /* Evaluate BSDF * cos(theta) */ const Spectrum bsdfVal = bsdf->eval(bRec); if (!bsdfVal.isZero()) { /* Calculate prob. of having sampled that direction using BSDF sampling */ if (!hasSpecular || exhaustiveSpecular) bRec.typeMask = BSDF::ESmooth; Float bsdfPdf = (emitter->isOnSurface() && dRec.measure == ESolidAngle && interactions == 0) ? bsdf->pdf(bRec) : (Float) 0.0f; /* Weight using the power heuristic */ const Float weight = miWeight(dRec.pdf * numEmitterSamples, bsdfPdf * numBSDFSamples) * weightLum; LiSurf += value * bsdfVal * weight; } } } } /* ==================================================================== */ /* BSDF sampling */ /* ==================================================================== */ /* Sample direct compontent via BSDF sampling if this is generally requested AND the BSDF is smooth, or there is a delta component that was not handled by the exhaustive sampling loop above */ bool bsdfSampleDirect = (rRec.type & RadianceQueryRecord::EDirectSurfaceRadiance) && ((bsdfType & BSDF::ESmooth) || (hasSpecular && !exhaustiveSpecular)); /* Sample indirect component via BSDF sampling if this is generally requested AND the BSDF is non-diffuse (diffuse is handled by the global photon map) or there is a delta component that was not handled by the exhaustive sampling loop above. */ bool bsdfSampleIndirect = (rRec.type & RadianceQueryRecord::EIndirectSurfaceRadiance) && !isDiffuse && ((bsdfType & BSDF::ESmooth) || (hasSpecular && !exhaustiveSpecular)); if (bsdfSampleDirect || bsdfSampleIndirect) { if (numBSDFSamples > 1) { sampleArray = rRec.sampler->next2DArray( std::max(m_directSamples, m_glossySamples)); } else { sample = rRec.nextSample2D(); sampleArray = &sample; } RadianceQueryRecord rRec2; Intersection &bsdfIts = rRec2.its; DirectSamplingRecord dRec(its); for (int i=0; i<numBSDFSamples; ++i) { /* Sample BSDF * cos(theta) */ BSDFSamplingRecord bRec(its, rRec.sampler, ERadiance); if (!hasSpecular || exhaustiveSpecular) bRec.typeMask = BSDF::ESmooth; Float bsdfPdf; Spectrum bsdfVal = bsdf->sample(bRec, bsdfPdf, sampleArray[i]); if (bsdfVal.isZero()) continue; /* Trace a ray in this direction */ RayDifferential bsdfRay(its.p, its.toWorld(bRec.wo), ray.time); Spectrum value; bool hitEmitter = false; if (scene->rayIntersect(bsdfRay, bsdfIts)) { /* Intersected something - check if it was a luminaire */ if (bsdfIts.isEmitter() && bsdfSampleDirect) { value = bsdfIts.Le(-bsdfRay.d); dRec.setQuery(bsdfRay, bsdfIts); hitEmitter = true; } } else if (bsdfSampleDirect) { /* Intersected nothing -- perhaps there is an environment map? */ const Emitter *env = scene->getEnvironmentEmitter(); if (env) { value = env->evalEnvironment(bsdfRay); if (env->fillDirectSamplingRecord(dRec, bsdfRay)) hitEmitter = true; } } if (hitEmitter) { const Float emitterPdf = scene->pdfEmitterDirect(dRec); Spectrum transmittance = rRec2.medium ? rRec2.medium->evalTransmittance(Ray(bsdfRay, 0, bsdfIts.t)) : Spectrum(1.0f); const Float weight = miWeight(bsdfPdf * numBSDFSamples, emitterPdf * numEmitterSamples) * weightBSDF; LiSurf += value * bsdfVal * weight * transmittance; } /* Recurse */ if (bsdfSampleIndirect) { rRec2.recursiveQuery(rRec, RadianceQueryRecord::ERadianceNoEmission); rRec2.type ^= RadianceQueryRecord::EIntersection; if (its.isMediumTransition()) rRec2.medium = its.getTargetMedium(bsdfRay.d); LiSurf += bsdfVal * m_parentIntegrator->Li(bsdfRay, rRec2) * weightBSDF; } } } return LiSurf * transmittance + LiMedium; }
Spectrum BSDF::getDiffuseReflectance(const Intersection &its) const { BSDFSamplingRecord bRec(its, Vector(0, 0, 1), Vector(0, 0, 1)); bRec.typeMask = EDiffuseReflection; return eval(bRec) * M_PI; }
/// Invoke a series of t-tests on the provided input void activate() { int total = 0, passed = 0; pcg32 random; if (!m_bsdfs.empty()) { if (m_references.size() * m_bsdfs.size() != m_angles.size()) throw NoriException("Specified a different number of angles and reference values!"); if (!m_scenes.empty()) throw NoriException("Cannot test BSDFs and scenes at the same time!"); /* Test each registered BSDF */ int ctr = 0; for (auto bsdf : m_bsdfs) { for (size_t i=0; i<m_references.size(); ++i) { float angle = m_angles[i], reference = m_references[ctr++]; cout << "------------------------------------------------------" << endl; cout << "Testing (angle=" << angle << "): " << bsdf->toString() << endl; ++total; BSDFQueryRecord bRec(sphericalDirection(degToRad(angle), 0)); cout << "Drawing " << m_sampleCount << " samples .. " << endl; double mean=0, variance = 0; for (int k=0; k<m_sampleCount; ++k) { Point2f sample(random.nextFloat(), random.nextFloat()); double result = (double) bsdf->sample(bRec, sample).getLuminance(); /* Numerically robust online variance estimation using an algorithm proposed by Donald Knuth (TAOCP vol.2, 3rd ed., p.232) */ double delta = result - mean; mean += delta / (double) (k+1); variance += delta * (result - mean); } variance /= m_sampleCount - 1; std::pair<bool, std::string> result = hypothesis::students_t_test(mean, variance, reference, m_sampleCount, m_significanceLevel, (int) m_references.size()); if (result.first) ++passed; cout << result.second << endl; } } } else { if (m_references.size() != m_scenes.size()) throw NoriException("Specified a different number of scenes and reference values!"); Sampler *sampler = static_cast<Sampler *>( NoriObjectFactory::createInstance("independent", PropertyList())); int ctr = 0; for (auto scene : m_scenes) { const Integrator *integrator = scene->getIntegrator(); const Camera *camera = scene->getCamera(); float reference = m_references[ctr++]; cout << "------------------------------------------------------" << endl; cout << "Testing scene: " << scene->toString() << endl; ++total; cout << "Generating " << m_sampleCount << " paths.. " << endl; double mean = 0, variance = 0; for (int k=0; k<m_sampleCount; ++k) { /* Sample a ray from the camera */ Ray3f ray; Point2f pixelSample = (sampler->next2D().array() * camera->getOutputSize().cast<float>().array()).matrix(); Color3f value = camera->sampleRay(ray, pixelSample, sampler->next2D()); /* Compute the incident radiance */ value *= integrator->Li(scene, sampler, ray); /* Numerically robust online variance estimation using an algorithm proposed by Donald Knuth (TAOCP vol.2, 3rd ed., p.232) */ double result = (double) value.getLuminance(); double delta = result - mean; mean += delta / (double) (k+1); variance += delta * (result - mean); } variance /= m_sampleCount - 1; std::pair<bool, std::string> result = hypothesis::students_t_test(mean, variance, reference, m_sampleCount, m_significanceLevel, (int) m_references.size()); if (result.first) ++passed; cout << result.second << endl; } } cout << "Passed " << passed << "/" << total << " tests." << endl; }
Spectrum Li(const RayDifferential &r, RadianceQueryRecord &rRec) const { /* Some aliases and local variables */ const Scene *scene = rRec.scene; Intersection &its = rRec.its; RayDifferential ray(r); Spectrum Li(0.0f); /* Perform the first ray intersection (or ignore if the intersection has already been provided). */ rRec.rayIntersect(ray); ray.mint = Epsilon; Spectrum pathThroughput(1.0f); while (rRec.depth <= m_maxDepth || m_maxDepth < 0) { if (!its.isValid()) { /* If no intersection could be found, potentially return radiance from a background luminaire if it exists */ if (rRec.type & RadianceQueryRecord::EEmittedRadiance) Li += pathThroughput * scene->LeBackground(ray); break; } const BSDF *bsdf = its.getBSDF(ray); if (EXPECT_NOT_TAKEN(bsdf == NULL)) { /* The MI path tracer doesn't support surfaces without a BSDF (e.g. medium transitions) -- give up. */ break; } /* Possibly include emitted radiance if requested */ if (its.isLuminaire() && (rRec.type & RadianceQueryRecord::EEmittedRadiance)) Li += pathThroughput * its.Le(-ray.d); /* Include radiance from a subsurface integrator if requested */ if (its.hasSubsurface() && (rRec.type & RadianceQueryRecord::ESubsurfaceRadiance)) Li += pathThroughput * its.LoSub(scene, rRec.sampler, -ray.d, rRec.depth); if (m_maxDepth > 0 && rRec.depth >= m_maxDepth) break; /* ==================================================================== */ /* Luminaire sampling */ /* ==================================================================== */ /* Prevent light leaks due to the use of shading normals */ Float wiDotGeoN = -dot(its.geoFrame.n, ray.d), wiDotShN = Frame::cosTheta(its.wi); if (wiDotGeoN * wiDotShN < 0 && m_strictNormals) break; /* Estimate the direct illumination if this is requested */ LuminaireSamplingRecord lRec; if (rRec.type & RadianceQueryRecord::EDirectSurfaceRadiance && scene->sampleLuminaire(its.p, ray.time, lRec, rRec.nextSample2D())) { /* Allocate a record for querying the BSDF */ const Vector wo = -lRec.d; const BSDFQueryRecord bRec(its, its.toLocal(wo)); /* Evaluate BSDF * cos(theta) */ const Spectrum bsdfVal = bsdf->fCos(bRec); Float woDotGeoN = dot(its.geoFrame.n, wo); /* Prevent light leaks due to the use of shading normals */ if (!bsdfVal.isZero() && (!m_strictNormals || woDotGeoN * Frame::cosTheta(bRec.wo) > 0)) { /* Calculate prob. of having sampled that direction using BSDF sampling */ Float bsdfPdf = (lRec.luminaire->isIntersectable() || lRec.luminaire->isBackgroundLuminaire()) ? bsdf->pdf(bRec) : 0; /* Weight using the power heuristic */ const Float weight = miWeight(lRec.pdf, bsdfPdf); Li += pathThroughput * lRec.value * bsdfVal * weight; } } /* ==================================================================== */ /* BSDF sampling */ /* ==================================================================== */ /* Sample BSDF * cos(theta) */ BSDFQueryRecord bRec(its); Float bsdfPdf; Spectrum bsdfVal = bsdf->sampleCos(bRec, bsdfPdf, rRec.nextSample2D()); if (bsdfVal.isZero()) break; bsdfVal /= bsdfPdf; /* Prevent light leaks due to the use of shading normals */ const Vector wo = its.toWorld(bRec.wo); Float woDotGeoN = dot(its.geoFrame.n, wo); if (woDotGeoN * Frame::cosTheta(bRec.wo) <= 0 && m_strictNormals) break; /* Trace a ray in this direction */ ray = Ray(its.p, wo, ray.time); bool hitLuminaire = false; if (scene->rayIntersect(ray, its)) { /* Intersected something - check if it was a luminaire */ if (its.isLuminaire()) { lRec = LuminaireSamplingRecord(its, -ray.d); lRec.value = its.Le(-ray.d); hitLuminaire = true; } } else { /* No intersection found. Possibly, there is a background luminaire such as an environment map? */ if (scene->hasBackgroundLuminaire()) { lRec.luminaire = scene->getBackgroundLuminaire(); lRec.value = lRec.luminaire->Le(ray); lRec.d = -ray.d; hitLuminaire = true; } else { rRec.depth++; break; } } /* If a luminaire was hit, estimate the local illumination and weight using the power heuristic */ if (hitLuminaire && (rRec.type & RadianceQueryRecord::EDirectSurfaceRadiance)) { /* Prob. of having generated this sample using luminaire sampling */ const Float lumPdf = (!(bRec.sampledType & BSDF::EDelta)) ? scene->pdfLuminaire(ray.o, lRec) : 0; const Float weight = miWeight(bsdfPdf, lumPdf); Li += pathThroughput * lRec.value * bsdfVal * weight; } /* ==================================================================== */ /* Indirect illumination */ /* ==================================================================== */ /* Set the recursive query type */ /* Stop if no surface was hit by the BSDF sample or if indirect illumination was not requested */ if (!its.isValid() || !(rRec.type & RadianceQueryRecord::EIndirectSurfaceRadiance)) break; rRec.type = RadianceQueryRecord::ERadianceNoEmission; /* Russian roulette - Possibly stop the recursion. Don't do this when dealing with a transmission component, since solid angle compression factors cause problems with the heuristic below */ if (rRec.depth >= m_rrDepth && !(bRec.sampledType & BSDF::ETransmission)) { /* Assuming that BSDF importance sampling is perfect, 'bsdfVal.max()' should equal the maximum albedo over all spectral samples */ Float approxAlbedo = std::min((Float) 0.9f, bsdfVal.max()); if (rRec.nextSample1D() > approxAlbedo) break; else pathThroughput /= approxAlbedo; } pathThroughput *= bsdfVal; rRec.depth++; } /* Store statistics */ avgPathLength.incrementBase(); avgPathLength += rRec.depth; return Li; }
Spectrum Li(const RayDifferential &r, RadianceQueryRecord &rRec) const { /* Some aliases and local variables */ const Scene *scene = rRec.scene; Intersection &its = rRec.its; RayDifferential ray(r); Spectrum Li(0.0f); bool scattered = false; /* Perform the first ray intersection (or ignore if the intersection has already been provided). */ rRec.rayIntersect(ray); ray.mint = Epsilon; Spectrum throughput(1.0f); Float eta = 1.0f; while (rRec.depth <= m_maxDepth || m_maxDepth < 0) { if (!its.isValid()) { /* If no intersection could be found, potentially return radiance from a environment luminaire if it exists */ if ((rRec.type & RadianceQueryRecord::EEmittedRadiance) && (!m_hideEmitters || scattered)) Li += throughput * scene->evalEnvironment(ray); break; } const BSDF *bsdf = its.getBSDF(ray); /* Possibly include emitted radiance if requested */ if (its.isEmitter() && (rRec.type & RadianceQueryRecord::EEmittedRadiance) && (!m_hideEmitters || scattered)) Li += throughput * its.Le(-ray.d); /* Include radiance from a subsurface scattering model if requested */ if (its.hasSubsurface() && (rRec.type & RadianceQueryRecord::ESubsurfaceRadiance)) Li += throughput * its.LoSub(scene, rRec.sampler, -ray.d, rRec.depth); if ((rRec.depth >= m_maxDepth && m_maxDepth > 0) || (m_strictNormals && dot(ray.d, its.geoFrame.n) * Frame::cosTheta(its.wi) >= 0)) { /* Only continue if: 1. The current path length is below the specifed maximum 2. If 'strictNormals'=true, when the geometric and shading normals classify the incident direction to the same side */ break; } /* ==================================================================== */ /* Direct illumination sampling */ /* ==================================================================== */ /* Estimate the direct illumination if this is requested */ DirectSamplingRecord dRec(its); if (rRec.type & RadianceQueryRecord::EDirectSurfaceRadiance && (bsdf->getType() & BSDF::ESmooth)) { Spectrum value = scene->sampleEmitterDirect(dRec, rRec.nextSample2D()); if (!value.isZero()) { const Emitter *emitter = static_cast<const Emitter *>(dRec.object); /* Allocate a record for querying the BSDF */ BSDFSamplingRecord bRec(its, its.toLocal(dRec.d), ERadiance); /* Evaluate BSDF * cos(theta) */ const Spectrum bsdfVal = bsdf->eval(bRec); /* Prevent light leaks due to the use of shading normals */ if (!bsdfVal.isZero() && (!m_strictNormals || dot(its.geoFrame.n, dRec.d) * Frame::cosTheta(bRec.wo) > 0)) { /* Calculate prob. of having generated that direction using BSDF sampling */ Float bsdfPdf = (emitter->isOnSurface() && dRec.measure == ESolidAngle) ? bsdf->pdf(bRec) : 0; /* Weight using the power heuristic */ Float weight = miWeight(dRec.pdf, bsdfPdf); Li += throughput * value * bsdfVal * weight; } } } /* ==================================================================== */ /* BSDF sampling */ /* ==================================================================== */ /* Sample BSDF * cos(theta) */ Float bsdfPdf; BSDFSamplingRecord bRec(its, rRec.sampler, ERadiance); Spectrum bsdfWeight = bsdf->sample(bRec, bsdfPdf, rRec.nextSample2D()); if (bsdfWeight.isZero()) break; scattered |= bRec.sampledType != BSDF::ENull; /* Prevent light leaks due to the use of shading normals */ const Vector wo = its.toWorld(bRec.wo); Float woDotGeoN = dot(its.geoFrame.n, wo); if (m_strictNormals && woDotGeoN * Frame::cosTheta(bRec.wo) <= 0) break; bool hitEmitter = false; Spectrum value; /* Trace a ray in this direction */ ray = Ray(its.p, wo, ray.time); if (scene->rayIntersect(ray, its)) { /* Intersected something - check if it was a luminaire */ if (its.isEmitter()) { value = its.Le(-ray.d); dRec.setQuery(ray, its); hitEmitter = true; } } else { /* Intersected nothing -- perhaps there is an environment map? */ const Emitter *env = scene->getEnvironmentEmitter(); if (env) { if (m_hideEmitters && !scattered) break; value = env->evalEnvironment(ray); if (!env->fillDirectSamplingRecord(dRec, ray)) break; hitEmitter = true; } else { break; } } /* Keep track of the throughput and relative refractive index along the path */ throughput *= bsdfWeight; eta *= bRec.eta; /* If a luminaire was hit, estimate the local illumination and weight using the power heuristic */ if (hitEmitter && (rRec.type & RadianceQueryRecord::EDirectSurfaceRadiance)) { /* Compute the prob. of generating that direction using the implemented direct illumination sampling technique */ const Float lumPdf = (!(bRec.sampledType & BSDF::EDelta)) ? scene->pdfEmitterDirect(dRec) : 0; Li += throughput * value * miWeight(bsdfPdf, lumPdf); } /* ==================================================================== */ /* Indirect illumination */ /* ==================================================================== */ /* Set the recursive query type. Stop if no surface was hit by the BSDF sample or if indirect illumination was not requested */ if (!its.isValid() || !(rRec.type & RadianceQueryRecord::EIndirectSurfaceRadiance)) break; rRec.type = RadianceQueryRecord::ERadianceNoEmission; if (rRec.depth++ >= m_rrDepth) { /* Russian roulette: try to keep path weights equal to one, while accounting for the solid angle compression at refractive index boundaries. Stop with at least some probability to avoid getting stuck (e.g. due to total internal reflection) */ Float q = std::min(throughput.max() * eta * eta, (Float) 0.95f); if (rRec.nextSample1D() >= q) break; throughput /= q; } } /* Store statistics */ avgPathLength.incrementBase(); avgPathLength += rRec.depth; return Li; }
bool PathEdge::pathConnectAndCollapse(const Scene *scene, const PathEdge *predEdge, const PathVertex *vs, const PathVertex *vt, const PathEdge *succEdge, int &interactions) { if (vs->isEmitterSupernode() || vt->isSensorSupernode()) { Float radianceTransport = vt->isSensorSupernode() ? 1.0f : 0.0f, importanceTransport = 1-radianceTransport; medium = NULL; length = 0.0f; d = Vector(0.0f); pdf[ERadiance] = radianceTransport; pdf[EImportance] = importanceTransport; weight[ERadiance] = Spectrum(radianceTransport); weight[EImportance] = Spectrum(importanceTransport); interactions = 0; } else { Point vsp = vs->getPosition(), vtp = vt->getPosition(); d = vsp-vtp; length = d.length(); int maxInteractions = interactions; interactions = 0; if (length == 0) { #if defined(MTS_BD_DEBUG) SLog(EWarn, "Tried to connect %s and %s, which are located at exactly the same position!", vs->toString().c_str(), vt->toString().c_str()); #endif return false; } d /= length; Float lengthFactor = vs->isOnSurface() ? (1-ShadowEpsilon) : 1; Ray ray(vtp, d, vt->isOnSurface() ? Epsilon : 0, length * lengthFactor, vs->getTime()); weight[ERadiance] = Spectrum(1.0f); weight[EImportance] = Spectrum(1.0f); pdf[ERadiance] = 1.0f; pdf[EImportance] = 1.0f; Intersection its; Float remaining = length; medium = vt->getTargetMedium(succEdge, d); while (true) { bool surface = scene->rayIntersectAll(ray, its); if (surface && (interactions == maxInteractions || !(its.getBSDF()->getType() & BSDF::ENull))) { /* Encountered an occluder -- zero transmittance. */ return false; } if (medium) { Float segmentLength = std::min(its.t, remaining); MediumSamplingRecord mRec; medium->eval(Ray(ray, 0, segmentLength), mRec); Float pdfRadiance = (surface || !vs->isMediumInteraction()) ? mRec.pdfFailure : mRec.pdfSuccess; Float pdfImportance = (interactions > 0 || !vt->isMediumInteraction()) ? mRec.pdfFailure : mRec.pdfSuccessRev; if (pdfRadiance == 0 || pdfImportance == 0 || mRec.transmittance.isZero()) { /* Zero transmittance */ return false; } weight[EImportance] *= mRec.transmittance / pdfImportance; weight[ERadiance] *= mRec.transmittance / pdfRadiance; pdf[EImportance] *= pdfImportance; pdf[ERadiance] *= pdfRadiance; } if (!surface || remaining - its.t < 0) break; /* Advance the ray */ ray.o = ray(its.t); remaining -= its.t; ray.mint = Epsilon; ray.maxt = remaining * lengthFactor; /* Account for the ENull interaction */ const BSDF *bsdf = its.getBSDF(); Vector wo = its.toLocal(ray.d); BSDFSamplingRecord bRec(its, -wo, wo, ERadiance); bRec.component = BSDF::ENull; Float nullPdf = bsdf->pdf(bRec, EDiscrete); if (nullPdf == 0) return false; Spectrum nullWeight = bsdf->eval(bRec, EDiscrete) / nullPdf; weight[EImportance] *= nullWeight; weight[ERadiance] *= nullWeight; pdf[EImportance] *= nullPdf; pdf[ERadiance] *= nullPdf; if (its.isMediumTransition()) { const Medium *expected = its.getTargetMedium(-ray.d); if (medium != expected) { #if defined(MTS_BD_TRACE) SLog(EWarn, "PathEdge::pathConnectAndCollapse(): attempted two connect " "two vertices that disagree about the medium in between! " "Please check your scene for leaks."); #endif ++mediumInconsistencies; return false; } medium = its.getTargetMedium(ray.d); } if (++interactions > 100) { /// Just a precaution.. SLog(EWarn, "pathConnectAndCollapse(): round-off error issues?"); return false; } } if (medium != vs->getTargetMedium(predEdge, -d)) { #if defined(MTS_BD_TRACE) SLog(EWarn, "PathEdge::pathConnectAndCollapse(): attempted two connect " "two vertices that disagree about the medium in between! " "Please check your scene for leaks."); #endif ++mediumInconsistencies; return false; } } return true; }
Spectrum Li(const RayDifferential &r, RadianceQueryRecord &rRec) const { /* Some aliases and local variables */ const Scene *scene = rRec.scene; Intersection &its = rRec.its, bsdfIts; RayDifferential ray(r); LuminaireSamplingRecord lRec; Spectrum Li(0.0f); Point2 sample; /* Perform the first ray intersection (or ignore if the intersection has already been provided). */ if (!rRec.rayIntersect(ray)) { /* If no intersection could be found, possibly return radiance from a background luminaire */ if (rRec.type & RadianceQueryRecord::EEmittedRadiance) return scene->LeBackground(ray); else return Spectrum(0.0f); } /* Possibly include emitted radiance if requested */ if (its.isLuminaire() && (rRec.type & RadianceQueryRecord::EEmittedRadiance)) Li += its.Le(-ray.d); /* Include radiance from a subsurface integrator if requested */ if (its.hasSubsurface() && (rRec.type & RadianceQueryRecord::ESubsurfaceRadiance)) Li += its.LoSub(scene, rRec.sampler, -ray.d, rRec.depth); const BSDF *bsdf = its.getBSDF(ray); if (EXPECT_NOT_TAKEN(!bsdf)) { /* The direct illumination integrator doesn't support surfaces without a BSDF (e.g. medium transitions) -- give up. */ return Li; } /* Leave here if direct illumination was not requested */ if (!(rRec.type & RadianceQueryRecord::EDirectSurfaceRadiance)) return Li; /* ==================================================================== */ /* Luminaire sampling */ /* ==================================================================== */ bool adaptiveQuery = (rRec.extra & RadianceQueryRecord::EAdaptiveQuery); Point2 *sampleArray; size_t numLuminaireSamples = m_luminaireSamples, numBSDFSamples = m_bsdfSamples; Float fracLum = m_fracLum, fracBSDF = m_fracBSDF, weightLum = m_weightLum, weightBSDF = m_weightBSDF; if (rRec.depth > 1 || adaptiveQuery) { /* This integrator is used recursively by another integrator. Be less accurate as this sample will not directly be observed. */ numBSDFSamples = numLuminaireSamples = 1; fracLum = fracBSDF = .5f; weightLum = weightBSDF = 1.0f; } if (numLuminaireSamples > 1) { sampleArray = rRec.sampler->next2DArray(numLuminaireSamples); } else { sample = rRec.nextSample2D(); sampleArray = &sample; } for (size_t i=0; i<numLuminaireSamples; ++i) { /* Estimate the direct illumination if this is requested */ if (scene->sampleLuminaire(its.p, ray.time, lRec, sampleArray[i])) { /* Allocate a record for querying the BSDF */ BSDFQueryRecord bRec(its, its.toLocal(-lRec.d)); /* Evaluate BSDF * cos(theta) */ const Spectrum bsdfVal = bsdf->eval(bRec); if (!bsdfVal.isZero()) { /* Calculate prob. of having sampled that direction using BSDF sampling */ Float bsdfPdf = (lRec.luminaire->isIntersectable() || lRec.luminaire->isBackgroundLuminaire()) ? bsdf->pdf(bRec) : 0; /* Weight using the power heuristic */ const Float weight = miWeight(lRec.pdf * fracLum, bsdfPdf * fracBSDF) * weightLum; Li += lRec.value * bsdfVal * weight; } } } /* ==================================================================== */ /* BSDF sampling */ /* ==================================================================== */ if (numBSDFSamples > 1) { sampleArray = rRec.sampler->next2DArray(numBSDFSamples); } else { sample = rRec.nextSample2D(); sampleArray = &sample; } for (size_t i=0; i<numBSDFSamples; ++i) { /* Sample BSDF * cos(theta) */ BSDFQueryRecord bRec(its, rRec.sampler, ERadiance); Float bsdfPdf; Spectrum bsdfVal = bsdf->sample(bRec, bsdfPdf, sampleArray[i]); if (bsdfVal.isZero()) continue; /* Trace a ray in this direction */ Ray bsdfRay(its.p, its.toWorld(bRec.wo), ray.time); if (scene->rayIntersect(bsdfRay, bsdfIts)) { /* Intersected something - check if it was a luminaire */ if (bsdfIts.isLuminaire()) { lRec = LuminaireSamplingRecord(bsdfIts, -bsdfRay.d); lRec.value = bsdfIts.Le(-bsdfRay.d); } else { continue; } } else { /* No intersection found. Possibly, there is a background luminaire such as an environment map? */ if (scene->hasBackgroundLuminaire()) { lRec.luminaire = scene->getBackgroundLuminaire(); lRec.d = -bsdfRay.d; lRec.value = lRec.luminaire->Le(bsdfRay); } else { continue; } } const Float lumPdf = (!(bRec.sampledType & BSDF::EDelta)) ? scene->pdfLuminaire(its.p, lRec) : 0; const Float weight = miWeight(bsdfPdf * fracBSDF, lumPdf * fracLum) * weightBSDF; Li += lRec.value * bsdfVal * weight; } return Li; }
void ParticleTracer::process(const WorkUnit *workUnit, WorkResult *workResult, const bool &stop) { const RangeWorkUnit *range = static_cast<const RangeWorkUnit *>(workUnit); MediumSamplingRecord mRec; Intersection its; ref<Sensor> sensor = m_scene->getSensor(); bool needsTimeSample = sensor->needsTimeSample(); PositionSamplingRecord pRec(sensor->getShutterOpen() + 0.5f * sensor->getShutterOpenTime()); Ray ray; m_sampler->generate(Point2i(0)); for (size_t index = range->getRangeStart(); index <= range->getRangeEnd() && !stop; ++index) { m_sampler->setSampleIndex(index); /* Sample an emission */ if (needsTimeSample) pRec.time = sensor->sampleTime(m_sampler->next1D()); const Emitter *emitter = NULL; const Medium *medium; Spectrum power; Ray ray; if (m_emissionEvents) { /* Sample the position and direction component separately to generate emission events */ power = m_scene->sampleEmitterPosition(pRec, m_sampler->next2D()); emitter = static_cast<const Emitter *>(pRec.object); medium = emitter->getMedium(); /* Forward the sampling event to the attached handler */ handleEmission(pRec, medium, power); DirectionSamplingRecord dRec; power *= emitter->sampleDirection(dRec, pRec, emitter->needsDirectionSample() ? m_sampler->next2D() : Point2(0.5f)); ray.setTime(pRec.time); ray.setOrigin(pRec.p); ray.setDirection(dRec.d); } else { /* Sample both components together, which is potentially faster / uses a better sampling strategy */ power = m_scene->sampleEmitterRay(ray, emitter, m_sampler->next2D(), m_sampler->next2D(), pRec.time); medium = emitter->getMedium(); handleNewParticle(); } int depth = 1, nullInteractions = 0; bool delta = false; Spectrum throughput(1.0f); // unitless path throughput (used for russian roulette) while (!throughput.isZero() && (depth <= m_maxDepth || m_maxDepth < 0)) { m_scene->rayIntersectAll(ray, its); /* ==================================================================== */ /* Radiative Transfer Equation sampling */ /* ==================================================================== */ if (medium && medium->sampleDistance(Ray(ray, 0, its.t), mRec, m_sampler)) { /* Sample the integral \int_x^y tau(x, x') [ \sigma_s \int_{S^2} \rho(\omega,\omega') L(x,\omega') d\omega' ] dx' */ throughput *= mRec.sigmaS * mRec.transmittance / mRec.pdfSuccess; /* Forward the medium scattering event to the attached handler */ handleMediumInteraction(depth, nullInteractions, delta, mRec, medium, -ray.d, throughput*power); PhaseFunctionSamplingRecord pRec(mRec, -ray.d, EImportance); throughput *= medium->getPhaseFunction()->sample(pRec, m_sampler); delta = false; ray = Ray(mRec.p, pRec.wo, ray.time); ray.mint = 0; } else if (its.t == std::numeric_limits<Float>::infinity()) { /* There is no surface in this direction */ break; } else { /* Sample tau(x, y) (Surface integral). This happens with probability mRec.pdfFailure Account for this and multiply by the proper per-color-channel transmittance. */ if (medium) throughput *= mRec.transmittance / mRec.pdfFailure; const BSDF *bsdf = its.getBSDF(); /* Forward the surface scattering event to the attached handler */ handleSurfaceInteraction(depth, nullInteractions, delta, its, medium, throughput*power); BSDFSamplingRecord bRec(its, m_sampler, EImportance); Spectrum bsdfWeight = bsdf->sample(bRec, m_sampler->next2D()); if (bsdfWeight.isZero()) break; /* Prevent light leaks due to the use of shading normals -- [Veach, p. 158] */ Vector wi = -ray.d, wo = its.toWorld(bRec.wo); Float wiDotGeoN = dot(its.geoFrame.n, wi), woDotGeoN = dot(its.geoFrame.n, wo); if (wiDotGeoN * Frame::cosTheta(bRec.wi) <= 0 || woDotGeoN * Frame::cosTheta(bRec.wo) <= 0) break; /* Keep track of the weight, medium and relative refractive index along the path */ throughput *= bsdfWeight; if (its.isMediumTransition()) medium = its.getTargetMedium(woDotGeoN); if (bRec.sampledType & BSDF::ENull) ++nullInteractions; else delta = bRec.sampledType & BSDF::EDelta; #if 0 /* This is somewhat unfortunate: for accuracy, we'd really want the correction factor below to match the path tracing interpretation of a scene with shading normals. However, this factor can become extremely large, which adds unacceptable variance to output renderings. So for now, it is disabled. The adjoint particle tracer and the photon mapping variants still use this factor for the last bounce -- just not for the intermediate ones, which introduces a small (though in practice not noticeable) amount of error. This is also what the implementation of SPPM by Toshiya Hachisuka does. Ultimately, we'll need better adjoint BSDF sampling strategies that incorporate these extra terms */ /* Adjoint BSDF for shading normals -- [Veach, p. 155] */ throughput *= std::abs( (Frame::cosTheta(bRec.wi) * woDotGeoN)/ (Frame::cosTheta(bRec.wo) * wiDotGeoN)); #endif ray.setOrigin(its.p); ray.setDirection(wo); ray.mint = Epsilon; } if (depth++ >= m_rrDepth) { /* Russian roulette: try to keep path weights equal to one, Stop with at least some probability to avoid getting stuck (e.g. due to total internal reflection) */ Float q = std::min(throughput.max(), (Float) 0.95f); if (m_sampler->next1D() >= q) break; throughput /= q; } } } }
Spectrum Li(const RayDifferential &r, RadianceQueryRecord &rRec) const { /* Some aliases and local variables */ const Scene *scene = rRec.scene; Intersection &its = rRec.its; MediumSamplingRecord mRec; RayDifferential ray(r); Spectrum Li(0.0f); bool nullChain = true, scattered = false; Float eta = 1.0f; /* Perform the first ray intersection (or ignore if the intersection has already been provided). */ rRec.rayIntersect(ray); Spectrum throughput(1.0f); if (m_maxDepth == 1) rRec.type &= RadianceQueryRecord::EEmittedRadiance; /** * Note: the logic regarding maximum path depth may appear a bit * strange. This is necessary to get this integrator's output to * exactly match the output of other integrators under all settings * of this parameter. */ while (rRec.depth <= m_maxDepth || m_maxDepth < 0) { /* ==================================================================== */ /* Radiative Transfer Equation sampling */ /* ==================================================================== */ if (rRec.medium && rRec.medium->sampleDistance(Ray(ray, 0, its.t), mRec, rRec.sampler)) { /* Sample the integral \int_x^y tau(x, x') [ \sigma_s \int_{S^2} \rho(\omega,\omega') L(x,\omega') d\omega' ] dx' */ const PhaseFunction *phase = rRec.medium->getPhaseFunction(); throughput *= mRec.sigmaS * mRec.transmittance / mRec.pdfSuccess; /* ==================================================================== */ /* Direct illumination sampling */ /* ==================================================================== */ /* Estimate the single scattering component if this is requested */ if (rRec.type & RadianceQueryRecord::EDirectMediumRadiance) { DirectSamplingRecord dRec(mRec.p, mRec.time); int maxInteractions = m_maxDepth - rRec.depth - 1; Spectrum value = scene->sampleAttenuatedEmitterDirect( dRec, rRec.medium, maxInteractions, rRec.nextSample2D(), rRec.sampler); if (!value.isZero()) Li += throughput * value * phase->eval( PhaseFunctionSamplingRecord(mRec, -ray.d, dRec.d)); } /* Stop if multiple scattering was not requested, or if the path gets too long */ if ((rRec.depth + 1 >= m_maxDepth && m_maxDepth > 0) || !(rRec.type & RadianceQueryRecord::EIndirectMediumRadiance)) break; /* ==================================================================== */ /* Phase function sampling / Multiple scattering */ /* ==================================================================== */ PhaseFunctionSamplingRecord pRec(mRec, -ray.d); Float phaseVal = phase->sample(pRec, rRec.sampler); if (phaseVal == 0) break; throughput *= phaseVal; /* Trace a ray in this direction */ ray = Ray(mRec.p, pRec.wo, ray.time); ray.mint = 0; scene->rayIntersect(ray, its); nullChain = false; scattered = true; } else { /* Sample tau(x, y) * (Surface integral). This happens with probability mRec.pdfFailure Account for this and multiply by the proper per-color-channel transmittance. */ if (rRec.medium) throughput *= mRec.transmittance / mRec.pdfFailure; if (!its.isValid()) { /* If no intersection could be found, possibly return attenuated radiance from a background luminaire */ if ((rRec.type & RadianceQueryRecord::EEmittedRadiance) && (!m_hideEmitters || scattered)) { Spectrum value = throughput * scene->evalEnvironment(ray); if (rRec.medium) value *= rRec.medium->evalTransmittance(ray); Li += value; } break; } /* Possibly include emitted radiance if requested */ if (its.isEmitter() && (rRec.type & RadianceQueryRecord::EEmittedRadiance) && (!m_hideEmitters || scattered)) Li += throughput * its.Le(-ray.d); /* Include radiance from a subsurface integrator if requested */ if (its.hasSubsurface() && (rRec.type & RadianceQueryRecord::ESubsurfaceRadiance)) Li += throughput * its.LoSub(scene, rRec.sampler, -ray.d, rRec.depth); /* Prevent light leaks due to the use of shading normals */ Float wiDotGeoN = -dot(its.geoFrame.n, ray.d), wiDotShN = Frame::cosTheta(its.wi); if (m_strictNormals && wiDotGeoN * wiDotShN < 0) break; /* ==================================================================== */ /* Direct illumination sampling */ /* ==================================================================== */ const BSDF *bsdf = its.getBSDF(ray); /* Estimate the direct illumination if this is requested */ if (rRec.type & RadianceQueryRecord::EDirectSurfaceRadiance && (bsdf->getType() & BSDF::ESmooth)) { DirectSamplingRecord dRec(its); int maxInteractions = m_maxDepth - rRec.depth - 1; Spectrum value = scene->sampleAttenuatedEmitterDirect( dRec, its, rRec.medium, maxInteractions, rRec.nextSample2D(), rRec.sampler); if (!value.isZero()) { /* Allocate a record for querying the BSDF */ BSDFSamplingRecord bRec(its, its.toLocal(dRec.d)); bRec.sampler = rRec.sampler; Float woDotGeoN = dot(its.geoFrame.n, dRec.d); /* Prevent light leaks due to the use of shading normals */ if (!m_strictNormals || woDotGeoN * Frame::cosTheta(bRec.wo) > 0) Li += throughput * value * bsdf->eval(bRec); } } /* ==================================================================== */ /* BSDF sampling / Multiple scattering */ /* ==================================================================== */ /* Sample BSDF * cos(theta) */ BSDFSamplingRecord bRec(its, rRec.sampler, ERadiance); Spectrum bsdfVal = bsdf->sample(bRec, rRec.nextSample2D()); if (bsdfVal.isZero()) break; /* Recursively gather indirect illumination? */ int recursiveType = 0; if ((rRec.depth + 1 < m_maxDepth || m_maxDepth < 0) && (rRec.type & RadianceQueryRecord::EIndirectSurfaceRadiance)) recursiveType |= RadianceQueryRecord::ERadianceNoEmission; /* Recursively gather direct illumination? This is a bit more complicated by the fact that this integrator can create connection through index-matched medium transitions (ENull scattering events) */ if ((rRec.depth < m_maxDepth || m_maxDepth < 0) && (rRec.type & RadianceQueryRecord::EDirectSurfaceRadiance) && (bRec.sampledType & BSDF::EDelta) && (!(bRec.sampledType & BSDF::ENull) || nullChain)) { recursiveType |= RadianceQueryRecord::EEmittedRadiance; nullChain = true; } else { nullChain &= bRec.sampledType == BSDF::ENull; } /* Potentially stop the recursion if there is nothing more to do */ if (recursiveType == 0) break; rRec.type = recursiveType; /* Prevent light leaks due to the use of shading normals */ const Vector wo = its.toWorld(bRec.wo); Float woDotGeoN = dot(its.geoFrame.n, wo); if (woDotGeoN * Frame::cosTheta(bRec.wo) <= 0 && m_strictNormals) break; /* Keep track of the throughput, medium, and relative refractive index along the path */ throughput *= bsdfVal; eta *= bRec.eta; if (its.isMediumTransition()) rRec.medium = its.getTargetMedium(wo); /* In the next iteration, trace a ray in this direction */ ray = Ray(its.p, wo, ray.time); scene->rayIntersect(ray, its); scattered |= bRec.sampledType != BSDF::ENull; } if (rRec.depth++ >= m_rrDepth) { /* Russian roulette: try to keep path weights equal to one, while accounting for the solid angle compression at refractive index boundaries. Stop with at least some probability to avoid getting stuck (e.g. due to total internal reflection) */ Float q = std::min(throughput.max() * eta * eta, (Float) 0.95f); if (rRec.nextSample1D() >= q) break; throughput /= q; } } avgPathLength.incrementBase(); avgPathLength += rRec.depth; return Li; }
size_t generateVPLs(const Scene *scene, Random *random, size_t offset, size_t count, int maxDepth, bool prune, std::deque<VPL> &vpls) { if (maxDepth <= 1) return 0; static Sampler *sampler = NULL; if (!sampler) { Properties props("halton"); props.setInteger("scramble", 0); sampler = static_cast<Sampler *> (PluginManager::getInstance()-> createObject(MTS_CLASS(Sampler), props)); sampler->configure(); } const Sensor *sensor = scene->getSensor(); Float time = sensor->getShutterOpen() + 0.5f * sensor->getShutterOpenTime(); const Frame stdFrame(Vector(1,0,0), Vector(0,1,0), Vector(0,0,1)); while (vpls.size() < count) { sampler->setSampleIndex(++offset); PositionSamplingRecord pRec(time); DirectionSamplingRecord dRec; Spectrum weight = scene->sampleEmitterPosition(pRec, sampler->next2D()); size_t start = vpls.size(); /* Sample an emitted particle */ const Emitter *emitter = static_cast<const Emitter *>(pRec.object); if (!emitter->isEnvironmentEmitter() && emitter->needsDirectionSample()) { VPL lumVPL(EPointEmitterVPL, weight); lumVPL.its.p = pRec.p; lumVPL.its.shFrame = pRec.n.isZero() ? stdFrame : Frame(pRec.n); lumVPL.emitter = emitter; appendVPL(scene, random, lumVPL, prune, vpls); weight *= emitter->sampleDirection(dRec, pRec, sampler->next2D()); } else { /* Hack to get the proper information for directional VPLs */ DirectSamplingRecord diRec( scene->getKDTree()->getAABB().getCenter(), pRec.time); Spectrum weight2 = emitter->sampleDirect(diRec, sampler->next2D()) / scene->pdfEmitterDiscrete(emitter); if (weight2.isZero()) continue; VPL lumVPL(EDirectionalEmitterVPL, weight2); lumVPL.its.p = Point(0.0); lumVPL.its.shFrame = Frame(-diRec.d); lumVPL.emitter = emitter; appendVPL(scene, random, lumVPL, false, vpls); dRec.d = -diRec.d; Point2 offset = Warp::squareToUniformDiskConcentric(sampler->next2D()); Vector perpOffset = Frame(diRec.d).toWorld(Vector(offset.x, offset.y, 0)); BSphere geoBSphere = scene->getKDTree()->getAABB().getBSphere(); pRec.p = geoBSphere.center + (perpOffset - dRec.d) * geoBSphere.radius; weight = weight2 * M_PI * geoBSphere.radius * geoBSphere.radius; } int depth = 2; Ray ray(pRec.p, dRec.d, time); Intersection its; while (!weight.isZero() && (depth < maxDepth || maxDepth == -1)) { if (!scene->rayIntersect(ray, its)) break; const BSDF *bsdf = its.getBSDF(); BSDFSamplingRecord bRec(its, sampler, EImportance); Spectrum bsdfVal = bsdf->sample(bRec, sampler->next2D()); if (bsdfVal.isZero()) break; /* Assuming that BSDF importance sampling is perfect, the following should equal the maximum albedo over all spectral samples */ Float approxAlbedo = std::min((Float) 0.95f, bsdfVal.max()); if (sampler->next1D() > approxAlbedo) break; else weight /= approxAlbedo; VPL vpl(ESurfaceVPL, weight); vpl.its = its; if (BSDF::getMeasure(bRec.sampledType) == ESolidAngle) appendVPL(scene, random, vpl, prune, vpls); weight *= bsdfVal; Vector wi = -ray.d, wo = its.toWorld(bRec.wo); ray = Ray(its.p, wo, 0.0f); /* Prevent light leaks due to the use of shading normals -- [Veach, p. 158] */ Float wiDotGeoN = dot(its.geoFrame.n, wi), woDotGeoN = dot(its.geoFrame.n, wo); if (wiDotGeoN * Frame::cosTheta(bRec.wi) <= 0 || woDotGeoN * Frame::cosTheta(bRec.wo) <= 0) break; /* Disabled for now -- this increases VPL weights and accuracy is not really a big requirement */ #if 0 /* Adjoint BSDF for shading normals -- [Veach, p. 155] */ weight *= std::abs( (Frame::cosTheta(bRec.wi) * woDotGeoN)/ (Frame::cosTheta(bRec.wo) * wiDotGeoN)); #endif ++depth; } size_t end = vpls.size(); for (size_t i=start; i<end; ++i) vpls[i].emitterScale = 1.0f / (end - start); } return offset; }
Color3f Li(const Scene *scene, Sampler *sampler, const Ray3f &r) const { /* Find the surface that is visible in the requested direction */ Intersection its; Ray3f ray(r); if (!scene->rayIntersect(ray, its)) return Color3f(0.0f); Color3f radiance(0.0f); bool specularBounce = false; Color3f pathThroughput(1.0f); for ( int bounces = 0; ; ++bounces ) { const Luminaire* luminaire = its.mesh->getLuminaire(); if ((bounces == 0 || specularBounce) && luminaire != NULL) { Vector3f wo = (-ray.d).normalized(); Color3f emission = luminaire->le(its.p, its.shFrame.n, wo); radiance += pathThroughput*emission; } const Texture* texture = its.mesh->getTexture(); Color3f texel(1.0f); if ( texture ) { texel = texture->lookUp(its.uv.x(), its.uv.y()); } const BSDF* bsdf = its.mesh->getBSDF(); // sample illumination from lights, add to path contribution if (!bsdf->isSpecular()){ radiance += pathThroughput*UniformSampleAllLights(scene, ray, its, sampler, m_samplePolicy)*texel; } // sample bsdf to get new path direction BSDFQueryRecord bRec(its.toLocal((-ray.d)).normalized()); Color3f f = bsdf->sample(bRec, sampler->next2D() ); if (f.isZero() ) { // farther path no contribution break; } specularBounce = bsdf->isSpecular(); Vector3f d = its.toWorld(bRec.wo); f *= texel; pathThroughput *= f; ray = Ray3f(its.p, d ); // possibly termination if (bounces > kSampleDepth) { #if 0 float continueProbability = std::min( 0.5f, pathThroughput.y() ); if ( sampler->next1D() > continueProbability ) { break; } #else float continueProbability = std::max(f.x(), std::max(f.y(), f.z())); if ( sampler->next1D() > continueProbability ) { break; } #endif pathThroughput /= continueProbability; } if (bounces == m_maxDepth) { break; } // find next vertex of path if ( !scene->rayIntersect(ray, its) ) { break; } } return radiance; }