Esfog_UnityShader教程_逐幀動畫


  有段日子沒出這個系列的新文章了,今天就拿一個比較常見也比較基礎的利用改變Shader來改變不斷調整UV實現播放逐幀動畫的小功能。很久沒寫了就當練練手了。在新版本的Unity中早就已經集成了Sprite2D的功能,而且可以編輯不規則的圖形,不過了解一下它的原理,也是蠻好的!


逐幀動畫


 

  幀動畫大家應該都不陌生,經常會看到把一個動畫幾幀的的狀態按一定順序整合在同一張圖片上,如下圖:

      

 

  從上圖中我們可以看出,這個圖片動畫一共有20幀,從左到右,從上到下依次排布(基本上都是這個規律).為了展示效果我們需要一個平面來作為模型,Unity里面自帶的Panel就可以,不過最終的效果很可能是這個動畫是倒着的,因為不清楚Unity自帶Panel的頂點UV情況,只能是大家自己手動轉一轉調到合適的角度觀察(如果你會建模的話自己做個平面導到Unity吧)。我們都知道一個貼圖具體如何被映射到模型的表面是根據模型定點的UV值來決定的。假設不修改的話,一個平面的左下角的UV是(0,0),右上角是(1,1),那么你直接把這個貼圖貼上去的話看到的效果就和上圖一樣,這顯然不是我們想要的。在之前的教程中我們都是直接用模型自帶的UV,UV是多少就按照它去到貼圖上取對應顏色,這次我們就需要修改了,比如頂點的UV是(1,1)我們也不去貼圖的(1,1)位置取。而是根據時間計算出當前播放到第幾幀然后算出貼圖上相應Sprite(也叫精靈,就是圖片上一塊一塊具體的小圖素)的UV位置.按照這個位置取貼圖中取出對應顏色即可。千萬不要以為模型頂點本身的UV信息變了,其實只是我們把他傳遞給我們的UV信息加工了一下獲取我們想要的位置上的像素。

 

