快速入門
關(guān)鍵類
- GLSurfaceView
- 繪制的載體
- 小部分區(qū)域時可使用TextureView
- 理論上可使用SurfaceView
- GLSurfaceView.Renderer
- 實質(zhì)的繪制動作
授權(quán) | |
---|---|
登錄 | 是 |
簽名 | 是 |
構(gòu)鍵一個GLSurfaceView對象
class MyGLSurfaceView extends GLSurfaceView {
public MyGLSurfaceView(Context context){
super(context);
// 創(chuàng)建一個OpenGL ES 2.0 context
setEGLContextClientVersion(2);
// 設(shè)置Renderer到GLSurfaceView
setRenderer(new MyRenderer());
// 只有在繪制數(shù)據(jù)改變時才繪制view
setRenderMode(GLSurfaceView.RENDERMODE_WHEN_DIRTY);
}
}
GLSurfaceView.RENDERMODE_WHEN_DIRTY
會阻止繪制GLSurfaceView的幀谦纱,直到你調(diào)用了requestRender()看成,這樣會非常高效。
繪制灰色背景的Renderer
public class MyGL20Renderer implements GLSurfaceView.Renderer {
// 僅調(diào)用一次跨嘉,用于設(shè)置view的OpenGLES環(huán)境
public void onSurfaceCreated(GL10 unused, EGLConfig config) {
// 設(shè)置背景的顏色
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參數(shù)川慌,但你使用的卻是OpengGLES 2.0 API們,這其實是為了使Android框架能簡單的兼容各OpenGLES版本而做的。
定義形狀
通過OpenGLES相對Android的坐標系統(tǒng),可以定義各種形狀梦重。其中三角形是所有形狀的基礎(chǔ)兑燥,因為很多復雜的三位圖形都可以看做是大量三角形組成的。因為我們先了解定義三角形琴拧,再通過三角形組成正方形降瞳。
三角形
定義一個三角形,需要定義這個三角形的三個頂點在三維空間中的坐標蚓胸。在OpenGL中力崇,典型的方式是為坐標定義一個浮點類型的頂點數(shù)組。
為了最高效赢织,你應(yīng)把這些坐標都寫進一個ByteBuffer,它會被傳到OpenGLES圖形管線以進行處理馍盟。
缺省情況下于置,OpenGLES 假定[0,0,0]是GLSurfaceView 幀的中心,[1,1,0]是右上角贞岭,[-1,-1,0]是左下角八毯。坐標的順序為(X, Y, Z)。
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
};
// 設(shè)置顏色瞄桨,分別為red, green, blue 和alpha (opacity)
float color[] = { 0.63671875f, 0.76953125f, 0.22265625f, 1.0f };
public Triangle() {
// 為存放形狀的坐標话速,初始化頂點字節(jié)緩沖
ByteBuffer bb = ByteBuffer.allocateDirect(
// (坐標數(shù) * 4)float占四字節(jié)
triangleCoords.length * 4);
// 設(shè)用設(shè)備的本點字節(jié)序
bb.order(ByteOrder.nativeOrder());
// 從ByteBuffer創(chuàng)建一個浮點緩沖
vertexBuffer = bb.asFloatBuffer();
// 把坐標們加入FloatBuffer中
vertexBuffer.put(triangleCoords);
// 設(shè)置buffer,從第一個坐標開始讀
vertexBuffer.position(0);
}
}
此形狀的坐標是按逆時針方向定義的芯侥。繪制順序很重要泊交,因為它定義了哪面是形狀的正面,哪面是反面柱查,使用OpenGLES 的cullface特性廓俭,你可以只畫正面而不畫反面。
正方形
定義正方形有很多方法唉工,典型的做法是使用兩個三角形研乒。
按照上面的圖形,定義4個關(guān)鍵坐標點淋硝,以及兩個三角形的繪制順序雹熬,即可定義好這個正方形。
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(
// (坐標數(shù) * 4)
squareCoords.length * 4);
bb.order(ByteOrder.nativeOrder());
vertexBuffer = bb.asFloatBuffer();
vertexBuffer.put(squareCoords);
vertexBuffer.position(0);
// 為繪制列表初始化字節(jié)緩沖
ByteBuffer dlb = ByteBuffer.allocateDirect(
// (對應(yīng)順序的坐標數(shù) * 2)short是2字節(jié)
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必須經(jīng)過編譯才能添加到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;" +
"}";
可使用如下的靜態(tài)方法編譯Shader
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;
}
編譯OpenGLES shader們和鏈接Program們是很耗CPU的,所以你應(yīng)該避免多次執(zhí)行僧界。
繪制
一般在形狀類中創(chuàng)建draw()負責繪制侨嘀。下面的代碼設(shè)置位置和顏色值到形狀的VertexShader和FragmentShader,然后執(zhí)行繪制功能捂襟。
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環(huán)境中
GLES20.glUseProgram(mProgram);
// 獲取指向vertex shader的成員vPosition的 handle
mPositionHandle = GLES20.glGetAttribLocation(mProgram, "vPosition");
// 啟用一個指向三角形的頂點數(shù)組的handle
GLES20.glEnableVertexAttribArray(mPositionHandle);
// 準備三角形的坐標數(shù)據(jù)
GLES20.glVertexAttribPointer(mPositionHandle, COORDS_PER_VERTEX,
GLES20.GL_FLOAT, false,
vertexStride, vertexBuffer);
// 獲取指向fragment shader的成員vColor的handle
mColorHandle = GLES20.glGetUniformLocation(mProgram, "vColor");
// 設(shè)置三角形的顏色
GLES20.glUniform4fv(mColorHandle, 1, color, 0);
// 畫三角形
GLES20.glDrawArrays(GLES20.GL_TRIANGLES, 0, vertexCount);
// 禁用指向三角形的頂點數(shù)組
GLES20.glDisableVertexAttribArray(mPositionHandle);
}
最終只需要在Renderer的onDrawFrame()調(diào)用draw()方法咬腕,即可完成形狀的繪制。
Square的繪制邏輯和Triangle類似葬荷,只需要修改關(guān)鍵的繪制方法涨共。
//GLES20.glDrawArrays(GLES20.GL_TRIANGLES, 0, vertexCount);
GLES20.glDrawElements(GLES20.GL_TRIANGLES, drawOrder.length, GLES20.GL_UNSIGNED_SHORT, drawListBuffer);