Skip to content

Commit 8cd651c

Browse files
The 2020 paper "Practical Product Sampling by Fitting and Composing Warps" IMPLEMENTED!
1 parent bb765be commit 8cd651c

File tree

1 file changed

+106
-29
lines changed

1 file changed

+106
-29
lines changed

examples_tests/42.FragmentShaderPathTracer/litByTriangle.frag

Lines changed: 106 additions & 29 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

@@ -82,13 +82,20 @@ vec3 irr_glsl_slerp_impl_impl(in vec3 start, in vec3 preScaledWaypoint, float co
8282

8383
// @Crisspl move this to `irr/builtin/glsl/shapes/triangle.glsl`
8484

85+
//
86+
mat3 irr_glsl_shapes_getSphericalTriangle(in mat3 vertices, in vec3 origin)
87+
{
88+
// the `normalize` cannot be optimized out
89+
return mat3(normalize(vertices[0]-origin),normalize(vertices[1]-origin),normalize(vertices[2]-origin));
90+
}
91+
8592
// returns solid angle of a spherical triangle
8693
// WARNING: can and will return NAN if one or three of the triangle edges are near zero length
8794
// 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)
95+
float irr_glsl_shapes_SolidAngleOfTriangle(in mat3 sphericalVertices, out vec3 cos_vertices, out vec3 sin_vertices, out float cosC, out float cscB)
8996
{
9097
// 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
91-
const vec3 cos_sides = vec3(dot(B,C),dot(C,A),dot(A,B));
98+
const vec3 cos_sides = vec3(dot(sphericalVertices[1],sphericalVertices[2]),dot(sphericalVertices[2],sphericalVertices[0]),dot(sphericalVertices[0],sphericalVertices[1]));
9299
const vec3 csc_sides = inversesqrt(vec3(1.0)-cos_sides*cos_sides);
93100

94101
// these variables might eventually get optimized out
@@ -109,32 +116,48 @@ float irr_glsl_SolidAngleOfTriangle(in vec3 A, in vec3 B, in vec3 C, out vec3 co
109116
return ((something0 ? something2:something1) ? (-absArccosSumABC):absArccosSumABC)+(something0||something1 ? irr_glsl_PI:(-irr_glsl_PI));
110117
}
111118
// 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)
119+
float irr_glsl_shapes_SolidAngleOfTriangle(in mat3 vertices, in vec3 origin)
113120
{
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-
119121
vec3 dummy0,dummy1;
120122
float dummy2,dummy3;
121-
return irr_glsl_SolidAngleOfTriangle(A,B,C,dummy0,dummy1,dummy2,dummy3);
123+
return irr_glsl_shapes_SolidAngleOfTriangle(irr_glsl_shapes_getSphericalTriangle(vertices,origin),dummy0,dummy1,dummy2,dummy3);
124+
}
125+
126+
// @Crisspl move this to `irr/builtin/glsl/sampling/bilinear.glsl`
127+
128+
float irr_glsl_sampling_generateLinearSample(in vec2 linearCoeffs, in float u)
129+
{
130+
const float rcpDiff = 1.0/(linearCoeffs[0]-linearCoeffs[1]);
131+
const vec2 squaredCoeffs = linearCoeffs*linearCoeffs;
132+
return abs(rcpDiff)<FLT_MAX ? (linearCoeffs[0]-sqrt(mix(squaredCoeffs[0],squaredCoeffs[1],u)))*rcpDiff:u;
133+
}
134+
135+
// The square's vertex values are defined in Z-order, so indices 0,1,2,3 (xyzw) correspond to (0,0),(1,0),(0,1),(1,1)
136+
vec2 irr_glsl_sampling_generateBilinearSample(out float rcpPdf, in vec4 bilinearCoeffs, vec2 u)
137+
{
138+
const vec2 twiceAreasUnderXCurve = vec2(bilinearCoeffs[0]+bilinearCoeffs[1],bilinearCoeffs[2]+bilinearCoeffs[3]);
139+
u.y = irr_glsl_sampling_generateLinearSample(twiceAreasUnderXCurve,u.y);
140+
141+
const vec2 ySliceEndPoints = vec2(mix(bilinearCoeffs[0],bilinearCoeffs[2],u.y),mix(bilinearCoeffs[1],bilinearCoeffs[3],u.y));
142+
u.x = irr_glsl_sampling_generateLinearSample(ySliceEndPoints,u.x);
143+
144+
rcpPdf = (twiceAreasUnderXCurve[0]+twiceAreasUnderXCurve[1])/(4.0*mix(ySliceEndPoints[0],ySliceEndPoints[1],u.x));
145+
146+
return u;
122147
}
148+
149+
// @Crisspl move this to `irr/builtin/glsl/sampling/triangle.glsl`
150+
123151
// WARNING: can and will return NAN if one or three of the triangle edges are near zero length
124152
// 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)
153+
vec3 irr_glsl_sampling_generateSphericalTriangleSample(out float rcpPdf, in mat3 sphericalVertices, in vec2 u)
126154
{
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);
131-
132155
// for angles between view-to-vertex vectors
133156
float cosC,cscB;
134157
// 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
135158
vec3 cos_vertices,sin_vertices;
136159
// get solid angle, which is also the reciprocal of the probability
137-
rcpPdf = irr_glsl_SolidAngleOfTriangle(A,B,C,cos_vertices,sin_vertices,cosC,cscB);
160+
rcpPdf = irr_glsl_shapes_SolidAngleOfTriangle(sphericalVertices,cos_vertices,sin_vertices,cosC,cscB);
138161

139162
// this part literally cannot be optimized further
140163
float negSinSubSolidAngle,negCosSubSolidAngle;
@@ -149,14 +172,64 @@ vec3 irr_glsl_sampling_generateSphericalTriangleSample(out float rcpPdf, in mat3
149172

150173
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)
151174

152-
vec3 C_s = irr_glsl_slerp_impl_impl(A, C*cscB, cosAngleAlongAC);
175+
vec3 C_s = irr_glsl_slerp_impl_impl(sphericalVertices[0], sphericalVertices[2]*cscB, cosAngleAlongAC);
153176

154-
const float cosBC_s = dot(C_s,B);
177+
const float cosBC_s = dot(C_s,sphericalVertices[1]);
155178
const float cosAngleAlongBC_s = cosBC_s*u.y - u.y + 1.0;
156179

157-
return irr_glsl_slerp_impl_impl(B, C_s*inversesqrt(1.0-cosBC_s*cosBC_s), cosAngleAlongBC_s);
180+
return irr_glsl_slerp_impl_impl(sphericalVertices[1], C_s*inversesqrt(1.0-cosBC_s*cosBC_s), cosAngleAlongBC_s);
181+
}
182+
vec3 irr_glsl_sampling_generateSphericalTriangleSample(out float rcpPdf, in mat3 vertices, in vec3 origin, in vec2 u)
183+
{
184+
return irr_glsl_sampling_generateSphericalTriangleSample(rcpPdf,irr_glsl_shapes_getSphericalTriangle(vertices,origin),u);
185+
}
186+
187+
// There are two different modes of sampling, one for BSDF and one for BRDF (depending if we throw away bottom hemisphere or not)
188+
vec3 irr_glsl_sampling_generateProjectedSphericalTriangleSample(out float rcpPdf, in mat3 sphericalVertices, in vec3 receiverNormal, vec2 u, in bool isBSDF)
189+
{
190+
// I don't see any problems with this being 0
191+
const float minimumProjSolidAngle = 0.0;
192+
193+
vec3 bxdfPdfAtVertex = transpose(sphericalVertices)*receiverNormal;
194+
// take abs of the value is we have a BSDF
195+
bxdfPdfAtVertex = uintBitsToFloat(floatBitsToUint(bxdfPdfAtVertex)&uvec3(isBSDF ? 0x7fFFffFFu:0xffFFffFFu));
196+
bxdfPdfAtVertex = max(bxdfPdfAtVertex,vec3(minimumProjSolidAngle));
197+
198+
// the swizzle needs to match the mapping of the [0,1]^2 square to the triangle vertices
199+
const vec4 bilinearDist = bxdfPdfAtVertex.yyxz;
200+
// pre-warp according to proj solid angle approximation
201+
u = irr_glsl_sampling_generateBilinearSample(rcpPdf,bilinearDist,u);
202+
203+
// now warp the points onto a spherical triangle
204+
float solidAngle;
205+
const vec3 L = irr_glsl_sampling_generateSphericalTriangleSample(solidAngle,sphericalVertices,u);
206+
rcpPdf *= solidAngle;
207+
208+
return L;
209+
}
210+
vec3 irr_glsl_sampling_generateProjectedSphericalTriangleSample(out float rcpPdf, in mat3 vertices, in vec3 origin, in vec3 receiverNormal, in vec2 u, in bool isBSDF)
211+
{
212+
return irr_glsl_sampling_generateProjectedSphericalTriangleSample(rcpPdf,irr_glsl_shapes_getSphericalTriangle(vertices,origin),receiverNormal,u,isBSDF);
213+
}
214+
/*
215+
//
216+
vec2 irr_glsl_sampling_generateSphericalTriangleSampleInverse(out float rcpPdf, in mat3 sphericalVertices, in vec3 L)
217+
{
218+
// for angles between view-to-vertex vectors
219+
float cosC,cscB;
220+
// 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
221+
vec3 cos_vertices,sin_vertices;
222+
// get solid angle, which is also the reciprocal of the probability
223+
rcpPdf = irr_glsl_shapes_SolidAngleOfTriangle(sphericalVertices,cos_vertices,sin_vertices,cosC,cscB);
224+
225+
return vec2(0.5,0.5);
226+
}
227+
vec2 irr_glsl_sampling_generateSphericalTriangleSampleInverse(out float rcpPdf, in mat3 vertices, in vec3 origin, in vec3 L)
228+
{
229+
return irr_glsl_sampling_generateSphericalTriangleSample(rcpPdf,irr_glsl_shapes_getSphericalTriangle(vertices,origin),L);
158230
}
159-
// End-of @Crisspl move this to `irr/builtin/glsl/shapes/triangle.glsl`
231+
*/
232+
// End-of @Crisspl move this to `irr/builtin/glsl/sampling/triangle.glsl`
160233

161234
// the interaction here is the interaction at the illuminator-end of the ray, not the receiver
162235
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)
@@ -171,7 +244,7 @@ vec3 irr_glsl_light_deferred_eval_and_prob(out float pdf, in vec3 origin, in flo
171244
float rcpProb = irr_glsl_SolidAngleOfTriangle(mat3(tri.vertex0,tri.vertex1,tri.vertex2),origin);
172245
pdf /= isnan(rcpProb) ? 0.0:rcpProb;
173246
#elif TRIANGLE_METHOD==2
174-
pdf /= Triangle_getApproxProjSolidAngle(tri,origin,interaction.isotropic.V.dir);
247+
//pdf /= Triangle_getApproxProjSolidAngle(tri,origin,interaction.isotropic.V.dir);
175248
#endif
176249
return Light_getRadiance(light);
177250
}
@@ -198,15 +271,19 @@ irr_glsl_LightSample irr_glsl_light_generate_and_remainder_and_pdf(out vec3 rema
198271
const float dist = 1.0/rcpDistance;
199272

200273
const float rcpPdf = abs(dot(Triangle_getNormalTimesArea_impl(edges),L))/(distanceSq*choicePdf);
201-
#elif TRIANGLE_METHOD==1
274+
#else
202275
float rcpPdf;
203-
const vec3 L = irr_glsl_sampling_generateSphericalTriangleSample(rcpPdf,mat3(tri.vertex0,tri.vertex1,tri.vertex2),origin,u.xy);
276+
277+
const mat3 sphericalVertices = irr_glsl_shapes_getSphericalTriangle(mat3(tri.vertex0,tri.vertex1,tri.vertex2),origin);
278+
#if TRIANGLE_METHOD==1
279+
const vec3 L = irr_glsl_sampling_generateSphericalTriangleSample(rcpPdf,sphericalVertices,u.xy);
280+
#elif TRIANGLE_METHOD==2
281+
const vec3 L = irr_glsl_sampling_generateProjectedSphericalTriangleSample(rcpPdf,sphericalVertices,interaction.isotropic.N,u.xy,false);
282+
#endif
204283
rcpPdf = isnan(rcpPdf) ? 0.0:rcpPdf;
205284

206285
const vec3 N = Triangle_getNormalTimesArea(tri);
207286
const float dist = dot(N,tri.vertex0-origin)/dot(N,L);
208-
#elif TRIANGLE_METHOD==2
209-
const float rcpPdf = Triangle_getApproxProjSolidAngle(tri,origin,interaction.isotropic.V.dir);
210287
#endif
211288

212289
remainder = Light_getRadiance(light)*rcpPdf;

0 commit comments

Comments
 (0)