Calmer的文章

  • 首页
  • 文章归档
  • 关于页面

  • 搜索
体验游戏 笔记 推荐 工具链 工具使用 小游戏 插件 UI 软件 教程

UE5集成ChatGPT和Stable Diffusion(SD)

发表于 2023-08-31 | 分类于 UE5 | 0 | 阅读次数 1828

前言

最近倒腾了一下之前比较火的AI绘画和ChatGPT,将两者都集成进了UE引擎中进行使用。

具体应用:

  • AI绘画,可以用来生成立绘,缓存其生成参数和图片内容。
  • ChatGPT,可以用来生成NPC对话。

虽然更多的应用还是离线生成之后,进行配置化。
不过在游戏运行时使用说不定更有趣呢。


AI绘画(Stable Diffusion,下简称SD)

1. 环境安装

这里就不过多赘述,网上有很多教程,部署在本地的,远程的都有。

2. 相关Git仓库

  • Stable Diffusion 1
    https://github.com/CompVis/stable-diffusion
  • Stable Diffusion 2
    https://github.com/Stability-AI/stablediffusion
  • Stable Diffusion WebUI (此示例中使用)
    https://github.com/AUTOMATIC1111/stable-diffusion-webui

3.加速(打开xformers)

在命令行设置xformers,加速效果还是很明显的
image.png

4.模型

模型去C站上下载:https://civitai.com/
image.png

5.命令行参数设置

修改webui-use.bat文件,主要是添加上--api参数,以支持通过HTTP后台API调用

set COMMANDLINE_ARGS= --listen --api --xformers --theme dark --enable-console-prompts

更多命令参数
https://github.com/AUTOMATIC1111/stable-diffusion-webui/wiki/Command-Line-Arguments-and-Settings


封装SD UE5引擎接口

这里将SD功能引入UE5引擎,本质是SD WebUI作为Web服务器,UE5引擎代替浏览器作为客户端,都是对Http协议的使用和API调用

整体实现思路

  • 整理SD WebUI的API的参数
  • 通过UE5 HTTP接口请求SD服务
  • 处理HTTPResponse,解码图片数据
  • 封装为AysncAction,方便蓝图调用,例如应用于UMGImage中

具体步骤

1.SD请求参数

UPROPERTY(BlueprintReadWrite, EditAnywhere, meta=(ExposeOnSpawn), Category="BaseParam")
FString Prompt;
UPROPERTY(BlueprintReadWrite, EditAnywhere, meta=(ExposeOnSpawn), Category="BaseParam")
FString NegativePrompt;
UPROPERTY(BlueprintReadWrite, EditAnywhere, meta=(ExposeOnSpawn), Category="BaseParam")
FString SamplerIndex = TEXT("Euler a");
UPROPERTY(BlueprintReadWrite, EditAnywhere, meta=(ExposeOnSpawn), Category="BaseParam")
int Steps = 20;
UPROPERTY(BlueprintReadWrite, EditAnywhere, meta=(ExposeOnSpawn), Category="BaseParam")
int Seed = -1;
UPROPERTY(BlueprintReadWrite, EditAnywhere, meta=(ExposeOnSpawn), Category="BaseParam")
int Width = 512;
UPROPERTY(BlueprintReadWrite, EditAnywhere, meta=(ExposeOnSpawn), Category="BaseParam")
int Height = 512;
UPROPERTY(BlueprintReadWrite, EditAnywhere, meta=(ExposeOnSpawn), Category="BaseParam")
int CfgScale = 7;

2.Http请求接口

void UAsyncTaskRequestSDImage::Start(FString URL, FString JsonParam)
{
	TSharedRef<IHttpRequest, ESPMode::ThreadSafe> HttpRequest = FHttpModule::Get().CreateRequest();
	HttpRequest->SetURL(URL);
	// FString JsonStr = "{\"prompt\": \"a beautiful girl\",\"negative_prompt\": \"\",\"sampler_index\": \"Euler a\",\"seed\": 3,\"steps\": 20,\"width\": 512,\"height\": 512,\"cfg_scale\": 7}";
	HttpRequest->SetHeader(TEXT("Content-Type"), TEXT("application/json; charset=utf-8"));
	HttpRequest->SetVerb(TEXT("POST"));
	HttpRequest->SetContentAsString(JsonParam);
	HttpRequest->OnProcessRequestComplete().BindUObject(this, &UAsyncTaskRequestSDImage::HandleImageRequest);
	HttpRequest->ProcessRequest();
}

3.处理HTTPResponse,解码图片数据

void UAsyncTaskRequestSDImage::HandleImageRequest(FHttpRequestPtr HttpRequest, FHttpResponsePtr HttpResponse,
                                                  bool bSucceeded)
{
	if (bSucceeded && HttpResponse.IsValid() && HttpResponse->GetResponseCode() == 200 && HttpResponse->
		GetContentLength() > 0)
	{
		FString JsonStr = HttpResponse->GetContentAsString();
		TSharedRef<TJsonReader<TCHAR>> JsonReader = TJsonReaderFactory<TCHAR>::Create(JsonStr);
		TSharedPtr<FJsonValue> CurrentJsonValue;
		FJsonSerializer::Deserialize(JsonReader, CurrentJsonValue);
		TArray<TSharedPtr<FJsonValue>> ImageJsonObjectArray = CurrentJsonValue->AsObject()->GetArrayField(
			TEXT("images"));

		IImageWrapperModule& ImageWrapperModule = FModuleManager::LoadModuleChecked<IImageWrapperModule>(
			FName("ImageWrapper"));
		TSharedPtr<IImageWrapper> ImageWrappers[3] =
		{
			ImageWrapperModule.CreateImageWrapper(EImageFormat::PNG),
			ImageWrapperModule.CreateImageWrapper(EImageFormat::JPEG),
			ImageWrapperModule.CreateImageWrapper(EImageFormat::BMP),
		};

		for (int j = 0; j < ImageJsonObjectArray.Num(); j++)
		{
			auto CurrentImage = ImageJsonObjectArray[j]->AsString();
			TArray<uint8> CurrentImageBytes;
			FBase64::Decode(CurrentImage, CurrentImageBytes);
			for (auto ImageWrapper : ImageWrappers)
			{
				if (ImageWrapper.IsValid() && ImageWrapper->SetCompressed(
					CurrentImageBytes.GetData(), CurrentImageBytes.GetAllocatedSize()))
				{
					TArray64<uint8> RawData;
					const ERGBFormat InFormat = ERGBFormat::BGRA;
					if (ImageWrapper->GetRaw(InFormat, 8, RawData))
					{
						if (UTexture2DDynamic* Texture = UTexture2DDynamic::Create(
							ImageWrapper->GetWidth(), ImageWrapper->GetHeight()))
						{
							Texture->SRGB = true;
							Texture->UpdateResource();

							FTexture2DDynamicResource* TextureResource = static_cast<FTexture2DDynamicResource*>(Texture
								->GetResource());
							if (TextureResource)
							{
								ENQUEUE_RENDER_COMMAND(FWriteRawDataToTexture)(
									[TextureResource, RawData = MoveTemp(RawData)](FRHICommandListImmediate& RHICmdList)
									{
										TextureResource->WriteRawToTexture_RenderThread(RawData);
									});
							}
							OnSuccess.Broadcast(Texture);
							return;
						}
					}
				}
			}
		}
	}
	OnFail.Broadcast(nullptr);
}

4.封装为AysncAction,方便蓝图调用(见源码)

继承AysncActionBase
8adf859f10df7f350f7758e461e6fea9d2efe29bbd19916ba1b9ba2d01aee9d4.png

使用示例

  • 先启动SD WebUI,打开其API调用功能
    15c069268d5edb40fb5a8816608f8a7abdb3129d3886ea56abe19dc94b646cf7.png
  • 示例蓝图BP_SDSample
    构造参数,发起请求,回调设置Image
    ee985199b49d9d380e83853133a94ff76e6e9bee12170bce7beecc65b7bf5d9c.png
  • 生成结果
    7aa87ce9ef4274bb1b8aa40912e14ade54b45e56c2e5e3b7e110ed2bc84fd7b8.png

封装GPT UE5引擎接口

整体实现思路

  • 整理GPT参数
  • 通过UE5 HTTP接口请求GPT服务
  • 处理HTTPResponse和Progress
  • 封装为AysncAction,方便蓝图调用

具体步骤

1.整理GPT参数

FString Content;  //问题内容
FString Key;  //api-key

2.通过UE5 HTTP接口请求GPT服务

