public override (SurfaceInteraction, double) Sample(SurfaceInteraction _si) { SurfaceInteraction si = WorldToObject.Apply(_si); double dc2 = si.Point.LengthSquared(); if (dc2 <= Radius * Radius) { // Point inside sphere return(base.Sample(_si)); } // Point outside sphere double dc = Math.Sqrt(dc2); double sinThetaMax = Radius / dc; double cosThetaMax = Utils.SinToCos(sinThetaMax); // Determine theta and phi for uniform cone sampling double cosTheta = (cosThetaMax - 1) * Samplers.ThreadSafeRandom.NextDouble() + 1; double sinTheta = Utils.CosToSin(cosTheta); double phi = Samplers.ThreadSafeRandom.NextDouble() * 2.0 * Math.PI; // Distance between reference point and sample point on sphere double ds = dc * cosTheta - Math.Sqrt(Math.Max(0, Radius * Radius - dc2 * sinTheta * sinTheta)); // Kosinusni zakon double cosAlpha = (dc2 + Radius * Radius - ds * ds) / (2 * dc * Radius); double sinAlpha = Utils.CosToSin(cosAlpha); // Construct coordinate system and use phi and theta as spherical coordinates to get point on sphere Vector3 wcZ = si.Point.Clone().Normalize(); (Vector3 wcX, Vector3 wcY) = Utils.CoordinateSystem(wcZ); Vector3 nObj = Utils.SphericalDirection(sinAlpha, cosAlpha, phi, wcX, wcY, wcZ); Vector3 pObj = nObj * Radius; // Surface interaction Vector3 dpdu = new Vector3(-nObj.y, nObj.x, 0.0); SurfaceInteraction siSample = new SurfaceInteraction(pObj, nObj, Vector3.ZeroVector, dpdu, this); // Uniform cone PDF double pdf = Samplers.UniformConePdf(cosThetaMax); return(ObjectToWorld.Apply(siSample), pdf); }