// 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 }); }
public void Dispose() { if (lightFieldCache != null) { lightFieldCache.Dispose(); lightFieldCache = null; } }
/// <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); }