I'm trying to write a raytracer in C++, but the result so far is not as I expected, so I guess there is an error in the lighting equations. This is what I've got so far:
Picture of my result. In the upper line what I've got, in the bottom line what I would like to get
I'm using the Blinn(-Phong) model and assume that all the lights are point lights. This is my code:
vec3f raytrace_ray(Scene* scene, ray3f ray) {
// get scene intersection
auto intersection = intersect_surfaces(scene, ray);
// if not hit, return background
if (!intersection.hit) {
return scene->background;
}
// accumulate color starting with ambient
vec3f c = zero3f;
c += scene->ambient*intersection.mat->kd;
//Add emission
c += intersection.mat->ke;
// foreach light
for (Light* light : scene->lights) {
// compute light response
auto lightRadiance = light->intensity / distSqr(light->frame.o, intersection.pos);
if(lightRadiance == zero3f) continue;
// compute light direction
auto l = normalize(light->frame.o - intersection.pos);
auto v = normalize(intersection.pos - ray.e);
auto h = normalize(l+v); //bisector
auto n = intersection.mat->n;
auto normal = intersection.norm;
// compute the material response (brdf*cos)
auto brdf = intersection.mat->ks*pow(max(0.0f, dot(normal,h)), intersection.mat->n);
// check for shadows and accumulate if needed
auto shadowRay = ray3f(intersection.pos, l, 0.001f, length(light->frame.o - intersection.pos));
float visibleTerm;
auto shadowIntersect = intersect_surfaces(scene, shadowRay);
if (shadowIntersect.hit) {
visibleTerm = 0;
}
else {
visibleTerm = 1;
}
//Accumulate
c += lightRadiance*visibleTerm*(intersection.mat->kd + brdf)*abs(dot(normal, l));
}
// if the material has reflections
// create the reflection ray
// accumulate the reflected light (recursive call) scaled by the material reflection
// return the accumulated color∫
return c;
}
Now my questions are:
- Where the equation is wrong?
- From the last edit I also added shadows, by creating a ray (the last two parameters of the ray constructor are tmin and tmax) and check if it hit something. If yes I set the visibileTerm to zero (I also updated the screenshots). I'm not sure if this is right.
How can I go further to add also reflection and refraction? I know I have to call recursively the function, but first what I have to compute and in which way? I tried with this code for reflection within the for-loop of lights, but it doesn't work, in some test infinite recursion:
ray3f reflectionRay = ray3f(intersection.pos, -l + 2 * dot(l, normal)*normal); c += intersection.mat->kr*raytrace_ray(scene, reflectionRay);
From the second test of the picture, there is also a problem in the intersection, but I don't know where. I tried also to change the normal calculation with the inverse in this way:
auto normal = transform_normal_inverse(scene->camera->frame, surface->frame.z);
But I get only another inclination of the plane without solve the second test as well. This is my code of the intersection (I used only Quads and Spheres):
intersection3f intersect_surfaces(Scene* scene, ray3f ray) { auto intersection = intersection3f(); float currentDistance = INFINITY; float t; // foreach surface for (Surface* surface : scene->surfaces) { // if it is a quad if (surface->isquad) { /// compute ray intersection (and ray parameter), continue if not hit auto normal = transform_normal(scene->camera->frame, surface->frame.z); if (dot(ray.d, normal) == 0) { continue; } t = dot(surface->frame.o - ray.e, normal)/dot(ray.d,normal); // check if computed param is within ray.tmin and ray.tmax if (t < ray.tmin or t > ray.tmax) continue; // check if this is the closest intersection, continue if not auto p = ray.eval(t);//Intersection point if (dist(ray.e, p) >= currentDistance) { continue; } currentDistance = dist(ray.e, p); // if hit, set intersection record values intersection.ray_t = t; intersection.pos = p; intersection.norm = normalize(ray.e - p); intersection.mat = surface->mat; intersection.hit = true; } // if it is a sphere else { // compute ray intersection (and ray parameter), continue if not hit auto a = lengthSqr(ray.d); auto b = 2 * dot(ray.d, ray.e - surface->frame.o); auto c = lengthSqr(ray.e - surface->frame.o) - surface->radius*surface->radius; auto det = b*b - 4 * a*c; if (det < 0) { continue; } float t1 = (-b - sqrt(det)) / (2 * a); float t2 = (-b + sqrt(det)) / (2 * a); // check if computed param is within ray.tmin and ray.tmax if (t1 >= ray.tmin && t1 <= ray.tmax) t = t1; else if (t2 >= ray.tmin && t2 <= ray.tmax) t = t2; else continue; auto p = ray.eval(t); //Intersection point // check if this is the closest intersection, continue if not if (dist(ray.e, p) > currentDistance) { continue; } currentDistance = dist(ray.e, p); // if hit, set intersection record values intersection.ray_t = t; intersection.pos = p; intersection.norm = normalize(ray.e - p); intersection.mat = surface->mat; intersection.hit = true; intersection.ray_t = 2; } } return intersection; }
Thanks in advance.