// 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); } }
/// <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 Float4DToRay(Float4D 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 = ((coord4D.Item1 /*+ 0.5*/) / maxValueUandS - 0.5) * Math.PI * 2; // [-π, π] var v = ((coord4D.Item2 /*+ 0.5*/) / maxValueVandT - 0.5) * Math.PI; // [-π/2, π/2] var s = ((coord4D.Item3 /*+ 0.5*/) / maxValueUandS - 0.5) * Math.PI * 2; // [-π, π] var t = ((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; }
// 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()); } }