GLSurfaceView

GLSurfaceView是一個視圖,繼承至SurfaceView,它內嵌的surface專門負責OpenGL渲染。

        GLSurfaceView提供了下列特性:
            1> 管理一個surface,這個surface就是一塊特殊的內存,能直接排版到android的視圖view上。
            2> 管理一個EGL display,它能讓opengl把內容渲染到上述的surface上。
            3> 用戶自定義渲染器(render)。
            4> 讓渲染器在獨立的線程裏運作,和UI線程分離。
            5> 支持按需渲染(on-demand)和連續渲染(continuous)。
            6> 一些可選工具,如調試。
        
使用GLSurfaceView
        通常會繼承GLSurfaceView,並重載一些和用戶輸入事件有關的方法。如果你不需要重載事件方法,GLSurfaceView也可以直接使用,你可以使用set方法來爲該類提供自定義的行爲。例如,GLSurfaceView的渲染被委託給渲染器在獨立的渲染線程裏進行,這一點和普通視圖不一樣,setRenderer(Renderer)設置渲染器。
        
初始化GLSurfaceView
        初始化過程其實僅需要你使用setRenderer(Renderer)設置一個渲染器(render)。當然,你也可以修改GLSurfaceView一些默認配置。
            * setDebugFlags(int)
            * setEGLConfigChooser(boolean)
            * setEGLConfigChooser(EGLConfigChooser)
            * setEGLConfigChooser(int, int, int, int, int, int)
            * setGLWrapper(GLWrapper) 

定製android.view.Surface
        GLSurfaceView默認會創建像素格式爲PixelFormat.RGB_565的surface。如果需要透明效果,調用getHolder().setFormat(PixelFormat.TRANSLUCENT)。透明(TRANSLUCENT)的surface的像素格式都是32位,每個色彩單元都是8位深度,像素格式是設備相關的,這意味着它可能是ARGB、RGBA或其它。
        
選擇EGL配置
        Android設備往往支持多種EGL配置,可以使用不同數目的通道(channel),也可以指定每個通道具有不同數目的位(bits)深度。因此,在渲染器工作之前就應該指定EGL的配置。GLSurfaceView默認EGL配置的像素格式爲RGB_656,16位的深度緩存(depth buffer),默認不開啓遮罩緩存(stencil buffer)。
        如果你要選擇不同的EGL配置,請使用setEGLConfigChooser方法中的一種。
        
調試行爲
        你可以調用調試方法setDebugFlags(int)或setGLWrapper(GLSurfaceView.GLWrapper)來自定義GLSurfaceView一些行爲。在setRenderer方法之前或之後都可以調用調試方法,不過最好是在之前調用,這樣它們能立即生效。
        
設置渲染器
        總之,你必須調用setRenderer(GLSurfaceView.Renderer)來註冊一個GLSurfaceView.Renderer渲染器。渲染器負責真正的GL渲染工作。
        
渲染模式
        渲染器設定之後,你可以使用setRenderMode(int)指定渲染模式是按需(on demand)還是連續(continuous)。默認是連續渲染。
        
Activity生命週期
        Activity窗口暫停(pause)或恢復(resume)時,GLSurfaceView都會收到通知,此時它的onPause方法和onResume方法應該被調用。這樣做是爲了讓GLSurfaceView暫停或恢復它的渲染線程,以便它及時釋放或重建OpenGL的資源。
        
事件處理
        爲了處理事件,一般都是繼承GLSurfaceView類並重載它的事件方法。但是由於GLSurfaceView是多線程操作,所以需要一些特殊的處理。由於渲染器在獨立的渲染線程裏,你應該使用Java的跨線程機制跟渲染器通訊。queueEvent(Runnable)方法就是一種相對簡單的操作,例如下面的例子。
        
class MyGLSurfaceView extends GLSurfaceView {
	private MyRenderer mMyRenderer;

        public void start() {
            mMyRenderer = ...;
            setRenderer(mMyRenderer);
        }


        public boolean onKeyDown(int keyCode, KeyEvent event) {

            if (keyCode == KeyEvent.KEYCODE_DPAD_CENTER) {
                queueEvent(new Runnable() {
                    // 這個方法會在渲染線程裏被調用
                         public void run() {
                             mMyRenderer.handleDpadCenter();
                         }});
                     return true;
                 }

                 return super.onKeyDown(keyCode, event);
            }
      }
}
 
        (注:如果在UI線程裏調用渲染器的方法,很容易收到“call to OpenGL ES API with no current context”的警告,典型的誤區就是在鍵盤或鼠標事件方法裏直接調用opengl es的API,因爲UI事件和渲染繪製在不同的線程裏。更甚者,這種情況下調用glDeleteBuffers這種釋放資源的方法,可能引起程序的崩潰,因爲UI線程想釋放它,渲染線程卻要使用它。)
實例說明:
GLSurfaceView是一個很好的基類對於構建一個使用OpenGL ES進行部分或全部渲染的應用程序。一個2D或3D的動作遊戲就是一個很好的例子,例如一個2D或3D的可視化應用如谷歌地圖。
 
以下是一個簡單的GLSurfaceView的應用,一個最簡單的OpenGL ES應用代碼如下:
 
package  com.javaeye.googlers

import javax.microedition.khronos.egl.EGLConfig;
import javax.microedition.khronos.opengles.GL10;
import android.app.Activity;
import android.opengl.GLSurfaceView;
import android.os.Bundle;
 

