shader入门精要第十一章

让画面动起来
Unity Shader内置的时间变量


纹理动画
序列帧动画


//个人感觉粒子编辑器完全能够胜任简单的粒子效果
要播放帧动画,从本质来说, 我们需要计算出每个时刻需要播放的关键帧在纹理中的位置 。

Shader "Unlit/序列帧动画"

{       
    Properties{  
        _Color("Main Clolr",Color) = (1,1,1,1)
    _MainTex("MainTex",2D) = "white"{}
    _HorizantalAmount("HorizantalAmount",Float) = 4//在水平方向包含的关键帧图像的个数
    _VerticalAmount("VerticalAmount",Float) = 4//在竖直方向包含的关键帧图像的个数
    _Speed("Speed",Range(1,100)) = 30
    }



    SubShader{
        Tags {
           "Queue" = "Transparent" 
           "IgnoreProjector" = "True" 
        "RenderType" = "Transparent"
        }
        Pass {
            Name "FORWARD"
            Tags {
                "LightMode" = "ForwardBase"
            }
        ZWrite Off
        Blend  SrcAlpha OneMinusSrcAlpha

            CGPROGRAM

            #pragma vertex vert
            #pragma fragment frag
        #include "Lighting.cginc"
     
        //参数定义
        fixed4 _Color;
        sampler2D _MainTex;
        float4 _MainTex_ST;
        float _HorizantalAmount;
        float _VerticalAmount;
        float _Speed;

struct VertexInput {                           //输入结构
    float4 vertex : POSITION;
    float4 uv0:TEXCOORD0;
};


struct VertexOutput {                          //输出结构
    float4 pos : SV_POSITION;
    float2 uv:TEXCOORD0;
};


VertexOutput vert(VertexInput v) {            //顶点Shader
    VertexOutput o = (VertexOutput)0;
    o.pos = UnityObjectToClipPos(v.vertex);
    o.uv = TRANSFORM_TEX(v.uv0, _MainTex);
    return o;
}

            float4 frag(VertexOutput i) : COLOR {          //像素Shader
               float time = floor(_Time.y * _Speed);   //内置时间变量 _Time(t/20,t,2t,3t)
            float row = floor(time / _HorizantalAmount);
            float column = time - row * _VerticalAmount;
            //根据播放的速度计算对应的序列帧行和列

            //纹理中包含许多关键帧图像,将采样坐标映射到每一个关键帧的坐标范围内
            //纹理中的采样方向与关键帧的播放顺序在Y方向是反的,因此Y坐标做减法
            half2 uv = float2(i.uv.x / _HorizantalAmount,i.uv.y / _VerticalAmount);
            uv.x += column / _HorizantalAmount;
            uv.y -= row / _VerticalAmount;

            fixed4 c = tex2D(_MainTex,uv);
            c.rgb *= _Color;

            return c;
            }
            ENDCG
        }
    }
        FallBack "Transparent/VertexLit"
}

 

滚动背景

滚动背景一般使用多个层以不同的速度进行滚动,滚动的关键在于使滚动方向上的纹理坐标的增量与时间变量相乘,这样随着时间的变化,滚动方向上的采样坐标不断变换,画面也能连续变化。

Shader "Unlit/背景"
 {      
    Properties{          
        _MainTex("Base Layer",2D) = "white"{}
    _DetialTex("Second Layer",2D) = "white"{}
    _ScrollX("Base Layer Scroll Speed",Float) = 1.0
    _Scroll2X("Second Layer Scroll Speed",Float) = 1.0
    _Multiplier("Layer Multiplier",Float) = 1
    }



    SubShader{
        Tags {
            "RenderType" = "Opaque"
        }
        Pass {
            Name "FORWARD"
            Tags {
                "LightMode" = "ForwardBase"
            }

            CGPROGRAM

            #pragma vertex vert//哪个函数包含了顶点着色器的代码
            #pragma fragment frag//哪个函数包含了片元着色器的代码
        #include "Lighting.cginc"
     //参数定义
        sampler2D _MainTex;
        float4 _MainTex_ST;
        sampler2D _DetialTex;
        float4  _DetialTex_ST;
        float	_ScrollX;
        float	_Scroll2X;
        float	_Multiplier;


struct VertexInput {                           //输入结构
    float4 vertex : POSITION;
    float4 uv0:TEXCOORD0;
};


struct VertexOutput {                          //输出结构
    float4 pos : SV_POSITION;
    float4 uv:TEXCOORD0;
};


VertexOutput vert(VertexInput v) {            //顶点Shader
    VertexOutput o = (VertexOutput)0;
    o.pos = UnityObjectToClipPos(v.vertex);
    o.uv.xy = TRANSFORM_TEX(v.uv0, _MainTex) + frac(float2(_ScrollX, 0) * _Time.y);
    o.uv.zw = TRANSFORM_TEX(v.uv0, _DetialTex) + frac(float2(_Scroll2X, 0) * _Time.y);
    //frac取小数函数,使取样坐标在[0,1]范围内,背景连续重复滚动
    return o;
}

            float4 frag(VertexOutput i) : COLOR {          //像素Shader
               fixed4 baseLayer = tex2D(_MainTex,i.uv.xy);
            fixed4 secondLayer = tex2D(_DetialTex,i.uv.zw);

            fixed4 c = lerp(baseLayer,secondLayer,secondLayer.a);

            c.rgb *= _Multiplier;  //_Multiplier用来控制整体亮度
            return c;
            }
            ENDCG
        }
    }
        FallBack "VertexLit"
}

顶点动画

流动的河流

原理通常就是使用正弦函数等来模拟水流的 波动效果

Shader "Unlit/河流"
 {        
    Properties{ 
        _Color("Main Color",Color) = (1,1,1,1)
    _MainTex("MainTex",2D) = "white"{}
    _Magnitude("Magnitude",Float) = 1
    _Frequency("Frequency",Float) = 1
    _InvWaveLength("InvWaveLength",Float) = 10
    _Speed("Speed",Float) = 0.5
    }



    SubShader{
        Tags {"Queue" = "Transparent"
        "IgnoreProjector" = "True"
        "RenderType" = "Transparent"
        "DisableBatching" = "True"}
        //为透明效果设置对应标签,这里“DisableBatching”的标签是关闭批处理,
        //包含模型空间顶点动画的Shader是需要特殊处理的Shader,
        //而批处理会合并所有相关的模型,这些模型各自的模型空间会丢失
        //而顶点动画需要在模型空间对顶点进行偏移
         
     
        Pass {
            Name "FORWARD"
            Tags {
                "LightMode" = "ForwardBase"
            }
        ZWrite Off
        Blend SrcAlpha OneMinusDstAlpha
        Cull Off

            CGPROGRAM

            #pragma vertex vert//哪个函数包含了顶点着色器的代码
            #pragma fragment frag//哪个函数包含了片元着色器的代码
        #include "Lighting.cginc"
            #include "UnityCG.cginc"
        

        //参数定义
        fixed4 _Color;
            sampler2D _MainTex;
            float4 _MainTex_ST;
            float _Magnitude;
            float _Frequency;
            float _InvWaveLength;
            float _Speed;

struct VertexInput {                           //输入结构
    float4 vertex : POSITION;
    float2 uv0:TEXCOORD0;
};


struct VertexOutput {                          //输出结构
    float4 pos : SV_POSITION;
    float2 uv:TEXCOORD0;
};


VertexOutput vert(VertexInput v) {            //顶点Shader
    VertexOutput o = (VertexOutput)0;
    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;
    //在顶点进行空间变换前,对x分量进行正弦操作
    o.pos = UnityObjectToClipPos(v.vertex);

    o.uv = TRANSFORM_TEX(v.uv0, _MainTex);
    o.uv += float2(0.0, _Time.y * _Speed);
    //进行纹理动画

    return o;
}

            float4 frag(VertexOutput i) : COLOR {          //像素Shader
               fixed4 c = tex2D(_MainTex,i.uv);
                c.rgb *= _Color.rgb;
                return c;
            }
            ENDCG
        }
    }
        FallBack "Transparent/VertexLit"
}

 

广告牌

广告牌会根据视角方向来旋转一个被纹理着色的多边形,使多边形看起来始终朝着摄像机。

广告牌技术的本质是构建旋转矩阵。计算过程中先根据初始计算得到目标的表面法线(例如视角方向)和指向上的方向,这两者的方向往往不是垂直的,根据这两者的方向做叉乘得到垂直于两者的方向,再根据这个得到的方向,假定之前的两个方向某一个不变,计算另一个垂直的方向,这样得到三个正交的方向

广告牌技术中需要指定一个锚点,这个锚点在旋转过程中是固定不变的,以此来确定多边形在空间中的位置。

