Unreal Engine 4 使用HLSL自定义着色器(Custom Shaders)教程(上)

原文|《Unreal Engine 4 Custom Shaders Tutorial》
作者|Tommy Tran Apr 2 2018 | 翻译 开发游戏的老王
阅读时长|25分钟 内容难度|入门级
在本文中你将学会使用HLSL创建自定义着色器

译者注: 本教程提供了范例工程,如果需要可以到原文网站免费注册并下载

基于节点的材质编辑器对于艺术家非常友好,然而它依然存在局限性,比如:你无法用它创建类似Loop循环或switch分支等结构。

幸运的是,你可以创建一个自定义节点,然后通过编写HLSL代码来摆脱这些局限性。

在本教程中,你将学会:

  • 创建自定义节点并设置其输入引脚
  • 将材质节点转换成HLSL
  • 使用外部编辑器编写着色器
  • 创建HLSL函数

为了诠释以上知识点,我们将使用HLSL为场景画面去色(Desaturate),输出多个场景图片并创建高斯模糊(Gaussian blur)

注:本教程假设读者已经掌握基本的Unreal Engine使用,并且熟悉诸如C++/C#这种类C语言,如果你熟悉Java这样的语言,也是可以的。

本文是Unreal Engine着色器教程四部曲之一:

开始吧

下载实例项目 (见原教程地址),解压并找到CustomShadersStarter目录,打开CustomShaders.uproject。你可以看到如下场景:

在这里插入图片描述
首先,让我们在后期处理材质中创建一个自定义节点,从而使用HLSL给画面去色。

创建自定义节点

定位到Materials目录下,打开PP_Desaturate。我们将在这里编辑并实现去色效果。

在这里插入图片描述
首先,创建一个Custom节点。和其它节点一样,它可以拥有多个输入引脚和唯一一个输出引脚。

在这里插入图片描述

接下来,确保Custom节点被选中的状态下,在细节面板上,你可以看到下面这样:

在这里插入图片描述

各个属性的说明:

  • Code:填写HLSL代码的地方
  • Output Type:输出值类型可以从一维浮点数(CMOT Float 1)到一个四维向量(CMOT Float 4)。
  • Description:该节点上显示的文字。可以用它来命名节点。本例中设为“Desaturate”
  • Inputs: 添加和命名输入引脚的地方。然后就可以在代码中使用这个名称来引用该输入了。这里我们将0号引脚设为SceneTexture。

在这里插入图片描述

在Code中填写如下内容,就可以实现去色了:

return dot(SceneTexture, float3(0.3,0.59,0.11));

注: dot()是一个HLSL的内置函数(见《Intrinsic Functions表》)。如果你想要使用类似atan()或者lerp(),可以先查一下它们是否已经存在了。

最后,如下图所示链接:

在这里插入图片描述
小结:

  1. SceneTexture:PostProcessInput0节点的作用是提供当前画面的像素。
  2. Desaturate节点将会获取颜色信息然后进行去色处理。然后它会把结果输出到自发光通道(Emissive Color)。

点击应用然后关闭PP_Desaturate,于是场景画面就被去色了。

在这里插入图片描述
你可能很诧异,Desaturate节点的代码到底是什么东西。实际上,当你使用材质节点的时候,它们都会被转换成HLSL。如果查看一下生成的节点,就可以发现对应的部分,这里就是Desaturate节点转换成HLSL的地方。

在下个部分,你将学会如何将材质节点转换成HLSL。

将材质节点转换成HLSL

本教程中我们将把SceneTexture节点转换成HLSL,这一步对于创建高斯模糊是非常重要的。

首先,找到Maps文件夹并打开GaussianBlur。然后回到Materials文件夹并打开PP_GaussianBlur

在这里插入图片描述

虚幻引擎会把所有的节点都转换成HLSL。本例中,虚幻会把SceneTexture节点转换成HLSL。

要查看整个材质所对应的HLSL代码,选择Window\HLSL Code,接着像下图一样的独立窗口将被打开。

在这里插入图片描述

注: 如果该窗口的HLSL代码是空白的,你需要开启工具栏中的Live Preview
在这里插入图片描述

由于生成的代码有几千行,很难定位。为了方便查找,点击copy按钮然后把内容粘贴到一个文本编辑器中(我使用的是Notepad++)。然后就可以关闭HLSL代码窗口。

现在,我们要找到SceneTexture部分代码,最简单的办法就是找到CalcPixelMaterialInputs()定义的地方。这个函数就是引擎计算所有材质输出的地方。当你定位到该函数的底部,会看到每一个节点的最终输出值:

在这里插入图片描述
由于这是一个后期处理材质,你只需要关心EmissiveColor。如你所见它的值是Local1。所谓 LocalX变量就是一个函数用来存储中间值的局部变量。向上翻看代码,你就会看到引擎如何计算每一个局部变量了。

