(一)内置时间变量
记一下好了,后面用到最多的是_Time.y
(二)序列帧动画
这一块感觉是李仕sense喜欢的内容,我就提一嘴就过了。
比较重要的是片元着色器,给了一个Unity里要记得注意点
half2 uv=i.uv+half2(column,-row)有个印象好了。
(三)滚动背景
这种效果做出来还是挺好的,用在过场动画里感觉不错。
代码内容并不复杂,主要工作在vert中,对两张纹理的采样坐标随时间做个水平位移就好了
然后在frag里利用secondLayer的透明度混合一下颜色就好了。
想要BaseLayer看上去更远的效果,就只要把他的速度调到比SecondLayer更慢一点就好了。
(四)顶点动画
这里本章毫无疑问最有用的。
首先来看一个流动的河流。这个例子里面就很明显应用了顶点位移。
先来看一些关键点,包括subpass的tags以及pass内的剔除。
这里的tags除了我们处理透明物体常用的三剑客以外,还加了一个DisableBatching=True,看完整本书后,就能够理解了,比较批处理的时候要把几个物体在揉合成一个新的网格结构,这就意味着自己本身的模型空间就不存在,对这些需要在模型空间进行顶点动画的物体来说变不能接受了。
之后是关闭了背面剔除,说是让水的每个面都能被看到,我忘了去从背面看看了,就是不知道为什么这里使用了Blend?。
SubShader
{
//使用新增tag DisableBatching 禁用批处理,防止模型各自的模型空间丢失
Tags { "RenderType"="Transparent" "Queue"="Transparent" "IgnoreProjector"="True" "DisableBatching"="True"}
Pass
{
Tags{"LightMode"="ForwardBase"}
ZWrite Off
Blend SrcAlpha OneMinusSrcAlpha
Cull Off
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"
struct appdata
{
float4 vertex : POSITION;
float2 uv : TEXCOORD0;
};
struct v2f
{
float2 uv : TEXCOORD0;
float4 pos : SV_POSITION;
};
sampler2D _MainTex;
float4 _MainTex_ST;
float4 _Color;
float _Magnitude;
float _Frequency;
float _InvWaveLength;
float _Speed;
v2f vert (appdata v)
{
v2f o;
float4 offset;
offset.yzw=float3(0.0,0.0,0.0);
offset.x=sin(_Frequency*_Time.y+v.vertex.x*_InvWaveLength+v.vertex.y*_InvWaveLength+
v.vertex.z*_InvWaveLength)*_Magnitude;
o.pos = UnityObjectToClipPos(v.vertex+offset);
o.uv = TRANSFORM_TEX(v.uv, _MainTex);
o.uv+=float2(0.0,_Time.y*_Speed);
return o;
}
fixed4 frag (v2f i) : SV_Target
{
fixed4 c = tex2D(_MainTex, i.uv);
c.rgb*=_Color.rgb;
return c;
}
ENDCG
}
}
FallBack "Transparent/VertexLit"
现在想想原来感觉很难得顶点动画感觉也就还行了。这里只是对了一个平面的x坐标下手,想必还有其他更复杂的顶点动画,还是要去拓展看看。
下面让我们来看看广告牌的技术吧。背后的原理都很重要,因为比较长,还是直接贴图吧。
这里我们可以看大广告牌技术的核心就是构建旋转矩阵,而这在代码中主要是由vert在模型空间实现的。
v2f vert (appdata v)
{
v2f o;
float3 center=float3(0,0,0);
float3 viewer=mul(unity_WorldToObject,float4(_WorldSpaceCameraPos,1));
float3 normalDir=viewer-center;
//这里如何靠在y方向约束的没怎么搞懂
normalDir.y*=_VerticalBillboard;
normalDir=normalize(normalDir);
//get the approximate up dir
float3 upDir=abs(normalDir.y)>0.999f?float3(0,0,1):float3(0,1,0);
float3 rightDir=normalize(cross(upDir,normalDir));
upDir=normalize(cross(normalDir,rightDir));
float3 centerOffs=v.vertex.xyz-center;
float3 localPos=center+rightDir*centerOffs.x+upDir*centerOffs.y+normalDir*centerOffs.z;
o.pos = UnityObjectToClipPos(float4(localPos,1));
o.uv = TRANSFORM_TEX(v.uv, _MainTex);
return o;
}
之前没搞懂normal怎么被约束的。现在总算知道了,约束要靠后面的的代码来实现。normal.y*1时normal不变,就相当于固定了normal,而normal.y*0时,相当于upDir被固定为(0,1,0)了。大概是这样吧,如此说来除了0、1的值感觉不太有意义。
其他代码里面注释的还行,第一个upDir是估摸着得到的,写这么长就是为了防止normal也是直接朝上的。这里可以看到一开始的upDir确实挺粗略的,怎么想都不会一开始就与normal垂直。
后面先叉积得到rightDir,再叉积得到准确的Updir就是挺奇妙的。
之后就是每一个点都根据中心,利用这三个向量做了旋转,就得到我们想要的广告牌效果了。
以后要是做纸片人敌人,可以写这么一个shader,让敌人都朝向你(doge)
(五)注意事项
总而言之就是默认的阴影时很难直接用默认的fallback实现了,这就需要我们为自己想要实现的效果去自定义一个shadowCaster了
这边是一个用来实现水波的阴影的例子。感觉就是使用了其他的内置函数,整体思路和传统的ShadowCAster Pass差不多。当然还是要根据实际看问题。
本文地址: UNITY SHADER 入门精要——第11章
您必须 登录 才能发表评论