快速入門
關鍵類
- GLSurfaceView
- 繪制的載體
- 小部分區域時可使用TextureView
- 理論上可使用SurfaceView
- GLSurfaceView.Renderer
- 實質的繪制動作
授權 | |
---|---|
登錄 | 是 |
簽名 | 是 |
構鍵一個GLSurfaceView對象
class MyGLSurfaceView extends GLSurfaceView {
public MyGLSurfaceView(Context context){
super(context);
// 創建一個OpenGL ES 2.0 context
setEGLContextClientVersion(2);
// 設置Renderer到GLSurfaceView
setRenderer(new MyRenderer());
// 只有在繪制數據改變時才繪制view
setRenderMode(GLSurfaceView.RENDERMODE_WHEN_DIRTY);
}
}
GLSurfaceView.RENDERMODE_WHEN_DIRTY
會阻止繪制GLSurfaceView的幀,直到你調用了requestRender(),這樣會非常高效。
繪制灰色背景的Renderer
public class MyGL20Renderer implements GLSurfaceView.Renderer {
// 僅調用一次,用于設置view的OpenGLES環境
public void onSurfaceCreated(GL10 unused, EGLConfig config) {
// 設置背景的顏色
GLES20.glClearColor(0.5f, 0.5f, 0.5f, 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);
}
}
這些方法們都具有一個GL10參數,但你使用的卻是OpengGLES 2.0 API們,這其實是為了使Android框架能簡單的兼容各OpenGLES版本而做的。
定義形狀
通過OpenGLES相對Android的坐標系統,可以定義各種形狀。其中三角形是所有形狀的基礎,因為很多復雜的三位圖形都可以看做是大量三角形組成的。因為我們先了解定義三角形,再通過三角形組成正方形。
三角形
定義一個三角形,需要定義這個三角形的三個頂點在三維空間中的坐標。在OpenGL中,典型的方式是為坐標定義一個浮點類型的頂點數組。
為了最高效,你應把這些坐標都寫進一個ByteBuffer,它會被傳到OpenGLES圖形管線以進行處理。
缺省情況下,OpenGLES 假定[0,0,0]是GLSurfaceView 幀的中心,[1,1,0]是右上角,[-1,-1,0]是左下角。坐標的順序為(X, Y, Z)。
class Triangle {
private FloatBuffer vertexBuffer;
// 數組中每個頂點的坐標數
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
};
// 設置顏色,分別為red, green, blue 和alpha (opacity)
float color[] = { 0.63671875f, 0.76953125f, 0.22265625f, 1.0f };
public Triangle() {
// 為存放形狀的坐標,初始化頂點字節緩沖
ByteBuffer bb = ByteBuffer.allocateDirect(
// (坐標數 * 4)float占四字節
triangleCoords.length * 4);
// 設用設備的本點字節序
bb.order(ByteOrder.nativeOrder());
// 從ByteBuffer創建一個浮點緩沖
vertexBuffer = bb.asFloatBuffer();
// 把坐標們加入FloatBuffer中
vertexBuffer.put(triangleCoords);
// 設置buffer,從第一個坐標開始讀
vertexBuffer.position(0);
}
}
此形狀的坐標是按逆時針方向定義的。繪制順序很重要,因為它定義了哪面是形狀的正面,哪面是反面,使用OpenGLES 的cullface特性,你可以只畫正面而不畫反面。
正方形
定義正方形有很多方法,典型的做法是使用兩個三角形。
按照上面的圖形,定義4個關鍵坐標點,以及兩個三角形的繪制順序,即可定義好這個正方形。
class Square {
private FloatBuffer vertexBuffer;
private ShortBuffer drawListBuffer;
// 每個頂點的坐標數
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(
// (坐標數 * 4)
squareCoords.length * 4);
bb.order(ByteOrder.nativeOrder());
vertexBuffer = bb.asFloatBuffer();
vertexBuffer.put(squareCoords);
vertexBuffer.position(0);
// 為繪制列表初始化字節緩沖
ByteBuffer dlb = ByteBuffer.allocateDirect(
// (對應順序的坐標數 * 2)short是2字節
drawOrder.length * 2);
dlb.order(ByteOrder.nativeOrder());
drawListBuffer = dlb.asShortBuffer();
drawListBuffer.put(drawOrder);
drawListBuffer.position(0);
}
}
繪制形狀
OpenGL ES 2.0畫一個定義好的圖形需要很多代碼,典型的需要定義以下幾個東西:
- VertexShader-用于渲染形狀的頂點
- FragmentShader-用于渲染形狀的外觀(顏色或紋理)
- Program-包含了你想要用來繪制一個或多個形狀的Shader。
繪制Program中至少需要保護一個VertexShader和一個FragmentShader。
OpenGLShading Language
如下為OpenGLShading Language (GLSL)代碼,Shader必須經過編譯才能添加到Program中,然后通過Program進行繪制。
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
public static int loadShader(int type, String shaderCode){
// 創建一個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;
}
編譯OpenGLES shader們和鏈接Program們是很耗CPU的,所以你應該避免多次執行。
繪制
一般在形狀類中創建draw()負責繪制。下面的代碼設置位置和顏色值到形狀的VertexShader和FragmentShader,然后執行繪制功能。
private final int vertexCount = triangleCoords.length; // COORDS_PER_VERTEX;
private final int vertexStride = COORDS_PER_VERTEX * 4; // bytes per vertex
public void draw() {
// 將program加入OpenGL ES環境中
GLES20.glUseProgram(mProgram);
// 獲取指向vertex shader的成員vPosition的 handle
mPositionHandle = GLES20.glGetAttribLocation(mProgram, "vPosition");
// 啟用一個指向三角形的頂點數組的handle
GLES20.glEnableVertexAttribArray(mPositionHandle);
// 準備三角形的坐標數據
GLES20.glVertexAttribPointer(mPositionHandle, COORDS_PER_VERTEX,
GLES20.GL_FLOAT, false,
vertexStride, vertexBuffer);
// 獲取指向fragment shader的成員vColor的handle
mColorHandle = GLES20.glGetUniformLocation(mProgram, "vColor");
// 設置三角形的顏色
GLES20.glUniform4fv(mColorHandle, 1, color, 0);
// 畫三角形
GLES20.glDrawArrays(GLES20.GL_TRIANGLES, 0, vertexCount);
// 禁用指向三角形的頂點數組
GLES20.glDisableVertexAttribArray(mPositionHandle);
}
最終只需要在Renderer的onDrawFrame()調用draw()方法,即可完成形狀的繪制。
Square的繪制邏輯和Triangle類似,只需要修改關鍵的繪制方法。
//GLES20.glDrawArrays(GLES20.GL_TRIANGLES, 0, vertexCount);
GLES20.glDrawElements(GLES20.GL_TRIANGLES, drawOrder.length, GLES20.GL_UNSIGNED_SHORT, drawListBuffer);