// Copyright (c) 2012-2021 Wojciech Figat. All rights reserved. #include "Sky.h" #include "DirectionalLight.h" #include "Engine/Core/Math/Color.h" #include "Engine/Content/Content.h" #include "Engine/Graphics/RenderView.h" #include "Engine/Renderer/RenderList.h" #include "Engine/Renderer/AtmospherePreCompute.h" #include "Engine/Renderer/GBufferPass.h" #include "Engine/Graphics/RenderBuffers.h" #include "Engine/Graphics/GPUContext.h" #include "Engine/Serialization/Serialization.h" #include "Engine/Graphics/Shaders/GPUConstantBuffer.h" #include "Engine/Level/Scene/SceneRendering.h" PACK_STRUCT(struct Data { Matrix WVP; GBufferData GBuffer; AtmosphericFogData Fog; }); Sky::Sky(const SpawnParams& params) : Actor(params) , _shader(nullptr) , _psSky(nullptr) , _psFog(nullptr) { // Load shader _shader = Content::LoadAsyncInternal(TEXT("Shaders/Sky")); if (_shader == nullptr) { LOG(Fatal, "Cannot load sky shader."); return; } #if COMPILE_WITH_DEV_ENV _shader.Get()->OnReloading.Bind(this); #endif } Sky::~Sky() { SAFE_DELETE_GPU_RESOURCE(_psSky); SAFE_DELETE_GPU_RESOURCE(_psFog); } void Sky::InitConfig(AtmosphericFogData& config) const { config.AtmosphericFogDensityScale = 1.0f; config.AtmosphericFogSunDiscScale = SunDiscScale; config.AtmosphericFogDistanceScale = 1; config.AtmosphericFogGroundOffset = 0; config.AtmosphericFogAltitudeScale = 1; config.AtmosphericFogStartDistance = 0; config.AtmosphericFogPower = 1; config.AtmosphericFogDistanceOffset = 0; config.AtmosphericFogSunPower = SunPower; config.AtmosphericFogDensityOffset = 0.0f; if (SunLight) { config.AtmosphericFogSunDirection = -SunLight->GetDirection(); config.AtmosphericFogSunColor = SunLight->Color.ToVector3(); } else { config.AtmosphericFogSunDirection = Vector3::UnitY; config.AtmosphericFogSunColor = Vector3::One; } } void Sky::Draw(RenderContext& renderContext) { if (HasContentLoaded()) { // Ensure to have pipeline state cache created if (_psSky == nullptr || _psFog == nullptr) { const auto shader = _shader->GetShader(); // Create pipeline states if (_psSky == nullptr) { _psSky = GPUDevice::Instance->CreatePipelineState(); GPUPipelineState::Description psDesc = GPUPipelineState::Description::Default; psDesc.VS = shader->GetVS("VS"); psDesc.PS = shader->GetPS("PS_Sky"); psDesc.CullMode = CullMode::Inverted; psDesc.DepthWriteEnable = false; psDesc.DepthClipEnable = false; psDesc.DepthFunc = ComparisonFunc::LessEqual; if (_psSky->Init(psDesc)) { LOG(Warning, "Cannot create graphics pipeline state object for '{0}'.", ToString()); } } if (_psFog == nullptr) { _psFog = GPUDevice::Instance->CreatePipelineState(); GPUPipelineState::Description psDesc = GPUPipelineState::Description::DefaultFullscreenTriangle; psDesc.PS = shader->GetPS("PS_Fog"); psDesc.DepthWriteEnable = false; psDesc.DepthClipEnable = false; psDesc.BlendMode = BlendingMode::Additive; if (_psFog->Init(psDesc)) { LOG(Warning, "Cannot create graphics pipeline state object for '{0}'.", ToString()); } } } // Register for the sky and fog pass renderContext.List->Sky = this; //if(renderContext.View.Flags & ViewFlags::Fog) != 0) //renderContext.List->AtmosphericFog = this; // TODO: finish atmosphere fog } } void Sky::Serialize(SerializeStream& stream, const void* otherObj) { // Base Actor::Serialize(stream, otherObj); SERIALIZE_GET_OTHER_OBJ(Sky); SERIALIZE_MEMBER(Sun, SunLight); SERIALIZE(SunDiscScale); SERIALIZE(SunPower); } void Sky::Deserialize(DeserializeStream& stream, ISerializeModifier* modifier) { // Base Actor::Deserialize(stream, modifier); DESERIALIZE_MEMBER(Sun, SunLight); DESERIALIZE(SunDiscScale); DESERIALIZE(SunPower); } bool Sky::HasContentLoaded() const { return _shader && _shader->IsLoaded() && AtmospherePreCompute::GetCache(nullptr); } bool Sky::IntersectsItself(const Ray& ray, float& distance, Vector3& normal) { return false; } void Sky::DrawFog(GPUContext* context, RenderContext& renderContext, GPUTextureView* output) { // Get precomputed cache and bind it to the pipeline AtmosphereCache cache; if (!AtmospherePreCompute::GetCache(&cache)) return; context->BindSR(4, cache.Transmittance); context->BindSR(5, cache.Irradiance); context->BindSR(6, cache.Inscatter->ViewVolume()); // Bind GBuffer inputs context->BindSR(0, renderContext.Buffers->GBuffer0); context->BindSR(1, renderContext.Buffers->GBuffer1); context->BindSR(2, renderContext.Buffers->GBuffer2); context->BindSR(3, renderContext.Buffers->DepthBuffer); // Setup constants data Data data; GBufferPass::SetInputs(renderContext.View, data.GBuffer); InitConfig(data.Fog); data.Fog.AtmosphericFogSunPower *= SunLight ? SunLight->Brightness : 1.0f; bool useSpecularLight = (renderContext.View.Flags & ViewFlags::SpecularLight) != 0; if (!useSpecularLight) { data.Fog.AtmosphericFogSunDiscScale = 0; } // Bind pipeline auto cb = _shader->GetShader()->GetCB(0); context->UpdateCB(cb, &data); context->BindCB(0, cb); context->SetState(_psFog); context->SetRenderTarget(output); context->DrawFullscreenTriangle(); } void Sky::ApplySky(GPUContext* context, RenderContext& renderContext, const Matrix& world) { // Get precomputed cache and bind it to the pipeline AtmosphereCache cache; if (!AtmospherePreCompute::GetCache(&cache)) return; context->BindSR(4, cache.Transmittance); context->BindSR(5, cache.Irradiance); context->BindSR(6, cache.Inscatter->ViewVolume()); // Setup constants data Matrix m; Data data; Matrix::Multiply(world, renderContext.View.Frustum.GetMatrix(), m); Matrix::Transpose(m, data.WVP); GBufferPass::SetInputs(renderContext.View, data.GBuffer); InitConfig(data.Fog); //data.Fog.AtmosphericFogSunPower *= SunLight ? SunLight->Brightness : 1.0f; bool useSpecularLight = (renderContext.View.Flags & ViewFlags::SpecularLight) != 0; if (!useSpecularLight) { // Hide sun disc if specular light is disabled data.Fog.AtmosphericFogSunDiscScale = 0; } // Bind pipeline auto cb = _shader->GetShader()->GetCB(0); context->UpdateCB(cb, &data); context->BindCB(0, cb); context->SetState(_psSky); } void Sky::EndPlay() { // Cleanup SAFE_DELETE_GPU_RESOURCE(_psSky); SAFE_DELETE_GPU_RESOURCE(_psFog); // Base Actor::EndPlay(); } void Sky::OnEnable() { GetSceneRendering()->AddCommonNoCulling(this); #if USE_EDITOR GetSceneRendering()->AddViewportIcon(this); #endif // Base Actor::OnEnable(); } void Sky::OnDisable() { #if USE_EDITOR GetSceneRendering()->RemoveViewportIcon(this); #endif GetSceneRendering()->RemoveCommonNoCulling(this); // Base Actor::OnDisable(); } void Sky::OnTransformChanged() { // Base Actor::OnTransformChanged(); _box = BoundingBox(_transform.Translation, _transform.Translation); _sphere = BoundingSphere(_transform.Translation, 0.0f); }