public class ClearActivity extends Activity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        mGLView = new GLSurfaceView(this);
        mGLView.setRenderer(new ClearRenderer());
        setContentView(mGLView);
    }

    @Override
    protected void onPause() {
        super.onPause();
        mGLView.onPause();
    }

    @Override

    protected void onResume() {
        super.onResume();
        mGLView.onResume();
    }

    private GLSurfaceView mGLView;
}

class ClearRenderer implements GLSurfaceView.Renderer {

    public void onSurfaceCreated(GL10 gl, EGLConfig config) {
        // Do nothing special.
    } 

    public void onSurfaceChanged(GL10 gl, int w, int h) {
        gl.glViewport(0, 0, w, h);
    }

    public void onDrawFrame(GL10 gl) {
        gl.glClear(GL10.GL_COLOR_BUFFER_BIT | GL10.GL_DEPTH_BUFFER_BIT);
    }
}
 
這個程序並沒有做太多東西:它在每幀是清除屏幕到黑色。但是它是一個完整的OpenGL應用程序,正確地按照Activity(活動)的生命週期實現。當活動暫停渲染它也暫停渲染,活動恢復它也恢復。你可以把這個例子作爲一個基本的交互的示例程序。僅僅更多地調用了ClearRenderer.onDrawFrame() 方法。注意你甚至不需要子類化一個GLSurfaceView視圖。
 
GLSurfaceView.Renderer 有三個方法:
onSurfaceCreated() :在開始渲染的時候被調用,無論什麼時候OpenGL ES 渲染不得不重新被創建。(渲染是典型的丟失並重新創建當活動被暫停或恢復。)該方法一個創建長生命週期OpenGL資源(如材質)的好地方。
onSurfaceChanged():該方法在surface大小改變時被調用。這是設置你opengl視圖端的好地方。如果相機是固定的,不會圍着場景移動,你也可以在這裏設置你的相機。
onDrawFrame():每幀的時候該方法都會被調用,這個用於畫場景是可靠的。你完全可以通過調用glClear方法開清楚幀緩存,接着通過其他的opengl ES來調用畫當前的場景。
 
用戶如何輸入?
假如你想做一個可以交互的程序(如遊戲),通常你會實現GLSurfaceView子類,因爲這是很容易獲取用戶輸入事件。以下代碼是一個清晰的長例子展示給你怎樣做到這個:
 
package com.javaeye.googlers;

import javax.microedition.khronos.egl.EGLConfig;
import javax.microedition.khronos.opengles.GL10;
import android.app.Activity;
import android.content.Context;
import android.opengl.GLSurfaceView;
import android.os.Bundle;
import android.view.MotionEvent;

public class ClearActivity extends Activity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        mGLView = new ClearGLSurfaceView(this);
        setContentView(mGLView);
    }

    @Override
    protected void onPause() {
        super.onPause();
        mGLView.onPause();
    }

    @Override
    protected void onResume() {
        super.onResume();
        mGLView.onResume();
    }

    private GLSurfaceView mGLView;
}

class ClearGLSurfaceView extends GLSurfaceView {

    public ClearGLSurfaceView(Context context) {
        super(context);
        mRenderer = new ClearRenderer();
        setRenderer(mRenderer);
    }

    public boolean onTouchEvent(final MotionEvent event) {
        queueEvent(new Runnable(){
            public void run() {
                mRenderer.setColor(event.getX() / getWidth(),
                        event.getY() / getHeight(), 1.0f);
            }});

            return true;
        }

        ClearRenderer mRenderer;
}
 

class ClearRenderer implements GLSurfaceView.Renderer {

    public void onSurfaceCreated(GL10 gl, EGLConfig config) {
        // Do nothing special.
    }

    public void onSurfaceChanged(GL10 gl, int w, int h) {
        gl.glViewport(0, 0, w, h);
    }

    public void onDrawFrame(GL10 gl) {
        gl.glClearColor(mRed, mGreen, mBlue, 1.0f);
        gl.glClear(GL10.GL_COLOR_BUFFER_BIT | GL10.GL_DEPTH_BUFFER_BIT);
    }

 
    public void setColor(float r, float g, float b) {
        mRed = r;
        mGreen = g;
        mBlue = b;
    }

    private float mRed;
    private float mGreen;
    private float mBlue;
}

 
這個應用每幀都在清楚屏幕。當你點擊屏幕時,它清除顏色基於你觸屏時間的X、Y座標。注意在 ClearGLSurfaceView.onTouchEvent()中使用queueEvent()。queueEvent()方法被安全地用於在UI線程和渲染線程之間進行交流。如果你願意,你還可以使用一些其他的java線程間交流技術,例如Renderer 類本身的同步方法。然而,queueing 事件經常是一種用於處理線程間信息交流的更簡單方式。
 
其他的GLSurfaceView示例:
如果你厭煩了上面的示例,你還可以從android的ApiDemo中找到更經典的示例,所有的openGL ES示例都是用GLSurfaceView視圖轉變的:
 
GLSurfaceView - 一個旋轉的三角形
Kube - 一個魔方例子
Translucent GLSurfaceView - 展示在一個透明的背景上顯示3d動畫
Textured Triangle - 顯示一個帶紋理的3D三角形
Sprite Text - 展示怎樣用材質畫出文字並混合進一個3d的場景中
Touch Rotate - 展示怎樣旋轉一個3D物體來響應用戶的輸入
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章