Vec Radiance(Ray ray, int depth, Random rand) { Prec t; SceneObject obj; if (!Scene.Intersect(ray, out t, out obj)) { return(Vec.Zero); } var x = ray.Origin + ray.Direction * t; var n = obj.GetNormal(x); var nl = n.Dot(ray.Direction) < 0 ? n : n * -1; var f = obj.Color; var p = (f.X > f.Y && f.X > f.Z) ? f.X : ((f.Y > f.Z) ? f.Y : f.Z); if (++depth > 5) { if (rand.NextDouble() < p) { f = f * (1.0 / p); } else { return(obj.Emission); } } if (obj.Material == MaterialType.Diffuse) { var r1 = 2 * Math.PI * rand.NextDouble(); var r2 = rand.NextDouble(); var r2s = Math.Sqrt(r2); var w = nl; var u = ((Math.Abs(w.X) > 0.1?new Vec(0, 1):new Vec(1)).Cross(w)).Norm; var v = w.Cross(u); var d = (u * Math.Cos(r1) * r2s + v * Math.Sin(r1) * r2s + w * Math.Sqrt(1 - r2)).Norm; return(obj.Emission + f.Mult(Radiance(new Ray(x, d), depth, rand))); } else if (obj.Material == MaterialType.Specular) { return(obj.Emission + f.Mult(Radiance(new Ray(x, ray.Direction - n * 2 * n.Dot(ray.Direction)), depth, rand))); } var reflRay = new Ray(x, ray.Direction - n * 2 * n.Dot(ray.Direction)); var intoo = n.Dot(nl) > 0; Prec nc = 1.0, nt = 1.5; var nnt = intoo?nc / nt:nt / nc; var ddn = ray.Direction.Dot(nl); var cos2t = 1 - nnt * nnt * (1 - ddn * ddn); if (cos2t < 0) { return(obj.Emission + f.Mult(Radiance(reflRay, depth, rand))); } var tdir = (ray.Direction * nnt - n * ((intoo?1:-1) * (ddn * nnt + Math.Sqrt(cos2t)))).Norm; Prec a = nt - nc, b = nt + nc; var R0 = a * a / (b * b); var c = 1 - (intoo ? -ddn : tdir.Dot(n)); var Re = R0 + (1 - R0) * c * c * c * c * c; var Tr = 1 - Re; var P = 0.25 + 0.5 * Re; var RP = Re / P; var TP = Tr / (1 - P); return(obj.Emission + f.Mult(depth > 2 ? (rand.NextDouble() < P ? Radiance(reflRay, depth, rand) * RP:Radiance(new Ray(x, tdir), depth, rand) * TP) : Radiance(reflRay, depth, rand) * Re + Radiance(new Ray(x, tdir), depth, rand) * Tr)); }