精品欧美一区二区三区在线观看 _久久久久国色av免费观看性色_国产精品久久在线观看_亚洲第一综合网站_91精品又粗又猛又爽_小泽玛利亚一区二区免费_91亚洲精品国偷拍自产在线观看 _久久精品视频在线播放_美女精品久久久_欧美日韩国产成人在线

移動應用中使用OpenGL生成轉場特效

移動開發
本議題主要包含了對OpenGL的簡單介紹及相關API使用,GLSL著色器語言的基本使用,以及如何通過編寫自定義的著色器程序來實現圖片的轉場效果。

作者 | jzg,攜程資深前端開發工程師,專注Android開發;zcc,攜程高級前端開發工程師,專注iOS開發。

一、前言

隨著移動端短視頻的火熱,音視頻編輯工具在做內容類APP上的地位舉足輕重。豐富的轉場方式可以給短視頻帶來更多炫酷的效果,從而更好地贏得用戶青睞。本議題主要包含了對OpenGL的簡單介紹及相關API使用,GLSL著色器語言的基本使用,以及如何通過編寫自定義的著色器程序來實現圖片的轉場效果。

二、為什么使用OpenGL以及使用的難點

視頻的轉場效果離不開圖形的處理,移動設備在處理3D圖形相關的計算時一般都會選擇使用GPU。相較于CPU,GPU在圖像動畫處理時具有更高效的性能。移動設備以android為例,GPU處理提供了兩套不同的API,分別是Vulkan和OpenGL ES。其中VulKan只支持 Android 7.0 以上的設備,OpenGL ES 則支持所有的 Android 版本,而iOS并沒有對vulkan的官方支持。同時 OpenGL ES 作為 OpenGL 的子集,針對手機、PDA 和游戲主機等嵌入式設備去除了 glBegin/glEnd,四邊形、多邊形等復雜圖元等許多非絕對必要的特性,消除它的冗余功能,從而提供了更容易學習和易于在移動圖形硬件中實現的庫。

目前,在短視頻圖像處理中, OpenGL ES 憑借良好的系統支持性和功能的高度精簡性,成為了最廣泛的 GPU 處理 API 之一。為了方便,本文中提到的 OpenGL 即表示 OpenGL ES。

使用OpenGL處理視頻轉場的難點是如何編寫轉場效果的著色器,關于這一點,我們可以參考開源的GLTransitions網站。該網站有很多開源的轉場效果,我們可以借鑒并學習,下文會有較為詳細的介紹。

三、OpenGL的基本介紹和轉場應用

OpenGL 是一種開放式的圖形庫,用于渲染2D、3D矢量圖形的跨語言,跨平臺的應用程序編程接口。OpenGL 可以?來做什么?

  • 視頻,圖形,圖?處理
  • 2D/3D 游戲引擎開發
  • 科學可視化
  • 醫學軟件開發
  • CAD(計算機輔助技術)
  • 虛擬實境(AR,VR)
  • AI ??智能

我們使用OpenGL來處理視頻轉場,就是上面提到的用OpenGL來對視頻、圖形、圖片進行處理。

在使用OpenGL進行繪制時,我們主要關注的是頂點著色器和片元著色器。頂點著色器用來確定繪制圖形的頂點位置,片元著色器負責給圖形添加顏色。主要繪制流程如下圖:

圖片

渲染的流程有以下幾步:

1)頂點數據的輸入:

頂點數據用來為后面的頂點著色器等階段提供處理的數據。

2)頂點著色器:

頂點著色器主要功能是進行坐標變換。

3)幾何著色器:

與頂點著色器不同,幾何著色器的輸入是完整的圖元(比如,點),輸出可以是一個或多個其他的圖元(比如,三角面),或者不輸出任何的圖元,幾何著色器是可選的。

4)圖元組裝、光柵化:

圖元組裝將輸入的頂點組裝成指定的圖元,經過圖元組裝以及屏幕映射階段后,我們將物體坐標變換到了窗口坐標,光柵化是個離散化的過程,將3D連續的物體轉化為離散屏幕像素點的過程。

5)片元著色器(片段著色器):

片元著色器用來決定屏幕上像素的最終顏色。

6)混合測試:

渲染的最后一個階段是測試混合階段。測試包括裁切測試、Alpha測試、模板測試和深度測試。沒有經過測試的片段會被丟棄,不需要進行混合階段,經過測試的片段會進入混合階段。

經過以上幾個步驟,OpenGL就能將最終的圖形顯示到屏幕上。

在OpenGL繪制流程中,我們能夠編碼的就是Vertex Shader(頂點著色器) 和 Fragment Shader(片元著色器)。這也是渲染過程中必備的2個著色器。

Vertex Shader處理從客戶端輸入的數據、應用變換、進行其他的類型的數學運算來計算光照效果、位移、顏色值等。比如為了渲染共有3個頂點的三角形,Vertex Shader將執行3次,也就是為了每個頂點執行一次。

圖中的3個頂點已經組合在一起,而三角形也已經逐個片段的進行了光柵化。每個片段通過執行Fragment Shader進行填充。Fragment Shader會輸出我們屏幕上看到的最終顏色值。

在繪制圖形的時候,我們會使用到OpenGL的多種狀態變量,例如當前的顏色,控制當前視圖和投影變換、直線和多邊形點畫模式、多邊形繪圖模式、像素包裝約定、光照的位置和特征以及被繪制物體的材料屬性等。可以設置它的各種狀態(或模式),然后讓這些狀態一直生效,直到再次修改它們。

