public OptixIntersectionDevice(IRayEngineScene scene, bool lowLatency = false) : base(scene)
        {
            this.lowLatency = lowLatency;
            Context = new Context { CpuNumThreads = 4, RayTypeCount = 1, EntryPointCount = 1 };
            
            var rayBufferDesc = new BufferDesc()
            {
                Format = Format.User,
                Width = (uint)(lowLatency ? RayBuffer.RayBufferSize / 8 : RayBuffer.RayBufferSize),
                Type = BufferType.InputOutput,
                ElemSize = (uint)Marshal.SizeOf(typeof(RayData))
            };
            var rayHitBufferDesc = new BufferDesc()
            {
                Format = Format.User,
                Width =  (uint)(lowLatency ? RayBuffer.RayBufferSize / 8 : RayBuffer.RayBufferSize),
                Type = BufferType.InputOutput,
                ElemSize = (uint)Marshal.SizeOf(typeof(RayHit))
            };

            this.todoRayBuffers = new InputRayBufferCollection();
            this.doneRayBuffers = new OutputRayBufferCollection();
            this.started = false;

            Rays = new DeviceBuffer(Context, rayBufferDesc);
            RayHits = new DeviceBuffer(Context, rayHitBufferDesc);
            Context["rayHits"].Set(RayHits);
            Context["rays"].Set(Rays);
            Builder = AccelBuilder.TriangleKdTree;
            Traverser = AccelTraverser.KdTree;
            GeoGroup = new GeometryGroup(Context);
            this.SetScene(scene);
        }
Beispiel #2
0
        public void EvaluatePhoton(IRayEngineScene scene, float u0, float u1, float u2, float u3, float u4, out LightSample result)
        {
            result = new LightSample();

            Vector dir = MC.UniformSampleSphere(u0, u1);

            result.LightRay = new RayData(ref Position, ref dir, 1e-4f, 1e4f);

            result.Pdf = MathLab.INV4PI;

            result.Spectrum = (Power * MathLab.M_PI * 4f);
        }
Beispiel #3
0
 public TriangleLight(IRayEngineScene sc, string meshName, RgbSpectrum gain = new RgbSpectrum())
 {
     this.scene = (RayEngineScene)sc;
     this.meshName = meshName;
     this.Gain = (RgbSpectrumInfo)gain;
     //cie.stdillum.F1.spd
     //@"F:\3D\spds\cie.stdillum.F1.spd"
     //cie.stdillum.A.spd
     //macbeth-1.spd
     //FromFile(@"F:\3D\spds\cie.stdillum.F9.spd")
     spectra = SPD_Data.FromSampled(SPD_Data.FromFile(@"F:\3D\spds\cie.stdillum.D5000.spd"), SpectrumType.Illuminant);
 }
        public OptixTraverseIntersectionDevice(IRayEngineScene scene, bool lowLatency = false)
            : base(scene)
        {
            this.lowLatency = lowLatency;
            Context = new Context { CpuNumThreads = 4, RayTypeCount = 1, EntryPointCount = 1 };


            this.todoRayBuffers = new InputRayBufferCollection();
            this.doneRayBuffers = new OutputRayBufferCollection();
            this.started = false;

            this.SetScene(scene);
        }
        public OptixPrimeIntersectionDevice(IRayEngineScene scene, bool lowLatency = false)
            : base(scene)
        {
            this.lowLatency = lowLatency;
            Context = new Context { CpuNumThreads = 4, RayTypeCount = 1, EntryPointCount = 1 };

            trav  = new OptixPrime(Context, QueryType.ClosestHit, RayFormat.OriginDirectionMinMaxInterleaved, TriFormat.Mesh, TraversalOutput.Normal, InitOptions.GpuOnly);

            this.todoRayBuffers = new InputRayBufferCollection();
            this.doneRayBuffers = new OutputRayBufferCollection();
            this.started = false;

            this.SetScene(scene);
        }
        public override void SetScene(IRayEngineScene secn)
        {

            Tracer.TraceLine("OptixIntersectionDevice  -  Initializing Optix");
            this.scene = (RayEngineScene)secn;
            this.vertices = scene.Vertices.ToFloatList().ToArray();

             indices =
                    scene.Triangles.ToList()
                         .SelectMany(tri => new []
                             {
                                 tri.v0.VertexIndex,
                                 tri.v1.VertexIndex,
                                 tri.v2.VertexIndex
                             })
                         .ToArray();
            if (trav == null)
                trav = new OptixPrime(Context, QueryType.ClosestHit, RayFormat.OriginDirectionMinMaxInterleaved, TriFormat.Mesh, TraversalOutput.Normal, InitOptions.GpuOnly);

            trav.SetTriangles(indices, vertices);
        }
        public override void SetScene(IRayEngineScene secn)
        {

            Tracer.TraceLine("OptixIntersectionDevice  -  Initializing Optix");
            this.scene = (RayEngineScene)secn;
            td = scene.Triangles.SelectMany(tr => new[]
            {
                scene.Vertices[tr.v0.VertexIndex].x,
                scene.Vertices[tr.v0.VertexIndex].y,
                scene.Vertices[tr.v0.VertexIndex].z,
                scene.Vertices[tr.v1.VertexIndex].x,
                scene.Vertices[tr.v1.VertexIndex].y,
                scene.Vertices[tr.v1.VertexIndex].z,
                scene.Vertices[tr.v2.VertexIndex].x,
                scene.Vertices[tr.v2.VertexIndex].y,
                scene.Vertices[tr.v2.VertexIndex].z,

            }).ToArray();
            this.triangles = scene.Triangles.Select(tr => new TriInfo
            {
                v0x = scene.Vertices[tr.v0.VertexIndex].x,
                v1x = scene.Vertices[tr.v1.VertexIndex].x,
                v2x = scene.Vertices[tr.v2.VertexIndex].x,

                v0y = scene.Vertices[tr.v0.VertexIndex].y,
                v1y = scene.Vertices[tr.v1.VertexIndex].y,
                v2y = scene.Vertices[tr.v2.VertexIndex].y,

                v0z = scene.Vertices[tr.v0.VertexIndex].z,
                v1z = scene.Vertices[tr.v1.VertexIndex].z,
                v2z = scene.Vertices[tr.v2.VertexIndex].z,

            }).ToArray();
            if (trav == null)
                trav = new Traversal(Context, QueryType.ClosestHit, RayFormat.OriginDirectionMinMaxInterleaved, TriFormat.TriangleSoup, TraversalOutput.BaryCentric, InitOptions.CullBackFace);

            trav.SetTriangles(td);
        }
