当前位置: 首页 > news >正文

UE输出场景2D全景图和2D全景深度图

1 准备

创建一个立方体渲染目标,使用USceneCaptureComponentCube组件,捕获全景图使用SCS_FinalColorHDR或者SCS_FinalColorLDR,捕获深度使用SCS_SceneDepth,然后通过引擎的CubemapHelpers::GenerateLongLatUnwrap函数来把渲染纹理转成2d全景图。

2 说明

捕获深度时候,纹理我使用的PF_A32B32G32R32F这个精度够也支持在Linux平台使用,其他格式可以自行探索,RenderTarget->OverrideFormat = PF_FloatRGBA;这一步代码是为了GenerateLongLatUnwrap函数能够正常处理成16位的结果。

3 代码

1 捕获2d全景图
// ===== 1. 捕获 RGB HDR ===== SceneCapture->CaptureSource = ESceneCaptureSource::SCS_FinalColorHDR; RenderTarget->Init(FaceSize, PF_FloatRGBA); SceneCapture->CaptureScene(); FlushRenderingCommands(); TArray64<uint8> RGBRawData; FIntPoint RGBOutputSize; EPixelFormat RGBOutputFormat; bool bSuccess = CubemapHelpers::GenerateLongLatUnwrap(RenderTarget, RGBRawData, RGBOutputSize, RGBOutputFormat); if (!bSuccess) { UE_LOG(LogTemp, Error, TEXT("RGB GenerateLongLatUnwrap Error")); return; } TArray<FLinearColor> ExrPixels; int32 RGBPixelCount = RGBOutputSize.X * RGBOutputSize.Y; ExrPixels.SetNum(RGBPixelCount); for (int32 i = 0; i < RGBPixelCount; i++) { const uint8* PixelPtr = &RGBRawData[i * 8]; FFloat16 R16, G16, B16, A16; FMemory::Memcpy(&R16, PixelPtr, 2); FMemory::Memcpy(&G16, PixelPtr + 2, 2); FMemory::Memcpy(&B16, PixelPtr + 4, 2); ExrPixels[i] = FLinearColor(R16.GetFloat(), G16.GetFloat(), B16.GetFloat(), 1.0f); } { TArray<FColor> Pixels; Pixels.SetNum(ExrPixels.Num()); for (int i = 0; i < ExrPixels.Num(); i++) { Pixels[i] = ExrPixels[i].ToFColor(true); } TArray64<uint8> PNGData; FImageUtils::PNGCompressImageArray(RGBOutputSize.X, RGBOutputSize.Y, Pixels, PNGData); FFileHelper::SaveArrayToFile(PNGData, *SaveRGBFilePath); }
2 捕获2d全景深度图
// ===== 捕获深度 ===== UTextureRenderTargetCube* RenderTarget = SceneCapture->TextureTarget; const int32 FaceSize = width; SceneCapture->CaptureSource = ESceneCaptureSource::SCS_SceneDepth; RenderTarget->Init(FaceSize, PF_A32B32G32R32F); SceneCapture->CaptureScene(); FlushRenderingCommands(); // 这一步代码是为了GenerateLongLatUnwrap函数能够正常处理成16位的结果 RenderTarget->OverrideFormat = PF_FloatRGBA; TArray64<uint8> DepthRawData; FIntPoint DepthOutputSize; EPixelFormat DepthOutputFormat; bSuccess = CubemapHelpers::GenerateLongLatUnwrap(RenderTarget, DepthRawData, DepthOutputSize, DepthOutputFormat); if (!bSuccess) { UE_LOG(LogTemp, Error, TEXT("Depth GenerateLongLatUnwrap Error")); return; } TArray<float> SceneDepths; int32 DepthPixelCount = DepthOutputSize.X * DepthOutputSize.Y; SceneDepths.SetNum(DepthPixelCount); float MaxClipDepth = 65504; for (int32 i = 0; i < DepthPixelCount; i++) { const uint8* PixelPtr = &DepthRawData[i * 8]; FFloat16 R16; FMemory::Memcpy(&R16, PixelPtr, 2); float Depth = R16.GetFloat(); if (Depth > 0.0f && Depth < MaxClipDepth) { SceneDepths[i] = Depth; } else { SceneDepths[i] = 0; } } { float MinDepth = TNumericLimits<float>::Max(); float MaxDepth = TNumericLimits<float>::Min(); for (int32 i = 0; i < SceneDepths.Num(); ++i) { float Depth = SceneDepths[i]; if (Depth > 0) { MinDepth = FMath::Min(MinDepth, Depth); MaxDepth = FMath::Max(MaxDepth, Depth); } } float DepthRange = MaxDepth - MinDepth; if (DepthRange <= KINDA_SMALL_NUMBER) DepthRange = 1.0f; TArray<uint16> DepthData16; DepthData16.SetNumUninitialized(SceneDepths.Num()); for (int32 i = 0; i < SceneDepths.Num(); ++i) { float Depth = SceneDepths[i]; float NormalizedDepth = (Depth - MinDepth) / DepthRange; NormalizedDepth = FMath::Clamp(NormalizedDepth, 0.0f, 1.0f); float FlippedDepth = 1.0f - NormalizedDepth; DepthData16[i] = static_cast<uint16>(FlippedDepth * MAX_uint16); } IImageWrapperModule& ImageWrapperModule = FModuleManager::LoadModuleChecked<IImageWrapperModule>(FName("ImageWrapper")); TSharedPtr<IImageWrapper> ImageWrapper = ImageWrapperModule.CreateImageWrapper(EImageFormat::PNG); if (ImageWrapper->SetRaw(DepthData16.GetData(), DepthData16.GetAllocatedSize(), DepthOutputSize.X, DepthOutputSize.Y, ERGBFormat::Gray, 16)) { TArray64<uint8> CompressedData = ImageWrapper->GetCompressed(); if (CompressedData.Num() > 0) { FFileHelper::SaveArrayToFile(CompressedData, *SaveDepthFilePath); } } else { UE_LOG(LogTemp, Error, TEXT("Depth SetRaw failed")); } }

