UE5.3 运行ComputeShader

ComputeShader是一些管线类操作或GPU数据处理所必须的一个环节,UE操作ComputeShader不同版本各种崩溃,API也是不断修改。本文基于我测试成功的UE5.3、UE5.4进行流程说明。

本文实现参考github库:
https://github.com/Will-Z666/UE5_ComputerShaderSample

该仓库就我写这篇文章时的状态,异步节点做了一半,传入贴图没有做ComputeShader的测试,因此我在代码中注释了一部分。

没有太多基础的童鞋可以先尝试测试跑通下这篇文章:
https://blog.csdn.net/grayrail/article/details/144704622


1.创建插件

1.1 首先新建一个UE5 C++工程以测试,并创建一个蓝图库插件。

1.2 修改CsRunner.cpp,在模块启动时导入Shader目录的usf:

cpp 复制代码
// Copyright Epic Games, Inc. All Rights Reserved.

#include "CsRunner.h"
#include "Interfaces/IPluginManager.h"
#define LOCTEXT_NAMESPACE "FCsRunnerModule"

void FCsRunnerModule::StartupModule()
{
	const FString ShaderDir = FPaths::Combine(IPluginManager::Get().FindPlugin(TEXT("CsRunner"))->GetBaseDir(), TEXT("Shaders"));
	AddShaderSourceDirectoryMapping("/Project/Shaders", ShaderDir);
}

void FCsRunnerModule::ShutdownModule()
{
	// This function may be called during shutdown to clean up your module.  For modules that support dynamic reloading,
	// we call this function before unloading the module.
	
}

#undef LOCTEXT_NAMESPACE
	
IMPLEMENT_MODULE(FCsRunnerModule, CsRunner)

1.3 修改build.cs,设置插件依赖库(slate其实不需要):

cpp 复制代码
PrivateDependencyModuleNames.AddRange(
			new string[]
			{
				"CoreUObject",
				"Engine",
				"Slate",
				"SlateCore",
				"Renderer",
				"RenderCore",
				"RHI",
				"Projects"
            }
			);

1.4 修改uplugin文件,设置插件导入阶段为"PostConfigInit":

cpp 复制代码
"Modules": [
		{
			"Name": "CsRunner",
			"Type": "Runtime",
			"LoadingPhase": "PostConfigInit"
		}
	]

1.5 在插件内目录新建Shader文件夹,新建ComputeShader的usf文件:

1.6 编写该usf文件内容以进行测试:

cpp 复制代码
#include "/Engine/Public/Platform.ush"

RWTexture2D<float3> RenderTarget;
Texture2D<float3> InputTexture;

[numthreads(THREADGROUPSIZE_X, THREADGROUPSIZE_Y, THREADGROUPSIZE_Z)]
void MainComputeShader(
                       uint3 DispatchThreadId : SV_DispatchThreadID,
	                   uint GroupIndex : SV_GroupIndex) 
{
    float x = DispatchThreadId.x;
    float y = DispatchThreadId.y;
    float z = 0;
    RenderTarget[DispatchThreadId.xy] = sin(x);
}

2.核心代码编写

新建ComputeShaderDeclaration.h类,编写调用ComputeShader核心代码。

这部分代码会检查游戏是否在渲染线程,并插入渲染线程执行逻辑。

核心部分DispatchRenderThread在cpp文件中。

cpp 复制代码
#pragma once

#include "CoreMinimal.h"
#include "GenericPlatform/GenericPlatformMisc.h"
#include "Kismet/BlueprintAsyncActionBase.h"
#include "Engine/TextureRenderTarget2D.h"

struct FCSParameters
{
	int X;
	int Y;
	int Z;


	UTexture2D* InputTexture;

	FRenderTarget* RenderTarget;

	FCSParameters(int x, int y, int z)
		: X(x)
		, Y(y)
		, Z(z)
	{
	}
};

class FComputeShaderInterface
{
public:
	static void DispatchRenderThread(
		FRHICommandListImmediate& RHICmdList,
		FCSParameters Params
	);

