OpenGL 學習系列---基本形狀的繪製

glumes發表於2018-05-25

在之前的一篇部落格中,講述了 OpenGL 繪製一個點的流程及相關的程式碼,其中關於 OpenGL 程式編譯部分都是可以在其他專案中接著複用的,接下來會講到如何去繪製其他的基本圖元。

繪製直線

兩點確定一條直線,顯然繪製一條直線是需要兩個頂點資料的。

定義如下頂點資料,繪製一條螢幕對角線的直線:

    float[] lineVertex = {
            -0.5f, 0.5f,
            0.5f, -0.5f
    };
複製程式碼

而我們的頂點著色器和片段著色器也會發生一些變化,最終還是繪製一條單一顏色的直線。

頂點著色器程式碼:

attribute vec4 a_Position;
void main(){
    gl_Position = a_Position;
}
複製程式碼

由於是繪製的直線,相比於繪製點,就沒有了 gl_PointSize變數來確定大小了。

片段著色器程式碼:

precision mediump float;
uniform vec4 u_Color;
void main()
{
    gl_FragColor = u_Color;
}
複製程式碼

依舊是純色,目前還沒有涉及到顏色的變化。

而在我們給著色器變數繫結資料時,依舊是通過glGetUniformLocationglGetAttribLocation方法給uniformattribute型別的u_Colora_Position變數賦值。

最後還是通過glDrawArrays方法執行繪製。

glDrawArrays(GL_LINES, 0, 2);
複製程式碼

GL_LINES代表繪製的型別是直線,而0,2則是繪製的頂點範圍。

繪製三角形

繪製三角形和繪製直線基本差不多,從兩個點的直線變成了三個點的三角形。

頂點資料也發生了相應的改動,假設如下的資料,注意要以逆時針定義資料。

    float[] triangleVertex = {
            -0.5f, 0.5f,
            -0.5f, -0.5f,
            0.5f, -0.5f
    };
複製程式碼

那麼最後繪製時,繪製型別也會發生變化了,頂點個數範圍也會變化。

 glDrawArrays(GL_TRIANGLES, 0, 3);
複製程式碼

繪製三角形結果如圖:

https://user-gold-cdn.xitu.io/2018/5/25/16397321d0679086?w=141&h=240&f=png&s=10948

繪製矩形

顯然,OpenGL 是沒有提供矩形這一基本圖元的,但是我們可以用兩個三角形來拼接成一個矩形。

OpenGL 中提供了一個繪製型別叫做三角形扇,如下圖所示:

https://user-gold-cdn.xitu.io/2018/5/25/16397321d2908ce6?w=253&h=240&f=png&s=32939

在上圖中,矩形的每一條邊上的頂點都被兩個三角形使用了,而且中心的頂點被所有四個三角形使用了。

我們不必輸入四個三角形的頂點資料來繪製四個三角形從而組成矩形,可以告訴 OpenGL 重用那些頂點資料,把這些頂點作為一個三角形扇繪製。

一個三角形扇以一箇中心頂點作為起始,使用相鄰的兩個頂點建立第一個三角形,接下來的每個頂點都會建立一個三角形,圍繞起始的中心點按扇形展開,為了使扇形閉合,我們需要在最後重複第二個點。

所以,以三角形扇的形式繪製一個矩形,我們可以重新定義矩形的頂點資料:

   float[] rectangleVertex = {
		   // 第一個點就是三角形扇的中心點
            0f,    0f, 
            -0.5f, -0.8f,   
            0.5f, -0.8f,   
            0.5f,  0.8f,   
            -0.5f,  0.8f,   
            -0.5f, -0.8f
          // 重複第二個點,使三角形扇閉合
    };
複製程式碼

而著色器程式碼依舊不變,繪製一個純色的矩形,繪製程式碼如下:

  glDrawArrays(GL_TRIANGLE_FAN, 0, 6);
複製程式碼

繪製型別為GL_TRIANGLE_FAN,頂點數量也變成了 6 個。

那麼問題來了,OpenGL 到底為我們提供哪些繪製方式呢?如下表所示:

繪製型別 繪製方式
GL_POINTS 將傳入的頂點座標作為單獨的點繪製
GL_LINES 將傳入的座標作為單獨線條繪製,ABCDEFG六個頂點,繪製AB、CD、EF三條線
GL_LINE_STRIP 將傳入的頂點作為折線繪製,ABCD四個頂點,繪製AB、BC、CD三條線
GL_LINE_LOOP 將傳入的頂點作為閉合折線繪製,ABCD四個頂點,繪製AB、BC、CD、DA四條線。
GL_TRIANGLES 將傳入的頂點作為單獨的三角形繪製,ABCDEF繪製ABC,DEF兩個三角形
GL_TRIANGLE_STRIP 將傳入的頂點作為三角條帶繪製,ABCDEF繪製ABC,BCD,CDE,DEF四個三角形
GL_TRIANGLE_FAN 將傳入的頂點作為扇面繪製,ABCDEF繪製ABC、ACD、ADE、AEF四個三角形

