Пример #1
0
        /// <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);
        }
Пример #2
0
        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
            });
        }
Пример #3
0
        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);
        }
Пример #4
0
        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);
        }
Пример #5
0
        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);
        }
Пример #6
0
        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()}");
        }
Пример #7
0
        /// <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);
        }
Пример #8
0
        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
            });
        }