// Main rendering function. We compute a camera ray for each pixel of the image // trace it and return a color. If the ray hits a sphere, we return the color of the // sphere at the intersection point, else we return the background color. void render(const unsigned size, const Sphere *spheres) { // unsigned width = 640, height = 480; unsigned width = 1024, height = 768; Vec3 *image, *pixel, aux, raydir; Real invWidth = 1 / (Real)width, invHeight = 1 / (Real)height; Real fov = 30, aspectratio = width / (Real)height; Real xx, yy, angle = tan(M_PI * 0.5 * fov / 180.0); unsigned x, y, i; FILE *F; image = malloc(width * height * sizeof(*image)); pixel = image; Vec3_new0(&aux); // Trace rays for (y = 0; y < height; ++y) { for (x = 0; x < width; ++x, ++pixel) { xx = (2 * ((x + 0.5) * invWidth) - 1) * angle * aspectratio; yy = (1 - 2 * ((y + 0.5) * invHeight)) * angle; Vec3_new(&raydir, xx, yy, -1); Vec3_normalize(&raydir); trace(pixel, &aux, &raydir, size, spheres, 0); } } // Save result to a PPM image (keep these flags if you compile under Windows) F = fopen("./untitled.ppm", "wb"); fprintf(F, "P6\n%u %u\n255\n", width, height); for (i = 0; i < width * height; ++i) { fprintf(F, "%c%c%c", (unsigned char)(min(1.0, image[i].x) * 255), (unsigned char)(min(1.0, image[i].y) * 255), (unsigned char)(min(1.0, image[i].z) * 255)); } fclose(F); free(image); }
void generateSphere(Vector3int* dest, int num, double size) { int i; for(i=0; i < num; i++) { Vector3 tmp; tmp.x = random()-0.5; tmp.y = random()-0.5; tmp.z = random()-0.5; Vec3_normalize(&tmp); dest[i].x = (int)(tmp.x*size); dest[i].y = (int)(tmp.y*size); dest[i].z = (int)(tmp.z*size); } }
// This is the main trace function. It takes a ray as argument (defined by its origin // and direction). We test if this ray intersects any of the geometry in the scene. // If the ray intersects an object, we compute the intersection point, the normal // at the intersection point, and shade this point using this information. // Shading depends on the surface property (is it transparent, reflective, diffuse). // The function returns a color for the ray. If the ray intersects an object that // is the color of the object at the intersection point, otherwise it returns // the background color. void trace(Vec3 *v,const Vec3 *rayorig, const Vec3 *raydir, const unsigned size, const Sphere *spheres, const int depth) { //if (raydir.length() != 1) std::cerr << "Error " << raydir << std::endl; Real tnear = INFINITY; const Sphere *sphere = NULL; unsigned i, j; Vec3 surfaceColor, phit, nhit, aux; Real bias; bool inside; // find intersection of this ray with the sphere in the scene for (i = 0; i < size; ++i) { Real t0 = INFINITY, t1 = INFINITY; if (Sphere_intersect(&spheres[i], rayorig, raydir, &t0, &t1)) { if (t0 < 0) t0 = t1; if (t0 < tnear) { tnear = t0; sphere = &spheres[i]; } } } // if there's no intersection return black or background color if (!sphere) { Vec3_new1(v,2); return; } Vec3_new0(&surfaceColor); // color of the ray/surfaceof the object intersected by the ray // phit = rayorig + raydir * tnear; // point of intersection Vec3_axpy(&phit,tnear,raydir,rayorig); Vec3_subs(&nhit, &phit, &sphere->center); // normal at the intersection point Vec3_normalize(&nhit); // normalize normal direction // If the normal and the view direction are not opposite to each other // reverse the normal direction. That also means we are inside the sphere so set // the inside bool to true. Finally reverse the sign of IdotN which we want // positive. bias = 1e-4; // add some bias to the point from which we will be tracing inside = false; if (Vec3_dot(raydir,&nhit) > 0) Vec3_minus(&nhit), inside = true; if ((sphere->transparency > 0 || sphere->reflection > 0) && depth < MAX_RAY_DEPTH) { Real facingratio = -Vec3_dot(raydir,&nhit); // change the mix value to tweak the effect Real fresneleffect = mix(pow(1 - facingratio, 3), 1, 0.1); // compute reflection direction (not need to normalize because all vectors // are already normalized) Vec3 refldir, reflection, refraction; // refldir = raydir - nhit * 2 * raydir.dot(nhit); Vec3_axpy(&refldir, -2 * Vec3_dot(raydir, &nhit), &nhit, raydir); Vec3_normalize(&refldir); Vec3_axpy(&aux, bias, &nhit, &phit); trace( &reflection, &aux, &refldir, size, spheres, depth + 1); Vec3_new0(&refraction); // if the sphere is also transparent compute refraction ray (transmission) if (sphere->transparency) { Real ior = 1.1, eta = (inside) ? ior : 1 / ior; // are we inside or outside the surface? Real cosi = -Vec3_dot(&nhit,raydir); Real k = 1 - eta * eta * (1 - cosi * cosi); Vec3 refrdir; // refrdir = raydir * eta + nhit * (eta * cosi - sqrt(k)); refrdir = nhit; Vec3_scale(&refrdir, eta * cosi - sqrt(k)); Vec3_axpy(&refrdir, eta, raydir, &refrdir); Vec3_normalize(&refrdir); Vec3_axpy(&aux, -bias, &nhit, &phit); trace(&refraction, &aux, &refrdir, size, spheres, depth + 1); } // the result is a mix of reflection and refraction (if the sphere is transparent) //surfaceColor = (reflection * fresneleffect + // refraction * (1 - fresneleffect) * sphere->transparency) * sphere->surfaceColor; surfaceColor = refraction; Vec3_scale(&surfaceColor, (1 - fresneleffect) * sphere->transparency); Vec3_axpy(&surfaceColor, fresneleffect, &reflection, &surfaceColor); Vec3_prod(&surfaceColor, &surfaceColor, &sphere->surfaceColor); } else { // it's a diffuse object, no need to raytrace any further for (i = 0; i < size; ++i) { if (spheres[i].emissionColor.x > 0) { // this is a light Vec3 transmission, lightDirection; Vec3_new1(&transmission, 1); Vec3_subs(&lightDirection, &spheres[i].center, &phit); Vec3_normalize(&lightDirection); for (j = 0; j < size; ++j) { if (i != j) { Real t0, t1; Vec3_axpy(&aux, bias, &nhit, &phit); if (Sphere_intersect(&spheres[j], &aux, &lightDirection, &t0, &t1)) { // Truco para suavizar sombras if (spheres[j].transparency == 0.0) { Vec3_new0(&transmission); break; } else { Vec3_scale(&transmission, spheres[j].transparency); Vec3_prod(&transmission, &transmission, &spheres[j].surfaceColor); } } } } //surfaceColor += sphere->surfaceColor * transmission * // std::max(T(0), nhit.dot(lightDirection)) * spheres[i]->emissionColor; Vec3_prod(&aux, &sphere->surfaceColor, &transmission); Vec3_prod(&aux, &aux, &spheres[i].emissionColor); Vec3_axpy(&surfaceColor, max(0.0, Vec3_dot(&nhit, &lightDirection)), &aux, &surfaceColor); } } } Vec3_add(v, &surfaceColor, &sphere->emissionColor); }