教程链接:Overwatch Shield
项目链接:OverwatchShieldTemplate
Pipeline & Shader:Built-in,Unlit

本文是对 Overwatch Shield 学习过程的记录和总结,不是完全的翻译,更多的细节和图文建议跳转原博

效果分析

使用守望先锋中Reinhardt的护盾作为参考

整体观察

可以看出护盾是半透明的,带有一层基色,可以微微照亮周边区域
绕着护盾走可以发现,护盾的背面也可以显示(关闭Cull),并且有明显的厚度

蜂窝纹理

蜂窝会从中心沿x轴方向搏动,仔细观察还可以发现蜂窝并不是同时搏动的

蜂窝状电流

电流沿着蜂窝状边缘向外扩散,不能难发现扩散的形状一个菱形
所以这个效果其实是一个从护盾中心,从点变大的菱形框点亮蜂窝边缘的过程

固定边缘

仔细看就会发现边缘效果由两部分组成:固定边缘、相交边缘

相交边缘

相交边缘的效果基本和固定边缘一样

效果总结

  1. 基础效果:整体透明,带有基色,Cull Off
  2. 蜂窝纹理:蜂窝纹理从护盾中心沿x轴搏动,带有一定先后顺序
  3. 电流脉冲:蜂窝电流从护盾中心按菱形扩散
  4. 固定边缘:越边缘颜色越实
  5. 相交边缘:与其他物体交叉处有高亮
  6. 自发光:护盾可以微微照亮周边

项目设置

场景方面,模拟了视频(图片)中的物体:
护盾与地面相交,有物体穿过护盾,方便测试效果

另外项目的色彩空间使用了线性空间,因为线性空间的精度更高

更多关于颜色空间的内容,可以查看GAMMA AND LINEAR SPACE - WHAT THEY ARE AND HOW THEY DIFFER和Unity User Manual Color space

Shader编写

基础效果

目标效果:整体透明,带有基色,Cull Off
没啥东西,改 Tag,关 Cull,设置 Blend,完事

Pass
{Tags {"RenderType" = "Transparent" "Queue" = "Transparent"}Cull offBlend SrcAlpha oneHLSLPROGRAM// ...fixed4 frag (v2f i) : SV_Target{return _Color;}ENDHLSL
}

基础效果图

蜂窝搏动

目标效果:蜂窝纹理从护盾中心沿x轴搏动,带有一定先后顺序

效果拆分:

  1. 蜂窝纹理 + 整体呼吸效果
  2. 打乱呼吸顺序
  3. 呼吸效果沿x轴扩散

1.蜂窝纹理 + 整体呼吸效果

fixed4 frag (v2f i) : SV_Target
{// 蜂窝图案fixed4 pulseTex = tex2D(_PulseTex, i.uv);fixed4 pulseTerm = pulseTex * _Color * _PulseIntensity;// 呼吸效果pulseTerm *= abs(sin(_Time.y * _PulseTimeScale));return fixed4(_Color.rgb + pulseTerm.rgb, _Color.a);
}

2.打乱呼吸顺序

打乱顺序的方法有很多,因为这里使用的蜂窝纹理刚好有深浅变化,可以直接用来作为决定呼吸顺序的因子

这里的代码会让 r 通道越大的地方亮的越早

fixed4 frag (v2f i) : SV_Target
{// 蜂窝图案fixed4 pulseTex = tex2D(_PulseTex, i.uv);fixed4 pulseTerm = pulseTex * _Color * _PulseIntensity;float breath = _Time.y * _PulseTimeScale;float pulseOffset = pulseTex.r * _PulseTexOffsetScale;// 随机呼吸效果pulseTerm *= abs(sin(breath + pulseOffset));return fixed4(_Color.rgb + pulseTerm.rgb, _Color.a);
}

为了防止 sin() 的负值导致奇怪的结果出现,需要滤去负值
同时也不希望负值直接被处理为常数导致图案无变化,使用了 abs()

3.呼吸效果沿x轴扩散