以把當前顏色設置為白色、紅色或其他任何顏色,在此之后繪制的所有物體都將使用這種顏色,直到再次把當前顏色設置為其他顏色。許多表示模式的狀態變量可以用glEnable()和glDisable()。所以我們說OpenGL是一個狀態機。

因為OpenGL在渲染處理過程中會順序執行一系列操作,就如流水線作業一樣,所以我們將OpenGL繪制的流程稱為渲染管線,包括固定管線和可編程管線。我們使用的是可編程管線,在可編程管線里,頂點的位置、顏色、貼圖座標、貼圖傳進來之后,如何對數據進行改動,產生的片元如何生成結果,可以很自由地控制。

下面就簡單介紹一下管線和在可變編程管線中必不可少的GLSL(著色器語言)。

管線:渲染管線可以理解為渲染流水線。指的是輸入需要渲染的3D物體的相關描述信息數據(例:頂點坐標、頂點顏色、頂點紋理等),經過渲染管線一系列的變化和渲染過程,輸出一幀最終的圖像。簡單理解就是一堆原始圖形數據經過一個輸送管道,期間經過各種變化處理最終出現展示到屏幕的過程。管線又分為固定管線和可編程管線兩種。

固定管線:在渲染圖像的過程,我們只能通過調用GLShaderManager類的固定管線效果實現一系列的著色器處理。

可編程管線:在渲染圖像的過程,我們能夠使用自定義頂點著色器和片元著色器的去處理數據的過程。由于OpenGL的使用場景非常豐富,固定管線或者存儲著色器無法完成的任務,這時我們可以使用可編程管線去處理。

OpenGL著色語言(OpenGL Shading Language)是用來在OpenGL中著色編碼的語言,也即開發人員寫的短小的自定義程序,他們是在GPU(Graphic Processor Unit圖形處理單元)上執行的,代替了固定的渲染管線的一部分,使渲染管線中不同層次具有可編程性。它可以得到當前OpenGL 中的狀態,GLSL內置變量進行傳遞。GLSL其使用C語言作為基礎高階著色語言,避免了使用匯編語言或硬件規格語言的復雜性。

GLSL的著色器代碼分成2個部分:VertexShader(頂點著色器) 和 Fragment Shader(片元著色器)。

著色器(Shader)是用來實現圖像渲染的,用來替代固定渲染管線的可編輯程序。其中Vertex Shader(頂點著色器)主要負責頂點的幾何關系等的運算,Pixel Shader(像素著色器)主要負責片源顏色等的計算。

頂點著色器是一個可編程的處理單元,一般用來處理圖形每個頂點變換(旋轉/平移/投影等)、光照、材質的應用與計算等頂點的相關操作。頂點著色器是逐頂點運算的程序,每個頂點數據都會執行一次。替代了原有固定管線的頂點變換、光照計算,采用GLSL進行開發 。我們可以根據自己的需求采用著色語言自行開發頂點變換、光照等功能,大大增加了程序的靈活性。

頂點著色器工作過程為將原始的頂點幾何信息(頂點坐標、顏色、紋理)及其他屬性傳送到頂點著色器中,經過自定義的頂點著色程序處理產生變化后的頂點位置信息,將變化后的頂點位置信息傳遞給后續圖元裝配階段,對應的頂點紋理、顏色等信息則經光柵化后傳遞到片元著色器。

頂點著色器的輸入主要為待處理頂點相應的attribute、uniform、采樣器以及臨時變量,輸出主要為經過頂點著色器后生成的varying及一些內建輸出變量。

頂點著色器示例代碼:

//頂點位置
attribute vec4 Position;
//紋理坐標
attribute vec2 TextureCoord;
//紋理坐標 用于接收和傳遞給片元著色器的紋理坐標
varying vec2 varyTextureCoord;
void main() {
gl_Position = Position;
varyTextureCoord = TextureCoord;
}
//高精度
precision highp float;
//用于接收頂點著色器的紋理坐標
varying vec2 varyTextureCoord;
//圖片紋理
uniform sampler2D Texture;
//圖片紋理
uniform sampler2D Texture2;
const vec2 direction = vec2(0.0, 1.0);
void main(){
vec2 p = varyTextureCoord.xy/vec2(1.0).xy;
vec4 color = mix(texture2D(Texture, varyTextureCoord), texture2D(Texture2, varyTextureCoord), step(1.0-p.y,progress));
gl_FragColor = vec4(color);
}

3.1.4 三種向OpenGL著?器傳遞數據的?法

上面的頂點著色器和片元著色器里出現了attribute,varying,uniform等類型定義,下面就簡單介紹一下這三種類型。

attribute

attribute:attribute變量是只能在頂點著色器中使用的變量,一般用attribute變量來表示一些頂點的數據,如:頂點坐標,法線,紋理坐標,頂點顏色等。

uniform

uniform:uniform變量是外部application程序傳遞給著色器的變量,uniform變量就像是C語言里面的常量,也就是說著色器只能用而不能修改uniform變量。

varying

varying:從頂點著色器傳遞到片元著色器的量,如用于傳遞到片元著色器中的頂點顏色,可以使用varying(易變變量)。

注意點: Attributes不能夠直接傳遞給Fragment Shader,如果需要傳遞給Fragment Shader,則需要通過Vertex Shader間接的傳遞過去。而 Unifrom和Texture Data可以直接傳遞給Vertex Shader和Fragment Shader,具體怎么傳遞,依需求而定。

3.1.5 如何使用OpenGL來繪制一張圖片

上面介紹了頂點著色器和片元著色器,以及如何向OpenGL程序傳遞數據的方法。

