Files

397 lines
11 KiB
C++
Raw Permalink Normal View History

2025-05-13 02:46:21 +03:00
#ifndef Sky_h__
#define Sky_h__
#include "Globals.h"
#include "DayTime.h"
#include "math/LifeMath.h"
namespace LifeGraphics
{
struct SkyVertex
{
LifeMath::float3 Position;
LifeMath::float2 Texcoord;
SkyVertex()
{
}
SkyVertex(LifeMath::float3& _pos, LifeMath::float2& _tex)
{
Position = _pos;
Texcoord = _tex;
}
};
struct cbRenderData
{
LifeMath::float4x4 View;
LifeMath::float4x4 Proj;
LifeMath::float4x4 ViewProj;
LifeMath::float3 SunDirection;
float dummy;
};
class Sky
{
private:
int DomeN;
int DVSize;
int DISize;
int MrlTexSize;
int MrlTexSizeHalf;
LifeMath::float3 SunrizeDirection;
LifeMath::float3 ZenithDirection;
LifeMath::float3 waveLengths;
LifeMath::float3 invWaveLengths;
LifeMath::float3 waveLengthsMie;
IVertexBuffer* skyVb;
IIndexBuffer* skyIb;
IShader* VS_Render;
IShader* VS_Update;
IShader* PS_Render;
IShader* PS_Update;
IConstantBuffer* cbUpdate;
IConstantBuffer* cbRender;
ITexture2D* skyDepth;
IInputLayout* vertexLayout;
DayTime Time;
DayTime SunsetTime;
DayTime SunrizeTime;
DayTime MidDayTime;
DayTime MidNightTime;
DayTime DayLength;
DayTime NightLength;
DayTime TimeStep;
int DayPhase;
float fExposure;
float updateTimeElapsed;
float StarsIntensity;
float updatePeriod;
bool ShouldUpdateData;
IDepthStencilState* dsZEnable;
IDepthStencilState* dsZDisable;
void RecomputeData()
{
SunrizeDirection = LifeMath::float3(-1,0,0);
ZenithDirection = LifeMath::float3(0,1,0);
DayLength = SunsetTime - SunrizeTime;
NightLength = SunrizeTime - SunsetTime;
MidNightTime = SunsetTime + (NightLength / 2);
MidDayTime = SunrizeTime + (DayLength / 2);
if (Time >= SunrizeTime && Time <= MidDayTime)
DayPhase = 0;
else if (Time >= MidDayTime && Time <= SunsetTime)
DayPhase = 1;
else if (MidNightTime - SunsetTime >= Time - SunsetTime)
DayPhase = 2;
else
DayPhase = 3;
float lamt = 0;
switch (DayPhase)
{
case 0:
lamt = (float)(Time - SunrizeTime).ToSeconds()
/ (float)(MidDayTime - SunrizeTime).ToSeconds();
SunDirection = Vec3Lerp(SunrizeDirection, ZenithDirection, lamt);
LifeMath::Vec3Normalize(&SunDirection);
StarsIntensity = 1.0f - lamt;
break;
case 1:
lamt = (float)(Time - MidDayTime).ToSeconds()
/ (float)(SunsetTime - MidDayTime).ToSeconds();
SunDirection = Vec3Lerp(ZenithDirection, -SunrizeDirection, lamt);
LifeMath::Vec3Normalize(&SunDirection);
StarsIntensity = lamt;
break;
case 2:
lamt = (float)(Time - SunsetTime).ToSeconds()
/ (float)(MidNightTime - SunsetTime).ToSeconds();
SunDirection = Vec3Lerp(-SunrizeDirection, -ZenithDirection, lamt);
LifeMath::Vec3Normalize(&SunDirection);
StarsIntensity = 1.0f;
break;
case 3:
lamt = (float)(Time - MidNightTime).ToSeconds()
/ (float)(SunrizeTime - MidNightTime).ToSeconds();
SunDirection = Vec3Lerp(-ZenithDirection, SunrizeDirection, lamt);
LifeMath::Vec3Normalize(&SunDirection);
StarsIntensity = 1.0f;
break;
}
StarsIntensity = pow(StarsIntensity,10);
invWaveLengths.X = 1.0f / pow(waveLengths.X, 4.0f);
invWaveLengths.Y = 1.0f / pow(waveLengths.Y, 4.0f);
invWaveLengths.Z = 1.0f / pow(waveLengths.Z, 4.0f);
waveLengthsMie.X = pow(waveLengths.X, -0.84f);
waveLengthsMie.Y = pow(waveLengths.Y, -0.84f);
waveLengthsMie.Z = pow(waveLengths.Z, -0.84f);
}
void GenerateDome()
{
int Latitude = DomeN / 2;
int Longitude = DomeN;
DVSize = Longitude * Latitude;
DISize = (Longitude - 1) * (Latitude - 1) * 2;
DVSize *= 2;
DISize *= 2;
IInputElement skyElements[] =
{
IInputElement("POSITION", 0, 0, FORMAT_R32G32B32_FLOAT, 0, 0, 0),
IInputElement("TEXCOORD", 0, 12, FORMAT_R32G32_FLOAT, 0, 0, 0),
};
vertexLayout = CG::me()->GetRenderer()->CreateInputLayout(skyElements, 2, VS_Render);
SkyVertex* domeVerts = new SkyVertex[DVSize];
// Fill Vertex Buffer
int DomeIndex = 0;
for (int i = 0; i < Longitude; i++)
{
float MoveXZ = 100.0f * ((float)i / (float)(Longitude - 1)) * 3.141592f / 180.0f;
for (int j = 0; j < Latitude; j++)
{
float MoveY = 3.1415f * (float)j / (float)(Latitude - 1);
domeVerts[DomeIndex] = SkyVertex();
domeVerts[DomeIndex].Position.X = (sin(MoveXZ) * cos(MoveY));
domeVerts[DomeIndex].Position.Y = cos(MoveXZ);
domeVerts[DomeIndex].Position.Z = (sin(MoveXZ) * sin(MoveY));
domeVerts[DomeIndex].Position *= 10.0f;
domeVerts[DomeIndex].Texcoord.X = 0.5f / (float)Longitude + (float)i / (float)Longitude;
domeVerts[DomeIndex].Texcoord.Y = 0.5f / (float)Latitude + (float)j / (float)Latitude;
DomeIndex++;
}
}
for (int i = 0; i < Longitude; i++)
{
float MoveXZ = 100.0f * ((float)i / (float)(Longitude - 1)) * 3.1415f / 180.0f;
for (int j = 0; j < Latitude; j++)
{
float MoveY = (3.1415f * 2.0f) - (3.1415f * (float)j / (float)(Latitude - 1));
domeVerts[DomeIndex] = SkyVertex();
domeVerts[DomeIndex].Position.X = (sin(MoveXZ) * cos(MoveY));
domeVerts[DomeIndex].Position.Y = cos(MoveXZ);
domeVerts[DomeIndex].Position.Z = (sin(MoveXZ) * sin(MoveY));
domeVerts[DomeIndex].Position *= 10.0f;
domeVerts[DomeIndex].Texcoord.X = 0.5f / (float)Longitude + (float)i / (float)Longitude;
domeVerts[DomeIndex].Texcoord.Y = 0.5f / (float)Latitude + (float)j / (float)Latitude;
DomeIndex++;
}
}
// Fill index buffer
USHORT* ib = new USHORT[DISize * 3];
int index = 0;
for (USHORT i = 0; i < Longitude - 1; i++)
{
for (short j = 0; j < Latitude - 1; j++)
{
ib[index++] = (USHORT)(i * Latitude + j);
ib[index++] = (USHORT)((i + 1) * Latitude + j);
ib[index++] = (USHORT)((i + 1) * Latitude + j + 1);
ib[index++] = (USHORT)((i + 1) * Latitude + j + 1);
ib[index++] = (USHORT)(i * Latitude + j + 1);
ib[index++] = (USHORT)(i * Latitude + j);
}
}
USHORT Offset = (USHORT)(Latitude * Longitude);
for (USHORT i = 0; i < Longitude - 1; i++)
{
for (USHORT j = 0; j < Latitude - 1; j++)
{
ib[index++] = (USHORT)(Offset + i * Latitude + j);
ib[index++] = (USHORT)(Offset + (i + 1) * Latitude + j + 1);
ib[index++] = (USHORT)(Offset + (i + 1) * Latitude + j);
ib[index++] = (USHORT)(Offset + i * Latitude + j + 1);
ib[index++] = (USHORT)(Offset + (i + 1) * Latitude + j + 1);
ib[index++] = (USHORT)(Offset + i * Latitude + j);
}
}
skyIb = CG::me()->GetRenderer()->CreateIndexBuffer(DISize * 3 * sizeof(USHORT), ib, TRUE);
skyVb = CG::me()->GetRenderer()->CreateVertexBuffer(DVSize * sizeof(SkyVertex), domeVerts, sizeof(SkyVertex));
}
public:
LifeMath::float3 SunDirection;
ITexture2D* mieTex;
ITexture2D* rayleighTex;
Sky()
:DayPhase(0), fExposure(-2.0f), updateTimeElapsed(0.0f), updatePeriod(2.0f), ShouldUpdateData(true)
{
waveLengths = LifeMath::float3(0.65f, 0.57f, 0.475f);
TimeStep = DayTime(0, 0, 1);
}
~Sky()
{
SAFE_DELETE(skyVb);
SAFE_DELETE(skyIb);
SAFE_DELETE(cbUpdate);
SAFE_DELETE(cbRender);
SAFE_DELETE(dsZDisable);
SAFE_DELETE(dsZEnable);
SAFE_DELETE(vertexLayout);
SAFE_DELETE(VS_Render);
// SAFE_DELETE(VS_Update);
SAFE_DELETE(PS_Render);
SAFE_DELETE(PS_Update);
SAFE_DELETE(mieTex);
SAFE_DELETE(rayleighTex);
SAFE_DELETE(skyDepth);
}
void Build()
{
cbUpdate = CG::me()->GetRenderer()->CreateConstantBuffer(sizeof(LifeMath::float4));
cbRender = CG::me()->GetRenderer()->CreateConstantBuffer(sizeof(cbRenderData));
dsZDisable = CG::me()->GetRenderer()->CreateDSState(false,false, COMPARISON_NEVER);
dsZEnable = CG::me()->GetRenderer()->CreateDSState(true,true, COMPARISON_LESS);
VS_Render = ResourceManager::me()->CompileUserShaderVS(String(_text("Sky.vsh")));
PS_Render = ResourceManager::me()->CompileUserShaderPS(String(_text("Sky_render.psh")));
PS_Update = ResourceManager::me()->CompileUserShaderPS(String(_text("Sky_update.psh")));
SunsetTime = DayTime(18, 2, 0);
SunrizeTime = DayTime(7, 10, 0);
Time = DayTime(12, 40, 0);
RecomputeData();
MrlTexSize = 512;
MrlTexSizeHalf = MrlTexSize / 2;
DomeN = 32;
GenerateDome();
mieTex = CG::me()->GetRenderer()->CreateRenderTarget(MrlTexSize, MrlTexSizeHalf, FORMAT_R16G16B16A16_FLOAT);
rayleighTex = CG::me()->GetRenderer()->CreateRenderTarget(MrlTexSize, MrlTexSizeHalf, FORMAT_R16G16B16A16_FLOAT);
skyDepth = CG::me()->GetRenderer()->CreateDepthTarget(MrlTexSize, MrlTexSizeHalf, FORMAT_D16_UNORM);
//g_screenQuad = new Quad(0, 0, MrlTexSize, MrlTexSizeHalf, true);
}
void SetDayTime(const DayTime& time)
{
Time = time;
RecomputeData();
}
void Update(float dt)
{
updateTimeElapsed += dt;
if (updateTimeElapsed >= updatePeriod)
{
updateTimeElapsed = 0.0f;
ShouldUpdateData = true;
}
Time.Add((int)(dt * (float)TimeStep.ToSeconds()));
RecomputeData();
}
void Render(ITexture2D* sceneRt, ITexture2D* sceneDt, Camera* cam)
{
IRenderer* renderer = CG::me()->GetRenderer();
renderer->SetDepthStencilState(dsZDisable);
//Update mie and rayleigh textures
if (ShouldUpdateData)
{
ITexture2D* tex[] = {rayleighTex, mieTex};
renderer->SetTargets(tex, 2, skyDepth);
renderer->SetPixelShader(PS_Update);
cbUpdate->Update(&SunDirection, sizeof(SunDirection));
renderer->SetPSConstantBuffer(cbUpdate, 1);
renderer->SetViewport(LifeMath::Rect2i(0,0, MrlTexSize, MrlTexSizeHalf));
renderer->RenderToTextureNoPs();
renderer->UnbindRenderTargets();
ShouldUpdateData = false;
}
renderer->SetViewport(LifeMath::Rect2i(0,0, sceneRt->Width(), sceneRt->Height()));
renderer->SetVertexShader(VS_Render);
renderer->SetPixelShader(PS_Render);
float starIntensity = 0.7f;
float g = -0.990f;
float g2 = (-0.990f) * (-0.990f);
LifeMath::float4x4 sm;
Mat4x4LookAtLH(LifeMath::float3(0, 0, 0), cam->getDirection(), LifeMath::float3(0, 1, 0), sm);
cbRenderData data;
data.View = sm;
data.Proj = cam->getProj();
data.ViewProj = data.View * data.Proj;
data.SunDirection = SunDirection;
cbRender->Update(&data, sizeof(cbRenderData));
renderer->SetPSConstantBuffer(cbRender, 1);
renderer->SetVSConstantBuffer(cbRender, 1);
renderer->SetTargets(sceneRt, sceneDt);
////PS_Render.SetFloat("fExposure", fExposure);
////PS_Render.SetFloat("starIntensity", StarsIntensity);
////PS_Render.SetTexture("starSampler", starTex);
////PS_Render.SetTexture("mieSampler", mieTex);
////PS_Render.SetTexture("rayleighSampler", rayleighTex);
/*CG.Engine.Renderer.SetTextureFilterMin(TextureFilter.Point, 2);
CG.Engine.Renderer.SetTextureFilterMip(TextureFilter.Point, 2);
CG.Engine.Renderer.SetTextureFilterMag(TextureFilter.Point, 2);*/
////PS_Render.SetFloat3("v3SunDir", SunDirection);
renderer->SetPSTexture(rayleighTex,0);
renderer->SetPSTexture(mieTex,1);
////VS_Render.SetMatrix("matWorld", Matrix4x4.Identity);
////VS_Render.SetMatrix("matView", sm);
////VS_Render.SetMatrix("matProjection", renderer.MainCamera.ProjectionMatrix);
renderer->SetInputLayout(vertexLayout);
renderer->SetIndexBuffer(skyIb);
renderer->SetVertexBuffer(skyVb);
renderer->SetPrimitiveTopology(PT_TRIANGLE_LIST);
renderer->DrawIndexed(0, DISize);
// renderer.DrawIndexedPrimitives(IPrimitiveTopology.TriangleList, 0, 0, SkyGeometry.NumberVertices, SkyGeometry.Subsets[0].Offset, SkyGeometry.Subsets[0].FaceCount);
renderer->SetDepthStencilState(dsZEnable);
}
};
}
#endif // Sky_h__