项目是根据网上的教程来实现的(资源也是网上的),最终示例效果如下图
这是网上教程的链接:https://www.raywenderlich.com/57-unreal-engine-4-custom-shaders-tutorial
教程使用的是后处理技术(Post Process),引用了 PPI_Blur 这个材质实例(Material Instance)
材质实例可以修改 PP_GaussianBlur 暴露的参数Radius(模糊半径),保存后便浏览(Browse)不同的模糊效果。
按照教程步骤操作,遇到二个问题:
(1)#include "/Project/Gaussian.usf" 报错
(2)SceneTextureIndex要选择”Post Process Input 0”(后期处理输入0,对应索引值14),教程截图给的是 WorldNormal(场景法线)
先看如何解决无法使用 #include ‘xx.usf’这个问题
(1)先将蓝图工程转为 C++ 工程,选中工程文件右击“生成 Visual Studio 工程文件”
(2)在当前项目的根目录创建 Shader 文件夹,里面编写自定义的 shader 文件(文件以.usf或.ush为后缀)
(3)打开<ProjectName>.build.cs文件,为工程添加“RenderCore”组件
(4)为工程定义一个自定义的主模块(ProjectName.h)
// Fill out your copyright notice in the Description page of Project Settings. #pragma once #include "CoreMinimal.h" #include "Modules/ModuleManager.h" class FCustomShadersModule : public IModuleInterface { public: virtual void StartupModule() override; virtual void ShutdownModule() override; };
模块名称为 F<ProjectName>Module,需要覆写二个方法:StartupModule、ShutdownModule
在对应的 cpp 文件中,映射“/Project”路径
// Fill out your copyright notice in the Description page of Project Settings. #include "CustomShaders.h" #include "Modules/ModuleManager.h" #include "Logging/LogMacros.h" #include "Misc/Paths.h" void FCustomShadersModule::StartupModule() { #if (ENGINE_MINOR_VERSION >= 21) FString ShaderDirectory = FPaths::Combine(FPaths::ProjectDir(), TEXT("Shaders")); AddShaderSourceDirectoryMapping("/Project", ShaderDirectory); #endif } void FCustomShadersModule::ShutdownModule() { } IMPLEMENT_PRIMARY_GAME_MODULE(FCustomShadersModule, CustomShaders, "CustomShaders");
到这里便解决了 #include "/Project/Gaussian.usf" 报错的问题。
PP_GaussianBlur 关键的实现部分,我用红色线框圈了起来。
SceneTextureIndex要选择”Post Process Input 0”(后期处理输入0,对应索引值14),而不是 WorldNormal。
因为上面我们定义了 Global 函数(全局函数),所以具体实现的 shader 里,结合网上的其它示例,用了二种方法(等价的):一种是调用全局函数,另一种是直接调用当前 shader 内的函数。
// return SceneTextureLookup(GetDefaultSceneTextureUV(Parameters, 2), 2, false); struct FunctionStruct { //计算一维高斯模糊 float Cal_1DGaussian(float x) { return exp(-0.5f * pow(3.141 * x, 2)); } }; FunctionStruct FS; //需要获得的场景贴图index static const int SceneTextureID = 14; //纹素大小,比如一张512 X 512大小的纹理,那么纹素大小为(1/512) //用于UV的偏移 float2 TexelSize = View.ViewSizeAndInvSize.zw; //获取当前像素的UV float2 UV = GetDefaultSceneTextureUV(Parameters, SceneTextureID); //用于存储累积的颜色 float3 PixelSum = float3(0, 0, 0); //累积权重值 float WeightSum = 0; //水平与垂直模糊 for (int x = -BlurRadius; x <= BlurRadius; x++) { for (int y = -BlurRadius; y <= BlurRadius; y++) { //计算偏移的UV float2 offsetUV = UV + float2(x, y) * TexelSize; //采样偏移后的贴图颜色 float3 PixelColor = SceneTextureLookup(offsetUV, SceneTextureID, false).rgb; //计算采样像素的权重,/Raduis的原因是为了限制输入范围为-1到1 // float weight = FS.Cal_1DGaussian(x / BlurRadius) * FS.Cal_1DGaussian(y / BlurRadius); float weight = Calculate1DGaussian(x / BlurRadius) * Calculate1DGaussian(y / BlurRadius); //累加颜色 PixelSum += PixelColor * weight; //累加权重值 WeightSum += weight; } } //返回加权平均值 return PixelSum / WeightSum;
上面的代码注释,参考:http://opda.tech/2021/01/03/UE4高斯模糊后处理/
参考链接:
- UE4 Doc :Post Process Materials
- Virtual Shader Source Path - Link custom Shaders - Shadertoy Demo download