現在我們就利用剛剛介紹的一些知識點,通過OpenGL程序將圖片繪制到屏幕上,這也是制作圖片輪播轉場特效的前提。圖片的繪制對于OpenGL來說就是紋理的繪制,這里只為了展示效果,不使用變換矩陣來處理圖片的寬高比例,直接鋪滿整個窗口。

首先定義一個頂點著色器:

attribute vec4 a_position;//傳入的頂點坐標
attribute vec2 a_texCoord;//傳入的紋理坐標
varying vec2 v_texCoord;//傳遞給片元著色器的紋理坐標
void main()
{
gl_Position = a_position;//將頂點坐標賦值給OpenGL的內置變量
v_texCoord = a_texCoord;//將傳入的紋理坐標傳遞給片元著色器
}
再定義一個片元著色器:
precision mediump float;//定義float精度,紋理坐標使用的是一個float類型的二維向量vec2
uniform sampler2D u_texture;//紋理
varying vec2 v_texCoord;//紋理坐標
void main(){
gl_FragColor = texture2D(u_texture, v_texCoord);//2D紋理采樣,將顏色賦值給OpenGL的內置變量gl_FragColor
}

再給出Android端使用這兩個著色器繪制一個圖片紋理的代碼:

class SimpleImageRender(private val context: Context) : GLSurfaceView.Renderer {
//頂點坐標
private val vCoordinates = floatArrayOf(
-1.0f, -1.0f,
1.0f, -1.0f,
-1.0f, 1.0f,
1.0f, 1.0f
)
//紋理坐標
private val textureCoordinates = floatArrayOf(
0.0f, 1.0f,
1.0f, 1.0f,
0.0f, 0.0f,
1.0f, 0.0f
)
//OpenGL程序id
var programId = 0
//頂點坐標句柄
var vCoordinateHandle = 0
//紋理坐標句柄
var textureCoordinateHandle = 0
//紋理id
var textureId = 0
private val vertexBuffer =
ByteBuffer.allocateDirect(vCoordinates.size * 4).order(ByteOrder.nativeOrder())
.asFloatBuffer()
.put(vCoordinates)


private val textureBuffer =
ByteBuffer.allocateDirect(textureCoordinates.size * 4).order(ByteOrder.nativeOrder())
.asFloatBuffer()
.put(textureCoordinates)


override fun onSurfaceCreated(gl: GL10?, config: EGLConfig?) {
vertexBuffer.position(0)
textureBuffer.position(0)
GLES20.glClearColor(0.0f, 0.0f, 0.0f, 1.0f)
GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT)
//根據頂點著色器和片元著色器編輯鏈接OpenGL程序
programId =
loadShaderWithResource(context, R.raw.simple_image_vs, R.raw.simple_image_fs)
//獲取頂點坐標的句柄
vCoordinateHandle = GLES20.glGetAttribLocation(programId, "a_position")
//獲取紋理坐標的句柄
textureCoordinateHandle = GLES20.glGetAttribLocation(programId, "a_texCoord")
//生成紋理
val textureIds = IntArray(1)
GLES20.glGenTextures(1, textureIds, 0)
if (textureIds[0] == 0) {
return
}
textureId = textureIds[0]
//綁定紋理
GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, textureId)
//環繞方式
GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_S, GLES20.GL_REPEAT)
GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_T, GLES20.GL_REPEAT)
//過濾方式
GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MIN_FILTER, GLES20.GL_LINEAR)
GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MAG_FILTER, GLES20.GL_LINEAR)


val bitmap = BitmapFactory.decodeResource(context.resources, R.drawable.scene1)
GLUtils.texImage2D(GLES20.GL_TEXTURE_2D, 0, bitmap, 0)
bitmap.recycle()
}


override fun onSurfaceChanged(gl: GL10?, width: Int, height: Int) {
GLES20.glViewport(0, 0, width, height)
}


override fun onDrawFrame(gl: GL10?) {
//清屏,清理掉顏色的緩沖區
GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT)
//設置清屏的顏色,這里是float顏色的取值范圍的[0,1]
GLES20.glClearColor(0.0f, 0.0f, 0.0f, 1.0f)


//使用program
GLES20.glUseProgram(programId)


//設置為可用的狀態
GLES20.glEnableVertexAttribArray(vCoordinateHandle)
//size 指定每個頂點屬性的組件數量。必須為1、2、3或者4。初始值為4。(如position是由3個(x,y,z)組成,而顏色是4個(r,g,b,a))
//stride 指定連續頂點屬性之間的偏移量。如果為0,那么頂點屬性會被理解為:它們是緊密排列在一起的。初始值為0。
//size 2 代表(x,y),stride 8 代表跨度 (2個點為一組,2個float有8個字節)
GLES20.glVertexAttribPointer(vCoordinateHandle, 2, GLES20.GL_FLOAT, false, 8, vertexBuffer)


GLES20.glEnableVertexAttribArray(textureCoordinateHandle)
GLES20.glVertexAttribPointer(
textureCoordinateHandle,
2,
GLES20.GL_FLOAT,
false,
8,
textureBuffer
)


GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, 0, 4)


}
}

這樣就完成了一個圖片的繪制:

3.2 OpenGL的轉場特效應用

3.2.1 移植開源的轉場效果

什么是轉場效果?一般來說,就是兩個視頻畫面之間的過渡銜接效果。在opengl中,圖片的轉場,其實就是兩個紋理的過渡切換。在這里推薦一個開源項目,該項目主要用來收集各種GL轉場特效及其 GLSL 實現代碼,開發者可以很方便地移植到自己的項目中。

