Android開發OpenGL ES的流程,從着色器編寫到顯示在屏幕上

創建頂點着色器和片段着色器

頂點着色器代碼示例:

attribute vec4 a_Position;

void main()
{
    gl_Position = a_Position;
}

gl_Position爲當前頂點的最終位置

片段着色器代碼示例:

precision mediump float;

uniform ver4 u_Color;

void main()
{
    gl_FragColor = u_Color;
}

gl_FragColor爲當前片段最終輸出的顏色

編譯着色器

因爲有頂點着色器和片段着色器,所以需要編譯兩次

  • 創建着色器對象,type值爲GL_VERTEX_SHADER和GL_FRAGMENT_SHADER,分別代表頂點着色器和片段着色器類型
final int shaderObjectId = glCreateShader(type);
  • 加載着色器代碼,shaderCode是前面寫的頂點着色器和片段着色器代碼,分別將其轉換成String類型即可
glShaderSource(shaderObjectId, shaderCode);
  • 編譯着色器
glCompileShader(shaderObjectId);

現在頂點着色器和片段着色器都編譯好了,我們之後只需要通過shaderObjectId來調用對應的着色器對象即可使用。

將着色器連接到OpenGL ES的program中

前面創建好了頂點着色器對象和片段着色器對象,使用時兩者需要一起工作,OpenGL ES就是通過program來將兩者鏈接到一起

鏈接program的過程和編譯着色器的過程很類似,如下:

  • 創建program對象
final int programObjectId = glCreateProgram();
  • 附上着色器,vertexShaderId和fragmentShaderId就是前面編譯生成的着色器對象的ID
glAttachShader(programObjectId, vertexShaderId);
glAttachShader(programObjectId, fragmentShaderId);
  • 鏈接program
glLinkProgram(programObjectId);
  • 告訴OpenGL ES使用此program程序
glUseProgram(programObjectId);

到這裏OpenGL ES繪製任何東西到屏幕上都是使用此program程序,這時你問了那我們是怎麼設置頂點座標和片段顏色的呢?

設置頂點座標和片段顏色

我們在頂點着色器和片段着色器中分別定義了一下兩個變量a_Position和u_Color,頂點着色器設置當前頂點的最終座標位置gl_Position = a_Position;片段着色器設置當前片段的最終顏色gl_FragColor = u_Color;當OpenGL ES把兩個着色器鏈接成一個程序時,會給所有的變量都分配一個位置,我們需要獲取這些位置來設置頂點座標和片段顏色。

  • 首先獲取頂點着色器的a_Position位置

我們在Renderer類中定義兩個實例變量,一個是所需獲取的着色器中的變量名,一個是代表此變量在program中的位置

private static final String A_POSITION = "a_Position";
private int aPositionLocation;

然後在Renderer的onSurfaceCreated()方法中獲取位置

aPositionLocation = glGetAttribLocation(program, A_POSITION);
  • 獲取片段着色器的u_Color位置

同樣在Renderer類中定義兩個實例變量

private static final String U_COLOR = "u_Color";
private int uColorLocation;

然後在Renderer的onSurfaceCreated()方法中獲取位置

uColorLocation = glGetUniformLocation(program, U_COLOR);

現在我們獲取到了所需屬性的位置,通過這個位置我們就可以設置OpenGL ES的頂點座標和片段顏色了

  • 定義頂點數據緩衝區,我們先畫個三角形

這裏是每兩個數據代表一個頂點的(x,y)座標,屏幕座標系如下,屏幕中間爲原點,(-1, -1)是屏幕的左下角,(1, -1)是右下角,(0, 1)在中上位置

float[] triangleVertices = {
    -1.0f, -1.0f,
    1.0f, -1.0f,
    0.0f, 1.0f
};

因爲OpenGL ES作爲本地系統庫直接運行中硬件上的,所以我們的頂點數據也希望不能被垃圾回收,因此我們使用jni方式,但是爲了方便我們還可以使用另一種方式NIO,

private final FloatBuffer vertexData;
vertexData = ByteBuffer
    .allocateDirect(triangleVertices.length * 4)
    .order(ByteOrder.nativeOrder())
    .asFloatBuffer();
vertexData.put(triangleVertices);

現在頂點數組就會從虛擬機拷貝到本地內存中,不會受到垃圾回收,當然當進程結束時這塊內存會被釋放掉。

關聯頂點數據和頂點屬性

現在頂點數據和OpenGL ES中的頂點位置都有了,我們還需要將兩者關聯起來,也就是讓OpenGL ES找到頂點座標的最終位置。

我們在Renderer的onSurfaceCreated方法中加入以下代碼:

//讓vertexData內部的指針指向數據的開頭
vertexData.position(0);處,否則可能有bug
//aPositionLocation代表對應OpenGL ES中的屬性位置,2代表這個屬性每次讀取幾個數據,GL_FLOAT代表數據類型,false表示我們暫時忽略此參數,0表示跨度,這裏就連着讀取數據,vertexData就是告訴OpenGL ES去哪讀數據
glVertexAttribPointer(aPositionLocation, 2, GL_FLOAT, false, 0, vertexData);

注意這個2,它是表示每次從vertexData讀兩個數據表示一個頂點,但是前面a_Position的類型是vec4,也就說每個頂點需要四個分量,而我們只傳了2個數據,那麼剩下的兩個分量會使用默認值(0,1),所以最後頂點的座標是(-1,-1,0,1),(1,-1,0,1),(0,1,0,1),對於0我覺得比較好理解,就是Z軸爲0,但是最後的是1怎麼說呢,它是用來做透視除法的,以後再細說,現在知道默認爲1就行

現在OpenGL ES的program程序已經和頂點數據關聯起來了,現在還需要使能頂點數據,也就是讓OpenGL ES開始更新頂點數據

在glVertexAttribPointer後面加入下述代碼

glEnableVertexAttribArray(aPositionLocation);

設置片段着色器輸出的顏色,也就是三角形的顏色

後面四個參數代表RGBA,此例爲紅色

在onDrawFrame()中加入下述代碼

glUniform4f(uColorLocation, 1.0f, 0.0f, 0.0f, 0.0f);

最後繪製出來三角形

在onDrawFrame()尾部加入下述代碼

//GL_TRIANGLES代表繪製三角形,GL_POINTS代表繪製點,GL_LINES代表繪製直線,0表示從頂點數組的起始位置讀頂點,3表示讀3個頂點,因爲一個三角形有三個頂點
glDrawArrays(GL_TRIANGLES, 0, 3);

現在運行起來屏幕上就會顯示紅色三角形了

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章