77#include < glm/gtx/norm.hpp>
88#include < light/Light.hpp>
99#include < light/SphereLight.hpp>
10+ #include < limits>
1011#include < memory>
1112#include < numeric> // For std::accumulate
1213#include < scene/Scene.hpp>
@@ -394,7 +395,88 @@ SoftRasterizer::Intersection SoftRasterizer::Scene::traceScene(Ray &ray) {
394395 return ret;
395396}
396397
397- glm::vec3 SoftRasterizer::Scene::whittedRayTracing (Ray &ray, int depth, const std::size_t sample) {
398+ [[nodiscard]] std::tuple<glm::dvec3, double >
399+ SoftRasterizer::Scene::sampleLightOnCenter (const glm::vec3 &shadingPoint) {
400+ // Collect all emissive objects and approximate their bounding spheres**
401+ std::vector<std::pair<glm::dvec3, double >> lightSpheres;
402+ for (const auto &obj : m_exportedObjs) {
403+ if (obj->isSelfEmissiveObject ()) {
404+ Bounds3 bbox = obj->getBounds ();
405+ glm::dvec3 center = (glm::dvec3 (bbox.min ) + glm::dvec3 (bbox.max )) * 0.5 ;
406+ double radius = glm::length (glm::dvec3 (bbox.diagonal ())) * 0.5 ;
407+ lightSpheres.emplace_back (center, radius);
408+ }
409+ }
410+
411+ if (lightSpheres.empty ()) {
412+ spdlog::warn (" No emissive objects found in the scene!" );
413+ return {glm::dvec3 (0.0 ), 0.0 };
414+ }
415+
416+ // Randomly select a light source
417+ int randomIndex =
418+ static_cast <int >(Tools::random_generator () * lightSpheres.size ());
419+ glm::dvec3 sphereCenter = lightSpheres[randomIndex].first ;
420+ double sphereRadius = lightSpheres[randomIndex].second ;
421+
422+ glm::dvec3 lightDir = glm::normalize (sphereCenter - glm::dvec3 (shadingPoint));
423+
424+ // Compute probability density function (PDF)
425+ double pdf = 0.5 * Tools::PI_INV ;
426+ return {lightDir, pdf};
427+ }
428+
429+ std::tuple<glm::dvec3, double >
430+ SoftRasterizer::Scene::sampleLight (const glm::vec3 &shadingPoint) {
431+ // Collect all emissive objects and approximate their bounding spheres**
432+ std::vector<std::pair<glm::dvec3, double >> lightSpheres;
433+ for (const auto &obj : m_exportedObjs) {
434+ if (obj->isSelfEmissiveObject ()) {
435+ Bounds3 bbox = obj->getBounds ();
436+ glm::dvec3 center = (glm::dvec3 (bbox.min ) + glm::dvec3 (bbox.max )) * 0.5 ;
437+ double radius = glm::length (glm::dvec3 (bbox.diagonal ())) * 0.5 ;
438+ lightSpheres.emplace_back (center, radius);
439+ }
440+ }
441+
442+ if (lightSpheres.empty ()) {
443+ spdlog::warn (" No emissive objects found in the scene!" );
444+ return {glm::dvec3 (0.0 ), 0.0 };
445+ }
446+
447+ // Randomly select a light source
448+ int randomIndex =
449+ static_cast <int >(Tools::random_generator () * lightSpheres.size ());
450+ glm::dvec3 sphereCenter = lightSpheres[randomIndex].first ;
451+ double sphereRadius = lightSpheres[randomIndex].second ;
452+
453+ glm::dvec3 baselineDir =
454+ glm::normalize (sphereCenter - glm::dvec3 (shadingPoint));
455+
456+ // Sample a random direction on the light source sphere
457+ glm::dvec3 sampleDir = glm::sphericalRand (1.0 );
458+ if (glm::dot (sampleDir, baselineDir) < 0.0 ) {
459+ sampleDir = -sampleDir;
460+ }
461+
462+ // Apply random perturbation for anti-aliasing and soft shadow
463+ double perturbationStrength = 1e-6 ;
464+ glm::dvec3 randomPerturbation = glm::sphericalRand (perturbationStrength);
465+ sampleDir = glm::normalize (sampleDir + randomPerturbation);
466+
467+ glm::dvec3 samplePos = sphereCenter + sampleDir * sphereRadius;
468+
469+ // Compute direction from shading point to light source
470+ glm::dvec3 lightDir = glm::normalize (samplePos - glm::dvec3 (shadingPoint));
471+
472+ // Compute probability density function (PDF)
473+ double cosTheta = glm::dot (lightDir, baselineDir);
474+ double pdf = 0.5 * Tools::PI_INV * cosTheta;
475+ return {lightDir, pdf};
476+ }
477+
478+ glm::vec3 SoftRasterizer::Scene::whittedRayTracing (Ray &ray, int depth,
479+ const std::size_t sample) {
398480
399481 glm::vec3 final_color = this ->m_backgroundColor ;
400482
@@ -408,7 +490,7 @@ glm::vec3 SoftRasterizer::Scene::whittedRayTracing(Ray &ray, int depth, const st
408490 }
409491
410492 Intersection intersection = traceScene (ray);
411- if (!intersection.intersected || !intersection. obj ) {
493+ if (!intersection.intersected ) {
412494 // Return black if the ray does not intersect with any object
413495 spdlog::debug (" Ray Intersection Not Found On Depth {}" , depth);
414496 return this ->m_backgroundColor ;
@@ -427,65 +509,70 @@ glm::vec3 SoftRasterizer::Scene::whittedRayTracing(Ray &ray, int depth, const st
427509
428510 /* Phong illumation model*/
429511 if (intersection.material ->getMaterialType () ==
430- MaterialType::DIFFUSE_AND_GLOSSY ) {
431-
432- final_color = tbb::parallel_reduce (
433- tbb::blocked_range<std::size_t >(0 , sample),
434- glm::vec3 (0 .0f ), // Initial value
435- [&](const tbb::blocked_range<std::size_t >& range,
436- glm::vec3 local_sum) -> glm::vec3 {
437- for (std::size_t i = range.begin (); i < range.end (); ++i) {
438- auto [shading2LightDir, lightAreaPdf] = sampleLightOnCenter (hitPoint);
439-
440- Ray lightSampleRay (hitPoint, shading2LightDir);
441-
442- Intersection lightSampleIntersection = traceScene (lightSampleRay);
443- if (!lightSampleIntersection.intersected ){
444- // || (lightSampleIntersection.intersected && glm::length(lightSampleIntersection.emit) < m_epsilon)) {
445- return glm::vec3 (0 .f );
446- }
447-
448- // Diffuse reflection (Lambertian)
449- double diff = std::max (0 ., glm::dot (glm::dvec3 (N), shading2LightDir));
450-
451- // Specular reflection (Blinn-Phong)
452- glm::dvec3 reflectDir =
453- glm::normalize (glm::reflect (-shading2LightDir, glm::dvec3 (hitNormal)));
454- double spec =
455- std::pow (std::max (0 ., -glm::dot (glm::dvec3 (ray.direction ), reflectDir)),
456- intersection.material ->specularExponent );
457-
458- double distanceSquare =
459- glm::length2 (hitPoint - lightSampleIntersection.coords );
460- double timeSquare = lightSampleIntersection.intersect_time *
461- lightSampleIntersection.intersect_time ;
462- bool is_shadow = std::abs (timeSquare - distanceSquare) > 1e-6f ;
463-
464- spdlog::debug (" timeSquare={}, distanceSquare={},delta = {}, is_shadow={}" ,
465- timeSquare, distanceSquare,
466- std::abs (distanceSquare - timeSquare), is_shadow);
467-
468- // Compute light contribution
469- glm::vec3 ambient =
470- !is_shadow ? lightSampleIntersection.emit : glm::vec3 (0 .f );
471- glm::vec3 diffuse = !is_shadow
472- ? glm::vec3 (diff) * lightSampleIntersection.emit
473- : glm::vec3 (0 .f );
474- glm::vec3 specular = spec * glm::dvec3 (lightSampleIntersection.emit );
475-
476- // Accumulate to local sum
477- local_sum = (ambient * intersection.material ->Ka ) +
478- (intersection.color * diffuse) +
479- (specular * intersection.material ->Ks );
480- }
481- return local_sum;
482- },
483- [](const glm::vec3& a, const glm::vec3& b) {
484- return a + b;
485- } // Combine partial results
486- ) ;
487-
488- final_color /= sample;
512+ MaterialType::DIFFUSE_AND_GLOSSY ) {
513+
514+ final_color = tbb::parallel_reduce (
515+ tbb::blocked_range<std::size_t >(0 , sample),
516+ glm::vec3 (0 .0f ), // Initial value
517+ [&](const tbb::blocked_range<std::size_t > &range,
518+ glm::vec3 local_sum) -> glm::vec3 {
519+ for (std::size_t i = range.begin (); i < range.end (); ++i) {
520+
521+ auto [shading2LightDir, lightAreaPdf] =
522+ sampleLightOnCenter (hitPoint);
523+
524+ Ray lightSampleRay (hitPoint, shading2LightDir);
525+ Intersection lightSampleIntersection = traceScene (lightSampleRay);
526+ if (!lightSampleIntersection.intersected ||
527+ (lightSampleIntersection.intersected &&
528+ glm::length (lightSampleIntersection.emit ) < m_epsilon)) {
529+ return glm::vec3 (0 .f );
530+ }
531+
532+ // Diffuse reflection (Lambertian)
533+ double diff =
534+ std::max (0 ., glm::dot (glm::dvec3 (N), shading2LightDir));
535+
536+ // Specular reflection (Blinn-Phong)
537+ glm::dvec3 reflectDir = glm::normalize (
538+ glm::reflect (-shading2LightDir, glm::dvec3 (hitNormal)));
539+ double spec = std::pow (
540+ std::max (0 ., -glm::dot (glm::dvec3 (ray.direction ), reflectDir)),
541+ intersection.material ->specularExponent );
542+
543+ double distanceSquare =
544+ glm::length2 (hitPoint - lightSampleIntersection.coords );
545+ double timeSquare = lightSampleIntersection.intersect_time *
546+ lightSampleIntersection.intersect_time ;
547+ bool is_shadow = std::abs (timeSquare - distanceSquare) > 1e-6f ;
548+
549+ spdlog::debug (
550+ " timeSquare={}, distanceSquare={},delta = {}, is_shadow={}" ,
551+ timeSquare, distanceSquare,
552+ std::abs (distanceSquare - timeSquare), is_shadow);
553+
554+ // Compute light contribution
555+ glm::vec3 ambient =
556+ !is_shadow ? lightSampleIntersection.emit : glm::vec3 (0 .f );
557+ glm::vec3 diffuse =
558+ !is_shadow ? glm::vec3 (diff) * lightSampleIntersection.emit
559+ : glm::vec3 (0 .f );
560+ glm::vec3 specular =
561+ spec * glm::dvec3 (lightSampleIntersection.emit );
562+
563+ // Accumulate to local sum
564+ local_sum = (ambient * intersection.material ->Ka ) +
565+ (intersection.color * diffuse) +
566+ (specular * intersection.material ->Ks );
567+ }
568+ return local_sum;
569+ },
570+ [](const glm::vec3 &a, const glm::vec3 &b) {
571+ return a + b;
572+ } // Combine partial results
573+ );
574+
575+ final_color /= sample;
489576 }
490577
491578 else if (intersection.material ->getMaterialType () ==
@@ -622,87 +709,6 @@ SoftRasterizer::Scene::sampleLight() {
622709 return {intersection, pdf};
623710}
624711
625- [[nodiscard]] std::tuple<glm::dvec3, double >
626- SoftRasterizer::Scene::sampleLightOnCenter (const glm::vec3& shadingPoint) {
627- // Collect all emissive objects and approximate their bounding spheres**
628- std::vector<std::pair<glm::dvec3, double >> lightSpheres;
629- for (const auto & obj : m_exportedObjs) {
630- if (obj->isSelfEmissiveObject ()) {
631- Bounds3 bbox = obj->getBounds ();
632- glm::dvec3 center = (glm::dvec3 (bbox.min ) + glm::dvec3 (bbox.max )) * 0.5 ;
633- double radius = glm::length (glm::dvec3 (bbox.diagonal ())) * 0.5 ;
634- lightSpheres.emplace_back (center, radius);
635- }
636- }
637-
638- if (lightSpheres.empty ()) {
639- spdlog::warn (" No emissive objects found in the scene!" );
640- return { glm::dvec3 (0.0 ), 0.0 };
641- }
642-
643- // Randomly select a light source
644- int randomIndex =
645- static_cast <int >(Tools::random_generator () * lightSpheres.size ());
646- glm::dvec3 sphereCenter = lightSpheres[randomIndex].first ;
647- double sphereRadius = lightSpheres[randomIndex].second ;
648-
649- // Compute direction from shading point to light source
650- glm::dvec3 lightDir = glm::normalize (sphereCenter - glm::dvec3 (shadingPoint));
651-
652- // Compute probability density function (PDF)
653- double pdf = 0.5 * Tools::PI_INV ;
654- return { lightDir, pdf };
655- }
656-
657- std::tuple<glm::dvec3, double >
658- SoftRasterizer::Scene::sampleLight (const glm::vec3 &shadingPoint) {
659- // Collect all emissive objects and approximate their bounding spheres**
660- std::vector<std::pair<glm::dvec3, double >> lightSpheres;
661- for (const auto &obj : m_exportedObjs) {
662- if (obj->isSelfEmissiveObject ()) {
663- Bounds3 bbox = obj->getBounds ();
664- glm::dvec3 center = (glm::dvec3 (bbox.min ) + glm::dvec3 (bbox.max )) * 0.5 ;
665- double radius = glm::length (glm::dvec3 (bbox.diagonal ())) * 0.5 ;
666- lightSpheres.emplace_back (center, radius);
667- }
668- }
669-
670- if (lightSpheres.empty ()) {
671- spdlog::warn (" No emissive objects found in the scene!" );
672- return {glm::dvec3 (0.0 ), 0.0 };
673- }
674-
675- // Randomly select a light source
676- int randomIndex =
677- static_cast <int >(Tools::random_generator () * lightSpheres.size ());
678- glm::dvec3 sphereCenter = lightSpheres[randomIndex].first ;
679- double sphereRadius = lightSpheres[randomIndex].second ;
680-
681- glm::dvec3 baselineDir =
682- glm::normalize (sphereCenter - glm::dvec3 (shadingPoint));
683-
684- // Sample a random direction on the light source sphere
685- glm::dvec3 sampleDir = glm::sphericalRand (1.0 );
686- if (glm::dot (sampleDir, baselineDir) < 0.0 ) {
687- sampleDir = -sampleDir;
688- }
689-
690- // Apply random perturbation for anti-aliasing and soft shadow
691- double perturbationStrength = 1e-6 ;
692- glm::dvec3 randomPerturbation = glm::sphericalRand (perturbationStrength);
693- sampleDir = glm::normalize (sampleDir + randomPerturbation);
694-
695- glm::dvec3 samplePos = sphereCenter + sampleDir * sphereRadius;
696-
697- // Compute direction from shading point to light source
698- glm::dvec3 lightDir = glm::normalize (samplePos - glm::dvec3 (shadingPoint));
699-
700- // Compute probability density function (PDF)
701- double cosTheta = glm::dot (lightDir, baselineDir);
702- double pdf = 0.5 * Tools::PI_INV * cosTheta;
703- return {lightDir, pdf};
704- }
705-
706712glm::vec3 SoftRasterizer::Scene::pathTracingDirectLight (
707713 const Intersection &shadeObjIntersection, const glm::vec3 &wo) {
708714
0 commit comments