GLTransitions 項目有接近大概70種轉場特效,能夠非常方便的使用在圖片或者視頻的轉場中,很多轉場特效包含了混合、邊緣檢測、腐蝕膨脹等常見的圖像處理方法,由易到難。

對于想學習 GLSL 的同學,既能快速上手,又能學習到一些高階圖像處理方法 GLSL 實現,強烈推薦。

由于glsl代碼在各個平臺都是通用的,所以將GLTransitions的效果移植到移動端也是比較簡單的?,F在我們以該網站的第一個轉場效果為例,介紹一下移植的大致流程。

首先我們來看一下轉場所需的片元著色器的代碼,這是實現轉場的關鍵。其中sign函數,mix函數,fract函數,step函數是glsl的內置函數。這里只為了展示效果,不使用變換矩陣來處理圖片的寬高比例,直接鋪滿整個窗口。

uniform vec2 direction; // = vec2(0.0, 1.0)


vec4 transition (vec2 uv) {
vec2 p = uv + progress * sign(direction);
vec2 f = fract(p);
return mix(
getToColor(f),
getFromColor(f),
step(0.0, p.y) * step(p.y, 1.0) * step(0.0, p.x) * step(p.x, 1.0)
);
}

我們可以看到,從GLTransitions的片元著色器代碼已經提供了轉場效果,但是還需要使用者進行一些修改。以上面的代碼為例,需要我們自己定義一個轉場進度的變量progress(取值為0到1的浮點數)。還有轉場最基本的兩個要素,即圖片紋理,一個轉場需要兩個圖片紋理,從紋理1過渡到紋理2,getToColor和getFromColor就是對紋理1和紋理2取色的函數。當然還有必不可少的main函數,將我們程序計算的顏色賦值給gl_FragColor,所以我們要將上面的片元著色器代碼修改一下。如下:

precision mediump float;
uniform vec2 direction;// = vec2(0.0, 1.0)
uniform float progress;//轉場的進度
uniform sampler2D u_texture0;//紋理1
uniform sampler2D u_texture1;//紋理2
varying vec2 v_texCoord;//紋理坐標
vec4 transition (vec2 uv) {
vec2 p = uv + progress * sign(direction);
vec2 f = fract(p);
return mix(
texture2D(u_texture1, f),
texture2D(u_texture0, f),
step(0.0, p.y) * step(p.y, 1.0) * step(0.0, p.x) * step(p.x, 1.0)
);
}


void main(){
gl_FragColor = transition(v_texCoord);
}

這里也順便給出頂點著色器的代碼,主要就是設置頂點坐標和紋理坐標,關于這兩個坐標上文已經介紹過了,這里就不贅述了。代碼如下:

attribute vec4 a_position;
attribute vec2 a_texCoord;
varying vec2 v_texCoord;
void main()
{
gl_Position = a_position;
v_texCoord = a_texCoord;
}

現在頂點著色器和片元著色器這兩個關鍵的著色器程序都有了,一個基本的轉場就實現了。只要在我們的程序中使用這兩個著色器,在繪制的時候根據當前的幀數不停地更新兩個紋理和轉場的進度就可以了。

下面給出繪制時的代碼邏輯,以安卓為例:       

frameIndex++ 
GLES20.glUseProgram(programId)


GLES20.glEnableVertexAttribArray(vCoordinateHandle)
GLES20.glVertexAttribPointer(vCoordinateHandle, 2, GLES20.GL_FLOAT, false, 8, vertexBuffer)


GLES20.glEnableVertexAttribArray(textureCoordinateHandle)
GLES20.glVertexAttribPointer(
textureCoordinateHandle,
2,
GLES20.GL_FLOAT,
false,
8,
textureBuffer
)


val uTexture0Handle = GLES20.glGetUniformLocation(programId, "u_texture0")
GLES20.glActiveTexture(GLES20.GL_TEXTURE0)
GLES20.glBindTexture(
GLES20.GL_TEXTURE_2D,
imageTextureIds[(frameIndex / transitionFrameCount) % imageNum]
)
GLES20.glUniform1i(uTexture0Handle, 0)


val uTexture1Handle = GLES20.glGetUniformLocation(programId, "u_texture1")
GLES20.glActiveTexture(GLES20.GL_TEXTURE1)
GLES20.glBindTexture(
GLES20.GL_TEXTURE_2D,
imageTextureIds[(frameIndex / transitionFrameCount + 1) % imageNum]
)
GLES20.glUniform1i(uTexture1Handle, 1)


val directionHandle = GLES20.glGetUniformLocation(programId, "direction")
GLES20.glUniform2f(directionHandle, 0f, 1f)


val uOffsetHandle = GLES20.glGetUniformLocation(programId, "u_offset")
val offset = (frameIndex % transitionFrameCount) * 1f / transitionFrameCount
GLES20.glUniform1f(uOffsetHandle, offset)
GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, 0, 4)

以上就是將一個GLTransitions網站中的轉場特效移植到Android端的基本流程。iOS的也是類似的,非常方便。

3.2.2 實現復雜轉場效果

通過上面的介紹,我們已經對如何使用opengl來處理圖片轉場有了一個簡單的了解。但是剛剛的操作只能讓多張圖片都使用同一種轉場,這樣比較單調乏味。下面介紹一個思路,在用多張圖片合成轉場效果時,將不同的轉場效果組合起來使用。

回想一下,剛剛做轉場移植的時候,只是使用了一個opengl程序?,F在咱們來加載多個opengl程序,然后在不同的時間段使用對應的opengl程序,這樣就能比較方便地實現多個轉場效果的組合使用了。

首先定義一個IDrawer接口,表示一個使用opengl程序的對象:

