在UE4中创建高斯模糊后期材质

本篇教程来自国外一位大佬的文章,在这里我会总结下制作过程,有些不重要的地方可能会直接跳过。原文链接:

https://www.raywenderlich.com/57-unreal-engine-4-custom-shaders-tutorial

关于如何在custom node中引用.usf文件,还是移步这里:

https://blog.csdn.net/weixin_43369654/article/details/92607776

准备工作

首先在工程文件根目录创建一个Shaders的文件夹,打开shaders文件夹,创建一个名为Gaussian.usf的文件。

unreal engine shadersunreal engine shaders

unreal engine shaders

新建一个材质球,并把细节面板的Material Domain设置成postprocess。按照如图的方式连接。Round(四舍五入)保证半径总是一个整数。Gaussian Blur(高斯模糊)就是我们本次的重点了,这是一个custom node:

创建好custom node后,看细节面板:

Code:写shader代码的地方,UE4只支持HLSL

Description:命名你的自定义节点

Inputs:输入接口

在custom node里写代码时需要注意的是,并不能按照常规的方法来写。

比如你写了如下代码:

return 1;

编译器会翻译成如下(没有缩进):

MaterialFloat3 CustomExpression0(FMaterialPixelParameters Parameters)
{
return 1;
}

如果你的代码是这样写的:

    return 1;
}

float MyGlobalVariable;

int MyGlobalFunction(int x)
{
    return x;

得到的HLSL语法结果将会是:

MaterialFloat3 CustomExpression0(FMaterialPixelParameters Parameters)
{
    return 1;
}

float MyGlobalVariable;

int MyGlobalFunction(int x)
{
    return x;
}

注意开头和结尾处会少两个大括号,务必按照这种方式来写custom node,不然会报错。

 

 

高斯模糊公式

\LARGE f(x) = e^{-0.5(\pi x)^{2}

函数的表现形式如下:

unreal engine shaders

这是一个简化的一维高斯模糊公式,如图可见它的接收值域为[-1,1],输出值域为[0,1]。

接下来,新建一个custom node并命名为Global,在代码栏里输入下列文本:

    return 1;
}

float Calculate1DGaussian(float x)
{
    return exp(-0.5 * pow(3.141 * (x), 2));

Calculate 1DGaussian():这个函数就是上述一维高斯模糊公式的代码化。

注意Output Type要改成Float4,因为接下来你需要将GlobalSceneTexture相乘,得到如下:

unreal engine shaders

如此一来任何后续的自定义节点都可以使用Global中定义的函数。

打开Gaussian.usf 并将里面的代码替换为下述代码:

static const int SceneTextureId = 14;
float2 TexelSize = View.ViewSizeAndInvSize.zw;  //将偏移量转换到UV空间
float2 UV = GetDefaultSceneTextureUV(Parameters, SceneTextureId);   //当前像素的UV
float3 PixelSum = float3(0, 0, 0);    //将内核(kernel)中每个像素的颜色累加(sum)起来。
float WeightSum = 0;    //将内核中每个像素的权重累加起来。

对于SceneTexture的ID这里有一个对照的列表:

unreal engine shaders

故SceneTextureId = 14 对应的就是Post Process Input 0 了。

关于内核(kernel)的概念会在下一篇文章单独拿出来讲,这里不理解的朋友可以暂时不用管它是什么意思。

 紧接着上述代码下面加入两个嵌套的for循环,加入后如下:

static const int SceneTextureId = 14;
float2 TexelSize = View.ViewSizeAndInvSize.zw;                      //将偏移量转换到UV空间
float2 UV = GetDefaultSceneTextureUV(Parameters, SceneTextureId);   //当前像素的UV
float3 PixelSum = float3(0, 0, 0);                                  //将内核(kernel)中每个像素的颜色累加(sum)起来。
float WeightSum = 0;                                                //将内核中每个像素的权重累加起来。

for (int x = -Radius; x <= Radius; x++)           //水平方向
{
    for (int y = -Radius; y <= Radius; y++)       //垂直方向
    {

    }
}

其中最外层的for循环是x轴(水平方向)的偏移量,嵌在内部的for循环是y轴(垂直方向)的偏移量。理论上来讲,这个两层嵌套的for循环将会以当前的像素为中心形成一个内核,如下:

unreal engine toon outline

不断运动的九宫格网格就是内核(kernel)。

内核的尺寸是2r+1,比如半径(radius)= 2,则内核 = (2 * 2+1)x(2 * 2+1)即 5x5网格。

接下来在内层的for循环里加入以下代码:

float2 Offset = UV + float2(x, y) * TexelSize;           //计算采样像素的相对偏移量并将其转换为UV空间
float3 PixelColor = SceneTextureLookup(Offset, SceneTextureId, 0).rgb;    //通过偏移量对场景纹理进行采样
float Weight = Calculate1DGaussian(x / Radius) * Calculate1DGaussian(y / Radius);    //计算采样像素的加权。为了计算二维的高斯模糊,你需要将两个一维的高斯模糊相乘(multiply),除以(divide)radius是因为简化的高斯模糊公式的值域是[-1,1],所以需要将它们的值归一化。
PixelSum += PixelColor * Weight;          //将加权的颜色添加到PixelSum
WeightSum += Weight;                      //将权重添加给WeightSum

最后,在for嵌套循环外加上返回值:

static const int SceneTextureId = 14;
float2 TexelSize = View.ViewSizeAndInvSize.zw;                      //将偏移量转换到UV空间
float2 UV = GetDefaultSceneTextureUV(Parameters, SceneTextureId);   //当前像素的UV
float3 PixelSum = float3(0, 0, 0);                                  //将内核(kernel)中每个像素的颜色累加(sum)起来。
float WeightSum = 0;                                                //将内核中每个像素的权重累加起来。

for (int x = -Radius; x <= Radius; x++)           //水平方向
{
    for (int y = -Radius; y <= Radius; y++)       //垂直方向
    {
        float2 Offset = UV + float2(x, y) * TexelSize;           //计算采样像素的相对偏移量并将其转换为UV空间
        float3 PixelColor = SceneTextureLookup(Offset, SceneTextureId, 0).rgb;    //通过偏移量对场景纹理进行采样
        float Weight = Calculate1DGaussian(x / Radius) * Calculate1DGaussian(y / Radius);    //计算采样像素的加权。为了计算二维的高斯模糊,你需要将两个一维的高斯模糊相乘(multiply),除以(divide)radius是因为简化的高斯模糊公式的值域是[-1,1],所以需要将它们的值归一化。
        PixelSum += PixelColor * Weight;          //将加权的颜色添加到PixelSum
        WeightSum += Weight;                      //将权重添加给WeightSum

    }
}

return PixelSum / WeightSum;

 应用后,调节Radius就可以看到效果了(记得要把该材质赋给PostProcess才能看到效果)。

Custom Node的局限性

  • 访问限制

 Custom Node无法访问大部分的渲染管线,例如光照信息和运动方向。

  • 引擎版本兼容性

 你在4.19版本写的Custom Node不保证能在4.22也可以继续运行。建议多留意每次更新的官方文档。

  • 优化

 这一条可以算作是优点而非局限性了。自定义节点最大的好处就是观赏简洁,不必在打开一个shader的时候发现满屏幕的连线而感到头疼。官方的建议是,如果使用连线的方式很麻烦的时候,可以选择使用自定义节点自己去写。

发布了10 篇原创文章 · 获赞 4 · 访问量 1501

猜你喜欢

转载自blog.csdn.net/weixin_43369654/article/details/93618504
今日推荐