Пример #1
0
        public override void Transform(Mat4x4D forward, Mat4x4D inverse)
        {
            Vec4D center = forward * GetCenter();

            Normal         = (inverse.Transpose3x3() * Normal).Normalize();
            OriginDistance = center.Dot(Normal);
        }
Пример #2
0
        private DoubleColor GetColor(Ray ray, ref DebugRay[] debug)
        {
            Hit         prevHit = default;
            Hit         hit     = default;
            DoubleColor tint    = new DoubleColor(1);

            for (int i = 0; i <= Scene.Recursion; i++)
            {
                // Periodically normalize the direction vector to prevent compounding error
                if (i % 3 == 0)
                {
                    ray = Ray.Directional(ray.Origin, ray.Direction);
                }

                hit = Scene.RayTrace(ray, prevHit);

                DebugRay debugRay = debug != null ? debug[i] = new DebugRay()
                {
                    Hit = hit
                } : null;

                if (hit == default)
                {
                    if (debugRay != null)
                    {
                        debugRay.Type = BounceType.Missed;
                    }

                    // Return emission or -1 for instant misses
                    if (i == 0)
                    {
                        return(DoubleColor.Placeholder);
                    }

                    // Return miss color for misses
                    return(Scene.AmbientRGB);
                }

                if (Scene.DebugGeom)
                {
                    if (debugRay != null)
                    {
                        debugRay.Type = BounceType.Debug;
                    }

                    return(hit.Primitive.Specular + hit.Primitive.Diffuse + hit.Primitive.Emission);
                }

                if (i >= Scene.Recursion)
                {
                    if (debugRay != null)
                    {
                        debugRay.Type = BounceType.RecursionComplete;
                    }
                    break;
                }

                Ray outRay = Ray.Zero;

                Vec4D roughNormal = RandomShine(hit.Normal, hit.Primitive.Shininess);

                double diffLum = hit.Primitive.Diffuse.Luminance;
                double specLum = hit.Primitive.Specular.Luminance;
                double refrLum = hit.Primitive.Refraction.Luminance;
                double emisLum = hit.Primitive.Emission.Luminance;

                double cos      = -roughNormal.Dot(ray.Direction);
                double cosOut   = 0;
                double iorRatio = 0;

                // Calculate ratio of reflection to transmission on this hit
                if ((refrLum > 0 | specLum > 0) && hit.Primitive.RefractiveIndex != 0 && cos >= 0)
                {
                    double iorIn;
                    double iorOut;

                    if (hit.Inside)
                    {
                        iorIn  = hit.Primitive.RefractiveIndex;
                        iorOut = Scene.AirRefractiveIndex;
                    }
                    else
                    {
                        iorIn  = Scene.AirRefractiveIndex;
                        iorOut = hit.Primitive.RefractiveIndex;
                    }

                    iorRatio = iorIn / iorOut;
                    double sinOut = iorRatio * Math.Sqrt(1 - (cos * cos));

                    // Skip refraction when we get total internal reflection
                    if (sinOut >= 1)
                    {
                        refrLum = 0;

                        if (debugRay != null)
                        {
                            debugRay.FresnelRatio = 1;
                        }
                    }
                    else
                    {
                        cosOut = Math.Sqrt(1 - (sinOut * sinOut));
                        double ratioSWave = ((iorOut * cos) - (iorIn * cosOut)) / ((iorOut * cos) + (iorIn * cosOut));
                        double ratioPWave = ((iorIn * cos) - (iorOut * cosOut)) / ((iorIn * cos) + (iorOut * cosOut));
                        double ratio      = ((ratioSWave * ratioSWave) + (ratioPWave * ratioPWave)) / 2;
                        specLum *= ratio;
                        refrLum *= 1 - ratio;

                        if (debugRay != null)
                        {
                            debugRay.FresnelRatio = ratio;
                        }
                    }
                }
                else
                {
                    refrLum = 0;
                }

                double totalLum = diffLum + specLum + refrLum + emisLum;

                if (totalLum <= 0)
                {
                    if (debugRay != null)
                    {
                        debugRay.Type = BounceType.PureBlack;
                    }
                    break;
                }

                if (i >= Scene.Recursion)
                {
                    if (debugRay != null)
                    {
                        debugRay.Type = BounceType.RecursionComplete;
                    }
                    break;
                }

                DoubleColor newTint = DoubleColor.Black;
                double      rayRand = Rand.NextDouble() * totalLum;

                // Choose whether to do a specular bounce based on brightness of specular
                if (refrLum != 0 && (rayRand -= refrLum) <= 0)
                {
                    // Transmission implementation
                    if (debugRay != null)
                    {
                        debugRay.Type = BounceType.Transmitted;
                    }

                    Vec4D outDir = (roughNormal * -cosOut) + ((ray.Direction + (roughNormal * cos)) * iorRatio);
                    outRay  = new Ray(hit.Position, outDir);
                    newTint = hit.Primitive.Refraction;

                    // Only tint on entering the object, and don't count the recursivity
                    if (hit.Inside)
                    {
                        newTint = new DoubleColor(1);
                    }
                }
                else if (specLum != 0 && (rayRand -= specLum) <= 0)
                {
                    if (debugRay != null)
                    {
                        debugRay.Type = BounceType.SpecularFail;
                    }

                    // Specular reflection
                    Vec4D outDir = Reflection(roughNormal, ray.Direction, cos);

                    // Determine whether the rough normal reflection is heading outward before continuing
                    if (outDir.Dot(hit.Normal) > 0)
                    {
                        if (debugRay != null)
                        {
                            debugRay.Type = BounceType.Specular;
                        }

                        outRay  = new Ray(hit.Position, outDir);
                        newTint = hit.Primitive.Specular;
                    }
                }
                else if (diffLum != 0 && (rayRand -= diffLum) <= 0)
                {
                    // Diffuse reflection
                    if (debugRay != null)
                    {
                        debugRay.Type = BounceType.Diffuse;
                    }

                    double z     = (2 * Math.Acos(Rand.NextDouble())) / Math.PI;
                    double theta = Rand.NextDouble() * Math.PI * 2;
                    outRay  = new Ray(hit.Position, Vec4D.CreateHorizon(hit.Normal, z, theta));
                    newTint = hit.Primitive.Diffuse;
                }
                else
                {
                    // Emission
                    if (debugRay != null)
                    {
                        debugRay.Type = BounceType.Emission;
                    }

                    Util.Assert(hit.Primitive.Emission != DoubleColor.Black, "Emission being returned with no luminance");

                    // Break out of the loop to return emission early
                    break;
                }

                if (outRay == Ray.Zero)
                {
                    break;
                }

                prevHit = hit;
                ray     = outRay;
                // Since we limit the total brightness of the reflection through our bounce selection above,
                // normalize to the total luminosity of our combined luminosities
                newTint = newTint * Math.Max(totalLum, 1);

                tint = tint * newTint;

                //color = color.add(hit.Primitive.Emission.mult(tint));
            }

            return(tint * hit.Primitive.Emission);
        }