Shader "Unlit/广告牌"
{
    Properties{
 _Color("Color",Color) = (1,1,1,1)
        _MainTex("MainTex",2D) = "white"{}
        _VerticalBillboarding("VerticalBillboarding",Range(0,1)) = 1
    }



        SubShader{
            Tags {
                "Queue" = "Transparent"
                "IgnoreProjector" = "True"
            "RendererType" = "Transparent" 
            "DisableBatching" = "True"}

          
            Pass {
                Name "FORWARD"
                Tags {
                    "LightMode" = "ForwardBase"
                }

             ZWrite Off
            Blend SrcAlpha OneMinusSrcAlpha
            Cull Off
            //这里关闭了深度写 ,开启并设置了混合模式,并关闭了剔除功能。这是为了让广告牌的每
个面都能显示
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            #include "UnityCG.cginc"
            #include "Lighting.cginc"

            fixed4 _Color;
            sampler2D _MainTex;
            float4 _MainTex_ST;
            float _VerticalBillboarding;


   struct VertexInput {                           //输入结构
       float4 vertex : POSITION;
       float4 uv0:TEXCOORD0;
   };


   struct VertexOutput {                          //输出结构
       float4 pos : SV_POSITION;
       float2 uv:TEXCOORD0;
   };


   VertexOutput vert(VertexInput v) {            //顶点Shader
       VertexOutput o = (VertexOutput)0;
  
           float3 center = float3(0, 0, 0);
           //选择模型空间的原点作为变换锚点
           float3 viewer = mul(unity_WorldToObject, float4(_WorldSpaceCameraPos, 1));
           //将模型空间的观察方向作为法线方向
           float3 normalDir = viewer - center;
           normalDir.y = normalDir.y * _VerticalBillboarding;
           normalDir = normalize(normalDir);
           //当_VerticalBillboarding的值为1时,法线方向固定为视角方向,
           //当_VerticalBillboarding的值为0时,法线在Y方向上没有分量,那么向上的方向固定为(0,1,0),这样才能保证与法线方向垂直
           float3 upDir = abs(normalDir.y) > 0.999 ? float3(0, 0, 1) : float3(0, 1, 0);
           //这里对向上方向是否与法向方向相平行,防止得到错误的叉乘结果
           float3 rightDir = normalize(cross(normalDir, upDir));
           upDir = normalize(cross(normalDir, rightDir));

           float3 offset = v.vertex.xyz - center;
           float3 localPos = center + rightDir * offset.x + upDir * offset.y + normalDir * offset.z;

           o.pos = UnityObjectToClipPos(float4(localPos, 1));
           o.uv = TRANSFORM_TEX(v.uv0, _MainTex);
           return o;
   }

               float4 frag(VertexOutput i) : COLOR {          //像素Shader
              fixed4 c = tex2D(_MainTex,i.uv);
                c.rgb *= _Color.rgb;
                return c;
               }
               ENDCG
           }
    }
        FallBack "Transparent/VertexLit"
}

 

 永远朝向相机

注意事项

在顶点变换的Shader中需要做特殊处理,即关闭批处理,这样能防止Unity自动对相关模型做合并处理,从而丢失模型空间。

在对顶点进行变换后,如果想要得到正确的阴影,需要添加一个自定义的ShadowCaster Pass,否则得到的阴影会是变换前的阴影。自定义的Pass中,需要使用内置的宏。

Shader "Unlit/河流"
{
    Properties{
        _Color("Main Color",Color) = (1,1,1,1)
    _MainTex("MainTex",2D) = "white"{}
    _Magnitude("Magnitude",Float) = 1
    _Frequency("Frequency",Float) = 1
    _InvWaveLength("InvWaveLength",Float) = 10
    _Speed("Speed",Float) = 0.5
    }



        SubShader{
            Tags {
            "DisableBatching" = "True"}
        //为透明效果设置对应标签,这里“DisableBatching”的标签是关闭批处理,
        //包含模型空间顶点动画的Shader是需要特殊处理的Shader,
        //而批处理会合并所有相关的模型,这些模型各自的模型空间会丢失
        //而顶点动画需要在模型空间对顶点进行偏移


        Pass {
            Name "FORWARD"
            Tags {
                "LightMode" = "ForwardBase"
            }
       
        Cull Off

            CGPROGRAM

            #pragma vertex vert//哪个函数包含了顶点着色器的代码
            #pragma fragment frag//哪个函数包含了片元着色器的代码
      
            #include "UnityCG.cginc"


        //参数定义
        fixed4 _Color;
            sampler2D _MainTex;
            float4 _MainTex_ST;
            float _Magnitude;
            float _Frequency;
            float _InvWaveLength;
            float _Speed;

struct VertexInput {                           //输入结构
    float4 vertex : POSITION;
    float2 uv0:TEXCOORD0;
};


struct VertexOutput {                          //输出结构
    float4 pos : SV_POSITION;
    float2 uv:TEXCOORD0;
};


VertexOutput vert(VertexInput v) {            //顶点Shader
    VertexOutput o = (VertexOutput)0;
    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;
    //在顶点进行空间变换前,对x分量进行正弦操作
    o.pos = UnityObjectToClipPos(v.vertex + offset);

    o.uv = TRANSFORM_TEX(v.uv0, _MainTex);
    o.uv += float2(0.0, _Time.y * _Speed);
    //进行纹理动画

    return o;
}

            float4 frag(VertexOutput i) : COLOR {          //像素Shader
               fixed4 c = tex2D(_MainTex,i.uv);
                c.rgb *= _Color.rgb;
                return c;
            }
            ENDCG
        }


Pass {
  Tags { "LightMode" = "ShadowCaster" }
  CGPROGRAM
  #pragma vertex vert
  #pragma fragment frag
  #pragma multi_compile_shadowcaster
  #include "UnityCG.cginc"
  float _Magnitude;
  float _Frequency;
  float _InvWaveLength;
  float _Speed;
  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
  }
    }
        FallBack "VertexLit"
}

 

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值