UE | Shader | 在UE中添加全局Shader

这篇具有很好参考价值的文章主要介绍了UE | Shader | 在UE中添加全局Shader。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。


做引擎开发的时候经常需要在UE中添加全局(Global)的Shader来提供新的渲染功能。

全局Shader与一般写在Custom节点中的Shader相比,可以直接被引擎中的其他Shader调用,同时可以从C++里调用,一般做新的底层渲染功能,添加后处理还有一些不需要依赖Material和Mesh运行的Shader的时候我们需要直接修改底层的全局Shader。

.usf(Unreal Shader Files) 和.usf的使用

顾名思义,Unreal Shader Files就是主要负责Shading部分的着色器文件。
UE实际编译的时候,会从Engine/Shaders文件夹读取.usf 并且编译Shader文件。所有新Shader都可以放在这里来和引擎一起编译。

一般我们会在 ConsoleVariables.ini 配置文件中启用 r.ShaderDevelopmentMode=1 来便于开发。
关于Shader Debug的部分,可以看官网的文档:Shader Debugging Workflows

.usf文件编写

举个例子,我们可以在 Engine/Shaders 文件夹中添加一个新的名为MyTest.usf 的 .usf 文件,。然后添加一个简单的直接输出的顶点着色器VS和一个返回自定义颜色的像素着色器PS:

// MyTest.usf 

// Simple pass-through vertex shader
void MainVS(
	in float4 InPosition : ATTRIBUTE0,
	out float4 Output : SV_POSITION
)
{
	Output = InPosition;
}

// Simple solid color pixel shader
float4 MyColor;
float4 MainPS() : SV_Target0
{
	return MyColor;
}

代码中的ATTRIBUTE0SV_POSITIONSV_Target0 都算是着色器语义,关于这个部分可以直接去DX的shader官网看:Direct3D中的语义。

.usf文件绑定.cpp和.h文件

现在我们手上有了MyTest.usf ,但是它是一个光杆司令,没有办法编译和被其他C++调用和识别,为了让这个文件能够顺利被编译和使用,我们还需要声明一个对应的C++类,这个类的声明可以放在.cpp文件里,也可以放在.h文件里:

// This can go on a header or cpp file
class FMyTestVS : public FGlobalShader
{
	DECLARE_EXPORTED_SHADER_TYPE(FMyTestVS, Global, MY_TEST_API);
 	
	FMyTestVS() { }	//构造函数1: 默认构造函数
	FMyTestVS(const ShaderMetaType::CompiledShaderInitializerType& Initializer)
		: FGlobalShader(Initializer) //构造函数2: 序列化构造函数
	{
	}
 
	static bool ShouldCache(EShaderPlatform Platform)
	{
		return true;
	}
};

这个类写的时候有以下几个要求:

  • 所有Shader的基类是FShader, Unreal有两个主要shader类别:

    1. FGlobalShader, FGlobalShader只有一个实例存在,也就是它无法为每个实例设置参数。
    2. FMaterialShader, FMaterialShader与materail绑定, 大量Shader派生于这个类,因为它是所有需要material和vertex factory参数Shader的基类。
      FMaterialShader一般分为FMaterialShader和FMeshMaterialShader两种。这两个Class都允许多个instance,每一个关联自己GPU资源拷贝。FMaterialShader添加一个SetParameter函数,允许你自己shader中C++代码来修改HLSL中参数。参数绑定通过FShaderParameter/FShaderResourceParameter完成,能在Shader构造函数中完成。SetParameters函数在使用Shader渲染前被调用,并传递一些信息下去,包括material,这为你想修改的参数提供一部分用于计算的信息。

    FShader与FShaderResource成对出现, FShaderResource追踪一个shader在GPU上相关资源。FShaderResource可以被多个FShaders共享。

    这里我们选择的基类是FGlobalShader,因为FGlobalShader能让Shader编译为一个全局的Shader来直接被引擎调用。

  • 使用 DECLARE_EXPORTED_SHADER_TYPE() 宏来 生成 Shader类型序列化等所需要的必要的导出信息。这个宏的第一个参数就是类名, 第二个参数一般固定为Global, 第三个参数是着色器模块所在的代码模块的外部链接类型。

  • 需要有两个构造函数 : 默认构造函数和序列化构造函数。

  • ShouldCache() 函数用来决定应不应该在编译这个Shader, 一般就调用IsFeatureLevelSupported看平台和编译环境支不支持这个Shader, 支持的话就编译, 这里我们直接return true也是可以的.(如果RHI不支持计算着色器的话, 就应该return false 来取消CS的编译)。

