Skip to content

Commit 05b893b

Browse files
Merge pull request #555 from buildaworldnet/property_pool
Workgroup Scan Fixes
2 parents e48b91e + ab420e9 commit 05b893b

35 files changed

+1732
-336
lines changed

examples_tests/42.FragmentShaderPathTracer/common.glsl

Lines changed: 52 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,8 @@ float Sphere_getSolidAngle(in Sphere sphere, in vec3 origin)
8181
return Sphere_getSolidAngle_impl(cosThetaMax);
8282
}
8383

84+
85+
8486
struct Triangle
8587
{
8688
vec3 vertex0;
@@ -132,6 +134,54 @@ vec3 Triangle_getNormalTimesArea(in Triangle tri)
132134
}
133135

134136

137+
138+
struct Rectangle
139+
{
140+
vec3 offset;
141+
uint bsdfLightIDs;
142+
vec3 edge0;
143+
uint padding0;
144+
vec3 edge1;
145+
uint padding1;
146+
};
147+
148+
Rectangle Rectangle_Rectangle(in vec3 offset, in vec3 edge0, in vec3 edge1, in uint bsdfID, in uint lightID)
149+
{
150+
Rectangle rect;
151+
rect.offset = offset;
152+
rect.edge0 = edge0;
153+
rect.edge1 = edge1;
154+
//
155+
rect.bsdfLightIDs = bitfieldInsert(bsdfID, lightID, 16, 16);
156+
return rect;
157+
}
158+
159+
// return intersection distance if found, FLT_NAN otherwise
160+
float Rectangle_intersect(in Rectangle rect, in vec3 origin, in vec3 direction)
161+
{
162+
const vec3 h = cross(direction,rect.edge1);
163+
const float a = dot(rect.edge0,h);
164+
165+
const vec3 relOrigin = origin-rect.offset;
166+
167+
const float u = dot(relOrigin,h)/a;
168+
169+
const vec3 q = cross(relOrigin,rect.edge0);
170+
const float v = dot(direction,q)/a;
171+
172+
const float t = dot(rect.edge1,q)/a;
173+
174+
const bool intersection = t>0.f&&u>=0.f&&v>=0.f&&u<=1.f&&v<=1.f;
175+
return intersection ? t:irr_glsl_FLT_NAN;
176+
}
177+
178+
vec3 Rectangle_getNormalTimesArea(in Rectangle rect)
179+
{
180+
return cross(rect.edge0,rect.edge1);
181+
}
182+
183+
184+
135185
#define DIFFUSE_OP 0u
136186
#define CONDUCTOR_OP 1u
137187
#define DIELECTRIC_OP 2u
@@ -240,7 +290,7 @@ struct ImmutableRay_t
240290
float maxT;
241291
vec3 direction;
242292
int typeDepthSampleIx;
243-
#ifdef TRIANGLE_METHOD
293+
#if defined(TRIANGLE_METHOD)||defined(RECTANGLE_METHOD)
244294
vec3 normalAtOrigin;
245295
bool wasBSDFAtOrigin;
246296
#endif
@@ -473,7 +523,7 @@ void main()
473523

474524
rayStack[stackPtr]._immutable.typeDepthSampleIx = bitfieldInsert(i,1,DEPTH_BITS_OFFSET,DEPTH_BITS_COUNT);
475525

