Пример #1
0
        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
        }