由于制作模型时,将模型的中心点设置在了正中心(默认是底部中心),可以直接取模型空间的x坐标来确定顶点与中心轴的距离

fixed4 frag (v2f i) : SV_Target
{// 蜂窝图案fixed4 pulseTex = tex2D(_PulseTex, i.uv);fixed4 pulseTerm = pulseTex * _Color * _PulseIntensity;float breath = _Time.y * _PulseTimeScale;float pulseOffset = pulseTex.r * _PulseTexOffsetScale;float horizontalDist = abs(i.posOS.x);float xOffset = horizontalDist  * _PulsePosScale;// 沿x轴扩散的随机呼吸效果pulseTerm *= abs(sin(breath + pulseOffset - xOffset));return fixed4(_Color.rgb + pulseTerm.rgb, _Color.a);
}

电流脉冲

目标效果:蜂窝电流从护盾中心按菱形扩散
大体上思路和蜂窝搏动效果有很多相似的地方

效果拆分:

  1. 蜂窝电流 + 整体呼吸效果
  2. 蜂窝电流以菱形从护盾中心扩散

1.蜂窝电流 + 整体呼吸效果

fixed4 frag (v2f i) : SV_Target
{// 蜂窝搏动// ...// 蜂窝电流fixed4 hexEdgeTex = tex2D(_HexEdgeTex, i.uv);fixed4 hexEdgeTerm = hexEdgeTex * _HexEdgeColor * _HexEdgeIntensity;// 呼吸float edgeBreath = _Time.y * _HexEdgeTimeScale;hexEdgeTerm *= saturate(sin(edgeBreath));// 只看电流// return fixed4(_Color.rgb + pulseTerm.rgb + hexEdgeTerm.rgb, _Color.a);return fixed4(hexEdgeTerm.rgb, _Color.a);
}

1.5呼吸周期调整

现在的呼吸周期使用的是 saturate(sin(x)) 函数,呼吸效果如下图:

图中当曲线位于 x 轴上方时,电流才会出现,到达 1 时达到最亮,随后变暗直到消失,开始下一轮循环

由于现在的曲线是一个单纯的 sin 曲线,电流亮和不亮的时间是相同的

将曲线向下平移,就会让亮的时间变短,暗的时间变长

可以将这个下移量设置为变量 _HexEdgeWidthModifier,方便调试

fixed4 frag (v2f i) : SV_Target
{// 蜂窝搏动// ...// 蜂窝电流fixed4 hexEdgeTex = tex2D(_HexEdgeTex, i.uv);fixed4 hexEdgeTerm = hexEdgeTex * _HexEdgeColor * _HexEdgeIntensity;// 呼吸float edgeBreath = _Time.y * _HexEdgeTimeScale;hexEdgeTerm *= saturate(sin(edgeBreath) - _HexEdgeWidthModifier);// 只看电流// return fixed4(_Color.rgb + pulseTerm.rgb + hexEdgeTerm.rgb, _Color.a);return fixed4(hexEdgeTerm.rgb, _Color.a);
}

但是根据实现效果看,电流只是淡淡的闪一下就灭了,效果非常不明显

这是因为在 x 轴上方的部分因为被下移,而无法到达 1 值,导致了电流颜色一直无法到达饱和的效果

解决方法是把它拉长到 1:
(1 - _HexEdgeWidthModifier)/ edgeNormalizer = 1,
edgeNormalizer = 1 - _HexEdgeWidthModifier

fixed4 frag (v2f i) : SV_Target
{// 蜂窝搏动// ...// 蜂窝电流fixed4 hexEdgeTex = tex2D(_HexEdgeTex, i.uv);fixed4 hexEdgeTerm = hexEdgeTex * _HexEdgeColor * _HexEdgeIntensity;// 呼吸float edgeBreath = _Time.y * _HexEdgeTimeScale;// 周期调整float edgeNormalizer = 1 - _HexEdgeWidthModifier;hexEdgeTerm *= saturate(sin(edgeBreath) - _HexEdgeWidthModifier) / edgeNormalizer;// 只看电流// return fixed4(_Color.rgb + pulseTerm.rgb + hexEdgeTerm.rgb, _Color.a);return fixed4(hexEdgeTerm.rgb, _Color.a);
}

