Skip to content

Commit 7e74551

Browse files
It all works with MIS!
1 parent e1d47d1 commit 7e74551

File tree

2 files changed

+59
-27
lines changed

2 files changed

+59
-27
lines changed

examples_tests/42.FragmentShaderPathTracer/common.glsl

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
// basic settings
22
#define MAX_DEPTH 2
3-
#define SAMPLES 128
3+
#define SAMPLES 512
44

55
// firefly and variance reduction techniques
66
//#define KILL_DIFFUSE_SPECULAR_PATHS
@@ -177,9 +177,10 @@ mat2x3 BSDFNode_getEta(in BSDFNode node)
177177

178178
float BSDFNode_getMISWeight(in BSDFNode bsdf)
179179
{
180+
return 0.5;
180181
const float alpha = BSDFNode_getRoughness(bsdf);
181182
const bool notDiffuse = BSDFNode_isNotDiffuse(bsdf);
182-
const float DIFFUSE_MIS_WEIGHT = 0.0;
183+
const float DIFFUSE_MIS_WEIGHT = 0.5;
183184
return notDiffuse ? mix(1.0,DIFFUSE_MIS_WEIGHT,alpha):DIFFUSE_MIS_WEIGHT; // TODO: test alpha*alpha
184185
}
185186

examples_tests/42.FragmentShaderPathTracer/litByTriangle.frag

Lines changed: 56 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -118,9 +118,9 @@ vec2 irr_glsl_sampling_generateBilinearSample(out float rcpPdf, in vec4 bilinear
118118

119119
return u;
120120
}
121-
float irr_glsl_sampling_rcpProbBilinearSample(in vec4 bilinearCoeffs, vec2 u)
121+
float irr_glsl_sampling_probBilinearSample(in vec4 bilinearCoeffs, vec2 u)
122122
{
123-
return (bilinearCoeffs[0]+bilinearCoeffs[1]+bilinearCoeffs[2]+bilinearCoeffs[3])/(4.0*mix(mix(bilinearCoeffs[0],bilinearCoeffs[1],u.x),mix(bilinearCoeffs[2],bilinearCoeffs[3],u.x),u.y));
123+
return 4.0*mix(mix(bilinearCoeffs[0],bilinearCoeffs[1],u.x),mix(bilinearCoeffs[2],bilinearCoeffs[3],u.x),u.y)/(bilinearCoeffs[0]+bilinearCoeffs[1]+bilinearCoeffs[2]+bilinearCoeffs[3]);
124124
}
125125

126126

