Unity Shader 学习笔记(18)纹理动画、顶点动画、广告牌技术

来源:互联网 发布:linux 查看数据包 编辑:程序博客网 时间:2024/05/18 02:57

Unity Shader 学习笔记(18)纹理动画、顶点动画、广告牌技术

参考书籍:《Unity Shader 入门精要》
【《Real-Time Rendering 3rd》 提炼总结】(九) 第十章 · 游戏开发中基于图像的渲染技术总结


Unity Shader的内置变量


纹理动画

可用于代替粒子系统模拟动画效果。

帧序列动画

8x8帧序列纹理动画

Properties {    _Color ("Color Tint", Color) = (1, 1, 1, 1)    _MainTex ("Image Sequence", 2D) = "white" {}            // 包含所有关键帧图像的纹理    _HorizontalAmount ("Horizontal Amount", Float) = 4      // 水平方向包含关键帧图像个数    _VerticalAmount ("Vertical Amount", Float) = 4          // 同上(竖直方向)    _Speed ("Speed", Range(1, 100)) = 30                    // 播放速度}

因为序列帧图像通常是透明纹理,所以就按半透明的方式设置。

// 序列帧图像通常包含透明通道Tags {"Queue"="Transparent" "IgnoreProjector"="True" "RenderType"="Transparent"}Pass {    Tags { "LightMode"="ForwardBase" }    // 关闭深度写入,开启混合模式    ZWrite Off    Blend SrcAlpha OneMinusSrcAlpha    ...    fixed4 frag (v2f i) : SV_Target {        float time = floor(_Time.y * _Speed);               // floor()取整。CG的函数        float row = floor(time / _HorizontalAmount);        // 行索引        float column = time % _HorizontalAmount;        // 列索引        // 纹理坐标从上到下,所以是-row。下面两种计算方法        //half2 uv = float2(i.uv.x /_HorizontalAmount, i.uv.y / _VerticalAmount);        //uv.x += column / _HorizontalAmount;        //uv.y -= row / _VerticalAmount;                half2 uv = i.uv + half2(column, -row);        uv.x /=  _HorizontalAmount;        uv.y /= _VerticalAmount;        fixed4 c = tex2D(_MainTex, uv);        c.rgb *= _Color;        return c;    }}

滚动动画

可以模拟2D跑酷游戏背景的视察效果。

Properties {    _MainTex ("Base Layer (RGB)", 2D) = "white" {}          // 第一层背景(较远背景)    _DetailTex ("2nd Layer (RGB)", 2D) = "white" {}         // 较近背景    _ScrollX ("Base layer Scroll Speed", Float) = 1.0       // 第一层背景滚动速度    _Scroll2X ("2nd layer Scroll Speed", Float) = 1.0       // 第二层速度    _Multiplier ("Layer Multiplier", Float) = 1             // 纹理亮度}
v2f vert (a2v v) {    v2f o;    o.pos = UnityObjectToClipPos(v.vertex);    // TRANSFORM_TEX 得到初始纹理坐标,加上偏移坐标。frac为获取小数部分的值。    o.uv.xy = TRANSFORM_TEX(v.texcoord, _MainTex) + frac(float2(_ScrollX, 0.0) * _Time.y);    o.uv.zw = TRANSFORM_TEX(v.texcoord, _DetailTex) + frac(float2(_Scroll2X, 0.0) * _Time.y);    return o;}fixed4 frag (v2f i) : SV_Target {    fixed4 firstLayer = tex2D(_MainTex, i.uv.xy);    fixed4 secondLayer = tex2D(_DetailTex, i.uv.zw);    fixed4 c = lerp(firstLayer, secondLayer, secondLayer.a);    c.rgb *= _Multiplier;    return c;}

顶点动画

即修改顶点信息的动画。

一张长方形纹理,通过修改顶点信息,实现扭曲效果。

Properties {    _MainTex ("Main Tex", 2D) = "white" {}    _Color ("Color Tint", Color) = (1, 1, 1, 1)    _Magnitude ("Distortion Magnitude", Float) = 1                  // 幅度    _Frequency ("Distortion Frequency", Float) = 1                  // 频率    _InvWaveLength ("Distortion Inverse Wave Length", Float) = 10   // 波长的倒数    _Speed ("Speed", Float) = 0.5}
// DisableBatching:取消批处理。需要对模型空间下的顶点位置进行偏移,所以就不合并模型。Tags {"Queue"="Transparent" "IgnoreProjector"="True" "RenderType"="Transparent" "DisableBatching"="True"}Pass {    Tags { "LightMode"="ForwardBase" }    ZWrite Off    Blend SrcAlpha OneMinusSrcAlpha    Cull Off                            // 关闭剔除模式(正反面都显示)    ...    v2f vert(a2v v) {        v2f o;        float4 offset;        offset.yzw = float3(0.0, 0.0, 0.0);        // 只改变X变量        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.texcoord, _MainTex);        o.uv +=  float2(0.0, _Time.y * _Speed);        return o;    }}


注意到前面使用内置阴影的Pass是没有处理顶点动画的,所以最后投影的阴影是错误的,需要自定义ShadowCasterPass,在这个Pass做相同顶点变换即可。

下面用到阴影的宏都在UnityCG.cginc中定义。

Pass {    Tags { "LightMode" = "ShadowCaster" }    ...    #pragma multi_compile_shadowcaster    ...    struct v2f {         // 内置宏,阴影投射需要的变量        V2F_SHADOW_CASTER;    };    v2f vert(appdata_base 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;        v.vertex = v.vertex + offset;        // 计算阴影        TRANSFER_SHADOW_CASTER_NORMALOFFSET(o)        return o;    }    fixed4 frag(v2f i) : SV_Target {        // 阴影投射        SHADOW_CASTER_FRAGMENT(i)    }    ENDCG}

广告牌技术(Billboarding)

即让纹理始终面对着镜头。本质就是构建旋转矩阵,三个基向量:表面法线、向上的方向、向右的方向。还需要指定一个锚点,即旋转中心。

计算方法(如下图):
1. 先计算向右方向 right = up × normal。起始up是竖直向上的。
2. 叉乘即可得到向上方向 up’ = normal × right。

Vertical Restraints值为1,所有星星不管原来什么角度都会转向到镜头。

Vertical Restraints值为0,所有星星固定向上方向,并最大限度面向镜头。

Properties {    _MainTex ("Main Tex", 2D) = "white" {}    _Color ("Color Tint", Color) = (1, 1, 1, 1)    _VerticalBillboarding ("Vertical Restraints", Range(0, 1)) = 1      // 垂直程度,0:固定向上,1:固定法线}
// 需要模型空间下的位置来作为锚点计算,所以要关掉批处理。Tags {"Queue"="Transparent" "IgnoreProjector"="True" "RenderType"="Transparent" "DisableBatching"="True"}Pass {     Tags { "LightMode"="ForwardBase" }    ZWrite Off    Blend SrcAlpha OneMinusSrcAlpha    Cull Off    CGPROGRAM    ...    v2f vert (a2v v) {        v2f o;        float3 center = float3(0, 0, 0);                // 模型空间的原点作为广告牌锚点        float3 viewer = mul(unity_WorldToObject,float4(_WorldSpaceCameraPos, 1));   // 获取模型空间下视角        float3 normalDir = viewer - center;        // _VerticalBillboarding 为1为法线固定(始终向上),为0为向上方向固定。         normalDir.y =normalDir.y * _VerticalBillboarding;        normalDir = normalize(normalDir);        // 通过normalDir.y 先判断法线和向上是否平行(叉积会错),来改变向上方向        float3 upDir = abs(normalDir.y) > 0.999 ? 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.texcoord,_MainTex);        return o;    }

关于 DisableBatching

如果要进行一些顶点动画,就要关闭批处理。性能会下降,所以尽量避免使用模型空间下的绝对位置和方向进行计算。如广告拍技术中,可以用顶点颜色来存储顶点到锚点的距离。