MaterialFloat4 Local0 = SceneTextureLookup(GetDefaultSceneTextureUV(Parameters, 14), 14, false);
MaterialFloat3 Local1 = (Local0.rgba.rgb + Material.VectorExpressions[1].rgb);

最终的局部变量一般只是个象征性的计算,我们可以忽略掉它。这就意味着SceneTextureLookup()就是我们要找的SceneTexture节点了。

现在我们找到目标函数了,测试一下它吧。

使用SceneTextureLookup函数

首先解释一下SceneTextureLookup()函数的参数都是干嘛的:

float4 SceneTextureLookup(float2 UV, int SceneTextureIndex, bool Filtered)
  • UV: 即UV坐标,像素的位置。例如:UV是 (0.5, 0.5) 意味着这个像素位于图像的正中央。
  • SceneTextureIndex: 决定从哪个缓存纹理中抽样。下表就是缓存纹理和其Index值的对应关系。例如:我们想从Post Process Input 0中抽样,那么就要将14作为index值。
  • Filtered: 被抽样纹理是否需要双线性过滤(bilinear filtering),这个值一般会被设为false。

在这里插入图片描述
我们尝试输出世界法线(World Normal)来测试一下。在材质编辑器中创建一个自定义节点并命名为Gaussian Blur,然后填上如下代码:

return SceneTextureLookup(GetDefaultSceneTextureUV(Parameters, 8), 8, false);

这将会输出每一个像素的世界法线。GetDefaultSceneTextureUV()将会获取当前像素的UV。

注:在4.19版本以前,我们的添加TextureCoordinate节点作为输入才能够获得UV。在4.19以后正确的方法是使用GetDefaultSceneTextureUV()并提供你想要的index.

接下来,断开SceneTexture节点,把Gaussian Blur节点连接到Emissive Color并点击应用。

在这里插入图片描述

此时,你将得到如下报错:

[SM5] /Engine/Generated/Material.ush(1410,8-76):  error X3004: undeclared identifier 'SceneTextureLookup'

它是在告诉你SceneTextureLookup()并不存在于你的材质中。为什么刚才使用SceneTexture节点时是好使的,在自定义节点里就不好使了呢?答案是:当你使用SceneTexture节点编译器会包含SceneTextureLookup()函数的定义。在自定义节点中没有包含该定义,因此就无法使用它。

幸运的是,这个问题很好解决。把SceneTexture节点连接到同样的抽样纹理上就可以了。本例中,我们使用WorldNormal。

然后,把它连接到Gaussian Blur节点。最后把输入引脚命名为SceneTexture

在这里插入图片描述
现在编译器中就包含SceneTextureLookup()的定义了。点击应用并回到主编辑器,你将看到每个像素的世界法线。

在这里插入图片描述
截止目前,在自定义节点中编辑代码还行,因为我们写的都是些小片段。然而从现在开始代码会越来越长,也会越来越难维护。

为了改善工作流,虚幻允许我们包含外部着色器文件。这样,我们就可以在自己的文本编辑器中写代码,然后回到虚幻里编译。

使用外部着色器文件

首先,我们得创建一个Shaders文件夹。当我们在自定义节点中使用#include时,虚幻会查找这个文件夹。

在工程文件夹下创建新一个文件夹并命名为Shaders,工程文件家结构如下:

在这里插入图片描述

接下来,在Shaders中新建一个文件并命名为Gaussian.usf。这就是我们得着色器文件。

在这里插入图片描述

注:着色器文件必须以 .usf 或 .ush 为扩展名。

在文本编辑器中打开Gaussian.usf并插入如下代码。请确保每次修改代码以后都要妥善保存。

return SceneTextureLookup(GetDefaultSceneTextureUV(Parameters, 2), 2, false);

这段代码和之前一样,只不过输出得是漫反射色( Diffuse Color)。

为了能够让虚幻引擎找到新的文件夹和着色器文件,需要重启编辑器。重启以后,请确保你当前在GaussianBlur地图中。然后重新打开PP_GaussianBlur并把Gaussian Blur中得的代码替换成以下内容:

#include "/Project/Gaussian.usf"
return 1;

这样当我们编译的时候,编译器会将代码第一行的内容替换成Gaussian.usf

注:我们无需Project替换成我们的项目名称。

点击应用并回到主编辑器,此时我们会看到之前的世界法线变成了漫反射色。

在这里插入图片描述

目前位置,对于简单得着色器开发已经万事俱备了,接下来的部分我们将正式开发一个高斯模糊!

注:本文并不是高斯模糊教程,我们并不会花太多时间解释高斯模糊的原理。如果你想了解更多,可以阅读《Gaussian Smoothing》以及《Calculating Gaussian Kernels》

评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值