開始正式學習OpenGL ES開發!java
本博客是我在學習過程當中作的記錄,也但願和各位分享個人學習過程,若有錯誤,歡迎留言指正,共同窗習。編程
開始繪製圖形以前,咱們必須先給OpenGL輸入一些頂點數據。OpenGL是一個3D圖形庫,因此咱們在OpenGL中指定的全部座標都是3D座標(x、y和z)。OpenGL不是簡單地把全部的3D座標變換爲屏幕上的2D像素;OpenGL僅當3D座標在3個軸(x、y和z)上都爲-1.0到1.0的範圍內時才處理它。全部在所謂的標準化設備座標(Normalized Device Coordinates)範圍內的座標纔會最終呈如今屏幕上(在這個範圍之外的座標都不會顯示)。數組
因爲咱們但願渲染一個三角形,咱們一共要指定三個頂點,每一個頂點都有一個3D位置。咱們會將它們以標準化設備座標的形式(OpenGL的可見區域)定義爲一個float
數組。ide
private float[] vertexPoints = new float[]{
0.0f, 0.5f, 0.0f,
-0.5f, -0.5f, 0.0f,
0.5f, -0.5f, 0.0f
};
複製代碼
因爲OpenGL是在3D空間中工做的,而咱們渲染的是一個2D三角形,咱們將它頂點的z座標設置爲0.0。這樣子的話三角形每一點的深度都是同樣的,從而使它看上去像是2D的。學習
定義這樣的頂點數據之後,咱們會把它做爲輸入發送給圖形渲染管線的第一個處理階段:頂點着色器。它會在GPU上建立內存用於儲存咱們的頂點數據,還要配置OpenGL如何解釋這些內存,而且指定其如何發送給顯卡。頂點着色器接着會處理咱們在內存中指定數量的頂點。this
一旦你的頂點座標已經在頂點着色器中處理過,它們就應該是標準化設備座標了,標準化設備座標是一個x、y和z值在-1.0到1.0的一小段空間。任何落在範圍外的座標都會被丟棄/裁剪,不會顯示在你的屏幕上。下面你會看到咱們定義的在標準化設備座標中的三角形(忽略z軸):spa
與一般的屏幕座標不一樣,y軸正方向爲向上,(0, 0)座標是這個圖像的中心,而不是左上角。最終你但願全部(變換過的)座標都在這個座標空間中,不然它們就不可見了。3d
由於OpenGL
做爲本地系統庫運行在系統中,虛擬機須要分配本地內存,供其存取。code
public SimpleRenderer() {
//分配內存空間,每一個浮點型佔4字節空間
vertexBuffer = ByteBuffer.allocateDirect(vertexPoints.length * 4)
.order(ByteOrder.nativeOrder())
.asFloatBuffer();
//傳入指定的座標數據
vertexBuffer.put(vertexPoints);
vertexBuffer.position(0);
}
複製代碼
/** * 頂點着色器 */
private String vertextShader =
"#version 300 es\n" +
"layout (location = 0) in vec4 vPosition;\n" +
"void main() {\n" +
" gl_Position = vPosition;\n" + //
" gl_PointSize = 10.0;\n" +
"}\n";
複製代碼
輸入屬性的數組
(一個名爲vPosition
的4份量向量),layout (location = 0)
表示這個變量的位置是頂點屬性0。orm
將vPosition
輸入屬性拷貝到名爲gl_Position
的特殊輸出變量。
將浮點數據10.0
拷貝到gl_PointSize
的變量中。
/** * 片斷着色器 */
private String fragmentShader =
"#version 300 es\n" +
"precision mediump float;\n" +
"out vec4 fragColor;\n" +
"void main() {\n" +
" fragColor = vec4(1.0,1.0,1.0,1.0);\n" +
"}\n";
複製代碼
聲明着色器中浮點變量的默認精度。
着色器聲明一個輸出變量fragColor
,這個是一個4份量的向量。
表示將顏色值(1.0,1.0,1.0,1.0)
,輸出到顏色緩衝區。
/** * 編譯 * * @param type 頂點着色器:GLES30.GL_VERTEX_SHADER * 片斷着色器:GLES30.GL_FRAGMENT_SHADER */
private static int compileShader(int type, String shaderCode) {
//建立一個着色器
final int shaderId = GLES30.glCreateShader(type);
if (shaderId != 0) {
//加載到着色器
GLES30.glShaderSource(shaderId, shaderCode);
//編譯着色器
GLES30.glCompileShader(shaderId);
//檢測狀態
final int[] compileStatus = new int[1];
GLES30.glGetShaderiv(shaderId, GLES30.GL_COMPILE_STATUS, compileStatus, 0);
if (compileStatus[0] == 0) {
String logInfo = GLES30.glGetShaderInfoLog(shaderId);
System.err.println(logInfo);
//建立失敗
GLES30.glDeleteShader(shaderId);
return 0;
}
return shaderId;
} else {
//建立失敗
return 0;
}
}
複製代碼
/** * 連接 * * @param vertexShaderId 頂點着色器 * @param fragmentShaderId 片斷着色器 */
public static int linkProgram(int vertexShaderId, int fragmentShaderId) {
final int programId = GLES30.glCreateProgram();
if (programId != 0) {
//將頂點着色器加入到程序
GLES30.glAttachShader(programId, vertexShaderId);
//將片元着色器加入到程序中
GLES30.glAttachShader(programId, fragmentShaderId);
//連接着色器程序
GLES30.glLinkProgram(programId);
final int[] linkStatus = new int[1];
//驗證OpenGL程序是否可用
GLES30.glGetProgramiv(programId, GLES30.GL_LINK_STATUS, linkStatus, 0);
if (linkStatus[0] == 0) {
String logInfo = GLES30.glGetProgramInfoLog(programId);
System.err.println(logInfo);
GLES30.glDeleteProgram(programId);
return 0;
}
return programId;
} else {
//建立失敗
return 0;
}
}
複製代碼
準備工做結束,下來就開始繪製圖形了。
public class SimpleRenderer implements GLSurfaceView.Renderer 複製代碼
實現GLSurfaceView.Renderer
接口
@Override
public void onSurfaceCreated(GL10 gl10, EGLConfig eglConfig) {
//設置背景顏色
GLES30.glClearColor(0.5f, 0.5f, 0.5f, 0.5f);
// 編譯頂點着色器
final int vertexShaderId = compileShader(GLES30.GL_VERTEX_SHADER, vertextShader);
// 編譯片斷着色器
final int fragmentShaderId = compileShader(GLES30.GL_FRAGMENT_SHADER, fragmentShader);
//在OpenGLES環境中使用程序
GLES30.glUseProgram(linkProgram(vertexShaderId, fragmentShaderId));
}
複製代碼
@Override
public void onSurfaceChanged(GL10 gl10, int width, int height) {
//設置視圖窗口
GLES30.glViewport(0, 0, width, height);
}
複製代碼
@Override
public void onDrawFrame(GL10 gl10) {
//把顏色緩衝區設置爲咱們預設的顏色
GLES30.glClear(GLES30.GL_COLOR_BUFFER_BIT);
//準備座標數據
GLES30.glVertexAttribPointer(0, 3, GLES30.GL_FLOAT, false, 0, vertexBuffer);
//啓用頂點的句柄
GLES30.glEnableVertexAttribArray(0);
//繪製三個點
// GLES30.glDrawArrays(GLES30.GL_POINTS, 0, 3);
//繪製直線
// GLES30.glDrawArrays(GLES30.GL_LINE_STRIP, 0, 2);
// GLES30.glLineWidth(10);
//繪製三角形
GLES30.glDrawArrays(GLES30.GL_TRIANGLES, 0, 3);
//禁止頂點數組的句柄
GLES30.glDisableVertexAttribArray(0);
}
複製代碼
經過glDrawArrays
方法來執行最後的繪製,GL_POINTS
表明繪製的類型(圖元類型),而參數0,1
則表明繪製的點的範圍,它是一個左閉右開的區間。
圖元類型 | 描述 |
---|---|
GL_POINTS | 點精靈圖元,對指定的每一個頂點進行繪製。 |
GL_LINES | 繪製一系列不相連的線段。 |
GL_LINE_STRIP | 繪製一系列相連的線段。 |
GL_LINE_LOOP | 繪製一系列相連的線段,首尾相連。 |
GL_TRIANGLES | 繪製一系列單獨的三角形。 |
GL_TRIANGLE_STRIP | 繪製一系列相互鏈接的三角形。 |
GL_TRIANGLE_FAN | 繪製一系列相互鏈接的三角形 |
public class SimpleRenderer implements GLSurfaceView.Renderer {
private float[] vertexPoints = new float[]{
0.0f, 0.5f, 0.0f,
-0.5f, -0.5f, 0.0f,
0.5f, -0.5f, 0.0f
};
private final FloatBuffer vertexBuffer;
/** * 頂點着色器 */
private String vertextShader =
"#version 300 es\n" +
"layout (location = 0) in vec4 vPosition;\n" +
"void main() {\n" +
" gl_Position = vPosition;\n" +
" gl_PointSize = 10.0;\n" +
"}\n";
/** * 片斷着色器 */
private String fragmentShader =
"#version 300 es\n" +
"precision mediump float;\n" +
"out vec4 fragColor;\n" +
"void main() {\n" +
" fragColor = vec4(1.0,1.0,1.0,1.0);\n" +
"}\n";
public SimpleRenderer() {
//分配內存空間,每一個浮點型佔4字節空間
vertexBuffer = ByteBuffer.allocateDirect(vertexPoints.length * 4)
.order(ByteOrder.nativeOrder())
.asFloatBuffer();
//傳入指定的座標數據
vertexBuffer.put(vertexPoints);
vertexBuffer.position(0);
}
@Override
public void onSurfaceCreated(GL10 gl10, EGLConfig eglConfig) {
GLES30.glClearColor(0f, 0f, 0f, 0f);
final int vertexShaderId = compileShader(GLES30.GL_VERTEX_SHADER, vertextShader);
final int fragmentShaderId = compileShader(GLES30.GL_FRAGMENT_SHADER, fragmentShader);
GLES30.glUseProgram(linkProgram(vertexShaderId, fragmentShaderId));
}
@Override
public void onSurfaceChanged(GL10 gl10, int width, int height) {
GLES30.glViewport(0, 0, width, height);
}
@Override
public void onDrawFrame(GL10 gl10) {
GLES30.glClear(GLES30.GL_COLOR_BUFFER_BIT);
//準備座標數據
GLES30.glVertexAttribPointer(0, 3, GLES30.GL_FLOAT, false, 0, vertexBuffer);
//啓用頂點的句柄
GLES30.glEnableVertexAttribArray(0);
//繪製三個點
GLES30.glDrawArrays(GLES30.GL_LINE_LOOP, 0, 3);
//繪製直線
// GLES30.glDrawArrays(GLES30.GL_LINE_STRIP, 0, 2);
// GLES30.glLineWidth(10);
//繪製三角形
// GLES30.glDrawArrays(GLES30.GL_TRIANGLES, 0, 3);
//禁止頂點數組的句柄
GLES30.glDisableVertexAttribArray(0);
}
/** * 編譯 */
private static int compileShader(int type, String shaderCode) {
//建立一個着色器
final int shaderId = GLES30.glCreateShader(type);
if (shaderId != 0) {
//加載到着色器
GLES30.glShaderSource(shaderId, shaderCode);
//編譯着色器
GLES30.glCompileShader(shaderId);
//檢測狀態
final int[] compileStatus = new int[1];
GLES30.glGetShaderiv(shaderId, GLES30.GL_COMPILE_STATUS, compileStatus, 0);
if (compileStatus[0] == 0) {
String logInfo = GLES30.glGetShaderInfoLog(shaderId);
System.err.println(logInfo);
//建立失敗
GLES30.glDeleteShader(shaderId);
return 0;
}
return shaderId;
} else {
//建立失敗
return 0;
}
}
/** * 連接 */
public static int linkProgram(int vertexShaderId, int fragmentShaderId) {
final int programId = GLES30.glCreateProgram();
if (programId != 0) {
//將頂點着色器加入到程序
GLES30.glAttachShader(programId, vertexShaderId);
//將片元着色器加入到程序中
GLES30.glAttachShader(programId, fragmentShaderId);
//連接着色器程序
GLES30.glLinkProgram(programId);
final int[] linkStatus = new int[1];
GLES30.glGetProgramiv(programId, GLES30.GL_LINK_STATUS, linkStatus, 0);
if (linkStatus[0] == 0) {
String logInfo = GLES30.glGetProgramInfoLog(programId);
System.err.println(logInfo);
GLES30.glDeleteProgram(programId);
return 0;
}
return programId;
} else {
//建立失敗
return 0;
}
}
}
public class MainActivity extends AppCompatActivity {
private GLSurfaceView mGLSurfaceView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setupViews();
}
private void setupViews() {
mGLSurfaceView = new GLSurfaceView(this);
setContentView(mGLSurfaceView);
mGLSurfaceView.setEGLContextClientVersion(3);
GLSurfaceView.Renderer renderer = new SimpleRenderer();
mGLSurfaceView.setRenderer(renderer);
}
}
複製代碼
參考:
《OpenGL ES 3.0 編程指南第2版》
《OpenGL 編程指南》(原書第八版)
《OpenGL ES應用開發實踐指南Android卷》