——20.9.14

Shader中主要有及两种动画,一种就是纹理动画还有一种就是顶点动画。

动画效果一般都需要把时间加入一些变量的计算,以便画面可以随时间发生变化。下面是Shader中的如何去访问时间的方法。

一、纹理动画

序列帧动画就是我们接触的第一种纹理动画。序列帧动画原理就是依次播放一系列关键帧动画。当图片的切换速度达到一定数值后,看上去就像是连续的动画。优点:在于它的灵活性强,不用进行物理计算就会有对应的动画效果。缺点:依旧是需要逐关键帧的动画内容,工作量依旧很大。

我们看看shaderlab部分。我们需要看有什么样的变量。一般来说序列帧一般是一张图片包括了所有的关键帧。并且按播放顺序排放。并且方便读取一般是规整的(就是没有边框)。所以我们要确定有几行_HorizontalAmount几列_VerticalAmount根据图片决定。还有就是播放速度_Speed,最后就是图片_MainTex,和颜色_Color。

//frag
float time = floor(_Time.y * _Speed);
float row = floor(time / _HorizontalAmount);
float column = time - row * _VerticalAmount;
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;

  首先是_Time.y就是取时间变量t,然后通过取整来确定行数,然后通过取小数来确定列数。这里是在片元着色器中的两种读取方式。一种是根据行和列把坐标放大到整张图片。并且通过增加基础单位来进行可以看到对uv.y进行的是减操作。是因为unity里面是从左下为(0,0)。然后第二种方法先去确定要读取的行列。然后再去细分到一个区间内。要注意上面这两种方法本质上都是为了取这张关键帧中的一张图片。于是就要把uv坐标缩放到其中的一张。

Shader "Unlit/11-1ImageSequenceAnimation"
{
Properties
{
_Color ("Color", Color) = (1,1,1,1)
_MainTex ("Texture", 2D) = "white" {}
_HorizontalAmount ("HorizontalAmount", Float) = 4
_VerticalAmount ("VerticalAmount", Float) = 4
_Speed ("Speed", Range(1, 100)) = 30
}
SubShader
{
Tags { "Queue"="Transparent" "IgnoreProjector"="True" "RenderType"="Transparent" } Pass
{
Tags { "LightMode"="ForwardBase" }
ZWrite off
Blend SrcAlpha OneMinusSrcAlpha
CGPROGRAM
#pragma multi_compile_fwdbase
#pragma vertex vert
#pragma fragment frag #include "UnityCG.cginc" struct appdata
{
float4 vertex : POSITION;
float2 texcoord : TEXCOORD0;
}; struct v2f
{
float2 uv : TEXCOORD0;
float4 pos : SV_POSITION;
}; sampler2D _MainTex;
float4 _MainTex_ST;
fixed4 _Color;
float _VerticalAmount;
float _HorizontalAmount;
float _Speed; v2f vert (appdata v)
{
v2f o;
o.pos = UnityObjectToClipPos(v.vertex);
o.uv = TRANSFORM_TEX(v.texcoord, _MainTex);
return o;
} fixed4 frag (v2f i) : SV_Target
{
float time = floor(_Time.y * _Speed);
float row = floor(time / _HorizontalAmount);
float column = time - row * _VerticalAmount; 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; }
ENDCG
}
}FallBack "Transparent/VertexLit"
}

下面就是针对不同的行列。相同的速度数值也是截然不同的速度。  

然后就是不同的背景进远景对应的速度不同。我们看一下首先需要两张图分别是近景和远景_MainTex _DetailTex。然后就是分别他们的速度_ScrollX _Scroll2X。最后就是有关亮度_Multiplier。

T frac(T v)
//vert
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;
fixed4 c = lerp(firstLayer, secondLayer, secondLayer.a);

  frac函数是返回变量的小数部分。其中的变量可以是float float2 float3 float4 都会分别对变量取小数。这样可以保证图片可以循环播放。因为上面每一个顶点取偏移值值是一样的。然后需要混合颜色采用的lerp然后第三个变量取的是secondlayer的a通道是因为这种图是一张黑白图取值分别就是1或者0。便可以确定近景中哪一些是镂空可以放远景的颜色。

