static Color3 finalGathering(KdTree<Photon> *map , Scene& scene , Intersection& inter , RNG& rng , const Vector3& wo , int gatherSamples , int knn , Real maxSqrDis) { Color3 res = Color3(0.0 , 0.0 , 0.0); for (int i = 0; i < gatherSamples; i++) { Real pdf; Vector3 wi = sampleCosHemisphere(rng.randVector3() , &pdf); Ray ray = Ray(inter.p + wi * EPS , wi); Intersection _inter; Geometry *_g = scene.intersect(ray , _inter); if (_g == NULL) continue; Color3 tmp = estimate(map , 0 , knn , scene , _inter , -wi , maxSqrDis); BSDF bsdf(wi , _inter , scene); Real cosine , bsdfPdf; Color3 brdf = bsdf.f(scene , wo , cosine , &bsdfPdf); if (brdf.isBlack()) continue; pdf *= bsdfPdf; res = res + (tmp | brdf) * (cosine / pdf); } res = res / gatherSamples; return res; }
void InstantRadiosityRenderer::processPixel(uint32_t threadID, uint32_t tileID, const Vec2u& pixel) const { PixelSensor pixelSensor(getSensor(), pixel, getFramebufferSize()); RaySample raySample; float positionPdf, directionPdf; auto We = pixelSensor.sampleExitantRay(getScene(), getFloat2(threadID), getFloat2(threadID), raySample, positionPdf, directionPdf); auto L = zero<Vec3f>(); if(We != zero<Vec3f>() && raySample.pdf) { auto I = getScene().intersect(raySample.value); BSDF bsdf(-raySample.value.dir, I, getScene()); if(I) { // Direct illumination for(auto& vpl: m_EmissionVPLBuffer) { RaySample shadowRay; auto Le = vpl.pLight->sampleDirectIllumination(getScene(), vpl.emissionVertexSample, I, shadowRay); if(shadowRay.pdf) { auto contrib = We * Le * bsdf.eval(shadowRay.value.dir) * abs(dot(I.Ns, shadowRay.value.dir)) / (vpl.lightPdf * shadowRay.pdf * m_nLightPathCount * raySample.pdf); if(contrib != zero<Vec3f>()) { if(!getScene().occluded(shadowRay.value)) { L += contrib; } } } } // Indirect illumination for(auto& vpl: m_SurfaceVPLBuffer) { auto dirToVPL = vpl.intersection.P - I.P; auto dist = length(dirToVPL); if(dist > 0.f) { dirToVPL /= dist; auto fs1 = bsdf.eval(dirToVPL); auto fs2 = vpl.bsdf.eval(-dirToVPL); auto geometricFactor = abs(dot(I.Ns, dirToVPL)) * abs(dot(vpl.intersection.Ns, -dirToVPL)) / sqr(dist); auto contrib = We * vpl.power * fs1 * fs2 * geometricFactor / raySample.pdf; if(contrib != zero<Vec3f>()) { Ray shadowRay(I, vpl.intersection, dirToVPL, dist); if(!getScene().occluded(shadowRay)) { L += contrib; } } } } } } accumulate(0, getPixelIndex(pixel.x, pixel.y), Vec4f(L, 1.f)); }
bool UniversalMaterial::coverageLessThan(const float alphaThreshold, const Point2& texCoord) const { const Component4& lambertian = bsdf()->lambertian(); if (lambertian.min().a > alphaThreshold) { // Opaque pixel return false; } const Image4::Ref& image = lambertian.image(); if (isNull(image)) { return lambertian.constant().a <= alphaThreshold; } const Point2& t = texCoord * Vector2(float(image->width()), float(image->height())); return (image->nearest(t).a * lambertian.constant().a < alphaThreshold); }
UniversalBSDF::Ref UniversalBSDF::create (const Component4& lambertian, const Component4& glossy, const Component3& transmissive, float eta_t, const Color3& extinction_t, float eta_r, const Color3& extinction_r) { UniversalBSDF::Ref bsdf(new UniversalBSDF()); bsdf->m_lambertian = lambertian; bsdf->m_glossy = glossy; bsdf->m_transmissive = transmissive; bsdf->m_eta_t = eta_t; bsdf->m_extinction_t = extinction_t; bsdf->m_eta_r = eta_r; bsdf->m_extinction_r = extinction_r; return bsdf; }
static Color3 directIllumination(Scene& scene , Intersection& inter , RNG& rng , const Vector3& visionDir) { Color3 res = Color3(0.0 , 0.0 , 0.0); Vector3 lightDir; Real cosine; Color3 brdf; Ray ray; BSDF bsdf(visionDir , inter , scene); int N = 1; for (int i = 0; i < N; i++) { int k = rand() % scene.lights.size(); AbstractLight *l = scene.lights[k]; Vector3 dirToLight; Real dist , directPdf; Color3 illu = l->illuminance(scene.sceneSphere , inter.p , rng.randVector3() , dirToLight , dist , directPdf); illu = illu / (directPdf / scene.lights.size()); if (scene.occluded(inter.p + dirToLight * EPS , dirToLight , inter.p + dirToLight * (inter.t - EPS))) continue; Real bsdfPdf; brdf = bsdf.f(scene , dirToLight , cosine , &bsdfPdf); if (brdf.isBlack()) continue; res = res + (illu | brdf) * (cosine / bsdfPdf); } res = res / N; return res; }
void test_d_bsdf() { Vector3f d{0.5, 0.4, 0.3}; Vector2f uv_scale{1, 1}; Texture3 diffuse{&d[0], -1, -1, -1, &uv_scale[0]}; Vector3f s{0.2, 0.3, 0.4}; Texture3 specular{&s[0], -1, -1, -1, &uv_scale[0]}; float r = 0.5; Texture1 roughness{&r, -1, -1, -1, &uv_scale[0]}; Material m{diffuse, specular, roughness,false}; DTexture3 d_diffuse_tex; DTexture3 d_specular_tex; DTexture1 d_roughness_tex; SurfacePoint p{Vector3{0, 0, 0}, Vector3{0, 1, 0}, Frame(Vector3{0, 1, 0}), Vector2{0.5, 0.5}}; auto wi = normalize(Vector3{0.5, 1.0, 0.5}); auto wo = normalize(Vector3{-0.5, 1.0, -0.5}); auto min_roughness = Real(0); auto d_p = SurfacePoint::zero(); auto d_wi = Vector3{0, 0, 0}; auto d_wo = Vector3{0, 0, 0}; d_bsdf(m, p, wi, wo, min_roughness, Vector3{1, 1, 1}, d_diffuse_tex, d_specular_tex, d_roughness_tex, d_p, d_wi, d_wo); // Check diffuse derivatives auto finite_delta = Real(1e-6); for (int i = 0; i < 3; i++) { auto delta_m = m; delta_m.diffuse_reflectance.texels[i] += finite_delta; auto positive = bsdf(delta_m, p, wi, wo, min_roughness); delta_m.diffuse_reflectance.texels[i] -= 2 * finite_delta; auto negative = bsdf(delta_m, p, wi, wo, min_roughness); auto diff = sum(positive - negative) / (2 * finite_delta); equal_or_error(__FILE__, __LINE__, diff, d_diffuse_tex.t000[i]); } // Check specular derivatives for (int i = 0; i < 3; i++) { auto delta_m = m; delta_m.specular_reflectance.texels[i] += finite_delta; auto positive = bsdf(delta_m, p, wi, wo, min_roughness); delta_m.specular_reflectance.texels[i] -= 2 * finite_delta; auto negative = bsdf(delta_m, p, wi, wo, min_roughness); auto diff = sum(positive - negative) / (2 * finite_delta); equal_or_error(__FILE__, __LINE__, diff, d_specular_tex.t000[i]); } // Check roughness derivatives { auto delta_m = m; delta_m.roughness.texels[0] += finite_delta; auto positive = bsdf(delta_m, p, wi, wo, min_roughness); delta_m.roughness.texels[0] -= 2 * finite_delta; auto negative = bsdf(delta_m, p, wi, wo, min_roughness); auto diff = sum(positive - negative) / (2 * finite_delta); equal_or_error(__FILE__, __LINE__, diff, d_roughness_tex.t000); } // Check surface point derivatives equal_or_error<Real>(__FILE__, __LINE__, Vector3{0, 0, 0}, d_p.position); equal_or_error<Real>(__FILE__, __LINE__, Vector3{0, 0, 0}, d_p.geom_normal); // Shading frame x for (int i = 0; i < 3; i++) { auto delta_p = p; delta_p.shading_frame.x[i] += finite_delta; auto positive = bsdf(m, delta_p, wi, wo, min_roughness); delta_p.shading_frame.x[i] -= 2 * finite_delta; auto negative = bsdf(m, delta_p, wi, wo, min_roughness); auto diff = sum(positive - negative) / (2 * finite_delta); equal_or_error(__FILE__, __LINE__, diff, d_p.shading_frame.x[i]); } // Shading frame y for (int i = 0; i < 3; i++) { auto delta_p = p; delta_p.shading_frame.y[i] += finite_delta; auto positive = bsdf(m, delta_p, wi, wo, min_roughness); delta_p.shading_frame.y[i] -= 2 * finite_delta; auto negative = bsdf(m, delta_p, wi, wo, min_roughness); auto diff = sum(positive - negative) / (2 * finite_delta); equal_or_error(__FILE__, __LINE__, diff, d_p.shading_frame.y[i]); } // Shading frame n for (int i = 0; i < 3; i++) { auto delta_p = p; delta_p.shading_frame.n[i] += finite_delta; auto positive = bsdf(m, delta_p, wi, wo, min_roughness); delta_p.shading_frame.n[i] -= 2 * finite_delta; auto negative = bsdf(m, delta_p, wi, wo, min_roughness); auto diff = sum(positive - negative) / (2 * finite_delta); equal_or_error(__FILE__, __LINE__, diff, d_p.shading_frame.n[i]); } // uv for (int i = 0; i < 2; i++) { auto delta_p = p; delta_p.uv[i] += finite_delta; auto positive = bsdf(m, delta_p, wi, wo, min_roughness); delta_p.uv[i] -= 2 * finite_delta; auto negative = bsdf(m, delta_p, wi, wo, min_roughness); auto diff = sum(positive - negative) / (2 * finite_delta); equal_or_error(__FILE__, __LINE__, diff, d_p.uv[i]); } // Check wi, wo for (int i = 0; i < 3; i++) { auto delta_wi = wi; delta_wi[i] += finite_delta; auto positive = bsdf(m, p, delta_wi, wo, min_roughness); delta_wi[i] -= 2 * finite_delta; auto negative = bsdf(m, p, delta_wi, wo, min_roughness); auto diff = sum(positive - negative) / (2 * finite_delta); equal_or_error(__FILE__, __LINE__, diff, d_wi[i]); } for (int i = 0; i < 3; i++) { auto delta_wo = wo; delta_wo[i] += finite_delta; auto positive = bsdf(m, p, wi, delta_wo, min_roughness); delta_wo[i] -= 2 * finite_delta; auto negative = bsdf(m, p, wi, delta_wo, min_roughness); auto diff = sum(positive - negative) / (2 * finite_delta); equal_or_error(__FILE__, __LINE__, diff, d_wo[i]); } }
void PhotonIntegrator::buildPhotonMap(Scene& scene) { if (scene.lights.size() <= 0) return; causticPhotons.clear(); indirectPhotons.clear(); bool causticDone = 0 , indirectDone = 0; int nShot = 0; bool specularPath; int nIntersections; Intersection inter; Real lightPickProb = 1.f / scene.lights.size(); while (!causticDone || !indirectDone) { nShot++; /* generate initial photon ray */ int k = (int)(rng.randFloat() * scene.lights.size()); Vector3 lightPos , lightDir; Real emissionPdf , directPdfArea , cosAtLight; Color3 alpha; AbstractLight *l = scene.lights[k]; alpha = l->emit(scene.sceneSphere , rng.randVector3() , rng.randVector3() , lightPos , lightDir , emissionPdf , &directPdfArea , &cosAtLight); alpha = alpha * cosAtLight / (emissionPdf * lightPickProb); Ray photonRay(lightPos , lightDir); if (!alpha.isBlack()) { specularPath = 1; nIntersections = 0; Geometry *g = scene.intersect(photonRay , inter); while (g != NULL && inter.matId > 0) { BSDF bsdf(-photonRay.dir , inter , scene); nIntersections++; bool hasNonSpecular = (cmp(bsdf.componentProb.diffuseProb) > 0 || cmp(bsdf.componentProb.glossyProb) > 0); if (hasNonSpecular) { Photon photon(inter.p , -photonRay.dir , alpha); if (specularPath && nIntersections > 1) { if (!causticDone) { causticPhotons.push_back(photon); if (causticPhotons.size() == nCausticPhotons) { causticDone = 1; nCausticPaths = nShot; causticMap = new KdTree<Photon>(causticPhotons); } } } else { if (nIntersections > 1 && !indirectDone) { indirectPhotons.push_back(photon); if (indirectPhotons.size() == nIndirectPhotons) { indirectDone = 1; nIndirectPaths = nShot; indirectMap = new KdTree<Photon>(indirectPhotons); } } } } if (nIntersections > maxPathLength) break; /* find new photon ray direction */ /* handle specular reflection and transmission first */ Real pdf , cosWo; int sampledType; Color3 bsdfFactor = bsdf.sample(scene , rng.randVector3() , photonRay.dir , pdf , cosWo , &sampledType); if (bsdfFactor.isBlack()) break; if (sampledType & BSDF_NON_SPECULAR) specularPath = 0; // Russian Roulette Real contProb = bsdf.continueProb; if (cmp(contProb - 1.f) < 0) { if (cmp(rng.randFloat() - contProb) > 0) break; pdf *= contProb; } alpha = (alpha | bsdfFactor) * (cosWo / pdf); photonRay.origin = inter.p + photonRay.dir * EPS; photonRay.tmin = 0.f; photonRay.tmax = INF; g = scene.intersect(photonRay , inter); } } } }
Color3 PhotonIntegrator::raytracing(const Ray& ray , int dep) { Color3 res = Color3(0.0 , 0.0 , 0.0); if (dep > 2) return res; Geometry *g = NULL; Intersection inter; Ray reflectRay , transRay; g = scene.intersect(ray , inter); if (g == NULL) return res; if (inter.matId < 0) { AbstractLight *l = scene.lights[-inter.matId - 1]; return l->getIntensity() * 100.f; } res = res + directIllumination(scene , inter , rng , -ray.dir); res = res + estimate(indirectMap , nIndirectPaths , knnPhotons , scene , inter , -ray.dir , maxSqrDis); // final gathering is too slow! //res = res + finalGathering(indirectMap , scene , inter , rng , -ray.dir , 50 , knnPhotons , maxSqrDis); res = res + estimate(causticMap , nCausticPaths , knnPhotons , scene , inter , -ray.dir , maxSqrDis); BSDF bsdf(-ray.dir , inter , scene); Real pdf , cosWo; int sampledType; Ray newRay; Color3 bsdfFactor = bsdf.sample(scene , rng.randVector3() , newRay.dir , pdf , cosWo , &sampledType); if (bsdfFactor.isBlack()) return res; // Russian Roulette Real contProb = bsdf.continueProb; if (cmp(contProb - 1.f) < 0) { if (cmp(rng.randFloat() - contProb) > 0) return res; pdf *= contProb; } newRay.origin = inter.p + newRay.dir * EPS; newRay.tmin = 0; newRay.tmax = INF; Color3 contrib = raytracing(newRay , dep + 1); res = res + (contrib | bsdfFactor) * (cosWo / pdf); return res; }
static Color3 estimate(KdTree<Photon> *map , int nPaths , int knn , Scene& scene , Intersection& inter , const Vector3& wo , Real maxSqrDis) { Color3 res = Color3(0.0 , 0.0 , 0.0); if (map == NULL) return res; Photon photon; photon.pos = inter.p; ClosePhotonQuery query(knn , 0); Real searchSqrDis = maxSqrDis; Real msd; /* max square distance */ while (query.kPhotons.size() < knn) { msd = searchSqrDis; query.init(msd); map->searchKnn(0 , photon.pos , query); searchSqrDis *= 2.0; } int nFoundPhotons = query.kPhotons.size(); if (nFoundPhotons == 0) return res; Vector3 nv; if (cmp(wo ^ inter.n) < 0) nv = -inter.n; else nv = inter.n; Real scale = 1.0 / (PI * msd * nFoundPhotons); BSDF bsdf(wo , inter , scene); Real cosTerm , pdf; for (int i = 0; i < nFoundPhotons; i++) { /* Real k = kernel(kPhotons[i].photon , p , msd); k = 1.0 / PI; Real scale = k / (nPaths * msd); */ if (cmp(nv ^ query.kPhotons[i].photon->wi) > 0) { Color3 brdf = bsdf.f(scene , query.kPhotons[i].photon->wi , cosTerm , &pdf); if (brdf.isBlack()) continue; res = res + (brdf | query.kPhotons[i].photon->alpha) * (cosTerm * scale / pdf); } else { Vector3 wi(query.kPhotons[i].photon->wi); wi.z *= -1.f; Color3 brdf = bsdf.f(scene , wi , cosTerm , &pdf); if (brdf.isBlack()) continue; res = res + (brdf | query.kPhotons[i].photon->alpha) * (cosTerm * scale / pdf); } } return res; }
void BidirPathTracing::runIteration(int iter) { lightPathNum = height * width; cameraPathNum = lightPathNum; lightStateIndex.resize(lightPathNum); memset(&lightStateIndex[0] , 0 , lightStateIndex.size() * sizeof(int)); lightStates.reserve(lightPathNum); lightStates.clear(); cameraStates.reserve(cameraPathNum); cameraStates.clear(); // generating light paths for (int pathIndex = 0; pathIndex < lightPathNum; pathIndex++) { BidirPathState lightState; bool deltaLight; generateLightSample(lightState); int s = 1; for (;; lightState.pathLength++ , s++) { Ray ray(lightState.origin + lightState.dir * EPS , lightState.dir); Intersection inter; if (scene.intersect(ray , inter) == NULL) break; Vector3 hitPos = inter.p; BSDF bsdf(-ray.dir , inter , scene); if (!bsdf.isValid()) break; lightState.pos = hitPos; lightState.bsdf = bsdf; if (lightState.pathLength > 1 || lightState.isFiniteLight) { lightState.dVCM *= mis(SQR(inter.t)); } lightState.dVCM /= mis(std::abs(bsdf.cosWi())); lightState.dVC /= mis(std::abs(bsdf.cosWi())); if (!bsdf.isDelta) lightStates.push_back(lightState); // connect to camera if (!bsdf.isDelta) { if (lightState.pathLength + 1 >= minPathLength && lightState.pathLength + 1 == controlLength) { Vector3 imagePos = scene.camera.worldToRaster.tPoint(hitPos); if (scene.camera.checkRaster(imagePos.x , imagePos.y)) { Color3 res = connectToCamera(lightState , hitPos , bsdf); // weight //Real weight = 1.f / (lightState.pathLength + 1 - lightState.specularVertexNum); film->addColor((int)imagePos.x , (int)imagePos.y , res); } } } if (lightState.pathLength + 2 > maxPathLength) break; if (!sampleScattering(bsdf , hitPos , lightState)) break; } lightStateIndex[pathIndex] = (int)lightStates.size(); } // generating camera paths for (int index = 0; index < cameraPathNum; index++) { int pathIndex = index % (height * width); if (pathIndex == 111 * 512 + 364) { int flag = 1; } BidirPathState cameraState; Vector3 screenSample = generateCameraSample(pathIndex , cameraState); Color3 color(0); for (;; cameraState.pathLength++) { Ray ray(cameraState.origin + cameraState.dir * EPS , cameraState.dir); Intersection inter; if (scene.intersect(ray , inter) == NULL) { /* if (scene.background != NULL) { if (cameraState.pathLength >= minPathLength) { // weight Real weight = 1.f / (cameraState.pathLength - cameraState.specularVertexNum); color = color + (cameraState.throughput | getLightRadiance(scene.background , cameraState , Vector3(0) , ray.dir)) * weight; } } */ break; } Vector3 hitPos = inter.p; BSDF bsdf(-ray.dir , inter , scene); if (!bsdf.isValid()) break; cameraState.dVCM *= mis(SQR(inter.t)); cameraState.dVCM /= mis(std::abs(bsdf.cosWi())); cameraState.dVC /= mis(std::abs(bsdf.cosWi())); if (inter.matId < 0) { AbstractLight *light = scene.lights[-inter.matId - 1]; if (cameraState.pathLength >= minPathLength && cameraState.pathLength == controlLength) { // weight //Real weight = 1.f / (cameraState.pathLength - cameraState.specularVertexNum); color = color + (cameraState.throughput | getLightRadiance(light , cameraState , hitPos , ray.dir)); } break; } if (cameraState.pathLength >= maxPathLength) break; // vertex connection: connect to light source if (!bsdf.isDelta) { if (cameraState.pathLength + 1 >= minPathLength && cameraState.pathLength + 1 == controlLength) { // weight Real weight = 1.f / (cameraState.pathLength + 1.f - cameraState.specularVertexNum); color = color + (cameraState.throughput | getDirectIllumination(cameraState , hitPos , bsdf)) * weight; } } // vertex connection: connect to light vertices if (!bsdf.isDelta) { int st , ed; if (pathIndex == 0) st = 0; else st = lightStateIndex[pathIndex - 1]; ed = lightStateIndex[pathIndex]; for (int i = st; i < ed; i++) { BidirPathState& lightState = lightStates[i]; if (lightState.pathLength + 1 + cameraState.pathLength < minPathLength) continue; if (lightState.pathLength + 1 + cameraState.pathLength > maxPathLength) break; if (lightState.bsdf.isDelta) continue; Color3 tmp = connectVertices(lightState , bsdf , hitPos , cameraState); // weight Real weight = 1.f / (lightState.pathLength + 1.f + cameraState.pathLength - lightState.specularVertexNum - cameraState.specularVertexNum); if (lightState.pathLength + 1 + cameraState.pathLength == controlLength) color = color + (cameraState.throughput | lightState.throughput | tmp) * weight; } } if (!sampleScattering(bsdf , hitPos , cameraState)) break; } film->addColor((int)screenSample.x , (int)screenSample.y , color); } }