Skip to content

Commit c06f32c

Browse files
implement fixed sample count MIS
1 parent 8b22095 commit c06f32c

File tree

2 files changed

+33
-65
lines changed

2 files changed

+33
-65
lines changed

examples_tests/42.FragmentShaderPathTracer/common.glsl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -247,7 +247,7 @@ float BSDFNode_getMISWeight(in BSDFNode bsdf)
247247
const float alpha = BSDFNode_getRoughness(bsdf);
248248
const bool notDiffuse = BSDFNode_isNotDiffuse(bsdf);
249249
const float DIFFUSE_MIS_WEIGHT = 0.5;
250-
return notDiffuse ? mix(1.0,DIFFUSE_MIS_WEIGHT,alpha):DIFFUSE_MIS_WEIGHT; // TODO: test alpha*alpha
250+
return (notDiffuse ? (alpha*alpha):1.0)*DIFFUSE_MIS_WEIGHT;
251251
}
252252

253253
#include <nbl/builtin/glsl/colorspace/EOTF.glsl>

examples_tests/42.FragmentShaderPathTracer/litBySphere.frag

Lines changed: 32 additions & 64 deletions
Original file line numberDiff line numberDiff line change
@@ -28,15 +28,14 @@ int traceRay(inout float intersectionT, in vec3 origin, in vec3 direction)
2828
return objectID;
2929
}
3030

31-
#if 0
31+
3232
// the interaction here is the interaction at the illuminator-end of the ray, not the receiver
3333
vec3 nbl_glsl_light_deferred_eval_and_prob(out float pdf, in Sphere sphere, in vec3 origin, in nbl_glsl_AnisotropicViewSurfaceInteraction interaction, in Light light)
3434
{
3535
// 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
3636
pdf = scene_getLightChoicePdf(light)/Sphere_getSolidAngle(sphere,origin);
3737
return Light_getRadiance(light);
3838
}
39-
#endif
4039

