android 下 opengles 的繪製圖形簡單來講步驟以下:java
浮點型數組,頂點的順序按逆時針排列。android
android 平臺上,app 運行在 jvm 中,內存由 jvm 管理,而 opengles 運行在 native 環境,因此爲了式 opengles 可以訪問圖形頂點數據,須要把頂點拷貝到 native 內存中。git
另外,爲了方便操做 native 中的頂點字節,將其映射到 FloatBuffer 中,而後就能夠像使用數組同樣使用了。github
//頂點,按逆時針順序排列
private static final float[] vertices = {
0.0f, 0.5f, 0.0f,
-0.5f, -0.5f, 0.0f,
0.5f, -0.5f, 0.0f};
public TriangleRender() {
//將頂點數據拷貝映射到 native 內存中,以便opengl可以訪問
verticesBuffer = ByteBuffer
.allocateDirect(vertices.length * BYTES_PER_FLOAT)//直接分配 native 內存,不會被gc
.order(ByteOrder.nativeOrder())//和本地平臺保持一致的字節序(大/小頭)
.asFloatBuffer();//將底層字節映射到FloatBuffer實例,方便使用
verticesBuffer
.put(vertices)//將頂點拷貝到 native 內存中
.position(0);//每次 put position 都會 + 1,須要在繪製前重置爲0
}
複製代碼
着色器的源代碼能夠寫到單獨的文件中,也能夠用字符串拼接(本文使用的方式,爲了方便)數組
頂點着色器數據結構
layout (location = 0)
用來指定該 vPosition 的屬性位置,後面須要使用該位置將頂點數據匹配到 vPosition 上。簡單來講,該着色器的內容就是:使用 opengles3.0 版本,將圖形頂點數據採用4份量向量的數據結構綁定到着色器的第 0 個屬性上,屬性的名字叫 vPosition,着色器執行時,將 vPosition 的值傳給用來表示頂點最終位置的內建變量 gl_Position。app
須要注意的是 layout (location = 0)
不是必需要寫,若是不寫的話,須要在後面着色器綁定到程序以後,程序鏈接以前使用 glBindAttribLocation
方法綁定。jvm
//頂點着色器
private static final String vertextShaderSource =
"#version 300 es\n"
+ "layout (location = 0) in vec4 vPosition;\n"
+ "void main()\n"
+ "{\n"
+ " gl_Position = vPosition;\n"
+ "}\n";
複製代碼
片斷着色器ide
//片斷着色器
private static final String fragmentShaderSource =
"#version 300 es \n"
+ "precision mediump float; \n"
+ "out vec4 fragColor; \n"
+ "void main() \n"
+ "{ \n"
+ " fragColor = vec4 ( 1.0, 0.0, 0.0, 1.0 ); \n"
+ "} \n";
複製代碼
着色器內容編輯完以後,想要使用還要通過編譯。spa
/** * 加載着色器源,並編譯 * * @param type 頂點着色器(GL_VERTEX_SHADER)/片斷着色器(GL_FRAGMENT_SHADER) * @param shaderSource 着色器源(上面編輯的內容) * @return 着色器 */
private int loadShader(int type, String shaderSource) {
//建立着色器對象
int shader = GLES30.glCreateShader(type);
if (shader == 0) return 0;//建立失敗
//加載着色器源
GLES30.glShaderSource(shader, shaderSource);
//編譯着色器
GLES30.glCompileShader(shader);
//檢查編譯狀態
int[] compiled = new int[1];
GLES30.glGetShaderiv(shader, GLES30.GL_COMPILE_STATUS, compiled, 0);
if (compiled[0] == 0) {
Log.e(TAG, GLES30.glGetShaderInfoLog(shader));
GLES30.glDeleteShader(shader);
return 0;//編譯失敗
}
return shader;
}
複製代碼
具體的流程下面代碼很清楚,須要注意的就是若是着色器中沒有指定屬性的位置,則須要調用 glBindAttribLocation
進行指定。
@Override
public void onSurfaceCreated(GL10 gl, EGLConfig config) {
//獲取頂點着色器
int vertextShader = loadShader(GLES30.GL_VERTEX_SHADER, vertextShaderSource);
//獲取片斷着色器
int fragmentShader =loadShader(GLES30.GL_FRAGMENT_SHADER, fragmentShaderSource);
//建立程序
int tmpProgram = GLES30.glCreateProgram();
if (tmpProgram == 0) return;//建立失敗
//綁定着色器到程序
GLES30.glAttachShader(tmpProgram, vertextShader);
GLES30.glAttachShader(tmpProgram, fragmentShader);
//綁定屬性位置 vPosition :0 着色器中沒有設定屬性位置時使用
// GLES30.glBindAttribLocation(tmpProgram, 0, "vPosition");
//鏈接程序
GLES30.glLinkProgram(tmpProgram);
//檢查鏈接狀態
int[] linked = new int[1];
GLES30.glGetProgramiv(tmpProgram,GLES30.GL_LINK_STATUS, linked, 0);
if (linked[0] == 0){
Log.e(TAG, "tmpProgram linked error");
Log.e(TAG, GLES30.glGetProgramInfoLog(tmpProgram));
GLES30.glDeleteProgram(tmpProgram);
return;//鏈接失敗
}
//保存程序,後面使用
program = tmpProgram;
//設置清除渲染時的顏色
GLES30.glClearColor(1.0f, 1.0f, 1.0f, 0.0f);
}
複製代碼
@Override
public void onDrawFrame(GL10 gl) {
//擦除屏幕
GLES30.glClear(GLES30.GL_COLOR_BUFFER_BIT);
//使用程序
GLES30.glUseProgram(program);
//獲取 vPosition 屬性的位置
int vposition = GLES30.glGetAttribLocation(program, "vPosition");
//加載頂點數據到 vPosition 屬性位置
GLES30.glVertexAttribPointer(vposition,3,GLES30.GL_FLOAT,false,0,verticesBuffer);
GLES30.glEnableVertexAttribArray(vposition);
//繪製
GLES30.glDrawArrays(GLES30.GL_TRIANGLES,0,3);
}
複製代碼
本文經過繪製一個三角形,梳理了 opengles 繪製圖形的流程,着色器、程序的使用方法。