top of page


Public·5 members

There Is No Light (v1.0.5.1)

The point lights you implemented in chapter 6, "Light and Shading", of The Ray Tracer Challenge are wonderfully simple things, but they are also unlike any actual light source in the real world. For one thing, being without any physical dimension, they cast surreally sharp shadows, with crisp, well-defined edges, like in the following image.

There Is No Light (v1.0.5.1)

Download Zip:

This is not going to be a sophisticated area light, but it will be enough to introduce you to some of the principles behind sampling a light source. By the end, and with a bit of patience, you'll be rendering soft shadows like a boss! And since the journey is never truly done, I'll also provide a few additional resources for you to study if you want to take your area lights further. It's really up to you.

In chapter 8, "Shadows", you implemented a function called is_shadowed(world, point), which returned true if the given point was in shadow, and false otherwise. The first thing you're going to do is rework that function so that it can be used with other kinds of lights to determine occlusion and visibility.

The updated function should not explicitly rely on any light source in the world itself. Instead, it will cast a ray from point to light_position and look to see if any objects intersect the ray between those two points.

Changing this interface is going to break some of your other tests. You can remove the other is_shadowed tests (the new test covers the same conditions). You'll also need to fix your shade_hit function so that it passes the position of your light source to is_shadowed.

For point lights, you'll implement this by calling your newly modified is_shadowed function, passing it the light's position. If the point is in shadow relative to the light's position, then intensity_at should return 0.0. Otherwise, it should return 1.0.

The next step is to take your intensity_at function and plug it into the rest of your ray tracer. You'll do this by updating your lighting() function so it can handle a fractional intensity instead of a boolean "is shadowed" value.

The last parameter of the lighting() function has been changed from a boolean to a floating point number, representing how much light is present. If that value is 1.0, it behaves the same as before when the "shadowed" parameter was false. If it's 0.0, then it's as if "shadowed" was true. The difference is, now you need to update lighting() to handle everything in between.

To make the test pass, your lighting() function needs to multiply the diffuse and specular components by this light_intensity value. Be sure and leave the ambient component alone, though; the ambient value is never affected by the amount of shadow.

That's overwhelming to contemplate from an implementation perspective, though. For this chapter, we're going to strip all the bells and whistles and implement a perfectly flat, rectangular light source. The following diagram shows how it'll be described.

The resulting light source encapsulates the usteps and vsteps arguments, but the full_uvec and full_vvec vectors have been divided by (respectively) usteps and vsteps, so that uvec and vvec represent the dimensions of a single cell of the grid. Also, samples is usteps times vsteps, telling how many cells (samples) will comprise the area light, and position is a point identifying the very center of the area light. (The position attribute is a temporary workaround, so we don't have to reimplement the lighting() function just yet.)

So, yeah, that's all good, but how does this actually translate into a functioning light source? For that, you'll need to implement the intensity_at function for the area light next. Mathematically speaking, your area light's intensity_at function needs to integrate the amount of light arriving at the point from over the entire surface of the area light, which (when framed like that) is non-trivial and more than a little daunting! Fortunately, there are some shortcuts you can take which involve finding intelligent ways to strategically sample the light source, and then averaging those samples together.

First, you need to be able to query the point that will be used to sample each cell on the area light. The cells are identified by (u, v) coordinates, with (0, 0) being the cell nearest the corner. Here's a test introducing a new function, point_on_light(light, u, v), which returns the point in the middle of the cell at the given coordinates.

To make this test pass, you need to iterate over every cell in your area light, fetching the corresponding point with point_on_light(), and then calling is_shadowed() with that point. Every point in shadow is worth 0.0, and every point not in shadow is worth 1.0. Find the average over all the points you've sampled, and voilà! You've got your intensity.

One of the weaknesses of the strategy presented previously, with a regular sampling of points on the light source, is that it fails to acknowledge that light is actually being emitted by the entire surface. This comes back to the idea of integrating across the whole light, which isn't really a feasible thing to do.

Let's simplify the problem, then. What if, instead of trying to sample every point, we sample a random subset of points? The following diagram shows an area light divided into cells, with sample points chosen randomly from within each cell.

Once your sequence works, you can update your area light so that it accepts a sequence to use for jittering the points that the point_on_light() function returns. Every element of the sequence must be a number between 0 and 1, inclusive. In practice, when actually rendering a scene, you'll replace this deterministic sequence with a sequence that actually returns random (or pseudo-random) numbers between 0 and 1.

(Note that the test assumes you're implementing point_on_light in just this way; if you're jittering v before u, for example, the sequence won't be applied in the same order, and your test results won't match these!)

Once that test passes, you can step back and make sure the intensity_at() function works with the jitter-enabled point_on_light() function. It should, since nothing else changed! Here's a test to make sure this is so.

So, yay! No banding! But, though the results are generally better than those that used a regular (non-random) sampling, the shadows do tend to be noisy. There's really no silver bullet in this case. It's all about trade-offs. You can use a smaller area light and get away with fewer samples and grainier shadows, or a larger area light with commensurately more samples and smoother shadows. Or you can find some heuristic to adapt the number of samples depending on how far away the light is from the point being shadow-tested, or maybe on how large the light source is relative to the point. In the end, though, there's no getting around the fact that area lights simply require your ray tracer to work harder.

That said, there are techniques you can use to further optimize how you sample your area lights. One such is adaptive subdivision, where you use some heuristics to determine which portions of your area light to sample at a finer resolution. You can read one (fairly sophisticated) approach in this article from EUROGRAPHICS '92: Adaptive Sampling of Area Light Sources in Ray Tracing Including Diffuse Interreflection. A more analytical approach is found in Soft Shadow Volumes for Ray Tracing with Frustum Shooting. You can find a wealth of additional ideas by looking at the bibliographies for articles like these, as well.

Shadows are not the only part of your ray tracer that involve sampling the light source. If you recall "The Phong Reflection Model" in chapter 6 ("Light and Shading") of The Ray Tracer Challenge, it involved querying the position of the light source in order to build the light vector, which was used to find both the diffuse and specular contributions to your surface shading.

Well, your area lights won't be done until you update these routines to sample your area light as well. That way, the diffuse and specular shading will more accurately depict the effect of the area light.

Take a moment and reacquaint yourself with your lighting() function. The code to compute the ambient component won't change at all, since it is not dependent on the position of the light source. However, for the diffuse and specular components, you're going to want to once again sample different positions on your area light, compute the diffuse and specular contributions at each point, and then average the results together before adding them to the ambient value. This test shows how your function ought to behave with an area light.

Note how the expected color in the first example is just a bit darker than pure white. If this had been a point light, it would have been pure white. Here, though, because the area light's intensity is the sum of all the illumination across its entire surface, the intensity at that point is diminished by the contributions from the light that aren't directly opposite the point.

That's really about it! Now, it's time to just play around with your area lights and see what you discover about them. How well do the shadows render when the area light is large? What about shadows cast by shapes that are small relative to the light source?

Take some time to read the articles mentioned at the end of the "Randomly sampling your area lights" section. Even if you don't try to implement them, it's worth knowing how this problem has been solved by others. Who knows? You might even be inspired to find an entirely new approach and revolutionize computer graphics forever!

Needs excel formula to calculate following:-If value of C2 (in a cell) is greater than 150000, take value greater than 150000 but less than 200000 in C3(in a cell) but if value of C2 is less than or equal to 150000 than ignore it. Please enlighten by providing the formula in MS Excel.

My doubt is that, there wasn't any false condition that was given so I put "0" in the formula I solved in this way because I couldn't remember another way at that time because all the entries in the question were among above-given conditions. So in spite of getting the right answers for all the entries will it be considered wrong? 041b061a72


Welcome to the group! You can connect with other members, ge...
Group Page: Groups_SingleGroup
bottom of page