interface IDrawer {
//準備階段,準備程序,資源
fun onPrepare()
//繪制
fun onDraw(frameIndex:Int){}


fun onSurfaceChanged(p0: GL10?, width: Int, height: Int){


}
}

然后定義一個render,來控制如何使用這些IDrawer:

class ComposeRender : GLSurfaceView.Renderer {
private var frameIndex = 0//當前繪制了多少幀
private var drawersFrames = 0 //所有的drawer繪制一遍需要的幀數,目前每一個drawer占用200幀
private val framesPerDrawer = 200//每一個IDrawer繪制所需要的幀數,這里暫時固定為200


//使用的IDrawer集合
private val drawers = mutableListOf(
HelloWorldTransitionDrawer(),
SimpleTransitionDrawer(),
PerlinTransitionDrawer(),
)


init {
drawersFrames = drawers.size.times(framesPerDrawer)
}


override fun onSurfaceCreated(p0: GL10?, p1: EGLConfig?) {
//設置清屏的顏色,這里是float顏色的取值范圍的[0,1]
GLES20.glClearColor(0.0f, 0.0f, 0.0f, 1.0f)
//清屏,清理掉顏色的緩沖區
GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT)
drawers.forEach {
it.onPrepare()
}
}


override fun onSurfaceChanged(p0: GL10?, p1: Int, p2: Int) {
GLES20.glViewport(0, 0, p1, p2)
drawers.forEach {
it.onSurfaceChanged(p0, p1, p2)
}
}


override fun onDrawFrame(p0: GL10?) {
frameIndex++
//清屏,清理掉顏色的緩沖區
GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT)
val offset = frameIndex % drawersFrames
val logicFrame = if (offset == 0) 1 else offset
//計算當前的幀數輪到哪個IDrawer的繪制,讓對應的IDrawer進行繪制
drawers.forEachIndexed { index, iDrawer ->
if (logicFrame <= (index + 1).times(framesPerDrawer) && logicFrame >= index.times(
framesPerDrawer
)
) {
iDrawer.onDraw(logicFrame - index.times(framesPerDrawer))
}
}
}
}

這里為了方便展示流程,先將紋理和每個轉場的耗時(即使用的幀數)的使用固定值寫在代碼里。比如現在有四張圖片編號為1,2,3,4,我們就定義三個IDrawer A,B,C。A使用圖片1和圖片2,B使用圖片2和圖片3,C使用圖片3和圖片4,然后每個轉場都耗時200幀,這樣就能實現三個opengl程序的組合轉場了。

下面給出其中一個IDrawer的實現類:

class HelloWorldTransitionDrawer() : IDrawer {
private val imageNum = 2//需要使用兩個圖片紋理


//轉場需要耗費的幀數,這里固定寫200幀
private val transitionFrameCount = 200
private val vCoordinates = floatArrayOf(
-1.0f, -1.0f,
1.0f, -1.0f,
-1.0f, 1.0f,
1.0f, 1.0f
)
private val textureCoordinates = floatArrayOf(
0.0f, 1.0f,
1.0f, 1.0f,
0.0f, 0.0f,
1.0f, 0.0f
)
var programId = 0
var vCoordinateHandle = 0
var textureCoordinateHandle = 0
var imageTextureIds = IntArray(imageNum)
private val vertexBuffer =
ByteBuffer.allocateDirect(vCoordinates.size * 4).order(ByteOrder.nativeOrder())
.asFloatBuffer()
.put(vCoordinates).position(0)


private val textureBuffer =
ByteBuffer.allocateDirect(textureCoordinates.size * 4).order(ByteOrder.nativeOrder())
.asFloatBuffer()
.put(textureCoordinates).position(0)


override fun onPrepare() {
GLES20.glClearColor(0.0f, 0.0f, 0.0f, 1.0f)
GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT)
programId =
loadShaderWithResource(
MyApplication.getApp(),
R.raw.helloworld_transition_vs,
R.raw.helloworld_transition_fs
)
vCoordinateHandle = GLES20.glGetAttribLocation(programId, "a_position")
textureCoordinateHandle = GLES20.glGetAttribLocation(programId, "a_texCoord")
//生成紋理
val textureIds = IntArray(1)
GLES20.glGenTextures(1, textureIds, 0)
if (textureIds[0] == 0) {
return
}
loadTextures(intArrayOf(R.drawable.scene1, R.drawable.scene2))
}


override fun onDraw(frameIndex:Int) {
//使用program
GLES20.glUseProgram(programId)


//設置為可用的狀態
GLES20.glEnableVertexAttribArray(vCoordinateHandle)
//size 指定每個頂點屬性的組件數量。必須為1、2、3或者4。初始值為4。(如position是由3個(x,y,z)組成,而顏色是4個(r,g,b,a))
//stride 指定連續頂點屬性之間的偏移量。如果為0,那么頂點屬性會被理解為:它們是緊密排列在一起的。初始值為0。
//size 2 代表(x,y),stride 8 代表跨度 (2個點為一組,2個float有8個字節)
GLES20.glVertexAttribPointer(vCoordinateHandle, 2, GLES20.GL_FLOAT, false, 8, vertexBuffer)


GLES20.glEnableVertexAttribArray(textureCoordinateHandle)
GLES20.glVertexAttribPointer(
textureCoordinateHandle,
2,
GLES20.GL_FLOAT,
false,
8,
textureBuffer
)


val uTexture0Handle = GLES20.glGetUniformLocation(programId, "u_texture0")
GLES20.glActiveTexture(GLES20.GL_TEXTURE0)
GLES20.glBindTexture(
GLES20.GL_TEXTURE_2D,
imageTextureIds[0]
)
GLES20.glUniform1i(uTexture0Handle, 0)


val uTexture1Handle = GLES20.glGetUniformLocation(programId, "u_texture1")
GLES20.glActiveTexture(GLES20.GL_TEXTURE1)
GLES20.glBindTexture(
GLES20.GL_TEXTURE_2D,
imageTextureIds[1]
)
GLES20.glUniform1i(uTexture1Handle, 1)


val directionHandle = GLES20.glGetUniformLocation(programId, "direction")
GLES20.glUniform2f(directionHandle, 0f, 1f)


val uOffsetHandle = GLES20.glGetUniformLocation(programId, "u_offset")
val offset = (frameIndex % transitionFrameCount) * 1f / transitionFrameCount
GLES20.glUniform1f(uOffsetHandle, offset)
GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, 0, 4)
}