Beispiel #8
0
        public void EvaluatePhoton(IRayEngineScene scen, float u0, float u1, float u2, float u3, float u4, out LightSample result)
        {
            result = new LightSample();
            var scene = (RayEngineScene)scen;
            float b0, b1, b2;
            Point orig;
            scene.Triangles[triangleIndex].Sample(scene.Vertices, u0, u1, out orig, out b0, out b1, out b2);

            // Ray direction
            var sampleN = this.TriangleNormal;
            Vector dir = MC.UniformSampleSphere(u2, u3);
            float RdotN = Normal.Dot(ref dir, ref sampleN);
            if (RdotN < 0f)
            {
                dir *= -1f;
                RdotN = -RdotN;
            }

            result.LightRay = new RayData(ref orig, ref dir);

            result.Pdf = (MathLab.INVTWOPI / area) * 1f / RdotN;

            result.Spectrum = this.lightSpectra;

        }
Beispiel #9
0
        public void SampleSpatialAdjoint(IRayEngineScene scen, float u0, float u1, float u2, float u3, float u4,
            out Vector dir, out float apdf, out float pdf)
        {
            var scene = (RayEngineScene)scen;
            float b0, b1, b2;
            Point orig;
            int index;

            var tri = scene.Triangles[triangleIndex];

            tri.Sample(scene.Vertices, u0, u1, out orig, out b0, out b1, out b2);
            // Ray direction
            var sampleN = -TriangleNormal;
            dir = MC.UniformSampleSphere(u2, u3);
            float rdotN = Normal.Dot(ref dir, ref sampleN);
            if (rdotN < 0f)
            {
                dir *= -1f;
                rdotN = -rdotN;
            }
            apdf = rdotN;

            pdf = ((MathLab.INVTWOPI / area));

        }
        public override void GenerateLiRays(IRayEngineScene scn, Sample sample, ref RayData ray, VolumeComputation comp)
        {
            comp.Reset();
            var scene = (RayEngineScene)(scn);

            var sigs = sig_s;
            comp.EmittedLight = lightEmission;
            float t0, t1;
            if (!region.Intersect(ray, out t0, out t1))
                return;

            if (sigs.IsBlack() || (scene.Lights.Length < 1))
            {
                float distance = t1 - t0;
                comp.EmittedLight = lightEmission * distance;
            }
            else
            {
                // Prepare for volume integration stepping
                float distance = t1 - t0;
                var nSamples = MathLab.Ceil2UInt(distance / stepSize);

                float step = distance / nSamples;
                RgbSpectrum Tr = new RgbSpectrum(1f);
                RgbSpectrum Lv = new RgbSpectrum();
                var p = ray.Point(t0);
                float offset = sample.GetLazyValue();
                t0 += offset * step;

                //Vector pPrev;
                for (var i = 0; i < nSamples; ++i, t0 += step)
                {
                    //pPrev = p;
                    p = ray.Point(t0);
                    Sigma_s(ref p, out sigs);
                    //sigs = NoiseProvider.Instance.Noise3D(((Vector)p).Normalize());
                    Lv += lightEmission;
                    if (!sigs.IsBlack() && (scene.Lights.Length) > 0)
                    {
                        // Select the light to sample
                        float lightStrategyPdf;
                        var light = scene.Lights[scene.SampleLights(sample.GetLazyValue(), out lightStrategyPdf)];

                        // Select a point on the light surface
                        float lightPdf;
                        Normal fakeNorml = new Normal(0f, 0f, 1f);
                        LightSample ls = new LightSample();
                        light.EvaluateShadow(ref p, ref fakeNorml, sample.GetLazyValue(),
                                                                sample.GetLazyValue(), sample.GetLazyValue(), ref ls);


                        var lightColor = (RgbSpectrumInfo)(ls.Spectrum);
                        lightPdf = ls.Pdf;
                        comp.Rays[comp.RayCount] = ls.LightRay;

                        if ((lightPdf > 0f) && !lightColor.IsBlack())
                        {
                            comp.ScatteredLight[comp.RayCount] =(RgbSpectrum)(Tr * sigs * lightColor * MathLab.Exp(-distance) *
                                                                 ((Density(ref p) * step) / (4f * MathLab.M_PI * lightPdf * lightStrategyPdf)));
                            comp.RayCount++;
                        }
                    }

                    comp.EmittedLight = Lv * step;
                }
            }
        }
Beispiel #11
0
 protected RenderThread(int index, IRayEngineScene scn)
 {
     threadIndex = index;
     scene = scn;
     started = false;
 }
Beispiel #12
0
        public void EvaluatePhoton(IRayEngineScene scen, float u0, float u1, float u2, float u3, float u4, out LightSample result)
        {
            result = new LightSample();
            var scene = (RayEngineScene)scen;
            float b0, b1, b2;
            Point orig;
            int index;
            var tri = SampleTriangle(u4, out index);

            tri.Sample(scene.Vertices, u0, u1, out orig, out b0, out b1, out b2);
            var TriangleNormal = triangleSampleData[index].Item2;
            var area = triangleSampleData[index].Item1;
            // Ray direction
            var sampleN = -TriangleNormal;
            Vector dir = MC.UniformSampleSphere(u2, u3);
            float rdotN = Normal.Dot(ref dir, ref sampleN);
            if (rdotN < 0f)
            {
                dir *= -1f;
                rdotN = -rdotN;
            }

            result.LightRay = new RayInfo(ref orig, ref dir);


            result.Pdf = ((MathLab.INVTWOPI / area) * (1f / mesh.TrianglesCount));

            result.Spectrum = Profile.Evaluate(b0, b1);
            result.Spectrum.Mul(rdotN);
        }
        public void SetScene(IRayEngineScene scn)
        {
            Tracer.TraceLine("Setting scene");
            this.scene = (RayEngineScene)scn;
            verts = scene.Vertices.ToArray();
            tris = scene.Triangles.Select(i => i.GetInfo()).ToArray();

            if (GlobalConfiguration.Instance.UseSceneCaching && scene.Cache != null)
            {
                bvh = scene.Cache.BvhData;
                nodesCount = scene.Cache.BvhData.Length;
            }
            else
            {
                var da = new BvhDataAdapter(scene);
                var treeData = da.BuildData();
                bvh = treeData;
                nodesCount = treeData.Length;

            }
        }
Beispiel #14
0
        public virtual void EvaluatePhoton(IRayEngineScene scen, float u0, float u1, float u2, float u3, float u4, out LightSample result)
        {
            result = new LightSample();
            var scene = (RayEngineScene) scen;

            var worldCenter = scene.SceneGeometry.BoundingSphereCenter;
            var worldRadius = scene.SceneGeometry.BoundingSphereRadius * 1.01f;

            var p1 = worldCenter + worldRadius * MC.UniformSampleSphere(u0, u1);
            var p2 = worldCenter + worldRadius * MC.UniformSampleSphere(u2, u3);

            // Construct ray between p1 and p2
            result.LightRay = new RayData(p1, (p2 - p1).Normalize(), 1e-4f, 1e4f);

            // Compute InfiniteAreaLight ray weight
            Vector toCenter = (worldCenter - p1).Normalize();
            var rayDir = result.LightRay.Dir;
            float costheta = MathLab.AbsDot(ref toCenter, ref rayDir);
            result.Pdf = costheta / (4f * MathLab.M_PI * MathLab.M_PI * worldRadius * worldRadius);

            result.Spectrum = Le(ref result.LightRay.Dir);
        }
Beispiel #15
0
        public void EvaluatePhoton(IRayEngineScene scene, float u0, float u1, float u2, float u3, float u4, out LightSample result)
        {
            result = new LightSample();

            var dir = MC.UniformSampleSphere(u0, u1);

            result.LightRay = new RayData(ref Position, ref dir, 1e-4f, 1e4f);

            result.Pdf = MathLab.INV4PI/Radius;
            float theta = Vector.SphericalTheta(ref result.LightRay.Dir);
            var i = (RgbSpectrumInfo)Profile.Evaluate(Vector.SphericalPhi(ref result.LightRay.Dir) * MathLab.INVTWOPI, theta * MathLab.INVPI);

            result.Spectrum = i * Radius * MathLab.M_PI * 4f;
        }
 protected abstract PathBufferBase CreatePathBuffer(int maxPath, IRayEngineScene scene, IImageFilm pixelDevice,
                                                    ISampler sampler, SurfaceSampler ss);
 protected override PathBufferBase CreatePathBuffer(int maxPath, IRayEngineScene scene, IImageFilm pixelDevice, ISampler sampler, SurfaceSampler ss)
 {
     var pathBuffer = new  PPMPathBuffer(context);
     pathBuffer.Init(maxPath, (RayEngineScene)scene, pixelDevice, new SamplingContext() { SurfaceSampler = ss, PrimarySpaceSampler = sampler, LightSampler = new LightSampler((RayEngineScene)scene)});
     return pathBuffer;
 }
Beispiel #18
0
 public virtual void SetScene(IRayEngineScene scene) {}
