第一章 創(chuàng)建OpenGL ES的環(huán)境
首先需要為OpenGL ES創(chuàng)建一個視圖(View)容器,一種實現(xiàn)方式是創(chuàng)建一個類實現(xiàn)GLSurfaceView和GLSurfaceView.Renderer岗照。GLSurfaceView是顯示圖形的視圖(View)容器蚪拦,GLSurfaceView.Renderer是控制畫圖的方法吨凑。更多的介紹可以看OpenGL ES的開發(fā)指南。
GLSurfaceView是多種集成OpenGL ES到應用方法中的一種柱徙,對于全屏或者近乎全屏的圖形視圖(graphics view)來說澎粟,應該選擇這種方式应闯。當用戶只是對一小區(qū)域使用OpenGL ES繪制圖形纤控,那么可以選擇TextureView。
這篇文章只是簡單的實現(xiàn)繪制OpenGL ES視圖的功能碉纺。
1.1 在Manifest中聲明所使用到的OpenGL ES
如果你使用到OpenGL ES 2.0API船万,你需要在manifest中添加以下聲明:
<uses-feature android:glEsVersion="0x00020000" android:required="true" />
如果應用程序使用紋理壓縮功能,則還必須聲明應用程序支持的壓縮格式骨田,以便僅安裝在兼容設備上耿导。
<supports-gl-texture android:name="GL_OES_compressed_ETC1_RGB8_texture" />
<supports-gl-texture android:name="GL_OES_compressed_paletted_texture" />
1.2 為OpenGL ES圖形創(chuàng)建一個Activity
這個Activity與其他類型應用的Activity并無不同,要說不同态贤,也僅僅是放到Activity的layout的view不一樣舱呻,你需要放入的是一個GLSurfaceView
下面的代碼展示了使用GLSurfaceView作為主視圖的Activity的最少代碼實現(xiàn):
public class OpenGLES20Activity extends Activity {
private GLSurfaceView mGLView;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// 創(chuàng)建一個GLSurfaceView實例然后設置為activity的ContentView.
mGLView = new MyGLSurfaceView(this);
setContentView(mGLView);
}
}
Note: OpenGL ES 2.0要求Android2.2(API Level 8)或者更高,所以需要指定Android工程的目標API必須高于或者等于 API 8.
1.3 創(chuàng)建GLSurfaceView對象
GLSurfaceView是專門用來繪制OpenGL ES圖形的悠汽,但是它自己沒有做多少工作箱吕,真正的繪制是由GLSurfaceView.Renderer來完成的。所以GLSurfaceView的代碼頁不多柿冲,你可以直接使用GLSurfaceView茬高,但是不要這樣做,因為需要處理觸摸事件假抄,我們需要自定義一個類繼承GLSurfaceView怎栽。
下面是一個簡單的自定義類:
class MyGLSurfaceView extends GLSurfaceView {
private final MyGLRenderer mRenderer;
public MyGLSurfaceView(Context context){
super(context);
// 創(chuàng)建OpenGL ES 2.0 的上下文
setEGLContextClientVersion(2);
mRenderer = new MyGLRenderer();
// 設置Renderer 到 GLSurfaceView
setRenderer(mRenderer);
}
}
除此之外你還需要設置GLSurfaceView繪制的方式,GLSurfaceView有兩種繪制方式宿饱,Google API:
When renderMode is
RENDERMODE_CONTINUOUSLY
, the renderer is called repeatedly to re-render the scene. When renderMode isRENDERMODE_WHEN_DIRTY
, the renderer only rendered when the surface is created, or when requestRender is called. Defaults toRENDERMODE_CONTINUOUSLY
.
Using RENDERMODE_WHEN_DIRTY
can improve battery life and overall system performance by allowing the GPU and CPU to idle when the view does not need to be updated.
大意是RENDERMODE_CONTINUOUSLY
模式就會一直Render熏瞄,如果設置成RENDERMODE_WHEN_DIRTY
,就是當有數(shù)據(jù)時才rendered或者主動調(diào)用了GLSurfaceView的requestRender
.默認是連續(xù)模式刑棵,很顯然Camera適合臟模式巴刻,一秒30幀,當有數(shù)據(jù)來時再渲染蛉签。
// Render the view only when there is a change in the drawing data
setRenderMode(GLSurfaceView.RENDERMODE_WHEN_DIRTY);
這里我們設置成為RENDERMODE_WHEN_DIRTY模式胡陪,當有數(shù)據(jù)時,我們調(diào)用requestRender()再進行繪制碍舍,這樣效率比較高柠座。
public void onFrameAvailable(SurfaceTexture surfaceTexture) {
// TODO Auto-generated method stub
this.requestRender();
}
1.4 創(chuàng)建Renderer類
GLSurfaceView.Renderer控制向GLSurfaceView的繪制工作,它有三個方法被Android系統(tǒng)調(diào)用來計算在GLSurfaceView上畫什么以及如何畫:
-
onSurfaceCreated()
:當GLSurfaceView被創(chuàng)建時片橡,會調(diào)用一次妈经,用于設置view的OpenGL ES環(huán)境。 -
onDrawFrame()
:每次繪制圖像的時候都會調(diào)用這個方法。 -
onSurfaceChanged()
:當幾何圖形變化時吹泡,會調(diào)用此方法骤星,例如,當手機屏幕大小變化時爆哑。
下面代碼實現(xiàn)了GLSurfaceView.Renderer最基本功能洞难,它僅在GLSurfaceView上畫了一個黑色的背景。
public class MyGLRenderer implements GLSurfaceView.Renderer {
public void onSurfaceCreated(GL10 unused, EGLConfig config) {
// 設置背景的顏色
GLES20.glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
}
public void onDrawFrame(GL10 unused) {
// 重繪背景顏色
GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT);
}
public void onSurfaceChanged(GL10 unused, int width, int height) {
GLES20.glViewport(0, 0, width, height);
}
}
以上就是所有需要做的東西了揭朝,上面的代碼創(chuàng)建了一個簡單的Android應用队贱,它使用OpengGL 顯示了一個黑色的屏幕。單這段代碼并沒有什么特殊之處潭袱,只是為使用OpenGL繪制做好準備柱嫌。
第二章 定義形狀
會定義在OpenGL ES View上所繪制的形狀,是你創(chuàng)建高端圖形應用杰作的第一步屯换。如果你不懂OpenGL ES定義圖形對象的一些基本只是编丘,使用OpenGL ES可能會有點棘手。
本章解釋OpenGL ES相對于Android設備屏幕的坐標系統(tǒng)彤悔、定義一個形狀的基礎只是瘪吏、形狀的外觀、以及如何定義三角形和正方形蜗巧。
2.1 定義一個三角形
OpenGL ES允許使用三維坐標系來繪制圖像掌眠。在繪制圖像之前,首先需要定義一個坐標系幕屹。然后蓝丙,在OpenGL中,典型的做法是定義一個浮點類型的頂點數(shù)組來指向相應的坐標系望拖。為了高效渺尘,將這個數(shù)組代表的坐標寫入到ByteBuffer,ByteBuffer會傳輸?shù)絆penGL ES圖形管道中進行處理说敏。
public class Triangle {
private FloatBuffer vertexBuffer;
// 數(shù)組中每個頂點的坐標數(shù)
static final int COORDS_PER_VERTEX = 3;
static float triangleCoords[] = { // 按照逆時針方向:
0.0f, 0.622008459f, 0.0f, // top
-0.5f, -0.311004243f, 0.0f, // bottom left
0.5f, -0.311004243f, 0.0f // bottom right
};
// 設置顏色RGBA(red green blue alpha)
float color[] = { 0.63671875f, 0.76953125f, 0.22265625f, 1.0f };
public Triangle() {
// 為存放形狀的坐標鸥跟,初始化頂點字節(jié)緩沖
ByteBuffer bb = ByteBuffer.allocateDirect(
// (坐標數(shù) * 4 )float 占四個字節(jié)
triangleCoords.length * 4);
// 使用設備的本點字節(jié)序
bb.order(ByteOrder.nativeOrder());
// 從ByteBuffer創(chuàng)建一個浮點緩沖
vertexBuffer = bb.asFloatBuffer();
// 把坐標加入FloatBuffer中
vertexBuffer.put(triangleCoords);
// 設置buffer,從第一個坐標開始讀
vertexBuffer.position(0);
}
}
默認情況下盔沫,OpenGL ES將坐標[0,0,0](X,Y,Z)當做坐標軸的中心医咨,[1,1,0]是坐標軸的右上角,[-1,-1,0]是坐標軸的坐下角架诞。
繪制的順序是非常重要的拟淮,繪制定義了哪邊是正面的形狀,哪邊是反面的形狀谴忧,一般情況下繪制的順序是逆時針的方向很泊,使用OpenGL ES的cull face特性角虫,你可以只畫正面而不畫反面。
2.2 繪制正方形
在OpenGL上繪制三角形還是比較容易的委造,但是繪制正方形戳鹅,相對來說就有一些麻煩了,繪制一個正方形一般的做法是昏兆,將兩個三角形繪制在一起粉楚,如下圖所示:
和繪制三角形一樣,你需要定義一個逆時針繪制頂點的數(shù)組亮垫,并且將這個數(shù)組放入到ByteBuffer中。為了避免分別為兩個三角形定義兩個坐標數(shù)組伟骨,我們需要使用一個繪制列表列來告訴OpengGL ES圖形管道怎么繪制這些頂點坐標饮潦,一下是具體的代碼:
public class Square {
private FloatBuffer vertexBuffer;
private ShortBuffer drawListBuffer;
// 每個頂點的坐標數(shù)
static final int COORDS_PER_VERTEX = 3;
static float squareCoords[] = {
-0.5f, 0.5f, 0.0f, // top left
-0.5f, -0.5f, 0.0f, // bottom left
0.5f, -0.5f, 0.0f, // bottom right
0.5f, 0.5f, 0.0f }; // top right
private short drawOrder[] = { 0, 1, 2, 0, 2, 3 }; // 繪制頂點的順序
public Square() {
// initialize vertex byte buffer for shape coordinates
ByteBuffer bb = ByteBuffer.allocateDirect(
// (# of coordinate values * 4 bytes per float)
squareCoords.length * 4);
bb.order(ByteOrder.nativeOrder());
vertexBuffer = bb.asFloatBuffer();
vertexBuffer.put(squareCoords);
vertexBuffer.position(0);
// initialize byte buffer for the draw list
ByteBuffer dlb = ByteBuffer.allocateDirect(
// (# of coordinate values * 2 bytes per short)
drawOrder.length * 2);
dlb.order(ByteOrder.nativeOrder());
drawListBuffer = dlb.asShortBuffer();
drawListBuffer.put(drawOrder);
drawListBuffer.position(0);
}
}
這個例子告訴你創(chuàng)建OpenGL更復雜的形狀需要什么。通常携狭,使用三角形的集合來繪制對象继蜡,下一張,將會描述這些形狀怎樣繪制到屏幕上逛腿。
第三章 繪制形狀
當你定義使用OpenGL繪制的形狀之后稀并,你可能想把它們繪制到屏幕上。使用OpenGL ES2.0繪制這些圖形可能會比較麻煩单默,因為API提供了大量的功能來控制圖形的渲染管道碘举。
3.1初始化圖形
在做任何繪制圖形之前,你必須初始化形狀然后再加載它搁廓。除非形狀的結構(指原始坐標)在執(zhí)行過程中發(fā)生改變引颈,你需要在Renderer的方法onSurfaceCreated()
中進行內(nèi)存和效率方面的初始化工作。
public class MyGLRenderer implements GLSurfaceView.Renderer {
...
private Triangle mTriangle;
private Square mSquare;
public void onSurfaceCreated(GL10 unused, EGLConfig config) {
...
// 初始化一個三角形
mTriangle = new Triangle();
// 初始化一個正方形
mSquare = new Square();
}
...
}
3.2 繪制圖形
使用OpenGL ES 2.0繪制一個定義好的圖形需要大量的代碼境蜕,因為需要必須提供圖形渲染管道的許多細節(jié)蝙场,具體來說,你需要定義以下內(nèi)容:
- vertex Shader - 頂點著色器粱年,用來繪制圖形的形狀
- Fragment Shader - 片段著色器售滤,用來繪制圖形的顏色或者是紋理
-
Program - 一個OpenGL ES對象,包含了用來繪制一個或者多個形狀的shader台诗。
你需要定義至少一個頂點著色器和片段著色器完箩,這些形狀必須被編譯然后被添加到一個OpenGL ES program中,program之后會被用來繪制形狀拉队。下面代碼是繪制三角形時定義的著色器語言:
public class Triangle {
private final String vertexShaderCode =
"attribute vec4 vPosition;" +
"void main() {" +
" gl_Position = vPosition;" +
"}";
private final String fragmentShaderCode =
"precision mediump float;" +
"uniform vec4 vColor;" +
"void main() {" +
" gl_FragColor = vColor;" +
"}";
...
}
著色器(shader)語言需要編譯到OpenGL ES環(huán)境中嗜憔,為了編譯著色器的代碼,需要在你的Renderer類中創(chuàng)建一個工具類方法:
public static int loadShader(int type, String shaderCode){
// 創(chuàng)建一個vertex shader 類型 (GLES20.GL_VERTEX_SHADER)
// 或者一個 fragment shader 類型(GLES20.GL_FRAGMENT_SHADER)
int shader = GLES20.glCreateShader(type);
// 將源碼添加到shader并編譯
GLES20.glShaderSource(shader, shaderCode);
GLES20.glCompileShader(shader);
return shader;
}
為了繪制你的形狀氏仗,必須編譯shader代碼吉捶,將著色器添加到OpengGL ES program對象中夺鲜,然后鏈接到program,在renderer對象的構造函數(shù)中做這些事情呐舔,只需要定義一次就好币励。
public class Triangle() {
...
private final int mProgram;
public Triangle() {
...
int vertexShader = MyGLRenderer.loadShader(GLES20.GL_VERTEX_SHADER,
vertexShaderCode);
int fragmentShader = MyGLRenderer.loadShader(GLES20.GL_FRAGMENT_SHADER,
fragmentShaderCode);
// 創(chuàng)建一個空的 OpenGL ES Program
mProgram = GLES20.glCreateProgram();
// 將vertex shader 添加到 program
GLES20.glAttachShader(mProgram, vertexShader);
// 將fragment shader 添加到 program
GLES20.glAttachShader(mProgram, fragmentShader);
// 創(chuàng)建一個可執(zhí)行的 OpenGL ES program
GLES20.glLinkProgram(mProgram);
}
}
使用OpengGL ES繪制圖形需要調(diào)用很多的函數(shù)和使用很多的參數(shù),一個比較明智的做法是創(chuàng)建一個自己的著色類珊拼,來管理這些操作食呻。
創(chuàng)建draw()
函數(shù)來繪制圖形。以下代碼設置了頂點著色器和片段著色器的位置和顏色澎现,然后執(zhí)行繪制功能仅胞。
private int mPositionHandle;
private int mColorHandle;
private final int vertexCount = triangleCoords.length / COORDS_PER_VERTEX;
private final int vertexStride = COORDS_PER_VERTEX * 4; // 4 bytes per vertex
public void draw() {
// 將program 添加到 OpenGL ES 環(huán)境中
GLES20.glUseProgram(mProgram);
// 獲取指向vertex shader的成員vPosition的句柄
mPositionHandle = GLES20.glGetAttribLocation(mProgram, "vPosition");
// 啟用一個指向三角形的頂點數(shù)組的句柄
GLES20.glEnableVertexAttribArray(mPositionHandle);
// 準備三角形的坐標數(shù)據(jù)
GLES20.glVertexAttribPointer(mPositionHandle, COORDS_PER_VERTEX,
GLES20.GL_FLOAT, false,
vertexStride, vertexBuffer);
// 獲取指向fragment shader的成員vColor的句柄
mColorHandle = GLES20.glGetUniformLocation(mProgram, "vColor");
// 設置三角形的顏色
GLES20.glUniform4fv(mColorHandle, 1, color, 0);
// 畫三角形
GLES20.glDrawArrays(GLES20.GL_TRIANGLES, 0, vertexCount);
// 禁用指向三角形的定點數(shù)組
GLES20.glDisableVertexAttribArray(mPositionHandle);
}
在renderer的onDrawFrame()
方法中調(diào)用draw()
就會繪制三角形了。
public void onDrawFrame(GL10 unused) {
...
mTriangle.draw();
}
在上面的代碼示例中有一些問題:
- 給人留下的印象不深刻剑辫。
- 當你改變屏幕的方向時干旧,三角形有點壓扁和變形。形狀變形的的原因是由于頂點的租表沒有根據(jù)GLSurfaceView的改變而改變妹蔽,下一章節(jié)中會有解決方法椎眯。
- 這個圖形沒有互動,也就是沒有觸摸事件胳岂。
在OpenGL ES環(huán)境中编整,投影和相機視圖的方式展現(xiàn)出來的物體更像人眼中的物體。通過轉(zhuǎn)換矩陣坐標的方式可以模擬出這種物理視圖乳丰。
-
投影-需要基于
GLSurfaceView
所展示界面的大小來進行坐標的調(diào)整掌测。如果不這樣,展示出來的圖像的比例是不對的产园。一般情況是是在方法onSurfaceChanged()
中進行計算的赏半。 - 相機視圖-需要基于虛擬的相機位置來進行坐標的轉(zhuǎn)換。OpenGL ES沒有定義一個真實的相機對象淆两,但是提供了方法來模擬相機断箫。
第四章 應用投影和相機視圖
在OpenGL ES環(huán)境中,投影和相機視圖使你繪制的對象以更接近物理對象的樣子顯示秋冰。這是通過對坐標精確的數(shù)學變換實現(xiàn)的仲义。
-
投影-這種變化根據(jù)所在的GLSurfaceView的寬和高調(diào)整對象的坐標。如果沒有此變化剑勾,對象會被不規(guī)則的視口扭曲埃撵。投射變換一般只需要在OpenGL view創(chuàng)建或者發(fā)生變化時調(diào)用,代碼卸載renderer的
onSurfaceChanged()
方法中虽另。 - 相機視圖-次變換是基于一個虛擬相機的位置調(diào)整對象的坐標暂刘。注意OpenGL ES并沒有定義一個真的相機對象,而是提供了一些工具方法變換繪制對象的顯示來模擬一個相機捂刺。一個相機視圖的變換只可能在創(chuàng)建GLSurfaceView時調(diào)用谣拣,或者根據(jù)用戶動作動態(tài)調(diào)用募寨。
本章講解了如何創(chuàng)建一個投影和一個相機視圖,然后用刀GLSurfaceView的形狀繪制過程森缠。
4.1 定義投影(projection)
投影一般定義在GLSurfaceView.Renderer
的onSurfaceChanged()
方法中拔鹰。下面的例子就是根據(jù)GLSurfaceView
的寬和高來計算轉(zhuǎn)換矩陣,使用了Matrix.frustumM()
方法來計算出了一個投影變化Matrix贵涵。
// mMVPMatrix is an abbreviation for "Model View Projection Matrix"
private final float[] mMVPMatrix = new float[16];
private final float[] mProjectionMatrix = new float[16];
private final float[] mViewMatrix = new float[16];
@Override
public void onSurfaceChanged(GL10 unused, int width, int height) {
GLES20.glViewport(0, 0, width, height);
float ratio = (float) width / height;
// 此投影矩陣在onDrawFrame()中將應用到對象的坐標
Matrix.frustumM(mProjectionMatrix, 0, -ratio, ratio, -1, 1, 3, 7);
}
以上代碼生成了一個投影矩陣列肢,mProjectionMatrix
在onDrawFrame()
方法中和相機視圖結合起來。
只對你的對象應用一個投影變換一般會導致什么也看不到宾茂。通常瓷马,你必須也對其應用一個視圖變換才能看到東西。
4.2 定義相機視圖
再定義一個相機視圖變換以使對繪制對象的變換處理變的完整跨晴。在下面的代碼中欧聘,使用Matrix.setLookAtM()
來計算相機視圖轉(zhuǎn)換,然后結合之前的投影矩陣坟奥。結合后的矩陣將之后傳給要繪制的對象。
@Override
public void onDrawFrame(GL10 unused) {
...
// 設置相機的位置 (視圖矩陣)
Matrix.setLookAtM(mViewMatrix, 0, 0, 0, -3, 0f, 0f, 0f, 0f, 1.0f, 0.0f);
// 計算投影和視圖變換
Matrix.multiplyMM(mMVPMatrix, 0, mProjectionMatrix, 0, mViewMatrix, 0);
// 繪制形狀
mTriangle.draw(mMVPMatrix);
}
4.3 使用投影和相機轉(zhuǎn)換
為了使用前面合并后的投影和相機圖像變換矩陣拇厢,需要在頂點著色器定義語句中添加以下代碼:
public class Triangle {
private final String vertexShaderCode =
//這個矩陣成員變量提供了一個勾子來操控
// 使用這個頂點著色器的對象的坐標
"uniform mat4 uMVPMatrix;" +
"attribute vec4 vPosition;" +
"void main() {" +
// the matrix must be included as a modifier of gl_Position
// Note that the uMVPMatrix factor *must be first* in order
// for the matrix multiplication product to be correct.
" gl_Position = uMVPMatrix * vPosition;" +
"}";
// Use to access and set the view transformation
private int mMVPMatrixHandle;
...
}
接下來爱谁,給draw()
方法添加參數(shù),接受之前的矩陣:
public void draw(float[] mvpMatrix) { // 傳遞計算出來的變換矩陣
...
// 獲得形狀的變換矩陣的句柄
mMVPMatrixHandle = GLES20.glGetUniformLocation(mProgram, "uMVPMatrix");
// Pass the projection and view transformation to the shader
GLES20.glUniformMatrix4fv(mMVPMatrixHandle, 1, false, mvpMatrix, 0);
// 繪制三角形
GLES20.glDrawArrays(GLES20.GL_TRIANGLES, 0, vertexCount);
// Disable vertex array
GLES20.glDisableVertexAttribArray(mPositionHandle);
}
做完以上步驟后孝偎,就可以得到一個正確的圖形了访敌。
現(xiàn)在你擁有了一個正確顯示形狀的應用了,接下來應該給圖形添加觸摸事件了衣盾。
第五章 添加事件
在屏幕上繪制是OpengGL的基礎能力寺旺,但是你也可以使用其他的Android圖形框架類來做,包括Canvas和Drawable势决。但是OpenGL ES提供了另外的能力阻塑,可以再三維上移動和變換對象」矗總之它能創(chuàng)造很好的用戶體驗陈莽。在本文中,你將學會如何使用OpenGL ES為形狀添加旋轉(zhuǎn)功能虽抄。
5.1 旋轉(zhuǎn)圖形
在OpengGL ES 2.0中旋轉(zhuǎn)一個圖形走搁,相對來說比較簡單。在渲染的時候迈窟,創(chuàng)建另外一個轉(zhuǎn)換矩陣(旋轉(zhuǎn)矩陣)私植,然后將這個矩陣合并到你的投影和相機圖形變換矩陣就行了。
private float[] mRotationMatrix = new float[16];
public void onDrawFrame(GL10 gl) {
float[] scratch = new float[16];
...
// 為三角形創(chuàng)建一個旋轉(zhuǎn)變化
long time = SystemClock.uptimeMillis() % 4000L;
float angle = 0.090f * ((int) time);
Matrix.setRotateM(mRotationMatrix, 0, angle, 0, 0, -1.0f);
// 把旋轉(zhuǎn)矩陣合并到投影和相機矩陣
// Note that the mMVPMatrix factor *must be first* in order
// for the matrix multiplication product to be correct.
Matrix.multiplyMM(scratch, 0, mMVPMatrix, 0, mRotationMatrix, 0);
// 畫三角形
mTriangle.draw(scratch);
}
如果你的三角形在此新代碼后旋轉(zhuǎn)不起來车酣,則要查看是否把GLSurfaceView.RENDERMODE_WHEN_DIRTY設置注釋了曲稼。
5.2 持續(xù)的渲染
如果你已經(jīng)跟著例子做到了這一步索绪,確保GLSurfaceView
的繪制模式不是dirty模式,否則躯肌,只有在調(diào)用requestRender()
時者春,GLSurfaceView
才會渲染。
public MyGLSurfaceView(Context context) {
...
// Render the view only when there is a change in the drawing data.
// To allow the triangle to rotate automatically, this line is commented out:
// 注釋掉清女,圖形就可以自動旋轉(zhuǎn)了
//setRenderMode(GLSurfaceView.RENDERMODE_WHEN_DIRTY);
}
除非你不讓對象與用戶有交互钱烟,否則啟用這個設置是一個好做法。要準備接觸這句的注釋了嫡丙,因為下一章節(jié)會用到它拴袭。
第六章 響應觸摸事件
使你的OpenGL ES應用能相應觸摸的關鍵是擴展你實現(xiàn)的GLSurfaceView代碼,覆寫onTouchEvent()方法來監(jiān)聽觸摸事件曙博。
本章節(jié)向你展示如何監(jiān)聽用戶的觸摸事件以使用戶可以旋轉(zhuǎn)某個OpenGL ES對象拥刻。
6.1 設置觸摸監(jiān)聽事件
為了使你的OpenGL ES應用響應觸摸事件,你必須在GLSurfaceView
類中實現(xiàn)onTouchEvent()
方法父泳。以下代碼在MOtionEvent.ACTION_MOVE
事件中計算了圖形旋轉(zhuǎn)的角度般哼。
private final float TOUCH_SCALE_FACTOR = 180.0f / 320;
private float mPreviousX;
private float mPreviousY;
@Override
public boolean onTouchEvent(MotionEvent e) {
// MotionEvent reports input details from the touch screen
// and other input controls. In this case, you are only
// interested in events where the touch position changed.
float x = e.getX();
float y = e.getY();
switch (e.getAction()) {
case MotionEvent.ACTION_MOVE:
float dx = x - mPreviousX;
float dy = y - mPreviousY;
// reverse direction of rotation above the mid-line
if (y > getHeight() / 2) {
dx = dx * -1 ;
}
// reverse direction of rotation to left of the mid-line
if (x < getWidth() / 2) {
dy = dy * -1 ;
}
mRenderer.setAngle(
mRenderer.getAngle() +
((dx + dy) * TOUCH_SCALE_FACTOR));
requestRender();
}
mPreviousX = x;
mPreviousY = y;
return true;
}
從代碼中可以看到,在計算完旋轉(zhuǎn)的角度后惠窄,調(diào)用了requesetRender()
蒸眠,這是為了告訴renderer要渲染幀了。這樣做的好處就是效率高杆融,因為只有在旋轉(zhuǎn)的時候才需要去渲染楞卡,其他時候不需要渲染。為了達到這種效果脾歇,我們需要設置GLSurfaceView
的渲染模式蒋腮。
public MyGLSurfaceView(Context context) {
...
// Render the view only when there is a change in the drawing data
setRenderMode(GLSurfaceView.RENDERMODE_WHEN_DIRTY);
}
6.2 添加旋轉(zhuǎn)角度接口
我們需要在自定義的MyGLRenderer
類中,定義旋轉(zhuǎn)角度的接口藕各,并且暴露出去池摧。
public class MyGLRenderer implements GLSurfaceView.Renderer {
...
public volatile float mAngle;
public float getAngle() {
return mAngle;
}
public void setAngle(float angle) {
mAngle = angle;
}
}
6.3 旋轉(zhuǎn)圖形
將旋轉(zhuǎn)的角度添加到渲染的代碼中:
public void onDrawFrame(GL10 gl) {
...
float[] scratch = new float[16];
// Create a rotation for the triangle
// long time = SystemClock.uptimeMillis() % 4000L;
// float angle = 0.090f * ((int) time);
Matrix.setRotateM(mRotationMatrix, 0, mAngle, 0, 0, -1.0f);
// Combine the rotation matrix with the projection and camera view
// Note that the mMVPMatrix factor *must be first* in order
// for the matrix multiplication product to be correct.
Matrix.multiplyMM(scratch, 0, mMVPMatrix, 0, mRotationMatrix, 0);
// Draw triangle
mTriangle.draw(scratch);
}
至此,OpenGL ES的初步教程就敘述完了激况。