Unity Shader 实现X光效果
- Unity Shader 实现实物遮挡外轮廓发光效果
- 第五人格
- 黎明杀机
- 火炬之光
- 实现方案
- 操作实现
- 立体感优化
- 总结
- 源码
Unity Shader 实现实物遮挡外轮廓发光效果
之前看过《火炬之光》、《黎明杀机》、《第五人格》等不少的游戏里面人物被建筑物遮挡呈现出不同的效果,在这里我们就叫他X-Ray效果,也可以叫透视效果。
第五人格
黎明杀机
火炬之光
实现方案
-
采用Amplify Shader Editor1.6.1
-
Fresnel Node
利用光到达具有不同折射率的两种材质之前的界面时的行为,以及反射和折射的量。
ReflectionCoefficient = Bias + Scale x (1 + N)
节点参数 | 描述 | 默认值 |
---|---|---|
法向空间 | 指定法向量所在的坐标空间 | 切 |
正切:法向量在切向空间坐标中 | ||
世界:法向量在世界空间坐标中 | ||
偏置 | 定义了菲涅耳方程的偏置变量。仅当各输入端口未连接时才可见 | 0 |
尺度 | 定义了菲涅耳方程的尺度变量。仅当各输入端口未连接时才可见 | 1 |
幂定 | 定义了菲涅耳方程的幂定变量。仅当各输入端口未连接时才可见 | 5 |
输入端口 | 描述 | 类型 |
---|---|---|
要使用的法向量 | 如果不连接,将使用表面世界法线 | float 3 |
偏置 | 定义了菲涅耳方程的偏置变量 | float |
尺度 | 定义了菲涅耳方程的尺度变量 | float |
幂 | 定义了菲涅耳方程的幂变量 | float |
- Swizzle Node
允许重新组织和复制其输入组件。输入和输出可以是不同的类型。
4. Outline Node
围绕某个对象创建一个Outline。
操作实现
X-Ray
- 创建一个Shader
(1)将Outline设置为Transparent(透明模式)
(2)Cull Mode模式设置有三个选项off front back
正常的我们使用一个front就可以 这样可以节省性能
因为透明材质是从前向后渲染的,当我们选择front就不需要渲染物体的背面,降低了GPU的性能消耗
注意:如果要做物体之间的遮挡关系我们需要知道z-buffer,然而我们对z-buffer的调用就是通过ZTest和ZWrite来实现的。
这里我直接拿之前做的测试来说就不演示了
ZTest(深度测试)和ZWrite(深度写入)
a.深度测试通过,深度写入开启:写入深度缓冲区,写入颜色缓冲区;
b.深度测试通过,深度写入关闭:不写深度缓冲区,写入颜色缓冲区;
c.深度测试失败,深度写入开启:不写深度缓冲区,不写颜色缓冲区;
d.深度测试失败,深度写入关闭:不写深度缓冲区,不写颜色缓冲区;
所以直接的影响还是要看ZTest
在ZTest和ZWrite相同的情况下,就需要通过调整Geometry队列的大小来影响渲染的先后顺序,Gemometry大的先渲染,小的后渲染
ZTest Less(深度小于当前缓存则通过, ZTest Greater(深度大于当前缓存则通过)
ZTest LEqual(深度小于等于当前缓存则通过)
ZTest GEqual(深度大于等于当前缓存则通过)
ZTest Equal(深度等于当前缓存则通过)
ZTest NotEqual(深度不等于当前缓存则通过)
ZTest Always(不论如何都通过)
注意:
ZTest Off等同于ZTest Always,关闭深度测试等于完全通过。
(3)ZWrite(深度写入)
这里直接off就可以,不需要写入深度缓冲区
(4)ZTest(深度测试)
这里直接Always(永远通过)
此时创建一个材质球赋值刚才创建的Shader
发现已经有了X光的效果,但是有不足,没有立体感,我们在进行优化一下
立体感优化
改进方案:
将边缘至中心颜色淡化,看起来更加立体
注意:将Fresnel中的Normal Vector选项更改为自身,而不是世界(立体感)
这时在加入一个Lerp差值运算
将颜色和上图运算公式做插值运算
优化完成效果图
这时候发现还有一个问题,当我们调Alpha值时,冷色调和段色调是相反的比如上图所示红色alpha值在-0.93左右比较好,但是冷色调是在1~2之间比较好,我们需要把值控制在一个范围内,继续优化
- 这时候我们在Color的做差值运算的时候我们添加一个Swizzle 并将他的输出类型改为Float
将端口改为alpha - 在加入一个One Minus取反
- 在Alpha的地方取值Remap(将原有值重新赋值)
效果图如下:
调节Alpha的值区间为[0,1]
总结
1.我图中采用的模型是使用单模型多材质球,所以只展示了一个身体,单模型但材质直接赋值材质球就好,如果是单模型多材质的需要多创建几个材质球,因为每一部分的发现切图和Albedo图是不一样的。
2.这里就不展示Demo了,东西比较简单,感兴趣的可以研究一下这个插件Amplify Shader Editor,我用的是1.6.1版本
3.源码我也附上在下面
源码
Shader “ASE/Ray”
{
Properties
{
_ASEOutlineWidth( “Outline Width”, Float ) = 0
_Albedo(“Albedo”, 2D) = “white” {}
_Normalmap(“Normal map”, 2D) = “white” {}
_Color0(“Color 0”, Color) = (1,0,0,0)
_Alpha(“Alpha”, Float) = 0
_Bias(“Bias”, Range( 0 , 1)) = 0
_Scale(“Scale”, Range( 0 , 1)) = 0
_Power(“Power”, Range( 0 , 1)) = 0
[HideInInspector] _texcoord( “”, 2D ) = “white” {}
[HideInInspector] __dirty( “”, Int ) = 1
}
SubShader
{
Tags{ "RenderType" = "Transparent" "Queue" = "Transparent+0"}
ZWrite Off
ZTest Always
Cull Front
CGPROGRAM
#pragma target 3.0
#pragma surface outlineSurf Outline nofog alpha:fade keepalpha noshadow noambient novertexlights nolightmap nodynlightmap nodirlightmap nometa noforwardadd vertex:outlineVertexDataFunc
struct Input
{
float3 worldPos;
float3 worldNormal;
INTERNAL_DATA
};
uniform float4 _Color0;
uniform float _Bias;
uniform float _Scale;
uniform float _Power;
uniform float _Alpha;
uniform half _ASEOutlineWidth;
void outlineVertexDataFunc( inout appdata_full v, out Input o )
{
UNITY_INITIALIZE_OUTPUT( Input, o );
v.vertex.xyz += ( v.normal * _ASEOutlineWidth );
}
inline half4 LightingOutline( SurfaceOutput s, half3 lightDir, half atten ) { return half4 ( 0,0,0, s.Alpha); }
void outlineSurf( Input i, inout SurfaceOutput o )
{
float3 ase_worldPos = i.worldPos;
float3 ase_worldViewDir = normalize( UnityWorldSpaceViewDir( ase_worldPos ) );
float3 ase_worldNormal = WorldNormalVector( i, float3( 0, 0, 1 ) );
float fresnelNdotV7 = dot( ase_worldNormal, ase_worldViewDir );
float fresnelNode7 = ( _Bias + _Scale * pow( 1.0 - fresnelNdotV7, _Power ) );
float lerpResult18 = lerp( ( 1.0 - (_Color0).a ) , fresnelNode7 , (-2.0 + (_Alpha - 0.0) * (0.0 - -2.0) / (1.0 - 0.0)));
o.Emission = _Color0.rgb;
o.Alpha = lerpResult18;
o.Normal = float3(0,0,-1);
}
ENDCG
Tags{ "RenderType" = "Opaque" "Queue" = "Geometry+1" }
Cull Back
ZWrite On
ZTest LEqual
CGPROGRAM
#pragma target 3.0
#pragma surface surf Standard keepalpha addshadow fullforwardshadows vertex:vertexDataFunc
struct Input
{
float2 uv_texcoord;
};
uniform sampler2D _Normalmap;
uniform float4 _Normalmap_ST;
uniform sampler2D _Albedo;
uniform float4 _Albedo_ST;
void vertexDataFunc( inout appdata_full v, out Input o )
{
UNITY_INITIALIZE_OUTPUT( Input, o );
v.vertex.xyz += 0;
}
void surf( Input i , inout SurfaceOutputStandard o )
{
float2 uv_Normalmap = i.uv_texcoord * _Normalmap_ST.xy + _Normalmap_ST.zw;
o.Normal = UnpackNormal( tex2D( _Normalmap, uv_Normalmap ) );
float2 uv_Albedo = i.uv_texcoord * _Albedo_ST.xy + _Albedo_ST.zw;
o.Albedo = tex2D( _Albedo, uv_Albedo ).rgb;
o.Alpha = 1;
}
ENDCG
}
Fallback "Diffuse"
CustomEditor "ASEMaterialInspector"
}