You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
416 lines
16 KiB
C++
416 lines
16 KiB
C++
/*
|
|
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
|
|
* its licensors.
|
|
*
|
|
* For complete copyright and license terms please see the LICENSE at the root of this
|
|
* distribution (the "License"). All use of this software is governed by the License,
|
|
* or, if provided, by the license below or the license accompanying this file. Do not
|
|
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
*
|
|
*/
|
|
// Original file Copyright Crytek GMBH or its affiliates, used under license.
|
|
|
|
#include "Cry3DEngine_precompiled.h"
|
|
#include "SkyLightManager.h"
|
|
#include "SkyLightNishita.h"
|
|
#include <CRESky.h>
|
|
|
|
#include <AzCore/Jobs/LegacyJobExecutor.h>
|
|
|
|
static AZ::LegacyJobExecutor s_jobExecutor;
|
|
|
|
CSkyLightManager::CSkyLightManager()
|
|
: m_pSkyLightNishita(CryAlignedNew<CSkyLightNishita>())
|
|
, m_pSkyDomeMesh(0)
|
|
, m_curSkyDomeCondition()
|
|
, m_updatingSkyDomeCondition()
|
|
, m_numSkyDomeColorsComputed(SSkyLightRenderParams::skyDomeTextureSize)
|
|
, m_curBackBuffer(0)
|
|
, m_lastFrameID(0)
|
|
, m_curSkyHemiColor()
|
|
, m_curHazeColor(0.0f, 0.0f, 0.0f)
|
|
, m_curHazeColorMieNoPremul(0.0f, 0.0f, 0.0f)
|
|
, m_curHazeColorRayleighNoPremul(0.0f, 0.0f, 0.0f)
|
|
, m_skyHemiColorAccum()
|
|
, m_hazeColorAccum(0.0f, 0.0f, 0.0f)
|
|
, m_hazeColorMieNoPremulAccum(0.0f, 0.0f, 0.0f)
|
|
, m_hazeColorRayleighNoPremulAccum(0.0f, 0.0f, 0.0f)
|
|
, m_bFlushFullUpdate(false)
|
|
, m_renderParams()
|
|
{
|
|
InitSkyDomeMesh();
|
|
|
|
m_updateRequested[0] = m_updateRequested[1] = 0;
|
|
|
|
// init textures with default data
|
|
m_skyDomeTextureDataMie[ 0 ].resize(SSkyLightRenderParams::skyDomeTextureSize);
|
|
m_skyDomeTextureDataMie[ 1 ].resize(SSkyLightRenderParams::skyDomeTextureSize);
|
|
m_skyDomeTextureDataRayleigh[ 0 ].resize(SSkyLightRenderParams::skyDomeTextureSize);
|
|
m_skyDomeTextureDataRayleigh[ 1 ].resize(SSkyLightRenderParams::skyDomeTextureSize);
|
|
|
|
// init time stamps
|
|
m_skyDomeTextureTimeStamp[ 0 ] = GetRenderer()->GetFrameID(false);
|
|
m_skyDomeTextureTimeStamp[ 1 ] = GetRenderer()->GetFrameID(false);
|
|
|
|
// init sky hemisphere colors and accumulators
|
|
memset(m_curSkyHemiColor, 0, sizeof(m_curSkyHemiColor));
|
|
memset(m_skyHemiColorAccum, 0, sizeof(m_skyHemiColorAccum));
|
|
|
|
// set default render parameters
|
|
UpdateRenderParams();
|
|
}
|
|
|
|
|
|
CSkyLightManager::~CSkyLightManager()
|
|
{
|
|
CryAlignedDelete(m_pSkyLightNishita);
|
|
}
|
|
|
|
inline static void Sync()
|
|
{
|
|
s_jobExecutor.WaitForCompletion();
|
|
}
|
|
|
|
void CSkyLightManager::PushUpdateParams()
|
|
{
|
|
//pushes the update parameters, explicite call since engine requests asynchronously
|
|
memcpy(&m_reqSkyDomeCondition[0], &m_reqSkyDomeCondition[1], sizeof(SSkyDomeCondition));
|
|
m_updateRequested[0] = m_updateRequested[1];
|
|
m_updateRequested[1] = 0;
|
|
}
|
|
|
|
void CSkyLightManager::SetSkyDomeCondition(const SSkyDomeCondition& skyDomeCondition)
|
|
{
|
|
m_reqSkyDomeCondition[1] = skyDomeCondition;
|
|
m_updateRequested[1] = 1;
|
|
}
|
|
|
|
|
|
void CSkyLightManager::FullUpdate()
|
|
{
|
|
Sync();
|
|
PushUpdateParams();
|
|
|
|
s_jobExecutor.Reset();
|
|
s_jobExecutor.StartJob([this]() { this->UpdateInternal(GetRenderer()->GetFrameID(false), SSkyLightRenderParams::skyDomeTextureSize, (int)1); });
|
|
|
|
m_needRenderParamUpdate = true;
|
|
m_bFlushFullUpdate = true;
|
|
}
|
|
|
|
void CSkyLightManager::IncrementalUpdate(f32 updateRatioPerFrame, const SRenderingPassInfo& passInfo)
|
|
{
|
|
Sync();
|
|
|
|
FUNCTION_PROFILER_3DENGINE;
|
|
|
|
// get current ID of "main" frame (no recursive rendering),
|
|
// incremental update should only be processed once per frame
|
|
if (m_lastFrameID != passInfo.GetMainFrameID())
|
|
{
|
|
int32 numUpdate((int32) ((f32) SSkyLightRenderParams::skyDomeTextureSize * updateRatioPerFrame / 100.0f + 0.5f));
|
|
numUpdate = clamp_tpl(numUpdate, 1, SSkyLightRenderParams::skyDomeTextureSize);
|
|
if (m_needRenderParamUpdate)
|
|
{
|
|
UpdateRenderParams(); // update render params
|
|
}
|
|
PushUpdateParams();
|
|
|
|
s_jobExecutor.Reset();
|
|
s_jobExecutor.StartJob([this, passInfo, numUpdate]() { this->UpdateInternal(passInfo.GetMainFrameID(), numUpdate, (int)0); });
|
|
}
|
|
}
|
|
|
|
|
|
void CSkyLightManager::UpdateInternal(int32 newFrameID, int32 numUpdates, int callerIsFullUpdate)
|
|
{
|
|
FUNCTION_PROFILER_3DENGINE;
|
|
|
|
// update sky dome if requested -- requires last update request to be fully processed!
|
|
int procUpdate = callerIsFullUpdate;
|
|
procUpdate |= (int)IsSkyDomeUpdateFinished();
|
|
procUpdate &= m_updateRequested[0];
|
|
if (procUpdate)
|
|
{
|
|
// set sky dome settings
|
|
memcpy(&m_updatingSkyDomeCondition, &m_reqSkyDomeCondition[0], sizeof(SSkyDomeCondition));
|
|
m_pSkyLightNishita->SetSunDirection(m_updatingSkyDomeCondition.m_sunDirection);
|
|
m_pSkyLightNishita->SetRGBWaveLengths(m_updatingSkyDomeCondition.m_rgbWaveLengths);
|
|
m_pSkyLightNishita->SetAtmosphericConditions(m_updatingSkyDomeCondition.m_sunIntensity,
|
|
1e-4f * m_updatingSkyDomeCondition.m_Km, 1e-4f * m_updatingSkyDomeCondition.m_Kr, m_updatingSkyDomeCondition.m_g); // scale mie and rayleigh scattering for more convenient editing in time of day dialog
|
|
|
|
// update request has been accepted
|
|
m_updateRequested[0] = 0;
|
|
m_numSkyDomeColorsComputed = 0;
|
|
|
|
// reset sky & haze color accumulator
|
|
m_hazeColorAccum = Vec3(0.0f, 0.0f, 0.0f);
|
|
m_hazeColorMieNoPremulAccum = Vec3(0.0f, 0.0f, 0.0f);
|
|
m_hazeColorRayleighNoPremulAccum = Vec3(0.0f, 0.0f, 0.0f);
|
|
memset(m_skyHemiColorAccum, 0, sizeof(m_skyHemiColorAccum));
|
|
}
|
|
|
|
// any work to do?
|
|
if (false == IsSkyDomeUpdateFinished())
|
|
{
|
|
if (numUpdates <= 0)
|
|
{
|
|
// do a full update
|
|
numUpdates = SSkyLightRenderParams::skyDomeTextureSize;
|
|
}
|
|
|
|
// find minimally required work load for this incremental update
|
|
numUpdates = min(SSkyLightRenderParams::skyDomeTextureSize - m_numSkyDomeColorsComputed, numUpdates);
|
|
|
|
// perform color computations
|
|
SkyDomeTextureData& skyDomeTextureDataMie(m_skyDomeTextureDataMie[ GetBackBuffer() ]);
|
|
SkyDomeTextureData& skyDomeTextureDataRayleigh(m_skyDomeTextureDataRayleigh[ GetBackBuffer() ]);
|
|
|
|
int32 numSkyDomeColorsComputed(m_numSkyDomeColorsComputed);
|
|
for (; numUpdates > 0; --numUpdates, ++numSkyDomeColorsComputed)
|
|
{
|
|
// calc latitude/longitude
|
|
int lon(numSkyDomeColorsComputed / SSkyLightRenderParams::skyDomeTextureWidth);
|
|
int lat(numSkyDomeColorsComputed % SSkyLightRenderParams::skyDomeTextureWidth);
|
|
|
|
float lonArc(DEG2RAD((float) lon * 90.0f / (float) SSkyLightRenderParams::skyDomeTextureHeight));
|
|
float latArc(DEG2RAD((float) lat * 360.0f / (float) SSkyLightRenderParams::skyDomeTextureWidth));
|
|
|
|
float sinLon(0);
|
|
float cosLon(0);
|
|
sincos_tpl(lonArc, &sinLon, &cosLon);
|
|
float sinLat(0);
|
|
float cosLat(0);
|
|
sincos_tpl(latArc, &sinLat, &cosLat);
|
|
|
|
// calc sky direction for given update latitude/longitude (hemisphere)
|
|
Vec3 skyDir(sinLon * cosLat, sinLon * sinLat, cosLon);
|
|
|
|
// compute color
|
|
//Vec3 skyColAtDir( 0.0, 0.0, 0.0 );
|
|
Vec3 skyColAtDirMieNoPremul(0.0, 0.0, 0.0);
|
|
Vec3 skyColAtDirRayleighNoPremul(0.0, 0.0, 0.0);
|
|
Vec3 skyColAtDirRayleigh(0.0, 0.0, 0.0);
|
|
|
|
m_pSkyLightNishita->ComputeSkyColor(skyDir, 0, &skyColAtDirMieNoPremul, &skyColAtDirRayleighNoPremul, &skyColAtDirRayleigh);
|
|
|
|
// store color in texture
|
|
skyDomeTextureDataMie[ numSkyDomeColorsComputed ] = CryHalf4(skyColAtDirMieNoPremul.x, skyColAtDirMieNoPremul.y, skyColAtDirMieNoPremul.z, 1.0f);
|
|
skyDomeTextureDataRayleigh[ numSkyDomeColorsComputed ] = CryHalf4(skyColAtDirRayleighNoPremul.x, skyColAtDirRayleighNoPremul.y, skyColAtDirRayleighNoPremul.z, 1.0f);
|
|
|
|
// update haze color accum (accumulate second last sample row)
|
|
if (lon == SSkyLightRenderParams::skyDomeTextureHeight - 2)
|
|
{
|
|
m_hazeColorAccum += skyColAtDirRayleigh;
|
|
m_hazeColorMieNoPremulAccum += skyColAtDirMieNoPremul;
|
|
m_hazeColorRayleighNoPremulAccum += skyColAtDirRayleighNoPremul;
|
|
}
|
|
|
|
// update sky hemisphere color accumulator
|
|
int y(lon >> SSkyLightRenderParams::skyDomeTextureHeightBy2Log);
|
|
int x(((lat + SSkyLightRenderParams::skyDomeTextureWidthBy8) & (SSkyLightRenderParams::skyDomeTextureWidth - 1)) >> SSkyLightRenderParams::skyDomeTextureWidthBy4Log);
|
|
int skyHemiColAccumIdx(x * y + y);
|
|
assert(((unsigned int)skyHemiColAccumIdx) < 5);
|
|
m_skyHemiColorAccum[skyHemiColAccumIdx] += skyColAtDirRayleigh;
|
|
}
|
|
|
|
m_numSkyDomeColorsComputed = numSkyDomeColorsComputed;
|
|
|
|
// sky dome update finished?
|
|
if (false != IsSkyDomeUpdateFinished())
|
|
{
|
|
// update time stamp
|
|
m_skyDomeTextureTimeStamp[ GetBackBuffer() ] = newFrameID;
|
|
|
|
// get new haze color
|
|
const float c_invNumHazeSamples(1.0f / (float) SSkyLightRenderParams::skyDomeTextureWidth);
|
|
m_curHazeColor = m_hazeColorAccum * c_invNumHazeSamples;
|
|
m_curHazeColorMieNoPremul = m_hazeColorMieNoPremulAccum * c_invNumHazeSamples;
|
|
m_curHazeColorRayleighNoPremul = m_hazeColorRayleighNoPremulAccum * c_invNumHazeSamples;
|
|
|
|
// get new sky hemisphere colors
|
|
const float c_scaleHemiTop(2.0f / (SSkyLightRenderParams::skyDomeTextureWidth * SSkyLightRenderParams::skyDomeTextureHeight));
|
|
const float c_scaleHemiSide(8.0f / (SSkyLightRenderParams::skyDomeTextureWidth * SSkyLightRenderParams::skyDomeTextureHeight));
|
|
m_curSkyHemiColor[0] = m_skyHemiColorAccum[0] * c_scaleHemiTop;
|
|
m_curSkyHemiColor[1] = m_skyHemiColorAccum[1] * c_scaleHemiSide;
|
|
m_curSkyHemiColor[2] = m_skyHemiColorAccum[2] * c_scaleHemiSide;
|
|
m_curSkyHemiColor[3] = m_skyHemiColorAccum[3] * c_scaleHemiSide;
|
|
m_curSkyHemiColor[4] = m_skyHemiColorAccum[4] * c_scaleHemiSide;
|
|
|
|
// toggle sky light buffers
|
|
ToggleBuffer();
|
|
}
|
|
}
|
|
|
|
// update frame ID
|
|
m_lastFrameID = newFrameID;
|
|
}
|
|
|
|
void CSkyLightManager::SetQuality(int32 quality)
|
|
{
|
|
if (quality != m_pSkyLightNishita->GetInScatteringIntegralStepSize())
|
|
{
|
|
Sync();
|
|
// when setting new quality we need to start sky dome update from scratch...
|
|
// ... to avoid "artifacts" in the resulting texture
|
|
m_numSkyDomeColorsComputed = 0;
|
|
m_pSkyLightNishita->SetInScatteringIntegralStepSize(quality);
|
|
}
|
|
}
|
|
|
|
const SSkyLightRenderParams* CSkyLightManager::GetRenderParams() const
|
|
{
|
|
return &m_renderParams;
|
|
}
|
|
|
|
void CSkyLightManager::UpdateRenderParams()
|
|
{
|
|
// sky dome mesh data
|
|
m_renderParams.m_pSkyDomeMesh = m_pSkyDomeMesh;
|
|
|
|
// sky dome texture access
|
|
m_renderParams.m_skyDomeTextureTimeStamp = m_skyDomeTextureTimeStamp[ GetFrontBuffer() ];
|
|
m_renderParams.m_pSkyDomeTextureDataMie = (const void*) &m_skyDomeTextureDataMie[ GetFrontBuffer() ][ 0 ];
|
|
m_renderParams.m_pSkyDomeTextureDataRayleigh = (const void*) &m_skyDomeTextureDataRayleigh[ GetFrontBuffer() ][ 0 ];
|
|
m_renderParams.m_skyDomeTexturePitch = SSkyLightRenderParams::skyDomeTextureWidth * sizeof(CryHalf4);
|
|
|
|
// shader constants for final per-pixel phase computation
|
|
m_renderParams.m_partialMieInScatteringConst = m_pSkyLightNishita->GetPartialMieInScatteringConst();
|
|
m_renderParams.m_partialRayleighInScatteringConst = m_pSkyLightNishita->GetPartialRayleighInScatteringConst();
|
|
Vec3 sunDir(m_pSkyLightNishita->GetSunDirection());
|
|
m_renderParams.m_sunDirection = Vec4(sunDir.x, sunDir.y, sunDir.z, 0.0f);
|
|
m_renderParams.m_phaseFunctionConsts = m_pSkyLightNishita->GetPhaseFunctionConsts();
|
|
m_renderParams.m_hazeColor = Vec4(m_curHazeColor.x, m_curHazeColor.y, m_curHazeColor.z, 0);
|
|
m_renderParams.m_hazeColorMieNoPremul = Vec4(m_curHazeColorMieNoPremul.x, m_curHazeColorMieNoPremul.y, m_curHazeColorMieNoPremul.z, 0);
|
|
m_renderParams.m_hazeColorRayleighNoPremul = Vec4(m_curHazeColorRayleighNoPremul.x, m_curHazeColorRayleighNoPremul.y, m_curHazeColorRayleighNoPremul.z, 0);
|
|
|
|
// set sky hemisphere colors
|
|
m_renderParams.m_skyColorTop = m_curSkyHemiColor[0];
|
|
m_renderParams.m_skyColorNorth = m_curSkyHemiColor[3];
|
|
m_renderParams.m_skyColorWest = m_curSkyHemiColor[4];
|
|
m_renderParams.m_skyColorSouth = m_curSkyHemiColor[1];
|
|
m_renderParams.m_skyColorEast = m_curSkyHemiColor[2];
|
|
|
|
// copy sky dome condition params
|
|
m_curSkyDomeCondition = m_updatingSkyDomeCondition;
|
|
|
|
m_needRenderParamUpdate = 0;
|
|
}
|
|
|
|
void CSkyLightManager::GetCurSkyDomeCondition(SSkyDomeCondition& skyCond) const
|
|
{
|
|
skyCond = m_curSkyDomeCondition;
|
|
}
|
|
|
|
bool CSkyLightManager::IsSkyDomeUpdateFinished() const
|
|
{
|
|
return(SSkyLightRenderParams::skyDomeTextureSize == m_numSkyDomeColorsComputed);
|
|
}
|
|
|
|
|
|
void CSkyLightManager::InitSkyDomeMesh()
|
|
{
|
|
ReleaseSkyDomeMesh();
|
|
|
|
#if defined(MOBILE)
|
|
const uint32 c_numRings(10);
|
|
const uint32 c_numSections(10);
|
|
#else
|
|
const uint32 c_numRings(20);
|
|
const uint32 c_numSections(20);
|
|
#endif
|
|
const uint32 c_numSkyDomeVertices((c_numRings + 1) * (c_numSections + 1));
|
|
const uint32 c_numSkyDomeTriangles(2 * c_numRings * c_numSections);
|
|
const uint32 c_numSkyDomeIndices(c_numSkyDomeTriangles * 3);
|
|
|
|
std::vector< vtx_idx > skyDomeIndices;
|
|
std::vector< SVF_P3F_C4B_T2F > skyDomeVertices;
|
|
|
|
// setup buffers with source data
|
|
skyDomeVertices.reserve(c_numSkyDomeVertices);
|
|
skyDomeIndices.reserve(c_numSkyDomeIndices);
|
|
|
|
// calculate vertices
|
|
float sectionSlice(DEG2RAD(360.0f / (float) c_numSections));
|
|
float ringSlice(DEG2RAD(180.0f / (float) c_numRings));
|
|
for (uint32 a(0); a <= c_numRings; ++a)
|
|
{
|
|
float w(sinf(a * ringSlice));
|
|
float z(cosf(a * ringSlice));
|
|
|
|
for (uint32 i(0); i <= c_numSections; ++i)
|
|
{
|
|
SVF_P3F_C4B_T2F v;
|
|
|
|
float ii(i - a * 0.5f); // Gives better tessellation, requires texture address mode to be "wrap"
|
|
// for u when rendering (see v.st[ 0 ] below). Otherwise set ii = i;
|
|
v.xyz = Vec3(cosf(ii * sectionSlice) * w, sinf(ii * sectionSlice) * w, z);
|
|
assert(fabs(v.xyz.GetLengthSquared() - 1.0) < 1e-2 /*1e-4*/); // because of FP-16 precision
|
|
v.st = Vec2(ii / (float) c_numSections, 2.0f * (float) a / (float) c_numRings);
|
|
skyDomeVertices.push_back(v);
|
|
}
|
|
}
|
|
|
|
// build faces
|
|
for (uint32 a(0); a < c_numRings; ++a)
|
|
{
|
|
for (uint32 i(0); i < c_numSections; ++i)
|
|
{
|
|
skyDomeIndices.push_back((vtx_idx) (a * (c_numSections + 1) + i + 1));
|
|
skyDomeIndices.push_back((vtx_idx) (a * (c_numSections + 1) + i));
|
|
skyDomeIndices.push_back((vtx_idx) ((a + 1) * (c_numSections + 1) + i + 1));
|
|
|
|
skyDomeIndices.push_back((vtx_idx) ((a + 1) * (c_numSections + 1) + i));
|
|
skyDomeIndices.push_back((vtx_idx) ((a + 1) * (c_numSections + 1) + i + 1));
|
|
skyDomeIndices.push_back((vtx_idx) (a * (c_numSections + 1) + i));
|
|
}
|
|
}
|
|
|
|
// sanity checks
|
|
assert(skyDomeVertices.size() == c_numSkyDomeVertices);
|
|
assert(skyDomeIndices.size() == c_numSkyDomeIndices);
|
|
|
|
// create static buffers in renderer
|
|
m_pSkyDomeMesh = gEnv->pRenderer->CreateRenderMeshInitialized(&skyDomeVertices[0], c_numSkyDomeVertices, eVF_P3F_C4B_T2F,
|
|
&skyDomeIndices[0], c_numSkyDomeIndices, prtTriangleList, "SkyHDR", "SkyHDR");
|
|
}
|
|
|
|
void CSkyLightManager::ReleaseSkyDomeMesh()
|
|
{
|
|
m_renderParams.m_pSkyDomeMesh = NULL;
|
|
m_pSkyDomeMesh = NULL;
|
|
}
|
|
|
|
int CSkyLightManager::GetFrontBuffer() const
|
|
{
|
|
assert(m_curBackBuffer >= 0 && m_curBackBuffer <= 1);
|
|
return((m_curBackBuffer + 1) & 1);
|
|
}
|
|
|
|
|
|
int CSkyLightManager::GetBackBuffer() const
|
|
{
|
|
assert(m_curBackBuffer >= 0 && m_curBackBuffer <= 1);
|
|
return(m_curBackBuffer);
|
|
}
|
|
|
|
|
|
void CSkyLightManager::ToggleBuffer()
|
|
{
|
|
assert(m_curBackBuffer >= 0 && m_curBackBuffer <= 1);
|
|
//better enforce cache flushing then making PPU wait til job has been finished
|
|
m_curBackBuffer = (m_curBackBuffer + 1) & 1;
|
|
m_needRenderParamUpdate = 1;
|
|
}
|
|
|
|
void CSkyLightManager::GetMemoryUsage(ICrySizer* pSizer) const
|
|
{
|
|
pSizer->AddObject(this, sizeof(*this));
|
|
pSizer->AddObject(m_pSkyLightNishita);
|
|
pSizer->AddObject(m_skyDomeTextureDataMie[0]);
|
|
pSizer->AddObject(m_skyDomeTextureDataMie[1]);
|
|
pSizer->AddObject(m_skyDomeTextureDataRayleigh[0]);
|
|
pSizer->AddObject(m_skyDomeTextureDataRayleigh[1]);
|
|
}
|