Beispiel #19
0
 public IntersectionEngine(IRayEngineScene scene) {
     this.scene = scene;
 }
        public override void SetScene(IRayEngineScene secn)
        {
            string shaderPath = Path.GetFullPath(@"G:\Git\RayDen\RayDen.OptixEngine\Cuda\Scripts\CudaScripts\hitkernel.ptx");

            Tracer.TraceLine("OptixIntersectionDevice  -  Initializing Optix");
            this.scene = (RayEngineScene) secn;
            var scn = scene.SceneGeometry;

            Material material = new Material(Context);
            material.Programs[0] = new SurfaceProgram(Context, RayHitType.Closest, shaderPath, "first_hit");
            //            material.Programs[1] = new SurfaceProgram(Context, RayHitType.Any, shaderPath, "shadow");


            var mVertices = scn.Vertices.ToList();
            var mNormals = scn.Normals.ToList();
            List<UV> mTexcoords = null;
            mTexcoords = scn.TexCoords == null ? new List<UV>() : scn.TexCoords.Select(it=>new UV(it.x,it.y)).ToList();

            var vDesc = new BufferDesc() { Width = (uint)mVertices.Count, Format = Format.Float3, Type = BufferType.Input };
            var nDesc = new BufferDesc() { Width = (uint)mNormals.Count, Format = Format.Float3, Type = BufferType.Input };
            var tcDesc = new BufferDesc() { Width = (uint)mTexcoords.Count, Format = Format.Float2, Type = BufferType.Input };

            // Create the buffers to hold our geometry data
            var vBuffer = new  DeviceBuffer(Context, vDesc);
            var nBuffer = new  DeviceBuffer(Context, nDesc);
            var tcBuffer = new DeviceBuffer(Context, tcDesc);


            vBuffer.SetData(mVertices.ToArray());
            nBuffer.SetData(mNormals.ToArray());
            tcBuffer.SetData(mTexcoords.ToArray());


            foreach (var triangleMesh in scene.Meshes.Cast<TriangleMeshInfo>())
            {
                var group = triangleMesh;


                var viDesc = new BufferDesc() { Width = (uint)(group.TrianglesCount), Format = Format.Int3, Type = BufferType.Input };
                var niDesc = new BufferDesc() { Width = (uint)(group.TrianglesCount), Format = Format.Int3, Type = BufferType.Input };
                var tiDesc = new BufferDesc() { Width = (uint)(group.TrianglesCount), Format = Format.Int3, Type = BufferType.Input };
                var ofsDesc = new BufferDesc()
                    {
                        Width = (uint) (group.TrianglesCount),
                        Format = Format.UInt,
                        Type = BufferType.Input
                    };

                var viBuffer = new DeviceBuffer(Context, viDesc);
                var niBuffer = new DeviceBuffer(Context, niDesc);
                var tiBuffer = new DeviceBuffer(Context, tiDesc);

                var gIndexes =
                    scene.Triangles.ToList().GetRange(group.StartTriangle, group.TrianglesCount)
                         .SelectMany(tri => new []
                             {
                                 tri.v0.VertexIndex,
                                 tri.v1.VertexIndex,
                                 tri.v2.VertexIndex
                             })
                         .ToArray();

                var gNIndexes =
                    scene.Triangles.ToList().GetRange(group.StartTriangle, group.TrianglesCount)
                         .SelectMany(tri => new[]
                             {
                                 tri.v0.NormalIndex,
                                 tri.v1.NormalIndex,
                                 tri.v2.NormalIndex
                             })
                         .ToArray();
                var gTIndexes =
                    scene.Triangles.ToList().GetRange(group.StartTriangle, group.TrianglesCount)
                         .SelectMany(tri => new []
                             {
                                 tri.v0.TexCoordIndex,
                                 tri.v1.TexCoordIndex,
                                 tri.v2.TexCoordIndex
                             })
                         .ToArray();

                viBuffer.SetData(Convert(gIndexes).ToArray());
                niBuffer.SetData(Convert(gNIndexes).ToArray());
                tiBuffer.SetData(Convert(gTIndexes).ToArray());

                var geometry = new Geometry(Context);
                geometry.IntersectionProgram = new Program(Context, IntersecitonProgPath, IntersecitonProgName);
                geometry.BoundingBoxProgram = new Program(Context, BoundingBoxProgPath, BoundingBoxProgName);
                geometry.PrimitiveCount = (uint)(group.EndTriangle - group.StartTriangle);

                geometry["vertex_buffer"].Set(vBuffer);
                geometry["normal_buffer"].Set(nBuffer);
                geometry["texcoord_buffer"].Set(tcBuffer);
                geometry["vindex_buffer"].Set(viBuffer);
                geometry["nindex_buffer"].Set(niBuffer);
                geometry["tindex_buffer"].Set(tiBuffer);
                geometry["mesh_offset"].SetInt4(group.StartTriangle);
                //geometry[ "material_buffer" ].Set(mtBuffer);

                //create a geometry instance
                var instance = new GeometryInstance(Context);
                instance.Geometry = geometry;
                instance.AddMaterial(material);
                //create an acceleration structure for the geometry
                Acceleration accel = new Acceleration(Context, Builder, Traverser);

                if (Builder == AccelBuilder.Sbvh || Builder == AccelBuilder.TriangleKdTree)
                {
                    accel.VertexBufferName = "vertex_buffer";
                    accel.IndexBufferName = "vindex_buffer";
                }

                //now attach the instance and accel to the geometry group
                GeoGroup.Acceleration = accel;
                GeoGroup.AddChildren(new[] { instance });
            }

            Program rayGen = new Program(Context, shaderPath, "intersector_camera");
            Program exception = new Program(Context, shaderPath, "exception");
            Program miss = new Program(Context, shaderPath, "miss");
            Context["intersect_ray_type"].Set(0u);
            Context["scene_epsilon"].Set(0.0001f);
            Context.SetRayGenerationProgram(0, rayGen);
            Context.SetExceptionProgram(0, exception);
            Context.SetRayMissProgram(0, miss);
            Context["top_object"].Set(GeoGroup);
            Tracer.TraceLine("OptixIntersectionDevice  -  Compiling Optix contex");
            Context.Compile();
            Context.BuildAccelTree();
            Tracer.TraceLine("OptixIntersectionDevice  -  Complete");
        }