Shader "Unlit/11-2ScrollingBackground"
{
Properties
{
_MainTex ("BaseLayer(RGB)", 2D) = "white" {}
_DetailTex ("2ndLayer(RGB)", 2D) = "white" {}
_ScrollX ("BaseLayerScrollSpeed", Float) = 1.0
_Scroll2X ("2ndLayerScrollSpeed", Float) = 1.0
_Multiplier ("LayerMultiplier", Float) = 1
}
SubShader
{
Tags { "RenderType"="Opaque" "Queue"="Geometry" } Pass
{
Tags { "LightMode"="ForwardBase" }
CGPROGRAM
#pragma vertex vert
#pragma fragment frag #include "UnityCG.cginc" struct appdata
{
float4 vertex : POSITION;
float2 texcoord : TEXCOORD0;
}; struct v2f
{
float4 uv : TEXCOORD0;
float4 pos : SV_POSITION;
}; sampler2D _MainTex;
float4 _MainTex_ST;
sampler2D _DetailTex;
float4 _DetailTex_ST;
float _ScrollX;
float _Scroll2X;
float _Multiplier; v2f vert (appdata v)
{
v2f o;
o.pos = UnityObjectToClipPos(v.vertex);
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;
}
ENDCG
}
}FallBack "VertexLit"
}

二、顶点动画

我们先做一个2d的顶点动画。就是河流。我们看一下我们需要河流的纹理_MainTex,_Color调整整体颜色,控制水流动的幅度_Magniture,水流的波动_Frequency,波长的倒数_InvWaveLength(即该值越大,波长越小),流动速度_Speed.

Tags { "Queue"="Transparent" "IgnoreProjector"="True" "RenderType"="Transparent" "DisableBatching"="True" }
//vert
offset.yzw = float3(0,0,0);
offset.x = sin(_Frequency * _Time.y + v.vertex.x * _InvWaveLength + v.vertex.y * _InvWaveLength + v.vertex.z * _InvWaveLength) * _Magnitude;

  DisableBatching关闭该tags是为了指明是否要对该SubShader进行批处理,因为这些需要特殊处理的Shader基本包括顶点动画。会合并与之相关的模型导致相关的顶点出现问题,合并模型会导致各自模型空间丢失(留个坑?)。顶点动画本质就是改变其中的顶点着色器中顶点的位置。这个的vertex是模型空间中的加上模型空间的位置分量。

Shader "Unlit/11-3Water"
{
Properties
{
_MainTex ("Texture", 2D) = "white" {}
_Color ("Color", Color) = (1,1,1,1)
_Magnitude ("Distortion Magnitude", Float) = 1
_Frequency ("Distortion Frequency", Float) = 1
_InvWaveLength ("InvWaveLength", Float) = 10
_Speed ("Speed", Float) = 0.5
}
SubShader
{
Tags { "Queue"="Transparent" "IgnoreProjector"="True" "RenderType"="Transparent" "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 texcoord : TEXCOORD0;
}; struct v2f
{
float2 uv : TEXCOORD0;
float4 pos : SV_POSITION;
}; sampler2D _MainTex;
float4 _MainTex_ST;
fixed4 _Color;
float _Magnitude;
float _Frequency;
float _InvWaveLength;
float _Speed; v2f vert (appdata v)
{
v2f o;
float4 offset;
offset.yzw = float3(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.texcoord, _MainTex);
o.uv += float2(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 "VertexLit"
}

  

另一种顶点动画就是广告牌技术。会根据视角方向来旋转多边形,看上去好像面向摄像头,比如延误云朵闪光。其中的难处在于建立三个相互垂直的基向量。视角方向和向上的向量往往不垂直,所以要通过叉乘得到一个与两者相垂直的变量,然后再取该向量与视角方向叉积,更新向上的变量。

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);
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));

  上面就是得到三个基向量的过程。_VerticalBillboarding主要是这个向量通过调整数值来改变normal变量来模拟特殊需求。比如草地下面的根部是不懂的。还有一些广告牌只会有旋转操作但是不会脱离垂直方向。

// Upgrade NOTE: replaced 'mul(UNITY_MATRIX_MVP,*)' with 'UnityObjectToClipPos(*)'

// Upgrade NOTE: replaced '_World2Object' with 'unity_WorldToObject'