有了上面这个类的声明之后,我们就可以把Shader类型注册到 UE 的列表中了:

// This needs to go on a cpp file
IMPLEMENT_SHADER_TYPE(, FMyTestVS, TEXT("MyTest"), TEXT("MainVS"), SF_Vertex);

这个宏的意思是它会把类FMyTestVS 映射到 后面的三个参数对应的三个不同的属性 :

  1. Shader所绑定的 usf文件MyTest.usf
  2. 着色器入口点( 可以理解为主函数 ) MainVS
  3. 着色器类型 SF_Vertex

上述宏IMPLEMENT_SHADER_TYPE的三个参数中 :

  1. 第一个TEXT("")内的参数可以写.usf的全路径, 因为这里使用的是引擎本身的Shader文件夹,所以可以直接写MyTest.usf的名称MyTest就可以;
  2. 第二个参数入口点EnterPoint (可以理解为主函数) MainVS的函数名并不固定, 和你usf文件中的主函数对应上就好;
  3. 第三个参数 SF_Vertex界定了绑定的Shader实现的类型, 同理如果绑定的是PS的话就是SF_Pixel, 计算着色器的话就是SF_Compute.

只要函数 ShouldCache() 方法返回 true,它就会把 Shader 添加进UE的编译列表中。

上面提到的像素着色器还是比较简单的, 它只返回了一个自定义的Color, 下面我们来写一个比较复杂的FMyTestPS :

class FMyTestPS : public FGlobalShader
{
	DECLARE_EXPORTED_SHADER_TYPE(FMyTestPS, Global, /*MYMODULE_API*/);
 
	FShaderParameter MyColorParameter;
 
	FMyTestPS() { } //构造函数1: 默认构造函数
	FMyTestPS(const ShaderMetaType::CompiledShaderInitializerType& Initializer)
		: FGlobalShader(Initializer) //构造函数2: 序列化构造函数
	{
		MyColorParameter.Bind(Initializer.ParameterMap, TEXT("MyColor"), SPF_Mandatory);
	}
 
	static void ModifyCompilationEnvironment(EShaderPlatform Platform, FShaderCompilerEnvironment& OutEnvironment)
	{
		FGlobalShader::ModifyCompilationEnvironment(Platform, OutEnvironment);
		// Add your own defines for the shader code
		OutEnvironment.SetDefine(TEXT("MY_DEFINE"), 1);
	}
 
	static bool ShouldCache(EShaderPlatform Platform)
	{
		// Could skip compiling for Platform == SP_METAL for example
		return true;
	}
 
	// FShader interface.
	virtual bool Serialize(FArchive& Ar) override
	{
		bool bShaderHasOutdatedParameters = FGlobalShader::Serialize(Ar);
		Ar << MyColorParameter;
		return bShaderHasOutdatedParameters;
	}
 
	void SetColor(FRHICommandList& RHICmdList, const FLinearColor& Color)
	{
		SetShaderValue(RHICmdList, GetPixelShader(), MyColorParameter, Color);
	}
};

