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

UEC++学习(十七)利用SceneCaptureComponent2d进行截图

       

         最近有个需求是需要将场景中的actor进行截图,并且将截图保存成png,png中需要将场景背景忽略掉,只显示特定的actor。

        这里是通过SceneCapture2d组件捕捉场景后,将背景的alpha通道设置为0,实现背景透明的功能。

(一)截图成png

(1)在.h文件

	UFUNCTION(BlueprintCallable)UTexture2D* SaveCaptureScene(bool IsSave, class USceneCaptureComponent2D* SceneCapture, const FString& SavePath, int32 NewWidth = 0, int32 NewHeight = 0, int32 CompressionQuality = 100);//保存为pngbool SaveRenderTargetToDisk(UTextureRenderTarget2D* RenderTarget, const FString& FilePath, int32 NewWidth, int32 NewHeight, int32 CompressionQuality);//设置png的sizevoid ResizeImage(const TArray<FColor>& SourceBitmap, int32 SourceWidth, int32 SourceHeight, int32 TargetWidth, int32 TargetHeight, TArray<FColor>& OutBitmap);//将png转为texture2dUTexture2D* LoadTextureFromFile(const FString& FilePath);

(2)在.cpp文件


#include "Engine/TextureRenderTarget2D.h"
#include "Components/SceneCaptureComponent2D.h"
#include "IImageWrapperModule.h"
#include "IImageWrapper.h"
#include "FileHelper.h"
#include "Paths.h"
#include "Engine/Texture2D.h"
#include "Misc/FileHelper.h"
#include "ImageUtils.h" UTexture2D* YourClass::SaveCaptureScene(const UObject* WorldContextObject, bool IsSave, USceneCaptureComponent2D* SceneCapture, const FString& SavePath, int32 NewWidth, int32 NewHeight, int32 CompressionQuality )
{if (!IsSave){SceneCapture->SetVisibility(false);return nullptr;}if (SceneCapture && SceneCapture->TextureTarget){SceneCapture->SetVisibility(true);// 捕捉场景SceneCapture->CaptureScene();// 将 RenderTarget 保存为 PNG 文件if (SaveRenderTargetToDisk(SceneCapture->TextureTarget, *SavePath,NewWidth,NewHeight,CompressionQuality)){// 导入 PNG 为 UTexture2DUTexture2D* CapturedTexture = LoadTextureFromFile(*SavePath);if (CapturedTexture){// 在此处处理导入的纹理,例如将其应用到材质中UE_LOG(LogTemp, Log, TEXT("LYM_Texture imported successfully."));return CapturedTexture;}else{UE_LOG(LogTemp, Error, TEXT("LYM_Failed to import texture."));return nullptr;}}else{UE_LOG(LogTemp, Error, TEXT("LYM_Failed to SavePNG"));return nullptr;}}UE_LOG(LogTemp, Error, TEXT("LYM_SceneCapture or TextureTarget is invalid."));return nullptr;}bool YourClass::SaveRenderTargetToDisk(UTextureRenderTarget2D* RenderTarget, const FString& FilePath, int32 NewWidth, int32 NewHeight, int32 CompressionQuality)
{FTextureRenderTargetResource* RenderTargetResource = RenderTarget->GameThread_GetRenderTargetResource();if (!RenderTargetResource){return false;}// 创建位图TArray<FColor> Bitmap;if (!RenderTargetResource->ReadPixels(Bitmap)){return false;}int32 Width = RenderTarget->SizeX;int32 Height = RenderTarget->SizeY;// 调整图像大小(如果指定了新的宽度和高度)if (NewWidth > 0 && NewHeight > 0){TArray<FColor> ResizedBitmap;//FImageUtils::ImageResize(Width, Height, Bitmap, NewWidth, NewHeight, ResizedBitmap);ResizeImage(Bitmap, Width, Height, NewWidth, NewHeight, ResizedBitmap);Bitmap = MoveTemp(ResizedBitmap);  // 使用调整后的图像Width = NewWidth;Height = NewHeight;}// 处理位图,设置背景透明for (FColor& Pixel : Bitmap){if (Pixel == FColor::Black) // 假设背景是黑色,或者你可以用特定的颜色进行判断{Pixel.A = 0; // 设置 Alpha 通道为 0,透明UE_LOG(LogTemp, Display, TEXT("LYM_ SetColor = 0"));}}// 压缩为 PNGIImageWrapperModule& ImageWrapperModule = FModuleManager::LoadModuleChecked<IImageWrapperModule>(FName("ImageWrapper"));TSharedPtr<IImageWrapper> ImageWrapper = ImageWrapperModule.CreateImageWrapper(EImageFormat::PNG);if (ImageWrapper.IsValid() && ImageWrapper->SetRaw(Bitmap.GetData(), Bitmap.Num() * sizeof(FColor), Width, Height, ERGBFormat::BGRA, 8)){// 设置压缩质量const TArray<uint8, FDefaultAllocator64> PngData = ImageWrapper->GetCompressed(CompressionQuality);if (FFileHelper::SaveArrayToFile(PngData, *FilePath)){return true;}}return false;
}void YourClass::ResizeImage(const TArray<FColor>& SourceBitmap, int32 SourceWidth, int32 SourceHeight, int32 TargetWidth, int32 TargetHeight, TArray<FColor>& OutBitmap)
{//(1)如果需要填充整个png使用这个//OutBitmap.SetNumUninitialized(TargetWidth * TargetHeight);//float XRatio = static_cast<float>(SourceWidth) / TargetWidth;//float YRatio = static_cast<float>(SourceHeight) / TargetHeight;//for (int32 y = 0; y < TargetHeight; ++y)//{//	for (int32 x = 0; x < TargetWidth; ++x)//	{//		int32 SrcX = FMath::Clamp(static_cast<int32>(x * XRatio), 0, SourceWidth - 1);//		int32 SrcY = FMath::Clamp(static_cast<int32>(y * YRatio), 0, SourceHeight - 1);//		OutBitmap[y * TargetWidth + x] = SourceBitmap[SrcY * SourceWidth + SrcX];//	}//}//(2)如果截图需要居中对齐,用这个// 清空目标位图并初始化为透明OutBitmap.SetNumZeroed(TargetWidth * TargetHeight);// 计算宽高比float SourceAspect = static_cast<float>(SourceWidth) / SourceHeight;float TargetAspect = static_cast<float>(TargetWidth) / TargetHeight;int32 NewWidth, NewHeight;if (SourceAspect > TargetAspect){// 宽度受限NewWidth = TargetWidth;NewHeight = static_cast<int32>(TargetWidth / SourceAspect);}else{// 高度受限NewHeight = TargetHeight;NewWidth = static_cast<int32>(TargetHeight * SourceAspect);}// 调整后的图像居中对齐的偏移量int32 OffsetX = (TargetWidth - NewWidth) / 2;int32 OffsetY = (TargetHeight - NewHeight) / 2;// 转换 TArray<FColor> 为 TArray<FLinearColor>TArray<FLinearColor> SourceData;SourceData.SetNum(SourceWidth * SourceHeight);for (int32 i = 0; i < SourceWidth * SourceHeight; ++i){FColor Color = SourceBitmap[i];SourceData[i] = Color.ReinterpretAsLinear();}// 缩放图像TArray<FLinearColor> ResizedData;ResizedData.SetNum(NewWidth * NewHeight);FImageUtils::ImageResize(SourceWidth, SourceHeight, SourceData, NewWidth, NewHeight, ResizedData);// 转换 TArray<FLinearColor> 回 TArray<FColor>TArray<FColor> ResizedBitmap;ResizedBitmap.SetNum(NewWidth * NewHeight);for (int32 i = 0; i < NewWidth * NewHeight; ++i){ResizedBitmap[i] = ResizedData[i].ToFColor(true);}// 将调整后的图像绘制到目标位图中for (int32 Y = 0; Y < NewHeight; Y++){for (int32 X = 0; X < NewWidth; X++){int32 SourceIndex = Y * NewWidth + X;int32 TargetIndex = (Y + OffsetY) * TargetWidth + (X + OffsetX);OutBitmap[TargetIndex] = ResizedBitmap[SourceIndex];}}
}UTexture2D* YourClass::LoadTextureFromFile(const FString& FilePath)
{TArray<uint8> RawFileData;if (!FFileHelper::LoadFileToArray(RawFileData, *FilePath)){return nullptr; // 文件加载失败}IImageWrapperModule& ImageWrapperModule = FModuleManager::LoadModuleChecked<IImageWrapperModule>(FName("ImageWrapper"));EImageFormat ImageFormat = ImageWrapperModule.DetectImageFormat(RawFileData.GetData(), RawFileData.Num());if (ImageFormat == EImageFormat::Invalid){return nullptr; // 图像格式无效}TSharedPtr<IImageWrapper> ImageWrapper = ImageWrapperModule.CreateImageWrapper(ImageFormat);if (ImageWrapper.IsValid() && ImageWrapper->SetCompressed(RawFileData.GetData(), RawFileData.Num())){TArray<uint8> RawData;if (ImageWrapper->GetRaw(ERGBFormat::BGRA, 8, RawData)){// 创建 UTexture2D 并填充它的数据UTexture2D* Texture = UTexture2D::CreateTransient(ImageWrapper->GetWidth(), ImageWrapper->GetHeight(), PF_B8G8R8A8);if (Texture){void* TextureData = Texture->PlatformData->Mips[0].BulkData.Lock(LOCK_READ_WRITE);//锁定和解锁纹理数据以写入图像内容FMemory::Memcpy(TextureData, RawData.GetData(), RawData.Num());Texture->PlatformData->Mips[0].BulkData.Unlock();//更新纹理资源以使更改生效Texture->UpdateResource();return Texture;}}}return nullptr; // 导入失败
}

(3)在蓝图中添加SceneCapture2d组件:首先如果你不需要这个组件一直捕捉的话,需要将它隐藏并且将自动激活关了,不然会很耗性能

(4)组建一个RT用于捕捉:

(5)将PrimitiveRenderMode设置为UseShowOnlyList,CaptureSource设置为FinalColor(LDR)inRGB

(60设置为UseShowOnlyList后,就需要将你需要截图的特定actor添加到数组中。因为我这个showactors中有很多别的actor,所以需要拿到所有的子类一起添加进去

(7)截图并保存:GetProjectSaveDirectory是项目的save缓存路径,剩下两个是项目content路径和项目路径。

PS:使用这种方法时场景最好是白天的时候,不然可能会出现一坨黑的情况。

(二)实时显示

(1)创建一个RT

(2)如果是需要在ui上一直实时显示的话,就需要基于RT创建一个材质用于ui。

(3)将节点修改为UserInterface(用于ui)和Translucent(可使用透明)

(4)在ui中创建一张image使用这个材质

(5)scenecapture组件使用默认设置即可

(6)UI中可以实时的看到捕捉到的画面

(7)但是这种会将背景也一起捕捉到,如果不需要背景的话,就需要使用UseShowOnlyList方式。将需要显示的actor添加到数组中

(8)材质用再将opacity节点连上,如果不连的话,背景就是纯黑色的

(9)修改完之后运行即可看到ui中只有需要显示的actor,背景就被剔除掉了。

(三)小地图

如果是需要做小地图那种的,就是显示人物在场景中移动,

(1~5) 前五步和第二种实时显示的情况步骤是一样的,

(6)将ProjectionType改为Orthographic,另外一个orthoWidth就是你需要显示的size大小,根据需要设置

(7)修改完之后就能在ui中看到效果

相关文章:

  • 北京网站建设多少钱?
  • 辽宁网页制作哪家好_网站建设
  • 高端品牌网站建设_汉中网站制作
  • 使用python对股票市场进行数据挖掘的书籍资料有哪些
  • 基于Python的自然语言处理系列(3):GloVe
  • “声”临其境:iKF Ultra 降噪耳机,音乐与静谧的完美融合
  • 2024国赛数学建模C题论文:基于优化模型的农作物的种植策略
  • 安防监控/视频汇聚平台EasyCVR无法启动并报错“error while loading shared libraries”,如何解决?
  • Ribbon快速了解
  • 如何在5个步骤中编写更好的ChatGPT提示
  • React Native防止重复点击
  • Prompt - 将图片的表格转换成Markdown
  • Django学习实战篇三(适合略有基础的新手小白学习)(从0开发项目)
  • 关于Hadoop重新格式化之后集群的崩溃问题
  • 服务器禁用远程(22)
  • 龙良曲pytorch笔记14-33
  • vc-align源码分析 -- ant-design-vue系列
  • RHEL 7 安装配置( Linux 网络操作系统 02)
  • 【347天】每日项目总结系列085(2018.01.18)
  • Angular js 常用指令ng-if、ng-class、ng-option、ng-value、ng-click是如何使用的?
  • Babel配置的不完全指南
  • CSS 专业技巧
  • Invalidate和postInvalidate的区别
  • JS正则表达式精简教程(JavaScript RegExp 对象)
  • k8s 面向应用开发者的基础命令
  • PHP CLI应用的调试原理
  • SegmentFault 社区上线小程序开发频道,助力小程序开发者生态
  • spring-boot List转Page
  • SSH 免密登录
  • 阿里云爬虫风险管理产品商业化,为云端流量保驾护航
  • 基于游标的分页接口实现
  • 爬虫模拟登陆 SegmentFault
  • 如何利用MongoDB打造TOP榜小程序
  • 使用Envoy 作Sidecar Proxy的微服务模式-4.Prometheus的指标收集
  • 数据可视化之 Sankey 桑基图的实现
  • 与 ConTeXt MkIV 官方文档的接驳
  • 正则与JS中的正则
  • mysql面试题分组并合并列
  • ​学习笔记——动态路由——IS-IS中间系统到中间系统(报文/TLV)​
  • # Redis 入门到精通(九)-- 主从复制(1)
  • #LLM入门|Prompt#2.3_对查询任务进行分类|意图分析_Classification
  • #我与Java虚拟机的故事#连载06:收获颇多的经典之作
  • #我与Java虚拟机的故事#连载19:等我技术变强了,我会去看你的 ​
  • #周末课堂# 【Linux + JVM + Mysql高级性能优化班】(火热报名中~~~)
  • (1) caustics\
  • (C11) 泛型表达式
  • (Charles)如何抓取手机http的报文
  • (LeetCode C++)盛最多水的容器
  • (附源码)apringboot计算机专业大学生就业指南 毕业设计061355
  • (含react-draggable库以及相关BUG如何解决)固定在左上方某盒子内(如按钮)添加可拖动功能,使用react hook语法实现
  • (每日持续更新)jdk api之FileFilter基础、应用、实战
  • (转)Linux整合apache和tomcat构建Web服务器
  • (转载)CentOS查看系统信息|CentOS查看命令
  • (转载)虚函数剖析
  • .bat批处理(七):PC端从手机内复制文件到本地
  • .bat批处理出现中文乱码的情况
  • .h头文件 .lib动态链接库文件 .dll 动态链接库
  • .net core webapi 大文件上传到wwwroot文件夹