I am trying to implement my own Gradient Domain Path Tracer by following the code of this guy who already implemented it:
https://gist.github.com/BachiLi/4f5c6e5a4fef5773dab1
I already managed to go through various steps but I wanted to do something more. I expanded the code of in the reference by implementing next event estimation and here are some results.
Normal Path Tracer image:
Gradient Domain resulting image:
Results are already good.. but as said before, I wanted something more. So I implemented Next Event Estimation and here is the result of the basic Path Tracer:
Here is my code:
private Vector3 SampleWithNEE( Ray ray )
{
// prepare
Vector3 T = (1,1,1), E = (0,0,0), NL = (0,-1,0);
int depth = 0;
// random walk
while (depth++ < MAXDEPTH)
{
// find nearest ray/scene intersection
Scene.Intersect( ray );
if (ray.objIdx == -1) break; //if there is no intersection
Vector3 I = ray.O + ray.t * ray.D; //go to the Hit Point on the scene
Material material = scene.GetMaterial( ray.objIdx, I );
if (material.emissive) //case of a light
{
E += material.diffuse;
break;
}
// next event estimation
Vector3 BRDF = material.diffuse * 1 / PI;
float f = RTTools.RandomFloat();
Vector3 L = Scene.RandomPointOnLight() - I;
float dist = L.Length();
L = Vector3.Normalize( L );
float NLdotL = Math.Abs( Vector3.Dot( NL, -L ) );
float NdotL = Vector3.Dot( ray.N, L );
if (NdotL > 0)
{
Ray r = new Ray( I + L * EPSILON, L, dist - 2 * EPSILON ); //make it a tiny bit shorter otherwise I risk to hit my starting and destination point
Scene.Intersect( r );
if (r.objIdx == -1) //no occlusion towards the light
{
float solidAngle= (nldotl * light.getArea()) / (dist * dist);
E += T * (NdotL) * solidAngle * BRDF * light.emission;
}
}
// sample random direction on hemisphere
Vector3 R = DiffuseReflectionCosWeighted( ray.N );
float hemi_PDF = Vector3.Dot( R, ray.N ) / PI;
T *= (Vector3.Dot( R, ray.N ) / hemiPDF) * BRDF;
ray = new Ray( I + R * EPSILON, R, 1e34f );
}
return E;
}
Things work really fine and the results are shown with the picture above. One more thing: I have only diffuse surfaces in my scene.
Now, the problem is that in this method I use 2 kind of PDF's:
- one is given by randomly sampling the light in the Direct Lighting of the Next Event Estimation, indeed that SolidAngle is our PDF, or better 1/PDF.
- while the second PDF is the one led by the use of DiffuseReflectionCosWeighted which brings a PDF equals to CosTheta/PI.
Everything fine so far and for any implementation detail you can just look at my code but problems come with my Gradient Domain Path Tracer. Indeed, there, as also in the reference link above implemented by Tzu-Mao Li, I need the final probability density of a whole path to compute the final gradient image. How did I calculate it in case without Next Event Estimation (NEE) ? In that case (since I have only Diffuse surfaces) this probability is the product of CosTheta / PI at each bounce in the scene. Everything is fine and the resulting gradient image is shown above.
Instead, in case I use NEE things don't work anymore because the Probability Density of my whole path changes and I don't manage to understand how it is. The resulting Gradient Domain Image with Next Event Estimation is:
I need to understand how to calculate the final density probability of a path. Can you help me doing it? Thanks in advance!