// Returns index of triangle intersected by central axis of lightfield cell's beam
        // Returns -1 if no triangle is intersected
        private int BeamTriangleSimpleIntersect(Coord4D lfCoord, RenderContext context)
        {
            // switch to tracing the canonical ray associated with this lightfield entry, instead of the original ray (to avoid biasing values in lightfield)
            Vector cellRayStart;
            Vector cellRayDir;

            lightFieldCache.Coord4DToRay(lfCoord, out cellRayStart, out cellRayDir);

            // TODO: once the light field cache 'fills up', do we cease tracing rays? Prove/measure this!

            // trace a primary ray against all triangles in scene
            // TODO: trace multiple rays to find the triangle with largest cross-section in the beam corresponding to this lightfield cell?
            // This may also avoid incorrectly storing 'missing triangle' value when primary ray misses because there are no triangles along central axis of cell's beam.
            IntersectionInfo intersection = geometry_subdivided.IntersectRay(cellRayStart, cellRayDir, context);

            if (intersection == null)
            {
                return(-1);
            }
            else
            {
                Contract.Assert(intersection.triIndex >= 0);
                return(intersection.triIndex);
            }
        }
        // Returns index of triangle most frequently intersected by random rays along lightfield cell's beam
        // Returns -1 if no triangle is intersected
        private int BeamTriangleComplexIntersect(Coord4D lfCoord, RenderContext context)
        {
            var triCount = new Dictionary <int, short>();

            for (int i = 0; i < 100; i++)
            {
                const double bias      = +0.5;
                Float4D      randCoord = new Float4D(
                    lfCoord.Item1 + random.NextDouble() + bias,
                    lfCoord.Item2 + random.NextDouble() + bias,
                    lfCoord.Item3 + random.NextDouble() + bias,
                    lfCoord.Item4 + random.NextDouble() + bias);

                // switch to tracing the canonical ray associated with this lightfield entry, instead of the original ray (to avoid biasing values in lightfield)
                Vector cellRayStart;
                Vector cellRayDir;
                lightFieldCache.Float4DToRay(randCoord, out cellRayStart, out cellRayDir);

                // TODO: once the light field cache 'fills up', do we cease tracing rays? Prove/measure this!

                // trace a primary ray against all triangles in scene
                // TODO: trace multiple rays to find the triangle with largest cross-section in the beam corresponding to this lightfield cell?
                // This may also avoid incorrectly storing 'missing triangle' value when primary ray misses because there are no triangles along central axis of cell's beam.
                IntersectionInfo intersection = geometry_subdivided.IntersectRay(cellRayStart, cellRayDir, context);
                if (null != intersection)
                {
                    var triIndex = intersection.triIndex;

                    Contract.Assert(triIndex >= 0);
                    if (triCount.ContainsKey(triIndex))
                    {
                        triCount[triIndex]++;
                    }
                    else
                    {
                        triCount[triIndex] = 1;
                    }
                }
            }

            if (triCount.Count == 0)
            {
                return(-1);
            }
            else
            {
                // find tri index that occurs most often
                var maxCount = triCount.Max(x => x.Value);
                return(triCount.First(x => x.Value == maxCount).Key);
            }
        }
Exemple #3
0
        /// <summary>
        /// Convert a 4D coordinate within the light field, to a ray travelling from first spherical point to second spherical point.
        /// </summary>
        /// <param name="coord4D">4D coordinate within the light field</param>
        /// <param name="start">The first point on the sphere</param>
        /// <param name="dir">The vector from the first to second points on the sphere</param>
        public void Coord4DToRay(Coord4D coord4D, out Vector start, out Vector dir)
        {
            Contract.Requires(coord4D != null);
            Contract.Requires(coord4D.Item1 < cacheRes * 2);
            Contract.Requires(coord4D.Item2 < cacheRes);
            Contract.Requires(coord4D.Item3 < cacheRes * 2);
            Contract.Requires(coord4D.Item4 < cacheRes);
            Contract.Ensures(!Contract.ValueAtReturn(out dir).IsZeroVector);

            // generate the canonical ray associated with this lightfield entry
            double maxValueUandS = cacheRes * 2 - 1;
            double maxValueVandT = cacheRes - 1;
            var    u             = (((double)coord4D.Item1 + 0.5) / maxValueUandS - 0.5) * Math.PI * 2; // [-π, π]
            var    v             = (((double)coord4D.Item2 + 0.5) / maxValueVandT - 0.5) * Math.PI;     // [-π/2, π/2]
            var    s             = (((double)coord4D.Item3 + 0.5) / maxValueUandS - 0.5) * Math.PI * 2; // [-π, π]
            var    t             = (((double)coord4D.Item4 + 0.5) / maxValueVandT - 0.5) * Math.PI;     // [-π/2, π/2]

            Sphere.LatLong spherePt1 = new Sphere.LatLong(u, v);
            Sphere.LatLong spherePt2 = new Sphere.LatLong(s, t);
            boundingSphere.ConvertLine(spherePt1, spherePt2, out start, out dir);
            dir = dir - start;
        }
