/// <summary> /// Creates a cone oriented such that it connects two points like an arrow tip /// </summary> /// <param name="baseCenter">The point in the center of the cone's base</param> /// <param name="tip">The position of the tip in world space</param> /// <param name="radius">Radius at the base of the cone</param> /// <param name="numSegments">Number of triangles used to build the side surface</param> public static Mesh MakeCone(Vector3 baseCenter, Vector3 tip, float radius, int numSegments) { ShadingSpace.ComputeBasisVectors(Vector3.Normalize(tip - baseCenter), out var tan, out var binorm); List <Vector3> vertices = new(); List <int> indices = new(); vertices.Add(tip); for (int i = 0; i < numSegments; ++i) { float angle = i * 2 * MathF.PI / numSegments; float y = MathF.Sin(angle) * radius; float x = MathF.Cos(angle) * radius; vertices.Add(baseCenter + tan * x + binorm * y); indices.AddRange(new List <int>() { 0, i, (i == numSegments - 1) ? 1 : (i + 1) }); } Mesh result = new(vertices.ToArray(), indices.ToArray()); return(result); }
public override EmitterSample SampleRay(Vector2 primaryPos, Vector2 primaryDir) { var posSample = SampleArea(primaryPos); // Transform primary to cosine hemisphere (z is up) // We add one to the exponent, to importance sample the cosine term from the jacobian also var local = SampleWarp.ToCosineLobe(exponent + 1, primaryDir); // Transform to world space direction var normal = posSample.Point.ShadingNormal; Vector3 tangent, binormal; ShadingSpace.ComputeBasisVectors(normal, out tangent, out binormal); Vector3 dir = local.Direction.Z * normal + local.Direction.X * tangent + local.Direction.Y * binormal; float cosine = local.Direction.Z; var weight = radiance * MathF.Pow(cosine, exponent + 1) * normalizationFactor; return(new EmitterSample { Point = posSample.Point, Direction = dir, Pdf = local.Pdf * posSample.Pdf, Weight = weight / posSample.Pdf / local.Pdf }); }
public override (Ray, RgbColor, float) SampleRay(Vector2 primaryPos, Vector2 primaryDir) { // Sample a direction from the scene to the background var dirSample = SampleDirection(primaryDir); // Sample a point on the unit disc var unitDiscPoint = SampleWarp.ToConcentricDisc(primaryPos); // And transform it to the scene spanning disc orthogonal to the selected direction Vector3 tangent, binormal; ShadingSpace.ComputeBasisVectors(dirSample.Direction, out tangent, out binormal); var pos = SceneCenter + SceneRadius * (dirSample.Direction // offset outside of the scene + tangent * unitDiscPoint.X // remap unit disc x coordinate + binormal * unitDiscPoint.Y); // remap unit disc y coordinate // Compute the pdf: uniform sampling of a disc with radius "SceneRadius" float discJacobian = SampleWarp.ToConcentricDiscJacobian(); float posPdf = discJacobian / (SceneRadius * SceneRadius); // Compute the final result var ray = new Ray { Origin = pos, Direction = -dirSample.Direction, MinDistance = 0 }; var weight = dirSample.Weight / posPdf; var pdf = posPdf * dirSample.Pdf; return(ray, weight, pdf); }
public override (Vector2, Vector2) SampleRayInverse(Vector3 dir, Vector3 pos) { var primaryDir = SampleDirectionInverse(-dir); // Project the point onto the plane with normal "dir" Vector3 tangent, binormal; ShadingSpace.ComputeBasisVectors(-dir, out tangent, out binormal); var offset = pos - SceneCenter; float x = Vector3.Dot(offset, tangent) / SceneRadius; float y = Vector3.Dot(offset, binormal) / SceneRadius; var primaryPos = SampleWarp.FromConcentricDisc(new(x, y)); return(primaryPos, primaryDir); }
public override (Vector2, Vector2) SampleRayInverse(SurfacePoint point, Vector3 direction) { var posPrimary = SampleAreaInverse(point); // Transform from world space to sampling space var normal = point.ShadingNormal; Vector3 tangent, binormal; ShadingSpace.ComputeBasisVectors(normal, out tangent, out binormal); float z = Vector3.Dot(normal, direction); float x = Vector3.Dot(tangent, direction); float y = Vector3.Dot(binormal, direction); var dirPrimary = SampleWarp.FromCosineLobe(exponent + 1, new(x, y, z)); return(posPrimary, dirPrimary); }
public static void BenchComputeBasisVectors(int numTrials) { Random rng = new(1337); Vector3 NextVector() => new ( (float)rng.NextDouble(), (float)rng.NextDouble(), (float)rng.NextDouble()); Vector3 avg = Vector3.Zero; Stopwatch stop = Stopwatch.StartNew(); for (int i = 0; i < numTrials; ++i) { Vector3 tan, binorm; ShadingSpace.ComputeBasisVectors(NextVector(), out tan, out binorm); avg += (tan + binorm) / numTrials * 0.5f; } Console.WriteLine($"Computing {numTrials} basis vectors took {stop.ElapsedMilliseconds}ms - {avg.Length()}"); }
/// <summary> /// Creates a cylinder that connects two given points like a pipe /// </summary> /// <param name="from">The first point, the center of one cylinder disc</param> /// <param name="to">The second point, the center of the other cylinder disc</param> /// <param name="radius">Radius of the cylinder</param> /// <param name="numSegments">Number of quads used to build the outer surface</param> public static Mesh MakeCylinder(Vector3 from, Vector3 to, float radius, int numSegments) { ShadingSpace.ComputeBasisVectors(Vector3.Normalize(to - from), out var tan, out var binorm); List <Vector3> vertices = new(); List <int> indices = new(); for (int i = 0; i < numSegments; ++i) { float angle = i * 2 * MathF.PI / numSegments; float y = MathF.Sin(angle) * radius; float x = MathF.Cos(angle) * radius; vertices.Add(from + tan * x + binorm * y); vertices.Add(to + tan * x + binorm * y); // Indices of the last edge are the first one (close the circle) int nextA = 2 * i + 2; int nextB = 2 * i + 3; if (i == numSegments - 1) { nextA = 0; nextB = 1; } indices.AddRange(new List <int>() { 2 * i, 2 * i + 1, nextA, nextA, 2 * i + 1, nextB }); } Mesh result = new(vertices.ToArray(), indices.ToArray()); return(result); }
public override EmitterSample SampleRay(Vector2 primaryPos, Vector2 primaryDir) { var posSample = SampleArea(primaryPos); // Transform primary to cosine hemisphere (z is up) var local = SampleWarp.ToCosHemisphere(primaryDir); // Transform to world space direction var normal = posSample.Point.ShadingNormal; Vector3 tangent, binormal; ShadingSpace.ComputeBasisVectors(normal, out tangent, out binormal); Vector3 dir = local.Direction.Z * normal + local.Direction.X * tangent + local.Direction.Y * binormal; return(new EmitterSample { Point = posSample.Point, Direction = dir, Pdf = local.Pdf * posSample.Pdf, Weight = Radiance / posSample.Pdf * MathF.PI // cosine cancels out with the directional pdf }); }