476-
#ifdef TRIANGLE_METHOD
526+
#if defined(TRIANGLE_METHOD)||defined(RECTANGLE_METHOD)
477527
rayStack[stackPtr]._immutable.normalAtOrigin = vec3(0.0,0.0,0.0);
478528
rayStack[stackPtr]._immutable.wasBSDFAtOrigin = false;
479529
#endif
@@ -541,9 +591,6 @@ When proper scheduling is available:
541591
- Divergence Optimization
542592
- Adaptive Sampling
543593
544-
Offline firefly removal:
545-
- Density Based Outlier Rejection (requires fast k-nearest neighbours on the GPU, at which point we've pretty much got photonmapping ready)
546-
547594
When finally texturing:
548595
- Covariance Rendering
549596
- CLEAR/LEAN/Toksvig for simult roughness + bumpmap filtering
Lines changed: 296 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,296 @@
1+
#version 430 core
2+
#extension GL_GOOGLE_include_directive : require
3+
4+
#define RECTANGLE_METHOD 0 // 0 area sampling, 1 solid angle sampling, 2 approximate projected solid angle sampling
5+
#include "common.glsl"
6+
7+
#define SPHERE_COUNT 8
8+
Sphere spheres[SPHERE_COUNT] = {
9+
Sphere_Sphere(vec3(0.0,-100.5,-1.0),100.0,0u,INVALID_ID_16BIT),
10+
Sphere_Sphere(vec3(2.0,0.0,-1.0),0.5,1u,INVALID_ID_16BIT),
11+
Sphere_Sphere(vec3(0.0,0.0,-1.0),0.5,2u,INVALID_ID_16BIT),
12+
Sphere_Sphere(vec3(-2.0,0.0,-1.0),0.5,3u,INVALID_ID_16BIT),
13+
Sphere_Sphere(vec3(2.0,0.0,1.0),0.5,4u,INVALID_ID_16BIT),
14+
Sphere_Sphere(vec3(0.0,0.0,1.0),0.5,4u,INVALID_ID_16BIT),
15+
Sphere_Sphere(vec3(-2.0,0.0,1.0),0.5,5u,INVALID_ID_16BIT),
16+
Sphere_Sphere(vec3(0.5,1.0,0.5),0.5,6u,INVALID_ID_16BIT)
17+
};
18+
#define RECTANGLE_COUNT 1
19+
Rectangle rectangles[RECTANGLE_COUNT] = {
20+
Rectangle_Rectangle(vec3(-1.8,0.35,0.3),vec3(0.6,0.0,-0.3),vec3(0.3,0.45,-0.6),INVALID_ID_16BIT,0u)
21+
};
22+
23+
24+
#define LIGHT_COUNT 1
25+
Light lights[LIGHT_COUNT] = {
26+
{vec3(30.0,25.0,15.0),0u}
27+
};
28+
29+
30+
bool traceRay(in ImmutableRay_t _immutable)
31+
{
32+
const bool anyHit = bitfieldExtract(_immutable.typeDepthSampleIx,31,1)!=0;
33+
34+
int objectID = -1;
35+
float intersectionT = _immutable.maxT;
36+
for (int i=0; i<SPHERE_COUNT; i++)
37+
{
38+
float t = Sphere_intersect(spheres[i],_immutable.origin,_immutable.direction);
39+
bool closerIntersection = t>0.0 && t<intersectionT;
40+
41+
objectID = closerIntersection ? i:objectID;
42+
intersectionT = closerIntersection ? t:intersectionT;
43+
44+
// allowing early out results in a performance regression, WTF!?
45+
//if (anyHit && closerIntersection && anyHitProgram(_immutable))
46+
//break;
47+
}
48+
for (int i=0; i<RECTANGLE_COUNT; i++)
49+
{
50+
float t = Rectangle_intersect(rectangles[i],_immutable.origin,_immutable.direction);
51+
bool closerIntersection = t>0.0 && t<intersectionT;
52+
53+
objectID = closerIntersection ? (i+SPHERE_COUNT):objectID;
54+
intersectionT = closerIntersection ? t:intersectionT;
55+
56+
// allowing early out results in a performance regression, WTF!?
57+
//if (anyHit && closerIntersection && anyHitProgram(_immutable))
58+
//break;
59+
}
60+
rayStack[stackPtr]._mutable.objectID = objectID;
61+
rayStack[stackPtr]._mutable.intersectionT = intersectionT;
62+
// hit
63+
return anyHit;
64+
}
65+
66+
67+
/// #include <irr/builtin/glsl/sampling/projected_spherical_rectangle.glsl>
68+
69+
70+
// the interaction here is the interaction at the illuminator-end of the ray, not the receiver
71+
vec3 irr_glsl_light_deferred_eval_and_prob(
72+
out float pdf, in Light light, in vec3 L
73+
#if RECTANGLE_METHOD==0
74+
,in float intersectionT
75+
#else
76+
,in vec3 origin
77+
#if RECTANGLE_METHOD==2
78+
,in vec3 normalAtOrigin, in bool wasBSDFAtOrigin
79+
#endif
80+
#endif
81+
)
82+
{
83+
// 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
84+
pdf = scene_getLightChoicePdf(light);
85+
86+
Rectangle rect = rectangles[Light_getObjectID(light)];
87+
#if RECTANGLE_METHOD==0
88+
pdf *= intersectionT*intersectionT/abs(dot(Rectangle_getNormalTimesArea(rect),L));
89+
#else
90+
const mat3 sphericalVertices = irr_glsl_shapes_getSphericalTriangle(mat3(tri.vertex0,tri.vertex1,tri.vertex2),origin);
91+
Triangle tmpTri = Triangle_Triangle(mat3(tri.vertex0,tri.vertex1,tri.vertex2),0u,0u);
92+
#if RECTANGLE_METHOD==1
93+
float rcpProb = irr_glsl_shapes_SolidAngleOfTriangle(sphericalVertices);
94+
// if `rcpProb` is NAN then the triangle's solid angle was close to 0.0
95+
pdf = rcpProb>FLT_MIN ? (pdf/rcpProb):FLT_MAX;
96+
#elif RECTANGLE_METHOD==2
97+
pdf *= irr_glsl_sampling_probProjectedSphericalTriangleSample(sphericalVertices,normalAtOrigin,wasBSDFAtOrigin,L);
98+
// 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
99+
pdf = pdf<FLT_MAX ? pdf:0.0;
100+
#endif
101+
#endif
102+
return Light_getRadiance(light);
103+
}
104+
105+
106+
irr_glsl_LightSample irr_glsl_light_generate_and_remainder_and_pdf(out vec3 remainder, out float pdf, out float newRayMaxT, in vec3 origin, in irr_glsl_AnisotropicViewSurfaceInteraction interaction, in bool isBSDF, in vec3 u, in int depth)
107+
{
108+
// normally we'd pick from set of lights, using `u.z`
109+
const Light light = lights[0];
110+
const float choicePdf = scene_getLightChoicePdf(light);
111+
112+
const Rectangle rect = rectangles[Light_getObjectID(light)];
113+
114+
#if RECTANGLE_METHOD==0
115+
const vec3 point = rect.offset+rect.edge0*u.x+rect.edge1*u.y;
116+
vec3 L = point-origin;
117+
118+
const float distanceSq = dot(L,L);
119+
const float rcpDistance = inversesqrt(distanceSq);
120+
L *= rcpDistance;
121+
122+
const float dist = 1.0/rcpDistance;
123+
124+
const float rcpPdf = abs(dot(Rectangle_getNormalTimesArea(rect),L))/(distanceSq*choicePdf);
125+
#else
126+
float rcpPdf;
127+
128+
const mat3 sphericalVertices = irr_glsl_shapes_getSphericalTriangle(mat3(tri.vertex0,tri.vertex1,tri.vertex2),origin);
129+
#if RECTANGLE_METHOD==1
130+
const vec3 L = irr_glsl_sampling_generateSphericalTriangleSample(rcpPdf,sphericalVertices,u.xy);
131+
#elif RECTANGLE_METHOD==2
132+
const vec3 L = irr_glsl_sampling_generateProjectedSphericalTriangleSample(rcpPdf,sphericalVertices,interaction.isotropic.N,isBSDF,u.xy);
133+
#endif
134+
// if `rcpProb` is NAN or negative then the triangle's solidAngle or projectedSolidAngle was close to 0.0
135+
rcpPdf = rcpPdf>FLT_MIN ? rcpPdf:0.0;
136+
137+
const vec3 N = Triangle_getNormalTimesArea(tri);
138+
const float dist = dot(N,tri.vertex0-origin)/dot(N,L);
139+
#endif
140+
141+
remainder = Light_getRadiance(light)*rcpPdf;
142+
pdf = 1.0/rcpPdf;
143+
144+
newRayMaxT = getEndTolerance(depth)*dist;
145+
146+
return irr_glsl_createLightSample(L,interaction);
147+
}
148+
149+
void closestHitProgram(in ImmutableRay_t _immutable, inout irr_glsl_xoroshiro64star_state_t scramble_state)
150+
{
151+
const MutableRay_t mutable = rayStack[stackPtr]._mutable;
152+
153+
vec3 intersection = _immutable.origin+_immutable.direction*mutable.intersectionT;
154+
const uint objectID = mutable.objectID;
155+
156+
uint bsdfLightIDs;
157+
irr_glsl_AnisotropicViewSurfaceInteraction interaction;
158+
{
159+
irr_glsl_IsotropicViewSurfaceInteraction isotropic;
160+
161+
isotropic.V.dir = -_immutable.direction;
162+
//isotropic.V.dPosdScreen = screw that
163+
if (objectID<SPHERE_COUNT)
164+
{
165+
Sphere sphere = spheres[objectID];
166+
isotropic.N = Sphere_getNormal(sphere,intersection);
167+
bsdfLightIDs = sphere.bsdfLightIDs;
168+
}
169+
else
170+
{
171+
Rectangle rect = rectangles[objectID-SPHERE_COUNT];
172+
isotropic.N = normalize(Rectangle_getNormalTimesArea(rect));
173+
bsdfLightIDs = rect.bsdfLightIDs;
174+
}
175+
isotropic.NdotV = dot(isotropic.V.dir,isotropic.N);
176+
isotropic.NdotV_squared = isotropic.NdotV*isotropic.NdotV;
177+
178+
interaction = irr_glsl_calcAnisotropicInteraction(isotropic);
179+
}
180+
181+
const uint lightID = bitfieldExtract(bsdfLightIDs,16,16);
182+
183+
vec3 throughput = rayStack[stackPtr]._payload.throughput;
184+
// finish MIS
185+
if (lightID!=INVALID_ID_16BIT) // has emissive
186+
{
187+
float lightPdf;
188+
vec3 lightVal = irr_glsl_light_deferred_eval_and_prob(
189+
lightPdf,lights[lightID],_immutable.direction
190+
#if RECTANGLE_METHOD==0
191+
,mutable.intersectionT
192+
#else
193+
,_immutable.origin
194+
#if RECTANGLE_METHOD==2
195+
,_immutable.normalAtOrigin,_immutable.wasBSDFAtOrigin
196+
#endif
197+
#endif
198+
);
199+
rayStack[stackPtr]._payload.accumulation += throughput*lightVal/(1.0+lightPdf*lightPdf*rayStack[stackPtr]._payload.otherTechniqueHeuristic);
200+
}
201+
202+
const int sampleIx = bitfieldExtract(_immutable.typeDepthSampleIx,0,DEPTH_BITS_OFFSET);
203+
const int depth = bitfieldExtract(_immutable.typeDepthSampleIx,DEPTH_BITS_OFFSET,DEPTH_BITS_COUNT);
204+
205+
// check if we even have a BSDF at all
206+
uint bsdfID = bitfieldExtract(bsdfLightIDs,0,16);
207+
if (depth<MAX_DEPTH && bsdfID!=INVALID_ID_16BIT)
208+
{
209+
// common preload
210+
BSDFNode bsdf = bsdfs[bsdfID];
211+
uint opType = BSDFNode_getType(bsdf);
212+
213+
#ifdef KILL_DIFFUSE_SPECULAR_PATHS
214+
if (BSDFNode_isNotDiffuse(bsdf))
215+
{
216+
if (rayStack[stackPtr]._payload.hasDiffuse)
217+
return;
218+
}
219+
else
220+
rayStack[stackPtr]._payload.hasDiffuse = true;
221+
#endif
222+
223+
224+
const float bsdfGeneratorProbability = BSDFNode_getMISWeight(bsdf);
225+
vec3 epsilon = rand3d(depth,sampleIx,scramble_state);
226+
227+
float rcpChoiceProb;
228+
const bool doNEE = irr_glsl_partitionRandVariable(bsdfGeneratorProbability,epsilon.z,rcpChoiceProb);
229+
230+
231+
float maxT;
232+
// the probability of generating a sample w.r.t. the light generator only possible and used when it was generated with it!
233+
float lightPdf;
234+
irr_glsl_LightSample _sample;
235+
irr_glsl_AnisotropicMicrofacetCache _cache;
236+
const bool isBSDF = BSDFNode_isBSDF(bsdf);
237+
if (doNEE)
238+
{
239+
vec3 lightRemainder;
240+
_sample = irr_glsl_light_generate_and_remainder_and_pdf(
241+
lightRemainder,lightPdf,maxT,
242+
intersection,interaction,
243+
isBSDF,epsilon,depth
244+
);
245+
throughput *= lightRemainder;
246+
}
247+
248+
bool validPath = true;
249+
const vec3 throughputCIE_Y = transpose(irr_glsl_sRGBtoXYZ)[1]*throughput;
250+
const float monochromeEta = dot(throughputCIE_Y,BSDFNode_getEta(bsdf)[0])/(throughputCIE_Y.r+throughputCIE_Y.g+throughputCIE_Y.b);
251+
if (doNEE)
252+
{
253+
// if we allowed non-watertight transmitters (single water surface), it would make sense just to apply this line.
254+
validPath = irr_glsl_calcAnisotropicMicrofacetCache(_cache,interaction,_sample,monochromeEta);
255+
// but we don't allow non watertight transmitters in this renderer
256+
validPath = validPath && _sample.NdotL>0.0;
257+
}
258+
else
259+
{
260+
maxT = FLT_MAX;
261+
_sample = irr_glsl_bsdf_cos_generate(interaction,epsilon,bsdf,monochromeEta,_cache);
262+
}
263+
264+
// do a cool trick and always compute the bsdf parts this way! (no divergence)
265+
float bsdfPdf;
266+
// the value of the bsdf divided by the probability of the sample being generated
267+
if (validPath)
268+
throughput *= irr_glsl_bsdf_cos_remainder_and_pdf(bsdfPdf,_sample,interaction,bsdf,monochromeEta,_cache);
269+
else
270+
throughput = vec3(0.0);
271+
272+
// OETF smallest perceptible value
273+
const float bsdfPdfThreshold = getLuma(irr_glsl_eotf_sRGB(vec3(1.0)/255.0));
274+
const float lumaThroughputThreshold = bsdfPdfThreshold;
275+
if (bsdfPdf>bsdfPdfThreshold && getLuma(throughput)>lumaThroughputThreshold)
276+
{
277+
rayStack[stackPtr]._payload.throughput = throughput*rcpChoiceProb;
278+
279+
float heuristicFactor = rcpChoiceProb-1.0; // weightNonGenerator/weightGenerator
280+
heuristicFactor /= doNEE ? lightPdf:bsdfPdf; // weightNonGenerator/(weightGenerator*probGenerated)
281+
heuristicFactor *= heuristicFactor; // (weightNonGenerator/(weightGenerator*probGenerated))^2
282+
if (doNEE)
283+
heuristicFactor = 1.0/(1.0/bsdfPdf+heuristicFactor*bsdfPdf); // numerically stable, don't touch
284+
rayStack[stackPtr]._payload.otherTechniqueHeuristic = heuristicFactor;
285+
286+
// trace new ray
287+
rayStack[stackPtr]._immutable.origin = intersection+_sample.L*(doNEE ? maxT:1.0/*kSceneSize*/)*getStartTolerance(depth);
288+
rayStack[stackPtr]._immutable.maxT = maxT;
289+
rayStack[stackPtr]._immutable.direction = _sample.L;
290+
rayStack[stackPtr]._immutable.typeDepthSampleIx = bitfieldInsert(sampleIx,depth+1,DEPTH_BITS_OFFSET,DEPTH_BITS_COUNT)|(doNEE ? ANY_HIT_FLAG:0);
291+
rayStack[stackPtr]._immutable.normalAtOrigin = interaction.isotropic.N;
292+
rayStack[stackPtr]._immutable.wasBSDFAtOrigin = isBSDF;
293+
stackPtr++;
294+
}
295+
}
296+
}

0 commit comments

Comments
 (0)