private fun loadTextures(resIds: IntArray) {
if (resIds.isEmpty()) return
//直接生成兩個紋理
GLES20.glGenTextures(2, imageTextureIds, 0)
resIds.forEachIndexed { index, resId ->
if (imageTextureIds.indexOfFirst {
it == 0


} == 0) return
GLES20.glActiveTexture(GLES20.GL_TEXTURE0 + index)
//綁定紋理
GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, imageTextureIds[index])
//環繞方式
GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_S, GLES20.GL_REPEAT)
GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_T, GLES20.GL_REPEAT)
//過濾方式
GLES20.glTexParameteri(
GLES20.GL_TEXTURE_2D,
GLES20.GL_TEXTURE_MIN_FILTER,
GLES20.GL_LINEAR
)
GLES20.glTexParameteri(
GLES20.GL_TEXTURE_2D,
GLES20.GL_TEXTURE_MAG_FILTER,
GLES20.GL_LINEAR
)


val bitmap = BitmapFactory.decodeResource(MyApplication.getApp().resources, resId)
GLUtils.texImage2D(GLES20.GL_TEXTURE_2D, 0, bitmap, 0)
bitmap.recycle()
}
}
}

這樣就可以達到將多個轉場組合使用的目的。

四、總結

在移動端進行圖形處理時,OpenGL憑借其效率高,兼容性好的優勢,得到了大家的青睞。

本文對OpenGL的基本概念和繪制流程進行了簡單介紹,讓大家對OpenGL的繪制流程有了一個初步的認識。在繪制流程中,對我們開發者比較重要的是使用GLSL來編寫頂點著色器和片元著色器。在使用OpenGL處理圖片輪播轉場時,關鍵點是編寫轉場所需的著色器,我們可以參考GLTransitions網站的開源轉場效果。該網站提供豐富的轉場效果和著色器代碼,可以很方便的移植到客戶端中。

對于實現復雜轉場,即將多個轉場效果組合使用,本文也提供了一個思路,就是組合使用多個OpenGL程序,在對應的時間點加載并使用對應的OpenGL程序。

鑒于篇幅原因,本文分享了部分我們基于OpenGL開發視頻轉場特效的思考與實踐,希望對大家有所幫助。

責任編輯:未麗燕 來源: 攜程技術
相關推薦

2012-05-02 16:25:47

JavaSwing

2011-07-29 11:10:56

IOS SimpleLogg 日志

2021-02-25 11:19:37

谷歌Android開發者

2011-08-17 14:57:31

iPhone應用視頻播放

2010-08-05 09:54:56

Flex特效

2011-02-22 10:23:43

2023-12-22 09:11:45

AndroidNFC移動開發

2009-09-22 12:17:59

ibmdwLotus

2021-12-20 20:30:48

鴻蒙HarmonyOS應用

2013-06-18 23:26:36

移動應用用戶使用成本控制管理移動互聯網

2011-09-02 16:42:51

Sencha ToucWeb應用

2011-07-21 14:50:06

Core Data SQL

2010-05-03 11:05:26

Widget開發

2021-09-07 10:24:36

Vue應用程序Web Workers

2011-05-27 08:48:13

Android HTML

2011-08-11 13:26:30

iPhoneNSLocalized

2013-10-09 11:15:49

Ubuntu應用程序

2009-06-19 13:45:53

Java應用程序Jfreechart

2009-11-23 19:52:55

ibmdwFlex

2012-06-05 09:54:50

Windows Pho
點贊
收藏

51CTO技術棧公眾號

