Beispiel #1
0
        /// <summary>
        /// Calculates ambient occlusion at a point on a surface.
        /// </summary>
        /// <param name="surface">Information about surface point</param>
        /// <param name="geometry">The geometry to be raytraced (both occlusion caster and occlusion receiver)</param>
        /// <param name="random">Random number generator to use</param>
        /// <returns>Fraction of non-occlusion at surface point (between 0 and 1): 0 = surface point fully occluded by neighbour surfaces; 1 = surface point not occluded at all</returns>
        private double CalcAmbientOcclusion(IntersectionInfo surface, Raytrace.IRayIntersectable geometry, RenderContext context)
        {
            Contract.Ensures(0 <= Contract.Result <double>() && Contract.Result <double>() <= 1);

            var random   = context.RNG;
            var rayStart = surface.pos + surface.normal * ambientOcclusionProbeOffset;

            Vector avgEscapedRayDir = new Vector();
            int    rayEscapeCount   = 0;

            for (int i = 0; i < ambientOcclusionQuality; i++)
            {
                // Pick a random direction within the hemisphere around the surface normal
                // TODO: works for external surfaces, but some self-intersection on interior surfaces
                var rayDir = new Vector(random.NextDouble() * 2 - 1, random.NextDouble() * 2 - 1, random.NextDouble() * 2 - 1);
                //rayDir.Normalise();
                if (rayDir.DotProduct(surface.normal) < 0)
                {
                    rayDir = -rayDir;
                }

                // Pick random directions until we find one roughly in the same direction as the surface normal
                //Vector rayDir;
                //double cosOfAngle;
                //do
                //{
                //    rayDir = new Vector(random.NextDouble() * 2 - 1, random.NextDouble() * 2 - 1, random.NextDouble() * 2 - 1);
                //    rayDir.Normalise();
                //    cosOfAngle = rayDir.DotProduct(surfaceInfo.normal);

                //    // force ray to be in the hemisphere around the surface normal
                //} while (cosOfAngle < 0.0);

                // Fire off ray to check for nearby surface in chosen direction
                // TODO: might be more efficient if ray tracing stopped after a short distance from ray origin
                Raytrace.IntersectionInfo shadowInfo = geometry.IntersectRay(rayStart, rayDir, context);
                if (shadowInfo == null || shadowInfo.rayFrac > ambientOcclusionProbeDist)
                {
                    // This ray did not hit a nearby surface
                    rayEscapeCount++;
                    avgEscapedRayDir += rayDir;
                }
            }


            // visualise direction of unobstructed space around surface point
            //avgEscapedRayDir.Normalise();
            //var v = new Vector(avgEscapedRayDir.x + 1, avgEscapedRayDir.y + 1, avgEscapedRayDir.z + 1);
            //v *= 128;
            //return Surface.PackRgb((byte)v.x, (byte)v.y, (byte)v.z);

            //avgEscapedRayDir.Normalise();
            //avgEscapedRayDir *= 255;
            //return Surface.PackRgb((byte)avgEscapedRayDir.x, (byte)avgEscapedRayDir.y, (byte)avgEscapedRayDir.z);


            return((double)rayEscapeCount / (double)ambientOcclusionQuality);
        }
Beispiel #2
0
        /// <summary>
        /// Intersect a ray against this object.
        /// </summary>
        /// <param name="start">The start position of the ray, in object space.</param>
        /// <param name="dir">The direction of the ray, in object space (not a unit vector).</param>
        /// <returns>Information about the nearest intersection, or null if no intersection.</returns>
        public IntersectionInfo IntersectRay(Vector start, Vector dir, RenderContext context)
        {
            Contract.Ensures(Contract.Result <IntersectionInfo>() == null || (Contract.Result <IntersectionInfo>().color & 0xff000000) == 0xff000000);

            // trace ray through underlying geometry
            IntersectionInfo info = geometry.IntersectRay(start, dir, context);

            // if we are disabled, pass-through the ray intersection
            if (!Enabled)
            {
                return(info);
            }

            // did ray not hit any geometry?
            if (info == null)
            {
                return(null);
            }

            // shade the surface point
            Vector surfaceNormal = info.normal;
            Vector newRayStart   = info.pos + surfaceNormal * raySurfaceOffset;
            Vector newRayDir     = RandomRayInHemisphere(surfaceNormal, context.RNG);

            newRayDir.Normalise(); // only needed for calling BRDF function

            // Fire off ray to check for another surface in the chosen direction
            Raytrace.IntersectionInfo newRayInfo = geometry.IntersectRay(newRayStart, newRayDir, context);
            // Did this ray did hit another surface?
            Color incomingLight = Color.Black; // TODO: use background color from Renderer

            if (newRayInfo != null)
            {
                incomingLight = new Color(newRayInfo.color);
            }

            // TODO: color conversions are probably very slow
            Color  surfaceEmission    = new Color(info.color); // TODO: info.color might be shaded surface color! We want the raw material color here!
            double reflectedLightFrac = BRDF(newRayDir, surfaceNormal /*, dir */);
            Color  outgoingLight      = incomingLight * reflectedLightFrac + surfaceEmission;

            // only normalise colour if R, G or B are greater than 1.0
            if (outgoingLight.r > 1.0 || outgoingLight.g > 1.0 || outgoingLight.b > 1.0)
            {
                outgoingLight.Normalise();
            }

            Contract.Assert(0.0 <= outgoingLight.r && outgoingLight.r <= 1.0);
            Contract.Assert(0.0 <= outgoingLight.g && outgoingLight.g <= 1.0);
            Contract.Assert(0.0 <= outgoingLight.b && outgoingLight.b <= 1.0);
            info.color = outgoingLight.ToARGB();

            return(info);
        }