2.菱形光从护盾中心扩散

在蜂窝搏动效果中,使用的是 sin 沿 x 轴扩散
这里要使用一个菱形扩散(“ 菱形 ” 虽然不准确,方便起见先这么叫)

由于 x 对应的 y 不唯一,这个菱形无法使用函数表示

但是,它可以表示为这个式子:abs(y) + abs(x) = 1,等式右边的数字越大,菱形就越大

这意味着如果计算 sin(abs(x)+abs(y)),就可以构造一个边长不断在一定区间变化的菱形的图案,只要加入时间参数就可以让它动起来

补充
上面的加粗字是这篇笔记全文唯一一个完完全全的翻译,没有一点自己的理解
因为坦白说,这句话压根就没理解…
想了好久也想不明白这到底是什么从 0 突变到 2048 的因果联系啊…???
 
Desmos 没办法表示二维图像,好在还有Gooogle计算器:

有了图果然就好理解多了,这波啊,绝对是先有图,后有粗体字

fixed4 frag (v2f i) : SV_Target
{// 蜂窝搏动// ...// 蜂窝电流fixed4 hexEdgeTex = tex2D(_HexEdgeTex, i.uv);fixed4 hexEdgeTerm = hexEdgeTex * _HexEdgeColor * _HexEdgeIntensity;// 呼吸float edgeBreath = _Time.y * _HexEdgeTimeScale;float verticalDist = abs(i.posOS.z);// 周期调整float edgeNormalizer = 1 - _HexEdgeWidthModifier;// 菱形float diamondPattern = (horizontalDist + verticalDist) * _HexEdgePosScale;hexEdgeTerm *= saturate(sin(diamondPattern - edgeBreath) - _HexEdgeWidthModifier) / edgeNormalizer;// 只看电流// return fixed4(_Color.rgb + pulseTerm.rgb + hexEdgeTerm.rgb, _Color.a);return fixed4(hexEdgeTerm.rgb, _Color.a);
}

这里的verticalDist取的是z方向的值,因为在这个模型的坐标系中,上方向是z

把最后的return修改一下,电流这里也算是完成了

轮廓线

目标效果:越边缘颜色越实
这个效果相对于前面的两个很好实现,只要使用渐变边缘的遮罩纹理,再进行一些调整就可以了

fixed4 frag (v2f i) : SV_Target
{// 蜂窝搏动、蜂窝电流// ...fixed4 edgeTex = tex2D(_EdgeTex, i.uv);fixed4 edgeTerm = edgeTex.a * _Color * _EdgeIntensity;// return fixed4(_Color.rgb + pulseTerm.rgb + hexEdgeTerm.rgb + edgeTerm, _Color.a);return fixed4(edgeTerm.rgb, _Color.a);
}


大粗边缘太傻太闷了,要想办法把边缘变薄
如果不想改贴图的话,就需要进行一些数学计算,比如pow()

fixed4 edgeTerm = pow(edgeTex.a, _EdgeExponent) * _Color * _EdgeIntensity;


需要注意的是,因为颜色的值域是[0, 1],_EdgeExponent越大意味着边缘遮罩将更快的达到1并保持恒定,导致边缘看起来缺乏过渡

相交边缘

目标效果:与其他物体相交处有高亮

虽然把固定边缘和相交边缘分成了不同的效果制作,为了让最终的效果不突兀,还是需要保证固定边缘和相交边缘看起来就像是护盾的连续边界一样