Exemple #4
0
        private uint CalcColorForCoord(Coord4D lfCoord, RenderContext context)
        {
            // Index into light field cache using 4D spherical coordinates
            uint lfCacheEntry = lightFieldCache.ReadCache(lfCoord.Item1, lfCoord.Item2, lfCoord.Item3, lfCoord.Item4);

            if (lfCacheEntry != lightFieldCache.EmptyCacheEntry)
            {
                // lightfield stores colors, and we found a color entry to return
                return(lfCacheEntry);
            }

            // switch to tracing the ray associated with this lightfield entry, instead of the original ray (to avoid biasing values in lightfield)
            Vector rayStart;
            Vector rayDir;

            lightFieldCache.Coord4DToRay(lfCoord, out rayStart, out rayDir);

            // TODO: once the light field cache 'fills up', do we cease tracing rays? Prove/measure this!

            // we did not find a color in the light field corresponding to this ray, so trace this ray against the geometry
            // TODO: this line is the major performance bottleneck!
            IntersectionInfo info = geometry.IntersectRay(rayStart, rayDir, context);

            if (info == null)
            {
                // ray did not hit the geometry, so cache background color into the 4D light field
                lightFieldCache.WriteCache(lfCoord.Item1, lfCoord.Item2, lfCoord.Item3, lfCoord.Item4, backgroundColor);
                return(backgroundColor);
            }

            // TODO: this is where lots of other AO, shadow code etc used to execute

            // cache ray colors in a 4D light field?
            lightFieldCache.WriteCache(lfCoord.Item1, lfCoord.Item2, lfCoord.Item3, lfCoord.Item4, info.color);

            return(info.color);
        }
        /// <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);
        }
Exemple #6
0
        // TODO: is this method multi-thread safe?
        // cache ray colors in a 4D light field
        private uint CalcColorForRay(Vector rayStart, Vector rayDir, RenderContext context)
        {
            // TODO: do we need locking to ensure another thread does not overwrite lightfield cache entry(s)?

            if (!Interpolate)
            {
                // no interpolation
                Coord4D lfCoord = null;

                // 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(backgroundColor);
                }

                return(CalcColorForCoord(lfCoord, context));
            }
            else
            {
                // quad-linear interpolation

                // Convert ray to 4D spherical coordinates
                Float4D lfFloat4D = lightFieldCache.RayToFloat4D(ref rayStart, ref rayDir);
                if (lfFloat4D == null)
                {
                    return(backgroundColor);
                }

                // this linearly interpolates lightfield colours along all four axes
                Coord4D coord      = new Coord4D((byte)lfFloat4D.Item1, (byte)lfFloat4D.Item2, (byte)lfFloat4D.Item3, (byte)lfFloat4D.Item4);
                double  uFrac      = lfFloat4D.Item1 - coord.Item1;
                double  vFrac      = lfFloat4D.Item2 - coord.Item2;
                double  sFrac      = lfFloat4D.Item3 - coord.Item3;
                double  tFrac      = lfFloat4D.Item4 - coord.Item4;
                Color   finalColor = new Color();
                for (byte u = 0; u <= 1; u++)
                {
                    var uFactor = (u == 1 ? uFrac : 1 - uFrac);
                    for (byte v = 0; v <= 1; v++)
                    {
                        var vFactor = (v == 1 ? vFrac : 1 - vFrac);
                        for (byte s = 0; s <= 1; s++)
                        {
                            var sFactor = (s == 1 ? sFrac : 1 - sFrac);
                            for (byte t = 0; t <= 1; t++)
                            {
                                Coord4D newCoord = new Coord4D(
                                    (byte)((coord.Item1 + u) % uRes),
                                    (byte)((coord.Item2 + v) % vRes),
                                    (byte)((coord.Item3 + s) % sRes),
                                    (byte)((coord.Item4 + t) % tRes));
                                var color = new Color(CalcColorForCoord(newCoord, context));
                                finalColor += color * uFactor * vFactor * sFactor * (t == 1 ? tFrac : 1 - tFrac);
                            }
                        }
                    }
                }
                return(finalColor.ToARGB());
            }
        }