private void PhotonShade(HitInfo hit, Line ray, int threadId, int depth, bool inside, ColorIntensity lightCol, Random rnd, double distance, double area) { if (depth > maxrecurse) return; if (lightCol.GreyScale() < minratio) return; RealHitInfo realHit = hit.GetReal(ray); Material surf = realHit.HitStuff; ColorIntensity pigment = realHit.Pigment.GetTexture(realHit.Normal, 0); double dist = realHit.Normal.Start.LineTo(ray.Start).Length; distance += dist; if (surf.Attenutive && inside) { lightCol.R *= Math.Exp(-(1.0 - surf.Attenuation[0]) * dist / surf.AttenuationDistance); lightCol.G *= Math.Exp(-(1.0 - surf.Attenuation[1]) * dist / surf.AttenuationDistance); lightCol.B *= Math.Exp(-(1.0 - surf.Attenuation[2]) * dist / surf.AttenuationDistance); } #if DEBUG Photon currentParent = PhotonParents[threadId]; #endif if ((depth > 0 || allLightsArePhotons)) { Photon photon = new Photon(); photon.HitPos.X = (float)realHit.Normal.Start.X; photon.HitPos.Y = (float)realHit.Normal.Start.Y; photon.HitPos.Z = (float)realHit.Normal.Start.Z; photon.TravelDir.Dx = (float)ray.Direct.Dx; photon.TravelDir.Dy = (float)ray.Direct.Dy; photon.TravelDir.Dz = (float)ray.Direct.Dz; ColorIntensity photonColor = lightCol; if (realLighting || distance == double.PositiveInfinity) { photonColor.R *= area / PhotonCount; photonColor.G *= area / PhotonCount; photonColor.B *= area / PhotonCount; } else { photonColor.R *= distance * distance * area / PhotonCount; photonColor.G *= distance * distance * area / PhotonCount; photonColor.B *= distance * distance * area / PhotonCount; } photon.PhotonColorPower.R = (float)photonColor.R; photon.PhotonColorPower.G = (float)photonColor.G; photon.PhotonColorPower.B = (float)photonColor.B; #if DEBUG photon.parent = currentParent; PhotonParents[threadId] = photon; #endif Map.AddPhoton(photon, threadId); } ColorIntensity reflectance = new ColorIntensity(); ColorIntensity transmitance = new ColorIntensity(); Line refractRay = new Line(); Line reflectRay = new Line(); if (surf.Refractive || surf.Reflective) { ray.Direct = ray.Direct.Scale(1 / ray.Direct.Length); reflectance.R = surf.Reflective ? surf.Reflectance[0] : 0.0; reflectance.G = surf.Reflective ? surf.Reflectance[1] : 0.0; reflectance.B = surf.Reflective ? surf.Reflectance[2] : 0.0; if (surf.Refractive) { double ni = inside ? surf.RefractIndex : 1.0; double nt = (!inside) ? surf.RefractIndex : 1.0; double cratio = ni / nt; double ct1 = -ray.Direct.Dot(realHit.Normal.Direct); double ct2sqrd = 1 - cratio * cratio * (1 - ct1 * ct1); if (ct2sqrd <= 0) { reflectance.R = 1; reflectance.G = 1; reflectance.B = 1; } else { double ct2 = Math.Sqrt(ct2sqrd); // fresnel equations for reflectance perp and parallel. double rperp = (ni * ct1 - nt * ct2) / (ni * ct1 + nt * ct2); double rpll = (nt * ct1 - ni * ct2) / (ni * ct2 + nt + ct1); // assume unpolarised light always - better then tracing 2 // rays for both sides of every interface. double reflectanceval = (rperp * rperp + rpll * rpll) / 2; reflectance.R = Math.Min(1.0, reflectance.R + reflectanceval); reflectance.G = Math.Min(1.0, reflectance.G + reflectanceval); reflectance.B = Math.Min(1.0, reflectance.B + reflectanceval); transmitance.R = 1 - reflectance.R; transmitance.G = 1 - reflectance.G; transmitance.B = 1 - reflectance.B; refractRay.Direct = ray.Direct.Scale(cratio); refractRay.Direct.Add(realHit.Normal.Direct.Scale(cratio * (ct1) - ct2)); refractRay.Start = realHit.Normal.Start.MoveBy(refractRay.Direct.Scale(EPSILON * 10)); } } reflectRay.Direct = new Vector(ray.Direct.Dx, ray.Direct.Dy, ray.Direct.Dz); reflectRay.Direct.Add(realHit.Normal.Direct.Scale(-2 * ray.Direct.Dot(realHit.Normal.Direct))); reflectRay.Start = realHit.Normal.Start.MoveBy(reflectRay.Direct.Scale(EPSILON * 10)); } bool doReflect = false; bool doRefract = false; bool doDifuse = false; double avr = reflectance.R + reflectance.G + reflectance.B; avr /= 3; double avt = transmitance.R + transmitance.G + transmitance.B; avt /= 3; double reflectWeight = Math.Max(avr, 0.0); double refractWeight = Math.Max(avt, 0.0); double diffuseWeight = Math.Max(pigment.GreyScale() * surf.Diffuse, 0.0); double specularity = 1.0; double diffusivity = 1.0; if (surf.Refractive) { specularity = surf.Specularity; diffusivity = 1.0 - specularity; reflectWeight *= surf.Specularity; refractWeight *= surf.Specularity; diffuseWeight *= 1.0-surf.Specularity; } double choice = rnd.NextDouble(); if (choice < diffuseWeight) doDifuse = true; else if (choice < diffuseWeight+reflectWeight) doReflect = true; else if (choice < diffuseWeight+reflectWeight+refractWeight) doRefract = true; if (doRefract && avt > minratio) { SetIn(ref refractRay.Start, threadId); HitInfo hitnew = scene.Intersect(refractRay, threadId); if (!(hitnew.HitDist == -1)) { ColorIntensity lightCol2 = new ColorIntensity(); lightCol2.R = lightCol.R * transmitance.R *specularity/ refractWeight; lightCol2.G = lightCol.G * transmitance.G * specularity / refractWeight; lightCol2.B = lightCol.B * transmitance.B * specularity / refractWeight; PhotonShade(hitnew, refractRay, threadId, depth + 1, !inside, lightCol2, rnd, distance, area); } } if (doReflect && avr > minratio) { SetIn(ref reflectRay.Start, threadId); HitInfo hitnew2 = scene.Intersect(reflectRay, threadId); if (!(hitnew2.HitDist == -1)) { ColorIntensity lightCol2 = new ColorIntensity(); lightCol2.R = lightCol.R * reflectance.R * specularity / reflectWeight; lightCol2.G = lightCol.G * reflectance.G * specularity / reflectWeight; lightCol2.B = lightCol.B * reflectance.B * specularity / reflectWeight; PhotonShade(hitnew2, reflectRay, threadId, depth + 1, inside, lightCol2, rnd, distance, area); } } if (doDifuse) { double z = rnd.NextDouble(); double theta = rnd.NextDouble() * 2.0 * Math.PI; double r = Math.Sqrt(1 - z * z); double x = Math.Cos(theta) * r; double y = Math.Sin(theta) * r; Line diffuseRay = new Line(); Vector a; Vector b; Vector basis = realHit.Normal.Direct.Scale(1.0/realHit.Normal.Direct.Length); GetPerp(basis, out a, out b); diffuseRay.Direct = basis.Scale(z); diffuseRay.Direct.Add(a.Scale(x/a.Length)); diffuseRay.Direct.Add(b.Scale(y/b.Length)); diffuseRay.Start = realHit.Normal.Start.MoveBy(diffuseRay.Direct.Scale(EPSILON * 10)); SetIn(ref diffuseRay.Start, threadId); HitInfo hitnew2 = scene.Intersect(diffuseRay, threadId); if (!(hitnew2.HitDist == -1)) { ColorIntensity lightCol2 = new ColorIntensity(); lightCol2.R = lightCol.R * surf.Diffuse * pigment.R *diffusivity/ diffuseWeight; lightCol2.G = lightCol.G * surf.Diffuse * pigment.G * diffusivity / diffuseWeight; lightCol2.B = lightCol.B * surf.Diffuse * pigment.B * diffusivity / diffuseWeight; PhotonShade(hitnew2, diffuseRay, threadId, depth + 1, inside, lightCol2, rnd, distance, area); } } #if DEBUG PhotonParents[threadId] = currentParent; #endif }