本章將學習OpenGL ES中點和線的繪製,在繪製之前需要先了解這些:座標系統;着色器;GLSL(OpenGL ES Shading Language),OpenGL ES着色語言;
座標系統
和android佈局的座標不同,OpenGL是一個右手座標系。簡單來說,就是正x軸在你的右手邊,正y軸朝上,而正z軸是朝向後方的。想象你的屏幕處於三個軸的中心,則正z軸穿過你的屏幕朝向你。座標系畫起來如下:
如果你想要繪製一個在屏幕中心的點時,那麼這個點的座標就是:0,0,0,當你繪製的是不是三維圖像時可以無視z軸(你設置0或者1都不會影響顯示效果)。座標的最大值是1,如果你的座標值大於1的話,這個座標是無法顯示的。
可以參考https://learnopengl-cn.github.io/01%20Getting%20started/08%20Coordinate%20Systems/來學習座標系統,當然還有一些概念,例如正射投影,透視投影,我們暫時用不到用到的時候我們再來學習。
着色器
從我們輸入數據到顯示圖像,這一流程可以查看下圖,
其中需要我們來寫是頂點着色器和片段着色器的代碼。
- 頂點着色程序 - 用於渲染形狀的頂點的 OpenGL ES 圖形代碼。
- 片段着色程序 - 用於使用顏色或紋理渲染形狀面的 OpenGL ES 代碼。
GLSL
GLSL是一種類C語言,用來編寫頂點着色程序和片段着色程序。GLSL語言還是比較複雜的,OpenGL ES2.0的GLSL官方文檔地址是https://www.khronos.org/registry/OpenGL/specs/es/2.0/GLSL_ES_Specification_1.00.pdf,如果英文水平比較高可閱讀參考,否則可用參考http://colin1994.github.io/2017/11/11/OpenGLES-Lesson04/,http://colin1994.github.io/2017/11/12/OpenGLES-Lesson05這兩篇文章,寫的還是不錯的。
變量類型
變量類別 | 變量類型 | 描述 |
---|---|---|
空 | void | 用於無返回值的函數或空的參數列表 |
標量 | float, int, bool | 浮點型,整型,布爾型的標量數據類型 |
浮點型向量 | float, vec2, vec3, vec4 | 包含1,2,3,4個元素的浮點型向量 |
整數型向量 | int, ivec2, ivec3, ivec4 | 包含1,2,3,4個元素的整型向量 |
布爾型向量 | bool, bvec2, bvec3, bvec4 | 包含1,2,3,4個元素的布爾型向量 |
矩陣 | mat2, mat3, mat4 | 尺寸爲2x2,3x3,4x4的浮點型矩陣 |
紋理句柄 | sampler2D, samplerCube | 表示2D,立方體紋理的句柄 |
限定符
限定符 | 描述 |
---|---|
< none: default > | 局部可讀寫變量,或者函數的參數 |
const | 編譯時常量,或只讀的函數參數 |
attribute | 由應用程序傳輸給頂點着色器的逐頂點的數據 |
uniform | 在圖元處理過程中其值保持不變,由應用程序傳輸給着色器 |
varying | 由頂點着色器傳輸給片段着色器中的插值數據 |
先說幾個會用到內置的變量,
gl_Position:只能用在頂點着色程序中,用來定義需要寫入的頂點。
gl_PointSize:定義頂點的大小,單位是像素。設置的越大,點就越大,當然顯示的點時正方形的。
gl_FragColor :用於片段着色程序中,用來設置圖像的顏色。
下面先寫頂點着色程序和片段着色程序,
String vertexShaderCode =
"attribute vec4 aPosition;" +
"void main() {" +
" gl_Position = aPosition;" +
" gl_PointSize = 19.0;" +
"}";
String fragmentShaderCode =
"precision mediump float;" +
"uniform vec4 vColor;" +
"void main() {" +
" gl_FragColor = vColor;" +
"}";
然後我們定義一個頂點的數組,
private final float[] triangleCoords = {
-0.9f, 0.9f, 0.0f,
-0.9f, 0.8f, 0.0f,
-0.8f, -0.1f, 0.0f,
-0.6f, -0.5f, 0.0f,
-0.5f, -0.8f, 0.0f,
-0.4f, 0.4f, 0.0f,
-0.2f, 0.1f, 0.0f,
-0.0f, 0.5f, 0.0f,
0.1f, 0f, 0.0f,
0.3f, -0.5f, 0.0f,
0.4f, -0.2f, 0.0f,
0.6f, -0.5f, 0.0f,
0.9f, -0.6f, 0.0f,
0.9f, -0.9f, 0.0f,
};
定義一個顏色的數組,用來繪製顏色,四個數值分別表示rbga。
// 0 到1 代表 0- 256 如 181 用0.70703125f
private float[] color = {0.70703125f, 0.10546875f, 0.84375f, 1.0f};
在上篇文章中我們顯示了一個窗口,現在我們繪製點和線,在onDrawFrame中進行繪製
首先我們加載着色器,加載着色器的代碼如下
public static int loadShader(int shaderType, String source) {
int shader = GLES20.glCreateShader(shaderType);
checkGlError("glCreateShader type=" + shaderType);
GLES20.glShaderSource(shader, source);
GLES20.glCompileShader(shader);
int[] compiled = new int[1];
GLES20.glGetShaderiv(shader, GLES20.GL_COMPILE_STATUS, compiled, 0);
if (compiled[0] == 0) {
LogUtil.e("Could not compile shader " + shaderType + ":" + GLES20.glGetShaderInfoLog(shader));
GLES20.glDeleteShader(shader);
shader = 0;
}
return shader;
}
主要的幾個方法
glCreateShader:創建shader,參數值:GLES20.GL_VERTEX_SHADER(頂點着色器)或GLES20.GL_FRAGMENT_SHADER(片段着色器),返回創建的shader值。
glShaderSource:加載着色器代碼,參數爲create的shader值和GLSL代碼。
glCompileShader:加載着色器完成。
glGetShaderiv:獲取加載完成的狀態,主要用來檢測是否加載完成,如果加載失敗了則使用glDeleteShader來刪除錯誤的shader。
checkGlError是封裝好的檢測是否有錯誤的方法,內容如下
public static void checkGlError(String op) {
int error = GLES20.glGetError();
if (error != GLES20.GL_NO_ERROR) {
String msg = op + ": glError 0x" + Integer.toHexString(error);
LogUtil.e(msg);
throw new RuntimeException(msg);
}
}
使用glCreateProgram創建一個空的程序,返回一個非0值。使用glAttachShader來連接當前的程序和創建好的shader值,最後使用glLinkProgram來完成鏈接program,如果不需要Program後可以使用glDeleteProgram刪除program。封裝好之後的代碼如下
public static int createProgram(String vertexSource, String fragmentSource) {
int vertexShader = loadShader(GLES20.GL_VERTEX_SHADER, vertexSource);
if (vertexShader == 0) {
return 0;
}
int pixelShader = loadShader(GLES20.GL_FRAGMENT_SHADER, fragmentSource);
if (pixelShader == 0) {
return 0;
}
int program = GLES20.glCreateProgram();
checkGlError("glCreateProgram");
if (program == 0) {
LogUtil.e("Could not create program");
}
GLES20.glAttachShader(program, vertexShader);
checkGlError("glAttachShader");
GLES20.glAttachShader(program, pixelShader);
checkGlError("glAttachShader");
GLES20.glLinkProgram(program);
int[] linkStatus = new int[1];
GLES20.glGetProgramiv(program, GLES20.GL_LINK_STATUS, linkStatus, 0);
if (linkStatus[0] != GLES20.GL_TRUE) {
LogUtil.e("Could not link program: ");
LogUtil.e(GLES20.glGetProgramInfoLog(program));
GLES20.glDeleteProgram(program);
program = 0;
}
return program;
}
封裝了一個OpenGLUtil工具類來進行shader的加載,便於使用。
加載完成後進行頂點的繪製,完整的onDrawFrame代碼如下
@Override
public void onDrawFrame(GL10 gl) {
super.onDrawFrame(gl);
int shaderProgram = OpenGLUtil.createProgram(vertexShaderCode, fragmentShaderCode);
GLES20.glUseProgram(shaderProgram);
int positionHandle = GLES20.glGetAttribLocation(shaderProgram, "aPosition");
GLES20.glEnableVertexAttribArray(positionHandle);
FloatBuffer vertexBuffer = OpenGLUtil.createFloatBuffer(triangleCoords);
GLES20.glVertexAttribPointer(positionHandle, 3, GLES20.GL_FLOAT,
false, 3 * 4, vertexBuffer);
int colorHandle = GLES20.glGetUniformLocation(shaderProgram, "vColor");
GLES20.glUniform4fv(colorHandle, 1, color, 0); // 設置顏色
// 畫點
GLES20.glDrawArrays(GLES20.GL_POINTS, 0, 13); // 畫點
// 設置線寬
GLES20.glLineWidth(18);
// 畫線,不連續的線,例如:有1,2,3,4四個點,1和2是一條線,3,4是一條線
GLES20.glDrawArrays(GLES20.GL_LINES, 2, 4);
// 畫線,封閉的線,例如:有1,2,3,4四個點,1,2,3,4,1會連接2,2連接3,3連接4,4連接1
GLES20.glDrawArrays(GLES20.GL_LINE_LOOP, 6, 4);
// 畫線,不封閉的線
GLES20.glDrawArrays(GLES20.GL_LINE_STRIP, 10, 4);
GLES20.glDisableVertexAttribArray(positionHandle);
}
一些主要方法
glUseProgram:使用創建好的program
glGetAttribLocation:查詢由程序指定的先前鏈接的程序對象,返回aPosition變量頂點屬性的索引
glEnableVertexAttribArray:啓用由索引指定的頂點屬性數組。
createFloatBuffer:封裝的生成FloatBuffer代碼。
glVertexAttribPointer:指定頂點數組的位置和數據格式,幾個參數:index,索引;size,每個頂點的數,當前是3;type,類型,float,short等等,當前是float;normalized,設置爲false;stride,頂點間的偏移,每個頂點3個float值,每個float是4個字節;pointer,頂點的集合。
glGetUniformLocation:獲取片段着色器中屬性的索引。
glUniform4fv:修改統一變量或統一變量數組的值。4fv表示 vec4,同樣的方法有很多:glUniform3fv,glUniform3iv等等,可以參考官網的glUniform文檔。
glDrawArrays:開始繪製。參數mode類型:有GL_POINTS(點),GL_LINES(線),GL_LINE_STRIP,GL_LINE_LOOP,GL_TRIANGLE_STRIP,GL_TRIANGLE_FAN和GL_TRIANGLE;參數first:開始位置;count:總數。
glDisableVertexAttribArray:禁用頂點屬性數組,當繪製完成後可以調用。
顯示效果如下
這一章瞭解了基本的OpenGL ES圖像的繪製方式,下一章會來學習面的顯示。