可以看到, 这个类里的序列化构造函数多了一些操作:

  • 类内多了一个FShaderParameter类型的局部变量MyColorParameter;
  • 序列化构造函数里面我们调用了MyColorParameter的.Bind函数,
    MyColorParameter.Bind(Initializer.ParameterMap, TEXT(“MyColor”), SPF_Mandatory);
    这个函数里面的MyColor这个名字必须和.usf里面的声明一样, .usf里面我们叫 float4 MyColor;那么在Bind函数里面就必须是TEXT(“MyColor”).
  • Serialize(), 这个函数是添加新参数所必须的. 这是运行时Shader绑定(在序列化构造函数期间匹配)的编译/cook时间信息被加载和存储的地方
  • ModifyCompilationEnvironment(),因为我们在同一个 C++ 类定义了不同的行为, 并能够在着色器中设置#define 值,所以需要设定编译环境.
  • 最后,我们有一个自定义的 SetColor() 方法用来在运行时 把 Color 赋值给 MyColorParameter。

有了上面这些操作, 我们就能够在运行时修改PS中的MyColor的值了.

添加控制台变量

让我们编写一个简单的函数来使用上面的着色器类型绘制 全屏的四边形:

void RenderMyTest(FRHICommandList& RHICmdList, ERHIFeatureLevel::Type FeatureLevel, const FLinearColor& Color)
{
	// Get the collection of Global Shaders
	auto ShaderMap = GetGlobalShaderMap(FeatureLevel);

	// Get the actual shader instances off the ShaderMap
	TShaderMapRef MyVS(ShaderMap);
	TShaderMapRef MyPS(ShaderMap);

	// Declare a bound shader state using those shaders and apply it to the command list
	static FGlobalBoundShaderState MyTestBoundShaderState;
	SetGlobalBoundShaderState(RHICmdList, FeatureLevel, MyTestBoundShaderState, GetVertexDeclarationFVector4(), *MyVS, *MyPS);

	// Call our function to set up parameters
	MyPS->SetColor(RHICmdList, Color);
 
	// Setup the GPU in prep for drawing a solid quad
	RHICmdList.SetRasterizerState(TStaticRasterizerState::GetRHI());
	RHICmdList.SetBlendState(TStaticBlendState<>::GetRHI());
	RHICmdList.SetDepthStencilState(TStaticDepthStencilState::GetRHI(), 0);
 
	// Setup the vertices
	FVector4 Vertices[4];
	Vertices[0].Set(-1.0f, 1.0f, 0, 1.0f);
	Vertices[1].Set(1.0f, 1.0f, 0, 1.0f);
	Vertices[2].Set(-1.0f, -1.0f, 0, 1.0f);
	Vertices[3].Set(1.0f, -1.0f, 0, 1.0f);

	// Draw the quad
	DrawPrimitiveUP(RHICmdList, PT_TriangleStrip, 2, Vertices, sizeof(Vertices[0]));
}

如果想要在你的代码库中测试上面这个函数,我们可以尝试声明一个控制台变量,这样它就可以在运行时切换,如下所示:

static TAutoConsoleVariable CVarMyTest(
	TEXT("r.MyTest"),
	0,
	TEXT("Test My Global Shader, set it to 0 to disable, or to 1, 2 or 3 for fun!"),
	ECVF_RenderThreadSafe
);

void FDeferredShadingSceneRenderer::RenderFinish(FRHICommandListImmediate& RHICmdList)
{
	[...]
	// ***
	// Inserted code, just before finishing rendering, so we can overwrite the screen’s contents!
	int32 MyTestValue = CVarMyTest.GetValueOnAnyThread();
	if (MyTestValue != 0)
	{
		FLinearColor Color(MyTestValue == 1, MyTestValue == 2, MyTestValue == 3, 1);
		RenderMyTest(RHICmdList, FeatureLevel, Color);
	}
	// End Inserted code
	// ***
	FSceneRenderer::RenderFinish(RHICmdList);
	[...]
}

关于添加控制台变量的方法, 可以看UE官方的文档: C++中的控制台变量
TAutoConsoleVariable的定义如下:

template <class T>
class TAutoConsoleVariable : public FAutoConsoleObject
{
public:
	//一共四个参数:
	TAutoConsoleVariable(const TCHAR* Name, const T& DefaultValue, const TCHAR* Help, uint32 Flags = ECVF_Default);
}

定义的时候需要注意, TAutoConsoleVariable 中的类型T只能是int32floatFString三种, 构造函数的四个参数如下:

  • Name:变量名
  • DefaultValue:变量的默认值
  • Help:如何输入“<变量名> ?”,将要显示的帮助信息
  • Flags:可选值是从ECVF_DefaultECVF_ScalabilityGroup的值

其中关于第四个参数Flags的说明如下:

enum EConsoleVariableFlags
{
	/**
	 * 默认,没有设置标志,值由构造函数设置。
	 * Default, no flags are set, the value is set by the constructor 
	 */
	ECVF_Default = 0x0,
	
	/**
	 * 正式版会隐藏在控制台中,用户无法更改。
	 * Console variables marked with this flag behave differently in a final release build.
	 * Then they are are hidden in the console and cannot be changed by the user.
	 */
	ECVF_Cheat = 0x1,
	
	/**
	 * 用户只能从从 C++ 或 ini 更改, 不能在控制台中更改这个变量的数值, 因为是只读, 没什么可说的。
	 * Console variables cannot be changed by the user (from console).
	 * Changing from C++ or ini is still possible.
	 */
	ECVF_ReadOnly = 0x4,
	
	/**
	 * 如果变量再次注册为同一类型,则该对象将重新激活。这有利于 DLL 卸载。
	 * UnregisterConsoleObject() was called on this one.
	 * If the variable is registered again with the same type this object is reactivated. This is good for DLL unloading.
	 */
	ECVF_Unregistered = 0x8,
	
	/**
	 * 在这个变量没有被注册初始化的时候,使用的是ini中的变量, 
	 * 注册之后这个变量的数值就会立马被拷贝到别的地方并且变量本身被销毁。
	 * This flag is set by the ini loading code when the variable wasn't registered yet.
	 * Once the variable is registered later the value is copied over and the variable is destructed.
	 */
	ECVF_CreatedFromIni = 0x10,
	
	/**
	 * 这个变量使用的时候基本上主要是通过维护另一个copy的变量,
	 * 使用的时候首先通过渲染线程命令来更新copy的变量。
	 * 并且需要注意:它假定引用仅在渲染线程上访问, 所以不要在任何其他线程中使用,
	 * 或者最好不要使用引用以避免潜在的问题。
	 * Maintains another shadow copy and updates the copy with render thread commands to maintain proper ordering.
	 * Could be extended for more/other thread.
 	 * Note: On console variable references it assumes the reference is accessed on the render thread only
	 * (Don't use in any other thread or better don't use references to avoid the potential pitfall).
	 */
	ECVF_RenderThreadSafe = 0x20,
 
	/** 
	* 如果未设置 ApplyCVarSettingsGroupFromIni 将警告,不能与 ECVF_Cheat 放在一起使用
	* ApplyCVarSettingsGroupFromIni will complain if this wasn't set, should not be combined with ECVF_Cheat */
	ECVF_Scalability = 0x40,
 
	/** 
	* 这些 cvar 控制其他带有标志 ECVF_Scalability 的 cvar,名称应以“sg”开头。
	* those cvars control other cvars with the flag ECVF_Scalability, names should start with "sg." */
	ECVF_ScalabilityGroup = 0x80,
 
	// ------------------------------------------------
 
	/* to get some history of where the last value was set by ( useful for track down why a cvar is in a specific state */
	ECVF_SetByMask =				0xff000000,
	
	// 剩下的这些主要表示 优先级 (从低到高)  
	//the ECVF_SetBy are sorted in override order (weak to strong), the value is not serialized, it only affects it's override behavior when calling Set()
 	
