跳轉到內容

GLSL 程式設計/GLUT/紋理球體

來自華夏公益教科書
從阿波羅 17 號看到的地球。地球的形狀接近一個相當光滑的球體。

本教程介紹了紋理對映

它是關於 OpenGL 2.x 中 GLSL 著色器紋理的一系列教程中的第一個。在本教程中,我們從球體上的單個紋理貼圖開始。更具體地說,我們將地球表面的影像對映到球體上。在此基礎上,後續教程將涵蓋諸如紋理表面的照明、透明紋理、多紋理、光澤對映等主題。

一個近似球體的三角形網格。
地球表面的影像。水平座標表示經度,垂直座標表示緯度。

紋理對映

[編輯 | 編輯原始碼]

“紋理對映”(或“紋理化”)的基本思想是將影像(即“紋理”或“紋理貼圖”)對映到三角形網格上;換句話說,將平面圖像放到三維形狀的表面上。

為此,定義了“紋理座標”,它們只是指定紋理(即影像)中的位置。水平座標正式稱為S,垂直座標稱為T。但是,通常將它們稱為xy。在動畫和建模工具中,紋理座標通常稱為UV

為了將紋理影像對映到網格,網格的每個頂點都賦予一對紋理座標。(這個過程(以及結果)有時被稱為“UV 對映”,因為每個頂點都對映到 UV 空間中的一個點。)因此,每個頂點都對映到紋理影像中的一個點。然後可以為三角形中三個頂點之間的每個點插值頂點的紋理座標,從而網格中所有三角形的每個點都可以具有一個(插值)紋理座標對。這些紋理座標將網格的每個點對映到紋理貼圖中的特定位置,從而對映到該位置的顏色。因此,渲染紋理對映網格包括對所有可見點執行兩個步驟:插值紋理座標以及在插值紋理座標指定的位置查詢紋理影像的顏色。

