public Normal3 Sample(Random random) { /// Compute Height double theta = Math.Pow((random.NextDouble() - 0.5d) * Math.PI, Exponent); double r = Math.Cos(theta); double w = Math.Sin(theta); /// Compute u and v double angle = random.NextDouble() * 2 * Math.PI; double u = r * Math.Cos(angle); double v = r * Math.Sin(angle); /// Transform to Orientation Normal3 uDirection = Normal3.AnyPerpendicular(Orientation); Normal3 vDirection = Normal3.Perpendicular(Orientation, uDirection); return(new Normal3(uDirection * (float)u + vDirection * (float)v + Orientation * (float)w)); }
public Normal3 Sample(Random random) { /// Get Point uniformly on Disc double angle = random.NextDouble() * 2 * Math.PI; double r = random.NextDouble() + random.NextDouble(); r = r < 1 ? r : 2 - r; double u = r * Math.Cos(angle); double v = r * Math.Sin(angle); /// Raise Disc point to Hemisphere double w = Math.Sqrt(1 - u * u - v * v); Normal3 uDirection = Normal3.AnyPerpendicular(Orientation); Normal3 vDirection = Normal3.Perpendicular(Orientation, uDirection); return(new Normal3(uDirection * (float)u + vDirection * (float)v + Orientation * (float)w)); }
Vector3 SampleVNDF(float r1, float r2) { // Rotate so UnitZ is up, and flip incoming to outgoing direction Normal3 up = Normal3.UnitZ; OpenTK.Mathematics.Quaternion rotation; if (Orientation == -up) { rotation = new OpenTK.Mathematics.Quaternion(Normal3.AnyPerpendicular(up).Vector, 0f); } else { rotation = new OpenTK.Mathematics.Quaternion(Vector3.Cross(Orientation.Vector, up.Vector), Vector3.Dot(Orientation.Vector, up.Vector) + 1f).Normalized(); } Vector3 Ve = rotation * -IncomingDirection.Vector; // Section 3.2: transforming the view direction to the hemisphere configuration Vector3 Vh = new Vector3(Roughness * Ve.X, Roughness * Ve.Y, Ve.Z).Normalized(); // Section 4.1: orthonormal basis (with special case if cross product is zero) float lensq = Vh.X * Vh.X + Vh.Y * Vh.Y; Vector3 T1 = lensq > 0 ? new Vector3(-Vh.Y, Vh.X, 0) / (float)Math.Sqrt(lensq) : new Vector3(1f, 0f, 0f); Vector3 T2 = Vector3.Cross(Vh, T1); // Section 4.2: parameterization of the projected area float r = (float)Math.Sqrt(r1); float phi = 2.0f * (float)Math.PI * r2; float t1 = r * (float)Math.Cos(phi); float t2 = r * (float)Math.Sin(phi); float s = 0.5f * (1f + Vh.Z); t2 = (1f - s) * (float)Math.Sqrt(1f - t1 * t1) + s * t2; // Section 4.3: reprojection onto hemisphere Vector3 Nh = t1 * T1 + t2 * T2 + (float)Math.Sqrt(Math.Max(0f, 1f - t1 * t1 - t2 * t2)) * Vh; // Section 3.4: transforming the normal back to the ellipsoid configuration Vector3 Ne = new(Roughness * Nh.X, Roughness *Nh.Y, (float)Math.Max(0.0, Nh.Z)); return(rotation.Inverted() * Ne); }