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

news2024/11/15 7:28:32

       

         最近有个需求是需要将场景中的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);
        //保存为png
		bool SaveRenderTargetToDisk(UTextureRenderTarget2D* RenderTarget, const FString& FilePath, int32 NewWidth, int32 NewHeight, int32 CompressionQuality);
        //设置png的size
		void ResizeImage(const TArray<FColor>& SourceBitmap, int32 SourceWidth, int32 SourceHeight, int32 TargetWidth, int32 TargetHeight, TArray<FColor>& OutBitmap);
        //将png转为texture2d
		UTexture2D* 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 为 UTexture2D
			UTexture2D* 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"));
		}
	}

	// 压缩为 PNG
	IImageWrapperModule& 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.coloradmin.cn/o/2117750.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

2024年音频转文字软件哪家强?4 款等你来测

hello&#xff0c;今天来聊聊一个超级方便的小工具&#xff0c;它能帮你把声音直接变成文字&#xff01;想想看&#xff0c;现在谁没有几个音频文件要处理的&#xff0c;比如记笔记的声音、开会的录音、做采访的素材&#xff0c;这些都能搞定。别着急&#xff0c;我现在就给你们…

static 的作用,static 在类中使用的注意事项(定义、初始化和使用),static 全局变量和普通全局变量的异同

目录 1. static 的基本作用 2. static 在类中的使用 2.1 静态成员变量 2.2 静态成员函数 3. static 变量在全局作用域中的使用 3.1 static 全局变量 3.2 普通全局变量 4. static 局部变量 5. static 全局变量与普通全局变量的异同 static 在类中的静态成员变量和成员函…

基于人工智能的图像分类系统

目录 引言项目背景环境准备 硬件要求软件安装与配置系统设计 系统架构关键技术代码示例 数据预处理模型训练模型预测应用场景结论 1. 引言 图像分类是计算机视觉中的一个重要任务&#xff0c;目标是自动识别图像中的对象类别。通过卷积神经网络&#xff08;CNN&#xff09;等…

mingw c++/qt使用grpc方法详细教程

1. RPC框架 RPC框架是什么 RPC 框架说白了就是让你可以像调用本地方法一样调用远程服务提供的方法,而不需要关心底层的通信细节。简单地说就让远程服务调用更加简单、透明。 RPC包含了客户端(Client)和服务端(Server) 业界主流的 RPC 框架整体上分为三类: 1> 支持多语…

Springboot课堂评测系统的设计与实现---附源码82642

目 录 摘要 Abstract 1 绪论 1.1 研究背景与意义 1.2 开发技术和开发特点 1.3论文结构与章节安排 2 课堂评测系统系统分析 2.1 可行性分析 2.2 系统流程分析 2.2.1 数据增加流程 2.2.2 数据修改流程 2.2.3 数据删除流程 2.3 系统功能分析 2.3.1 功能性分析 2.3.…

MyBatis-pulsdruid数据源

MyBatis-Plus 是 MyBatis 的增强工具&#xff0c;主要用于简化数据库操作和提升开发效率。Druid 是阿里巴巴开源的数据库连接池组件&#xff0c;提供了高效的数据库连接管理和监控功能。将这两者结合使用可以更好地管理和操作数据库。以下是 MyBatis-Plus 和 Druid 数据源的总结…

数据重删技术

目录 一、名词介绍 二、重删概述 三、重删分类 四、源端重删 一、名词介绍 指纹&#xff1a;不同数据块数据通过哈希算法所生成的唯一标识。 重删率&#xff1a;&#xff08;1 - 实际备份数据量 / 已完成数据量&#xff09;* 100%。 重删卷&#xff1a;存放指纹库的物理卷。…

JAVA一键开启缘分之旅红娘相亲交友系统小程序源码

一键开启缘分之旅 —— 红娘相亲交友系统 &#x1f496; 初遇心动&#xff0c;一键启程 在这个快节奏的时代&#xff0c;找到那个对的人似乎成了一种奢侈。但别担心&#xff0c;有了“红娘相亲交友系统”&#xff0c;你的缘分之旅只需一键即可开启&#xff01;无需复杂的注册流…

【网页播放器】播放自己喜欢的音乐

// 错误处理 window.onerror function(message, source, lineno, colno, error) {console.error("An error occurred:", message, "at", source, ":", lineno);return true; };// 检查 particlesJS 是否已定义 if (typeof particlesJS ! undefi…

【Day10-配置文件日志多线程】