4140
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 vec3 u, in uint depth)
4241
{
@@ -82,15 +81,37 @@ bool closestHitProgram(in uint depth, in uint _sample, inout Ray_t ray, inout nb
8281
const MutableRay_t _mutable = ray._mutable;
8382

8483
Sphere sphere = spheres[_mutable.objectID];
84+
85+
// interaction stuffs
86+
const vec3 intersection = ray._immutable.origin+ray._immutable.direction*_mutable.intersectionT;
87+
nbl_glsl_AnisotropicViewSurfaceInteraction interaction;
88+
{
89+
nbl_glsl_IsotropicViewSurfaceInteraction isotropic;
90+
91+
isotropic.V.dir = -ray._immutable.direction;
92+
//isotropic.V.dPosdScreen = screw that
93+
isotropic.N = Sphere_getNormal(sphere,intersection);
94+
isotropic.NdotV = dot(isotropic.V.dir,isotropic.N);
95+
isotropic.NdotV_squared = isotropic.NdotV*isotropic.NdotV;
96+
97+
interaction = nbl_glsl_calcAnisotropicInteraction(isotropic);
98+
}
99+
100+
//
85101
const uint bsdfLightIDs = sphere.bsdfLightIDs;
86102

103+
//
87104
vec3 throughput = ray._payload.throughput;
88-
#if 0
89-
// add emissive
105+
106+
// add emissive and finish MIS
90107
const uint lightID = bitfieldExtract(bsdfLightIDs,16,16);
91108
if (lightID!=INVALID_ID_16BIT) // has emissive
92-
ray._payload.accumulation += throughput*Light_getRadiance(lights[lightID]);
93-
#endif
109+
{
110+
float lightPdf;
111+
vec3 lightVal = nbl_glsl_light_deferred_eval_and_prob(lightPdf,sphere,ray._immutable.origin,interaction,lights[lightID]);
112+
ray._payload.accumulation += lightVal*throughput/(1.0+lightPdf*lightPdf*ray._payload.otherTechniqueHeuristic);
113+
}
114+
94115
// check if we even have a BSDF at all
95116
uint bsdfID = bitfieldExtract(bsdfLightIDs,0,16);
96117
if (bsdfID!=INVALID_ID_16BIT)
@@ -108,21 +129,6 @@ bool closestHitProgram(in uint depth, in uint _sample, inout Ray_t ray, inout nb
108129

109130
//rand
110131
mat2x3 epsilon = rand3d(depth,_sample,scramble_state);
111-
112-
// interaction stuffs
113-
const vec3 intersection = ray._immutable.origin+ray._immutable.direction*_mutable.intersectionT;
114-
nbl_glsl_AnisotropicViewSurfaceInteraction interaction;
115-
{
116-
nbl_glsl_IsotropicViewSurfaceInteraction isotropic;
117-
118-
isotropic.V.dir = -ray._immutable.direction;
119-
//isotropic.V.dPosdScreen = screw that
120-
isotropic.N = Sphere_getNormal(sphere,intersection);
121-
isotropic.NdotV = dot(isotropic.V.dir,isotropic.N);
122-
isotropic.NdotV_squared = isotropic.NdotV*isotropic.NdotV;
123-
124-
interaction = nbl_glsl_calcAnisotropicInteraction(isotropic);
125-
}
126132

127133
// thresholds
128134
const float bsdfPdfThreshold = 0.0001;
@@ -131,7 +137,8 @@ bool closestHitProgram(in uint depth, in uint _sample, inout Ray_t ray, inout nb
131137
const float monochromeEta = dot(throughputCIE_Y,BSDFNode_getEta(bsdf)[0])/(throughputCIE_Y.r+throughputCIE_Y.g+throughputCIE_Y.b);
132138

133139
// do NEE
134-
float rcpNEEProb = 1.0;
140+
const float neeProbability = 1.0;//BSDFNode_getMISWeight(bsdf);
141+
const float neeProbability2 = neeProbability*neeProbability;
135142
if (true)
136143
{
137144
vec3 neeContrib; float lightPdf, t;
@@ -145,8 +152,9 @@ bool closestHitProgram(in uint depth, in uint _sample, inout Ray_t ray, inout nb
145152
{
146153
float bsdfPdf;
147154
neeContrib *= nbl_glsl_bsdf_cos_remainder_and_pdf(bsdfPdf,_sample,interaction,bsdf,monochromeEta,_cache)*throughput;
155+
neeContrib /= bsdfPdf/(lightPdf*lightPdf)+neeProbability2/bsdfPdf; // MIS weight
148156
if (bsdfPdf<FLT_MAX && getLuma(neeContrib)>lumaContributionThreshold && traceRay(t,intersection+_sample.L*t*getStartTolerance(depth),_sample.L)==-1)
149-
ray._payload.accumulation += neeContrib*bsdfPdf;
157+
ray._payload.accumulation += neeContrib;
150158
}
151159
}
152160

@@ -166,6 +174,7 @@ bool closestHitProgram(in uint depth, in uint _sample, inout Ray_t ray, inout nb
166174
if (bsdfPdf>bsdfPdfThreshold && getLuma(throughput)>lumaThroughputThreshold)
167175
{
168176
ray._payload.throughput = throughput;
177+
ray._payload.otherTechniqueHeuristic = neeProbability2/(bsdfPdf*bsdfPdf); // numerically stable, don't touch
169178

170179
// trace new ray
171180
ray._immutable.origin = intersection+bsdfSampleL*(1.0/*kSceneSize*/)*getStartTolerance(depth);
@@ -176,46 +185,5 @@ bool closestHitProgram(in uint depth, in uint _sample, inout Ray_t ray, inout nb
176185
return false;
177186
}
178187
#if 0
179-
// finish MIS
180-
if (lightID!=INVALID_ID_16BIT) // has emissive
181-
{
182-
float lightPdf;
183-
vec3 lightVal = nbl_glsl_light_deferred_eval_and_prob(lightPdf,sphere,ray._immutable.origin,interaction,lights[lightID]);
184-
ray._payload.accumulation += ray._payload.throughput*lightVal/(1.0+lightPdf*lightPdf*ray._payload.otherTechniqueHeuristic);
185-
}
186-
187-
188-
189-
190-
191-
const float bsdfGeneratorProbability = BSDFNode_getMISWeight(bsdf);
192188
const bool doNEE = nbl_glsl_partitionRandVariable(bsdfGeneratorProbability,epsilon[0].z,rcpChoiceProb);
193-
194-
float maxT;
195-
// the probability of generating a sample w.r.t. the light generator only possible and used when it was generated with it!
196-
float lightPdf;
197-
nbl_glsl_LightSample _sample;
198-
199-
200-
// OETF smallest perceptible value
201-
const float bsdfPdfThreshold = getLuma(nbl_glsl_eotf_sRGB(vec3(1.0)/255.0));
202-
const float lumaThroughputThreshold = bsdfPdfThreshold;
203-
if (bsdfPdf>bsdfPdfThreshold && (!doNEE || bsdfPdf<FLT_MAX) && getLuma(throughput)>lumaThroughputThreshold)
204-
{
205-
ray._payload.throughput = throughput*rcpChoiceProb;
206-
207-
float heuristicFactor = rcpChoiceProb-1.0; // weightNonGenerator/weightGenerator
208-
heuristicFactor /= doNEE ? lightPdf:bsdfPdf; // weightNonGenerator/(weightGenerator*probGenerated)
209-
heuristicFactor *= heuristicFactor; // (weightNonGenerator/(weightGenerator*probGenerated))^2
210-
if (doNEE)
211-
heuristicFactor = 1.0/(1.0/bsdfPdf+heuristicFactor*bsdfPdf); // numerically stable, don't touch
212-
ray._payload.otherTechniqueHeuristic = heuristicFactor;
213-
214-
// trace new ray
215-
ray._immutable.origin = intersection+_sample.L*(doNEE ? maxT:1.0/*kSceneSize*/)*getStartTolerance(depth);
216-
ray._immutable.maxT = maxT;
217-
ray._immutable.direction = _sample.L;
218-
ray._immutable.anyHit = doNEE;
219-
return false;
220-
}
221189
#endif

0 commit comments

Comments
 (0)