Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
103 changes: 55 additions & 48 deletions 31_HLSLPathTracer/app_resources/hlsl/next_event_estimator.hlsl
Original file line number Diff line number Diff line change
Expand Up @@ -177,9 +177,7 @@ struct ShapeSampling<T, PST_TRIANGLE, PPM_APPROX_PROJECTED_SOLID_ANGLE>
const vector3_type tri_vertices[3] = {tri.vertex0, tri.vertex1, tri.vertex2};
shapes::SphericalTriangle<scalar_type> st = shapes::SphericalTriangle<scalar_type>::create(tri_vertices, ray.origin);
sampling::ProjectedSphericalTriangle<scalar_type> pst = sampling::ProjectedSphericalTriangle<scalar_type>::create(st, ray.normalAtOrigin, ray.wasBSDFAtOrigin);
const scalar_type pdf = pst.backwardPdf(L);
// if `pdf` is NAN then the triangle's projected solid angle was close to 0.0, if its close to INF then the triangle was very small
return pdf < numeric_limits<scalar_type>::max ? pdf : numeric_limits<scalar_type>::max;
return pst.backwardWeight(L);
}

template<class Aniso>
Expand Down Expand Up @@ -246,6 +244,7 @@ template<typename T>
struct ShapeSampling<T, PST_RECTANGLE, PPM_SOLID_ANGLE>
{
using scalar_type = T;
using vector2_type = vector<T, 2>;
using vector3_type = vector<T, 3>;

static ShapeSampling<T, PST_RECTANGLE, PPM_SOLID_ANGLE> create(NBL_CONST_REF_ARG(Shape<T, PST_RECTANGLE>) rect)
Expand All @@ -262,48 +261,56 @@ struct ShapeSampling<T, PST_RECTANGLE, PPM_SOLID_ANGLE>
matrix<scalar_type, 3, 3> rectNormalBasis;
vector<T, 2> rectExtents;
rect.getNormalBasis(rectNormalBasis, rectExtents);

shapes::SphericalRectangle<scalar_type> sphR0;
sphR0.origin = rect.offset;
sphR0.extents = rectExtents;
sphR0.basis = rectNormalBasis;
scalar_type solidAngle = sphR0.solidAngle(ray.origin).value;
if (solidAngle > numeric_limits<scalar_type>::min)
pdf = 1.f / solidAngle;
else
pdf = bit_cast<scalar_type>(numeric_limits<scalar_type>::infinity);
return pdf;

// 1.f/0.f gives infinity no special checks needed
return 1.f / sphR0.solidAngle(ray.origin).value;
}

template<class Aniso>
vector3_type generate_and_pdf(NBL_REF_ARG(scalar_type) pdf, NBL_REF_ARG(scalar_type) newRayMaxT, NBL_CONST_REF_ARG(vector3_type) origin, NBL_CONST_REF_ARG(Aniso) interaction, NBL_CONST_REF_ARG(vector3_type) xi)
{
const vector3_type N = rect.getNormalTimesArea();
const vector3_type origin2origin = rect.offset - origin;

matrix<scalar_type, 3, 3> rectNormalBasis;
vector<T, 2> rectExtents;
rect.getNormalBasis(rectNormalBasis, rectExtents);

shapes::SphericalRectangle<scalar_type> sphR0;
sphR0.origin = rect.offset;
sphR0.extents = rectExtents;
sphR0.basis = rectNormalBasis;
vector3_type L = hlsl::promote<vector3_type>(0.0);

//
sampling::SphericalRectangle<scalar_type> ssph = sampling::SphericalRectangle<scalar_type>::create(sphR0, origin);
if ( ssph.solidAngle > numeric_limits<scalar_type>::min)
typename sampling::SphericalRectangle<scalar_type>::cache_type cache;

const vector3_type origin2origin = rect.offset - origin;
vector3_type L = hlsl::promote<vector3_type>(0.0);
const bool FastVersion = true;
if (FastVersion)
{
typename sampling::SphericalRectangle<scalar_type>::cache_type cache;
const vector3_type localDir = ssph.generate(xi.xy, cache);
// not sure if generate() can produce NaN/inf when solidAngle > min
assert(!hlsl::any(hlsl::isinf(localDir) || hlsl::isnan(localDir)));
// transform local direction to world space
L = localDir.x * rectNormalBasis[0] + localDir.y * rectNormalBasis[1] + localDir.z * rectNormalBasis[2];
pdf = ssph.forwardPdf(xi.xy, cache);
// actually the slowest
//L = ssph.generate(xi.xy, cache);
//newRayMaxT = ssph.computeHitT(L);

// fastest
const vector3_type localL = ssph.generateNormalizedLocal(xi.xy,cache,newRayMaxT);
L = hlsl::mul(hlsl::transpose(ssph.basis),localL);
}
else
pdf = bit_cast<scalar_type>(numeric_limits<scalar_type>::infinity);
{
L = ssph.generateUnnormalized(xi.xy,cache);
const scalar_type rcpLen = hlsl::rsqrt(hlsl::dot(L,L));
newRayMaxT = 1.f / rcpLen;
L *= rcpLen;
}
// prevent self intersections against the emitter
newRayMaxT -= 0.0001f;

newRayMaxT = hlsl::dot<vector3_type>(N, origin2origin) / hlsl::dot<vector3_type>(N, L);
pdf = ssph.forwardPdf(xi.xy,cache);
return L;
}

