Point3D RayScene::GetColor(Ray3D ray,int rDepth,Point3D cLimit){ if (rDepth == 0) {return Point3D();} if (cLimit[0] > 1 && cLimit[1] > 1 && cLimit[2] > 1) {return Point3D();} RayIntersectionInfo iInfo; double resp = group->intersect(ray, iInfo, -1); if (resp > 0) { //Calculating reflection values. Point3D reflectedDirection = Reflect(ray.direction, iInfo.normal); Ray3D reflectedRay(iInfo.iCoordinate+(reflectedDirection.unit()*0.0001), reflectedDirection.unit()); //Calculating and finding the refraction values. Point3D refractedDirection; double refIndex; if (ray.direction.dot(iInfo.normal) < 0) { refIndex = iInfo.material->refind; } else { refIndex = 1/iInfo.material->refind; } int refract = Refract(ray.direction.unit(), iInfo.normal.unit(), refIndex, refractedDirection); Ray3D refractedRay(iInfo.iCoordinate+(refractedDirection.unit()*0.0001), refractedDirection.unit()); Point3D refractedTransparency = iInfo.material->transparent; //Calculating light contributions to the point. Point3D diffuse = Point3D(0,0,0); Point3D specular = Point3D(0,0,0); for (int i=0; i<lightNum; i++) { RayIntersectionInfo iInfo2 = iInfo; int iSectCount = 0; Point3D diffuseResp = lights[i]->getDiffuse(camera->position, iInfo2); Point3D specularResp = lights[i]->getSpecular(camera->position, iInfo2); Point3D transparency = lights[i]->transparency(iInfo2, group, cLimit); diffuse += diffuseResp*transparency; specular += specularResp*transparency; } Point3D response = iInfo.material->ambient*ambient+iInfo.material->emissive + diffuse + specular + GetColor(reflectedRay, rDepth-1, cLimit/iInfo.material->specular) + GetColor(refractedRay, rDepth-1, cLimit/iInfo.material->specular)*refractedTransparency; for (int i = 0; i <3; i++) { if (response[i] < 0) { response[i] = 0; } if (response[i] > 1) { response[i] = 1; } } return response; } else if (ray.position[0]==camera->position[0] && ray.position[1]==camera->position[1] && ray.position[2]==camera->position[2]) { return background; } else return Point3D(); }
// Light that bounces multiple times before reaching the eye Rgb Dielectric::indirectRadiance(Intersection *intersection, Ray ray, Scene *scene, int depth) { /* * IDEA: Set index of refraction to be the same as the airs. In this event, should go straight through * and the towardsViewer and refracted vector should differ in sign value (ie, they will be pointing * in opposite directions, but parallel) */ //TODO: Might as well store the surface normal with the intersection Vector3 towardsViewer = -ray.getDirection().normalized(); Vector3 normal = intersection->getSurface()->computeSurfaceNormal(intersection->getIntersectionPoint()).normalized(); Ray reflectedRay(intersection->getIntersectionPoint(), -towardsViewer.reflect(normal), ray.getRefractiveIndex(), ray.getMaxDepth()); // Add refracted ray bool isReflected = false; Vector3 refractedDirection = towardsViewer.refract(normal, ray.getRefractiveIndex(), refractiveIndex, isReflected); Ray refractedRay(intersection->getIntersectionPoint(), refractedDirection, refractiveIndex, ray.getMaxDepth()); // Compute fresnel double fresnel = computeFresnel(towardsViewer, normal, ray.getRefractiveIndex(), refractiveIndex); Rgb radiance; if (fresnel >= 1.0 || isReflected == true) { radiance = scene->trace(reflectedRay, EPSILON, depth + 1).getPixelColor(); } else { Rgb refractedColor = scene->trace(refractedRay, EPSILON, depth + 1).getPixelColor(); Rgb reflectedColor = scene->trace(reflectedRay, EPSILON, depth + 1).getPixelColor(); radiance.u.a[0] = (1.0 - fresnel) * refractedColor.u.a[0] + (fresnel) * reflectedColor.u.a[0]; radiance.u.a[1] = (1.0 - fresnel) * refractedColor.u.a[1] + (fresnel) * reflectedColor.u.a[1]; radiance.u.a[2] = (1.0 - fresnel) * refractedColor.u.a[2] + (fresnel) * reflectedColor.u.a[2]; } return kAbsorption * radiance; }
float3 trace(const Ray& ray, int depth) { Hit hit = firstIntersect(ray); // If ray hits nothing, we return the aurora borealis if(hit.t < 0) { return ray.dir * ray.dir * float3(0,1,0.6); } float3 lightSum = {0,0,0}; for(LightSource *l : lightSources) { float3 li = l->getLightDirAt(ray.origin); float dist = l->getDistanceFrom(hit.position); int maxDepth = 5; // Deals with shadows Ray shadowRay(hit.position + (hit.normal*0.1), li); Hit shadowHit = firstIntersect(shadowRay); if(shadowHit.t > 0 && shadowHit.t < dist) continue; // Handles types of materials differently if(depth > maxDepth) return lightSum; if(hit.material->reflective){ // for smooth surface float3 reflectionDir = hit.material->reflect(ray.dir, hit.normal); Ray reflectedRay(hit.position + hit.normal*0.1, reflectionDir ); lightSum += trace(reflectedRay, depth+1) * hit.material->getReflectance(ray.dir, hit.normal); } if(hit.material->refractive) { // for smooth surface float3 refractionDir = hit.material->refract(ray.dir, hit.normal); Ray refractedRay(hit.position - hit.normal*0.1, refractionDir ); lightSum += trace(refractedRay, depth+1) * (float3(1,1,1) - hit.material->getReflectance(ray.dir, hit.normal)); } else { // for rough surface lightSum += hit.material->shade(hit.normal, -ray.dir, l->getLightDirAt(hit.position), l->getPowerDensityAt(hit.position)); } } return lightSum; }
// Do recursive ray tracing! You'll want to insert a lot of code here // (or places called from here) to handle reflection, refraction, etc etc. vec3f RayTracer::traceRay( Scene *scene, const ray& r, const vec3f& thresh, int depth ) { isect i; vec3f colorC; if (scene->intersect(r, i)) { // YOUR CODE HERE // An intersection occurred! We've got work to do. For now, // this code gets the material for the surface that was intersected, // and asks that material to provide a color for the ray. // This is a great place to insert code for recursive ray tracing. // Instead of just returning the result of shade(), add some // more steps: add in the contributions from reflected and refracted // rays. const Material& m = i.getMaterial(); vec3f intensity = m.shade(scene, r, i); if (depth == 0) return intensity; if (thresh.length() < AdaptiveThreshold) return intensity; vec3f Qpt = r.at(i.t); vec3f minusD = -1 * r.getDirection(); vec3f cosVector = i.N * (minusD * i.N); vec3f sinVector = cosVector + r.getDirection(); vec3f newThresh = thresh; // Reflected Ray if (!m.kr(i).iszero()) { vec3f reflectedDirection = cosVector + sinVector; reflectedDirection.normalize(); ray reflectedRay(Qpt, reflectedDirection, ray::REFLECTION); newThresh = prod(newThresh, i.getMaterial().kr(i)); // change the threshold value intensity = intensity + prod(m.kr(i), traceRay(scene, reflectedRay, newThresh, depth - 1)); } //Refracted Ray if (!m.kt(i).iszero()) { double cosineAngle = acos(i.N * r.getDirection()) * 180 / M_PI; double n_i, n_r; double criticalAngle = 360; int iDirection; // bool goingIn = true; // double cosThetaI = 0; if (cosineAngle > 90) // Coming into an object from air { n_i = 1; n_r = m.index(i); iDirection = 1; // cosThetaI = i.N * -1 * r.d; } else // Going out from object to air { n_i = m.index(i); n_r = 1; // goingIn = false; // cosThetaI = i.N * r.d; iDirection = -1; } double n = n_i / n_r; if (1 - n * n * (1 - (minusD * i.N) * (minusD * i.N)) > 0.0) // NO total internal refraction { vec3f sinT = n * sinVector; // vec3f cosT = (-1 * i.N) * sqrt(1 - sinT*sinT); // not sure if there are any differences between the two eqn, please check!!!!!! vec3f cosT = (-1 * i.N) * sqrt(1 - n * n * (1 - (minusD * i.N) * (minusD * i.N))); vec3f refractedDirection = cosT + iDirection*sinT; refractedDirection.normalize(); ray refractedRay(Qpt, iDirection * refractedDirection, ray::REFRACTION); newThresh = prod(newThresh, i.getMaterial().kt(i)); // change the threshold value intensity = intensity + prod(m.kt(i), traceRay(scene, refractedRay, newThresh, depth - 1)); } } colorC = intensity; } else { // No intersection. This ray travels to infinity, so we color // it according to the background color, which in this (simple) case // is just black. colorC = vec3f (0.0, 0.0, 0.0); } return colorC; }