Learn OpenGLES:顏色漸變

這一貼,是繼上一貼的補充。主要介紹,Shader的頂點屬性數組(即頂點有多個屬性)的情況,以及Shader是如何對頂點渲染顏色的。
我們將以簡單的 長方形爲例, 並在最後討論當手機橫豎屏時,長方形顯示適配的問題。

在上一節中,我們講了如何對三角形進行上色。 它的關鍵點很簡單,就是通過在fragment shader裏定義一個uniform變量(unifrom是圖像管線中的全局變量,在圖形渲染中,其值不會被改變)。在圖形管線中,越是前面的shader越涉及到頂點的操作。 在光柵化之前,經過一些座標變換和燈光的顏色操作(T&L), 着色器通過生成新的頂點,完成對頂點屬性的賦值,這種賦值是通過頂點插值完成的。 如下圖所示,

假如在左邊的頂點定義爲紅色(rgb: 1, 0, 0),右邊頂點定義爲白色(rgb: 1, 1, 1)。 那麼, 左右頂點之間的頂點的顏色就會是一個線性插值滿足如下等時關係:

1
Color_point = rightColor *x + (1-x)*leftColor;
其中, x表示中間的點的位置,這裏我們假定左右點的距離爲1。
在光柵化之後,空間中的這些點被映射到屏幕上,fragment其實就相當於像素, 關於這些像素的位置、顏色信息其實都存在相應的buffer中,然後經過fragment shader和 blending,最終會將具有顏色的像素顯示在屏幕上。 如果,我們在fragment shader中定義了uniform變量,將其賦值給gl_FragColor輸出, 那麼,它將刷新每個fragment的顏色。如果,我們想讓每個fragment都具有自己不同的顏色,通過在vertex shader中定義每個頂點的顏色屬性,可以簡單的 實現這一目的。

修改simple_vertex_shader.glsl:
1
2
3
4
5
6
7
8
9
10
attribute vec4 a_Position;
attribute vec4 a_Color;
         
varying vec4 v_Color;
         
void main()
{
    v_Color = a_Color;
    gl_Position = a_Position;
}
simple_frag_shader.glsl:
1
2
3
4
5
6
7
precision mediump float;
varying vec4 v_Color;
         
void main()
{
    gl_FragColor = v_Color;
}
相應的我們也需要修改MyGLRenderer.java裏的頂點數據:
1
2
3
4
5
6
float[] tableVerticesWithTriangles = {
               //X, Y, Z ,    R,    G,    B
               -0.5f, -0.5f, 1.0f, 0.0f, 0.0f,
                 0f,  0.5f,  0.0f, 1.0f, 0.0f,
               0.5f, -0.5f,   0.0f, 0.0f, 1.0f// Triangle 1
       };
那麼,這個時候,vertex shader中每個頂點就要多個屬性了。 那麼shader有是如何找到上面的頂點數據呢? 

通過上圖我們知道,shader中的頂點屬性與我們輸入的頂點數據之間的關係,vertex shader中定義的頂點屬性是相應的放在一個頂點數組中,每個屬性都分別指向GPU的一段內存。通過以下語句我,可以知道shader中的頂點屬性的這種指向關係。
在onSurfaceCreated最後添加:
1
2
3
4
5
6
7
8
9
10
aColorLocation = glGetAttribLocation(program, A_COLOR);
aPositionLocation = glGetAttribLocation(program, A_POSITION);
         
vertexData.position(0);  // 指向頂點位置數據起始的位置
glVertexAttribPointer(aPositionLocation, NUM_POSITION_COMPONENT, GL_FLOAT, false, STRIDE, vertexData);
glEnableVertexAttribArray(aPositionLocation);
         
vertexData.position(NUM_POSITION_COMPONENT);  // 指向頂點顏色數據起始的位置
glVertexAttribPointer(aColorLocation, NUM_COLOR_COMPONENT, GL_FLOAT, false, STRIDE, vertexData);
glEnableVertexAttribArray(aColorLocation);

即通過vertexData的position 操作修改的數據的起始位置,即指向不同的內存段,而Stride參數表示一個頂點所有屬性佔用的內存。還需記住的一點是,我們輸入的頂點數據在內存中是連續存放的

接着,在MyGLRenderer.java類中定義如下私有變量:

1
2
3
4
5
6
7
private static final int NUM_POSITION_COMPONENT = 2;
private static final int NUM_COLOR_COMPONENT = 3;
         
private static final int STRIDE = (NUM_POSITION_COMPONENT+NUM_COLOR_COMPONENT)*BYTES_PER_FLOAT;
         
private static final String A_COLOR = "a_Color";
private int aColorLocation;

最後,修改onDrawFrame:
1
2
3
4
5
6
public void onDrawFrame(GL10 gl) {
      glClear(GL_COLOR_BUFFER_BIT);
         
      // glUniform4f(uColorLocation, 1.0f, 0.0f, 1.0f, 1.0f);
       glDrawArrays(GL_TRIANGLES, 03);
   }
我們來看一下輸出效果:




由上面的步驟,我們可以總結一下寫opengl的流程: 定義需求--》在shader中添加相應的屬性和算法--》在onSurfaceCreated裏添加獲取shader裏相應屬性的位置信息和數據--》在onDrawFrame裏完成相應的操作。

下面,我們將實現一個小例子: 
這個小例子實現的是一箇中間發光的桌面。 我們將先實現畫一個長方形,然後,通過顏色漸變完成發光效果。由於glDrawArrays的圖形單元中,只接受點,線, 三角形等基本的圖形,因此如果我們想畫一個長方形,只需畫兩個三角形即可。

按照之前總結的流程, 首先修改shader。因爲我們還只是用到兩個頂點屬性,不用其它操作,因此無需更改。 然後, 更改頂點數據: 用兩個三角形畫一個長方形的順序如下:

即按照 1-2-3  3-4-2畫兩個三角形即可。 但是根據需要,我們需要在長方形的中間高亮。 考慮到之前的顏色漸變,我們應該畫如下的圖:


即按照0-1-2, 0-2-3, 0-3-4, 0-4-1, 然後爲每個頂點賦予不同的顏色, 0點的顏色最亮即可。 但是一次畫這麼多三角形需要重複定義每個頂點數據,很費勁。好在opengl爲我們提供了GL_TRIANGLE_FAN,這樣的基本畫法, 這樣我們只需按照0-1-2-3-4-1定義頂點數據即可。如下,

1
2
3
4
5
6
7
// X,     Y,    R,    G,   B
  0f,     0f,   1.0f, 1.0f, 1.0f,
 -0.5f, -0.75f, 0.7f, 0.7f, 0.7f,
 0.5f, -0.75f,  0.7f, 0.7f, 0.7f,
 0.5f, 0.75f,   0.7f, 0.7f, 0.7f,
 -0.5f, 0.75f,  0.7f, 0.7f, 0.7f,
-0.5f, -0.75f,  0.7f, 0.7f, 0.7f,
然後再onDrawFrame裏修改glDrawArrays

1
glDrawArrays(GL_TRIANGLE_FAN, 06);
這次,我們橫豎屏的顯示這一結果



可以發現: 豎屏有些拉長了, 橫屏還好一點。這是因爲我們定義的長方形 長寬比爲1.5:1,而我手機的 長寬比 1.7777:1, 我們已經知道我們所定義的點都會在X, Y軸爲(-1, 1)的範圍內, 這種設備歸一化座標不受屏幕分辨率的影響。但是glViewPort會考慮屏幕分辨率重新把點映射會屏幕座標,如果我們保持寬(X軸)是1的情況下, 那麼在豎屏狀態下,它的高(Y軸)就拉伸了1.77倍。 因此,我們需要對頂點進行操作,即使得Y軸座標能夠縮小1.77倍, 然後再經過設備歸一化,之後經過glViewPort就正常了。

我們可以利用一個被稱爲正交投影的矩陣實現這一步。 百度百科是如此定義正交投影的: 投影線垂直於投影面的投影屬於正交投影 ,也稱爲平行投影。即這種變換不會產生近大遠小的感覺,非常適合於2D的圖像變換。

到這裏,我們需要接觸一些矢量, 矩陣的概念。 OpenGL經常會與這兩個東西打交道。在解決手機橫豎屏適配問題之前,也是爲以後進入3D世界打下基礎。下面幾節,我會主要介紹這兩個概念以及一些關於座標系轉換及OpenGL最終如何成像的問題。


————————————————————————————————————

ARVR技術交流羣:129340649

歡迎加入!


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