问题
I'm working on a raytracer using C++, and so far I've been able to compute a lighting model based on diffuse, specular and ambient components. My problem appeared when I tried to add shadows to my scenes: the scenes get really messed up:
My code is divided as follows:
- I have a base class "SceneObject", which has a virtual method intersect(), which takes a ray (defined by origin and direction) and outputs a boolean, as well as return arguments for the calculated t value, a hitpoint and the normal of the object.
- The "Material" class contains a specular and diffuse colours (vectors) as well as a value for a phong exponent (int).
- I have 3 derived classes from the above SceneObject: plane, triangle and sphere classes, each with it's own version of the intersect defined in the base class.
- I have a function that calculates output colour for a given pixel using the normal of the object, the hitpoint, a source of light and the object material.
- All my objects to render are stored in a vector.
Here is one of my derived classes, Triangle:
class Triangle: public SceneObject{
public:
Triangle (vec3 a, vec3 b, vec3 c, Material mat){
name = "Triangle";
p0 = a;
p1 = b;
p2 = c;
objectMaterial = mat;
normal = normalize(cross(p0-p1, p0-p2));
}
//Möller-Trumbore algorithm
bool intersect(Ray aRay, float &t, vec3 &hitPoint, vec3 &n){//we will use ray-plane intersection and barycentric coords:
bool returnValue = false;
//first we need to get a t of intersection between the passed ray and the triangle
vec3 v0v1 = p1-p0;
vec3 v0v2 = p2-p0;
vec3 pvec = cross(aRay.getDirection(), v0v2);
float det = dot(v0v1, pvec);
if ( det >= 1e-6 ){ // Only draw if not backfacing
float invDet = 1/det;
float u = dot(-p0, pvec) * invDet;
// No intersection if u < 0 or u > 1
if (u >=0 && u <= 1) {
vec3 qvec = cross(-p0, v0v1);
float v = dot(aRay.getDirection(), qvec) * invDet;
// No intersection if v < 0 or u + v > 1
if (v >=0 && (u + v) <= 1){
t = dot(v0v2, qvec) * invDet;
returnValue = true;
hitPoint = aRay.getOrigin() + (t*aRay.getDirection());
n = normal;
//calculated_Out_Colour = calculateOutputColour(normal, aRay, lightSource, objectMaterial, t, hitPoint);
}
}
}
return returnValue;
}
private:
vec3 p0;
vec3 p1;
vec3 p2;
vec3 normal;
};
And this is my main loop where i generate all my rays for every pixel of my window, and determine the colour and if the current position is in shade or not:
for(int i=0;i<imageBuffer.Height();i++){
for(int j=0;j<imageBuffer.Width();j++){
float currentX = ((float)i-256);
float currentY = ((float)j-256);
//cout << currentX << ", " << currentY << ", " << currentZ << endl;
//make a ray for this pixel (i,j)
glm::vec3 rayDirection = glm::normalize(glm::vec3(currentX, currentY, -d));
//make a ray for this pixel (i,j)
Ray currentRay(vec3(0,0,0), rayDirection);
vec3 hitPoint;
vec3 normalAtHit;
float tnear = 999; // closest intersection, set to INFINITY to start with
SceneObject* object = NULL;
for (int k = 0; k < objects.size(); k++) {
float t; // intersection to the current object if any
if (objects[k]->intersect(currentRay, t, hitPoint, normalAtHit) && t < tnear) {
object = objects[k].get();
tnear = t;
vec3 shadowRayDirection = normalize(light1.getLightOrigin()-hitPoint);
Ray shadowRay(hitPoint+vec3(0.03, 0.03,0.03), shadowRayDirection);
float shadowT;
vec3 shadowHitPoint;
vec3 shadowN;
for (int m = 0; m < objects.size(); ++m) {
if (objects[m]->intersect(shadowRay, shadowT, shadowHitPoint, shadowN)) {
imageBuffer.SetPixel(i, j, ambientColour*ambientIntensity);
break;
} else {
imageBuffer.SetPixel(i, j, calculateOutputColour(normalAtHit, currentRay, light1, objects[k]->getMaterial(), hitPoint));
}
}
}
}
}
}
I'm honestly at a loss here and I have no clue why this is happening. I've tried using the algorithm described here, but it produces the same result shown in the image. For reference, if I take the loop and check for shadows, my scene looks like this:
I appreciate any help in trying to debug this thing. Thanks.
回答1:
Cornstalks is right but one thing important if you are using hitPoint+0.03*normalAtHit, you need to be sure that the normal is the one that matches the incident ray, from what we see in your code you just take the normal of your primitive but each face has 2 normals (one reversed from the other).
Depending on that you may have a ray that start from the wrong side and thus may cause invalid shadow.
To do this you can check the dot product of your incidentRay with the normal, depending on the result (greater than 0 or less than 0), you will know if you need to reverse your normal.
来源:https://stackoverflow.com/questions/36255098/unexpected-result-when-computing-shadows-in-a-ray-tracer