	static void DispatchGameThread(
		FCSParameters Params
	)
	{
		ENQUEUE_RENDER_COMMAND(SceneDrawCompletion)(
			[Params](FRHICommandListImmediate& RHICmdList)
			{
				DispatchRenderThread(RHICmdList, Params);
			});
	}

	static void Dispatch(
		FCSParameters Params
	)
	{
		if (IsInRenderingThread()) {
			DispatchRenderThread(GetImmediateCommandList_ForRenderCommand(), Params);
		}
		else {
			DispatchGameThread(Params);
		}
	}
};

新建ComputeShaderDeclaration.cpp类:

cpp 复制代码
#include "ComputeShaderDeclaration.h"

#include "GlobalShader.h"
#include "MaterialShader.h"
#include "ShaderParameterStruct.h"
#include "RenderGraphResources.h"
#include "RenderGraphUtils.h"
#include "RenderGraphBuilder.h"
#include "RenderTargetPool.h"

#define NUM_THREADS_PER_GROUP_DIMENSION_X 32
#define NUM_THREADS_PER_GROUP_DIMENSION_Y 32
#define NUM_THREADS_PER_GROUP_DIMENSION_Z 1

DECLARE_STATS_GROUP(TEXT("ComputeShader"), STATGROUP_ComputeShader, STATCAT_Advanced);
DECLARE_CYCLE_STAT(TEXT("ComputeShader Execute"), STAT_ComputeShader_Execute, STATGROUP_ComputeShader);

class FComputeShader : public FGlobalShader
{
public:
	//Declare this class as a global shader
	DECLARE_GLOBAL_SHADER(FComputeShader);
	//Tells the engine that this shader uses a structure for its parameters
	SHADER_USE_PARAMETER_STRUCT(FComputeShader, FGlobalShader);
	/// <summary>
	/// DECLARATION OF THE PARAMETER STRUCTURE
	/// The parameters must match the parameters in the HLSL code
	/// For each parameter, provide the C++ type, and the name (Same name used in HLSL code)
	/// </summary>
	BEGIN_SHADER_PARAMETER_STRUCT(FParameters, )
		SHADER_PARAMETER_RDG_TEXTURE_UAV(RWTexture2D, RenderTarget)
		SHADER_PARAMETER_RDG_TEXTURE(Texture2D, InputTexture)
		END_SHADER_PARAMETER_STRUCT()


public:
	//Called by the engine to determine which permutations to compile for this shader
	static bool ShouldCompilePermutation(const FGlobalShaderPermutationParameters& Parameters)
	{


		return true;
	}
	//Modifies the compilations environment of the shader
	static inline void ModifyCompilationEnvironment(const FGlobalShaderPermutationParameters& Parameters, FShaderCompilerEnvironment& OutEnvironment)
	{
		FGlobalShader::ModifyCompilationEnvironment(Parameters, OutEnvironment);

		//We're using it here to add some preprocessor defines. That way we don't have to change both C++ and HLSL code 
		// when we change the value for NUM_THREADS_PER_GROUP_DIMENSION

		OutEnvironment.SetDefine(TEXT("THREADGROUPSIZE_X"), NUM_THREADS_PER_GROUP_DIMENSION_X);
		OutEnvironment.SetDefine(TEXT("THREADGROUPSIZE_Y"), NUM_THREADS_PER_GROUP_DIMENSION_Y);
		OutEnvironment.SetDefine(TEXT("THREADGROUPSIZE_Z"), NUM_THREADS_PER_GROUP_DIMENSION_Z);
	}
};

IMPLEMENT_GLOBAL_SHADER(FComputeShader, "/Project/Shaders/Shader.usf", "MainComputeShader", SF_Compute);


