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中看到效果

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/423526.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

计算机网络:概述 - 计算机网络概述

目录 一. 互联网概述 1.1 网络 1.2 互联网 1.3 因特网 二. 互联网发展的三个阶段 三. 互联网的标准化工作 四. 互联网的组成 五. 计算机网络的类别 5.1 计算机网络的定义 5.2 计算机网络的不同类别 一. 互联网概述 起源于美国的互联网现如今已…

力扣213-打家劫舍 II(Java详细题解)

题目链接&#xff1a;213. 打家劫舍 II - 力扣&#xff08;LeetCode&#xff09; 前情提要&#xff1a; 本体是打家劫舍的一个变形题&#xff0c;希望大家能先做198. 打家劫舍 - 力扣&#xff08;LeetCode&#xff09;&#xff0c;并看一下我上题的讲解力扣198-打家劫舍&…

sheng的学习笔记-AI-规则学习(rule learning)

AI目录&#xff1a;sheng的学习笔记-AI目录-CSDN博客 什么是规则学习 机器学习中的“规则”(rule)通常是指语义明确、能描述数据分布所隐含的客观规律或领域概念、可写成“若……&#xff0c;则……”形式的逻辑规则。​“规则学习”(rule learning)是从训练数据中学习出一组能…

Lua发邮件:实现自动化邮件发送教程指南!

Lua发邮件高级技巧有哪些&#xff1f;如何利用Lua发送电子邮件&#xff1f; 自动化邮件发送是一个非常实用的功能&#xff0c;广泛应用于各种场景&#xff0c;如通知、提醒、报告生成等。Lua作为一种轻量级脚本语言&#xff0c;因其简洁和高效而受到广泛欢迎。AokSend将详细介…

OpenCV class2-C#+winfrom显示控件使用窗口大小并内存管理

一.控件效果说明 二.代码声明&#xff08;已经循环读取10000次&#xff09; 全局 OpenCvSharp.Point point new OpenCvSharp.Point(0, 0); OpenCvSharp.Size size2; Mat src new Mat(); 初始化 size2 new OpenCvSharp.Size(pictureBox1.Size.Width, pictureBox1.Size.Hei…

PHP智驭未来悦享生活智慧小区物业管理小程序系统源码

智驭未来&#xff0c;悦享生活 —— 探索智慧小区物业管理小程序 一、引言&#xff1a;智慧生活的新篇章 在这个日新月异的时代&#xff0c;科技正以前所未有的速度改变着我们的生活。从智能家居到智慧城市&#xff0c;每一处都闪耀着智慧的光芒。而今天&#xff0c;我要带大家…

elementui Cascader 级联选择器的使用总结

实现效果 技术要点总结如下&#xff1a; 1、点击添加自动增加多行&#xff0c;实现自主选择增加多条节点数据 2、节点地址使用的是Cascader 级联选择器&#xff0c;需要动态生成&#xff0c;涉及到一个技术要点是&#xff1a;因v-modal只能获取value不能获取label&#xff0c;故…

汇编实现从1加到1000(《X86汇编语言 从实模式到保护模式(第2版》) 第135页第2题解答)

题目: 编写一段主引导扇区程序,计算从1加到1000的和,并在屏幕上显示结果 输出结果: 代码: jmp near start text db 123...1000 start:mov ax,0x07c0mov ds,ax ;数据段从主引导区开始mov ax,0xb800mov es,ax ;显存地址从B8000物理地址开始mov si,text ;si指向text的第…

极狐GitLab 新一代容器镜像仓库正式上线啦!

从极狐GitLab 17.3 开始&#xff0c;私有化部署实例也可以使用新一代容器镜像仓库啦&#xff01;新一代容器镜像仓库具有更高效的零宕机垃圾收集功能和其他优势。 从去年开始&#xff0c;极狐GitLab 就启动了重构容器镜像仓库的计划&#xff0c;用以构建具有更强功能的镜像仓库…

什么是测试驱动开发?

测试驱动开发&#xff08;Test-Driven Development&#xff0c;简称TDD&#xff09;是一种软件开发方法&#xff0c;它强调在编写功能代码之前&#xff0c;先编写测试代码。这种方法的核心思想是通过测试来推动整个开发过程的进行&#xff0c;确保代码的质量和可维护性。 一、基…

Hibernate QueryPlanCache 查询计划缓存引发的内存溢出

