[译文]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着色器教程四部曲之一:
第一部分: 卡通着色(Cel Shading)
第二部分:卡通风轮廓线(Toon Outline)
第三部分:使用HLSL自定义着色器(Custom Shaders Using HLSL) 【本文】第四部分:绘画风滤镜(Paint Filter)【敬请期待】
开始吧
下载实例项目 (见原教程地址),解压并找到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(),可以先查一下它们是否已经存在了。最后,如下图所示链接:
小结:
SceneTexture:PostProcessInput0节点的作用是提供当前画面的像素。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.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并点击应用。
此时,你将得到如下报错:
/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》
页:
[1]