@@ -240,60 +240,80 @@ vec3 irr_glsl_sampling_generateProjectedSphericalTriangleSample(out float rcpPdf
240240

241241

242242
//
243-
vec2 irr_glsl_sampling_generateProjectedSphericalTriangleSampleInverse(out float rcpPdf, in mat3 sphericalVertices, in vec3 L)
243+
vec2 irr_glsl_sampling_generateSphericalTriangleSampleInverse(out float pdf, in mat3 sphericalVertices, in vec3 L)
244244
{
245245
// for angles between view-to-vertex vectors
246246
float cos_a,cos_c,csc_b,csc_c;
247247
// Both vertices and angles at the vertices are denoted by the same upper case letters A, B, and C. The angles A, B, C of the triangle are equal to the angles between the planes that intersect the surface of the sphere or, equivalently, the angles between the tangent vectors of the great circle arcs where they meet at the vertices. Angles are in radians. The angles of proper spherical triangles are (by convention) less than PI
248248
vec3 cos_vertices,sin_vertices;
249249
// get solid angle, which is also the reciprocal of the probability
250-
rcpPdf = irr_glsl_shapes_SolidAngleOfTriangle(sphericalVertices,cos_vertices,sin_vertices,cos_a,cos_c,csc_b,csc_c);
250+
pdf = 1.0/irr_glsl_shapes_SolidAngleOfTriangle(sphericalVertices,cos_vertices,sin_vertices,cos_a,cos_c,csc_b,csc_c);
251+
252+
// get the modified B angle of the first subtriangle by getting it from the triangle formed by vertices A,B and the light sample L
253+
const float cosAngleAlongBC_s = dot(L,sphericalVertices[1]);
254+
const float csc_a_ = inversesqrt(1.0-cosAngleAlongBC_s*cosAngleAlongBC_s); // only NaN if L is close to B which implies v=0
255+
const float cos_b_ = dot(L,sphericalVertices[0]);
251256

252-
// get the modified B angle of the first subtriangle by getting it from the first
253-
const vec2 cos_subtri_sides = vec2(dot(L,sphericalVertices[1]),dot(L,sphericalVertices[0]));
254-
const float cosB_ = (cos_subtri_sides[1]-cos_a*cos_subtri_sides[0])*csc_c*inversesqrt(1.0-cos_subtri_sides[0]*cos_subtri_sides[0]);
257+
const float cosB_ = (cos_b_-cosAngleAlongBC_s*cos_c)*csc_a_*csc_c; // only NaN if `csc_a_` (L close to B) is NaN OR if `csc_c` is NaN (which would mean zero solid angle triangle to begin with, so uv can be whatever)
258+
const float sinB_ = sqrt(1.0-cosB_*cosB_);
255259

256-
const float cosC_ = sin_vertices[0]*sin_vertices[1]*cos_c-cos_vertices[0]*cos_vertices[1];
260+
// now all that remains is to obtain the modified C angle, which is the angle at the unknown vertex `C_s`
261+
const float cosC_ = sin_vertices[0]*sinB_*cos_c-cos_vertices[0]*cosB_; // if cosB_ is NaN then cosC_ doesn't matter because the subtriangle has zero Solid Angle (we could pretend its `-cos_vertices[0]`)
257262
const float sinC_ = sqrt(1.0-cosC_*cosC_);
258-
const float u = irr_glsl_getArccosSumofABC_minus_PI(cos_vertices[0],cosB_,cosC_,sin_vertices[0],sqrt(1.0-cosB_*cosB_),sinC_)/rcpPdf;
259263

260-
//
261-
const float cosAngleAlongBC_s = cos_subtri_sides[0];
262-
const float cosBC_s = (cos_vertices[0]+cosB_*cosC_)/(sin_vertices[0]*sinC_);
263-
const float v = (1.0-cosAngleAlongBC_s)/(1.0-cosBC_s);
264+
const float subTriSolidAngleRatio = irr_glsl_getArccosSumofABC_minus_PI(cos_vertices[0],cosB_,cosC_,sin_vertices[0],sinB_,sinC_)*pdf; // will only be NaN if either the original triangle has zero solid angle or the subtriangle has zero solid angle (all can be satisfied with u=0)
265+
const float u = subTriSolidAngleRatio>FLT_MIN ? subTriSolidAngleRatio:0.0; // tiny overruns of u>1.0 will not affect the PDF much because a bilinear warp is used and the gradient has a bound (won't be true if LTC will get used)
266+
267+
// INF if any angle is 0 degrees, which implies L lays along BA arc, if the angle at A is PI minus the angle at either B_ or C_ while the other of C_ or B_ has a zero angle, we get a NaN (which is also a zero solid angle subtriangle, implying L along AB arc)
268+
const float cosBC_s = (cos_vertices[0]+cosB_*cosC_)/(sinB_*sinC_);
269+
// if cosBC_s is really large then we have numerical issues (should be 1.0 which means the arc is really short), if its NaN then either the original or sub-triangle has zero solid angle, in both cases we can consider that the BC_s arc is actually the BA arc and substitute
270+
const float v = (1.0-cosAngleAlongBC_s)/(1.0-(cosBC_s<uintBitsToFloat(0x3f7fffff) ? cosBC_s:cos_c));
264271

265-
return vec2(isnan(u) ? 0.5:u,isnan(v) ? 0.5:v);
272+
return vec2(u,v);
266273
}
267274

268275
//
269-
float irr_glsl_sampling_rcpProbProjectedSphericalTriangleSample(in mat3 sphericalVertices, in vec3 receiverNormal, in bool receiverWasBSDF, in vec3 L)
276+
float irr_glsl_sampling_probProjectedSphericalTriangleSample(in mat3 sphericalVertices, in vec3 receiverNormal, in bool receiverWasBSDF, in vec3 L)
270277
{
271-
float rcpPdf;
272-
const vec2 u = irr_glsl_sampling_generateProjectedSphericalTriangleSampleInverse(rcpPdf,sphericalVertices,L);
278+
float pdf;
279+
const vec2 u = irr_glsl_sampling_generateSphericalTriangleSampleInverse(pdf,sphericalVertices,L);
273280

274-
return rcpPdf*irr_glsl_sampling_rcpProbBilinearSample(irr_glsl_sampling_computeBilinearPatchForProjSphericalTriangle(sphericalVertices,receiverNormal,receiverWasBSDF),u);
281+
return pdf*irr_glsl_sampling_probBilinearSample(irr_glsl_sampling_computeBilinearPatchForProjSphericalTriangle(sphericalVertices,receiverNormal,receiverWasBSDF),u);
275282
}
276283
// End-of @Crisspl move this to `irr/builtin/glsl/sampling/triangle.glsl`
277284

278285

279286
// the interaction here is the interaction at the illuminator-end of the ray, not the receiver
280-
vec3 irr_glsl_light_deferred_eval_and_prob(out float pdf, in vec3 origin, in vec3 normalAtOrigin, in bool wasBSDFAtOrigin, in float intersectionT, in irr_glsl_AnisotropicViewSurfaceInteraction interaction, in Light light)
287+
vec3 irr_glsl_light_deferred_eval_and_prob(
288+
out float pdf, in Light light, in vec3 L,
289+
#if TRIANGLE_METHOD==0
290+
in float intersectionT,
291+
#else
292+
in vec3 origin,
293+
#if TRIANGLE_METHOD==2
294+
in vec3 normalAtOrigin, in bool wasBSDFAtOrigin
295+
#endif
296+
#endif
297+
)
281298
{
282299
// we don't have to worry about solid angle of the light w.r.t. surface of the light because this function only ever gets called from closestHit routine, so such ray cannot be produced
283300
pdf = scene_getLightChoicePdf(light);
284301

285302
Triangle tri = triangles[Light_getObjectID(light)];
286303
#if TRIANGLE_METHOD==0
287-
pdf *= intersectionT*intersectionT/abs(dot(Triangle_getNormalTimesArea(tri),interaction.isotropic.V.dir));
304+
pdf *= intersectionT*intersectionT/abs(dot(Triangle_getNormalTimesArea(tri),L));
288305
#else
289306
const mat3 sphericalVertices = irr_glsl_shapes_getSphericalTriangle(mat3(tri.vertex0,tri.vertex1,tri.vertex2),origin);
307+
Triangle tmpTri = Triangle_Triangle(mat3(tri.vertex0,tri.vertex1,tri.vertex2),0u,0u);
290308
#if TRIANGLE_METHOD==1
291309
float rcpProb = irr_glsl_shapes_SolidAngleOfTriangle(sphericalVertices);
310+
// if `rcpProb` is NAN then the triangle's solid angle was close to 0.0
311+
pdf = rcpProb>FLT_MIN ? (pdf/rcpProb):FLT_MAX;
292312
#elif TRIANGLE_METHOD==2
293-
float rcpProb = irr_glsl_sampling_rcpProbProjectedSphericalTriangleSample(sphericalVertices,normalAtOrigin,wasBSDFAtOrigin,interaction.isotropic.V.dir);
313+
pdf *= irr_glsl_sampling_probProjectedSphericalTriangleSample(sphericalVertices,normalAtOrigin,wasBSDFAtOrigin,L);
314+
// 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
315+
pdf = pdf<FLT_MAX ? pdf:0.0;
294316
#endif
295-
// if `rcpProb` is NAN or INF then the triangle's projectedSolidAngle was close to 0.0
296-
pdf = rcpProb<=FLT_MAX ? (pdf/rcpProb):0.0;
297317
#endif
298318
return Light_getRadiance(light);
299319
}
@@ -329,7 +349,8 @@ irr_glsl_LightSample irr_glsl_light_generate_and_remainder_and_pdf(out vec3 rema
329349
#elif TRIANGLE_METHOD==2
330350
const vec3 L = irr_glsl_sampling_generateProjectedSphericalTriangleSample(rcpPdf,sphericalVertices,interaction.isotropic.N,isBSDF,u.xy);
331351
#endif
332-
rcpPdf = isnan(rcpPdf) ? 0.0:rcpPdf;
352+
// if `rcpProb` is NAN or negative then the triangle's solidAngle or projectedSolidAngle was close to 0.0
353+
rcpPdf = rcpPdf>FLT_MIN ? rcpPdf:0.0;
333354

334355
const vec3 N = Triangle_getNormalTimesArea(tri);
335356
const float dist = dot(N,tri.vertex0-origin)/dot(N,L);
@@ -382,7 +403,17 @@ void closestHitProgram(in ImmutableRay_t _immutable, inout irr_glsl_xoroshiro64s
382403
if (lightID!=INVALID_ID_16BIT) // has emissive
383404
{
384405
float lightPdf;
385-
vec3 lightVal = irr_glsl_light_deferred_eval_and_prob(lightPdf,_immutable.origin,_immutable.normalAtOrigin,_immutable.wasBSDFAtOrigin,mutable.intersectionT,interaction,lights[lightID]);
406+
vec3 lightVal = irr_glsl_light_deferred_eval_and_prob(
407+
lightPdf,lights[lightID],_immutable.direction,
408+
#if TRIANGLE_METHOD==0
409+
mutable.intersectionT,
410+
#else
411+
_immutable.origin,
412+
#if TRIANGLE_METHOD==2
413+
_immutable.normalAtOrigin,_immutable.wasBSDFAtOrigin
414+
#endif
415+
#endif
416+
);
386417
rayStack[stackPtr]._payload.accumulation += throughput*lightVal/(1.0+lightPdf*lightPdf*rayStack[stackPtr]._payload.otherTechniqueHeuristic);
387418
}
388419

0 commit comments

Comments
 (0)