public static void GetSunlight(float altitude, float turbidity, Vector3D sunDirection, out Vector3F directSunlight, out Vector3F scatteredSunlight) { _spectrum.SetSolarSpectrum(); sunDirection.TryNormalize(); double cosZenith = sunDirection.Y; Vector3F direct, indirect; if (cosZenith > 0) { // Daylight - Sun is above horizon. double zenithAngle = Math.Acos(cosZenith); _spectrum.ApplyAtmosphericTransmittance(zenithAngle, turbidity, altitude, _spectrumDirect, _spectrumIndirect); direct = _spectrumDirect.ToXYZ(); indirect = _spectrumIndirect.ToXYZ(); } else { // Twilight - Sun is below horizon. // We lookup luminance based on experimental results on cloudless nights. // Get sun angle in degrees for table lookup. float solarAltitude = (float)MathHelper.ToDegrees(Math.Asin(sunDirection.Y)); // Get luminance from table (linearly interpolating the next two table entries). int lower = (int)Math.Floor(solarAltitude); int higher = (int)Math.Ceiling(solarAltitude); float a, b; TwilightLuminance.TryGetValue(lower, out a); TwilightLuminance.TryGetValue(higher, out b); float Y = InterpolationHelper.Lerp(a, b, solarAltitude - lower); // We use fixed chromacity values. float x = 0.2f; float y = 0.2f; // Convert xyY to XYZ. float X = x * (Y / y); float Z = (1.0f - x - y) * (Y / y); // Get sunlight from slightly above the horizon. const float epsilon = 0.001f; const double zenithAngle = ConstantsD.PiOver2 - epsilon; _spectrum.ApplyAtmosphericTransmittance(zenithAngle, turbidity, altitude, _spectrumDirect, _spectrumIndirect); direct = _spectrumDirect.ToXYZ(); indirect = _spectrumIndirect.ToXYZ(); // Blend between table values and sunset light. float blend = MathHelper.Clamp(-solarAltitude / 5.0f, 0, 1); direct = InterpolationHelper.Lerp(direct, new Vector3F(0, 0, 0), blend); indirect = InterpolationHelper.Lerp(indirect, new Vector3F(X, Y, Z), blend); } // Convert XYZ to RGB. directSunlight = GraphicsHelper.XYZToRGB * direct; scatteredSunlight = GraphicsHelper.XYZToRGB * indirect; }