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