|
Post by linuxdeveloper on Jan 24, 2021 9:33:09 GMT
Hello, I have all tests passing up until page 199 and I am stuck on "Finding the normal on a child object" The correct answer is: Then n = vector(0.2857, 0.4286, -0.8571) But I am getting Then n = vector(0.0433, 0.0650, 0.9969) I have reviewed my code and do not see anything wrong with the normal_at, world_to_object or normal_to_world functions. I have included them here for review, written in Python. def normal_at(shape, world_point): local_point = world_to_object(shape, world_point) local_normal = shape.local_normal_at(shape, local_point) return normal_to_world(shape, local_normal)
def world_to_object(shape, point): if shape.parent is not None: point = world_to_object(shape.parent, point) return inverse(shape.transform) * point
def normal_to_world(shape, normal): normal = transpose(inverse(shape.transform)) * normal normal.w = 0 normal = normalize(normal)
if shape.parent is not None: normal = normal_to_world(shape.parent, normal)
return normal Does anyone have any suggestions for what I might be doing wrong? I am stumped. My full code is available here: github.com/natehouk/ray-tracerThanks in advance.
|
|
|
Post by jdunlap on Jan 25, 2021 1:53:08 GMT
That code looks just fine to me. Other than yours being Python and mine C#, the code is practically the same. The only difference is that I transform the parent normal before I normalize in normal_to_world, but that should not make a difference.
|
|
|
Post by linuxdeveloper on Jan 25, 2021 11:03:28 GMT
Can you clarify what you mean by "transform the parent normal before I normalize in normal_to_world"? Could you please provide some example code on how yours differs from mine?
|
|
|
Post by jdunlap on Jan 25, 2021 16:34:03 GMT
Sure!
Here is my version of normal_to_world:
public Vec4 NormalToWorld(Vec4 normal)
{
normal = InverseTransform.Transpose() * normal;
if (Parent != null)
normal = Parent.NormalToWorld(normal);
normal.W = 0;
normal = normal.Normalize();
return normal;
}
So, I call Parent.NormalToWorld before I normalize the vector. That is the only difference in all of the posted code, but I can't imagine that would be the problem.
|
|
|
Post by linuxdeveloper on Jan 25, 2021 18:17:09 GMT
You are correct; changing my code to match yours didn't change the values I am receiving. The odd thing is that this code doesn't really touch much else. I am pretty stumped on what could be causing these values to be off, considering all my other tests pass. Not really sure how to even begin debugging this.
|
|
|
Post by jdunlap on Jan 25, 2021 20:55:26 GMT
Can you grab the values at various points and provide a list? That way, I could run it through mine, and we could see where your values are going wrong.
|
|
|
Post by jdunlap on Jan 26, 2021 0:25:02 GMT
Here are my values. I just grabbed them at each point in normal_at to narrow down where the problem is:
Value passed to NormalAt: {1.7321, 1.1547, -5.5774, 1 } Value of localPoint: {0.5774, 0.57735, 0.577366666666666, 1 } Value of localNormal: {0.5774, 0.57735, 0.577366666666666, 0 } Value of WorldNormal: {0.285703681841407, 0.428543151781141, -0.857160529448102, 0 }
|
|
|
Post by linuxdeveloper on Jan 26, 2021 17:56:39 GMT
It is clearly going wrong when I call local_normal:
local_point tuple(0.5773999999999999, 0.57735, 0.5773666666666665, 1.0) local_normal tuple(-0.9833810694334576, 0.128375855930314, 0.1283795618237908, 0.0) normal_to_world tuple(0.04338309366959556, 0.0650727620146843, 0.9969370305227083, 0.0) Here is my local_normal_at function for a sphere. Does this look right?
def local_normal_at(self, sphere, world_point): object_point = inverse(sphere.transform) * world_point object_normal = object_point - point(0, 0, 0) world_normal = transpose(inverse(sphere.transform)) * object_normal world_normal.w = 0 return normalize(world_normal) Did I miss something? All my other tests including the sphere tests are passing.
|
|
|
Post by linuxdeveloper on Jan 26, 2021 18:07:01 GMT
Now I am really confused, because when I change my local_normal_at function in the Sphere to the following, then the shapes tests pass and I get the correct tuple from above which I was getting incorrect before.
def local_normal_at(self, sphere, world_point): return normalize(world_point - point(0, 0, 0)) However, after doing this, two of my spheres tests start failing. If I leave the code as I had it as follows:
def local_normal_at(self, sphere, world_point): object_point = inverse(sphere.transform) * world_point object_normal = object_point - point(0, 0, 0) world_normal = transpose(inverse(sphere.transform)) * object_normal world_normal.w = 0 return normalize(world_normal) Then the spheres tests all pass, but the shapes test fails.
|
|
|
Post by jdunlap on Jan 26, 2021 18:49:49 GMT
Your local_normal_at for the sphere should be what you changed it to (the top one). Normal_at transforms the coordinates to object space before passing them in to local_normal_at, so your second version, in essence, is transforming the points twice in each direction which is causing this test to fail. Which sphere tests fail if you use the top version?
|
|
|
Post by linuxdeveloper on Jan 26, 2021 19:50:48 GMT
Awesome! I got it solved. I corrected the sphere local_normal_at function to be the following, which made all my shapes tests start passing:
def local_normal_at(self, sphere, world_point): return normalize(world_point - point(0, 0, 0)) I then found on page 120 of the book that the two tests "Computer the normal on a translated sphere" and "Computing the normal on a transformed sphere" were to be replaced, which is why they were failing when I corrected the above function. I commented these tests out.
Now all my tests are passing!
Thank you very much for your help.
|
|
|
Post by jdunlap on Jan 26, 2021 20:59:04 GMT
Glad I could be of help, but even more glad that it’s working for you.
|
|