在 OpenGL 中,任何有效的浮點數都是有效的紋理座標。但是,當 GPU 被要求查詢紋理影像的畫素(或“紋素”(例如使用下面描述的“texture2D”指令)時,它將在內部將紋理座標對映到 0 到 1 之間的範圍,其方式取決於匯入紋理時指定的“包裝模式”:包裝模式“repeat”基本上使用紋理座標的小數部分來確定 0 到 1 之間的紋理座標。另一方面,包裝模式“clamp”將紋理座標鉗制到此範圍。然後,使用 0 到 1 之間的這些內部紋理座標來確定紋理影像中的位置: 指定紋理影像的左下角; 右下角; 左上角;等等。

紋理球體

[編輯 | 編輯原始碼]

要將地球表面的影像對映到球體上,首先要載入影像。為此,請使用 OpenGL 程式設計教程 06中解釋的 SOIL。

  glActiveTexture(GL_TEXTURE0);
  GLuint texture_id = SOIL_load_OGL_texture
    (
     "Earthmap720x360_grid.jpg",
     SOIL_LOAD_AUTO,
     SOIL_CREATE_NEW_ID,
     SOIL_FLAG_INVERT_Y | SOIL_FLAG_TEXTURE_REPEATS
     );

SOIL_FLAG_TEXTURE_REPEATS將在使用 [0, 1] 之外的紋理座標時使紋理重複自身。

頂點著色器

attribute vec3 v_coord;
varying vec4 texCoords;
uniform mat4 m,v,p;

void main(void) {
    mat4 mvp = p*v*m;
    gl_Position = mvp * vec4(v_coord, 1.0);
    texCoords = vec4(v_coord, 1.0);
}

片段著色器

varying vec4 texCoords;
uniform sampler2D mytexture;

void main(void) {
    vec2 longitudeLatitude = vec2((atan(texCoords.y, texCoords.x) / 3.1415926 + 1.0) * 0.5,
                                  (asin(texCoords.z) / 3.1415926 + 0.5));
        // processing of the texture coordinates;
        // this is unnecessary if correct texture coordinates are specified by the application

    gl_FragColor = texture2D(mytexture, longitudeLatitude);
        // look up the color of the texture image specified by the uniform "mytexture"
        // at the position specified by "longitudeLatitude.x" and
        // "longitudeLatitude.y" and return it in "gl_FragColor"
}

如果一切順利,紋理影像現在應該出現在球體上。恭喜!

它是如何工作的

[編輯 | 編輯原始碼]

由於許多技術使用紋理對映,因此瞭解這裡發生了什麼非常有益。因此,讓我們回顧一下著色器程式碼。

球體物件的頂點來自 FreeGLUT glutSolidSphere 函式。我們將在片段著色器中需要它們,以便將它們轉換為紋理影像空間中的紋理座標。

然後,頂點著色器將每個頂點的紋理座標寫入 varying 變數 texCoords。對於三角形的每個片段(即每個覆蓋的畫素),對該 varying 在三個三角形頂點處的值進行插值(參見 “光柵化”中的描述),並將插值紋理座標傳遞給片段著色器。然後,片段著色器使用它們在由 uniform mytexture 指定的紋理影像中查詢紋理空間中插值位置的顏色,並將該顏色返回到 gl_FragColor 中,然後將其寫入幀緩衝區並在螢幕上顯示。

在這種情況下,我們以演算法方式生成紋理座標,但通常它們是透過您的 3D 建模器指定的,並作為額外的頂點屬性傳遞。

為了理解其他教程中介紹的更復雜的紋理對映技術,您必須對這些步驟有一個很好的瞭解。

重複和移動紋理

[編輯 | 編輯原始碼]

在某些 3D 框架中,您可能已經遇到了平鋪偏移引數,每個引數都有一個x和一個y分量。這些引數允許您重複紋理(透過縮小紋理座標空間中的紋理影像)並在表面上移動紋理影像(透過在紋理座標空間中偏移它)。為了再現這種行為,必須定義另一個 uniform。

uniform vec4 mytexture_ST; // tiling and offset parameters

我們可以為每個紋理指定一個這樣的 vec4 uniform。(記住:“S”和“T”是紋理座標的正式名稱,它們通常稱為“U”和“V”或“x”和“y”。)此 uniform 在 mytexture_ST.xmytexture_ST.y 中儲存平鋪引數的xy分量,而偏移引數的xy分量儲存在 mytexture_ST.wmytexture_ST.z 中。uniform 應該像這樣使用

gl_FragColor = texture2D(mytexture, mytexture_ST.xy * texCoords.xy + mytexture_ST.zw);

這使得著色器表現得像內建著色器。在其他教程中,為了使著色器程式碼更簡潔,通常不實現此功能。

為了完整起見,以下是具有此功能的新片段著色器程式碼

varying vec4 texCoords;
uniform sampler2D mytexture;
uniform vec4 mytexture_ST; // tiling and offset parameters

void main(void) {
    vec2 longitudeLatitude = vec2((atan(texCoords.y, texCoords.x) / 3.1415926 + 1.0) * 0.5,
                                  (asin(texCoords.z) / 3.1415926 + 0.5));

    // Apply tiling and offset
    vec2 texCoordsTransformed = longitudeLatitude * mytexture_ST.xy + mytexture_ST.zw;

    gl_FragColor = texture2D(mytexture, texCoordsTransformed);
}

例如,您可以嘗試複製所有大陸(水平縮放 2x 以檢視兩次紋理 - 確保您的紋理是GL_REPEAT的),並縮小北極(從 -0.05 垂直開始以縮小頂部)

  glUniform4f(uniform_mytexture_ST, 2,1, 0,-.05);

您已經完成了最重要的教程之一。我們已經瞭解了

  • 如何匯入紋理影像以及如何將其附加到著色器的紋理屬性。
  • 頂點著色器和片段著色器如何協同工作將紋理影像對映到網格上。
  • 紋理的平鋪和偏移引數如何工作以及如何實現它們。

進一步閱讀

[編輯 | 編輯原始碼]

如果您想了解更多

  • 關於頂點著色器和片段著色器的資料流進出(即頂點屬性、varying 等),您應該閱讀 “OpenGL ES 2.0 管道”的描述。
  • 關於片段著色器中不同變數的插值,您應該閱讀關於 “光柵化” 的討論。


< GLSL 程式設計/GLUT

除非另有說明,本頁的所有示例原始碼均已進入公有領域。
返回 OpenGL 程式設計 - 光照部分 返回 GLSL 程式設計 - GLUT 部分
華夏公益教科書