void UAsyncTaskRequestChatGPT::Start(FString Content, FString Key)
{
	TSharedPtr<FJsonObject> HeaderObjectJson = MakeShareable(new FJsonObject);
	TSharedPtr<FJsonObject> ModelObjectJson = MakeShareable(new FJsonObject);
	ModelObjectJson->SetStringField(TEXT("id"), TEXT("gpt-3.5-turbo"));
	ModelObjectJson->SetStringField(TEXT("name"), TEXT("GPT-3.5"));
	ModelObjectJson->SetNumberField(TEXT("maxLength"), 12000);
	ModelObjectJson->SetNumberField(TEXT("tokenLimit"), 4000);
	HeaderObjectJson->SetObjectField(TEXT("model"), ModelObjectJson);

	TArray<TSharedPtr<FJsonValue>> JsonArray;

	TSharedPtr<FJsonObject> OneTempJsonObject = MakeShareable(new FJsonObject);
	OneTempJsonObject->SetStringField(TEXT("role"), TEXT("user"));
	OneTempJsonObject->SetStringField(TEXT("content"), Content);
	TSharedPtr<FJsonValue> FirstJsonValue = MakeShareable(new FJsonValueObject(OneTempJsonObject));
	JsonArray.Add(FirstJsonValue);
	HeaderObjectJson->SetArrayField(TEXT("messages"), JsonArray);

	HeaderObjectJson->SetStringField(TEXT("key"), Key);
	HeaderObjectJson->SetStringField(
		TEXT("prompt"),
		TEXT(
			"You are ChatGPT, a large language model trained by OpenAI. Follow the user's instructions carefully. Respond using markdown."));
	HeaderObjectJson->SetNumberField(TEXT("temperature"), 1);

	FString HeaderString;
	TSharedRef<TJsonWriter<>> Writer = TJsonWriterFactory<>::Create(&HeaderString);
	FJsonSerializer::Serialize(HeaderObjectJson.ToSharedRef(), Writer);
	HeaderString = HeaderString.Replace(TEXT("\n"), TEXT(""));
	HeaderString = HeaderString.Replace(TEXT("\r"), TEXT(""));
	HeaderString = HeaderString.Replace(TEXT("\t"), TEXT(""));

	TSharedRef<IHttpRequest, ESPMode::ThreadSafe> HttpRequest = FHttpModule::Get().CreateRequest();
	FString URL;
	check(GWorld);
	USDIntegrationSubsystem* SDIntegrationSubsystem = GWorld->GetGameInstance()->GetSubsystem<
		USDIntegrationSubsystem>();
	check(SDIntegrationSubsystem);
	SDIntegrationSubsystem->GetRequestChatGPTURL(URL);
	HttpRequest->SetURL(URL);
	HttpRequest->SetHeader(TEXT("Content-Type"), TEXT("application/json; charset=utf-8"));
	HttpRequest->SetHeader(
		TEXT("User-Agent"),
		TEXT(
			"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.0.0 Safari/537.36"));
	HttpRequest->SetVerb(TEXT("POST"));
	
	HttpRequest->SetContentAsString(HeaderString);
	HttpRequest->OnRequestProgress().BindUObject(this, &UAsyncTaskRequestChatGPT::HandleProgress);
	HttpRequest->OnProcessRequestComplete().BindUObject(this, &UAsyncTaskRequestChatGPT::HandleChatGPTRequest);
	HttpRequest->ProcessRequest();
}

3.处理HTTPResponse和Progress

处理Progress,可以在回调中进行文本的设置,可以实现类似打印机的效果

void UAsyncTaskRequestChatGPT::HandleProgress(FHttpRequestPtr Request, int32 BytesSent, int32 BytesReceived)
{
	UE_LOG(LogTemp, Log, TEXT("HandleProgress:%s"), *Request->GetResponse()->GetContentAsString());
	OnProgress.Broadcast(Request->GetResponse()->GetContentAsString());
}

4.封装为AysncAction,方便蓝图调用(见源码)

继承AysncActionBase
0ddccc7720990f8e4bc566879a0f9cb3b05c77f8a32dc154dcdf87e1f480b8da.png

使用示例

  • 在初始化中设置一个全局的Api-Key
    9ee0be54ed365cd6779432845094f8e83ca481d553d4eae2c8409eb027eb6b18.png

  • 蓝图BP_ChatGPTSample,调用异步任务节点即可
    da8f3aa5cfd613ea814953e51990c4a4843d13f317113931dd13a920273c9bd2.png

  • 生成结果
    92f221b57e8c2469929f8e21e57d5dd9e20ecfdf862e9213869d4841216c65f8.png

UE5 SDIntegration Plugin v1

https://github.com/CalmLoader/ChatGPT-SD-Integration-UE5


其他问题

Lora模型没法加载的问题
https://github.com/AUTOMATIC1111/stable-diffusion-webui/issues/7984

PostCat(API测试工具)
https://github.com/Postcatlab/postcat

参考链接

https://github.com/AUTOMATIC1111/stable-diffusion-webui/wiki/API
https://zhuanlan.zhihu.com/p/629845245
https://zhuanlan.zhihu.com/p/624042359

  • 本文作者: Calmer
  • 本文链接: https://mytechplayer.com/archives/ue5-ji-cheng-chatgpt-he-stablediffusionsd
  • 版权声明: 本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明出处!
UE5中GAS接入增强输入(EnhancedInput)
UE4/5中编辑器和命令的扩展(新)
  • 文章目录
  • 站点概览
Calmer

Calmer

88 日志
7 分类
10 标签
RSS
Creative Commons
0%
© 2020 — 2025 Calmer
由 Halo 强力驱动
蜀ICP备20010026号-1川公网安备51019002006543
Copyright © 2020-2025 Calmer的文章