原理就是這樣的,下面我們進行實例代碼的分析:

 1 Shader "Esfog/SpriteUV" 
 2 {  3  Properties  4  {  5         _SpriteTex ("SpriteTexture (RGB)", 2D) = "white" {}  6         _SpriteRowCount ("RowCounts",float) = 0
 7         _SpriteColumnCount ("ColumnCounts",float) = 0
 8         _Speed ("AnimationSpeed",Range(0.01,10)) = 4
 9  } 10  SubShader 11  { 12  Pass 13  { 14             Tags { "RenderType"="Opaque" } 15             
16  CGPROGRAM 17             #pragma vertex vert
18             #pragma fragment frag
19             #include "UnityCG.cginc"
20             
21  uniform sampler2D _SpriteTex; 22             uniform float _SpriteRowCount; 23             uniform float _SpriteColumnCount; 24             uniform float _Speed; 25             
26             struct VertexOutput 27  { 28  float4 pos:SV_POSITION; 29  float2 uv:TEXCOORD0; 30  }; 31 
32  VertexOutput vert(appdata_base input) 33  { 34  VertexOutput o; 35                 o.pos = mul(UNITY_MATRIX_MVP,input.vertex); 36                 o.uv = input.texcoord.xy; 37                 return o; 38  } 39             
40  float4 frag(VertexOutput input):COLOR 41  { 42                 float totalSpriteCount = _SpriteRowCount * _SpriteColumnCount; 43                 float rowAvgPercent = 1 / _SpriteColumnCount; 44                 float columnAvgPercent = 1 / _SpriteRowCount; 45                 float SpriteIndex = fmod(_Time.y * _Speed,totalSpriteCount); 46                 SpriteIndex = floor(SpriteIndex); 47                 float columnIndex = fmod(SpriteIndex,_SpriteColumnCount); 48                 float rowIndex = SpriteIndex / _SpriteColumnCount; 49                 rowIndex = _SpriteRowCount - 1 - floor(rowIndex); 50                 float2 spriteUV = input.uv; 51                 spriteUV.x = (spriteUV.x + columnIndex) * rowAvgPercent; 52                 spriteUV.y = (spriteUV.y + rowIndex) * columnAvgPercent; 53                 float4 col = tex2D(_SpriteTex,spriteUV); 54                 return col; 55  } 56             
57  ENDCG 58  } 59  } 60     FallBack "Diffuse"
61 }

  和之前一樣,有一部分內容在之前的教程中已經提過了,不再贅述。

    第5~8行,在Shader的屬性部分,我們定義了用來存放動畫圖片的_SpriteTex.用來存放這個動畫圖片上一共有幾行圖素的_SpriteRowCount.存放圖片上一共有幾列圖素的_SpriteColumnCount.以及控制動畫播放速度的_Speed.其中_Speed應用了一種新的屬性類型Range(x,y).這個屬性會在材質球的Inspector面板上創建一共Slider滑塊,他會返回一共滑塊所處位置的float值.最左端為x,最右端為y.用戶可以直接拖動,十分方便。

  第42~44行,大家從代碼可以看到VertexOutput和vert函數都很簡單,因為我們的內容都在frag函數里呢,totalSpriteCount用來存放動畫圖片上精靈圖素的總數量(一共有多少幀).rowAvgPercent用來存放在一行中一個精靈圖素在橫向上占得比例,因為一張圖片的左下角是uv的(0,0),右上角是(1,1)。所以也可以認為是橫向上每一個圖素占得uv比例。從現在開始大家要注意代碼中的row和column.有時候是代表橫縱有的有時候卻是代表縱橫.不要被我搞暈了.我也覺得很別扭啊。這里的_SpriteColumnCount是我們之前定義的一共有幾列,所以1/_SpriteColumnCount就是我們要的一行中每個圖素占得比例了。columnAvgPercent同理。

  第45~46行,因為我們是逐幀動畫,就是一幀一幀的播放,那么必須記錄當前播放在第幾幀了,SpriteIndex就是用來干這個的。后面有個看上去比較陌生的表達式,其中_Time是一個Unity為我么提供的float4變量,_Time.y分量代表的就是游戲開始以后流逝的時間(s).關於_Time的更多解釋可以查看unity的幫助文檔中關於Shader內置變量的內容。而后面乘以一個_Speed是我們用來調節播放速度的,如果_Speed = 1那么我們就是1秒1楨的速度播放動畫,如果_Speed = 2就是1秒2幀的速度播放。而整個fmod(_Time.y * _Speed,totalSpriteCount)其中fmod(x,y)是x對y取余。因為我們的動畫是循環播放的,而且序號是從0開始計算的,所以整個取余操作可以根據當前的時間和播放速度來確定播放到第幾幀了,並且是循環播放。因為是兩個浮點數取余,余數可能含有小數位。而我們具體播放到第幾幀是不可能為小數的.所以我們用floor函數對結果進行下取整,為什么不用上取整呢,大家自己思考一下吧。

  第47~49行,這一步中,我們要去根據播放到第幾幀去計算出相應圖素在整個圖素矩陣中的第幾列第幾行。這個我想大家上大學的時候一定見過很多了。其中SpriteIndex / _SpriteColumnCount用當前第幾幀除以總列數就可以得到當前播放的圖素處在第幾行(從0開始算).而fmod(SpriteIndex,_SpriteColumnCount)用當前幀數對總列數取余得到當前圖素處在第幾列,為什么這次我們不需要對fmod結果進行取余呢,大家再思考一下吧。最后我們又對得到的rowIndex先進行了下取整,然后又用(總行數-1)去減去他得到結果,其中取整是因為第48行中的rowIndex是由兩個浮點數相除而來,結果可能包含小數位。而用(_SpriteRowCount-1)去減它,是因為圖片的UV原點是在左下角的那么我們算出來的第幾行實際上是從下往上數的,這顯然與我們期望的相反,而又由於行數是從0開始算的所以我們用(總行數-1)減去原來的rowIndex就能得到我們想要的圖素在第幾行了(從下往上數)。

  第50行~53行,創建一個spriteUV並賦予原始的uv信息,然后我們開始對整個原始uv進行加工了,先對uv信息的x方向值進行加工,(spriteUV.x + columnIndex) * rowAvgPercent可以拆除spriteUV.x*rowAvgPercent + columnIndex*rowAvgPercent.加號左邊是相當於我們把原來的uv.x(0~1)縮放到(0~rowAvgPercent),也就是一行圖素中第一個圖素所占的橫向上的uv比例。實際上現在這個位置就相當於每一行中的第一個圖素,而加號右面的值實際上是我們相對於把剛才計算出來的uv當做一個基地址然后加上這個偏移,一個圖素在橫向上占rowAvgPercent這么多的uv比例,那么他處在第columnIndex列所以就是說他相對於加號左面的基礎位置偏移了(columnIndex*rowAvgPercent)這么多的uv比例。可能比較繞,大家好好想一想就明白了。后面spriteUV.y同理。最后用我們加工后的spriteUV作為新的UV值去貼圖中取出相應的顏色返回就可以了。

  

  (~ o ~)~系列教程的第六篇到此結束了,這篇的內容比較基礎,就是解釋起來比較繞,我也有段時間沒寫了,有些生疏了。還望大家見諒。日后會再接再厲,多多分享。現在都凌晨一點了,明天還要上班,希望大家有所收獲吧。由於這個效果是動態的為了展示效果本人能力不足,只能用截圖軟件一幀一幀截屏然后合成成gif展示一下了.由於截圖大小參差不齊,所以播放起來晃來晃去的。大家將就着看吧。洗洗睡了~~

  

 

  尊重他人智慧成果,歡迎轉載,請注明作者esfog,原文地址 http://www.cnblogs.com/Esfog/p/4088597.html 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM