1/3
2/3
3/3
【UE4教程】能量护盾制作分享(1/2)
MSuperficial
2021年10月19日 01:30

本专栏是之前上传的可交互能量护盾的教程:

(暂时没精力做视频教程,各位就专栏将就看吧,我会尽量描述仔细一点。)

效果展示

效果拆解

我们先把上面的效果拆解成几部分,然后逐个分析,这样思路也比较清晰:

  1. 菲涅尔

  2. 和其他物体接触的部分

  3. 线框的提取

  4. 护盾甲片的位置偏移

  5. 护盾生成和收起的动画

  6. 护盾受击后产生的能量波

其他细节的问题会在后面说。

模型准备

我是用的 Blender 来制作:

添加初始的 Ico Sphere,细分3级(2级也行),添加倒角,调整参数,让每个面的大小比较均衡:

然后应用掉倒角,展 UV,这里选择 Blender 自带的 Sphere Projection,参数设置如下:

这个时候可以随便添加一个纹理看一下 UV 的情况,会发现球的上下两端不太正确,可以手动调一下,但是也无伤大雅:

这个是正常用来贴纹理的 UV,除此之外还需要另外两套 UV,具体有什么用后面再说。

第二套,先把模型的所有边进行拆分(使用 Edge Split 操作),然后复制一套 UV,在这套 UV 里面,把所有的面在它们各自的位置缩放为 0(先设置中心点为各自的原点,然后A全选,S缩放,输入0):

第三套,再复制一套 UV ,然后使用 Reset 操作重置 UV:

然后把 UV 里面的所有面都调整成这样的三角形:

三套 UV 制作完毕,之后把模型导入到 UE 里面,开始材质制作。

导入 UE 的时候注意不需要生成 Lightmap UV,也不需要生成碰撞体,而是手动添加一个球形碰撞体,因为默认生成的凸包碰撞体并不适合这个模型。

材质设置

创建一个新材质,Blend Mode 设置为 Additive,因为是透明效果的,所以再设置为双面材质。设置 Base Color 为黑色,Opacity 为0.5,添加折射效果:

菲涅尔

使用 Fresnel 节点就可以直接创建一个菲涅尔遮罩,也可以用法线点乘相机方向的方式手动创建。

除此之外,我不希望护盾的里面也有自发光效果,因为会显得比较乱,所以使用了黄框里的节点计算正反面。和计算菲涅尔的方法很像,唯一的区别在于使用世界位置 - 物体位置代替法线。其中 Absolute World Position - Object Position 得到的是由物体中心指向着色点的向量,归一化之后点乘 Camera Vector(由着色点指向摄像机)得到范围(-1, 1)的值。最后在函数外还应用了 Ceil(向上取整)函数,所以正面就是1,背面就是0。

MF_Outside

节点连接到 Emissive,目前的效果:

物体接触部分

这部分有两种方法,一种是像素深度 - 场景深度,一种是借助 UE 的 Mesh Distance Field 实现,总的来说网格距离场的方法效果更稳定,因为第一种方法会被视角方向影响。节点也很简单:

其中黄框是发光距离,里面有一个 MF_ObjectUniformScale 函数,求物体缩放,原理就是把物体空间的(1, 0, 0)变换到世界空间求长度。(假如缩放是2倍,那这个向量就会变成 (2, 0, 0) )

后面还有一个 MF_Texture 给自发光添加纹理,一张六边形纹理,一张噪波纹理。要注意的就是在计算纹理坐标缩放时乘了个 (2, 1),这是由于模型UV本身被挤压了,所以要矫正一下,其他没什么特别的地方。

把这部分和菲涅尔部分相加,连接到Emissive,效果如图:

物体接触部分都会发光

提取线框

线框这部分想了很多种办法,分别有:

  1. 直接勾选上UE材质的 Wireframe 选项。优点是操作简单;缺点是一不能控制线框宽度,二线框渲染不能使用类似于 Material Layer 的方法直接叠加,所以需要第二个 Mesh 专门渲染线框,这样开销增加了很多,遂淘汰。

  2. 使用 Post Process 的方法,基于法线或深度信息做 Outline 渲染。但是半透明材质不适用。

  3. 使用一张线框的 Texture。要单独做一张表示线框信息的 Texture,这应该是比较常用的方法,但这种方法缺点是要增加一次纹理采样,还十分受限于纹理的分辨率。

  4. 使用一个特殊的 UV Map + 程序化计算线框信息。这是我最终采用的方法。

还记得之前做的第三套UV吗,这里就派上用场了。

如何用这套 UV 程序化的计算线框信息呢?这里要用到三角形重心坐标的知识,不熟悉的可以查一下它的定义,简单来说就是下面这张图所示:

(α, β, γ) 就是这个点的重心坐标

具体可以看一下这篇文章:

https://zhuanlan.zhihu.com/p/58199366

越靠近三角形的边,重心坐标的某一个分量就越接近0,所以我们就利用这个特点来计算,先看节点,定义一个新的材质函数 MF_Wireframe:

注意要使用特殊的 UV

