ASE快捷键

ASE不支持Ctrl + Z撤销,点击Editor右上角的蓝色感叹号图标,就能显示帮助面板。

在编辑器左侧,默认情况下可以看到Material Properties,列出了所有暴露的Shader 属性,可以调节各个属性上下位置。

ASE中,一维数据一根线,二位数据二根线……

基本数据节点

1 :按住1键点击舞台,快速新建float节点。

2 :按住2键点击舞台,快速新建vector2节点。

3 :按住3键点击舞台,快速新建vector3节点。

4 :按住4键点击舞台,快速新建vector4节点。

5 :按住5键点击舞台,快速新建color节点。

T :按住T键点击舞台,快速新建贴图节点。

U : 按住U键点击舞台,快速新建贴图UV坐标节点。

数学节点

A S M D :按住这些键点击舞台,快速新建加、减、乘、除节点。

L :按住L键点击舞台,快速新建Lerp节点。

O :按住T键点击舞台,快速新建One Minus节点。

E :按住T键点击舞台,快速新建Power节点。

。:按住点号键点击舞台,快速新建Dot节点。

函数节点

V :按住V键点击舞台,快速新建Append节点,这个节点用来将各个分量组合起来,比如我们需要世界坐标的xy分量,那么将World Position节点的xy分量连接到Append的XY当中,并且将Append节点的Output Type改为Vector2

Z :按住Z键点击舞台,快速新建Swizzle节点,这个节点的作用和Append相反,用于拆分各个分量。

整理用节点

R :按住R键点击舞台,快速新建Register Local Var节点,这个节点用来存储任意本地变量,可以用来将各个部分混乱的连线分隔开

G :按住R键点击舞台,快速新建Get Local Var节点,和Register Local Var节点配合使用

C :框选一部分Node后按C键,新建一个Commentary组,可以分组各个Node。

场景搭建

  1. 新建材质球,Shader选择Skybox——Panoramic,将颜色改为深蓝黑色,不指定贴图
  2. 打开Lighting面板,指定上面的材质球,打开Fog,拾取上面天空的深蓝黑色,将Density设为0.1
  3. 新建Plane,Reset一下,给一张简单贴图,放大24倍,Unity默认给它Standard材质,我们在Inspector面板中修改这个材质,关掉Specular Highlights和Reflection,将Smoothness设为0
  4. 放上需要演示的人物模型

ASE的Blend

  1. 新建一个ASE Surface Shader,命名为Scan
  2. 在弹出的面板左侧,将Light Model设为Unlit,在Blend Mode中,将Render Type设为Custom,RenderQueue设为Transparent,Blend RGB改为 Particle Additive
  3. 我们在舞台中按住T键添加一个贴图节点,将RGBA链接到输出节点的Emission中,点击保存。
  4. 使用这个Shader新建材质并给一个黑白渐变的贴图,可以看到这个贴图已经去黑了,这相当于我们在上一节的Blend,ASE在设置了Blend RGB的相关的模式之后,会自动将贴图的RGB通道根据灰度进行计算,但是需要注意,这个时候计算出来的Alpha值是通过灰度计算得来的,也就是只计算了RGB通道,Alpha通道并没有计算,像上一节一样,如果我们想整体调节材质的透明度,就需要再添加一个颜色属性,让这个颜色的Alpha影响整个材质的Opacity,注意一定要控制材质的Opacity,否则阴影会不正确(除非不投射阴影)。
  5. 我们在舞台中按住5键添加一个Color节点,按住M键添加一个相乘节点,按下图连接。选择Color节点,把左侧的Type改为Property,这样就能暴露在材质编辑器中。

最终调节节点

这个节点可以用来测试Blend RGB各种混合模式的效果,注意看输出节点有一个Debug节点,可以方便调试。我们将一些节点的输出连接到Debug节点并保存,会实时查看到结果,ASE会优先输出Debug的结果。

ASE的深度预写入

在ASE中选择Depth——勾选Extra Depth Pass即可

ASE边缘光

在ASE中,边缘光有默认的函数,名字叫做Fresnel(菲涅尔),直接添加此节点就能实现边缘光效果,原理如下图

ASE边缘光

使用SmoothStep控制边缘光

除了使用RimPower控制边缘光对比度之外,还可以使用SmoothStep函数来控制。

SmoothStep使用平缓插值,将输入的值映射在0-1之间。

SmoothStep

它有三个输入值:原始值、Min和Max,当原始值 < Min —— 输出0、当原始值 > Max —— 输出1 、 当原始值在Min和Max之间时,输出

0到1之间的插值。

使用Lerp混合内外颜色

在ASE中,Lerp节点通过一个Alpha值来线性混合AB两个输入节点,这个Alpha值可以是一张灰度图的r通道,或者是其他一维的线性值。

基本的边缘光

上图就是使用SmoothStep + Lerp制作的边缘光效果,还能调节内外光的颜色。RimMin(-10)和RimMax(02)控制Rim的对比度,RimIntensity控制Rim颜色的强度。

注意SmoothStep输出的值要作为Alpha值,这个Alpha值控制Lerp混合内外颜色的效果,还要输出到输出节点的Opacity中。

一定要输出到Opacity,否则阴影就会出现错误。

ASE扫光

如果要进行扫光,我们首先要将人物的整个UV转换到世界空间下,否则扫光的贴图会按照模型空间扫,看起来很乱。

在ASE中,想要偏移UV,按住U键并点击舞台,会出现一个Texture Coordinates节点,这个节点在Tex port没有被连接的情况下,代表的是本物体模型空间下的UV,将他的UV输出到Texture(下面Flow Emiss)节点的UV。但是在中途我们可以Add一个Vector2,这个Vector2和Time节点相乘。

最基本的UV偏移

上面这张图展示的是最基本模型空间下的UV偏移,并没有将UV转换到世界空间下。

注意,一定要通过Texture Coordinates节点获取模型空间下的UV,如果我们直接将Multiply节点连接到FlowEmiss的UV port,贴图就会错乱。

想要把UV转换到世界空间,意味着并不需要Texture Coordinates节点提供的UV信息,像上一节“UV映射到平面”提到的,我们直接使用世界空间下的顶点坐标就可以。

ASE提供了World Position节点,这个节点代表的就是世界空间下的顶点坐标,我们想要映射到xy平面,所以将World Position节点的X和Y port输出到Append(Vector2类型)节点的X Y Port。

需要注意的是,我们直接使用World Position节点来偏移UV的话,当我们移动模型时,贴图并不跟随模型移动,因为贴图是直接根据世界空间映射的,模型本身的位移会被忽略,所以我们需要模型提供自己的一个“原点坐标”,我们把这个“原点坐标”转换到世界空间,当模型移动时,“原点坐标”在世界空间当中的位置也会变化,此时我们只需要将World Position计算出来的顶点坐标和“原点坐标”相减,就能让贴图跟随模型了。ASE中提供了Transform Position节点,用来将自定义的“原点坐标”转换到世界空间,默认的“原点坐标”是(0,0,0)。

下面的图是完整的世界空间下xy平面扫光的连线图

XY平面扫光

注意,贴图是向XY平面映射的,模型向Z轴移动时,根本不影响XY的坐标,所以我们这里不需要考虑Z坐标

ASE最终效果转Shader代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
Shader "KerryTA/ASE/01ScanCode"
{
Properties
{
_MainTex ("Texture", 2D) = "white" {}
_TexPower("TexPower", Float) = 2.0
_RimMax("RimMax", Range(0.0,2.0)) = 1.0
_RimMin("RimMin", Range(-1.0,1.0)) = 0.0
_RimColor("RimColor", Color) = (1,1,1,1)
_InnerColor("InnerColor", Color) = (0,0,0,0)
_RimIntensity("RimIntensity", Float) = 1.0
_InnerAlpha("InnerAlpha", Range(0.0,1.0)) = 0.0
_FlowEmissTex("FlowEmissTex", 2D) = "white"{}
_FlowTilling("FlowTilling", Vector) = (1,1,0,0)
_FlowSpeed("FlowSpeed", Vector) = (0,1,0,0)
_FlowIndensity("FlowIndensity", Float) = 0.7
}
SubShader
{
Tags { "Queue"="Transparent" }

Pass
{
Cull Off
ZWrite On
ColorMask 0//只写入深度,不写入颜色
CGPROGRAM
float4 _Color;
#pragma vertex vert
#pragma fragment frag

float4 vert(float4 vertexPos : POSITION) : SV_POSITION
{
return UnityObjectToClipPos(vertexPos);
}
float4 frag(void) : COLOR
{
return _Color;
}
ENDCG
}
Pass
{
ZWrite Off
Blend SrcAlpha One

CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"

struct appdata
{
float4 vertex : POSITION;
float2 texcoord : TEXCOORD0;
float3 normal : NORMAL;
};

struct v2f
{
float4 pos : SV_POSITION;
float2 uv : TEXCOORD0;
float3 normal_world : TEXCOORD1;
float3 pos_world : TEXCOORD2;
float3 pivot_world : TEXCOORD3;
};

sampler2D _MainTex;
float4 _MainTex_ST;
float _TexPower;
float _RimMax;
float _RimMin;
float4 _RimColor;
float4 _InnerColor;
float _RimIntensity;
float _InnerAlpha;
sampler2D _FlowEmissTex;
float4 _FlowTilling;
float4 _FlowSpeed;
float _FlowIndensity;

v2f vert (appdata v)
{
v2f o;
o.pos = UnityObjectToClipPos(v.vertex);
o.pivot_world = UnityObjectToClipPos(float4(0.0,0.0,0.0,1.0)).xyz;
//计算当前顶点世界空间下的法线向量
o.normal_world = normalize(mul(float4(v.normal,0.0),unity_WorldToObject).xyz);
float3 pos_world = mul(unity_ObjectToWorld, v.vertex).xyz;
o.pos_world = pos_world;
o.uv = v.texcoord * _MainTex_ST.zy + _MainTex_ST.zw;
return o;
}

fixed4 frag (v2f i) : SV_Target
{
half3 normal_world = normalize(i.normal_world);
half3 view_world = normalize(_WorldSpaceCameraPos - i.pos_world);
half NdotV = saturate(dot(normal_world, view_world));

half fresnel = smoothstep(_RimMin, _RimMax, (1.0 - NdotV));
half texRed = pow(tex2D(_MainTex, i.uv).r, _TexPower);
half finalRimAlpha = saturate(fresnel + texRed);

half3 finalRimColor = lerp(_InnerColor, _RimColor * _RimIntensity, finalRimAlpha).xyz;

half2 uv_flow = (i.pos_world.xy - i.pivot_world.xy) * _FlowTilling.xy;
uv_flow = uv_flow +_Time.y * _FlowSpeed.xy;

half4 flow_rgba = tex2D(_FlowEmissTex, uv_flow) * _FlowIndensity;

half3 final_col = finalRimColor + flow_rgba.xyz;

half final_alpha = saturate(finalRimAlpha + flow_rgba.w + _InnerAlpha);

return half4(final_col,final_alpha);
}
ENDCG
}
}
}