|
Post by jzarra on Jul 17, 2020 11:55:38 GMT
Hello! As always thanks for your attention and support. Currently I am going through Chapter 11. Everything seems cool so far, but checking out my reflected spheres on a mirror surface (material.reflective = 1) they seem to get stretched. I attach a sample image, where it can be appreciated in the left mirror the enlargement of the middle green sphere, which I am sure it is abnormal since a flat mirror does not distortion images in this way. A little bit of background so far is that the mirrors at the back were not reflecting nothing at all. This was because my implementation was not transforming the normal vector {0,1,0} of the XZ plane. After realizing of this, I override the normal_at function to apply the transformation matrix to the normal, similar to how a sphere overrides the normal_at function. The implementation for a plane is different from the sphere because I assume that for my XZ plane the object_normal is always pointing towards the +Y axis {0,1,0}. In other words, I do not consider the specific point where the ray hits the plane, since wherever happens the normal vector will have the same value. So basically my normal_at function applies the transpose of the inverse transformation matrix of the Plane instance like this: void Plane::normal_at(Matrix& inv_tx, Vector& normal) {
Vector object_normal = this->m_plane_normal; // all plane instances have normal pointing towards +Y {0,1,0} Vector world_normal = Matrix::multiply_vector(Matrix::transpose(inv_tx), object_normal); // returns normal from object space to world space. normal = Vector::normalize(world_normal); // normalize the normal } Has anyone come out with a similar situation? What are your thoughts? Please let me know your comments, I highly appreciate it since I might be assuming something incorrectly, made a mistake in my implementation, etc. If you are curious about the rest of my code please let me know. You could also take a glance at my repository for this project github.com/JSebastianZ/Ray-Tracer . Thanks!
|
|
|
Post by chrisd on Jul 18, 2020 16:19:36 GMT
Do you have your scene available as a YML, or the code you used to generate it? I didn't see that in your repository. Is it one of the methods in Tests.cpp? It would help if we could see what it should look like.
I took a look at the major classes (Shape, Plane). In general, your code looks significantly different than what I have. You don't seem to be relying on the Shape super-class for functionality. For example, your Shape class has an empty intersections method that is overridden by Plane and every other subclass. However, I have the intersections(Ray) method in my Shape class calling an abstract localIntersections(Ray) method (sorry, it's Java, not C++):
public abstract IntersectionList localIntersections(Ray ray);
public final IntersectionList intersections(Ray ray) { Ray localRay = ray.transform(transform.inverse()); savedRay = localRay;
return localIntersections(localRay); }
Then every Shape subclass overrides the localIntersections(Ray) method for their specific functionality. From Plane:
@Override public IntersectionList localIntersections(Ray ray) { if (Math.abs(ray.getDirection().getY()) < Tuple.EPSILON) return new IntersectionList(); double distance = -ray.getOrigin().getY() / ray.getDirection().getY(); return new IntersectionList(new Intersection(distance, this)); }
Similarly, for normals, my Shape class has the basic functionality:
protected abstract Vector localNormalAt(Point point, Intersection intersection);
public final Vector normalAt(Point worldPoint, Intersection hit) { Point localPoint = worldToObject(worldPoint);
Vector localNormalVector = localNormalAt(localPoint, hit);
return normalToWorld(localNormalVector); }
public Point worldToObject(Point point) { if (parent != null) point = parent.worldToObject(point);
return transform.inverse().multiply(point); }
public final Vector normalToWorld(Vector normal) { normal = transform.inverse().transpose().multiply(normal); normal = normal.withW(0).normalize();
if (parent != null) normal = parent.normalToWorld(normal);
return normal; }
And the Plane only knows about localNormalAt(Point, Intersection):
private static final Vector LOCAL_NORMAL = new Vector(0, 1, 0); @Override protected final Vector localNormalAt(Point point, Intersection intersection) { return LOCAL_NORMAL; }
I think it would be most helpful for you to revisit Chapter 9 Planes, section "Refactoring Shapes" and try to make a solid Shape base class. Notice how similar my Shape.intersect method looks to the pseudo code on page 119. (The savedRay = localRay was added in a later chapter.)
I hope this helps!
|
|
|
Post by jzarra on Jul 19, 2020 6:46:21 GMT
Dear Mr. ChrisD Thanks for looking at my post. Regarding the code I use to generate my scene I only have it in C++ like this (it is also available in my repository at the end of Tests.cpp source file): void Tests::reflux() {
World w = World(); // Creates the world with light and shapes Tracer t; // Creates an instance of a "whitted" ray tracer containing all data structures that store hitpoints and hit shapes. Camera camera = Camera(512, 512, (pi/1.5)); // Creates camera instance Point from = Point(-1, 1.5, -5);
Point to = Point(0, 1, 0);
Vector up = Vector(0, 1, 0);
camera.m_transform = Matrix::view_transform(from, to, up);
camera.m_inv_tx = Matrix::inverse(camera.m_transform);
t.render(camera, w); // Render the "w" scene with the whtted ray-tracer }
I agree, my code probably looks different because of features of the C++ language that I am using to improve rendering performance (virtual functions/inheritance instead of nested methods). But from the snippets you share I find our implementations/logic quite similar (same book XD), which makes me believe that if you do not have this issue then my anomaly could come from some other part outside the Plane class implementation. It would be interesting if you could render the same scene with your implementation. The scene/world objects can be found in the World.cpp source file from lines 40 to 149 (not-commented): github.com/JSebastianZ/Ray-Tracer/blob/master/World.cppPlease let me know if you are interest on doing it, and any other question. I would appreciate it. Thanks!
|
|
|
Post by chrisd on Jul 19, 2020 23:18:55 GMT
Hi, jzarra, Thanks for the pointers to where to look in your code. Based on World.cpp, I created a YML file (see below). Here is the resulting image within my renderer. Very similar results as yours, with the same distortion in the reflected spheres you had. Based on this, we can probably rule out a problem in the actual renderer. It looks like you are trying to zoom out by having a high field_of_view. I think this partially accounts for the distortion, but when lowering it I get distortion in the reflected sphere. Maybe one of the others can account for it? The distortion might be less noticeable if you move the camera farther away and have a smaller field_of_view.
Here is my .yml file that tries to replicate your scene: - add: camera width: 512 height: 512 field-of-view: 2.0943951023931954923084289221863 from: [-1, 1.5, -5] to: [0, 1, 0] up: [0, 1, 0]
- add: light at: [ -10, 10, -10 ] intensity: [ 0.5, 0.5, 0.5 ]
# p1 - add: plane material: color: [1, 1, 1] specular: 0 reflective: 1 pattern: type: checker colors: - [ 1.0, 1.0, 1.0 ] - [ 0.0, 0.0, 0.0 ] transform: - [ scale, 2, 2, 2 ]
# p2 - add: plane transform: - [ rotate-x, 1.5707963267948966192313216916398 ] - [ rotate-y, 0.78539816339744830961566084581988 ] - [ translate, 2, 0, 8 ] material: color: [1, 1, 1] specular: 0 reflective: 1 ambient: 0 shininess: 0 diffuse: 1 # pattern-exist = false
# p3 - add: plane transform: - [ rotate-x, 1.5707963267948966192313216916398 ] - [ rotate-y, -0.78539816339744830961566084581988 ] - [ translate, 2, 0, 8 ] material: color: [1, 1, 1] specular: 0 reflective: 1 # pattern-exist = false
# s4 - add: sphere transform: - [ rotate-z, 1.5707963267948966192313216916398 ] - [ translate, -0.15, 1, 0.5 ] material: color: [0.1, 1, 0.5] diffuse: 0.7 specular: 0.3 reflective: 0 # pattern-exist = false
# s5 - add: sphere transform: - [scale, 0.5, 0.5, 0.5] - [translate, 1.5, 0.5, -0.5] material: color: [0.5, 1, 0.1] diffuse: 0.7 specular: 0.3 reflective: 0 # pattern-exist = false
# s6 - add: sphere transform: - [scale, 0.33, 0.33, 0.33] - [translate, -1.5, 0.33, -0.75] material: color: [1, 0.8, 0.1] diffuse: 0.7 specular: 0.3 reflective: 0 # pattern-exist = false
|
|
|
Post by jzarra on Jul 20, 2020 4:54:33 GMT
Hello Mr. ChrisD, Thanks a lot for your input, it guided me to notice some interesting details. Following your suggestion, I have noticed that the distortion is a function of the view_transform "from" parameter = the closer to the rendered object, less distortion on objects far from the origin. I got the following image by position the camera farther from the origin: In addition, the farther the object is from the view_transform "to" parameter, more distorted it gets: I think this makes sense, like the effects produced by fisheye or telephoto lenses. I guess there must be an interesting optic theory behind this, wish I will explore after finishing this chapter. Thanks again for your follow up and support. Good luck!
|
|