找回密码
 立即注册
查看: 512|回复: 0

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

[复制链接]
发表于 2021-4-9 13:01 | 显示全部楼层 |阅读模式
原文|《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[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》

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

×
懒得打字嘛,点击右侧快捷回复 【右侧内容,后台自定义】
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

小黑屋|手机版|Unity开发者联盟 ( 粤ICP备20003399号 )

GMT+8, 2024-11-15 08:41 , Processed in 0.091454 second(s), 26 queries .

Powered by Discuz! X3.5 Licensed

© 2001-2024 Discuz! Team.

快速回复 返回顶部 返回列表