SkyEmitter(Stream *stream, InstanceManager *manager) : Emitter(stream, manager) { m_scale = stream->readFloat(); m_turbidity = stream->readFloat(); m_stretch = stream->readFloat(); m_resolution = stream->readInt(); m_extend = stream->readBool(); m_albedo = Spectrum(stream); m_sun = SphericalCoordinates(stream); Float sunElevation = 0.5f * M_PI - m_sun.elevation; #if SPECTRUM_SAMPLES == 3 for (int i=0; i<SPECTRUM_SAMPLES; ++i) m_state[i] = arhosek_rgb_skymodelstate_alloc_init( m_turbidity, m_albedo[i], sunElevation); #else for (int i=0; i<SPECTRUM_SAMPLES; ++i) m_state[i] = arhosekskymodelstate_alloc_init( m_turbidity, m_albedo[i], sunElevation); #endif configure(); }
SkyEmitter(const Properties &props) : Emitter(props) { m_scale = props.getFloat("scale", 1.0f); m_turbidity = props.getFloat("turbidity", 3.0f); m_stretch = props.getFloat("stretch", 1.0f); m_resolution = props.getInteger("resolution", 512); m_albedo = props.getSpectrum("albedo", Spectrum(0.2f)); m_sun = computeSunCoordinates(props); m_extend = props.getBoolean("extend", false); if (m_turbidity < 1 || m_turbidity > 10) Log(EError, "The turbidity parameter must be in the range [1,10]!"); if (m_stretch < 1 || m_stretch > 2) Log(EError, "The stretch parameter must be in the range [1,2]!"); for (int i=0; i<SPECTRUM_SAMPLES; ++i) { if (m_albedo[i] < 0 || m_albedo[i] > 1) Log(EError, "The albedo parameter must be in the range [0,1]!"); } Float sunElevation = 0.5f * M_PI - m_sun.elevation; if (sunElevation < 0) Log(EError, "The sun is below the horizon -- this is not supported by the sky model."); #if SPECTRUM_SAMPLES == 3 for (int i=0; i<SPECTRUM_SAMPLES; ++i) m_state[i] = arhosek_rgb_skymodelstate_alloc_init( m_turbidity, m_albedo[i], sunElevation); #else for (int i=0; i<SPECTRUM_SAMPLES; ++i) m_state[i] = arhosekskymodelstate_alloc_init( m_turbidity, m_albedo[i], sunElevation); #endif configure(); }
Float3 SunLuminance(bool& cached) { Float3 sunDirection = AppSettings::SunDirection; sunDirection.y = Saturate(sunDirection.y); sunDirection = Float3::Normalize(sunDirection); const float turbidity = Clamp(AppSettings::Turbidity.Value(), 1.0f, 32.0f); const float intensityScale = AppSettings::SunIntensityScale; const Float3 tintColor = AppSettings::SunTintColor; const bool32 normalizeIntensity = AppSettings::NormalizeSunIntensity; const float sunSize = AppSettings::SunSize; static float turbidityCache = 2.0f; static Float3 sunDirectionCache = Float3(-0.579149902f, 0.754439294f, -0.308879942f); static Float3 luminanceCache = Float3(1.61212531e+009f, 1.36822630e+009f, 1.07235315e+009f); static Float3 sunTintCache = Float3(1.0f, 1.0f, 1.0f); static float sunIntensityCache = 1.0f; static bool32 normalizeCache = false; static float sunSizeCache = AppSettings::BaseSunSize; if(turbidityCache == turbidity && sunDirection == sunDirectionCache && intensityScale == sunIntensityCache && tintColor == sunTintCache && normalizeCache == normalizeIntensity && sunSize == sunSizeCache) { cached = true; return luminanceCache; } cached = false; float thetaS = std::acos(1.0f - sunDirection.y); float elevation = Pi_2 - thetaS; // Get the sun's luminance, then apply tint and scale factors Float3 sunLuminance; // For now, we'll compute an average luminance value from Hosek solar radiance model, even though // we could compute illuminance directly while we're sampling the disk SampledSpectrum groundAlbedoSpectrum = SampledSpectrum::FromRGB(GroundAlbedo); SampledSpectrum solarRadiance; const uint64 NumDiscSamples = 4; for(uint64 x = 0; x < NumDiscSamples; ++x) { for(uint64 y = 0; y < NumDiscSamples; ++y) { float u = (x + 0.5f) / NumDiscSamples; float v = (y + 0.5f) / NumDiscSamples; Float2 discSamplePos = SquareToConcentricDiskMapping(u, v); float theta = elevation + discSamplePos.y * DegToRad(AppSettings::BaseSunSize); float gamma = discSamplePos.x * DegToRad(AppSettings::BaseSunSize); for(int32 i = 0; i < NumSpectralSamples; ++i) { ArHosekSkyModelState* skyState = arhosekskymodelstate_alloc_init(elevation, turbidity, groundAlbedoSpectrum[i]); float wavelength = Lerp(float(SampledLambdaStart), float(SampledLambdaEnd), i / float(NumSpectralSamples)); solarRadiance[i] = float(arhosekskymodel_solar_radiance(skyState, theta, gamma, wavelength)); arhosekskymodelstate_free(skyState); skyState = nullptr; } Float3 sampleRadiance = solarRadiance.ToRGB(); sunLuminance += sampleRadiance; } } // Account for luminous efficiency, coordinate system scaling, and sample averaging sunLuminance *= 683.0f * 100.0f * (1.0f / NumDiscSamples) * (1.0f / NumDiscSamples); sunLuminance = sunLuminance * tintColor; sunLuminance = sunLuminance * intensityScale; if(normalizeIntensity) { // Normalize so that the intensity stays the same even when the sun is bigger or smaller const float baseIntegral = IlluminanceIntegral(DegToRad(AppSettings::BaseSunSize)); const float currIntegral = IlluminanceIntegral(DegToRad(AppSettings::SunSize)); sunLuminance *= (baseIntegral / currIntegral); } turbidityCache = turbidity; sunDirectionCache = sunDirection; luminanceCache = sunLuminance; sunIntensityCache = intensityScale; sunTintCache = tintColor; normalizeCache = normalizeIntensity; sunSizeCache = sunSize; return sunLuminance; }