	// lowest priority (default after console variable creation)
	ECVF_SetByConstructor =			0x00000000,
	// from Scalability.ini (lower priority than game settings so it's easier to override partially)
	ECVF_SetByScalability =			0x01000000,
	// (in game UI or from file)
	ECVF_SetByGameSetting =			0x02000000,
	// project settings (editor UI or from file, higher priority than game setting to allow to enforce some setting fro this project)
	ECVF_SetByProjectSetting =		0x03000000,
	// per device setting (e.g. specific iOS device, higher priority than per project to do device specific settings)
	ECVF_SetByDeviceProfile =		0x04000000,
	// per project setting (ini file e.g. Engine.ini or Game.ini)
	ECVF_SetBySystemSettingsIni =	0x05000000,
	// consolevariables.ini (for multiple projects)
	ECVF_SetByConsoleVariablesIni = 0x06000000,
	// a minus command e.g. -VSync (very high priority to enforce the setting for the application)
	ECVF_SetByCommandline =			0x07000000,
	// least useful, likely a hack, maybe better to find the correct SetBy...
	ECVF_SetByCode =				0x08000000,
	// editor UI or console in game or editor
	ECVF_SetByConsole =				0x09000000,
 
	// ------------------------------------------------
};

一般用的比较多的就是 ECVF_Scalability | ECVF_RenderThreadSafe .
ECVF_Cheat表示此变量会在正式版本中隐藏。
ECVF_SetByConstructorECVF_SetByConstructor是表示优先级。

也可以注册一个对现有变量的引用FAutoConsoleVariableRef, 引用方便快速但会绕过多项功能(如线程安全、回调、sink、cheat), 调用的函数 类似如下:

FAutoConsoleVariableRef CVarVisualizeGPUSimulation(
    TEXT("FX.VisualizeGPUSimulation"),
    VisualizeGPUSimulation,
    TEXT("Visualize the current state of GPU simulation.\n")
    TEXT("0 = off\n")
    TEXT("1 = visualize particle state\n")
    TEXT("2 = visualize curve texture"),
    ECVF_Cheat
    );

这样在运行项目的时候, 用波浪号 (~) 调出控制台之后就可以

  • 输入 r.MyTest 1启用SetColor功能, r.MyTest 0 禁用SetColor功能。
  • 输入 r.MyTest 2r.MyTest 3 来更改颜色。

Debug生成的源码

如果您希望能够调试 .usf 文件的编译和/或查看处理后的文件,请查看博客文章Debugging the Shader Compiling Process。

快速编译

在编辑器运行时修改 .usf 文件,然后按 Ctrl+Shift+. (句点)或在控制台中输入recompileshaders changed来rebuild 改动过的 Shader .文章来源地址https://www.toymoban.com/news/detail-796016.html

