1
votes

I've written a ray tracing program that (for the moment) has two options for surface lighting: ambient and reflective. Ambient lighting replicates how natural surfaces scatter light. Reflections obviously replicate how mirrors reflect light. Everything works properly, but I can't figure out how to mix colors with glossy reflections.

The reflection algorithm returns a color and works recursively. Rays are "cast" in the form of parameterized lines. When they hit a reflective surface, they bounce off perfectly (and my methods for this work). Then these reflected rays are used as parameters to call the reflection algorithm again. This goes on until either the current ray hits an ambient (non reflective) surface or the current ray doesn't hit a surface at all.

The way I'm calculating colors now is I'm averaging the colors of the reflected surface and the newly hit surface from back to front. So that the colors on surfaces that the ray hits early on are represented more than later surface colors.

If color A is the color of the first (reflective) surface it hits, color B is the color of the second surface it hits, C is the third, and so on. So in the final color returned will be 50% A, 25% B, 12.5% C...

The method I use for this actually supports a weighted average so that mirrored surfaces have less effect on the final color. Here it is:

public void addColor(Color b, double dimFac) {
    double red = c.getRed() * (1 - dimFac) + b.getRed() * dimFac;
    double green = c.getGreen() * (1 - dimFac) + b.getGreen() * dimFac;
    double blue = c.getBlue() * (1 - dimFac) + b.getBlue() * dimFac;
    c = new Color((int) red,
                  (int) green,
                  (int) blue);
}

Here's a screenshot of the program with this. There are three ambient spheres hovering over a glossy reflective plane, With a 'dimFac' of 0.5:

enter image description here

Here's the same simulation with a dimFac of 1 so that the mirror has no effect on the final color:

enter image description here

Here dimFac is 0.8

enter image description here

And here it's 0.1

enter image description here

Maybe it's just me, but none of these reflections look amazingly realistic. What I'm using as a guide is a powerpoint by Cornell that, among other things, does mention anything about adding the colors. Mirrors do have color to a degree, and I don't know the correct way of mixing the colors. What am I doing something wrong?

So the way I get a color from a ray is as follows. Each iteration of the ray tracer begins with the initiation of shapes. This program supports three shapes: planes, spheres, and rectangular prisms (which is ultimately just 6 planes). I have a class for each shapes, and a class (called Shapes) that can store each type of shape (but only one per object).

After the shapes have been made, a class (called Projector) casts the rays via another class called MasterLight (which actually holds the methods for basic ray tracing, shadows, reflections, and (now) refractions).

In order to get a color of an intersection, I call the method getColor() which takes the vector (how I store 3d points) of the intersection. I use that to determine the unshaded color of a surface. If the surface is untextured and is just a blank color (like the shapes above), then an unshaded color is returned (this is simply stored in each of the shape classes "Color c = Color.RED"). An example being Color.RED

I take that color and recursively plug that back into MasterLight as the base color to get shading as if the surface was normal and ambient. This process returns the shade that the shape would normally have. Now the RGB value might be (128, 0, 0);

public Color getColor(Vector v) {
    if (texturing) {
         return texturingAlgorithm;
    }
    else {
         return c;
    }
}

DimFac the way it's being used has the potential to be anything from 0 to 1; In my program now, it's 0.8 (which is the universal shading constant. In ambient shading, I take the value that I'm dimming the color by and multiply it by 0.8 and add 0.2 (1 - 0.8), so that the dimmest a color can be is at 0.2 of its original brightness).

The addColor is in another class (I have 17 at the moment, one of which is an enum) called Intersection. This stores all important information about intersections of rays with shapes (color, position, normal vector of the hit surface, and some other constant that pertain to the object's material). Color c is the current color at that point in the calculations.

Each iteration of reflections calls addColor with the most recent surface color. To elaborate, if (in the picture above) a ray had just bounced off of the plane and hit a sphere and bounced off into empty space, I first find the color of the sphere's surface at the point of bounce, which is what 'c' is set to. Then I call addColor with the color of the plane at the point of intersection (the first time).

As soon as I've back tracked all of the reflections, I'm left with a color, which is what I use to color the pixel of that particular ray.

Tell if I missed anything or it was unclear.

1
Can you provide a copy of the method you're using to get a color for a ray? Also, can you provide some context around your addColor() method: is dimFac a per-object attribute, like in a material? Or is it a global value? Can you explain how you're using the Color c?Daniel A. Thompson

1 Answers

4
votes

You should use the Phong Shading method, created by Phong-Bui Tong in 1975. The Phong equation simplifies lighting into three components: ambient, diffuse, and specular.

Ambient light is the lighting of your object when in complete darkness. Ambient lighting is not affected by light sources.

Diffuse light is the brightness of light based on the angle between the surface normal of an intersection's and the light vector from the intersection.

Specular light is what I believe you're looking for. It is based on the angle between the vector from the angle of intersection to the camera position and the reflection vector for the light vector about the surface.

Here's how I typically use Phong Shading:

  • For any objects on your scene, define three constants: Ka (ambient lighting), Kd (diffuse lighting), and Ks (specular lighting). We will also define a constant "n" for the shininess of your object. I would keep this value above 3.
  • Find the dot product of the normal vector and the light vector, we'll call this quantity "dF" for now.
  • Now let's calculate the reflection vector: it is the normal vector, multiplied by the dot product of the normal vector and the light vector, multiplied by two. Subtract the light vector, and this should have a magnitude of 1 if the normal and light vectors did.
  • Find the dot product of the reflection vector and the vector to the viewer from the intersection, we'll call this "sF".
  • Finally, we'll call the color of your object "clr" and the final color will be called "fClr".
  • To get the final color, use the formula: fClr = Ka(clr) + Kd(factor)(clr) + Ks(specularFactor^n)(clr)
  • Finally, I check if any of your R, G, or B values are out of bounds. If this is the case, make that R, G, or B value equal to the closest bound.

**Perform the equation for each RGB value, if you are using RGB.

**I would like to note that all RGB values should be scalars 0.0 - 1.0. If you are using 8-bit RGB (0-255), divide the values by 255 before putting them into the equation, and multiply the output values by 255.

**Any time I refer to a vector, it should be a unit vector, that is, it should have a magnitude of 1.

I hope this helps! Good luck!