|
Post by klomeli on Sept 27, 2021 13:47:03 GMT
I have been happily chugging along TRTC absorbing the concepts as they come along. However in Ch5 Hint #3, the suggestion to create a pixel_size variable threw me for a loop. I don't understand why this is needed. My initial implementation for the "Putting It Together" section didn't initially use this step and my sphere's projection was blocky as a result. When I factored in Hint #3, the output was more in line with what I would expect. The hints were enough to get my implementation to work as expected, but I hate moving on without understanding what I just did. I am missing some conceptual understanding as to what this crucial step is doing. Could someone provide some hints as to why this step is necessary?
Possibly related, I was puzzled to see Hint #4 define the ray in terms of a normalized direction vector. Is this related to the use of the world-based pixel_size variable I mentioned above? If not, why do we need to normalize the direction vector?
Thank you!
|
|
|
Post by inventordave on Sept 27, 2021 21:30:39 GMT
Sure, I'll have a go at explaining it.
Basically, as you know the smallest-sized unit of information your screen can display is a pixel (in the case of screens, a single pixel of a single colour, usually defined by mixing intensities of Red, Green, and Blue). So, when your raytracer fires a ray through the world, to calculate a potential return colour value - having intersected with a Shape, then identified at the "local co-ordinates on a Shape's surface, what the colour it finds, is - the smallest "size" (area, region) of the Shape's surface it wants to make the effort of calculating a result for is going to be a "region" the size of your screen's pixels. Obviously, that's why the simplest way to get the most detail is to return one single colour per pixel-size region of your screen. If you modified your Jamis-based raytracer to fire multiple rays per pixel, your raytracer would need to combine the colour values returned by each of the Rays_Per_Pixel to produce one final combined colour value, to "render"/draw as the single colour value of the screen pixel the ray(s) were fired for.
Your Spheres were originally blocky, because the size of each "point" on it's surface (according to the ray) was bigger than your screen's resolution(pixel size). It got better when you implemented the instructions to calculate the pixel size, because now each individual region on the Shape's surface the ray calculated a colour for ("hit") was as small as possible without being too small for the screen's individual pixels.
The reason the ray is normalized, is because normalizing a vector gives it a "unit" length (a length of 1). Note, a length of 1 is unit-agnostic, which means the units are entirely up to your imagination. You could imagine conceptually for yourself, the units were metres. This would mean a Sphere with a Radius of 1 was 1 metre in radius (so, 2 metres in diameter, or across). It could as easily be miles, km, cm, parsecs, whatever. THe reason to set the direction vector of the ray to 1, is because when it is first "emitted" (conceptually from the light source, computationally in the reverse from the Eye/Camera), it does not have to have a non-unit length. All we need to know from the direction vector is literally the direction in 3d space it is travelling (hence, a "direction vector"). Then the raytracer can kind-of continue to extend the direction vector in it's direction until it encounters a Shape (at this point, we will know the distance from the emition point of the ray to the Shape, because it's simply magnitude(TO - FROM), where TO is the Point in World Space of the point on the surface of the Shape, and FROM is the Point in World Space the Ray was emitted from).
Hope I've done a good job there.
Dave.
|
|
|
Post by klomeli on Sept 28, 2021 13:59:43 GMT
Thanks, inventordave! Your description on the need to normalize makes sense to me. I'm still unsure about the pixel_size. I'm going to internalize what you wrote, go back to my code, and look at some of the debug data to make sense of it. I'll come back and post any updates to my understanding. Thanks again!
|
|
|
Post by Jamis on Oct 4, 2021 15:22:39 GMT
Thanks for your explanation, @inventordave ! I'll add just one more metaphor, which will hopefully not muddy things for klomeli. Imagine you are painting on a canvas which has been divided into a grid. You may only paint on the canvas by filling individual squares of that grid (it isn't legal/possible to paint only *part* of a grid, for example). Now, imagine you are standing a certain distance from the canvas, and you hold up a small pane of glass. Instead of the grid being on the canvas, the grid is now on the glass, and as you look through the glass, you can see the grid overlaid on the canvas. In order to paint a color to fill each square of the grid, you now need to know what the size of a single square is, on the canvas. This is what the pixel_size variable represents---the size of one of those grid squares as *projected* onto the canvas. The pane of glass is like an image, with the grid being the pixels in the image. "pixel_size" is how large a single pixel looks when projected onto your canvas. It's pretty abstract! But if you take a clear bit of glass or plastic and draw a grid on it, and then hold it up and look at a wall or desk or counter through it, you might start to get a more intuitive grasp of what I'm describing. You're basically trying to compute how much of the world you can see through a single square of that grid.
|
|
|
Post by klomeli on Nov 13, 2021 14:34:02 GMT
Thank you, both! This was helpful!
|
|