public Vec3f trace(Vec3f rayorig, Vec3f raydir, List <Sphere> spheres, int depth) { float tnear = INFINITY; Sphere sphere = null; //find intersection of this ray with the sphere in the scene int spCount = spheres.Count(); for (int i = 0; i < spCount; ++i) { float t0 = INFINITY; float t1 = INFINITY; (bool, float, float)inter = spheres[i].intersect(rayorig, raydir); if (inter.Item1) { t0 = inter.Item2; t1 = inter.Item3; 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 == null) { return(new Vec3f(1.0f)); } Vec3f surfaceColor = new Vec3f(0.0f); //color of the ray/surfaceof the object intersected by the ray Vec3f phit = rayorig + raydir * tnear; //point of intersection Vec3f nhit = phit - sphere.center; //normal at the intersection point nhit.normalize(); //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. float bias = 0.0001f; //add some bias to the point from which we will be tracing bool inside = false; if (raydir.dot(nhit) > 0) { nhit = -1 * nhit; inside = true; } if ((sphere.transparency > 0 || sphere.reflection > 0) && depth < MAX_RAY_DEPTH) { float facingratio = -raydir.dot(nhit); // change the mix value to tweak the effect float fresneleffect = mix((float)Math.Pow(1 - facingratio, 3), 1, 0.1f); // compute reflection direction (not need to normalize because all vectors // are already normalized) Vec3f refldir = raydir.reflect(nhit); refldir.normalize(); Vec3f reflection = trace(phit + nhit * bias, refldir, spheres, depth + 1); Vec3f refraction = new Vec3f(0.0f); // if the sphere is also transparent compute refraction ray (transmission) if (sphere.transparency != 0.0f) { float ior = 1.1f; float eta = (inside) ? ior : 1 / ior; // are we inside or outside the surface? float cosi = -nhit.dot(raydir); float k = 1 - eta * eta * (1 - cosi * cosi); Vec3f refrdir = raydir * eta + nhit * (eta * cosi - (float)Math.Sqrt(k)); refrdir.normalize(); refraction = trace(phit - nhit * bias, refrdir, spheres, depth + 1); } // the result is a mix of reflection and refraction (if the sphere is transparent) surfaceColor = (reflection * fresneleffect + refraction * (1.0f - fresneleffect) * sphere.transparency) * sphere.surfaceColor; } else { // it's a diffuse object, no need to raytrace any further for (int i = 0; i < spCount; ++i) { if (spheres[i].emissionColor.x > 0) { // this is a light Vec3f transmission = new Vec3f(1.0f); Vec3f lightDirection = spheres[i].center - phit; lightDirection.normalize(); for (int j = 0; j < spCount; ++j) { if (i != j) { if (spheres[j].intersect(phit + nhit * bias, lightDirection).Item1) { transmission = new Vec3f(0.0f); break; } } } surfaceColor += sphere.surfaceColor * transmission * Math.Max(0.0f, nhit.dot(lightDirection)) * spheres[i].emissionColor; } } } return(surfaceColor + sphere.emissionColor); }