Shader "Unlit/11-4Billboard"
{
Properties
{
_MainTex ("Texture", 2D) = "white" {}
_Color ("Color", Color) = (1,1,1,1)
_VerticalBillboarding ("VerticalBillboarding", Range(0, 1)) = 1
}
SubShader
{
Tags { "Queue"="Transparent" "IgnoreProjector"="True" "RenderType"="Transparent" "DisableBatching"="True" } Pass
{
Tags { "LightMode"="ForwardBase" }
ZWrite Off
Blend SrcAlpha OneMinusSrcAlpha
Cull off
CGPROGRAM
#pragma vertex vert
#pragma fragment frag #include "UnityCG.cginc"
#include "Lighting.cginc" struct appdata
{
float4 vertex : POSITION;
float2 texcoord : TEXCOORD0;
}; struct v2f
{
float2 uv : TEXCOORD0;
float4 pos : SV_POSITION;
}; sampler2D _MainTex;
float4 _MainTex_ST;
fixed4 _Color;
float _VerticalBillboarding; v2f vert (appdata v)
{
v2f o;
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);
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;
} fixed4 frag (v2f i) : SV_Target
{
fixed4 c = tex2D(_MainTex, i.uv);
c.rgb *= _Color.rgb;
return c;
}
ENDCG
}
}FallBack "Transparent/VertexLit"
}

然后可以看到我们在做河流的时候河流的影子是不对的,我们需要重写ShaderCaster Pass。

struct v2f {
V2F_SHADOW_CASTER;
; v2f vert(appdata_base v){
v2f o;
float4 offset;
offset.yzw = float3(0,0,0);
offset.x = sin(_Frequency * _Time.y + v.vertex.x * _InvWaveLength + v.vertex.y * _InvWaveLength + v.vertex.z * _InvWaveLength) * _Magnitude;
v.vertex += offset;
TRANSFER_SHADOW_CASTER_NORMALOFFSET(o);
return o;
} fixed4 frag(v2f i) : SV_Target{
SHADOW_CASTER_FRAGMENT(i);
}

