// Copyright (c) 2012-2024 Wojciech Figat. All rights reserved. #include "AtmospherePreCompute.h" #include "Engine/Engine/Engine.h" #include "Engine/Engine/Time.h" #include "Engine/Content/Content.h" #include "Engine/Engine/EngineService.h" #include "Engine/Graphics/GPUContext.h" #include "Engine/Graphics/GPUDevice.h" #include "Engine/Graphics/GPUPipelineState.h" #include "Engine/Graphics/RenderTask.h" #include "Engine/Graphics/Shaders/GPUShader.h" #include "Engine/Graphics/Textures/GPUTexture.h" #include "Engine/Graphics/Textures/TextureData.h" #include "Engine/Platform/Window.h" #include "RendererPass.h" #include "Engine/Threading/ThreadPoolTask.h" #include "Engine/Content/Assets/Shader.h" #include "Engine/Content/AssetReference.h" // Amount of frames to wait for data from atmosphere precompute job #define ATMOSPHERE_PRECOMPUTE_LATENCY_FRAMES 1 const float DensityHeight = 0.5f; const int32 MaxScatteringOrder = 4; const int32 TransmittanceTexWidth = 256; const int32 TransmittanceTexHeight = 64; const int32 IrradianceTexWidth = 64; const int32 IrradianceTexHeight = 16; const int32 InscatterMuNum = 128; const int32 InscatterMuSNum = 32; const int32 InscatterNuNum = 8; const int32 InscatterAltitudeSampleNum = 4; const int32 InscatterWidth = InscatterMuSNum * InscatterNuNum; const int32 InscatterHeight = InscatterMuNum; const int32 InscatterDepth = InscatterAltitudeSampleNum; const static float RadiusScale = 1; const static float RadiusGround = 6360 * RadiusScale; const static float RadiusAtmosphere = 6420 * RadiusScale; class DownloadJob : public ThreadPoolTask { private: GPUTexture* _transmittance; GPUTexture* _irradiance; GPUTexture* _inscatter; public: DownloadJob(GPUTexture* transmittance, GPUTexture* irradiance, GPUTexture* inscatter); protected: // [ThreadPoolTask] bool Run() override; }; PACK_STRUCT(struct Data { float First; float AtmosphereR; int AtmosphereLayer; float Dummy0; Float4 dhdh; }); namespace AtmospherePreComputeImpl { bool _isUpdatePending = false; bool _isReadyForCompute = false; bool _hasDataCached = false; AssetReference _shader; GPUPipelineState* _psTransmittance = nullptr; GPUPipelineState* _psIrradiance1 = nullptr; GPUPipelineState* _psIrradianceN = nullptr; GPUPipelineState* _psCopyIrradiance = nullptr; GPUPipelineState* _psCopyIrradianceAdd = nullptr; GPUPipelineState* _psInscatter1_A = nullptr; GPUPipelineState* _psInscatter1_B = nullptr; GPUPipelineState* _psCopyInscatter1 = nullptr; GPUPipelineState* _psCopyInscatterNAdd = nullptr; GPUPipelineState* _psInscatterS = nullptr; GPUPipelineState* _psInscatterN = nullptr; SceneRenderTask* _task = nullptr; // GPUTexture* AtmosphereTransmittance = nullptr; GPUTexture* AtmosphereIrradiance = nullptr; GPUTexture* AtmosphereInscatter = nullptr; // GPUTexture* AtmosphereDeltaE = nullptr; GPUTexture* AtmosphereDeltaSR = nullptr; GPUTexture* AtmosphereDeltaSM = nullptr; GPUTexture* AtmosphereDeltaJ = nullptr; uint64 _updateFrameNumber; bool _wasCancelled; FORCE_INLINE bool isUpdateSynced() { return _updateFrameNumber > 0 && _updateFrameNumber + ATMOSPHERE_PRECOMPUTE_LATENCY_FRAMES <= Engine::FrameCount; } void onRender(RenderTask* task, GPUContext* context); } using namespace AtmospherePreComputeImpl; class AtmospherePreComputeService : public EngineService { public: AtmospherePreComputeService() : EngineService(TEXT("Atmosphere Pre Compute"), 50) { } void Update() override; void Dispose() override; }; AtmospherePreComputeService AtmospherePreComputeServiceInstance; bool AtmospherePreCompute::GetCache(AtmosphereCache* cache) { if (_hasDataCached) { if (cache) { cache->Transmittance = AtmosphereTransmittance; cache->Irradiance = AtmosphereIrradiance; cache->Inscatter = AtmosphereInscatter; } } else if (_task == nullptr || !_task->Enabled) { _isUpdatePending = true; } return _hasDataCached; } bool init() { if (_isReadyForCompute) return false; LOG(Info, "Starting Atmosphere Pre Compute service"); // Load shader if (_shader == nullptr) { LOG(Warning, "Failed to load AtmospherePreCompute shader!"); return true; } if (_shader->WaitForLoaded()) { LOG(Warning, "Loading AtmospherePreCompute shader timeout!"); return true; } auto shader = _shader->GetShader(); ASSERT(shader->GetCB(0) != nullptr); if (shader->GetCB(0)->GetSize() != sizeof(Data)) { REPORT_INVALID_SHADER_PASS_CB_SIZE(shader, 0, Data); return true; } // Create pipeline stages _psTransmittance = GPUDevice::Instance->CreatePipelineState(); _psIrradiance1 = GPUDevice::Instance->CreatePipelineState(); _psIrradianceN = GPUDevice::Instance->CreatePipelineState(); _psCopyIrradiance = GPUDevice::Instance->CreatePipelineState(); _psCopyIrradianceAdd = GPUDevice::Instance->CreatePipelineState(); _psInscatter1_A = GPUDevice::Instance->CreatePipelineState(); _psInscatter1_B = GPUDevice::Instance->CreatePipelineState(); _psCopyInscatter1 = GPUDevice::Instance->CreatePipelineState(); _psInscatterS = GPUDevice::Instance->CreatePipelineState(); _psInscatterN = GPUDevice::Instance->CreatePipelineState(); _psCopyInscatterNAdd = GPUDevice::Instance->CreatePipelineState(); GPUPipelineState::Description psDesc, psDescLayers; psDesc = GPUPipelineState::Description::DefaultFullscreenTriangle; psDescLayers = psDesc; //psDescLayers.GS = shader->GetGS(0); { psDesc.PS = shader->GetPS("PS_Transmittance"); if (_psTransmittance->Init(psDesc)) return true; } { psDesc.PS = shader->GetPS("PS_Irradiance1"); if (_psIrradiance1->Init(psDesc)) return true; } { psDesc.PS = shader->GetPS("PS_IrradianceN"); if (_psIrradianceN->Init(psDesc)) return true; } { psDesc.PS = shader->GetPS("PS_CopyIrradiance1"); if (_psCopyIrradiance->Init(psDesc)) return true; } { psDescLayers.PS = shader->GetPS("PS_Inscatter1_A"); if (_psInscatter1_A->Init(psDescLayers)) return true; } { psDescLayers.PS = shader->GetPS("PS_Inscatter1_B"); if (_psInscatter1_B->Init(psDescLayers)) return true; } { psDescLayers.PS = shader->GetPS("PS_CopyInscatter1"); if (_psCopyInscatter1->Init(psDescLayers)) return true; } { psDescLayers.PS = shader->GetPS("PS_InscatterS"); if (_psInscatterS->Init(psDescLayers)) return true; } { psDescLayers.PS = shader->GetPS("PS_InscatterN"); if (_psInscatterN->Init(psDescLayers)) return true; } psDescLayers.BlendMode = BlendingMode::Add; psDesc.BlendMode = BlendingMode::Add; { psDescLayers.PS = shader->GetPS("PS_CopyInscatterN"); if (_psCopyInscatterNAdd->Init(psDescLayers)) return true; } { psDesc.PS = shader->GetPS("PS_CopyIrradiance1"); if (_psCopyIrradianceAdd->Init(psDesc)) return true; } // Init rendering pipeline _task = New(); _task->Enabled = false; _task->IsCustomRendering = true; _task->Render.Bind(onRender); // Init render targets AtmosphereTransmittance = GPUDevice::Instance->CreateTexture(TEXT("AtmospherePreCompute.Transmittance")); if (AtmosphereTransmittance->Init(GPUTextureDescription::New2D(TransmittanceTexWidth, TransmittanceTexHeight, PixelFormat::R16G16B16A16_Float, GPUTextureFlags::ShaderResource | GPUTextureFlags::RenderTarget))) return true; AtmosphereIrradiance = GPUDevice::Instance->CreateTexture(TEXT("AtmospherePreCompute.Irradiance")); if (AtmosphereIrradiance->Init(GPUTextureDescription::New2D(IrradianceTexWidth, IrradianceTexHeight, PixelFormat::R16G16B16A16_Float, GPUTextureFlags::ShaderResource | GPUTextureFlags::RenderTarget))) return true; AtmosphereDeltaE = GPUDevice::Instance->CreateTexture(TEXT("AtmospherePreCompute.DeltaE")); if (AtmosphereDeltaE->Init(GPUTextureDescription::New2D(IrradianceTexWidth, IrradianceTexHeight, PixelFormat::R16G16B16A16_Float, GPUTextureFlags::ShaderResource | GPUTextureFlags::RenderTarget))) return true; AtmosphereInscatter = GPUDevice::Instance->CreateTexture(TEXT("AtmospherePreCompute.Inscatter")); if (AtmosphereInscatter->Init(GPUTextureDescription::New3D(InscatterWidth, InscatterHeight, InscatterDepth, PixelFormat::R16G16B16A16_Float, GPUTextureFlags::ShaderResource | GPUTextureFlags::RenderTarget | GPUTextureFlags::PerSliceViews))) return true; AtmosphereDeltaSR = GPUDevice::Instance->CreateTexture(TEXT("AtmospherePreCompute.DeltaSR")); if (AtmosphereDeltaSR->Init(GPUTextureDescription::New3D(InscatterWidth, InscatterHeight, InscatterDepth, PixelFormat::R16G16B16A16_Float, GPUTextureFlags::ShaderResource | GPUTextureFlags::RenderTarget | GPUTextureFlags::PerSliceViews))) return true; AtmosphereDeltaSM = GPUDevice::Instance->CreateTexture(TEXT("AtmospherePreCompute.DeltaSM")); if (AtmosphereDeltaSM->Init(GPUTextureDescription::New3D(InscatterWidth, InscatterHeight, InscatterDepth, PixelFormat::R16G16B16A16_Float, GPUTextureFlags::ShaderResource | GPUTextureFlags::RenderTarget | GPUTextureFlags::PerSliceViews))) return true; AtmosphereDeltaJ = GPUDevice::Instance->CreateTexture(TEXT("AtmospherePreCompute.DeltaJ")); if (AtmosphereDeltaJ->Init(GPUTextureDescription::New3D(InscatterWidth, InscatterHeight, InscatterDepth, PixelFormat::R16G16B16A16_Float, GPUTextureFlags::ShaderResource | GPUTextureFlags::RenderTarget | GPUTextureFlags::PerSliceViews))) return true; // Mark as ready _isReadyForCompute = true; _wasCancelled = false; return false; } void release() { if (!_isReadyForCompute) return; if (_updateFrameNumber != 0) { _wasCancelled = true; } _updateFrameNumber = 0; LOG(Info, "Disposing Atmosphere Pre Compute service"); // Release data SAFE_DELETE_GPU_RESOURCE(_psTransmittance); SAFE_DELETE_GPU_RESOURCE(_psIrradiance1); SAFE_DELETE_GPU_RESOURCE(_psIrradianceN); SAFE_DELETE_GPU_RESOURCE(_psCopyIrradiance); SAFE_DELETE_GPU_RESOURCE(_psCopyIrradianceAdd); SAFE_DELETE_GPU_RESOURCE(_psInscatter1_A); SAFE_DELETE_GPU_RESOURCE(_psInscatter1_B); SAFE_DELETE_GPU_RESOURCE(_psCopyInscatter1); SAFE_DELETE_GPU_RESOURCE(_psInscatterS); SAFE_DELETE_GPU_RESOURCE(_psInscatterN); SAFE_DELETE_GPU_RESOURCE(_psCopyInscatterNAdd); _shader = nullptr; SAFE_DELETE(_task); SAFE_DELETE_GPU_RESOURCE(AtmosphereTransmittance); SAFE_DELETE_GPU_RESOURCE(AtmosphereIrradiance); SAFE_DELETE_GPU_RESOURCE(AtmosphereInscatter); SAFE_DELETE_GPU_RESOURCE(AtmosphereDeltaE); SAFE_DELETE_GPU_RESOURCE(AtmosphereDeltaSR); SAFE_DELETE_GPU_RESOURCE(AtmosphereDeltaSM); SAFE_DELETE_GPU_RESOURCE(AtmosphereDeltaJ); _isReadyForCompute = false; } void AtmospherePreComputeService::Update() { // Calculate time delta since last update auto currentTime = Time::Update.UnscaledTime; // Check if render job is done if (isUpdateSynced()) { // Create async job to gather data from the GPU //ThreadPool::AddJob(New(AtmosphereTransmittance, AtmosphereIrradiance, AtmosphereInscatter)); // TODO: download data from generated cache textures // TODO: serialize cached textures // Clear flags _updateFrameNumber = 0; _isUpdatePending = false; // Release service //release(); // TODO: release service data } else if (_isUpdatePending && (_task == nullptr || !_task->Enabled)) { // TODO: init but without a stalls, just wait for resources loaded and then start rendering // Init service if (!_shader) _shader = Content::LoadAsyncInternal(TEXT("Shaders/AtmospherePreCompute")); if (_shader && !_shader->IsLoaded()) return; if (init()) { LOG(Fatal, "Cannot setup Atmosphere Pre Compute!"); } // Mark task to update _task->Enabled = true; _updateFrameNumber = 0; } } void AtmospherePreComputeService::Dispose() { release(); } void GetLayerValue(int32 layer, float& atmosphereR, Float4& dhdh) { float r = layer / Math::Max(InscatterAltitudeSampleNum - 1.0f, 1.0f); r = r * r; r = Math::Sqrt(RadiusGround * RadiusGround + r * (RadiusAtmosphere * RadiusAtmosphere - RadiusGround * RadiusGround)) + (layer == 0 ? 0.01f : (layer == InscatterAltitudeSampleNum - 1 ? -0.001f : 0.0f)); const float dMin = RadiusAtmosphere - r; const float dMax = Math::Sqrt(r * r - RadiusGround * RadiusGround) + Math::Sqrt(RadiusAtmosphere * RadiusAtmosphere - RadiusGround * RadiusGround); const float dMinP = r - RadiusGround; const float dMaxP = Math::Sqrt(r * r - RadiusGround * RadiusGround); atmosphereR = r; dhdh = Float4(dMin, dMax, dMinP, dMaxP); } void AtmospherePreComputeImpl::onRender(RenderTask* task, GPUContext* context) { // If job has been cancelled (eg. on window close) if (_wasCancelled) { LOG(Warning, "AtmospherePreCompute job cancelled"); return; } ASSERT(_isUpdatePending && _updateFrameNumber == 0); const auto shader = _shader->GetShader(); const auto cb = shader->GetCB(0); Data data; // Compute transmittance texture T (line 1 in algorithm 4.1) context->SetRenderTarget(*AtmosphereTransmittance); context->SetViewportAndScissors((float)TransmittanceTexWidth, (float)TransmittanceTexHeight); context->SetState(_psTransmittance); context->DrawFullscreenTriangle(); context->ResetRenderTarget(); // Compute irradiance texture deltaE (line 2 in algorithm 4.1) context->SetRenderTarget(*AtmosphereDeltaE); context->SetViewportAndScissors((float)IrradianceTexWidth, (float)IrradianceTexHeight); context->BindSR(0, AtmosphereTransmittance); context->SetState(_psIrradiance1); context->DrawFullscreenTriangle(); context->ResetRenderTarget(); // Compute single scattering texture deltaS (line 3 in algorithm 4.1) // Rayleigh and Mie separated in deltaSR + deltaSM context->SetViewportAndScissors((float)InscatterWidth, (float)InscatterHeight); context->SetState(_psInscatter1_A); for (int32 layer = 0; layer < InscatterAltitudeSampleNum; layer++) { GetLayerValue(layer, data.AtmosphereR, data.dhdh); data.AtmosphereLayer = layer; context->UpdateCB(cb, &data); context->BindCB(0, cb); context->SetRenderTarget(AtmosphereDeltaSR->View(layer)); context->DrawFullscreenTriangle(); } context->SetState(_psInscatter1_B); for (int32 layer = 0; layer < InscatterAltitudeSampleNum; layer++) { GetLayerValue(layer, data.AtmosphereR, data.dhdh); data.AtmosphereLayer = layer; context->UpdateCB(cb, &data); context->BindCB(0, cb); context->SetRenderTarget(AtmosphereDeltaSM->View(layer)); context->DrawFullscreenTriangle(); } context->ResetRenderTarget(); //// old way to render Inscatter1 to DeltaSR and DeltaSM at once but didn't work well :/ (no time to find out why) //for (int32 layer = 0; layer < InscatterAltitudeSampleNum; layer++) //{ // GetLayerValue(layer, data.AtmosphereR, data.dhdh); // data.AtmosphereLayer = layer; // cb->SetData(&data); // context->Bind(0, cb); // GPUTextureView* deltaRTs[2] = { AtmosphereDeltaSR->HandleSlice(layer), AtmosphereDeltaSM->HandleSlice(layer) }; // //GPUTextureView* deltaRTs[2] = { AtmosphereIrradiance->Handle(), AtmosphereDeltaE->Handle() }; // context->SetRenderTarget(nullptr, 2, deltaRTs); // //context->SetRenderTarget(AtmosphereIrradiance->View()); // //context->SetRenderTarget(AtmosphereDeltaSR->HandleSlice(layer)); // //context->SetRenderTarget(AtmosphereDeltaSR->View()); // context->DrawFullscreenTriangle(); //} //context->SetRenderTarget(); // Copy deltaE into irradiance texture E (line 4 in algorithm 4.1) /*context->SetRenderTarget(*AtmosphereIrradiance); context->SetViewport(IrradianceTexWidth, IrradianceTexHeight); context->Bind(3, AtmosphereDeltaE); context->SetState(_psCopyIrradiance); context->DrawFullscreenTriangle();*/ // TODO: remove useless CopyIrradiance shader program? context->SetViewportAndScissors((float)IrradianceTexWidth, (float)IrradianceTexHeight); context->Clear(*AtmosphereIrradiance, Color::Transparent); // Copy deltaS into inscatter texture S (line 5 in algorithm 4.1) context->SetViewportAndScissors((float)InscatterWidth, (float)InscatterHeight); context->SetState(_psCopyInscatter1); context->BindSR(4, AtmosphereDeltaSR->ViewVolume()); context->BindSR(5, AtmosphereDeltaSM->ViewVolume()); for (int32 layer = 0; layer < InscatterAltitudeSampleNum; layer++) { GetLayerValue(layer, data.AtmosphereR, data.dhdh); data.AtmosphereLayer = layer; context->UpdateCB(cb, &data); context->BindCB(0, cb); context->SetRenderTarget(AtmosphereInscatter->View(layer)); context->DrawFullscreenTriangle(); } context->ResetRenderTarget(); // Loop for each scattering order (line 6 in algorithm 4.1) for (int32 order = 2; order <= MaxScatteringOrder; order++) { // Compute deltaJ (line 7 in algorithm 4.1) context->UnBindSR(6); context->SetViewportAndScissors((float)InscatterWidth, (float)InscatterHeight); context->SetState(_psInscatterS); data.First = order == 2 ? 1.0f : 0.0f; context->BindSR(0, AtmosphereTransmittance); context->BindSR(3, AtmosphereDeltaE); context->BindSR(4, AtmosphereDeltaSR->ViewVolume()); context->BindSR(5, AtmosphereDeltaSM->ViewVolume()); for (int32 layer = 0; layer < InscatterAltitudeSampleNum; layer++) { GetLayerValue(layer, data.AtmosphereR, data.dhdh); data.AtmosphereLayer = layer; context->UpdateCB(cb, &data); context->BindCB(0, cb); context->SetRenderTarget(AtmosphereDeltaJ->View(layer)); context->DrawFullscreenTriangle(); } // Compute deltaE (line 8 in algorithm 4.1) context->UnBindSR(3); context->SetRenderTarget(AtmosphereDeltaE->View()); context->SetViewportAndScissors((float)IrradianceTexWidth, (float)IrradianceTexHeight); context->BindSR(0, AtmosphereTransmittance); context->BindSR(4, AtmosphereDeltaSR->ViewVolume()); context->BindSR(5, AtmosphereDeltaSM->ViewVolume()); context->SetState(_psIrradianceN); context->DrawFullscreenTriangle(); // Compute deltaS (line 9 in algorithm 4.1) context->UnBindSR(4); context->SetViewportAndScissors((float)InscatterWidth, (float)InscatterHeight); context->SetState(_psInscatterN); context->BindSR(0, AtmosphereTransmittance); context->BindSR(6, AtmosphereDeltaJ->ViewVolume()); for (int32 layer = 0; layer < InscatterAltitudeSampleNum; layer++) { GetLayerValue(layer, data.AtmosphereR, data.dhdh); data.AtmosphereLayer = layer; context->UpdateCB(cb, &data); context->BindCB(0, cb); context->SetRenderTarget(AtmosphereDeltaSR->View(layer)); context->DrawFullscreenTriangle(); } // Add deltaE into irradiance texture E (line 10 in algorithm 4.1) context->SetRenderTarget(*AtmosphereIrradiance); context->SetViewportAndScissors((float)IrradianceTexWidth, (float)IrradianceTexHeight); context->BindSR(3, AtmosphereDeltaE); context->SetState(_psCopyIrradianceAdd); context->DrawFullscreenTriangle(); // Add deltaS into inscatter texture S (line 11 in algorithm 4.1) context->SetViewportAndScissors((float)InscatterWidth, (float)InscatterHeight); context->SetState(_psCopyInscatterNAdd); context->BindSR(4, AtmosphereDeltaSR->ViewVolume()); for (int32 layer = 0; layer < InscatterAltitudeSampleNum; layer++) { GetLayerValue(layer, data.AtmosphereR, data.dhdh); data.AtmosphereLayer = layer; context->UpdateCB(cb, &data); context->BindCB(0, cb); context->SetRenderTarget(AtmosphereInscatter->View(layer)); context->DrawFullscreenTriangle(); } } // Cleanup context->ResetRenderTarget(); context->ResetSR(); // Mark as rendered _hasDataCached = true; _isUpdatePending = false; _updateFrameNumber = Engine::FrameCount; _task->Enabled = false; } DownloadJob::DownloadJob(GPUTexture* transmittance, GPUTexture* irradiance, GPUTexture* inscatter) : _transmittance(transmittance) , _irradiance(irradiance) , _inscatter(inscatter) { } bool DownloadJob::Run() { TextureData textureData; Array data; #if BUILD_DEBUG // Transmittance /*{ if (_transmittance->DownloadData(textureData)) { LOG(Fatal, 193, "AtmospherePreCompute::DownloadJob::Transmittance"); return; } auto in = textureData.Get(0, 0); int rowPich = 3 * TransmittanceTexWidth * sizeof(BYTE); int size = rowPich * TransmittanceTexHeight; data.EnsureCapacity(size, false); byte* p = data.Get(); for (int y = 0; y < TransmittanceTexHeight; y++) { for (int x = 0; x < TransmittanceTexWidth; x++) { Float4 t = ((Half4*)&in->Data[y * in->RowPitch + x * sizeof(Half4)])->ToFloat4(); *p++ = Math::Saturate(t.Z) * 255; *p++ = Math::Saturate(t.Y) * 255; *p++ = Math::Saturate(t.X) * 255; } } FileSystem::SaveBitmapToFile(data.Get(), TransmittanceTexWidth, TransmittanceTexHeight, 24, 0, L"C:\\Users\\Wojtek\\Downloads\\transmittance_Flax.bmp"); }*/ // Irradiance /*{ textureData.Clear(); if (_irradiance->DownloadData(textureData)) { LOG(Fatal, 193, "AtmospherePreCompute::DownloadJob::Irradiance"); return; } auto in = textureData.Get(0, 0); int rowPich = 3 * IrradianceTexWidth * sizeof(BYTE); int size = rowPich * IrradianceTexHeight; data.EnsureCapacity(size, false); byte* p = data.Get(); for (int y = 0; y < IrradianceTexHeight; y++) { for (int x = 0; x < IrradianceTexWidth; x++) { Float4 t = ((Half4*)&in->Data[y * in->RowPitch + x * sizeof(Half4)])->ToFloat4(); *p++ = Math::Saturate(t.Z) * 255; *p++ = Math::Saturate(t.Y) * 255; *p++ = Math::Saturate(t.X) * 255; } } FileSystem::SaveBitmapToFile(data.Get(), IrradianceTexWidth, IrradianceTexHeight, 24, 0, L"C:\\Users\\Wojtek\\Downloads\\irradiance_Flax.bmp"); }*/ /*// Inscatter { textureData.Clear(); if (_inscatter->DownloadData(textureData)) { LOG(Fatal, 193, "AtmospherePreCompute::DownloadJob::Inscatter"); return; } auto in = textureData.Get(0, 0); int rowPich = 3 * InscatterWidth * sizeof(BYTE); int size = rowPich * InscatterHeight; data.EnsureCapacity(size, false); for (int32 depthSlice = 0; depthSlice < InscatterDepth; depthSlice++) { byte* p = data.Get(); byte* s = in->Data.Get() + depthSlice * in->DepthPitch; for (int y = 0; y < InscatterHeight; y++) { for (int x = 0; x < InscatterWidth; x++) { Float4 t = ((Half4*)&s[y * in->RowPitch + x * sizeof(Half4)])->ToFloat4(); *p++ = Math::Saturate(t.Z) * 255; *p++ = Math::Saturate(t.Y) * 255; *p++ = Math::Saturate(t.X) * 255; } } FileSystem::SaveBitmapToFile(data.Get(), InscatterWidth, InscatterHeight, 24, 0, *String::Format(TEXT("C:\\Users\\Wojtek\\Downloads\\inscatter_{0}_Flax.bmp"), depthSlice)); break; } }*/ #else MISSING_CODE("download precomputed atmosphere data from the GPU!"); #endif return false; }