Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Samples "leak out" from the medium due to imperfect ray-intersection logic #328

Open
Enigmatisms opened this issue Jun 18, 2023 · 0 comments

Comments

@Enigmatisms
Copy link

I have been working on pbrt-v3 for volumetric rendering, and I found this problem:

  • I created a very simple scene, with a homogeneous scattering medium box (the AABB for this box can be defined by two points (0, 0, 0) and (1, 1, 1))
  • I render the scene with volpath (though I think this problem can be observed using any volumetric renderer)
  • Some paths wander out of the volumetric medium but continue to perform distance/direction sampling as if they are still inside of the medium (I am sure I got the medium interface correctly set)

Here is how samples "leak out" from the medium:

  • When doing ray-intersection (for triangles), pbrt-v3 will compute a deltaT (which as I understand is a threshold to prevent rays start from a triangle surface to intersect exactly the same triangle). When the computed depth to the intersection point t is smaller than deltaT, the intersection will be invalidated.
  • However, when the ray is just inside the medium (but very close to the medium surface), if we invalidate this intersection, we might wind up with no intersection (ray.tMax = inf)
  • This is bad, since the medium doesn't know that: (1) we should have a valid intersection althought ray.tMax will be extremely small (2) the intersection is actually rejected. SO, It will continue to sample the medium during distance sampling phase, even if sampling distance actually exits the medium! What's worse, when the sample leaks out like this, nothing can really stop it. We can still have NEE (in volpath) or path connection (in BDPT).

Here is the experiment illustrating my findings, note that the medium box is bounded in [0, 1] (for all x, y, z axis, so you should not see coordinates greater than 1 or less than 0), this is the terminal output:

ray starting at (0.000000, 0.621527, 0.091540) has no intersection.
Newly generated medium interaction is at (-0.003284, 0.705012, 0.093444)
ray starting at (-0.003284, 0.705012, 0.093444) has no intersection.
Newly generated medium interaction is at (-0.006952, 0.697732, 0.102618)
ray starting at (-0.006952, 0.697732, 0.102618) has no intersection.
Newly generated medium interaction is at (-0.065556, 0.628726, 0.039051)
ray starting at (-0.065556, 0.628726, 0.039051) has no intersection.
Newly generated medium interaction is at (-0.069751, 0.630400, 0.013837)
ray starting at (-0.069751, 0.630400, 0.013837) has no intersection.
Newly generated medium interaction is at (-0.257248, 0.561523, 0.116825)
ray starting at (-0.257248, 0.561523, 0.116825) has no intersection.
Newly generated medium interaction is at (-0.280786, 0.589093, 0.151559)
ray starting at (-0.280786, 0.589093, 0.151559) has no intersection.
...

We can see that, the first MediumInteraction is generated at (0.000000, 0.621527, 0.091540), right on the boundary (maybe inside due to the limitation of printing floating digits), the intersection is invalidated but the distance sampling continues, and then it goes on and on outside of the medium, wandering further away and producing bias.

Here is the code I modified to print the output, in volpath.cpp, starting from line 79:

if (ray.medium) {
    beta *= ray.medium->Sample(ray, sampler, arena, &mi);
    if (!foundIntersection) {
        printf("ray starting at (%f, %f, %f) has no intersection.\n", ray.o[0], ray.o[1], ray.o[2]);
        printf("Newly generated medium interaction is at (%f, %f, %f)\n", mi.p[0], mi.p[1], mi.p[2]);
    }
}

So I suppose, the whole thing is caused by the invalidating mechanism in ray-intersection: medium does not know the ray should be classified as being outside of the medium, so the sampling continues. The solution here is however, not easy, I suppose. If the scene is more complex and has other objects, pbrt-v3 might use a further-away intersection point. This would make the renderer confused. I think the solution is Intersect function returns a status flag, indicating how the intersection failed. If we found an intersection invalidated due to being too close to the triangle, we should test the normal and the ray direction to check if we are inside of the object, if so then we notify the medium sampler.

To reproduce, the scene file can be found in github-gist/volume.pbrt, using just the lastest version of this repo and modify volpath.cpp with the code, then you can see this effect, very easy to reproduce. Note that this phenomenon is not rare during rendering.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant