Ejemplo n.º 1
0
        // TODO: store depth-fraction-within-sphere into lightfield alpha channel, to be able to return an intersection point/rayfrac? This would allow shadow rays to use lightfield!

        // TODO: is this entire method thread safe?
        /// <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>
        /// <remarks>Construction of underlying <typeparamref name="LightField4D"/> is thread-safe</remarks>
        public IntersectionInfo IntersectRay(Vector start, Vector dir, RenderContext context)
        {
            Contract.Ensures(!Enabled || Contract.Result <IntersectionInfo>() != null);

            // if lightfield is disabled, pass-through the ray intersection
            if (!Enabled)
            {
                return(geometry.IntersectRay(start, dir, context));
            }

            // double-check locking pattern
            if (lightFieldCache == null)
            {
                lock (cacheLock)
                {
                    // another thread may have got the lock first, and already constructed the cache
                    if (lightFieldCache == null)
                    {
                        this.lightFieldCache = new LightField4D <uint>(resolution, instanceKey, default(uint), default(uint) + 1, cachePath);
                        this.uRes            = lightFieldCache.cacheRes * 2;
                        this.vRes            = lightFieldCache.cacheRes;
                        this.sRes            = uRes;
                        this.tRes            = vRes;
                    }
                }
            }

            // TODO: if ray start is within lightfield sphere, throw an exception? Might occur for shadow/AO rays.

            uint finalColor = CalcColorForRay(start, dir, context);

            return(new IntersectionInfo {
                color = finalColor, normal = Vector.Forward
            });
        }
Ejemplo n.º 2
0
 public void Dispose()
 {
     if (lightFieldCache != null)
     {
         lightFieldCache.Dispose();
         lightFieldCache = null;
     }
 }
Ejemplo n.º 3
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)
        {
            // if lightfield is disabled, pass-through the ray intersection
            if (!Enabled)
            {
                return(geometry.IntersectRay(start, dir, context));
            }

            // lazily create the lightfield cache
            if (lightFieldCache == null)
            {
                lightFieldCache = new LightField4D <ushort>(resolution, instanceKey, default(ushort), default(ushort) + 1, cachePath);
            }

            // cache triangle indices in a 4D light field
            Coord4D lfCoord  = null;
            Vector  rayStart = start;
            Vector  rayDir   = dir;

            // Convert ray to 4D spherical coordinates
            // TODO: do we need locking to ensure another thread does not overwrite this lightfield cache entry?
            lfCoord = lightFieldCache.RayToCoord4D(ref rayStart, ref rayDir);
            if (lfCoord == null)
            {
                return(null);
            }

            // TODO: debugging. Very slow! Also broken as of Aug 2016.
            //Vector tmpStart;
            //Vector tmpDir;
            //lightFieldCache.Coord4DToRay(lfCoord, out tmpStart, out tmpDir);
            //var tmpCoord = lightFieldCache.RayToCoord4D(ref tmpStart, ref tmpDir);
            //Contract.Assert(tmpCoord.Equals(lfCoord));

            // Index into light field cache using 4D spherical coordinates
            ushort lfCacheEntry = lightFieldCache.ReadCache(lfCoord.Item1, lfCoord.Item2, lfCoord.Item3, lfCoord.Item4);

            if (lfCacheEntry == lightFieldCache.EmptyCacheEntryReplacement)
            {
                // cache entry indicates 'missing triangle', so nothing to intersect against
                return(null);
            }

            // if lightfield cache entry is empty, populate it with a triangle from the cell's beam
            int triIndex;

            if (lightFieldCache.EmptyCacheEntry == lfCacheEntry)
            {
                // lightfield does not yet contain a triangle entry to intersect against
                // TODO: once the light field cache 'fills up', do we cease tracing rays? Prove/measure this!

                // Intersect random rays along beam of lightfield cell, against all triangles
                // TODO: this *should* improve quality, but seems to decrease it, and adds a little randomness to the regression tests
                //triIndex = BeamTriangleComplexIntersect(lfCoord, context);

                // Intersect central axis of lightfield cell against all triangles
                triIndex = BeamTriangleSimpleIntersect(lfCoord, context);

                if (triIndex == -1)
                {
                    // Cache 'missing triangle' value into the light field cache
                    lightFieldCache.WriteCache(lfCoord.Item1, lfCoord.Item2, lfCoord.Item3, lfCoord.Item4, lightFieldCache.EmptyCacheEntryReplacement);
                    return(null);
                }
                else
                {
                    // Take triangle that we intersected, and store its index into the light field cache
                    // TODO: triIndex could overflow a ushort and wrap around to an incorrect triangle index!
                    lfCacheEntry = (ushort)(triIndex + 2); // account for empty cache value and replacement cache value (i.e. avoid storing 0 or 1 into lightfield)
                    lightFieldCache.WriteCache(lfCoord.Item1, lfCoord.Item2, lfCoord.Item3, lfCoord.Item4, lfCacheEntry);
                }
            }

            Contract.Assert(lightFieldCache.EmptyCacheEntry != lfCacheEntry);

            // lightfield contains a triangle entry to intersect against

            // store triangle indices in light field, and raytrace the triangle that we get from the lightfield

            // TODO: Store index of bucket of triangles into lightfield? Tradeoff between small buckets and many buckets. Optimise for 8-bit or 16-bit bucket indices?

            // Intersect primary ray against closest/likely/best triangle (from lightfield cache)
            triIndex = (int)lfCacheEntry - 2; // discount empty and replacement cache values
            // TODO: Contracts analyser says assert unproven
            Contract.Assert(triIndex >= 0);
            var tri = geometry_simple[triIndex] as Raytrace.Triangle;
            IntersectionInfo intersection = tri.IntersectRay(rayStart, rayDir, context); // intersect ray against triangle

            // intersect ray against plane of triangle, to avoid holes by covering full extent of lightfield cell. Creates spatial tearing around triangle silhouettes.
            //info = tri.Plane.IntersectRay(rayStart, rayDir);
            //info.color = tri.Color; // all planes are white, so use color of triangle

            if (intersection == null)
            {
                // Intersect ray against triangles 'near' to the triangle from the lightfield (i.e. in the same subdivision node).
                // This is slower than intersecting the single triangle from the lightfield, but much faster than intersecting the entire subdivision structure.
                // TODO: need to walk the subdivision tree to find triangles in nearby tree nodes
                intersection = geometry_subdivided.IntersectRayWithLeafNode(rayStart, rayDir, tri, context);

#if VISUALISATION
                if (intersection != null)
                {
                    return new IntersectionInfo {
                               color = 0x000000FF, normal = new Vector(0, 1, 0)
                    }
                }
                ;                                                                                     // visualise hitting nearby triangle (blue)
#endif
            }

            if (intersection == null)
            {
                // Intersect ray against all triangles in the geometry.
                // TODO: this is much slower than intersecting the single triangle from the lightfield, or the triangles in the same node.
                // Performance will be reasonable if not many pixels reach this code path.
                intersection = geometry_subdivided.IntersectRay(rayStart, rayDir, context);

#if VISUALISATION
                if (intersection != null)
                {
                    return new IntersectionInfo {
                               color = 0x00FF0000, normal = new Vector(0, 1, 0)
                    }
                }
                ;                                                                                     // visualise hitting triangle in distant node (red)
#endif
            }

            //if (info == null)
            //{
            //    // intersect ray against plane of triangle, to avoid holes by covering full extent of lightfield cell
            //    // TODO: this creates spatial tearing around triangle silhouettes, as all background pixels in this cell are covered by this plane.
            //    info = tri.Plane.IntersectRay(rayStart, rayDir);
            //    if (info != null)
            //        info.color = tri.Color; // all planes are white, so use color of triangle
            //}

#if VISUALISATION
            if (intersection == null)
            {
                return new IntersectionInfo {
                           color = 0x0000FF00, normal = new Vector(0, 1, 0)
                }
            }
            ;                                                                                     // visualise missing nearby triangles (green)
#endif

            return(intersection);
        }