1
+ // Copyright (C) 2018-2020 - DevSH Graphics Programming Sp. z O.O.
2
+ // This file is part of the "Nabla Engine".
3
+ // For conditions of distribution and use, see copyright notice in nabla.h
4
+
1
5
#version 430 core
2
6
#extension GL_GOOGLE_include_directive : require
3
7
4
8
#define SPHERE_COUNT 8
5
- #define RECTANGLE_METHOD 0 // 0 area sampling, 1 solid angle sampling, 2 approximate projected solid angle sampling
9
+ #define POLYGON_METHOD 0 // 0 area sampling, 1 solid angle sampling, 2 approximate projected solid angle sampling
6
10
#include "common.glsl"
7
11
12
+
8
13
#define RECTANGLE_COUNT 1
14
+ const vec3 edge0 = normalize (vec3 (2 ,0 ,- 1 ));
15
+ const vec3 edge1 = normalize (vec3 (2 ,- 5 ,4 ));
9
16
Rectangle rectangles[RECTANGLE_COUNT] = {
10
- 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)
17
+ Rectangle_Rectangle(vec3 (- 3 .8 ,0.35 ,1 .3 ),edge0 * 7.0 ,edge1 * 0.1 ,INVALID_ID_16BIT,0u)
11
18
};
12
19
13
20
14
- bool traceRay( in ImmutableRay_t _immutable )
21
+ void traceRay_extraShape( inout int objectID, inout float intersectionT, in vec3 origin, in vec3 direction )
15
22
{
16
- const bool anyHit = bitfieldExtract(_immutable.typeDepthSampleIx,31 ,1 )!= 0 ;
17
-
18
- int objectID = - 1 ;
19
- float intersectionT = _immutable.maxT;
20
- for (int i= 0 ; i< SPHERE_COUNT; i++ )
21
- {
22
- float t = Sphere_intersect(spheres[i],_immutable.origin,_immutable.direction);
23
- bool closerIntersection = t> 0.0 && t< intersectionT;
24
-
25
- objectID = closerIntersection ? i: objectID;
26
- intersectionT = closerIntersection ? t: intersectionT;
27
-
28
- // allowing early out results in a performance regression, WTF!?
29
- // if (anyHit && closerIntersection && anyHitProgram(_immutable))
30
- // break;
31
- }
32
23
for (int i= 0 ; i< RECTANGLE_COUNT; i++ )
33
24
{
34
- float t = Rectangle_intersect(rectangles[i],_immutable. origin,_immutable. direction);
25
+ float t = Rectangle_intersect(rectangles[i],origin,direction);
35
26
bool closerIntersection = t> 0.0 && t< intersectionT;
36
27
37
28
objectID = closerIntersection ? (i+ SPHERE_COUNT): objectID;
38
29
intersectionT = closerIntersection ? t: intersectionT;
39
-
40
- // allowing early out results in a performance regression, WTF!?
41
- // if (anyHit && closerIntersection && anyHitProgram(_immutable))
42
- // break;
43
30
}
44
- rayStack[stackPtr]._mutable.objectID = objectID;
45
- rayStack[stackPtr]._mutable.intersectionT = intersectionT;
46
- // hit
47
- return anyHit;
48
31
}
49
32
50
-
51
33
// / #include <nbl/builtin/glsl/sampling/projected_spherical_rectangle.glsl>
52
-
53
-
54
- // the interaction here is the interaction at the illuminator-end of the ray, not the receiver
55
- vec3 nbl_glsl_light_deferred_eval_and_prob(
56
- out float pdf, in Light light, in vec3 L
57
- #if RECTANGLE_METHOD== 0
58
- ,in float intersectionT
59
- #else
60
- ,in vec3 origin
61
- #if RECTANGLE_METHOD== 2
62
- ,in vec3 normalAtOrigin, in bool wasBSDFAtOrigin
63
- #endif
64
- #endif
65
- )
34
+ float nbl_glsl_light_deferred_pdf(in Light light, in Ray_t ray)
66
35
{
67
- // 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
68
- pdf = scene_getLightChoicePdf(light);
36
+ const Rectangle rect = rectangles[Light_getObjectID(light)];
69
37
70
- Rectangle rect = rectangles[Light_getObjectID(light)];
71
- #if RECTANGLE_METHOD== 0
72
- pdf *= intersectionT* intersectionT/ abs (dot (Rectangle_getNormalTimesArea(rect),L));
38
+ const vec3 L = ray._immutable.direction;
39
+ #if POLYGON_METHOD== 0
40
+ const float dist = ray._mutable.intersectionT;
41
+ return dist* dist/ abs (dot (Rectangle_getNormalTimesArea(rect),L));
73
42
#else
74
- const mat3 sphericalVertices = nbl_glsl_shapes_getSphericalTriangle( mat3 (tri.vertex0,tri.vertex1,tri.vertex2),origin) ;
75
- Triangle tmpTri = Triangle_Triangle (mat3 (tri.vertex0,tri.vertex1,tri.vertex2),0u,0u );
76
- #if RECTANGLE_METHOD == 1
77
- float rcpProb = nbl_glsl_shapes_SolidAngleOfTriangle(sphericalVertices);
43
+ const ImmutableRay_t _immutable = ray._immutable ;
44
+ const mat3 sphericalVertices = nbl_glsl_shapes_getSphericalTriangle (mat3 (tri.vertex0,tri.vertex1,tri.vertex2),_immutable.origin );
45
+ #if POLYGON_METHOD == 1
46
+ const float rcpProb = nbl_glsl_shapes_SolidAngleOfTriangle(sphericalVertices);
78
47
// if `rcpProb` is NAN then the triangle's solid angle was close to 0.0
79
- pdf = rcpProb> FLT_MIN ? (pdf / rcpProb): FLT_MAX;
80
- #elif RECTANGLE_METHOD == 2
81
- pdf * = nbl_glsl_sampling_probProjectedSphericalTriangleSample(sphericalVertices,normalAtOrigin,wasBSDFAtOrigin,L);
48
+ return rcpProb> FLT_MIN ? (1.0 / rcpProb): FLT_MAX;
49
+ #elif POLYGON_METHOD == 2
50
+ const float pdf = nbl_glsl_sampling_probProjectedSphericalTriangleSample(sphericalVertices,_immutable. normalAtOrigin,_immutable. wasBSDFAtOrigin,L);
82
51
// 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
83
- pdf = pdf< FLT_MAX ? pdf: 0.0 ;
52
+ return pdf< FLT_MAX ? pdf: 0.0 ;
84
53
#endif
85
54
#endif
86
- return Light_getRadiance(light);
87
55
}
88
56
89
-
90
- nbl_glsl_LightSample nbl_glsl_light_generate_and_remainder_and_pdf(out vec3 remainder, out float pdf, out float newRayMaxT, in vec3 origin, in nbl_glsl_AnisotropicViewSurfaceInteraction interaction, in bool isBSDF, in vec3 u, in int depth)
57
+ vec3 nbl_glsl_light_generate_and_pdf(out float pdf, out float newRayMaxT, in vec3 origin, in nbl_glsl_AnisotropicViewSurfaceInteraction interaction, in bool isBSDF, in vec3 xi, in uint objectID)
91
58
{
92
- // normally we'd pick from set of lights, using `u.z`
93
- const Light light = lights[0 ];
94
- const float choicePdf = scene_getLightChoicePdf(light);
95
-
96
- const Rectangle rect = rectangles[Light_getObjectID(light)];
59
+ const Rectangle rect = rectangles[objectID];
97
60
98
- #if RECTANGLE_METHOD == 0
99
- const vec3 point = rect.offset+ rect.edge0* u .x+ rect.edge1* u .y;
100
- vec3 L = point- origin;
61
+ #if POLYGON_METHOD == 0
62
+ const vec3 point = rect.offset+ rect.edge0* xi .x+ rect.edge1* xi .y; // TODO: refactor
63
+ const vec3 L = point- origin;
101
64
102
65
const float distanceSq = dot (L,L);
103
66
const float rcpDistance = inversesqrt (distanceSq);
104
- L *= rcpDistance;
105
-
106
- const float dist = 1.0 / rcpDistance;
107
67
108
- const float rcpPdf = abs (dot (Rectangle_getNormalTimesArea(rect),L))/ (distanceSq* choicePdf);
68
+ pdf = distanceSq/ abs (dot (Rectangle_getNormalTimesArea(rect),L));
69
+ newRayMaxT = 1.0 / rcpDistance;
70
+ return L* rcpDistance;
109
71
#else
110
72
float rcpPdf;
111
73
112
74
const mat3 sphericalVertices = nbl_glsl_shapes_getSphericalTriangle(mat3 (tri.vertex0,tri.vertex1,tri.vertex2),origin);
113
- #if RECTANGLE_METHOD == 1
114
- const vec3 L = nbl_glsl_sampling_generateSphericalTriangleSample(rcpPdf,sphericalVertices,u .xy);
115
- #elif RECTANGLE_METHOD == 2
116
- const vec3 L = nbl_glsl_sampling_generateProjectedSphericalTriangleSample(rcpPdf,sphericalVertices,interaction.isotropic.N,isBSDF,u .xy);
75
+ #if POLYGON_METHOD == 1
76
+ const vec3 L = nbl_glsl_sampling_generateSphericalTriangleSample(rcpPdf,sphericalVertices,xi .xy);
77
+ #elif POLYGON_METHOD == 2
78
+ const vec3 L = nbl_glsl_sampling_generateProjectedSphericalTriangleSample(rcpPdf,sphericalVertices,interaction.isotropic.N,isBSDF,xi .xy);
117
79
#endif
80
+
118
81
// if `rcpProb` is NAN or negative then the triangle's solidAngle or projectedSolidAngle was close to 0.0
119
- rcpPdf = rcpPdf> FLT_MIN ? rcpPdf: 0.0 ;
82
+ pdf = rcpPdf> FLT_MIN ? ( 1.0 / rcpPdf) : 0.0 ;
120
83
121
84
const vec3 N = Triangle_getNormalTimesArea(tri);
122
- const float dist = dot (N,tri.vertex0- origin)/ dot (N,L);
85
+ newRayMaxT = dot (N,tri.vertex0- origin)/ dot (N,L);
86
+ return L;
123
87
#endif
124
-
125
- remainder = Light_getRadiance(light)* rcpPdf;
126
- pdf = 1.0 / rcpPdf;
127
-
128
- newRayMaxT = getEndTolerance(depth)* dist;
129
-
130
- return nbl_glsl_createLightSample(L,interaction);
131
88
}
132
89
133
- void closestHitProgram(in ImmutableRay_t _immutable, inout nbl_glsl_xoroshiro64star_state_t scramble_state)
134
- {
135
- const MutableRay_t mutable = rayStack[stackPtr]._mutable;
136
-
137
- vec3 intersection = _immutable.origin+ _immutable.direction* mutable.intersectionT;
138
- const uint objectID = mutable.objectID;
139
-
140
- uint bsdfLightIDs;
141
- nbl_glsl_AnisotropicViewSurfaceInteraction interaction;
142
- {
143
- nbl_glsl_IsotropicViewSurfaceInteraction isotropic;
144
90
145
- isotropic.V.dir = - _immutable.direction;
146
- // isotropic.V.dPosdScreen = screw that
147
- if (objectID< SPHERE_COUNT)
148
- {
149
- Sphere sphere = spheres[objectID];
150
- isotropic.N = Sphere_getNormal(sphere,intersection);
151
- bsdfLightIDs = sphere.bsdfLightIDs;
152
- }
153
- else
154
- {
155
- Rectangle rect = rectangles[objectID- SPHERE_COUNT];
156
- isotropic.N = normalize (Rectangle_getNormalTimesArea(rect));
157
- bsdfLightIDs = rect.bsdfLightIDs;
158
- }
159
- isotropic.NdotV = dot (isotropic.V.dir,isotropic.N);
160
- isotropic.NdotV_squared = isotropic.NdotV* isotropic.NdotV;
161
-
162
- interaction = nbl_glsl_calcAnisotropicInteraction(isotropic);
163
- }
164
-
165
- const uint lightID = bitfieldExtract(bsdfLightIDs,16 ,16 );
166
-
167
- vec3 throughput = rayStack[stackPtr]._payload.throughput;
168
- // finish MIS
169
- if (lightID!= INVALID_ID_16BIT) // has emissive
91
+ uint getBSDFLightIDAndDetermineNormal(out vec3 normal, in uint objectID, in vec3 intersection)
92
+ {
93
+ if (objectID< SPHERE_COUNT)
170
94
{
171
- float lightPdf;
172
- vec3 lightVal = nbl_glsl_light_deferred_eval_and_prob(
173
- lightPdf,lights[lightID],_immutable.direction
174
- #if RECTANGLE_METHOD== 0
175
- ,mutable.intersectionT
176
- #else
177
- ,_immutable.origin
178
- #if RECTANGLE_METHOD== 2
179
- ,_immutable.normalAtOrigin,_immutable.wasBSDFAtOrigin
180
- #endif
181
- #endif
182
- );
183
- rayStack[stackPtr]._payload.accumulation += throughput* lightVal/ (1.0 + lightPdf* lightPdf* rayStack[stackPtr]._payload.otherTechniqueHeuristic);
95
+ Sphere sphere = spheres[objectID];
96
+ normal = Sphere_getNormal(sphere,intersection);
97
+ return sphere.bsdfLightIDs;
184
98
}
185
-
186
- const int sampleIx = bitfieldExtract(_immutable.typeDepthSampleIx,0 ,DEPTH_BITS_OFFSET);
187
- const int depth = bitfieldExtract(_immutable.typeDepthSampleIx,DEPTH_BITS_OFFSET,DEPTH_BITS_COUNT);
188
-
189
- // check if we even have a BSDF at all
190
- uint bsdfID = bitfieldExtract(bsdfLightIDs,0 ,16 );
191
- if (depth< MAX_DEPTH && bsdfID!= INVALID_ID_16BIT)
99
+ else
192
100
{
193
- // common preload
194
- BSDFNode bsdf = bsdfs[bsdfID];
195
- uint opType = BSDFNode_getType(bsdf);
196
-
197
- #ifdef KILL_DIFFUSE_SPECULAR_PATHS
198
- if (BSDFNode_isNotDiffuse(bsdf))
199
- {
200
- if (rayStack[stackPtr]._payload.hasDiffuse)
201
- return ;
202
- }
203
- else
204
- rayStack[stackPtr]._payload.hasDiffuse = true;
205
- #endif
206
-
207
-
208
- const float bsdfGeneratorProbability = BSDFNode_getMISWeight(bsdf);
209
- mat2x3 epsilon = rand3d(depth,sampleIx,scramble_state);
210
-
211
- float rcpChoiceProb;
212
- const bool doNEE = nbl_glsl_partitionRandVariable(bsdfGeneratorProbability,epsilon[0 ].z,rcpChoiceProb);
213
-
214
-
215
- float maxT;
216
- // the probability of generating a sample w.r.t. the light generator only possible and used when it was generated with it!
217
- float lightPdf;
218
- nbl_glsl_LightSample _sample;
219
- nbl_glsl_AnisotropicMicrofacetCache _cache;
220
- const bool isBSDF = BSDFNode_isBSDF(bsdf);
221
- if (doNEE)
222
- {
223
- vec3 lightRemainder;
224
- _sample = nbl_glsl_light_generate_and_remainder_and_pdf(
225
- lightRemainder,lightPdf,maxT,
226
- intersection,interaction,
227
- isBSDF,epsilon[0 ],depth
228
- );
229
- throughput *= lightRemainder;
230
- }
231
-
232
- bool validPath = true;
233
- const vec3 throughputCIE_Y = transpose (nbl_glsl_sRGBtoXYZ)[1 ]* throughput;
234
- const float monochromeEta = dot (throughputCIE_Y,BSDFNode_getEta(bsdf)[0 ])/ (throughputCIE_Y.r+ throughputCIE_Y.g+ throughputCIE_Y.b);
235
- if (doNEE)
236
- {
237
- // if we allowed non-watertight transmitters (single water surface), it would make sense just to apply this line.
238
- validPath = nbl_glsl_calcAnisotropicMicrofacetCache(_cache,interaction,_sample,monochromeEta);
239
- // but we don't allow non watertight transmitters in this renderer
240
- validPath = validPath && _sample.NdotL> 0.0 ;
241
- }
242
- else
243
- {
244
- maxT = FLT_MAX;
245
- _sample = nbl_glsl_bsdf_cos_generate(interaction,epsilon[0 ],bsdf,monochromeEta,_cache);
246
- }
247
-
248
- // do a cool trick and always compute the bsdf parts this way! (no divergence)
249
- float bsdfPdf;
250
- // the value of the bsdf divided by the probability of the sample being generated
251
- if (validPath)
252
- throughput *= nbl_glsl_bsdf_cos_remainder_and_pdf(bsdfPdf,_sample,interaction,bsdf,monochromeEta,_cache);
253
- else
254
- throughput = vec3 (0.0 );
255
-
256
- // OETF smallest perceptible value
257
- const float bsdfPdfThreshold = getLuma(nbl_glsl_eotf_sRGB(vec3 (1.0 )/ 255.0 ));
258
- const float lumaThroughputThreshold = bsdfPdfThreshold;
259
- if (bsdfPdf> bsdfPdfThreshold && getLuma(throughput)> lumaThroughputThreshold)
260
- {
261
- rayStack[stackPtr]._payload.throughput = throughput* rcpChoiceProb;
262
-
263
- float heuristicFactor = rcpChoiceProb- 1.0 ; // weightNonGenerator/weightGenerator
264
- heuristicFactor /= doNEE ? lightPdf: bsdfPdf; // weightNonGenerator/(weightGenerator*probGenerated)
265
- heuristicFactor *= heuristicFactor; // (weightNonGenerator/(weightGenerator*probGenerated))^2
266
- if (doNEE)
267
- heuristicFactor = 1.0 / (1.0 / bsdfPdf+ heuristicFactor* bsdfPdf); // numerically stable, don't touch
268
- rayStack[stackPtr]._payload.otherTechniqueHeuristic = heuristicFactor;
269
-
270
- // trace new ray
271
- rayStack[stackPtr]._immutable.origin = intersection+ _sample.L* (doNEE ? maxT: 1.0 /* kSceneSize*/ )* getStartTolerance(depth);
272
- rayStack[stackPtr]._immutable.maxT = maxT;
273
- rayStack[stackPtr]._immutable.direction = _sample.L;
274
- rayStack[stackPtr]._immutable.typeDepthSampleIx = bitfieldInsert(sampleIx,depth+ 2 ,DEPTH_BITS_OFFSET,DEPTH_BITS_COUNT)| (doNEE ? ANY_HIT_FLAG: 0 );
275
- rayStack[stackPtr]._immutable.normalAtOrigin = interaction.isotropic.N;
276
- rayStack[stackPtr]._immutable.wasBSDFAtOrigin = isBSDF;
277
- stackPtr++ ;
278
- }
101
+ Rectangle rect = rectangles[objectID- SPHERE_COUNT];
102
+ normal = normalize (Rectangle_getNormalTimesArea(rect));
103
+ return rect.bsdfLightIDs;
279
104
}
280
105
}
0 commit comments