~SkyEmitter() { #if SPECTRUM_SAMPLES == 3 for (int i=0; i<SPECTRUM_SAMPLES; ++i) arhosek_tristim_skymodelstate_free(m_state[i]); #else for (int i=0; i<SPECTRUM_SAMPLES; ++i) arhosekskymodelstate_free(m_state[i]); #endif }
TextureResult HosekWilkieSky(const BufferUploads::TextureDesc& desc, const ParameterBox& parameters) { // The "turbidity" parameter is Linke’s turbidity factor. Hosek and Wilkie give these example parameters: // T = 2 yields a very clear, Arctic-like sky // T = 3 a clear sky in a temperate climate // T = 6 a sky on a warm, moist day // T = 10 a slightly hazy day // T > 50 represent dense fog auto defaultSunDirection = Normalize(Float3(1.f, 1.f, 0.33f)); Float3 sunDirection = parameters.GetParameter<Float3>(ParameterBox::ParameterNameHash("SunDirection"), defaultSunDirection); sunDirection = Normalize(sunDirection); auto turbidity = (double)parameters.GetParameter(ParameterBox::ParameterNameHash("turbidity"), 3.f); auto albedo = (double)parameters.GetParameter(ParameterBox::ParameterNameHash("albedo"), 0.1f); auto elevation = (double)Deg2Rad(parameters.GetParameter(ParameterBox::ParameterNameHash("elevation"), XlASin(sunDirection[2]))); auto* state = arhosek_rgb_skymodelstate_alloc_init(turbidity, albedo, elevation); auto pixels = std::make_unique<Float4[]>(desc._width*desc._height); for (unsigned y=0; y<desc._height; ++y) for (unsigned x=0; x<desc._width; ++x) { auto p = y*desc._width+x; pixels[p] = Float4(0.f, 0.f, 0.f, 1.f); Float3 direction(0.f, 0.f, 0.f); bool hitPanel = false; for (unsigned c = 0; c < 6; ++c) { Float2 tc(x / float(desc._width), y / float(desc._height)); auto tcMins = s_verticalPanelCoords[c][0]; auto tcMaxs = s_verticalPanelCoords[c][1]; if (tc[0] >= tcMins[0] && tc[1] >= tcMins[1] && tc[0] < tcMaxs[0] && tc[1] < tcMaxs[1]) { tc[0] = 2.0f * (tc[0] - tcMins[0]) / (tcMaxs[0] - tcMins[0]) - 1.0f; tc[1] = 2.0f * (tc[1] - tcMins[1]) / (tcMaxs[1] - tcMins[1]) - 1.0f; hitPanel = true; auto plusX = s_verticalPanels[c][0]; auto plusY = s_verticalPanels[c][1]; auto center = s_verticalPanels[c][2]; direction = center + plusX * tc[0] + plusY * tc[1]; } } if (hitPanel) { auto theta = CartesianToSpherical(direction)[0]; theta = std::min(.4998f * gPI, theta); auto gamma = XlACos(std::max(0.f, Dot(Normalize(direction), sunDirection))); auto R = arhosek_tristim_skymodel_radiance(state, theta, gamma, 0); auto G = arhosek_tristim_skymodel_radiance(state, theta, gamma, 1); auto B = arhosek_tristim_skymodel_radiance(state, theta, gamma, 2); pixels[p][0] = (float)R; pixels[p][1] = (float)G; pixels[p][2] = (float)B; } } arhosekskymodelstate_free(state); return TextureResult { BufferUploads::CreateBasicPacket( (desc._width*desc._height)*sizeof(Float4), pixels.get(), BufferUploads::TexturePitches(desc._width*sizeof(Float4), desc._width*desc._height*sizeof(Float4))), RenderCore::Metal::NativeFormat::R32G32B32A32_FLOAT, UInt2(desc._width, desc._height) }; }
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; }