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:
Here's the same simulation with a dimFac of 1 so that the mirror has no effect on the final color:
Here dimFac is 0.8
And here it's 0.1
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.
addColor()
method: isdimFac
a per-object attribute, like in a material? Or is it a global value? Can you explain how you're using theColor
c
? – Daniel A. Thompson