From d0b3f63511d4ec199dc58b348d0ba11e04925b6b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tur=C3=A1nszki=20J=C3=A1nos?= Date: Wed, 27 Apr 2022 12:51:53 +0200 Subject: [PATCH] Starry sky (#418) --- Editor/TerrainGenerator.cpp | 91 +++++++++++--------- Editor/TerrainGenerator.h | 10 +-- Editor/WeatherWindow.cpp | 10 +++ Editor/WeatherWindow.h | 1 + WickedEngine/ArchiveVersionHistory.txt | 1 + WickedEngine/shaders/ShaderInterop_Weather.h | 5 ++ WickedEngine/shaders/skyAtmosphere.hlsli | 35 +++++--- WickedEngine/wiArchive.cpp | 2 +- WickedEngine/wiScene.cpp | 3 + WickedEngine/wiScene.h | 2 + WickedEngine/wiScene_Serializers.cpp | 10 +++ WickedEngine/wiVersion.cpp | 2 +- 12 files changed, 111 insertions(+), 61 deletions(-) diff --git a/Editor/TerrainGenerator.cpp b/Editor/TerrainGenerator.cpp index a9b38f3ef..16fbe52e8 100644 --- a/Editor/TerrainGenerator.cpp +++ b/Editor/TerrainGenerator.cpp @@ -27,14 +27,14 @@ void TerrainGenerator::init() if (lod == 0) { - for (int x = 0; x < width - 1; x++) + for (int x = 0; x < chunk_width - 1; x++) { - for (int z = 0; z < width - 1; z++) + for (int z = 0; z < chunk_width - 1; z++) { - int lowerLeft = x + z * width; - int lowerRight = (x + 1) + z * width; - int topLeft = x + (z + 1) * width; - int topRight = (x + 1) + (z + 1) * width; + int lowerLeft = x + z * chunk_width; + int lowerRight = (x + 1) + z * chunk_width; + int topLeft = x + (z + 1) * chunk_width; + int topRight = (x + 1) + (z + 1) * chunk_width; indices.push_back(topLeft); indices.push_back(lowerLeft); @@ -50,14 +50,14 @@ void TerrainGenerator::init() { const int step = 1 << lod; // inner grid: - for (int x = 1; x < width - 2; x += step) + for (int x = 1; x < chunk_width - 2; x += step) { - for (int z = 1; z < width - 2; z += step) + for (int z = 1; z < chunk_width - 2; z += step) { - int lowerLeft = x + z * width; - int lowerRight = (x + step) + z * width; - int topLeft = x + (z + step) * width; - int topRight = (x + step) + (z + step) * width; + int lowerLeft = x + z * chunk_width; + int lowerRight = (x + step) + z * chunk_width; + int topLeft = x + (z + step) * chunk_width; + int topRight = (x + step) + (z + step) * chunk_width; indices.push_back(topLeft); indices.push_back(lowerLeft); @@ -69,12 +69,12 @@ void TerrainGenerator::init() } } // bottom border: - for (int x = 0; x < width - 1; ++x) + for (int x = 0; x < chunk_width - 1; ++x) { const int z = 0; - int current = x + z * width; - int neighbor = x + 1 + z * width; - int connection = 1 + ((x + (step + 1) / 2 - 1) / step) * step + (z + 1) * width; + int current = x + z * chunk_width; + int neighbor = x + 1 + z * chunk_width; + int connection = 1 + ((x + (step + 1) / 2 - 1) / step) * step + (z + 1) * chunk_width; indices.push_back(current); indices.push_back(neighbor); @@ -82,7 +82,7 @@ void TerrainGenerator::init() if (((x - 1) % (step)) == step / 2) // halfway fill triangle { - int connection1 = 1 + (((x - 1) + (step + 1) / 2 - 1) / step) * step + (z + 1) * width; + int connection1 = 1 + (((x - 1) + (step + 1) / 2 - 1) / step) * step + (z + 1) * chunk_width; indices.push_back(current); indices.push_back(connection); @@ -90,12 +90,12 @@ void TerrainGenerator::init() } } // top border: - for (int x = 0; x < width - 1; ++x) + for (int x = 0; x < chunk_width - 1; ++x) { - const int z = width - 1; - int current = x + z * width; - int neighbor = x + 1 + z * width; - int connection = 1 + ((x + (step + 1) / 2 - 1) / step) * step + (z - 1) * width; + const int z = chunk_width - 1; + int current = x + z * chunk_width; + int neighbor = x + 1 + z * chunk_width; + int connection = 1 + ((x + (step + 1) / 2 - 1) / step) * step + (z - 1) * chunk_width; indices.push_back(current); indices.push_back(connection); @@ -103,7 +103,7 @@ void TerrainGenerator::init() if (((x - 1) % (step)) == step / 2) // halfway fill triangle { - int connection1 = 1 + (((x - 1) + (step + 1) / 2 - 1) / step) * step + (z - 1) * width; + int connection1 = 1 + (((x - 1) + (step + 1) / 2 - 1) / step) * step + (z - 1) * chunk_width; indices.push_back(current); indices.push_back(connection1); @@ -111,12 +111,12 @@ void TerrainGenerator::init() } } // left border: - for (int z = 0; z < width - 1; ++z) + for (int z = 0; z < chunk_width - 1; ++z) { const int x = 0; - int current = x + z * width; - int neighbor = x + (z + 1) * width; - int connection = x + 1 + (((z + (step + 1) / 2 - 1) / step) * step + 1) * width; + int current = x + z * chunk_width; + int neighbor = x + (z + 1) * chunk_width; + int connection = x + 1 + (((z + (step + 1) / 2 - 1) / step) * step + 1) * chunk_width; indices.push_back(current); indices.push_back(connection); @@ -124,7 +124,7 @@ void TerrainGenerator::init() if (((z - 1) % (step)) == step / 2) // halfway fill triangle { - int connection1 = x + 1 + ((((z - 1) + (step + 1) / 2 - 1) / step) * step + 1) * width; + int connection1 = x + 1 + ((((z - 1) + (step + 1) / 2 - 1) / step) * step + 1) * chunk_width; indices.push_back(current); indices.push_back(connection1); @@ -132,12 +132,12 @@ void TerrainGenerator::init() } } // right border: - for (int z = 0; z < width - 1; ++z) + for (int z = 0; z < chunk_width - 1; ++z) { - const int x = width - 1; - int current = x + z * width; - int neighbor = x + (z + 1) * width; - int connection = x - 1 + (((z + (step + 1) / 2 - 1) / step) * step + 1) * width; + const int x = chunk_width - 1; + int current = x + z * chunk_width; + int neighbor = x + (z + 1) * chunk_width; + int connection = x - 1 + (((z + (step + 1) / 2 - 1) / step) * step + 1) * chunk_width; indices.push_back(current); indices.push_back(neighbor); @@ -145,7 +145,7 @@ void TerrainGenerator::init() if (((z - 1) % (step)) == step / 2) // halfway fill triangle { - int connection1 = x - 1 + ((((z - 1) + (step + 1) / 2 - 1) / step) * step + 1) * width; + int connection1 = x - 1 + ((((z - 1) + (step + 1) / 2 - 1) / step) * step + 1) * chunk_width; indices.push_back(current); indices.push_back(connection); @@ -547,6 +547,7 @@ void TerrainGenerator::Generation_Restart() weather.cloud_shadow_amount = 0.5f; weather.cloud_shadow_scale = 0.003f; weather.cloud_shadow_speed = 0.25f; + weather.stars = 0.6f; } if (scene->lights.GetCount() == 0) { @@ -559,6 +560,7 @@ void TerrainGenerator::Generation_Restart() //light.SetVolumetricsEnabled(true); TransformComponent& transform = *scene->transforms.GetComponent(sunEntity); transform.RotateRollPitchYaw(XMFLOAT3(XM_PIDIV4, 0, XM_PIDIV4)); + transform.Translate(XMFLOAT3(0, 2, 0)); } } @@ -579,8 +581,8 @@ void TerrainGenerator::Generation_Update() if (centerToCamCheckBox.GetCheck()) { const CameraComponent& camera = GetCamera(); - center_chunk.x = (int)std::floor((camera.Eye.x + half_width) * width_rcp); - center_chunk.z = (int)std::floor((camera.Eye.z + half_width) * width_rcp); + center_chunk.x = (int)std::floor((camera.Eye.x + chunk_half_width) * chunk_width_rcp); + center_chunk.z = (int)std::floor((camera.Eye.z + chunk_half_width) * chunk_width_rcp); } // Chunk removal checks: @@ -641,6 +643,7 @@ void TerrainGenerator::Generation_Update() const float region1 = region1Slider.GetValue(); const float region2 = region2Slider.GetValue(); const float region3 = region3Slider.GetValue(); + bool generated_something = false; auto request_chunk = [&](int offset_x, int offset_z) { @@ -660,7 +663,7 @@ void TerrainGenerator::Generation_Update() TransformComponent& transform = *generation_scene.transforms.GetComponent(chunk_data.entity); transform.ClearTransform(); - const XMFLOAT3 chunk_pos = XMFLOAT3(float(chunk.x * (width - 1)), 0, float(chunk.z * (width - 1))); + const XMFLOAT3 chunk_pos = XMFLOAT3(float(chunk.x * (chunk_width - 1)), 0, float(chunk.z * (chunk_width - 1))); transform.Translate(chunk_pos); transform.UpdateTransform(); @@ -690,10 +693,10 @@ void TerrainGenerator::Generation_Update() // Do a parallel for loop over all the chunk's vertices and compute their properties: wi::jobsystem::context ctx; - wi::jobsystem::Dispatch(ctx, vertexCount, width, [&](wi::jobsystem::JobArgs args) { + wi::jobsystem::Dispatch(ctx, vertexCount, chunk_width, [&](wi::jobsystem::JobArgs args) { uint32_t index = args.jobIndex; - const float x = float(index % width) - half_width; - const float z = float(index / width) - half_width; + const float x = float(index % chunk_width) - chunk_half_width; + const float z = float(index / chunk_width) - chunk_half_width; XMVECTOR corners[3]; XMFLOAT2 corner_offsets[3] = { XMFLOAT2(0, 0), @@ -763,7 +766,7 @@ void TerrainGenerator::Generation_Update() mesh.vertex_positions[index] = XMFLOAT3(x, height, z); mesh.vertex_normals[index] = normal; mesh.vertex_colors[index] = wi::Color::fromFloat4(materialBlendWeights); - const XMFLOAT2 uv = XMFLOAT2(x * width_rcp + 0.5f, z * width_rcp + 0.5f); + const XMFLOAT2 uv = XMFLOAT2(x * chunk_width_rcp + 0.5f, z * chunk_width_rcp + 0.5f); mesh.vertex_uvset_0[index] = uv; XMFLOAT3 vertex_pos(chunk_pos.x + x, height, chunk_pos.z + z); @@ -793,7 +796,7 @@ void TerrainGenerator::Generation_Update() chunk_data.grass = std::move(grass); // the grass will be added to the scene later, only when the chunk is close to the camera (center chunk's neighbors) chunk_data.grass.meshID = chunk_data.entity; chunk_data.grass.strandCount = grass_valid_vertex_count.load() * 3; - chunk_data.grass.viewDistance = width; + chunk_data.grass.viewDistance = chunk_width; generation_scene.materials.Create(chunk_data.entity) = material_GrassParticle; } @@ -861,6 +864,7 @@ void TerrainGenerator::Generation_Update() } wi::jobsystem::Wait(ctx); // wait until mesh.CreateRenderData() async task finishes + generated_something = true; } // Grass patch placement: @@ -884,12 +888,13 @@ void TerrainGenerator::Generation_Update() grass.CreateRenderData(*mesh); } chunk_data.grass_exists = true; // don't generate more grass here + generated_something = true; } } } } - if (timer.elapsed_milliseconds() > generation_time_budget_milliseconds) + if (generated_something && timer.elapsed_milliseconds() > generation_time_budget_milliseconds) { generation_cancelled.store(true); } diff --git a/Editor/TerrainGenerator.h b/Editor/TerrainGenerator.h index 15e4d786c..bfbc09c9f 100644 --- a/Editor/TerrainGenerator.h +++ b/Editor/TerrainGenerator.h @@ -39,11 +39,11 @@ struct ChunkData struct TerrainGenerator : public wi::gui::Window { - inline static const int width = 64 + 3; // + 3: filler vertices for lod apron and grid perimeter - inline static const float half_width = (width - 1) * 0.5f; - inline static const float width_rcp = 1.0f / (width - 1); - inline static const uint32_t vertexCount = width * width; - inline static const int max_lod = (int)std::log2(width - 3) + 1; + inline static const int chunk_width = 64 + 3; // + 3: filler vertices for lod apron and grid perimeter + inline static const float chunk_half_width = (chunk_width - 1) * 0.5f; + inline static const float chunk_width_rcp = 1.0f / (chunk_width - 1); + inline static const uint32_t vertexCount = chunk_width * chunk_width; + inline static const int max_lod = (int)std::log2(chunk_width - 3) + 1; wi::scene::Scene* scene = &wi::scene::GetScene(); // by default it uses the global scene, but this can be changed wi::ecs::Entity terrainEntity = wi::ecs::INVALID_ENTITY; wi::ecs::Entity materialEntity_Base = wi::ecs::INVALID_ENTITY; diff --git a/Editor/WeatherWindow.cpp b/Editor/WeatherWindow.cpp index 40a87c2bc..ad99f888c 100644 --- a/Editor/WeatherWindow.cpp +++ b/Editor/WeatherWindow.cpp @@ -162,6 +162,15 @@ void WeatherWindow::Create(EditorComponent* editor) }); AddWidget(&skyExposureSlider); + starsSlider.Create(0, 1, 0.5f, 10000, "Stars: "); + starsSlider.SetTooltip("Amount of stars in the night sky (0 to disable). \nIt will only work with the realistic sky enabled. \nThey will be more visible at night time."); + starsSlider.SetSize(XMFLOAT2(100, hei)); + starsSlider.SetPos(XMFLOAT2(x, y += step)); + starsSlider.OnSlide([&](wi::gui::EventArgs args) { + GetWeather().stars = args.fValue; + }); + AddWidget(&starsSlider); + simpleskyCheckBox.Create("Simple sky: "); simpleskyCheckBox.SetTooltip("Simple sky will simply blend horizon and zenith color from bottom to top."); simpleskyCheckBox.SetSize(XMFLOAT2(hei, hei)); @@ -686,6 +695,7 @@ void WeatherWindow::Update() windWaveSizeSlider.SetValue(weather.windWaveSize); windRandomnessSlider.SetValue(weather.windRandomness); skyExposureSlider.SetValue(weather.skyExposure); + starsSlider.SetValue(weather.stars); windMagnitudeSlider.SetValue(XMVectorGetX(XMVector3Length(XMLoadFloat3(&weather.windDirection)))); switch (colorComboBox.GetSelected()) diff --git a/Editor/WeatherWindow.h b/Editor/WeatherWindow.h index 03547aa1b..33aa849ff 100644 --- a/Editor/WeatherWindow.h +++ b/Editor/WeatherWindow.h @@ -32,6 +32,7 @@ public: wi::gui::Slider windWaveSizeSlider; wi::gui::Slider windRandomnessSlider; wi::gui::Slider skyExposureSlider; + wi::gui::Slider starsSlider; wi::gui::CheckBox simpleskyCheckBox; wi::gui::CheckBox realisticskyCheckBox; wi::gui::Button skyButton; diff --git a/WickedEngine/ArchiveVersionHistory.txt b/WickedEngine/ArchiveVersionHistory.txt index 3ca869b14..01d9c76ba 100644 --- a/WickedEngine/ArchiveVersionHistory.txt +++ b/WickedEngine/ArchiveVersionHistory.txt @@ -1,5 +1,6 @@ This file contains changelog of wi::Archive versions +78: serialized stars parameter in weather 77: serialized cloud shadow properties 76: serialized LOD parameters 75: PreviousFrameTransformComponent is no longer serialized diff --git a/WickedEngine/shaders/ShaderInterop_Weather.h b/WickedEngine/shaders/ShaderInterop_Weather.h index 5c9bdcb5b..66f9cc12c 100644 --- a/WickedEngine/shaders/ShaderInterop_Weather.h +++ b/WickedEngine/shaders/ShaderInterop_Weather.h @@ -303,6 +303,11 @@ struct ShaderWeather float cloud_shadow_scale; float cloud_shadow_speed; + float3 padding; + float stars; // number of stars (0: disable stars, >0: increase number of stars) + + float4x4 stars_rotation; + ShaderFog fog; ShaderWind wind; ShaderOcean ocean; diff --git a/WickedEngine/shaders/skyAtmosphere.hlsli b/WickedEngine/shaders/skyAtmosphere.hlsli index 989f2b156..2a08ec3f2 100644 --- a/WickedEngine/shaders/skyAtmosphere.hlsli +++ b/WickedEngine/shaders/skyAtmosphere.hlsli @@ -411,22 +411,35 @@ float3 GetSunLuminance(float3 worldPosition, float3 worldDirection, float3 sunDi float sunHalfApexAngleRadian = 0.5 * sunApexAngleDegree * PI / 180.0; float sunCosHalfApexAngle = cos(sunHalfApexAngleRadian); - float VdotL = dot(worldDirection, normalize(sunDirection)); // weird... the sun disc shrinks near the horizon if we don't normalize sun direction - float3 retval = 0; - if (VdotL > sunCosHalfApexAngle) - { - float t = RaySphereIntersectNearest(worldPosition, worldDirection, float3(0.0f, 0.0f, 0.0f), atmosphere.bottomRadius); - if (t < 0.0f) // no intersection - { - const float3 atmosphereTransmittance = GetAtmosphereTransmittance(worldPosition, worldDirection, atmosphere, transmittanceLutTexture); - // Edge fade + float t = RaySphereIntersectNearest(worldPosition, worldDirection, float3(0.0f, 0.0f, 0.0f), atmosphere.bottomRadius); + if (t < 0.0f) // no intersection + { + float VdotL = dot(worldDirection, normalize(sunDirection)); // weird... the sun disc shrinks near the horizon if we don't normalize sun direction + if (VdotL > sunCosHalfApexAngle) + { + // Edge fade const float halfCosHalfApex = sunCosHalfApexAngle + (1.0f - sunCosHalfApexAngle) * 0.25; // Start fading when at 75% distance from light disk center const float weight = 1.0 - saturate((halfCosHalfApex - VdotL) / (halfCosHalfApex - sunCosHalfApexAngle)); - - retval = atmosphereTransmittance * weight * sunIlluminance; + + retval = weight * sunIlluminance; } + + if (GetWeather().stars > 0) + { + float3 stars_direction = mul(worldDirection, (float3x3)GetWeather().stars_rotation); + float stars_visibility = pow(saturate(1 - sunDirection.y), 2); + float stars_density_at_maximum = lerp(22, 8, GetWeather().stars); + float stars_threshold = lerp(32, stars_density_at_maximum, stars_visibility); // modifies the number of stars that are visible + float stars_exposure = lerp(0, 512, stars_visibility); // modifies the overall strength of the stars + float stars = saturate(pow(noise_gradient_3D(stars_direction * 300), stars_threshold)) * stars_exposure; + stars *= lerp(0.4, 1.4, noise_gradient_3D(stars_direction * 256 + GetTime())); // time based flickering + retval += stars; + } + + const float3 atmosphereTransmittance = GetAtmosphereTransmittance(worldPosition, worldDirection, atmosphere, transmittanceLutTexture); + retval *= atmosphereTransmittance; } return retval; diff --git a/WickedEngine/wiArchive.cpp b/WickedEngine/wiArchive.cpp index f4ad113d1..a73a434f0 100644 --- a/WickedEngine/wiArchive.cpp +++ b/WickedEngine/wiArchive.cpp @@ -5,7 +5,7 @@ namespace wi { // this should always be only INCREMENTED and only if a new serialization is implemeted somewhere! - static constexpr uint64_t __archiveVersion = 77; + static constexpr uint64_t __archiveVersion = 78; // this is the version number of which below the archive is not compatible with the current version static constexpr uint64_t __archiveVersionBarrier = 22; diff --git a/WickedEngine/wiScene.cpp b/WickedEngine/wiScene.cpp index c3024b961..ae511d8ad 100644 --- a/WickedEngine/wiScene.cpp +++ b/WickedEngine/wiScene.cpp @@ -1906,6 +1906,8 @@ namespace wi::scene shaderscene.weather.ocean.patch_size_rcp = 1.0f / weather.oceanParameters.patch_length; shaderscene.weather.ocean.texture_displacementmap = device->GetDescriptorIndex(ocean.getDisplacementMap(), SubresourceType::SRV); shaderscene.weather.ocean.texture_gradientmap = device->GetDescriptorIndex(ocean.getGradientMap(), SubresourceType::SRV); + shaderscene.weather.stars = weather.stars; + XMStoreFloat4x4(&shaderscene.weather.stars_rotation, XMMatrixRotationQuaternion(XMLoadFloat4(&weather.stars_rotation_quaternion))); shaderscene.ddgi.color_texture = device->GetDescriptorIndex(&ddgiColorTexture[0], SubresourceType::SRV); shaderscene.ddgi.depth_texture = device->GetDescriptorIndex(&ddgiDepthTexture[0], SubresourceType::SRV); @@ -3755,6 +3757,7 @@ namespace wi::scene weather.sunColor = light.color; weather.sunDirection = light.direction; weather.sunEnergy = light.energy; + weather.stars_rotation_quaternion = light.rotation; } locker.unlock(); break; diff --git a/WickedEngine/wiScene.h b/WickedEngine/wiScene.h index a6be8a95f..38acc5673 100644 --- a/WickedEngine/wiScene.h +++ b/WickedEngine/wiScene.h @@ -1172,6 +1172,7 @@ namespace wi::scene float windRandomness = 5; float windWaveSize = 1; float windSpeed = 1; + float stars = 0.5f; wi::Ocean::OceanParameters oceanParameters; AtmosphereParameters atmosphereParameters; @@ -1184,6 +1185,7 @@ namespace wi::scene uint32_t most_important_light_index = ~0u; wi::Resource skyMap; wi::Resource colorGradingMap; + XMFLOAT4 stars_rotation_quaternion = XMFLOAT4(0, 0, 0, 1); void Serialize(wi::Archive& archive, wi::ecs::EntitySerializer& seri); }; diff --git a/WickedEngine/wiScene_Serializers.cpp b/WickedEngine/wiScene_Serializers.cpp index 2938bac1c..f0e35612d 100644 --- a/WickedEngine/wiScene_Serializers.cpp +++ b/WickedEngine/wiScene_Serializers.cpp @@ -1114,6 +1114,11 @@ namespace wi::scene archive >> cloud_shadow_scale; archive >> cloud_shadow_speed; } + + if (archive.GetVersion() >= 78) + { + archive >> stars; + } } else { @@ -1254,6 +1259,11 @@ namespace wi::scene archive << cloud_shadow_scale; archive << cloud_shadow_speed; } + + if (archive.GetVersion() >= 78) + { + archive << stars; + } } } void SoundComponent::Serialize(wi::Archive& archive, EntitySerializer& seri) diff --git a/WickedEngine/wiVersion.cpp b/WickedEngine/wiVersion.cpp index 00a269dda..658291d58 100644 --- a/WickedEngine/wiVersion.cpp +++ b/WickedEngine/wiVersion.cpp @@ -9,7 +9,7 @@ namespace wi::version // minor features, major updates, breaking compatibility changes const int minor = 60; // minor bug fixes, alterations, refactors, updates - const int revision = 59; + const int revision = 60; const std::string version_string = std::to_string(major) + "." + std::to_string(minor) + "." + std::to_string(revision);