Beispiel #21
0
        public void Initialize(IRayEngineScene scen, params object[] data)
        {

            this.scene = (RayEngineScene)scen;
            var tri = scene.Triangles[triangleIndex];
            var computeNormal = true;
            //Configuration.Instance.Get("Integrator.ComputeNormals", false);
            var normalInverse = true;
            //Configuration.Instance.Get("Integrator.InverseLightNormals", false);
            var multiplicator = normalInverse ? -1f : 1f;
            this.TriangleNormal = multiplicator * (computeNormal ? tri.ComputeNormal(scene.Vertices) : (Normal)scene.SceneGeometry.Normals[triangleIndex]);
            this.area = tri.AreaV(scene.Vertices);
        }
Beispiel #22
0
 public abstract void GenerateLiRays(IRayEngineScene scene, Sample sample, ref RayData ray, VolumeComputation comp);
 public CorrectorRenderThread(int index, IRayEngineScene scn) : base(index, scn)
 {
 }
Beispiel #24
0
        public void Initialize(IRayEngineScene scen, params object[] data)
        {
            zeroSpectra = GlobalConfiguration.Instance.SpectralRendering
                            ? (ISpectrum)ZeroCSpectrumArray
                            : this.RgbSpectrumZeroArray;
            var scn = (RayEngineScene)scen;
            if (mesh == null && !string.IsNullOrWhiteSpace(MeshName))
            {

                this.mesh =
                    scn.Meshes.FirstOrDefault(
                        item => item.MeshName.Equals(MeshName, StringComparison.CurrentCultureIgnoreCase));
                if (this.mesh != null)
                {
                    if (this.mesh.MeshProfile == null)
                    {
                        this.mesh.MeshProfile = new LightsourceProfile()
                        {
                            Light = this
                        };
                    }
                    else
                    {
                        var lightsourceProfile = this.mesh.MeshProfile as LightsourceProfile;
                        if (lightsourceProfile != null)
                            lightsourceProfile.Light = this;
                    }
                    meshMeshArea = 0;
                    for (int i = mesh.StartTriangle; i < mesh.EndTriangle; i++)
                    {
                        meshMeshArea += scn.Triangles[i].AreaV(scn.Vertices);
                    }
                }
            }


            if (mesh == null)
            {
                throw new ArgumentException("Cant find light mesh", this.LightName ?? this.MeshName);
            }

            triangleSampleData = new TriangleSample[this.mesh.TrianglesCount];
            this.scene = scn;

            for (int i = mesh.StartTriangle, j = 0; i < mesh.EndTriangle; i++, j++)
            {
                triangleSampleData[j] = new TriangleSample(scene.Triangles[i].AreaV(scene.Vertices), NormalModifier * scene.Triangles[i].ComputeNormal(scene.Vertices));
            }

            triangleSampleData.PartialSort((a, b) => a.Item1.CompareTo(a.Item1), 0, triangleSampleData.Length);

            if (gain.IsBlack() || infoGain == null)
            {
                gain = scn.DefaultLightGain;
                infoGain = new RgbSpectrumInfo(gain);
            }
            lightSpectra = GlobalConfiguration.Instance.SpectralRendering ? spectra.ToArray() : gain.ToArray();
        }
Beispiel #25
0
 public void SampleSpatialAdjoint(IRayEngineScene scene, float u0, float u1, float u2, float u3, float u4, out Vector dir, out float distance, out float pdf)
 {
     dir = MC.UniformSampleSphere(u0, u1);
     distance = 1.0f;
     pdf = MathLab.INV4PI;
 }
Beispiel #26
0
        public void SampleSpatialAdjoint(IRayEngineScene scen, float u0, float u1, float u2, float u3, float u4, out Vector dir, out float distance, out float pdf)
        {
            var scene = (RayEngineScene)scen;

            var worldCenter = scene.SceneGeometry.BoundingSphereCenter;
            var worldRadius = scene.SceneGeometry.BoundingSphereRadius * 1.01f;

            var p1 = worldCenter + worldRadius * MC.UniformSampleSphere(u0, u1);
            var p2 = worldCenter + worldRadius * MC.UniformSampleSphere(u2, u3);

            // Construct ray between p1 and p2
            dir= (p2 - p1).Normalize();

            // Compute InfiniteAreaLight ray weight
            Vector toCenter = (worldCenter - p1).Normalize();
            var rayDir= dir;
            float costheta = MathLab.AbsDot(ref toCenter, ref rayDir);
            distance = 1e4f;
            pdf = costheta / (4f * MathLab.M_PI * MathLab.M_PI * worldRadius * worldRadius);
        }