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); }