首先根据三角形的三个顶点坐标和当前着色点的 UV 坐标计算出重心坐标,然后对重心坐标的三个分量取最小值,计算结果可以在预览图看到,之后对这个结果做一些处理,就可以得到线框的遮罩。

要注意的几个点:第一,Blender 和 UE 的 UV 坐标系是不同的,Blender 原点在左下角,UE 原点在左上角,所以输入的顶点坐标 Y 轴要翻转一下;第二,求重心坐标的节点是 Custom Node,也就是用的 HLSL 代码,代码就是根据重心坐标的计算公式来写的:

(不喜欢用代码也可以照着连节点,我是觉得连节点比较乱,而代码挺清晰的)

代码块
text/x-c++src
自动换行
复制代码
//Calculates barycentric coordinates of a point on a 2d triangle

//Inputs
//float2 vPointA
//float2 vPointB
//float2 vPointC
//float2 vPointP

float2 v0 = vPointB - vPointA;
float2 v1 = vPointC - vPointA;
float2 v2 = vPointP - vPointA;
float v, w, u;

float fInvDenominator = 1 / (v0.x*v1.y - v1.x*v0.y);
v = (v2.x*v1.y - v1.x*v2.y) * fInvDenominator;
w = (v0.x*v2.y - v2.x*v0.y) * fInvDenominator;

float4 result;
result.x=v;
result.y=w;
result.z=1.0-(v+w);
result.w=( (v > 0) && (w > 0) && ((v + w) < 1) );

return result;
复制成功

有了线框的遮罩,就可以把这个遮罩拿来计算自发光了,其中的纹理动画也是随时间添加一些明暗变化:

可以注意到这里也用了之前定义的 MF_Outside 函数(作用和之前一样),同时还用到了 SmoothStep 函数,这个是 HLSL 内置的一个函数,作用是在 Min 到 Max 的范围内生成 (0, 1) 的平滑渐变,具体的原理可以看下面这篇文章:

https://zhuanlan.zhihu.com/p/157758600

在这里用一个系数控制最小值,最大值默认是1,目的是控制边缘部分的线框自发光亮度。

把这部分也加到Emissive的部分,效果如图:

可以看到,不论是三角形还是多边形,使用这种提取线框的方法经过同样的处理后都可以有不错的效果。这种方法的优点是可以控制线框的宽度,还不需要额外的 Mesh 或者 Texture,只是需要多一个 UV 通道。但也有一个缺点就是当一个三角面比较扁的时候,三条边的线框宽度会出现比较明显的差异,但在这个例子中基本没有影响。

护盾甲片偏移

从这部分开始就进入生成动画的制作,这是材质中最重要的部分之一。我们知道材质中的 World Position Offset 就是在世界空间内对顶点坐标进行一个偏移,借此可以实现各种顶点动画。

先新建一个材质用来测试,直接将法线连接到WPO,使用一个参数控制。调整参数看一下效果:

面有一些撕裂,是模型本身有点问题,但是影响不大

在生成动画里,面片还有一个缩放的过程,正常情况下我们可以对整个模型进行缩放,但是要让每个面在各自的位置上缩放需要同时结合两个方向的偏移来近似达到这样的效果。如图,第一部分是向物体中心偏移(这一步就相当于缩放物体,也就是让面缩小),第二部分是法线,只要这两部分移动的距离相同,那面片基本上就是在原地缩小。

有一个问题就是这个系数的范围不好控制,如果模型大小变化,这个系数也要相应变化,所以我希望用一个0到1的系数统一控制缩放,同时使用 Object Bounds 来获取护盾的大小(因为护盾是个球,所以可以这么用)。

在这个基础上,如果再加上一些法线和切线方向的额外偏移,效果会更多样。里面的切线不是 Vertex Tangent 节点,这个切线方向是不统一的,用(0, 0, 1)叉乘法线计算的切线会拥有统一的方向。在计算额外偏移距离那里也使用了 MF_ObjectUniformScale 来适应物体的缩放。

这儿换了个细分更多的球

然后我们需要一个从下到上渐变的系数,来实现生成动画的参数控制,这里使用 UV 坐标计算,其中 Time 控制动画进度,Fallout 控制系数衰减的快慢:

可以用这个理解系数的计算:https://www.desmos.com/calculator/jdpyqislls

左边是系数,右边是效果

是不是发现效果不太对,要注意 WPO 是以顶点为单位的,而不是以我们这个护盾的一个“甲片”(也就是一个面)为单位的,所以面会被扭曲掉。这时我们就还需要特殊手段来处理。

回忆一下之前制作的第二套 UV:

在这个 UV 里面,所有面都缩小到一点,也就是说,同一个面上的所有点都拥有一个统一的参数——UV 坐标,这也就可以做到以一个面为单位去操作模型了。把节点里面的UV坐标换成这个特殊的:

这样就完成了生成动画的材质部分,后面要通过蓝图控制材质参数。先把这部分节点复制到原来的材质里面,然后给材质上个色:

目前的效果(参数可以自己调整):

目前已经实现了材质的大部分,还差能量波以及通过蓝图控制材质参数这两大块,后面的部分也是比较重要的地方,之后会放到下半篇教程里面。

欢迎大家在评论区交流讨论,如果有讲的不清楚的地方我再补充。