到了这里,关于UE | Shader | 在UE中添加全局Shader的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 利用blender为ue4动画添加根骨骼

    看教程是用3ds max为动画加根骨骼,但是我一直用的是blender(blender是真滴好用),懒得下载3ds max,在网上看了一些教程,记录一下怎么添加根骨骼。 第一步 导入fbx文件(记得导入时选择自动骨骼坐标系,这是为了骨骼位置整齐一点) 导入后记得按ctrl+a选旋转进行变换(如果

    2024年02月11日
    浏览(102)
  • UE4 中可全局获取的变量(例如游戏实例、玩家控制器等) 详解

    🙋‍♂️ 作者:海码007 📜 专栏:UE虚幻引擎专栏 💥 标题:UE4 中可全局获取的变量(例如游戏实例、玩家控制器、游戏模式等) 详解 ❣️ 寄语:加油,一次专注一件事! 🎈 最后: 文章作者技术和水平有限,如果文中出现错误,希望大家能指正,同时有问题的话,欢迎

    2024年02月06日
    浏览(113)
  • UE4动画系统,蒙太奇动画使用,添加动作

    提示:仅供学习参考 前言 一、什么是蒙太奇? 二、实现步骤 1.使用第三人称游戏c++模板创建一个项目,创建动画蒙太奇 2.在动画蓝图中添加蒙太奇 3.绑定鼠标左键输入  4.添加c++代码 5.设置动画蒙太奇  6.点击播放,鼠标右键就能看到挥手效果了 总结 本文介绍如何使用UE4的

    2024年02月05日
    浏览(54)
  • UE4 -使用快捷方式添加命令行参数启动项目

    命令行参数(Command-Line Arguments) 是各类称之为的字符串,当运行可执行文件时可以通过命令行或者可执行文件的快捷方式将其传入。它们的目的是自定义引擎运行的方式,以便符合开发人员或用户的需要。这可以像使得运行编辑器而不运行游戏那么简单;或它也可以更

    2024年02月07日
    浏览(51)
  • UE4 Sequence添加基础动画效果 (05-蓝图触发Sequence)

    在上一篇博客(UE4 Sequence添加基础动画效果 (04-在序列中使用粒子效果))的基础上增加角色进入某个区域触发过场动画的效果。 1.点击编辑FallingRocks来打开落石蓝图  打开后可以发现一个自定义事件节点RockTrigger  2.打开过场动画主序列  将两个落石Actor拖入  3.点击“+Tr

    2024年02月07日
    浏览(58)
  • AirSim学习(2)创建UE4项目并添加AirSim插件

    AirSim学习(1)安装Unreal Engine和AirSim AirSim学习(2)创建UE4项目并添加AirSim插件 AirSim学习(3)AirSim的PythonAPI基本操作——VehicleClient类 AirSim学习(4)AirSim的PythonAPI基本操作——MultirotorClient类 AirSim学习(5)AirSim的C++接口、AirSim与ROS的联合仿真 使用AirSim的一个重要原因就是它的

    2024年04月15日
    浏览(67)
  • UE4 Sequence添加基础动画效果 (03-主序列的使用)

    在上一篇的基础上添加一些摄像头的跟拍效果 1.鼠标右键新建 Animation-》关卡序列    命名为主序列 2.双击打开主序列 3.点击 窗口-》内容浏览器-》内容浏览器2 找到入口序列  4.将入口序列拖入主序列中  5.让时间轴总长保持与入口序列长度一致  6.双击时间轴来进入入口序

    2024年02月06日
    浏览(48)
  • 【UE4 第一人称射击游戏】07-添加“AK47”武器

    素材资料地址: 链接:https://pan.baidu.com/s/1epyD62jpOZg-o4NjWEjiyg 密码:jlhr 效果: 步骤: 1.打开“WalkRun_BS”,将内插时间改为1 2.创建一个文件夹,命名为“Weapons”  进入“Weapons”文件夹后,再创建一个名为“AK47”的文件夹 将“AK.obj”拖入“AK47”文件夹中 勾选骨架网格体,然

    2024年02月05日
    浏览(42)
  • UE4 添加按键输入事件 并在蓝图中使用按键输入节点

    选择Edit/ProjectSettings/Engine/Input 在bindings中可以选择添加ActionMappings或则AxisMappings ActionMappings:按键事件,有按下和抬起两个事件,需要分别用两个键触发 AxisMappings:输入事件,返回值为float,对于键盘,值为0和1 ,对于摇杆为0-1 新建一个事件 : 点击ActionMappings或AxisMappings后的

    2024年01月20日
    浏览(45)
  • carla0.9.13-UE4添加4轮车模型(Linux系统)

    前期准备 建模工具:blender:v3.4.1;可以在Ubuntu Software商店直接下载 虚拟引擎:carla-UE4 (carla v0.9.13),无需额外安装UE4,carla中自带插件 编译carla参照官方文档:https://carla.readthedocs.io/en/0.9.13/ 建模 假设前期工作准备就绪。 1)初始界面,点击General新建 进入界面后,按键盘A全部

    2024年02月05日
    浏览(41)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包