配置文件 介绍 配置文件 在企业开发过程中&#xff0c;我们习惯把一些需要灵活配置的数据放在一些文本文件中&#xff0c;而不是在Java代码写死我们把这种存放程序配置信息的文件&#xff0c;统称为配置文件 Properties 是一个Map集合&#xff08;键值对集合&#xff09;&am…

推荐系统的基础_协同过滤(CF)

协同过滤&#xff08;Collaborative Filtering&#xff09;是一种推荐系统算法&#xff0c;它通过分析用户之间的相似性或者物品之间的相似性来预测用户可能感兴趣的物品。协同过滤算法主要有两种类型&#xff1a; 1. 用户基协同过滤&#xff08;User-based Collaborative Filt…

OceanMind海睿思“一种业务驱动数据治理的方法和系统”获国家发明专利!

近日&#xff0c;中新赛克海睿思最新技术&#xff1a;一种业务驱动数据治理的方法和系统&#xff08;专利号ZL 202410567107.8&#xff09;&#xff0c;获得国家知识产权局的正式授权&#xff0c;并取得专利证书。 当前&#xff0c;现有的数据治理方法论和平台工具主要聚焦于数…

IDEA 常用插件推荐,美观又实用!

1、 TONGYl Lingma - Your Al Coding Assistant. Type less, Code more. 通义灵码&#xff0c;是一款基于通义大模型的智能编码辅助工具&#xff0c;提供行级/函数级实时续写、自然语言生成代码、单元测试生成、代码注释生成、代码解释、研发智能问答、异常报错排查等能力&…

JVM 调优篇2 jvm的内存结构以及堆栈参数设置与查看

一 jvm的内存模型 2.1 jvm内存模型概览 二 实操案例 2.1 设置和查看栈大小 1.代码 /*** 演示栈中的异常:StackOverflowError** author shkstart* create 2020 下午 9:08** 设置栈的大小&#xff1a; -Xss (-XX:ThreadStackSize)** -XX:PrintFlagsFinal*/ public class S…

【C++】C++ STL 探索:List使用与背后底层逻辑

C语法相关知识点可以通过点击以下链接进行学习一起加油&#xff01;命名空间缺省参数与函数重载C相关特性类和对象-上篇类和对象-中篇类和对象-下篇日期类C/C内存管理模板初阶String使用String模拟实现Vector使用及其模拟实现 本文将通过模拟实现List&#xff0c;从多个角度深入…

第J3周:DenseNet算法实战与解析(pytorch版)

>- **&#x1f368; 本文为[&#x1f517;365天深度学习训练营]中的学习记录博客** >- **&#x1f356; 原作者&#xff1a;[K同学啊]** &#x1f4cc; 本周任务&#xff1a; ●1.请根据本文 Pytorch 代码&#xff0c;编写出相应的 TensorFlow 代码&#xff08;建议使用…

《黑神话·悟空》背后的佛学义理探析

《黑神话悟空》不仅是一款备受期待的动作冒险游戏&#xff0c;其背后的深厚文化内涵&#xff0c;尤其是佛教义理的体现&#xff0c;更是吸引了不少玩家和佛学爱好者的关注。本文将通过对游戏剧情的解析&#xff0c;结合佛教思想&#xff0c;探讨《黑神话悟空》中所蕴含的哲学智…

冯·诺依曼体系结构

纯硬件的计算机结构应该就是输入设备——CPU——输出设备 冯诺依曼体系结构加入了存储器&#xff08;内存&#xff09; 因为数据是要在计算机体系结构中流动的&#xff0c;流动过程中对数据进行加工处理&#xff0c;数据从一个设备到另流动到另一个设备本质是一种数据拷贝。C…

HDFS常用命令及Python连接HDFS操作

目录 一、HDFS常用命令 二、Python连接HDFS操作 一、HDFS常用命令 HDFS&#xff08;Hadoop Distributed File System&#xff0c;Hadoop分布式文件系统&#xff09;是Hadoop集群中的一部分&#xff0c;用于存储大量数据&#xff0c;并运行在商用硬件集群上。以下是HDFS中常用…

6款好用到离谱的宝藏软件,每一款都超出你的认知

你的电脑里有没有那种&#xff0c;无论重装多少次系统&#xff0c;都要第一时间安装的软件&#xff1f; 请把它的名字打在评论区&#xff01;本期分享6款&#xff0c;免费也能吊打付费的Windows电脑必装软件。 最大程度的增强Windows系统的功能&#xff0c;良心分享&#xff…