繪製圓形

現在我們要繪製一個圓形,顯然 OpenGL 是沒有提供圓形的繪製型別的,這就要用到上面提供的繪製方式了。

以繪製一個實心的圓形為例子:

有了上面三角形扇繪制矩形的例子,我們按照同樣的思想,把一個圓形分成多個三角形組成,如下圖所示:

https://user-gold-cdn.xitu.io/2018/5/25/16397321d31717eb?w=640&h=208&f=jpeg&s=17716

我們分的三角形越多,三角形個數趨向於無限大的時候,整個圖案也就越趨向於圓。

這樣一來,頂點資料就不能再靠手寫了。

    // 圓形分割的數量,分成 360 份,可由 360 個線段組成空心圓,也可以由 360 個三角形組成實心圓
    public static final int VERTEX_DATA_NUM = 360;
    // 360 個頂點的位置,因為有 x 和 y 座標,所以 double 一下,再加上中心點 和 閉合的點
    float[] circleVertex = new float[VERTEX_DATA_NUM * 2 + 4];
    // 分成 360 份,每一份的弧度
    float radian = (float) (2 * Math.PI / VERTEX_DATA_NUM);
    // 繪製的半徑
    float radius = 0.8f;
    
    // 初始化圓形的頂點資料
    private void initVertexData() {
        // 中心點
        circleVertex[0] = 0f;
        circleVertex[1] = 0f;
        // 圓的 360 份的頂點資料
        for (int i = 0; i < VERTEX_DATA_NUM; i++) {
            circleVertex[2 * i + 2] = (float) (radius * Math.cos(radian * i));
            circleVertex[2 * i + 1 + 2] = (float) (radius * Math.sin(radian * i));
        }
        // 閉合點
        circleVertex[VERTEX_DATA_NUM * 2 + 2] = (float) (radius * Math.cos(radian));
        circleVertex[VERTEX_DATA_NUM * 2 + 3] = (float) (radius * Math.sin(radian));
    }
複製程式碼

把圓分成了 360 份。圓形的頂點資料也分為了三部分了,以原心作為我們的中心點,中間的 360 個點用來繪製三角形,最後一個點使得我們的圖形閉合。

在繪製時依舊使用三角形扇的形式來繪製。

	// 要把頂點資料個數對應上
    glDrawArrays(GL_TRIANGLE_FAN, 0, VERTEX_DATA_NUM + 2);
複製程式碼

當然,在繪製圓形時,我們也可以不單獨定義原點和閉合點,直接使用圓形的 360 個頂點來繪製,最終的結果依舊會是一個圓形。

https://user-gold-cdn.xitu.io/2018/5/25/16397321d08f9c63?w=150&h=240&f=png&s=12937

當然,我們也可以使用其他的繪製型別,比如直線,來繪製一個空心的圓形。

還是上面定義的頂點資料,但是我們只用其中分割成 360 份的那部分頂點資料就好了,也就是去掉首位兩個點,然後把這個 360 個點依次連線繪製成圓形。

        glDrawArrays(GL_LINE_LOOP, 1, VERTEX_DATA_NUM );
複製程式碼

hhttps://user-gold-cdn.xitu.io/2018/5/25/16397321d090986b?w=148&h=240&f=png&s=13447

這樣就完成一個圓形的繪製。

正多邊形的繪製

在繪製圓形的基礎上,我們還可以進行擴充一下。

要知道,最後我們的圓形實際上是一個正多邊形來趨近於圓形的,只是肉眼難以觀察到了,畢竟它是一個正三百六十邊形...

那麼假設我們要繪製正五邊形、正六邊形、正七邊形呢?

實際上也很簡單,只要把圓分成五份、六份、七份就好了。

展示一些繪製圖如下:

正五邊形:

https://user-gold-cdn.xitu.io/2018/5/25/16397321d07eaffe?w=151&h=240&f=png&s=14003

正六邊形:

https://user-gold-cdn.xitu.io/2018/5/25/1639732223b71ae4?w=153&h=240&f=png&s=13277

正七邊形:

https://user-gold-cdn.xitu.io/2018/5/25/16397322260ba3a5?w=150&h=240&f=png&s=14160

小結

到此,基本講述了 OpenGL 的繪製流程以及基本圖形的繪製。

根據圖形和繪製型別來採用以何種方式進行繪製,以及定義頂點資料,最後直接繪製對應圖形即可。

但顯然,這還是不夠的,還是有很多問題的。

想要繪製一個圓形,結果卻成了橢圓;想要繪製一個正五邊形,卻成了歪的;這到底是道德的淪喪還是人性的泯滅,一切的揭曉就在下一篇部落格中了。

具體程式碼詳情,可以參考我的 Github 專案: https://github.com/glumes/AndroidOpenGLTutorial

參考

1、http://blog.csdn.net/junzia/article/details/52818488

最後,如果覺得文章不錯,歡迎關注微信公眾號:【紙上淺談】

紙上淺談

相關文章