重要
爲了方便大家共同交流學習,我對項目進行了升級,建議大家去我的GitHub去查看,本文章的算法部分還是可以借鑑的。改動具體如下。
1、添加詳細類註釋,概括類的功能。
2、修改了部分變量名稱。
3、 升級項目環境爲Android Studio 3.2,原版本爲2.2,已經淘汰。
1 說在開始
最近模擬了切水果裏面的拖尾效果,其可以應用在許多的場景裏面,例如,武器的刀光,飛機的尾焰效果等等。我開發的這個Demo是基於OpenGL ES的,開發環境(IDE)使用的Android Studio。如果想讓案例使用在其他平臺,還需要借鑑本節的算法自己開發,如果是OpenGL或者OpenGL ES的話,直接就可以使用。(當然也需要適當的修改的)。
2 代碼位置
作者:憨豆酒(YinDou),聯繫我[email protected],熟悉圖形學,圖像處理領域,本章的源代碼可在此倉庫中找到: https://github.com/douysu/person-summary 如果對您有幫助,還請幫忙點一個star。如果大家發現錯誤以及不合理之處,還希望多多指出。
3 運行效果
當然我觸控的方法都是基於Android的觸控寫的。
4 原理部分
其算法與cocos的的MotionStreak類似。
OpenGL ES中有三種基本的圖元,點線和三角形。點主要用在粒子系統,最常用的就是三角形,我們看到的做工精美的3D模型就是很多三角形組成的,三角形的數量也就決定了模型的精細程度,因爲在我們的需求裏用三角形就措措有餘了。
思路大體如下:
其實繪製的就是一個三角形帶。在平常的程序中,經常使用的繪製方式是GL_TRIANGLES,此繪製方式就是以三角形進行繪製。但是這裏使用的繪製方式是GL_TRIANGLE_STRIP,此繪製方式就是使用三角帶形式進行繪製。
其實可以發現,不管這種特效是跟着武器走還是跟着手指滑動走,都是動態改變的,這就說明我們需要動態去更新這個三角帶,不斷的向三角形帶中加入或者移除頂點。
頂點的移除有兩種方式:
- 第一種是每個頂點都有個生存週期,過了生命週期的時間就會被移除
- 第二種是規定一個頂點個數的上限,超過頂點個數就會被移除
4.1 如何生成三角形帶的頂點位置?
需要注意的是,我們不能直接將武器的軌道頂點或者手指滑動的位置直接拿過來使用,是需要去計算三角形帶頂點位置的。
算法如下:
- 取上一個頂點的位置,和當前頂點的位置形成一個二維向量V1,然後得到V1的垂直向量V2,三角形帶是有寬度width的,根據寬度width得到V2方向上的兩個頂點,將得到的頂點存儲起來。
- 當然只有頂點是不夠的,還需要一幅紋理圖和對應紋理座標。紋理座標就很簡單了,t的值不是1就是0,而s的值需要切割成不同的段數。
紋理圖如下
紋理座標如下所示:
當然頂點數據和紋理座標都是需要動態更新的,然後使用OpenGL ES的紋理就可以畫出來了。
5 代碼部分
代碼較長,我就簡單貼一下存儲控制參數的類吧。完整的Demo可以在我github上找到。
/**
* Simple to Introduction
* @Author [憨豆酒 [email protected]]
* @Date [2018-10-18]
* @Description [拖尾參數常量類]
* @version [2.0]
*/
public class StreakDataConstant {
public static Object lock=new Object(); //資源鎖
public static float STREAK_WIDTH=0.06f;//條帶的寬度
public static int STREAK_MAX_NUMBER=30*2;//拖尾的最大長度(必須是2的倍數)
public static int THREAD_DISAPPEAR_TIME=10;//拖尾的消失時間(手指離開後線程休息時間)
public static float[] LINE_COLOR={1.0f,1.0f,0.0f,1.0f};//拖尾的顏色
public static final float MAX_LIFE_SPAN= 1.5f; //最大生命週期
public static final float LIFE_SPAN_STEP= 0.05f;//生命週期步進
public static int SRC_BLEND= GLES30.GL_SRC_ALPHA;//源混合因子
public static int DST_BLEND= GLES30.GL_ONE;//目標混合因子(得到背景全部顏色)
public static int BLEND_FUNC= GLES30.GL_FUNC_ADD;//混合方式
}
註釋中說明的很清楚了就不多介紹了。
頂點着色器部分:
#version 300 es
uniform mat4 uMVPMatrix; //總變換矩陣
uniform float maxLifeSpan;//最大生命期
layout (location = 0) in vec3 aPosition;//頂點位置
in vec2 aTexCoor;//頂點紋理座標
out vec3 vPosition;//傳遞給片元着色器頂點位置和週期
out float sjFactor;//用於傳遞給片元着色器的總衰減因子
out vec2 vTextureCoord;//用於傳遞給片元着色器的變量
void main()
{
gl_Position = uMVPMatrix * vec4(aPosition.xy,0,1); //根據總變換矩陣計算此次繪製此頂點位置
vPosition=aPosition;//x,y,0,週期
sjFactor=aPosition.z/maxLifeSpan;//計算總衰減因子,並將其傳遞給片元着色器
vTextureCoord=aTexCoor;//將紋理座標傳給片元着色器
}
總衰減因子=當前生命週期/最大生命週期。當衰減因子達到極限值的時候,對應片元就會消失,這樣就實現了根據生命週期衰減的目的了。
片元着色器:
#version 300 es
precision mediump float;
uniform sampler2D sTexture;//紋理內容數據
uniform vec4 lineColor;//紋理內容數據
in vec3 vPosition;//接收x,y,0,週期
in float sjFactor;//接收衰減因子
in vec2 vTextureCoord;//用於傳遞給片元着色器的變量
out vec4 fragColor;//輸出到的片元顏色
void main()
{
vec4 stColor=texture(sTexture, vTextureCoord);//採樣出紋理顏色
fragColor=lineColor*sjFactor*stColor.a;//給此片元顏色值(線條顏色*衰減因子*Alpha值)
}
最終片元顏色需要乘以採樣出的紋理顏色的Alpha值,因爲這裏的線條顏色是我自己自定義的,不是從紋理衝採樣出的顏色,紋理只提供“形狀”,這裏需要注意。
6 最後
本人的知識有限,如果本節內容有錯誤和不合理之處,還請朋友們多多指出,我會虛心接受每一個建議。