#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__