void FComputeShaderInterface::DispatchRenderThread(FRHICommandListImmediate& RHICmdList, FCSParameters Params) {
	FRDGBuilder GraphBuilder(RHICmdList);

	{
		SCOPE_CYCLE_COUNTER(STAT_ComputeShader_Execute);
		DECLARE_GPU_STAT(ComputeShader)
		RDG_EVENT_SCOPE(GraphBuilder, "ComputeShader");
		RDG_GPU_STAT_SCOPE(GraphBuilder, ComputeShader);

		typename FComputeShader::FPermutationDomain PermutationVector;

		TShaderMapRef<FComputeShader> ComputeShader(GetGlobalShaderMap(GMaxRHIFeatureLevel), PermutationVector);

		bool bIsShaderValid = ComputeShader.IsValid();

		if (bIsShaderValid) {
			FComputeShader::FParameters* PassParameters = GraphBuilder.AllocParameters<FComputeShader::FParameters>();


			FRDGTextureDesc Desc(FRDGTextureDesc::Create2D(Params.RenderTarget->GetSizeXY(), PF_B8G8R8A8, FClearValueBinding::White, TexCreate_RenderTargetable | TexCreate_ShaderResource | TexCreate_UAV));
			FRDGTextureRef TmpTexture = GraphBuilder.CreateTexture(Desc, TEXT("ComputeShader_TempTexture"));
			FRDGTextureRef TargetTexture = RegisterExternalTexture(GraphBuilder, Params.RenderTarget->GetRenderTargetTexture(), TEXT("ComputeShader_RT"));
			PassParameters->RenderTarget = GraphBuilder.CreateUAV(TmpTexture);
			//传入贴图这里我注释了
			//FRHITexture* InputTexture = Params.InputTexture->GetResource()->GetTextureRHI();
			//FRDGTextureRef RDGSourceTexture = RegisterExternalTexture(GraphBuilder, InputTexture, TEXT("ComputeShader_InputTexture"));
			//PassParameters->InputTexture = RDGSourceTexture;

			auto GroupCount = FComputeShaderUtils::GetGroupCount(FIntVector(Params.X, Params.Y, Params.Z), FComputeShaderUtils::kGolden2DGroupSize);
			GraphBuilder.AddPass(
				RDG_EVENT_NAME("ExecuteExampleComputeShader"),
				PassParameters,
				ERDGPassFlags::AsyncCompute,
				[&PassParameters, ComputeShader, GroupCount](FRHIComputeCommandList& RHICmdList)
				{
					FComputeShaderUtils::Dispatch(RHICmdList, ComputeShader, *PassParameters, GroupCount);
				});

			if (TargetTexture->Desc.Format == PF_B8G8R8A8) {
				AddCopyTexturePass(GraphBuilder, TmpTexture, TargetTexture, FRHICopyTextureInfo());
			}
		}
	}

	GraphBuilder.Execute();
}

现在C++部分基本已经完成,下面开始讲解蓝图配置部分。

3.蓝图配置

3.1 右键新建RenderTarget,配置好格式参数:

3.2 新建Material,拖入RenedrTarget并设置材质参数:

3.3 将该材质赋予场景中的测试模型。

3.4 新建Actor蓝图,增加如下节点,RenderTarget外部传入即可:

最后在场景中放入该Actor进行测试。

相关推荐
宇寒风暖41 分钟前
软件工程大复习(五) 需求工程与需求分析
笔记·学习·软件工程
yvestine43 分钟前
数据挖掘——规则和最近邻分类器
人工智能·笔记·学习·机器学习·数据挖掘·规则·knn
m0_748231311 小时前
Node.js使用教程
node.js·编辑器·vim
leilifengxingmw1 小时前
Docker学习相关笔记,持续更新
笔记·学习·docker
thesky1234562 小时前
每天你好20250103(距离春节26天!!!)
学习·算法
小吃饱了2 小时前
vim、watch、cp和mv
linux·编辑器·vim
1.1的24次方2 小时前
【公开笔记】-小白如何写论文
笔记·学习·论文笔记
nnnimok2 小时前
渗透学习笔记(十)PowerShell基础
笔记·学习
知难行难3 小时前
持续学习入门
学习
ue星空3 小时前
UE5材质节点Distance
ue5·材质