钢铁侠Unlit光照Shader,三种效果变化
大家好,我是阿赵,这里是钢铁侠材质制作第二部分,线条轮廓部分的制作
为了实现这个效果,可以把细节拆分成以下几个部分:
这是一个很基础的轮廓光实现,用到的就是法线和观察方向的点乘(NdotV)。
Shader "azhao/IronManBodyCode"
{
Properties
{
_RimBias("RimBias", Float) = 1
_RimPow("RimlPow", Float) = 2
_RimlCol("RimCol", Color) = (0,0,0,0)
}
SubShader
{
Tags { "RenderType"="Opaque" }
LOD 100
Pass
{
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"
struct appdata
{
float4 vertex : POSITION;
float3 normal : NORMAL;
};
struct v2f
{
float4 pos : SV_POSITION;
float3 worldPos :TEXCOORD0;
float3 worldNormal : TEXCOORD1;
};
float _RimBias;
float _RimPow;
float4 _RimlCol;
v2f vert (appdata v)
{
v2f o;
o.pos = UnityObjectToClipPos(v.vertex);
o.worldPos = mul(unity_ObjectToWorld, v.vertex);
o.worldNormal = UnityObjectToWorldNormal(v.normal);
return o;
}
half4 frag (v2f i) : SV_Target
{
float3 worldViewDir = normalize(UnityWorldSpaceViewDir(i.worldPos));
float NdotV = dot(i.worldNormal, worldViewDir);
float fresnelVal = pow((1 - NdotV)*_RimBias, _RimPow);
half4 col = _RimlCol* fresnelVal;
return col;
}
ENDCG
}
}
}
这个其实就是菲涅尔的标准算法,在1-NdotV后,乘以一个bias值控制边缘光的强度,再计算一个Pow运算,控制边缘光的边缘厚度。
看到这个效果,可能很多人都在考虑怎样画这个横纹的贴图。其实这个实现方式很巧妙,只需要一张Noise噪声图就可以了。
Shader "azhao/IronManBodyCode"
{
Properties
{
_RimBias("RimBias", Float) = 1
_RimPow("RimlPow", Float) = 2
_RimlCol("RimCol", Color) = (0,0,0,0)
_NoiseMap("NoiseMap",2D) = "black"{}
_NoiseTiling("NoiseTiling",Vector) = (1,1,0,0)
}
SubShader
{
Tags { "RenderType"="Opaque" }
LOD 100
Pass
{
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"
struct appdata
{
float4 vertex : POSITION;
float2 uv : TEXCOORD0;
float3 normal : NORMAL;
};
struct v2f
{
float4 pos : SV_POSITION;
float3 worldPos :TEXCOORD0;
float3 worldNormal : TEXCOORD1;
};
float _RimBias;
float _RimPow;
float4 _RimlCol;
sampler2D _NoiseMap;
float4 _NoiseTiling;
v2f vert (appdata v)
{
v2f o;
o.pos = UnityObjectToClipPos(v.vertex);
o.worldPos = mul(unity_ObjectToWorld, v.vertex);
o.worldNormal = UnityObjectToWorldNormal(v.normal);
return o;
}
half4 frag (v2f i) : SV_Target
{
float3 worldViewDir = normalize(UnityWorldSpaceViewDir(i.worldPos));
float NdotV = dot(i.worldNormal, worldViewDir);
float fresnelVal = pow((1 - NdotV)*_RimBias, _RimPow);
float2 noiseUV = i.worldPos.xy *_NoiseTiling.xy + _NoiseTiling.zw;
float4 noiseCol = tex2D(_NoiseMap, noiseUV);
half4 col = _RimlCol * (fresnelVal+noiseCol.r);
return col;
}
ENDCG
}
}
}
由于我们想要的横纹是永远水平于世界空间,并不是希望会随着角色的动作而变形的,所以我这里并没有使用模型本身的UV坐标来采样,而是使用了模型的顶点世界坐标的xy坐标来模拟了UV坐标,并且添加了一个Tiling值来控制Noise贴图的平铺次数。
如果不调整参数的情况下,添加Noise到材质球,会看到下面的效果。
虽然这个效果看起来也很cool,但并不是我想要的效果,所以再调整一下Tiling值
把平铺y值调大,这个时候,就可以看到,Noise图就变成了横纹了。
我希望在显示这个光线轮廓的过程中,身上的横纹是有一个慢慢上升的动画效果的
这个实现起来很简单,只需要给Noise图的uv做一个偏移就可以了,这里用到了_Time,根据时间来让UV偏移。
float2 noiseUV = i.worldPos.xy _NoiseTiling.xy + _NoiseTiling.zw;
noiseUV.y += frac(_Time.y)_NoiseSpeed;
这里还加了一个frac函数,frac函数的作用是去掉数值的整数部分只保留小数部分。由于某些设备的数值精度支持范围有问题,如果_Time不停的累加,达到很大的值时,可能会出问题。所以加一个frac函数让它的值只使用小数部分。
Shader "azhao/IronManBodyCode"
{
Properties
{
_RimBias("RimBias", Float) = 1
_RimPow("RimlPow", Float) = 2
_RimlCol("RimCol", Color) = (0,0,0,0)
_NoiseMap("NoiseMap",2D) = "black"{}
_NoiseTiling("NoiseTiling",Vector) = (1,1,0,0)
_NoiseSpeed("NoiseSpeed",float) = 0
}
SubShader
{
Tags { "RenderType"="Opaque" }
LOD 100
Pass
{
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"
struct appdata
{
float4 vertex : POSITION;
float3 normal : NORMAL;
};
struct v2f
{
float4 pos : SV_POSITION;
float3 worldPos :TEXCOORD0;
float3 worldNormal : TEXCOORD1;
};
float _RimBias;
float _RimPow;
float4 _RimlCol;
sampler2D _NoiseMap;
float4 _NoiseTiling;
float _NoiseSpeed;
v2f vert (appdata v)
{
v2f o;
o.pos = UnityObjectToClipPos(v.vertex);
o.worldPos = mul(unity_ObjectToWorld, v.vertex);
o.worldNormal = UnityObjectToWorldNormal(v.normal);
return o;
}
half4 frag (v2f i) : SV_Target
{
float3 worldViewDir = normalize(UnityWorldSpaceViewDir(i.worldPos));
float NdotV = dot(i.worldNormal, worldViewDir);
float fresnelVal = pow((1 - NdotV)*_RimBias, _RimPow);
float2 noiseUV = i.worldPos.xy *_NoiseTiling.xy + _NoiseTiling.zw;
noiseUV.y += frac(_Time.y)*_NoiseSpeed;
float4 noiseCol = tex2D(_NoiseMap, noiseUV);
half4 col = _RimlCol * (fresnelVal+noiseCol.r);
return col;
}
ENDCG
}
}
}