轉載請註明出處: http://blog.csdn.net/lb377463323/article/details/52136518
OpenGL ES簡介
- OpenGL ES是一個爲便攜式或嵌入式設備例如:移動電話、監視器等發展的3D繪圖API。
- 在Android框架中有兩個基礎類用於使用OpenGL ES創建和處理圖形
- GLSurfaceView類是OpenGL ES繪製圖形的view容器
- GLSurfaceView.Renderer類是用於控制上述view容器中顯示什麼內容
Android使用OpenGL ES繪製三角形
- 整個過程比較繁瑣,代碼也挺多的。因爲 OpenGL ES 2.0 是以可編程着色器爲基礎的,這意味着你繪製任何圖形都必須有一
個合適的着色器裝載和綁定,比使用固定管線的桌面版本有更多代碼。
1. 在manifest中聲明使用OpenGL ES 2.0 API
<uses-feature android:glEsVersion="0x00020000" android:required="true" />
0x00020000代表OpenGL ES 2.0版本,3.0版本是0x00030000,3.1版本是0x00030001
若應用需要使用紋理壓縮(紋理壓縮通過減少內存需求和更有效的使用內存帶寬使OpenGL ES應用的性能顯著增加)功能,還需在manifest聲明所支持的壓縮格式,如下:
2. 將Activity的ContentView設爲GLSurfaceView
public class OpenGLES20Activity extends Activity {
private GLSurfaceView mGLView;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mGLView = new MyGLSurfaceView(this);
setContentView(mGLView);
}
}
3. 構造GLSurfaceView對象
class MyGLSurfaceView extends GLSurfaceView {
private final MyGLRenderer mRenderer;
public MyGLSurfaceView(Context context){
super(context);
// 創建OpenGL ES 2.0的上下文
setEGLContextClientVersion(2);
mRenderer = new MyGLRenderer();
//設置Renderer用於繪圖
setRenderer(mRenderer);
//只有在繪製數據改變時才繪製view,可以防止GLSurfaceView幀重繪
setRenderMode(GLSurfaceView.RENDERMODE_WHEN_DIRTY);
}
}
4. 構造GLSurfaceView.Renderer對象
Renderer類提供三個回調方法供Android系統調用,用來計算在GLSurfaceView中繪製什麼以及如何繪製。
- onSurfaceCreated():僅調用一次,用於設置view的OpenGL ES環境
- onDrawFrame():每次重繪view時調用
- onSurfaceChanged():當view的幾何形狀發生變化時調用,比如設備屏幕方向改變時
public class MyGLRenderer implements GLSurfaceView.Renderer {
public void onSurfaceCreated(GL10 unused, EGLConfig config) {
//設置背景色(r,g,b,a)
GLES20.glClearColor(1.0f, 1.0f, 1.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);
}
}
5. 在定義三角形之前需瞭解Android和OpenGL ES的座標系
- Android屏幕左上角爲原點,往右爲X正方向,往下爲Y正方向
- Android屏幕的中心點座標爲OpenGL的原點,方向如下圖,並且最大和最小值爲1和-1
- 也就是說OpenGL的(-1,1,0)爲屏幕的左上角,
6. 定義一個三角形
public class Triangle {
private FloatBuffer vertexBuffer;
//設置每個頂點的座標數
static final int COORDS_PER_VERTEX = 3;
//設置三角形頂點數組
static float triangleCoords[] = { //默認按逆時針方向繪製
0.0f, 1.0f, 0.0f, // 頂點
-1.0f, -0.0f, 0.0f, // 左下角
1.0f, -0.0f, 0.0f // 右下角
};
// 設置三角形顏色和透明度(r,g,b,a)
float color[] = {0.0f, 1.0f, 0f, 1.0f};//綠色不透明
public Triangle() {
// 初始化頂點字節緩衝區,用於存放形狀的座標
ByteBuffer bb = ByteBuffer.allocateDirect(
//(每個浮點數佔用4個字節
triangleCoords.length * 4);
//設置使用設備硬件的原生字節序
bb.order(ByteOrder.nativeOrder());
//從ByteBuffer中創建一個浮點緩衝區
vertexBuffer = bb.asFloatBuffer();
// 把座標都添加到FloatBuffer中
vertexBuffer.put(triangleCoords);
//設置buffer從第一個座標開始讀
vertexBuffer.position(0);
}
}
7. 在MyGLRenderer中初始化三角形形狀
private Triangle mTriangle;
public void onSurfaceCreated(GL10 unused, EGLConfig config) {
...
//初始化三角形
mTriangle = new Triangle();
}
8. 繪製三角形形狀
使用OpenGL ES 2.0繪製一個定義好的形狀需要大量的代碼,因爲必須提供給圖形渲染管道很多細節信息
- VertexShader:用於渲染形狀的頂點的OpenGL ES圖形代碼
- FragmentShader:用於渲染形狀的外觀(顏色或紋理)的OpenGL ES代碼
- Program:一個OpenGL ES對象,包含了你想要用來繪製一個或多個形狀的shader
至少需要一個vertex shader來繪製一個形狀和一個fragment shader來爲形狀着色
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 Shading Language(GLSL)代碼,必須在OpenGL ES環境下先編譯再使用。想要編譯這些代碼,需要在你的Renderer類中創建一個工具類方法:
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;
}
在三角形類中編譯shader代碼,並將它們添加到一個OpenGL ES program 對象中,然後鏈接這個program
public class Triangle() {
...
private final int mProgram;
public Triangle() {
...
// 編譯shader代碼
int vertexShader = MyGLRenderer.loadShader(GLES20.GL_VERTEX_SHADER,
vertexShaderCode);
int fragmentShader = MyGLRenderer.loadShader(GLES20.GL_FRAGMENT_SHADER,
fragmentShaderCode);
// 創建空的OpenGL ES Program
mProgram = GLES20.glCreateProgram();
// 將vertex shader添加到program
GLES20.glAttachShader(mProgram, vertexShader);
// 將fragment shader添加到program
GLES20.glAttachShader(mProgram, fragmentShader);
// 創建可執行的 OpenGL ES program
GLES20.glLinkProgram(mProgram);
}
}
在三角形類中創建一個draw()方法負責繪製形狀
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環境中
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);
// Draw the triangle
GLES20.glDrawArrays(GLES20.GL_TRIANGLES, 0, vertexCount);
// 禁用指向三角形的頂點數組
GLES20.glDisableVertexAttribArray(mPositionHandle);
}
9. 在Render類中的onDrawFrame()方法中調用draw()方法
public void onDrawFrame(GL10 unused) {
...
mTriangle.draw();
}
此時運行程序顯示如下圖:
到這裏,三角形已繪製完畢,下一篇介紹添加動作及觸摸事件。