目录 1.排查方式2.结论3.解决办法 前言&#xff1a;在生产环境中有一个后端程序多次报oom然后导致程序中断。 1.排查方式 通过下载后端程序产生的oom文件&#xff0c;将oom文件导入MemoryAnalyzer程序分析程序堆内存使用情况。 1、将oom文件导入MemoryAnalyzer后可以看到概览信…

玩转扩展库,温湿度传感器篇!—合宙Air201资产定位模组LuatOS快速入门05

随着LuatOS快速入门系列教程的推出&#xff0c;小伙伴们学习热情高涨。 合宙Air201不仅支持三种定位方式&#xff0c;还具有丰富的扩展功能&#xff0c;通过外扩BTB链接方案&#xff0c;最多可支持21个IO接口&#xff1a;SPI、I2C、UART等多种接口全部支持。 本期&#xff0c…

uniapp小程序富文本编辑器 简单不需要下载插件 复制代码直接复用

题外话&#xff1a;富文本编辑器搞了好久&#xff0c;下载好几个插件&#xff0c;都没成功&#xff0c;最后复制这篇文章的代码&#xff0c;我又修改了一点东西&#xff0c;就成功了&#xff1a;&#xff08;买下面的css文件还花了2块钱&#xff0c;现在我免费给大家&#xff0…

STM32常用数据采集滤波算法

例如&#xff0c;STM32进行滤波处理时&#xff0c;主要目的是处理数据采集过程中可能产生的噪声和尖刺信号。这些噪声可能来自电源干扰、传感器自身的不稳定性或其他外部因素。 1.一阶互补滤波 方法&#xff1a;取a0~1,本次滤波结果&#xff08;1-a&#xff09;本次采样值a上…

[开源]YOLOv8+Pyside6的交通红绿灯目标检测源码

[开源]YOLOv8Pyside6的交通红绿灯目标检测源码 一. 项目介绍源码链接 该系统是yolov8目标检测可视化界面检测系统&#xff0c;支持图片、视频、摄像头检测. 系统的模型是自己训练的模型, 源码自取 源码链接 如需自己训练模型, 数据集链接 二. 作者的运行环境 python3.8tor…

828华为云征文|华为云Flexus X实例docker部署mediacms,功能齐全的现代化开源视频和媒体CMS

828华为云征文&#xff5c;华为云Flexus X实例docker部署mediacms&#xff0c;功能齐全的现代化开源视频和媒体CMS 华为云最近正在举办828 B2B企业节&#xff0c;Flexus X实例的促销力度非常大&#xff0c;特别适合那些对算力性能有高要求的小伙伴。如果你有自建MySQL、Redis、…

【专题】2024年8月医药行业报告合集汇总PDF分享(附原数据表)

原文链接&#xff1a;https://tecdat.cn/?p37621 在科技飞速发展的当今时代&#xff0c;医药行业作为关乎人类生命健康的重要领域&#xff0c;正处于前所未有的变革浪潮之中。数智医疗服务的崛起&#xff0c;为医疗模式带来了全新的转变&#xff0c;开启了医疗服务的新时代。…

git如何灵活切换本地账号对应远程github的两个账号

git如何灵活切换本地账号对应远程github的两个账号 问题&#xff1a; 有时候我们会同时维护两个github的账号里面的仓库内容&#xff0c;这时候本地git需要频繁的切换ssh&#xff0c;以方便灵活的与两个账号的仓库可以通信。这篇日记将阐述我是怎么解决这个问题的。1. 第一个账…

Linux shell编程学习笔记78:cpio命令——文件和目录归档工具(上)

0 前言 在Linux系统中&#xff0c;除了tar命令&#xff0c;我们还可以使用cpio命令来进行文件和目录的归档。 1 cpio命令的功能&#xff0c;帮助信息&#xff0c;格式&#xff0c;选项和参数说明 1.1 cpio命令的功能 cpio 名字来自 "copy in, copy out"&#xf…

游戏开发| Unreal5.2-5.4接入chatGPT定制游戏NPC

引擎版本UE5.2 (也支持到5.4,有试用其它插件所以选择之前版本) 使用插件(免费) 1.VArest (插件官方介绍:Plugin that makes REST communications much easier.)可以让REST(Representational State Transfer)通信变得更加容易,涉及客户端与服务器之间通过 HTTP 协议…