日韩精品一级二级 | 性一交一黄一片| 免费在线毛片网站| 国产精品正在播放| 青青草原成人在线视频| 国产精品无码无卡无需播放器| 91成人短视频在线观看| 亚洲第一av色| 欧美韩国日本精品一区二区三区| 国产精品一区二区av白丝下载| 在线观看一区| 久久精品成人欧美大片古装| 女同性恋一区二区三区| 亚洲毛片在线免费| 一本久道久久综合中文字幕| dy888午夜| 亚洲欧美高清视频| 久热成人在线视频| 欧美一区二区三区艳史| 欧美黄色一区二区三区| 激情综合网站| 日韩av一区在线| 色婷婷激情视频| 在线成人视屏| 欧美日韩免费网站| 日韩一级片一区二区| aiai在线| 久久九九久久九九| 国产亚洲一区在线播放| 女人被狂躁c到高潮| 国产白丝一区二区三区| 日韩第二十一页| 色综合久久中文综合久久牛| 久久久久久www| 快射av在线播放一区| 久久久久久一二三区| 国产亚洲自拍偷拍| 理论片中文字幕| 国产一区二区导航在线播放| 国产欧美精品日韩精品| 尤物视频免费观看| 免费视频一区| 欧美在线xxx| 免费日韩一级片| 亚洲成人在线| 国内精品小视频在线观看| 欧美丰满艳妇bbwbbw| 久久久久久美女精品 | 国产精品福利视频| 国产a级免费视频| 国内精品不卡在线| 91免费欧美精品| 国产精品久久久久久久一区二区| 蜜臀av在线播放一区二区三区| 国产精品高潮在线| 伊人亚洲综合网| 美女性感视频久久| 成人疯狂猛交xxx| 国产精品爽爽久久久久久| 美国一区二区三区在线播放| 国产噜噜噜噜噜久久久久久久久| 中文字幕免费高清在线观看| 麻豆国产精品一区二区三区| 91精品视频免费| av 一区二区三区| 国产成人免费视频网站高清观看视频| 999热视频在线观看| 亚洲精品国产精品乱码不卡| 成人一道本在线| 色综合久久天天| 国内一区二区三区在线视频| 午夜性色福利视频| 久久网站最新地址| 亚洲女人毛片| а√天堂官网中文在线| 亚洲综合免费观看高清完整版在线 | av资源种子在线观看| 国产精品久久久久久亚洲毛片 | aa国产成人| 日韩欧美福利视频| 日本美女高潮视频| 激情久久免费视频| 亚洲精品国产欧美| 日本免费www| 欧美精品色网| 日韩av手机在线看| 国产麻豆91视频| 99视频一区二区三区| 亚洲v日韩v欧美v综合| 黄视频在线观看网站| 亚洲一区二区在线观看视频| 国产男女无遮挡| 亚洲免费资源| 精品亚洲男同gayvideo网站| 国产大屁股喷水视频在线观看| 国产精品大片免费观看| 国产成人在线视频| 国产大学生av| 黄色大全在线观看| 日韩精品免费专区| av资源站久久亚洲| 国产区视频在线| 亚洲激情中文1区| 久久久久国产精品熟女影院| 日本精品在线观看| 这里只有精品久久| 中文字幕亚洲高清| 国产一区二区0| 日韩精品久久久| 手机在线免费av| 欧美三级日本三级少妇99| 成年女人免费视频| 97在线精品| 国产精品wwwwww| 神马午夜精品95| 亚洲欧美国产毛片在线| 任你操这里只有精品| 给我免费播放日韩视频| 久久精品国产亚洲一区二区| 精人妻无码一区二区三区| 国产成人自拍网| 国产精品亚洲天堂| 国产69精品久久| 国产视频综合在线| 日产精品久久久久久久| 狠狠色丁香婷综合久久| 日韩欧美电影一区二区| 美女福利一区二区| 日韩电影免费观看中文字幕| 久久久精品人妻一区二区三区四| 美国av一区二区| 日韩不卡av| 天堂а√在线中文在线| 女海盗2成人h版中文字幕| 欧美一区在线视频| 91制片厂在线| 麻豆精品精品国产自在97香蕉| 免费99视频| 免费电影日韩网站| 精品中文视频在线| 国产成人免费看| 91影院在线观看| 国产视频一视频二| 日韩超碰人人爽人人做人人添| 久久久这里只有精品视频| 精品国产无码一区二区三区| 亚洲激情图片一区| 久久久久99人妻一区二区三区| 自拍偷拍欧美专区| 91福利视频导航| 青青草原av在线| 欧美mv日韩mv国产网站app| 国产精品老熟女一区二区| 国产高清精品在线| 日本香蕉视频在线观看| 久久久免费毛片| 2018日韩中文字幕| 欧美zozo| 欧美日韩精品专区| 欧美爱爱免费视频| 国产成人a级片| www.av毛片| 日本精品影院| 国产精品视频在线播放| 国产在线高清理伦片a| 日韩精品一区二区三区三区免费| 日韩免费一二三区| 久久综合九色综合97婷婷女人| 男女曰b免费视频| 欧美激情国产在线| 99re视频在线观看| 国产美女高潮在线观看| 亚洲图中文字幕| 国产又粗又长又黄| 亚洲国产一区二区三区青草影视 | 精品一区91| 国内精品久久久久影院 日本资源| 天堂av在线资源| 在线观看欧美黄色| 欧美成人三级在线观看| 91在线视频免费91| 五月天av在线播放| 亚洲日韩成人| 五码日韩精品一区二区三区视频| 国产精选久久| 2019中文字幕免费视频| 91caoporn在线| 日韩免费高清视频| 亚洲综合图片网| 亚洲乱码国产乱码精品精的特点| 亚洲熟女乱综合一区二区三区 | 国产精品久久电影观看| 亚洲欧美成人影院| 亚洲人成毛片在线播放| a视频免费在线观看| 日韩欧美亚洲成人| 动漫性做爰视频| 2020国产精品| 欧洲成人午夜精品无码区久久| 日韩成人午夜精品| 成人在线国产视频| 欧美mv日韩| 欧美大香线蕉线伊人久久国产精品| 日韩欧乱色一区二区三区在线| 97免费中文视频在线观看| 欧美日韩在线资源| 亚洲人午夜色婷婷| 日本激情一区二区三区| 第一区第二区在线| 欧美一级淫片007| 91porny九色| 亚洲va欧美va人人爽| 黄色激情小视频| 久久在线免费观看| 亚洲无人区码一码二码三码| 久久精品国产精品青草| 美女福利视频在线| 在线欧美日韩| 99中文字幕在线观看| 精品国产一区二区三区香蕉沈先生| 国产高清在线一区| 九九99久久精品在免费线bt| 国产精品一区久久久| 日本综合字幕| 欧美在线一级视频| 川上优av中文字幕一区二区| 色综合久久88色综合天天看泰| 男人天堂手机在线| 中文字幕av一区中文字幕天堂| 香蕉久久一区二区三区| 亚洲精品一区二区在线观看| av无码精品一区二区三区宅噜噜| 欧美性欧美巨大黑白大战| 日本熟女毛茸茸| 欧美性猛交xxxxx免费看| 日操夜操天天操| 五月婷婷色综合| 国产成人无码精品久在线观看| 亚洲综合视频网| 精品无码人妻一区二区三区| 亚洲最新视频在线观看| 欧美老熟妇一区二区三区| 亚洲欧洲性图库| 国产又粗又硬又长又爽| 亚洲色欲色欲www在线观看| 国产三级精品三级观看| 成人欧美一区二区三区白人 | 国产经典一区二区| 性欧美gay| 国产欧美 在线欧美| 祥仔av免费一区二区三区四区| 国产欧美日韩高清| aa亚洲一区一区三区| 91天堂在线观看| 澳门成人av| 精品视频在线观看| 九热爱视频精品视频| 五月天亚洲综合| 99视频精品全部免费在线视频| 五月天色婷婷综合| 欧美日韩国产在线一区| 福利视频一区二区三区四区| 国产婷婷精品| 一区二区三区国产免费| 精品一区在线看| 中文字幕1区2区| 99re8在线精品视频免费播放| caopeng视频| 亚洲同性同志一二三专区| 国产一级二级毛片| 欧美性高跟鞋xxxxhd| 一级特黄色大片| 欧美成人在线直播| 日本天堂在线| 色偷偷噜噜噜亚洲男人| 蜜桃传媒在线观看免费进入| 91精品国产色综合久久不卡98| 免费电影日韩网站| 3d动漫精品啪啪一区二区三区免费 | heyzo高清在线| 国产成人在线视频| 亚洲视频一起| 日本精品视频一区| 亚洲天堂免费| 国产综合免费视频| 狠狠色综合播放一区二区| 亚洲一区二区三区综合| 国产精品灌醉下药二区| 日本在线小视频| 欧美精品欧美精品系列| 香蕉视频免费看| 久久九九亚洲综合| 最近高清中文在线字幕在线观看1| 国产这里只有精品| 欧美一区 二区| 97超碰人人爱| 天堂蜜桃一区二区三区| 少妇伦子伦精品无吗| 日本一区二区久久| 男人的天堂一区| 欧美一区二区三区免费| 黄色av网站在线免费观看| 欧美激情精品久久久久久蜜臀| 91伊人久久| 久久免费一区| 国色天香一区二区| 男人午夜视频在线观看| 久久老女人爱爱| 国产无套内射又大又猛又粗又爽| 欧美日韩一区二区三区免费看| 五月婷婷六月丁香| 欧美剧在线观看| 外国成人毛片| 日韩精品伦理第一区| 亚洲少妇一区| 动漫美女无遮挡免费| 亚洲人成亚洲人成在线观看图片 | 日韩成人xxxx| 在线观看小视频| 成人黄色片在线| 久久国产精品亚洲人一区二区三区| 亚洲自偷自拍熟女另类| 成人免费视频视频在线观看免费| 午夜激情视频在线播放| 日本久久电影网| 久草在线青青草| 情事1991在线| 亚洲aa在线| 欧美成人xxxxx| av成人老司机| 日产欧产va高清| 亚洲国产成人av在线| 暖暖在线中文免费日本| 91在线精品视频| 一级毛片免费高清中文字幕久久网| 污污的视频免费| 国产精品高潮久久久久无| 中文字幕欧美人妻精品| 尤物精品国产第一福利三区| 3d性欧美动漫精品xxxx软件| 蜜桃999成人看片在线观看| 亚久久调教视频| 无码人妻精品一区二区中文| 日本韩国视频一区二区| wwwxxx在线观看| 成人xvideos免费视频| 天天揉久久久久亚洲精品| 午夜天堂在线视频| 亚洲欧美日韩久久精品| 国产xxxxxx| 久久久久国产精品免费| 大型av综合网站| 春日野结衣av| 亚洲国产成人私人影院tom| 中文字幕一二三四| 久久人人爽人人爽爽久久| 日韩精品视频一区二区三区| av在线免费观看国产| 99国产精品久久久久久久久久久| 国产精品黄色大片| 亚洲一区二区久久久| 91精品一久久香蕉国产线看观看 | 国产极品视频在线观看| 欧美精品色综合| 污网站在线免费看| 国产精品日韩一区二区三区 | 麻豆国产在线播放| 国产精品色悠悠| 午夜久久tv| 91精品人妻一区二区| 欧美日韩一区二区在线观看 | 一区二区成人在线| 天天射天天色天天干| 国产精品jvid在线观看蜜臀| 国产精品99久久精品| 欧美xxxx日本和非洲| 岛国精品视频在线播放| 天堂аⅴ在线地址8| 99精品国产高清在线观看| 午夜影院日韩| 亚洲波多野结衣| 日韩成人在线播放| 久久69成人| 成年人网站免费视频| 国产欧美一区二区精品性色| av网站在线观看免费| 青草青草久热精品视频在线观看| 91视频综合| 波多野结衣先锋影音| 欧美精品日日鲁夜夜添| 亚洲精品国产日韩| 精品人妻少妇嫩草av无码| 欧美日韩国产免费一区二区 | 色喇叭免费久久综合| 亚洲色欲综合一区二区三区| 国产精品久久看| 日本中文字幕一区二区有码在线 | 美女福利精品视频| 免费观看久久av| 少妇熟女视频一区二区三区|