Skip to content

Commit bb765be

Browse files
MIS works with Spherical Sampling, fixed a bug in area sampling.
1 parent bf39946 commit bb765be

File tree

2 files changed

+92
-59
lines changed

2 files changed

+92
-59
lines changed

examples_tests/42.FragmentShaderPathTracer/common.glsl

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

55
// firefly and variance reduction techniques
66
//#define KILL_DIFFUSE_SPECULAR_PATHS
@@ -19,7 +19,7 @@ layout(location = 0) out vec4 pixelColor;
1919
#include <irr/builtin/glsl/math/constants.glsl>
2020
#include <irr/builtin/glsl/utils/common.glsl>
2121

22-
//! @Crisspl move this
22+
//! @Crisspl move this to `irr/builtin/glsl/sampling.glsl` (along with the circle transform)
2323
vec2 irr_glsl_BoxMullerTransform(in vec2 xi, in float stddev)
2424
{
2525
float sinPhi, cosPhi;
@@ -81,7 +81,7 @@ float Sphere_getSolidAngle(in Sphere sphere, in vec3 origin)
8181
return Sphere_getSolidAngle_impl(cosThetaMax);
8282
}
8383

84-
#define TRIANGLE_METHOD 1 // 0 area sampling, 1 solid angle sampling, 2 approximate projected solid angle sampling
84+
#define TRIANGLE_METHOD 0 // 0 area sampling, 1 solid angle sampling, 2 approximate projected solid angle sampling
8585
struct Triangle
8686
{
8787
vec3 vertex0;
@@ -123,9 +123,13 @@ float Triangle_intersect(in Triangle tri, in vec3 origin, in vec3 direction)
123123
return t>0.f&&u>=0.f&&v>=0.f&&(u+v)<=1.f ? t:irr_glsl_FLT_NAN;
124124
}
125125

126+
vec3 Triangle_getNormalTimesArea_impl(in mat2x3 edges)
127+
{
128+
return cross(edges[0],edges[1])*0.5;
129+
}
126130
vec3 Triangle_getNormalTimesArea(in Triangle tri)
127131
{
128-
return cross(tri.vertex1-tri.vertex0,tri.vertex2-tri.vertex0)*0.5;
132+
return Triangle_getNormalTimesArea_impl(mat2x3(tri.vertex1-tri.vertex0,tri.vertex2-tri.vertex0));
129133
}
130134

131135

@@ -172,7 +176,7 @@ float BSDFNode_getMISWeight(in BSDFNode bsdf)
172176
{
173177
const float alpha = BSDFNode_getRoughness(bsdf);
174178
const bool notDiffuse = BSDFNode_isNotDiffuse(bsdf);
175-
const float DIFFUSE_MIS_WEIGHT = 0.0;
179+
const float DIFFUSE_MIS_WEIGHT = 0.5;
176180
return notDiffuse ? mix(1.0,DIFFUSE_MIS_WEIGHT,alpha):DIFFUSE_MIS_WEIGHT; // TODO: test alpha*alpha
177181
}
178182

@@ -300,7 +304,7 @@ void missProgram()
300304
vec2 uv = SampleSphericalMap(rayStack[stackPtr]._immutable.direction);
301305
finalContribution *= textureLod(envMap, uv, 0.0).rgb;
302306
#else
303-
const vec3 kConstantEnvLightRadiance = vec3(0.0);// 0.15, 0.21, 0.3);
307+
const vec3 kConstantEnvLightRadiance = vec3(0.15, 0.21, 0.3);
304308
finalContribution *= kConstantEnvLightRadiance;
305309
#endif
306310
}

examples_tests/42.FragmentShaderPathTracer/litByTriangle.frag

Lines changed: 81 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -16,15 +16,15 @@ Sphere spheres[SPHERE_COUNT] = {
1616
};
1717
#define TRIANGLE_COUNT 1
1818
Triangle triangles[TRIANGLE_COUNT] = {
19-
//Triangle_Triangle(mat3(vec3(-1.8,0.35,0.3),vec3(-1.2,0.35,0.0),vec3(-1.5,0.8,-0.3)),INVALID_ID_16BIT,0u)
20-
Triangle_Triangle(mat3(vec3(-4,0.7,-4),vec3(0.0,0.7,0.0),vec3(-4.0,0.8,4.0)),INVALID_ID_16BIT,0u)
19+
Triangle_Triangle(mat3(vec3(-1.8,0.35,0.3),vec3(-1.2,0.35,0.0),vec3(-1.5,0.8,-0.3)),INVALID_ID_16BIT,0u)
20+
//Triangle_Triangle(mat3(vec3(-4,0.7,-4),vec3(0.0,0.7,0.0),vec3(-4.0,0.8,4.0)),INVALID_ID_16BIT,0u)
2121
};
2222

2323

2424
#define LIGHT_COUNT 1
2525
Light lights[LIGHT_COUNT] = {
26-
//{vec3(30.0,25.0,15.0),0u}
27-
{vec3(30.0,25.0,15.0)*0.01,0u}
26+
{vec3(30.0,25.0,15.0),0u}
27+
//{vec3(30.0,25.0,15.0)*0.01,0u}
2828
};
2929

3030

@@ -65,25 +65,8 @@ bool traceRay(in ImmutableRay_t _immutable)
6565
}
6666

6767

68-
// the interaction here is the interaction at the illuminator-end of the ray, not the receiver
69-
vec3 irr_glsl_light_deferred_eval_and_prob(out float pdf, in float intersectionT, in irr_glsl_AnisotropicViewSurfaceInteraction interaction, in Light light)
70-
{
71-
// 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
72-
pdf = scene_getLightChoicePdf(light);
73-
74-
Triangle tri = triangles[Light_getObjectID(light)];
75-
#if TRIANGLE_METHOD==0
76-
pdf *= intersectionT*intersectionT/abs(dot(Triangle_getNormalTimesArea(tri),interaction.isotropic.V.dir));
77-
#elif TRIANGLE_METHOD==1
78-
//pdf /= Triangle_getSolidAngle(tri),origin);
79-
#elif TRIANGLE_METHOD==2
80-
pdf /= Triangle_getApproxProjSolidAngle(tri,origin,interaction.isotropic.V.dir);
81-
#endif
82-
return Light_getRadiance(light);
83-
}
84-
85-
86-
vec3 faster_slerp(in vec3 start, in vec3 preScaledWaypoint, float cosAngleFromStart)
68+
// @Crisspl move this to `irr/builtin/glsl/math/quaternions.glsl`
69+
vec3 irr_glsl_slerp_impl_impl(in vec3 start, in vec3 preScaledWaypoint, float cosAngleFromStart)
8770
{
8871
vec3 planeNormal = cross(start,preScaledWaypoint);
8972

@@ -97,35 +80,61 @@ vec3 faster_slerp(in vec3 start, in vec3 preScaledWaypoint, float cosAngleFromSt
9780
return start+precompPart*cosAngle+cross(planeNormal,precompPart);
9881
}
9982

100-
// WARNING: can and will return NAN if one or three of the triangle edges are near zero length
101-
vec3 sampleSphericalTriangle(out float rcpPdf, in mat3 vertices, in vec3 origin, in vec2 u)
102-
{
103-
mat3 localVertices = mat3(vertices[0]-origin,vertices[1]-origin,vertices[2]-origin);
104-
105-
const vec3 A = normalize(localVertices[0]);
106-
const vec3 B = normalize(localVertices[1]);
107-
const vec3 C = normalize(localVertices[2]);
83+
// @Crisspl move this to `irr/builtin/glsl/shapes/triangle.glsl`
10884

85+
// returns solid angle of a spherical triangle
86+
// WARNING: can and will return NAN if one or three of the triangle edges are near zero length
87+
// this function is beyond optimized.
88+
float irr_glsl_SolidAngleOfTriangle(in vec3 A, in vec3 B, in vec3 C, out vec3 cos_vertices, out vec3 sin_vertices, out float cosC, out float cscB)
89+
{
10990
// The sides are denoted by lower-case letters a, b, and c. On the unit sphere their lengths are numerically equal to the radian measure of the angles that the great circle arcs subtend at the centre. The sides of proper spherical triangles are (by convention) less than PI
11091
const vec3 cos_sides = vec3(dot(B,C),dot(C,A),dot(A,B));
11192
const vec3 csc_sides = inversesqrt(vec3(1.0)-cos_sides*cos_sides);
11293

94+
// these variables might eventually get optimized out
95+
cosC = cos_sides[2];
96+
cscB = csc_sides[1];
97+
11398
// 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
114-
const vec3 cos_vertices = (cos_sides-cos_sides.yzx*cos_sides.zxy)*csc_sides.yzx*csc_sides.zxy; // using Spherical Law of Cosines
115-
const vec3 sin_vertices = sqrt(vec3(1.0)-cos_vertices*cos_vertices);
99+
cos_vertices = (cos_sides-cos_sides.yzx*cos_sides.zxy)*csc_sides.yzx*csc_sides.zxy; // using Spherical Law of Cosines
100+
sin_vertices = sqrt(vec3(1.0)-cos_vertices*cos_vertices);
101+
102+
// sorry about the naming of `something` I just can't seem to be able to give good name to the variables that is consistent with semantics
103+
const bool something0 = cos_vertices[0]<-cos_vertices[1];
104+
const float cosSumAB = cos_vertices[0]*cos_vertices[1]-sin_vertices[0]*sin_vertices[1];
105+
const bool something1 = cosSumAB<(-cos_vertices[2]);
106+
const bool something2 = cosSumAB<cos_vertices[2];
107+
// apply triple angle formula
108+
const float absArccosSumABC = acos(cosSumAB*cos_vertices[2]-(cos_vertices[0]*sin_vertices[1]+sin_vertices[0]*cos_vertices[1])*sin_vertices[2]);
109+
return ((something0 ? something2:something1) ? (-absArccosSumABC):absArccosSumABC)+(something0||something1 ? irr_glsl_PI:(-irr_glsl_PI));
110+
}
111+
// returns solid angle of a triangle given by its world-space vertices and world-space viewing position
112+
float irr_glsl_SolidAngleOfTriangle(in mat3 vertices, in vec3 origin)
113+
{
114+
// the `normalize` cannot be optimized out
115+
const vec3 A = normalize(vertices[0]-origin);
116+
const vec3 B = normalize(vertices[1]-origin);
117+
const vec3 C = normalize(vertices[2]-origin);
118+
119+
vec3 dummy0,dummy1;
120+
float dummy2,dummy3;
121+
return irr_glsl_SolidAngleOfTriangle(A,B,C,dummy0,dummy1,dummy2,dummy3);
122+
}
123+
// WARNING: can and will return NAN if one or three of the triangle edges are near zero length
124+
// this function could use some more optimizing
125+
vec3 irr_glsl_sampling_generateSphericalTriangleSample(out float rcpPdf, in mat3 vertices, in vec3 origin, in vec2 u)
126+
{
127+
// the `normalize` cannot be optimized out
128+
const vec3 A = normalize(vertices[0]-origin);
129+
const vec3 B = normalize(vertices[1]-origin);
130+
const vec3 C = normalize(vertices[2]-origin);
116131

132+
// for angles between view-to-vertex vectors
133+
float cosC,cscB;
134+
// 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
135+
vec3 cos_vertices,sin_vertices;
117136
// get solid angle, which is also the reciprocal of the probability
118-
{
119-
// sorry about the naming of `something` I just can't seem to be able to give good name to the variables that is consistent with semantics
120-
bool something0 = cos_vertices[0]<-cos_vertices[1];
121-
float cosSumAB = cos_vertices[0]*cos_vertices[1]-sin_vertices[0]*sin_vertices[1];
122-
bool something1 = cosSumAB<(-cos_vertices[2]);
123-
bool something2 = cosSumAB<cos_vertices[2];
124-
// apply triple angle formula
125-
float absArccosSumABC = acos(cosSumAB*cos_vertices[2]-(cos_vertices[0]*sin_vertices[1]+sin_vertices[0]*cos_vertices[1])*sin_vertices[2]);
126-
rcpPdf = (something0 ? something2:something1) ? (-absArccosSumABC):absArccosSumABC;
127-
rcpPdf += something0||something1 ? irr_glsl_PI:(-irr_glsl_PI);
128-
}
137+
rcpPdf = irr_glsl_SolidAngleOfTriangle(A,B,C,cos_vertices,sin_vertices,cosC,cscB);
129138

130139
// this part literally cannot be optimized further
131140
float negSinSubSolidAngle,negCosSubSolidAngle;
@@ -136,16 +145,35 @@ vec3 sampleSphericalTriangle(out float rcpPdf, in mat3 vertices, in vec3 origin,
136145
const float q = -negSinSubSolidAngle*sin_vertices[0]-negCosSubSolidAngle*cos_vertices[0];
137146

138147
float u_ = q - cos_vertices[0];
139-
float v_ = p + sin_vertices[0]*cos_sides[2];
148+
float v_ = p + sin_vertices[0]*cosC;
140149

141150
const float cosAngleAlongAC = clamp(((v_*q - u_*p)*cos_vertices[0] - v_) / ((v_*p + u_*q)*sin_vertices[0]), -1.0, 1.0); // TODO: get rid of this clamp (by improving the precision here)
142151

143-
vec3 C_s = faster_slerp(A, C*csc_sides[1], cosAngleAlongAC);
152+
vec3 C_s = irr_glsl_slerp_impl_impl(A, C*cscB, cosAngleAlongAC);
144153

145154
const float cosBC_s = dot(C_s,B);
146155
const float cosAngleAlongBC_s = cosBC_s*u.y - u.y + 1.0;
147156

148-
return faster_slerp(B, C_s*inversesqrt(1.0-cosBC_s*cosBC_s), cosAngleAlongBC_s);
157+
return irr_glsl_slerp_impl_impl(B, C_s*inversesqrt(1.0-cosBC_s*cosBC_s), cosAngleAlongBC_s);
158+
}
159+
// End-of @Crisspl move this to `irr/builtin/glsl/shapes/triangle.glsl`
160+
161+
// the interaction here is the interaction at the illuminator-end of the ray, not the receiver
162+
vec3 irr_glsl_light_deferred_eval_and_prob(out float pdf, in vec3 origin, in float intersectionT, in irr_glsl_AnisotropicViewSurfaceInteraction interaction, in Light light)
163+
{
164+
// 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
165+
pdf = scene_getLightChoicePdf(light);
166+
167+
Triangle tri = triangles[Light_getObjectID(light)];
168+
#if TRIANGLE_METHOD==0
169+
pdf *= intersectionT*intersectionT/abs(dot(Triangle_getNormalTimesArea(tri),interaction.isotropic.V.dir));
170+
#elif TRIANGLE_METHOD==1
171+
float rcpProb = irr_glsl_SolidAngleOfTriangle(mat3(tri.vertex0,tri.vertex1,tri.vertex2),origin);
172+
pdf /= isnan(rcpProb) ? 0.0:rcpProb;
173+
#elif TRIANGLE_METHOD==2
174+
pdf /= Triangle_getApproxProjSolidAngle(tri,origin,interaction.isotropic.V.dir);
175+
#endif
176+
return Light_getRadiance(light);
149177
}
150178

151179

@@ -158,8 +186,9 @@ irr_glsl_LightSample irr_glsl_light_generate_and_remainder_and_pdf(out vec3 rema
158186
const Triangle tri = triangles[Light_getObjectID(light)];
159187

160188
#if TRIANGLE_METHOD==0
161-
const vec3 edges[2] = vec3[2](tri.vertex1-tri.vertex0,tri.vertex2-tri.vertex0);
162-
vec3 point = tri.vertex0+edges[0]*(1.0-u.y)+edges[1]*u.y*sqrt(u.x);
189+
const mat2x3 edges = mat2x3(tri.vertex1-tri.vertex0,tri.vertex2-tri.vertex0);
190+
const float sqrtU = sqrt(u.x);
191+
vec3 point = tri.vertex0+edges[0]*(1.0-sqrtU)+edges[1]*sqrtU*u.y;
163192
vec3 L = point-origin;
164193

165194
const float distanceSq = dot(L,L);
@@ -168,10 +197,10 @@ irr_glsl_LightSample irr_glsl_light_generate_and_remainder_and_pdf(out vec3 rema
168197

169198
const float dist = 1.0/rcpDistance;
170199

171-
const float rcpPdf = abs(dot(Triangle_getNormalTimesArea(tri),L))/(distanceSq*choicePdf);
200+
const float rcpPdf = abs(dot(Triangle_getNormalTimesArea_impl(edges),L))/(distanceSq*choicePdf);
172201
#elif TRIANGLE_METHOD==1
173202
float rcpPdf;
174-
const vec3 L = sampleSphericalTriangle(rcpPdf,mat3(tri.vertex0,tri.vertex1,tri.vertex2),origin,u.xy);
203+
const vec3 L = irr_glsl_sampling_generateSphericalTriangleSample(rcpPdf,mat3(tri.vertex0,tri.vertex1,tri.vertex2),origin,u.xy);
175204
rcpPdf = isnan(rcpPdf) ? 0.0:rcpPdf;
176205

177206
const vec3 N = Triangle_getNormalTimesArea(tri);
@@ -227,7 +256,7 @@ void closestHitProgram(in ImmutableRay_t _immutable, inout irr_glsl_xoroshiro64s
227256
if (lightID!=INVALID_ID_16BIT) // has emissive
228257
{
229258
float lightPdf;
230-
vec3 lightVal = irr_glsl_light_deferred_eval_and_prob(lightPdf,mutable.intersectionT,interaction,lights[lightID]);
259+
vec3 lightVal = irr_glsl_light_deferred_eval_and_prob(lightPdf,_immutable.origin,mutable.intersectionT,interaction,lights[lightID]);
231260
rayStack[stackPtr]._payload.accumulation += throughput*lightVal/(1.0+lightPdf*lightPdf*rayStack[stackPtr]._payload.otherTechniqueHeuristic);
232261
}
233262

0 commit comments

Comments
 (0)