4 效果图

2d全景图

2d全景图深度图

http://www.jsqmd.com/news/824306/

相关文章:

  • 7th grade math (2026.05.15)Binary Linear Equation Group
  • 告别手动写公式:ChatGPT生成VLOOKUP、XLOOKUP、动态数组公式的7种精准提示词(附可复制模板)
  • 谷歌Android重大更新!底层植入Gemini,苹果已掉队
  • 前端FFmpeg实战:从零构建浏览器内视频压缩工具
  • 2026 登封市搏击馆哪家好?本地内行带路与避坑考察 - 资讯速览
  • LunaTranslator终极指南:如何免费快速实现Galgame实时翻译
  • 神经中枢:输出解析器,搭建文本与数据的桥梁
  • 深度学习之感知机详解
  • Zotero文献去重终极指南:快速清理重复文献的高效解决方案
  • 少儿AI英语背单词APP的开发
  • 从零分到满分:DeepSeek在高考物理计算题中的7次迭代优化全过程(含中间态输出与公式链校验日志)
  • 2026广州版权代理机构TOP5|众致9年深耕,版权登记、维权一站式护航,补贴精准申领 - 资讯速览
  • 深度学习之MLP与反向传播算法详解
  • 网盘直链获取神器LinkSwift:告别繁琐下载,开启高效文件管理新时代
  • 终极指南:Translumo实时屏幕翻译器如何打破游戏与视频的语言壁垒
  • 7th grade math (2026.05.15)
  • 【RL】RL Grokking Recipe: How Does RL Unlock and Transfer New Algorithms in LLMs?
  • 如何30分钟搞定Obsidian Copilot:新手快速安装与智能笔记配置指南
  • 别再死磕传统FWI了!用PyTorch+OpenFWI数据集,5分钟跑通你的第一个DL-FWI模型
  • v8时代审美淘汰倒计时:掌握这5个动态权重调节技巧,避免90%创作者正在经历的风格失语症
  • AI技术开发知识库系统
  • Dify插件标准化封装:从脚本到可分发Python包的工程实践
  • 0基础该如何转行网络安全?值得吗?我将从6个方面带你学一笔经济账
  • ROS2导航避坑指南:手把手教你用Nav2和Cartographer保存、加载地图并实现精准定位
  • 2025ccpc南昌补题笔记(前六题)
  • 【信息科学与工程学】【物理/化学和工程科学】第三十九篇 工程力学02
  • Unity云资源分发(CCD)从入门到放弃?这些命令行(CLI)技巧让你效率翻倍
  • CircuitPython硬件通信接口实战:SPI、UART、I2C与HID引脚验证与应用
  • Teamcenter 第一个节点自动审批完成 - 张永全
  • 极简主义提示工程白皮书(含Adobe+Midjourney双平台对照表|限免领取倒计时48h)