I've been reading numerous articles about ray tracing and shading, but my ray traced image does not look too good. I'm talking about the very bright green area near the specular highlight. The result green color is maxed out here, it looks like. How to adjust colors and/or shading calculations to make this look correct?
(Never mind the silly code, I'm just trying to get the principles correct first).
Here's how it looks:
Here is the diffuse component only:
Here is the specular component only:
EDIT: Changing diffuse to Color diffuseColor = ColorMake(0.0f, 0.6f, 0.0f); Then the image looks like this:
Point lightPosition = PointMake(-100.0f, 100.0f, -100.0f);
Color diffuseColor = ColorMake(0.0f, 1.0f, 0.0f);
Color specularColor = ColorMake(1.0f, 1.0f, 1.0f);
Color pixelColor = ColorMake(0.0f, 0.0f, 0.0f);
// Trace...
// Diffuse
Point intersectionPosition = PointMake(x, y, z);
Vector intersectionNormal = VectorMake((x - xs) / rs, (y - ys) / rs, (z - zs) / rs);
Vector intersectionNormalN = VectorNormalize(intersectionNormal);
Vector lightVector = VectorSubtract(lightPosition, intersectionPosition);
VectorlightVectorN = VectorNormalize(lightVector);
float cosTheta = VectorDotProduct(intersectionNormalN, lightVectorN);
if (cosTheta < 0.0f)
{
cosTheta = 0.0f;
}
pixelColor = ColorMultScalar(diffuseColor, cosTheta);
// Specular
Vector incomVector = VectorSubtract(intersectionPosition, lightPosition);
Vector incomVectorN = VectorNormalize(incomVector);
float myDot = - VectorDotProduct(incomVectorN, intersectionNormalN);
float myLen = 2.0f * myDot;
Vector tempNormal = VectorMultScalar(intersectionNormalN, myLen);
Vector reflectVector = VectorAdd(tempNormal, incomVectorN);
Vector reflectVectorN = VectorNormalize(reflectVector);
float mySpec = MAX(-VectorDotProduct(reflectVectorN, incomVectorN), 0);
mySpec = powf(mySpec, 5);
specularColor = ColorMultScalar(specularColor, mySpec);
pixelColor = ColorAdd(pixelColor, specularColor);
pixelColor = ColorClamp(pixelColor);
[self putPixelatX:i andY:j andR:pixelColor.r andG:pixelColor.g andB:pixelColor.b];
The problem is, when you compute the diffuse color of the sphere you already have a small area of pixels that are 1 or very near 1 (in the green channel). Adding up the "phong" component (which has a value near one in all the channels) to that, gives an area of pixels that are >= 1. When you then clamp the color value to 1, that area of >=1 stands out.
You can test this by using a picture editing program and do the "addition" overlay of the two layers (the phong layer above the diffuse one). This gives the result you see - and what is to be expected.
You can avoid the problem by a number of measures:
My ray tracing experiments date back some years, but you can try these things.
Update 1:
One thing I noticed, when you gamma correct your output image - the effect is less pronounced ;) - OK, that is kinda patchy.
The ultimate solution is to go phsically-correct or just use another shading model: Wikipedia on Specular highlight.
Update 2:
One actual solution would be to calculate the phong contribution to the final pixel color (that is your variable mySpec
). The idea is to only use a part of the diffuse component where the specular is actually not 0, that is, if you have some specular component, you don't actually see the diffuse component that much (or at all) so it can be adjusted for:
float diffuseContrib = 1.f - mySpec;
That should look nice, but I am not really sure how correct it actually is :).
Note however; this assumes that your specular and diffuse components are in the range [0..1].
My result looks that way: