|
大家好,我是老莫,今天学习一下游戏中的透视扫描效果。
本文在unity built-in管线中实现,在新版URP管线中,已经可以通过render feature功能实现,操作简单效率高。后期我会再写一篇关于render feature的使用方法。
但这篇文章可以让你更深入地了解菲涅尔效果的实现、深度测试的原理、pass的调用。
<hr/>首先看一下成品。
分析
这个效果很简单,只有两个部分:扫描效果+遮挡透视
<hr/>首先,我们来只做扫描效果。
添加三个属性:颜色、线条宽度、扫描速度
[HDR]_LineColor(&#34;Color&#34;,Color) = (1,1,1,1)
_LineSize(&#34;LineSize&#34;,float) = 1
_Speed(&#34;Speed&#34;,float) = 1
注意,我们的扫描效果是包含了边缘光的,所以先实现菲涅尔效果。
打开菲涅尔效果公式。
fresnel = fresnel基础值 + fresnel缩放量*pow( 1 - dot( N, V ), 5 )
float fresnel = (0.2 + 2.0 * pow (1.0 - ndotv, 2.0));后面我再写一篇关于菲涅尔原理的文章,这里不多赘述。我们在unityshader中实现菲涅尔效果,最重要的两个向量是摄像机到顶点的方向(Vdir),顶点的法线方向(Ndir),进行点击运算。其Vdir的获取方式是摄像机的坐标减去顶点坐标,Ndir的则直接用unity内置函数获取,使用前对两个值进行归一化处理。
fixed3 V = normalize(UnityWorldSpaceViewDir(i.worldPos));
fixed3 N = normalize(i.worldNormal);
fixed VdotN = dot(V,N);
写完公式,开始添加我们所需要的参数,分别是世界空间下的顶点坐标和世界空间下的法线方向。这里我喜欢先写公式后加参数,按个人喜好来。
v2f vert (appdata v)
{
v2f o;
o.vertex = UnityObjectToClipPos(v.vertex);
o.worldPos = mul(unity_ObjectToWorld ,v.vertex);
o.worldNormal = UnityObjectToWorldNormal(v.normal);
return o;
}
return VdotN,我们看一下效果。
这里已经有了菲涅尔效果的雏形,我们接着套用菲涅尔公式,乘以一个颜色。这里我暴露了一个属性_FresnelWidth。
fixed4 col = tex2D(_MainTex, i.uv);
fixed4 frag (v2f i) : SV_Target
{
fixed3 V = normalize(UnityWorldSpaceViewDir(i.worldPos));
fixed3 N = normalize(i.worldNormal);
fixed VdotN = dot(V,N);
fixed fresnel = 0.2+2* pow(1-VdotN,_FresnelWidth);
fixed4 col = tex2D(_MainTex, i.uv);
return fresnel * _LineColor;
}
感觉有点生硬,把材质球改为透明材质,并且高亮叠加
Tags { &#34;Queue&#34; = &#34;Transparent&#34; }
Blend One One
菲尼尔效果完成后,开始制作扫描效果。
首先是得到一个模型水平方向的线条,对模型世界空间下的顶点坐标Y方向做frac处理。frac可以得到一张0-1的渐变图,原理也很简单,为了使效果更明显,我在此基础上乘以了10。
fixed line_Y = frac(i.worldPos.y*10);
return fresnel * _LineColor*line_Y;
到这一步,线条还是固定不动的,再做一个Y方向的流动效果,这时候就用上了我们一开始准备好的_Speed和_LineWidth属性
fixed line_Y = frac(i.worldPos.y*_LineSize+_Time.y*_Speed);
<hr/>扫描效果完成后,开始实现透视功能
透视功能的原理可以简单概括为,当模型深度大于另一个模型时,则通多渲染,否则不通过。
所谓深度,也就是顶点距离摄像机的距离,距离越远,深度值越大,反之越小。所以模型深度值大于另一个模型,也就是代表它被这个模型遮挡住了。
这里就不得不用到深度测试了,并且设为Greater。
现在用一个Cube遮挡住模型,看一下效果
现在,我们只需要保存shader文件,随便打开一个shader,在里面调用它就可以了。
首先将这个shader中的pass命名
Name &#34;TEST&#34;
Tags { &#34;Queue&#34; = &#34;Transparent&#34; }
Blend One One
ZTest Greater
在新创建的shader中调用此pass
UsePass&#34;MyShader/Xray02/TEST&#34;
这时候遇到了bug,看样子应该是调用成功了,因为深度测试已经被打开,并且是按照Greater执行的。此时我们需要打开frame debug,寻找原因。
可以看到本来已经实现了pass的调用,效果也是对的,但是在mesh渲染第二个pass(也就是自身原有的pass)时,纹理又被覆盖了。问了各位大佬和查阅资料后,最终解决方法是在调用的pass(透视效果)中,在深度测试后关闭深度写入。
原因是unityshader中默认是ZWrite On,在我们UsePass后,下一个Pass中再次进行了深度写入,这样原有的测试就失效了,也就无法判断哪些像素通过了深度测试,从而看不到透视效果。
Name &#34;TEST&#34;
Tags { &#34;Queue&#34; = &#34;Transparent&#34; }
Blend One One
ZTest Greater
ZWrite Off
<hr/>到此为止,就完成了unity shader透视效果的实现,谢谢大家,欢迎大佬们指点。
写文章真的是个体力活,求求各位大佬收藏的同时点个赞,评论个666也行。
<hr/>下面是源码
Shader &#34;MyShader/Xray02&#34;
{
Properties
{
_MainTex (&#34;Texture&#34;, 2D) = &#34;white&#34; {}
[HDR]_LineColor(&#34;Color&#34;,Color) = (1,1,1,1)
_FresnelWidth (&#34;FresnelWidth&#34;,float) = 1
_LineSize(&#34;LineSize&#34;,float) = 1
_Speed(&#34;Speed&#34;,float) = 1
}
SubShader
{
Pass
{
Name &#34;TEST&#34;
Tags { &#34;Queue&#34; = &#34;Transparent&#34; }
Blend One One
ZTest Greater
ZWrite Off
LOD 100
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include &#34;UnityCG.cginc&#34;
struct appdata
{
float4 vertex : POSITION;
float2 uv : TEXCOORD0;
float3 normal: NORMAL;
};
struct v2f
{
float2 uv : TEXCOORD0;
float4 vertex : SV_POSITION;
float3 worldPos : TEXCOORD1;
half3 worldNormal: TEXCOORD2;
};
sampler2D _MainTex;float4 _MainTex_ST;
fixed4 _LineColor;
float _LineSize;
float _Speed;
float _FresnelWidth;
v2f vert (appdata v)
{
v2f o;
o.vertex = UnityObjectToClipPos(v.vertex);
o.uv = TRANSFORM_TEX(v.uv, _MainTex);
o.worldPos = mul(unity_ObjectToWorld,v.vertex);
o.worldNormal = UnityObjectToWorldNormal(v.normal);
return o;
}
fixed4 frag (v2f i) : SV_Target
{
fixed3 V = normalize(UnityWorldSpaceViewDir(i.worldPos));
fixed3 N = normalize(i.worldNormal);
fixed VdotN = dot(V,N);
fixed fresnel = 2* pow(1-VdotN,_FresnelWidth);
fixed4 col = tex2D(_MainTex, i.uv);
fixed line_Y = frac(i.worldPos.y*_LineSize+_Time.y*_Speed);
return fresnel * _LineColor*line_Y;
}
ENDCG
}
}
} |
本帖子中包含更多资源
您需要 登录 才可以下载或查看,没有账号?立即注册
×
|