Skip to content

Commit 75b0063

Browse files
reimplemented triangle light source case
1 parent 027b1b2 commit 75b0063

File tree

4 files changed

+91
-102
lines changed

4 files changed

+91
-102
lines changed

examples_tests/42.FragmentShaderPathTracer/common.glsl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44

55
// basic settings
66
#define MAX_DEPTH 15
7-
#define SAMPLES 32
7+
#define SAMPLES 64
88

99
// firefly and variance reduction techniques
1010
//#define KILL_DIFFUSE_SPECULAR_PATHS

examples_tests/42.FragmentShaderPathTracer/litBySphere.frag

Lines changed: 11 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@
1212

1313
int traceRay(inout float intersectionT, in vec3 origin, in vec3 direction)
1414
{
15+
const bool anyHit = intersectionT!=FLT_MAX;
16+
1517
int objectID = -1;
1618
for (int i=0; i<SPHERE_COUNT; i++)
1719
{
@@ -22,7 +24,7 @@ int traceRay(inout float intersectionT, in vec3 origin, in vec3 direction)
2224
intersectionT = closerIntersection ? t:intersectionT;
2325

2426
// allowing early out results in a performance regression, WTF!?
25-
//if (anyHit && closerIntersection && anyHitProgram(_immutable))
27+
//if (anyHit && closerIntersection)
2628
//break;
2729
}
2830
return objectID;
@@ -142,19 +144,19 @@ bool closestHitProgram(in uint depth, in uint _sample, inout Ray_t ray, inout nb
142144
if (nbl_glsl_partitionRandVariable(neeSkipProbability,epsilon[0].z,rcpChoiceProb))
143145
{
144146
vec3 neeContrib; float lightPdf, t;
145-
nbl_glsl_LightSample _sample = nbl_glsl_light_generate_and_remainder_and_pdf(neeContrib,lightPdf,t,intersection,interaction,epsilon[0],depth);
147+
nbl_glsl_LightSample nee_sample = nbl_glsl_light_generate_and_remainder_and_pdf(neeContrib,lightPdf,t,intersection,interaction,epsilon[0],depth);
146148
// We don't allow non watertight transmitters in this renderer
147-
bool validPath = _sample.NdotL>0.0;
149+
bool validPath = nee_sample.NdotL>0.0;
148150
// but if we allowed non-watertight transmitters (single water surface), it would make sense just to apply this line by itself
149151
nbl_glsl_AnisotropicMicrofacetCache _cache;
150-
validPath = validPath && nbl_glsl_calcAnisotropicMicrofacetCache(_cache,interaction,_sample,monochromeEta);
152+
validPath = validPath && nbl_glsl_calcAnisotropicMicrofacetCache(_cache,interaction,nee_sample,monochromeEta);
151153
if (validPath)
152154
{
153155
float bsdfPdf;
154-
neeContrib *= nbl_glsl_bsdf_cos_remainder_and_pdf(bsdfPdf,_sample,interaction,bsdf,monochromeEta,_cache)*throughput;
156+
neeContrib *= nbl_glsl_bsdf_cos_remainder_and_pdf(bsdfPdf,nee_sample,interaction,bsdf,monochromeEta,_cache)*throughput;
155157
const float oc = bsdfPdf*rcpChoiceProb;
156158
neeContrib /= 1.0/oc+oc/(lightPdf*lightPdf); // MIS weight
157-
if (bsdfPdf<FLT_MAX && getLuma(neeContrib)>lumaContributionThreshold && traceRay(t,intersection+_sample.L*t*getStartTolerance(depth),_sample.L)==-1)
159+
if (bsdfPdf<FLT_MAX && getLuma(neeContrib)>lumaContributionThreshold && traceRay(t,intersection+nee_sample.L*t*getStartTolerance(depth),nee_sample.L)==-1)
158160
ray._payload.accumulation += neeContrib;
159161
}
160162
}
@@ -163,11 +165,11 @@ bool closestHitProgram(in uint depth, in uint _sample, inout Ray_t ray, inout nb
163165
float bsdfPdf; vec3 bsdfSampleL;
164166
{
165167
nbl_glsl_AnisotropicMicrofacetCache _cache;
166-
nbl_glsl_LightSample _sample = nbl_glsl_bsdf_cos_generate(interaction,epsilon[1],bsdf,monochromeEta,_cache);
168+
nbl_glsl_LightSample bsdf_sample = nbl_glsl_bsdf_cos_generate(interaction,epsilon[1],bsdf,monochromeEta,_cache);
167169
// the value of the bsdf divided by the probability of the sample being generated
168-
throughput *= nbl_glsl_bsdf_cos_remainder_and_pdf(bsdfPdf,_sample,interaction,bsdf,monochromeEta,_cache);
170+
throughput *= nbl_glsl_bsdf_cos_remainder_and_pdf(bsdfPdf,bsdf_sample,interaction,bsdf,monochromeEta,_cache);
169171
//
170-
bsdfSampleL = _sample.L;
172+
bsdfSampleL = bsdf_sample.L;
171173
}
172174

173175
// additional threshold

examples_tests/42.FragmentShaderPathTracer/litByTriangle.frag

Lines changed: 78 additions & 91 deletions
Original file line numberDiff line numberDiff line change
@@ -14,40 +14,36 @@ Triangle triangles[TRIANGLE_COUNT] = {
1414
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)
1515
};
1616

17-
bool traceRay(in ImmutableRay_t _immutable)
17+
int traceRay(inout float intersectionT, in vec3 origin, in vec3 direction)
1818
{
19-
const bool anyHit = bitfieldExtract(_immutable.typeDepthSampleIx,31,1)!=0;
19+
const bool anyHit = intersectionT!=FLT_MAX;
2020

2121
int objectID = -1;
22-
float intersectionT = _immutable.maxT;
2322
for (int i=0; i<SPHERE_COUNT; i++)
2423
{
25-
float t = Sphere_intersect(spheres[i],_immutable.origin,_immutable.direction);
24+
float t = Sphere_intersect(spheres[i],origin,direction);
2625
bool closerIntersection = t>0.0 && t<intersectionT;
2726

2827
objectID = closerIntersection ? i:objectID;
2928
intersectionT = closerIntersection ? t:intersectionT;
3029

3130
// allowing early out results in a performance regression, WTF!?
32-
//if (anyHit && closerIntersection && anyHitProgram(_immutable))
31+
//if (anyHit && closerIntersection)
3332
//break;
3433
}
3534
for (int i=0; i<TRIANGLE_COUNT; i++)
3635
{
37-
float t = Triangle_intersect(triangles[i],_immutable.origin,_immutable.direction);
36+
float t = Triangle_intersect(triangles[i],origin,direction);
3837
bool closerIntersection = t>0.0 && t<intersectionT;
3938

4039
objectID = closerIntersection ? (i+SPHERE_COUNT):objectID;
4140
intersectionT = closerIntersection ? t:intersectionT;
4241

4342
// allowing early out results in a performance regression, WTF!?
44-
//if (anyHit && closerIntersection && anyHitProgram(_immutable))
43+
//if (anyHit && closerIntersection)
4544
//break;
4645
}
47-
rayStack[stackPtr]._mutable.objectID = objectID;
48-
rayStack[stackPtr]._mutable.intersectionT = intersectionT;
49-
// hit
50-
return anyHit;
46+
return objectID;
5147
}
5248

5349

@@ -90,7 +86,7 @@ vec3 nbl_glsl_light_deferred_eval_and_prob(
9086
}
9187

9288

93-
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)
89+
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 uint depth)
9490
{
9591
// normally we'd pick from set of lights, using `u.z`
9692
const Light light = lights[0];
@@ -135,13 +131,17 @@ nbl_glsl_LightSample nbl_glsl_light_generate_and_remainder_and_pdf(out vec3 rema
135131
return nbl_glsl_createLightSample(L,interaction);
136132
}
137133

138-
void closestHitProgram(in ImmutableRay_t _immutable, inout nbl_glsl_xoroshiro64star_state_t scramble_state)
134+
135+
bool closestHitProgram(in uint depth, in uint _sample, inout Ray_t ray, inout nbl_glsl_xoroshiro64star_state_t scramble_state)
139136
{
140-
const MutableRay_t mutable = rayStack[stackPtr]._mutable;
137+
const MutableRay_t _mutable = ray._mutable;
141138

142-
vec3 intersection = _immutable.origin+_immutable.direction*mutable.intersectionT;
143-
const uint objectID = mutable.objectID;
144-
139+
const uint objectID = _mutable.objectID;
140+
Sphere sphere = spheres[_mutable.objectID];
141+
142+
// interaction stuffs
143+
const ImmutableRay_t _immutable = ray._immutable;
144+
const vec3 intersection = _immutable.origin+_immutable.direction*_mutable.intersectionT;
145145
uint bsdfLightIDs;
146146
nbl_glsl_AnisotropicViewSurfaceInteraction interaction;
147147
{
@@ -167,119 +167,106 @@ void closestHitProgram(in ImmutableRay_t _immutable, inout nbl_glsl_xoroshiro64s
167167
interaction = nbl_glsl_calcAnisotropicInteraction(isotropic);
168168
}
169169

170-
const uint lightID = bitfieldExtract(bsdfLightIDs,16,16);
170+
//
171+
vec3 throughput = ray._payload.throughput;
171172

172-
vec3 throughput = rayStack[stackPtr]._payload.throughput;
173-
// finish MIS
173+
// add emissive and finish MIS
174+
const uint lightID = bitfieldExtract(bsdfLightIDs,16,16);
174175
if (lightID!=INVALID_ID_16BIT) // has emissive
175176
{
176177
float lightPdf;
177178
vec3 lightVal = nbl_glsl_light_deferred_eval_and_prob(
178179
lightPdf,lights[lightID],_immutable.direction
179180
#if TRIANGLE_METHOD==0
180-
,mutable.intersectionT
181+
,_mutable.intersectionT
181182
#else
182183
,_immutable.origin
183184
#if TRIANGLE_METHOD==2
184185
,_immutable.normalAtOrigin,_immutable.wasBSDFAtOrigin
185186
#endif
186187
#endif
187188
);
188-
rayStack[stackPtr]._payload.accumulation += throughput*lightVal/(1.0+lightPdf*lightPdf*rayStack[stackPtr]._payload.otherTechniqueHeuristic);
189+
ray._payload.accumulation += lightVal*throughput/(1.0+lightPdf*lightPdf*ray._payload.otherTechniqueHeuristic);
189190
}
190-
191-
const int sampleIx = bitfieldExtract(_immutable.typeDepthSampleIx,0,DEPTH_BITS_OFFSET);
192-
const int depth = bitfieldExtract(_immutable.typeDepthSampleIx,DEPTH_BITS_OFFSET,DEPTH_BITS_COUNT);
193191

194192
// check if we even have a BSDF at all
195193
uint bsdfID = bitfieldExtract(bsdfLightIDs,0,16);
196-
if (depth<MAX_DEPTH && bsdfID!=INVALID_ID_16BIT)
194+
if (bsdfID!=INVALID_ID_16BIT)
197195
{
198-
// common preload
199196
BSDFNode bsdf = bsdfs[bsdfID];
200-
uint opType = BSDFNode_getType(bsdf);
201-
202197
#ifdef KILL_DIFFUSE_SPECULAR_PATHS
203198
if (BSDFNode_isNotDiffuse(bsdf))
204199
{
205-
if (rayStack[stackPtr]._payload.hasDiffuse)
206-
return;
200+
if (ray._payload.hasDiffuse)
201+
return true;
207202
}
208203
else
209-
rayStack[stackPtr]._payload.hasDiffuse = true;
204+
ray._payload.hasDiffuse = true;
210205
#endif
211-
212-
213-
const float bsdfGeneratorProbability = BSDFNode_getMISWeight(bsdf);
214-
mat2x3 epsilon = rand3d(depth,sampleIx,scramble_state);
215-
216-
float rcpChoiceProb;
217-
const bool doNEE = nbl_glsl_partitionRandVariable(bsdfGeneratorProbability,epsilon[0].z,rcpChoiceProb);
218-
219-
220-
float maxT;
221-
// the probability of generating a sample w.r.t. the light generator only possible and used when it was generated with it!
222-
float lightPdf;
223-
nbl_glsl_LightSample _sample;
224-
nbl_glsl_AnisotropicMicrofacetCache _cache;
206+
225207
const bool isBSDF = BSDFNode_isBSDF(bsdf);
226-
if (doNEE)
208+
//rand
209+
mat2x3 epsilon = rand3d(depth,_sample,scramble_state);
210+
211+
// thresholds
212+
const float bsdfPdfThreshold = 0.0001;
213+
const float lumaContributionThreshold = getLuma(nbl_glsl_eotf_sRGB(vec3(1.0)/255.0)); // OETF smallest perceptible value
214+
const vec3 throughputCIE_Y = transpose(nbl_glsl_sRGBtoXYZ)[1]*throughput;
215+
const float monochromeEta = dot(throughputCIE_Y,BSDFNode_getEta(bsdf)[0])/(throughputCIE_Y.r+throughputCIE_Y.g+throughputCIE_Y.b);
216+
217+
// do NEE
218+
const float neeSkipProbability = BSDFNode_getNEESkipProb(bsdf);
219+
float rcpChoiceProb;
220+
if (nbl_glsl_partitionRandVariable(neeSkipProbability,epsilon[0].z,rcpChoiceProb))
227221
{
228-
vec3 lightRemainder;
229-
_sample = nbl_glsl_light_generate_and_remainder_and_pdf(
230-
lightRemainder,lightPdf,maxT,
222+
vec3 neeContrib; float lightPdf, t;
223+
nbl_glsl_LightSample nee_sample = nbl_glsl_light_generate_and_remainder_and_pdf(
224+
neeContrib,lightPdf,t,
231225
intersection,interaction,
232226
isBSDF,epsilon[0],depth
233227
);
234-
throughput *= lightRemainder;
235-
}
236-
237-
bool validPath = true;
238-
const vec3 throughputCIE_Y = transpose(nbl_glsl_sRGBtoXYZ)[1]*throughput;
239-
const float monochromeEta = dot(throughputCIE_Y,BSDFNode_getEta(bsdf)[0])/(throughputCIE_Y.r+throughputCIE_Y.g+throughputCIE_Y.b);
240-
if (doNEE)
241-
{
242-
// if we allowed non-watertight transmitters (single water surface), it would make sense just to apply this line.
243-
validPath = nbl_glsl_calcAnisotropicMicrofacetCache(_cache,interaction,_sample,monochromeEta);
244-
// but we don't allow non watertight transmitters in this renderer
245-
validPath = validPath && _sample.NdotL>0.0;
228+
// We don't allow non watertight transmitters in this renderer
229+
bool validPath = nee_sample.NdotL>0.0;
230+
// but if we allowed non-watertight transmitters (single water surface), it would make sense just to apply this line by itself
231+
nbl_glsl_AnisotropicMicrofacetCache _cache;
232+
validPath = validPath && nbl_glsl_calcAnisotropicMicrofacetCache(_cache,interaction,nee_sample,monochromeEta);
233+
if (validPath)
234+
{
235+
float bsdfPdf;
236+
neeContrib *= nbl_glsl_bsdf_cos_remainder_and_pdf(bsdfPdf,nee_sample,interaction,bsdf,monochromeEta,_cache)*throughput;
237+
const float oc = bsdfPdf*rcpChoiceProb;
238+
neeContrib /= 1.0/oc+oc/(lightPdf*lightPdf); // MIS weight
239+
if (bsdfPdf<FLT_MAX && getLuma(neeContrib)>lumaContributionThreshold && traceRay(t,intersection+nee_sample.L*t*getStartTolerance(depth),nee_sample.L)==-1)
240+
ray._payload.accumulation += neeContrib;
241+
}
246242
}
247-
else
243+
244+
// sample BSDF
245+
float bsdfPdf; vec3 bsdfSampleL;
248246
{
249-
maxT = FLT_MAX;
250-
_sample = nbl_glsl_bsdf_cos_generate(interaction,epsilon[0],bsdf,monochromeEta,_cache);
247+
nbl_glsl_AnisotropicMicrofacetCache _cache;
248+
nbl_glsl_LightSample bsdf_sample = nbl_glsl_bsdf_cos_generate(interaction,epsilon[1],bsdf,monochromeEta,_cache);
249+
// the value of the bsdf divided by the probability of the sample being generated
250+
throughput *= nbl_glsl_bsdf_cos_remainder_and_pdf(bsdfPdf,bsdf_sample,interaction,bsdf,monochromeEta,_cache);
251+
//
252+
bsdfSampleL = bsdf_sample.L;
251253
}
252-
253-
// do a cool trick and always compute the bsdf parts this way! (no divergence)
254-
float bsdfPdf;
255-
// the value of the bsdf divided by the probability of the sample being generated
256-
if (validPath)
257-
throughput *= nbl_glsl_bsdf_cos_remainder_and_pdf(bsdfPdf,_sample,interaction,bsdf,monochromeEta,_cache);
258-
else
259-
throughput = vec3(0.0);
260254

261-
// OETF smallest perceptible value
262-
const float bsdfPdfThreshold = getLuma(nbl_glsl_eotf_sRGB(vec3(1.0)/255.0));
263-
const float lumaThroughputThreshold = bsdfPdfThreshold;
255+
// additional threshold
256+
const float lumaThroughputThreshold = lumaContributionThreshold;
264257
if (bsdfPdf>bsdfPdfThreshold && getLuma(throughput)>lumaThroughputThreshold)
265258
{
266-
rayStack[stackPtr]._payload.throughput = throughput*rcpChoiceProb;
267-
268-
float heuristicFactor = rcpChoiceProb-1.0; // weightNonGenerator/weightGenerator
269-
heuristicFactor /= doNEE ? lightPdf:bsdfPdf; // weightNonGenerator/(weightGenerator*probGenerated)
270-
heuristicFactor *= heuristicFactor; // (weightNonGenerator/(weightGenerator*probGenerated))^2
271-
if (doNEE)
272-
heuristicFactor = 1.0/(1.0/bsdfPdf+heuristicFactor*bsdfPdf); // numerically stable, don't touch
273-
rayStack[stackPtr]._payload.otherTechniqueHeuristic = heuristicFactor;
259+
ray._payload.throughput = throughput;
260+
ray._payload.otherTechniqueHeuristic = (1.0-neeSkipProbability)/bsdfPdf; // numerically stable, don't touch
261+
ray._payload.otherTechniqueHeuristic *= ray._payload.otherTechniqueHeuristic;
274262

275263
// trace new ray
276-
rayStack[stackPtr]._immutable.origin = intersection+_sample.L*(doNEE ? maxT:1.0/*kSceneSize*/)*getStartTolerance(depth);
277-
rayStack[stackPtr]._immutable.maxT = maxT;
278-
rayStack[stackPtr]._immutable.direction = _sample.L;
279-
rayStack[stackPtr]._immutable.typeDepthSampleIx = bitfieldInsert(sampleIx,depth+2,DEPTH_BITS_OFFSET,DEPTH_BITS_COUNT)|(doNEE ? ANY_HIT_FLAG:0);
280-
rayStack[stackPtr]._immutable.normalAtOrigin = interaction.isotropic.N;
281-
rayStack[stackPtr]._immutable.wasBSDFAtOrigin = isBSDF;
282-
stackPtr++;
264+
ray._immutable.origin = intersection+bsdfSampleL*(1.0/*kSceneSize*/)*getStartTolerance(depth);
265+
ray._immutable.direction = bsdfSampleL;
266+
ray._immutable.normalAtOrigin = interaction.isotropic.N;
267+
ray._immutable.wasBSDFAtOrigin = isBSDF;
268+
return true;
283269
}
284270
}
271+
return false;
285272
}

examples_tests/42.FragmentShaderPathTracer/main.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -261,7 +261,7 @@ int main()
261261
return { gpuPipeline, gpuMeshBuffer };
262262
};
263263

264-
E_LIGHT_GEOMETRY lightGeom = ELG_SPHERE;
264+
E_LIGHT_GEOMETRY lightGeom = ELG_TRIANGLE;
265265
constexpr const char* shaderPaths[] = {"../litBySphere.frag","../litByTriangle.frag","../litByRectangle.frag"};
266266
auto gpuEnvmapResources = createGpuResources(shaderPaths[lightGeom]);
267267
auto gpuEnvmapPipeline = gpuEnvmapResources.first;

0 commit comments

Comments
 (0)