// This is where the magic happens! Trace a ray... Vector3 Trace(Ray ray, short depth, bool drawDebugLine, bool absorb = false) { if (depth == maxDepth) { return(new Vector3()); } // see if the ray hits anything. Intersection intersect = scene.Intersect(ray); if (intersect == null) { // We didn't hit anything. return the background. return(GetSkyColor(ray)); } // Draw some debug output. if (drawDebugLine) { Debugger.DrawDebugLine(ray.origin.X, ray.origin.Z, intersect.intersectionPoint.X, intersect.intersectionPoint.Z, Color.Green); } // Magic! if (absorb && intersect.GetMaterial().absorbtion.Length() > 0) { return(intersect.dist * -intersect.GetMaterial().absorbtion + DoFancyColorCalculations(ray, intersect, depth, drawDebugLine)); } else { return(DoFancyColorCalculations(ray, intersect, depth, drawDebugLine)); } }
private float GetSchlickReflection(Intersection intersect, Ray ray) { float nDotD = Vector3.Dot(intersect.normal, ray.direction); float eta1 = 1; // air float eta2 = intersect.GetMaterial().refractionIndex; if (nDotD < 0) { nDotD = -nDotD; // we are outside the surface, we want cos(theta) to be positive } else { eta1 = eta2; eta2 = 1; } // Calculate some neccessary variables: float r0 = (eta1 - eta2) / (eta1 + eta2); r0 *= r0; float oneMinusCos = 1 - nDotD; // (1 - cos alpha) // Calculate how much light is reflected: return(r0 + (1 - r0) * oneMinusCos * oneMinusCos * oneMinusCos * oneMinusCos * oneMinusCos); }
private Vector3 Refract(Ray ray, Intersection intersect, short depht, bool drawDebugline) { float nDotD = -Vector3.Dot(intersect.normal, ray.direction); float eta1 = 1, eta2 = intersect.GetMaterial().refractionIndex; bool absorb = true; if (nDotD < 0) { // we are inside the surface, cos(theta) is already positive but reverse normal direction eta1 = eta2; eta2 = 1; intersect.InvertNormal(); absorb = false; } nDotD = -Vector3.Dot(intersect.normal, ray.direction); float eta = eta1 / eta2; float c = eta * eta * (1 - nDotD * nDotD); if (c > 1) { return(new Vector3(0, 0, 0)); // Total internal reflection } float cosT = (float)Math.Sqrt(1 - c); Ray refractRay = new Ray(intersect.intersectionPoint, eta * ray.direction + (eta * nDotD - cosT) * intersect.normal); refractRay.origin += 0.001f * refractRay.direction; // offset by a small margin return(Trace(refractRay, depht, drawDebugline, absorb)); }
// Do we have to pass all these ugly arguments? ='( // If we want to multi-thread it: yes. private Vector3 DoFancyColorCalculations(Ray ray, Intersection intersect, short depth, bool drawDebugLine) { depth++; // increment our recursion depth. Vector3 color = new Vector3(); Material material = intersect.GetMaterial(); if (material.isMirror) { color = material.reflectiveness * Reflect(ray, intersect, depth, drawDebugLine); } if (material.isDielectic) { float reflection = GetSchlickReflection(intersect, ray); // Now do the magic: color += material.transparency * reflection * Reflect(ray, intersect, depth, drawDebugLine); // reFLECT color += material.transparency * (1 - reflection) * Refract(ray, intersect, depth, drawDebugLine); //reFRACT } if (material.isShiny) { // Calculate reflection of shiny object } color += intersect.primitive.material.diffuseness * scene.DirectIllumination(intersect, drawDebugLine) * intersect.primitive.GetColor(intersect.intersectionPoint); return(color); }