/// <summary> /// Generates a ray from a position in the image into the scene /// </summary> /// <param name="filmPos"> /// Position on the film plane: integer pixel coordinates and fractional position within /// </param> /// <param name="rng"> /// Random number generator used to sample additional decisions (lens position for depth of field) /// </param> /// <returns>The sampled camera ray and related information like PDF and contribution</returns> public override CameraRaySample GenerateRay(Vector2 filmPos, RNG rng) { Debug.Assert(Width != 0 && Height != 0); // Transform the direction from film to world space. // The view space is vertically flipped compared to the film. var view = new Vector3(2 * filmPos.X / Width - 1, 1 - 2 * filmPos.Y / Height, 0); var localDir = Vector3.Transform(view, viewToCamera); var dirHomo = Vector4.Transform(new Vector4(localDir, 0), cameraToWorld); var dir = new Vector3(dirHomo.X, dirHomo.Y, dirHomo.Z); // Compute the camera position var pos = Vector3.Transform(new Vector3(0, 0, 0), cameraToWorld); var ray = new Ray { Direction = Vector3.Normalize(dir), MinDistance = 0, Origin = pos }; // Sample depth of field float pdfLens = 1; if (lensRadius > 0) { var lensSample = rng.NextFloat2D(); var lensPos = lensRadius * SampleWarp.ToConcentricDisc(lensSample); // Intersect ray with focal plane var focalPoint = ray.ComputePoint(focalDistance / ray.Direction.Z); // Update the ray ray.Origin = new Vector3(lensPos, 0); ray.Direction = Vector3.Normalize(focalPoint - ray.Origin); pdfLens = 1 / (MathF.PI * lensRadius * lensRadius); } return(new CameraRaySample { Ray = ray, Weight = RgbColor.White, Point = new SurfacePoint { Position = Position, Normal = Direction }, PdfRay = SolidAngleToPixelJacobian(pos + dir) * pdfLens, PdfConnect = pdfLens }); }