Files
FlaxEngine/Source/Engine/Level/Actors/Sky.cpp
2022-01-14 13:31:12 +01:00

268 lines
7.7 KiB
C++

// Copyright (c) 2012-2022 Wojciech Figat. All rights reserved.
#include "Sky.h"
#include "DirectionalLight.h"
#include "Engine/Core/Math/Color.h"
#include "Engine/Content/Content.h"
#include "Engine/Renderer/RenderList.h"
#include "Engine/Renderer/AtmospherePreCompute.h"
#include "Engine/Renderer/GBufferPass.h"
#include "Engine/Graphics/RenderBuffers.h"
#include "Engine/Graphics/RenderView.h"
#include "Engine/Graphics/RenderTask.h"
#include "Engine/Graphics/GPUContext.h"
#include "Engine/Graphics/GPUDevice.h"
#include "Engine/Graphics/Shaders/GPUConstantBuffer.h"
#include "Engine/Graphics/Shaders/GPUShader.h"
#include "Engine/Serialization/Serialization.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<Shader>(TEXT("Shaders/Sky"));
if (_shader == nullptr)
{
LOG(Fatal, "Cannot load sky shader.");
return;
}
#if COMPILE_WITH_DEV_ENV
_shader.Get()->OnReloading.Bind<Sky, &Sky::OnShaderReloading>(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);
_sphere = BoundingSphere(_transform.Translation, 0.0f);
}