把它们分成两部制作,只是因为两个边缘的形成原因不一样,需要使用不同的方法来判定边缘:
一个是护盾原有的边缘,固定存在(边缘纹理);一个是护盾与其他物体相交形成的边缘,需要根据相交情况进行判断(深度纹理

原理分析

为什么判断相交情况需要深度纹理呢?

首先,当两个物体相交时,它们之间的距离会变成0
而深度值指的是点到相机(屏幕)的距离
那么只要护盾上某点的深度值和环境中其他物体上的点的深度值差值足够小,就会表明这两个点足够接近,足以产生相交线

护盾的深度值可以在相机空间计算获得,环境的深度值可以通过采样相机渲染的深度纹理获得

补充
根据之前学过的阴影章节可以知道,如果 Shader (和它的 FallBack)中没有 ShadowCaster Pass,它所在的物体就不会出现在深度纹理中
本例的 Shader 中不包含 默认的 ShadowCaster Pass 和 FallBack,所以深度纹理中没有存储护盾的深度值

摄像机深度纹理获取以及采样

想要获得相机的深度纹理,需要在相机上挂一个脚本,确保它渲染深度纹理:

void OnEnable()
{GetComponent<Camera>().depthTextureMode = DepthTextureMode.DepthNormals;
}

如果设置正确,在相机组件的Inspector面板上,会出现下面的提示:

然后就可以在Shader中使用这个纹理了

sampler2D _CameraDepthNormalsTexture; // 必须使用这个名字

这里需要注意的是,此时相机渲染出来的深度纹理是在屏幕空间中得到的,因此也需要在屏幕空间进行采样

o.posSS = ComputeScreenPos(o.pos);// UnityCG.cginc中的ComputeScreenPos大意
inline float4 ComputeScreenPos (float4 pos)
{float4 o = pos * 0.5f;o.xy = float2(o.x, o.y*_ProjectionParams.x) + o.w;o.zw = pos.zw;return o;
}

其中 _ProjectionParams.x 取值为 1.0 或 -1.0,取决于当前平台的屏幕坐标系的 y 轴是向上还是向下,其他的具体原理可以见之前的计算机图形学笔记,齐次裁剪空间到屏幕空间的变换

护盾深度值计算

想要计算深度值,必须先计算相机空间的顶点位置,它的 z 值就是该点的深度

因为在片元着色器中进行比较时,两个深度值都为正更方便,所以把这个深度值乘以 -1(相机空间中 z 的反方向指向相机的朝向,在相机面前的点的 z 都为负数)

o.depth = -mul(UNITY_MATRIX_MV, v.vertex).z;

现在,这个深度值是顶点到相机的实际距离
但是深度纹理存储的值是颜色,值域是 [0 ,1](在近平面上为 0,在远平面上为 1)
所以为了能正确比较两个深度值,将顶点深度值重映射到 [0,1],需要除以相机到远平面的距离(_ProjectionParams.w = 1 / FarPlane)

o.depth = -mul(UNITY_MATRIX_MV, v.vertex).z * _ProjectionParams.w;

补充
其实这里数学上并不严谨,顶点到相机的实际距离值域应该是 [near,far],重映射的结果应该是 (z - near) / (far - near),而不是简单的 z / far
但是由于本例中相机的近平面为 0.1,所以在这里没有太大影响,不如简化计算直接取 z / far

相交效果制作

将上面两步得到的屏幕空间坐标、护盾深度值传递到 Fragment Shader,然后就开始制作效果

首先是求深度纹理中环境深度值,和护盾深度值的差值

float diff = tex2D(_CameraDepthNormalsTexture, i.posSS.xy).r - i.depth;

然而事情并没有这么简单,还有两个问题要处理:

  1. 因为使用了透视投影,需要做一个透视除法来减小透视带来的歪斜效果
  2. 深度值并不是简单的存储在_CameraDepthNormalsTexture纹理的一个通道中,而是存储在 zw 两个通道中(可以提升精度)

解决方法倒是很简单:

// DecodeFloatRG为UnityCG.cginc中自带的辅助函数
float diff = DecodeFloatRG(tex2D(_CameraDepthNormalsTexture, i.posSS.xy / i.posSS.w).zw) - i.depth;

补充
注意 diff 的值是非负的
虽然两个距离做差应该有三种情况(>、<、= 0),但被减项是 “ 场景中不透明物体的深度值 ”,意味着当 diff < 0 时(即护盾深度值较大),护盾本身被不透明物体遮挡,不会被渲染;只有在 diff > 0 时(即护盾深度值较小),护盾才会被渲染。因此,从渲染结果的角度看,diff ≥ 0,是非负的
 
关于透视除法,可以复习之前的计算机图形学笔记,或者画个侧面的视锥体切一切

理论上讲,现在已经得到了环境深度值和护盾深度值的非负差值(值域 [0,1]),只要取 1 - diff,就可以得到一个相交边缘的遮罩

fixed4 frag (v2f i) : SV_Target
{// 蜂窝搏动、电流脉冲、固定边缘// ...float diff = DecodeFloatRG(tex2D(_CameraDepthNormalsTexture, i.posSS.xy / i.posSS.w).zw) - i.depth;float intersectGradient = 1 - diff;return intersectGradient;
}

但是事情好像没有这么简单:

这是因为 diff 的值太小了,没能将非相交边缘的部分减到 0,可以通过乘上一个常量来增大这个值,比如 20

diff *= 20;
float intersectGradient = 1 - min(diff, 1.0);

这里将 intersectGradient 的值改成了 1 - min(diff, 1.0);,是因为1 - diff; 无法保证 diff 乘了 20 后 intersectGradient 依旧 > 0

虽然负值在普通 Shader 中会显示为黑色,但是在修改过 BlendMode 的 Shader 中,负值颜色依然可以显示,因此必须防止负值出现

修改后问题看起来解决了

但是这个方法并不通用,因为当改变相机的远平面时,这个遮罩也会跟着改变

这其实是因为在一开始计算护盾深度值的时候,做了一个 “ 除以相机到远平面的距离 ” 的操作

当时这么做的理由是,z 值是距离,纹理采样是颜色,值域不同
既然现在发现,统一成颜色值域的话会出问题,那干脆就都整成距离吧…

所以解决方法是给差值 “ 乘以相机到远平面的距离 ”

fixed4 frag (v2f i) : SV_Target
{// 蜂窝搏动、电流脉冲、固定边缘// ...float diff = DecodeFloatRG(tex2D(_CameraDepthNormalsTexture, i.posSS.xy / i.posSS.w).zw) - i.depth;diff *= _ProjectionParams.z;float intersectGradient = 1 - min(diff, 1.0);return intersectGradient;
}

这下就完全不受相机设置的影响了,因为它就是一个绝对的距离,不是一个基于远平面参数的 Range(0, 1)

推荐直接从前面就改成使用距离计算,可以节省一点计算量…
前面的笔记就不改了,反正栽坑爬坑也是思考的一部分…

v2f vert (appdata v)
{// ...// 距离o.depth = - mul(UNITY_MATRIX_MV, v.vertex).z;return o;
}fixed4 frag (v2f i) : SV_Target
{// 蜂窝搏动、蜂窝电流、固定边缘// ...// 距离差float diff = DecodeFloatRG(tex2D(_CameraDepthNormalsTexture, i.posSS.xy / i.posSS.w).zw) * _ProjectionParams.z - i.depth;float intersectGradient = 1 - min(diff, 1.0f);fixed4 intersectTerm = _Color * pow(intersectGradient, _IntersectExponent) * _IntersectIntensity;return fixed4(_Color.rgb + pulseTerm.rgb + hexEdgeTerm.rgb + edgeTerm + intersectTerm, _Color.a);
}

虽然想要两种边缘无缝连接,计算相交线的一些参数(指数、强度)还是设置成了与固定边缘不一样的变量参数

图中护盾材质的固定边缘和相交边缘的强度、指数使用的是同样的值

自发光

目标效果:护盾可以微微照亮周边

因为护盾的自发光是整体效果,可以把它交给后处理

给相机添加 Post Process Layer 和 Post Process Volume
在 Post Process Layer 中,选择 Default Layer,反走样选择 FXAA

补充
正常情况下应该对需要进行不同后处理的物体在 Inspector 设置 Layer 进行分层,但是这里只是为了实现一个效果,而且还是整体的效果,所以可以忽略

在 Post Process Volume 中,勾选 is Global,创建一个新的 Profile,添加Bloom效果,调节 Bloom 强度

优化

将使用同一套UV的多张灰度纹理存储进一张纹理,通过.r.gb.a使用对应通道的纹理
一张纹理有RGBA四个通道,所以一张纹理最多可以存4张灰度图
R:蜂窝边缘,G:蜂窝纹理,B:护盾边缘

这是一个很常见的优化手段,有很多优点:

  1. 节省项目空间:多张图存成了1张
  2. 节省运行内存,提升加载速度:只需要读取并存储1张纹理在内存中
  3. 对于多张图只需要进行一次纹理采样、一次 TRANSFORM_TEX 操作,减小时间空间消耗,简化代码

简单来说就是节省空间,提升速度,简化操作

总结

完整 Shader

Shader "Lexdev/CaseStudies/OverwatchShield"
{Properties{_HexTex("R:HexEdge  G:HexPulse  B:Edge", 2D) = "white" {}_Color ("Color", Color) = (1,1,1,1)[Header(Hex Pulse)] _PulseIntensity ("Hex Pulse Intensity", float) = 3.0_PulseTimeScale("Hex Pulse Time Scale", float) = 2.0_PulsePosScale("Hex Pulse Position Scale", float) = 50.0_PulseTexOffsetScale("Hex Pulse Texture Offset Scale", float) = 1.5[Header(Electronic Pulse)]_HexEdgeColor("Hex Edge Color", COLOR) = (0,0,0,0)_HexEdgeIntensity("Hex Edge Intensity", float) = 2.0_HexEdgeTimeScale("Hex Edge Time Scale", float) = 2.0_HexEdgeWidthModifier("Hex Edge Width Modifier", Range(0,1)) = 0.8_HexEdgePosScale("Hex Edge Position Scale", float) = 80.0[Header(Edge)]_EdgeIntensity("Edge Intensity", float) = 10.0_EdgeExponent("Edge Falloff Exponent", float) = 6.0_IntersectIntensity("Intersection Intensity", float) = 10.0_IntersectExponent("Intersection Falloff Exponent", float) = 6.0}SubShader{Pass{Tags {"RenderType" = "Transparent" "Queue" = "Transparent"}Cull offBlend SrcAlpha oneHLSLPROGRAM#pragma vertex vert#pragma fragment frag#include "UnityCG.cginc"sampler2D _HexTex;  float4 _HexTex_ST;float4 _Color;float _PulseIntensity;float _PulseTimeScale;float _PulsePosScale;float _PulseTexOffsetScale;float4 _HexEdgeColor;float _HexEdgeIntensity;float _HexEdgeTimeScale;float _HexEdgeWidthModifier;float _HexEdgePosScale;float _EdgeIntensity;float _EdgeExponent;// Camera rendered depth texture, must use the namesampler2D _CameraDepthNormalsTexture;float _IntersectIntensity;float _IntersectExponent;struct appdata{float4 vertex : POSITION;float2 uv : TEXCOORD0;};struct v2f{float4 pos : SV_POSITION;float2 uv : TEXCOORD0;float4 posOS : TEXCOORD1;float4 posSS : TEXCOORD2;float depth : TEXCOORD3;};v2f vert (appdata v){v2f o;o.pos = UnityObjectToClipPos(v.vertex);o.uv = TRANSFORM_TEX(v.uv, _HexTex);o.posOS = v.vertex;o.posSS = ComputeScreenPos(o.pos);// Z-Distanceo.depth = - mul(UNITY_MATRIX_MV, v.vertex).z;return o;}fixed4 frag (v2f i) : SV_Target{fixed4 hexTex = tex2D(_HexTex, i.uv);fixed pulseTex = hexTex.g;fixed hexEdgeTex = hexTex.r;fixed edgeTex = hexTex.b;// Hex pulsefixed3 pulseTerm = pulseTex * _Color * _PulseIntensity;float breath = _Time.y * _PulseTimeScale;float pulseOffset = pulseTex * _PulseTexOffsetScale;float horizontalDist = abs(i.posOS.x);float xOffset = horizontalDist * _PulsePosScale;pulseTerm *= abs(sin(breath + pulseOffset - xOffset));// Electronic pulsefixed3 hexEdgeTerm = hexEdgeTex * _HexEdgeColor * _HexEdgeIntensity;float edgeBreath = _Time.y * _HexEdgeTimeScale;float edgeNormalizer = 1 - _HexEdgeWidthModifier;float verticalDist = abs(i.posOS.z); // Z-Up in Object-Spacefloat diamondPattern = (horizontalDist + verticalDist) * _HexEdgePosScale;hexEdgeTerm *= saturate(sin(diamondPattern - edgeBreath) - _HexEdgeWidthModifier) / edgeNormalizer;// Z-Distancefloat diff = DecodeFloatRG(tex2D(_CameraDepthNormalsTexture, i.posSS.xy / i.posSS.w).zw) * _ProjectionParams.z - i.depth;float intersectGradient = 1 - min(diff, 1.0f);// Edge & Intersectionfixed3 edgeTerm = pow(edgeTex, _EdgeExponent) * _Color * _EdgeIntensity;fixed3 intersectTerm = pow(intersectGradient, _IntersectExponent) * _Color * _IntersectIntensity;return fixed4(_Color.rgb + pulseTerm + hexEdgeTerm + edgeTerm + intersectTerm, _Color.a);}ENDHLSL}}
}

基本思路总结
总的来说效果可以分为两大部分:图案和边缘

图案部分可以拆解为沿 x 轴扩散的蜂窝图案,和以菱形向外扩大的电流
边缘部分可以拆分为护盾的固有边缘,和护盾与其他物体相交产生的边缘

图案部分的计算是先用颜色纹理强度因子得到基础图形,再通过对时间进行数学运算获得图形遮罩,二者相乘得到动画
边缘部分的计算是采样纹理获得遮罩,再通过对颜色进行数学计算,二者相乘得到边缘

杂七杂八总结
平移 sin 函数可以打算 > 0 和 < 0 的长度平衡;可能需要对平移后的值做矫正
abs(x) + abs(y) = n(常)是中心在原点的斜放正方形
sin(abs(x) + abs(y)) 是s&fja@inc*ud^ishd(#svd%abaabaaba
有奇奇怪怪的非二维函数可以扔到Google里看图像
采样相机渲染的深度图时,需要先做一个透视除法矫正透视导致的歪斜

一点题外话
感觉捡到宝了,这个博主真的好良心啊
博客里一共有5个教程,目前算是跟着学完了其中两个最感兴趣的效果(Gears Hammer of Dawn、Overwatch Shield),受益良多,慢慢的开始明白拆解思路、参数设置思路了

虽然依然是数学渣一个,但是还是很开心终于开始慢慢体会到数学的神奇了

哎…失去了 gif 感觉放图的乐趣都没了QWQ

【ShaderLab实例笔记】Overwatch Shield - 守望先锋护盾特效制作笔记相关推荐

  1. 【ShaderLab实例笔记】Gears Hammer of Dawn - 激光 碎石特效制作笔记

    教程链接:Gears Hammer of Dawn 项目链接:GearsHammerOfDawnTemplate Pipeline & Shader:Built-in,Standard Sur ...

  2. ue4 怎么修改骨骼动画_【2017 GDC挖坟】守望先锋动画制作管线(下篇)

    写在前面的话:GDC总是可以挖掘到很多值得学习的分享,今天拆解一下2017GDC上关于动画管线的分享,里面很多东西值得借鉴.思考,文中偶尔会有一点自己的想法.总结,欢迎大佬吐槽.提意见.(文章很长,多 ...

  3. 《守望先锋》模型提取实例

    <守望先锋>模型提取 最近研究了下守望先锋的模型提取,一般的unity.手游模型都好取,突然遇到个头疼的,研究了一下,找到了一个大神的方案,以下是使用的大概步骤. toolchain工具介 ...

  4. 《守望先锋》架构设计与网络同步

    原文链接 <守望先锋>架构设计与网络同步 Overwatch Gameplay Architecture and Netcode Timothy Ford Lead Gameplay En ...

  5. 《守望先锋》中网络脚本化的武器和技能系统

    在GDC2017[Networking Scripted Weapons and Abilities in Overwatch]的分享会上,来自暴雪的Dan Reed介绍了<守望先锋>中网 ...

  6. 使用机器学习预测电子竞技游戏《守望先锋》的胜负

    摘要: 机器学习可以预测游戏的输赢?来看看Bowen Yang博士是如何构建这一模型的. <守望先锋>中的英雄 来自加州大学河滨分校的物理学博士学位的Bowen Yang正在致力于构建一个 ...

  7. 8 更新全局变量_2月26日守望先锋版本更新

    综合更新 新功能:英雄池 从竞技比赛的第21赛季开始,我们将加入英雄池机制.和地图池一样,并非所有英雄都可随时上阵,每周的可用英雄列表都会变化,每周的列表将包含阵容中的大部分英雄,仅有少数几位英雄将无 ...

  8. java/php/net/python守望先锋网站设计

    本系统带文档lw万字以上+答辩PPT+查重 如果这个题目不合适,可以去我上传的资源里面找题目,找不到的话,评论留下题目,或者站内私信我, 有时间看到机会给您发 管理员用例图 系统中的核心用户是用户管理 ...

  9. 浅谈《守望先锋》中的 ECS 构架

    转自云风的BLOG: https://blog.codingnow.com/2017/06/overwatch_ecs.html 今天读了一篇 <守望先锋>架构设计与网络同步 .这是根据 ...

最新文章

  1. 鸿蒙系统开发者公测,公测尝鲜开启!华为Mate40/P40开始和安卓渐行渐远
  2. 华为首款Harmonyos摄像头,掀起家居安防大变革 华为首款HarmonyOS智能摄像头发布...
  3. request获取网页单选框的值
  4. DL:深度学习算法(神经网络模型集合)概览之《THE NEURAL NETWORK ZOO》的中文解释和感悟(一)
  5. Hibernate常见问题集锦
  6. 向Java添加@atomic操作
  7. Codeforces Round #325 (Div. 2) B. Laurenty and Shop 前缀和
  8. ecshop程序设置伪静态简单三步骤
  9. mysql修改字段结构_MySQL修改表结构及其添加删除修改字段功能
  10. SVN客户端下载和Svn visual studio插件
  11. java执行php代码块_Java示例讲解普通代码块以及静态代码块的执行顺序
  12. 一些非常简单的Python代码
  13. 米家和苹果HomeKit二选一,你怎么选?
  14. 项目vite1.0升级到2.0打包遇到Some chunks are larger问题如何解决
  15. 【CSDN|每日一练】小艺的英文名
  16. yolov5 deepsort 行人车辆 双向计数 跟踪检测 | 开源项目分享
  17. HDU 6078 Wavel Sequence
  18. esp8266灯上电闪一下_怎样使用ESP8266-01引脚和指示灯
  19. 使用JavaScript进行销毁:操作指南
  20. 【云原生 | Kubernetes 系列】--Envoy熔断

热门文章

  1. CentOS7 ftp服务离线安装
  2. 查看win10电脑电池损耗
  3. 使用清华镜像站下包、pip通过清华镜像站下载
  4. 8.3CSS代码缩写 颜色值
  5. 这次经历给我埋下了学理财的种子
  6. AHRS 原理算法+代码实现(好文记录)
  7. 《ECMAScript 6 入门教程》学习笔记Ⅰ
  8. 【火车头采集】如何采集一个网页的多张图片并且下载
  9. WEB测试番外之----XSS攻击
  10. 【天工Godwork精品教程】任务二:导入控制点、POS权重设置、连接点分布检查、自由空三