Expand All @@ -322,7 +329,6 @@ struct EffectivePolygonMethod<PST_SPHERE, PPM>
NBL_CONSTEXPR_STATIC_INLINE NEEPolygonMethod value = PPM_SOLID_ANGLE;
};


// Projected solid angle NEE for rectangles using "Practical Warps":
// bilinear warp over 4-corner NdotL + spherical rectangle sampling.
// Same grazing-angle limitations as the triangle variant -- see comments
Expand Down Expand Up @@ -352,21 +358,12 @@ struct ShapeSampling<T, PST_RECTANGLE, PPM_APPROX_PROJECTED_SOLID_ANGLE>
sphR0.extents = rectExtents;
sphR0.basis = rectNormalBasis;
sampling::ProjectedSphericalRectangle<scalar_type> psr = sampling::ProjectedSphericalRectangle<scalar_type>::create(sphR0, ray.origin, ray.normalAtOrigin, ray.wasBSDFAtOrigin);
// Reconstruct normalized [0,1]^2 position on the rectangle from the ray direction
const vector3_type N = rect.getNormalTimesArea();
const scalar_type t = hlsl::dot<vector3_type>(N, rect.offset - ray.origin) / hlsl::dot<vector3_type>(N, ray.direction);
const vector3_type hitPoint = ray.origin + ray.direction * t;
const vector3_type localHit = hitPoint - rect.offset;
const vector<T, 2> p = vector<T, 2>(hlsl::dot(localHit, rectNormalBasis[0]) / rectExtents.x, hlsl::dot(localHit, rectNormalBasis[1]) / rectExtents.y);
const scalar_type pdf = psr.backwardPdf(p);
return pdf < numeric_limits<scalar_type>::max ? pdf : numeric_limits<scalar_type>::max;
return psr.backwardWeight(ray.direction);
}

template<class Aniso>
vector3_type generate_and_pdf(NBL_REF_ARG(scalar_type) pdf, NBL_REF_ARG(scalar_type) newRayMaxT, NBL_CONST_REF_ARG(vector3_type) origin, NBL_CONST_REF_ARG(Aniso) interaction, NBL_CONST_REF_ARG(vector3_type) xi)
{
const vector3_type N = rect.getNormalTimesArea();
const vector3_type origin2origin = rect.offset - origin;

matrix<scalar_type, 3, 3> rectNormalBasis;
vector<T, 2> rectExtents;
Expand All @@ -375,31 +372,41 @@ struct ShapeSampling<T, PST_RECTANGLE, PPM_APPROX_PROJECTED_SOLID_ANGLE>
sphR0.origin = rect.offset;
sphR0.extents = rectExtents;
sphR0.basis = rectNormalBasis;
vector3_type L = hlsl::promote<vector3_type>(0.0);

sampling::ProjectedSphericalRectangle<scalar_type> psr = sampling::ProjectedSphericalRectangle<scalar_type>::create(sphR0, origin, interaction.getN(), interaction.isMaterialBSDF());
const scalar_type solidAngle = psr.sphrect.solidAngle;
if (solidAngle > numeric_limits<scalar_type>::min)
typename sampling::ProjectedSphericalRectangle<scalar_type>::cache_type cache;

const vector3_type origin2origin = rect.offset - origin;
vector3_type L = hlsl::promote<vector3_type>(0.0);
const bool FastVersion = true;
if (FastVersion)
{
typename sampling::ProjectedSphericalRectangle<scalar_type>::cache_type cache;
const vector3_type localDir = psr.generate(xi.xy, cache);
// not sure if generate() can produce NaN/inf when solidAngle > min
assert(!hlsl::any(hlsl::isinf(localDir) || hlsl::isnan(localDir)));
// transform local direction to world space
L = localDir.x * rectNormalBasis[0] + localDir.y * rectNormalBasis[1] + localDir.z * rectNormalBasis[2];
pdf = psr.forwardPdf(xi.xy, cache);
// actually the slowest
//L = psr.generate(xi.xy, cache);
//newRayMaxT = psr.sphrect.computeHitT(L);

// fastest
const vector3_type localL = psr.generateNormalizedLocal(xi.xy,cache,newRayMaxT);
// hopefully CSE kicks in for the `UsePdfAsWeight==true`
L = hlsl::mul(hlsl::transpose(psr.sphrect.basis),localL);
}
else
pdf = bit_cast<scalar_type>(numeric_limits<scalar_type>::infinity);

newRayMaxT = hlsl::dot<vector3_type>(N, origin2origin) / hlsl::dot<vector3_type>(N, L);
{
L = psr.generateUnnormalized(xi.xy,cache);
const scalar_type rcpLen = hlsl::rsqrt(hlsl::dot(L,L));
newRayMaxT = 1.f / rcpLen;
L *= rcpLen;
}
// prevent self intersections against the emitter
newRayMaxT -= 0.0001f;

pdf = psr.forwardPdf(xi.xy,cache);
return L;
}

Shape<T, PST_RECTANGLE> rect;
};


template<class Scene, class Light, typename Ray, class LightSample, class Aniso, ProceduralShapeType PST, NEEPolygonMethod PPM = PPM_APPROX_PROJECTED_SOLID_ANGLE>
struct NextEventEstimator
{
Expand Down