  这里采用了UnityCG.cginc中定义的一些宏。来计算阴影所需的内容。V2F_SHADOW_CASTER用于定义一些变量。

Shader "Unlit/11-5VertexAnimWithShadow"
{
Properties
{
_MainTex ("Texture", 2D) = "white" {}
_Color ("Color", Color) = (1,1,1,1)
_Magnitude ("Distortion Magnitude", Float) = 1
_Frequency ("Distortion Frequency", Float) = 1
_InvWaveLength ("InvWaveLength", Float) = 10
_Speed ("Speed", Float) = 0.5
}
SubShader
{
Tags { "DisableBatching"="True" } Pass
{
Tags { "LightMode"="ForwardBase" } Cull off
CGPROGRAM
#pragma vertex vert
#pragma fragment frag #include "UnityCG.cginc" struct appdata
{
float4 vertex : POSITION;
float2 texcoord : TEXCOORD0;
}; struct v2f
{
float2 uv : TEXCOORD0;
float4 pos : SV_POSITION;
}; sampler2D _MainTex;
float4 _MainTex_ST;
fixed4 _Color;
float _Magnitude;
float _Frequency;
float _InvWaveLength;
float _Speed; v2f vert (appdata v)
{
v2f o;
float4 offset;
offset.yzw = float3(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.texcoord, _MainTex);
o.uv += float2(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
}
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 a2v {
float4 vertex : POSITION;
float4 texcoord : TEXCOORD0;
}; struct v2f {
V2F_SHADOW_CASTER;
}; v2f vert(appdata_base v){
v2f o;
float4 offset;
offset.yzw = float3(0,0,0);
offset.x = sin(_Frequency * _Time.y + v.vertex.x * _InvWaveLength + v.vertex.y * _InvWaveLength + v.vertex.z * _InvWaveLength) * _Magnitude;
v.vertex += offset;
TRANSFER_SHADOW_CASTER_NORMALOFFSET(o);
return o;
} fixed4 frag(v2f i) : SV_Target{
SHADOW_CASTER_FRAGMENT(i);
} ENDCG
}
}FallBack "VertexLit"
}

  

最后就是一些小事项。取消批处理可以放置模型空间出现问题,但是会带来性能的问题,就是DrawCall增加了。所以应该避免一些在模型空间的计算,用顶点颜色存顶点到锚点的距离,避免使用模型空间中性作为锚点。

感谢你看到这里,Cheers!

不用画的动画——ShaderCp11的更多相关文章

  1. 画线动画——SVG版和纯CSS版

    概述 我们常常在网站中看到一些画线的动画效果,非常炫酷,大多数这种画线动画效果是通过SVG实现的,也有不少是用纯css实现的,下面我总结了一下这2种方法,供以后开发时参考,相信对其他人也有用. 参考资 ...

  2. WPF 画线动画效果实现

    原文:WPF 画线动画效果实现 弄了将近三天才搞定的,真是艰辛的实现. 看了很多博客,都太高深了,而且想要实现的功能都太强大了,结果基础部分一直实现不了,郁闷啊~ 千辛万苦终于找到了一个Demo,打开 ...

  3. pygame学习笔记(2)——从画点到动画

    转载请注明:@小五义 http://www.cnblogs.com/xiaowuyi 1.单个像素(画点)利用pygame画点主要有三种方法:方法一:画长宽为1个像素的正方形 #@小五义 http:/ ...

  4. css3实现画对号动画

    目标:实现对号动画,慢慢画出来的感觉: 原理:外层div的背景是一个对号图片,用一个div做遮罩,让遮罩div层从左到右做运动一次即可实现动画,需要注意的是遮罩div的初始位置应该在外层div的外面: ...

  5. 不用画线 设置UITableView的全屏分隔线

    如图  添加如下代码 sTableView.separatorInset = UIEdgeInsetsZero; sTableView.layoutMargins = UIEdgeInsetsZero ...

  6. css 画的动画表情

    <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8&quo ...

  7. canvas 画圈 demo

    html代码: <canvas id="clickCanvas2"  width="180" height="180" data-to ...

  8. iOS 10 :用 UIViewPropertyAnimator 编写动画

    英文:shinobicontrols 译文:戴仓薯 链接:http://www.jianshu.com/p/4244cf130478 [iOS 10 day by day] Day 1:开发 iMes ...

  9. 用HTML和CSS实现WWDC 2015上的动画效果

    用HTML和CSS实现WWDC 2015上的动画效果 动画效果WWDC 2015   原文:https://cssanimation.rocks/wwdc15/ 译者:周晓楷(@Helkyle) 每年 ...

  10. unity3d 纹理动画

    不知道大家有没有玩过赛车游戏 赛车游戏的跑道有路标,如下图 玩过赛车游戏的都知道,大多数赛车游戏的路标是会动的,如上图,它会从右往左运动 不会发动态图,大家脑补一下吧 没有玩过赛车游戏的也不要紧,大家 ...

随机推荐

  1. ZIAO日报 202302

    2023.2 2023年2月14日 10:23 2023.2.14 继续读<Multi-View Transformer for 3D Visual Grounding>,读到了relat ...

  2. Task :app:lintVitalRelease FAILED

    错误信息:Task :app:lintVitalRelease FAILED 问题原因:dl.google.com 无法连接 解决办法: 修改hosts(推荐)通过在线查询ip网站,找到dl.goog ...

  3. 用 go 实现多线程下载器

    本篇文章我们用Go实现一个简单的多线程下载器. 1.多线程下载原理 通过判断下载文件链接返回头信息中的 Accept-Ranges 字段,如果为 bytes 则表示支持断点续传. 然后在请求头中设置 ...

  4. 3 - 标准数据加密(DES)及其备选

    标准数据加密(DES)及其备选 我的博客 原书:<Understanding Cryptography: A Text book for Students and Practitioners&g ...

  5. CH32V00+WS2812制作音乐谱显示

    CH32V003,自带运放.SPI.PWM等外设模块,关键还便宜,便宜,便宜! 可以尝试来实现一个低成本的音乐谱显示. 1. 硬件设计 显示方面,使用64颗ws2812组成8*8的显示阵列,通过 CH ...

  6. Java基础__05.网络编程

    通信协议 即约定网络通信时的一些内容. TCP和UDP对比 TCP:类比打电话 连接稳定 三次握手.四次挥手 客户端.服务端 传输完成.释放连接,效率低 UDP:类比发短信 不连接.不稳定: 客户端. ...

  7. django解决网站CORS前后端跨域问题

    1.安装cors-headers⼯具   pip install django-cors-headers 2.安装cors-headers应⽤ # 注册应用 INSTALLED_APPS = [ 'd ...

  8. oracle之PGA相关的sql

    在上篇文章中初步介绍了关于pga的基础知识,阅读了其他很多关于pga的内容,今天总结一些关于pga的sql和其他知识. 在网上找了相关资料整理而来,可能有点乱,先码上后再整理下. https://bl ...

  9. selenium爬取PDF预览文件

    python selenium 爬取某网站的pdf预览文件,下载图片转换pdf 参考链接:https://blog.csdn.net/weixin_44740756/article/details/1 ...

  10. Echarts实现不均匀刻度的方法,自定义刻度(转)

    原文地址 今天突然有个我们的咨询公司找我问一个echarts问题,这个问题确实值得一解决,很有意思. 问题是这样的.数据中有很多低于100的数值,但是最高值却能达到14000. data = [93. ...