Example debugging log
This is an excellent example debugging log, shared with permission of the students that created it. Note how the authors observed data, formed a hypothesis, conducted tests, and repeated the process.
You need to be practicing this methodology in order to solve bugs successfully.
Java ray tracer log
3/14/19:
- Goal: Try to locate which section of code is causing created images to be "Noisy".
12:30 PM:
-
Hypothesis: If we make changes to the code in the pigments section and the bug is not altered, then we can rule out the bug being located in the pigments section.
-
Change: modified the values of isReflective and isTransmittive in Finish from 0 to 1.
-
Result: Images 4 and 5 became darker. The "noise" remained unaffected.
-
Observation: Test 1 and Test 2 do not use pigments, but still share the same bug. It is reasonable to assume that the bug is not located in the raytracer/pigments/.
We will create a new Hypothesis:
3/17/19:
12:33 PM:
-
Observation: We can narrow our current search by seeing which classes are always called/used in Main. Main uses Scene and Renderer, and calls scene.readScene() and render.getPixelColor. Renderer uses Tracer and Raytracer.
-
Hypothesis: If we change Renderer's getPixilColor to return red, and the bug persists, we will know that The bug lies in Renderer.
-
Change: We modified getPixilColor to return solid red.
-
Result: All images were solid red. We can conclude that the "Noise" does not come from how we draw the image, but is in how the image is made.
We will create a new Hyponthesis.
- Observation: We noticed in the Renderer that ColorUtil is only used if Main.ANTI_ALIAS is true. After setting a breakpoint, we noticed that it was never called, so the bug is not in ColorUtil.
We believe that the bug is located in one of the classes used by Render. We will investigate Tracer first since during a quick test of changing the Shader's initial Color to white, we get a clear image of white circles with gray background. This means that the math used by the RayGenerator is more than likely Correct.
1:09
- Observation: While brainstorming, we realized that something appears to be off with how the shadows are made. EX: There is a section in image 3, where the location of the shadow is free of noise. Further investigation supports the idea that the shadows may have something to do with the bug. EX: The overlaping shadows in image 5 are free of noise (and the clear image provided to us shows that the shadows are overlapping).
1:14
Tracer also uses Shader, so this is a good location to start looking.
- Hypothesis: If we modify Tracer.trace to return red on hit and the bug persists, the bug is not in Shader.
- Change: We modified Tracer.trace to return red instead of using the Shader.
- Result: The shapes were all solid red, but there was a lack of noise. We can conclude that the bug is likely in Shader.
1:22
-
Observation: Shader Actually uses ColorUtil.blend(). We may need to investigate it afterall.
-
Observation: If we tell the shader not add phong shading to things that are not in shadow, the noise disappears.
-
Use of paint told us that the black noise was not actually truely black.
-
Hypothesis: If I modify the sections of the shade method that occur if depth<=MAX_RECURSION_LEVEL and the bug persists, The bug is not in this section of Shader.
-
Change: Made the isReflective() portion return solid red.
-
Result: Tests 4 and 5 were modified, 4 showing that the ground and objects (but not background) were changed to red. Test 5 changed the back two marbles to red. Interestingly, there is now red visible in the first marble, likely the images of the other two.
-
Change: Made a similar change to the isTransmittive() portion:
-
Result: Only test 5 was changed, turning the front marble red.
-
Conclusion, the bug is not located within the MAX_RECURSION_LEVEL.
-
Hypothesis: The bug is located within the "if(obstruction == null)" section
-
Change: Commented out that section.
-
Result: Bug persisted. Bug not located in this section.
2:28 PM
We believe we have narrowed down the bug to somewhere in the shade method, after its start and before it checks for obstructions.
3/19/19
After talking with our professor, we learned that there were no planes in these tests. Instead, they are larger spheres, meaning sphere intersect code that may have the bug.
5:15 PM
Our new working Hypothesis is that the bug is located somewhere in the Sphere's intersect method.
-
Hypothesis: If I modify the value of sphere's this.radius and the bug persists as before, I can conclude that the construction of the sphere is not where the bug is located.
-
Change: I added "-5;" to the end of "this.radius = radius" in the constructor for Sphere.
-
Result: The size of the spheres changed drastically, with test 5 being almost completely obscured. Bug persisted though, so the constructor is unlikely to contain the bug.
-
Observation: A large version of code has been commented out in the intersect() method. There is also a section that has block comments prepared.
-
Hypothesis: If I restore the commented out code and comment out the current intersect code, I can determine if the previous code was fully functional.
-
Change: Enabled prepared block comments, uncommented other code.
-
Result: Test2 produces a completely black image, test1 is black circles, bug persists in test 4.
-
Conclusion: The commented out code is not the code we're looking for.
5:50 PM
Stepping through a pixel that is offcolor, the bug seems to be that light vectors to a point on a sphere are sometimes hitting that sphere and thus being considered obstructed.
- Change: tell the sphere intersection to ignore points on the surface of the sphere by adding a floating point tolerance check
- Result: tests 1 and 2 are bug-free. 3 and 4 seem to be unchanged, and 5 is noise-free but has strange reflectance on the first sphere
- Conclusion: floating point inaccuracy seems to be the cause of at least parts of the bug
Sadly, we don't know enough about how the mathematics works to be able to tell what needs to be checked for imprecision and what doesn't. As of now, noise persists only on very large spheres, which would probably break the tolerance check outright.
Shared with the premission of Dominic Grazioli and Sean Mongoven