跳轉到內容

GLSL 程式設計/GLUT/透明紋理

來自華夏公益教科書
地球地圖,水體透明,即水的alpha分量為0,陸地為1。

本教程涵蓋了alpha紋理貼圖的各種常見用法,即RGBA紋理影像,其中A(alpha)分量指定了紋理的透明度。

它結合了紋理球體教程中的著色器程式碼,以及切面教程透明度教程中介紹的概念。

如果您尚未閱讀這些教程,現在是一個很好的機會。

丟棄透明片段

[編輯 | 編輯原始碼]

讓我們從丟棄片段開始,如切面教程中所述。按照紋理球體教程中描述的步驟,將左側的影像分配給球體的材質,使用以下片段著色器(保持相同的頂點著色器)

varying vec4 texCoords;
uniform sampler2D mytexture;
float cutoff = 0.1;

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

    gl_FragColor = texture2D(mytexture, longitudeLatitude);

    if (gl_FragColor.a < cutoff)
        // alpha value less than user-specified threshold?
    {
        discard; // yes: discard this fragment
    }
}

(您可以將cutoff變數作為uniform傳遞。)

如果您現在啟動應用程式,片段著色器應該讀取RGBA紋理,並將alpha值與變數cutoff中指定的閾值進行比較。如果alpha值小於閾值,則片段將被丟棄,表面將顯示為透明。

由於我們可以透過透明部分,因此有意義的是,不要啟用背面剔除,如切面教程中所述。

Alpha 測試

[編輯 | 編輯原始碼]

OpenGL(非ES)有一個固定功能特性,類似於模板緩衝區,可以透過glAlphaFunc根據片段的alpha值接受或丟棄片段。

    glAlphaFunc(GL_GREATER, 0.1);
    glEnable(GL_ALPHA_TEST);

透明度教程描述瞭如何使用alpha混合來渲染半透明物體。

讓我們提醒一下,正確的透明度支援需要按距離相機排序三角形,因為我們必須停用深度測試才能在透明三角形“後面”寫入。為了避免對單個球形物件排序三角形,我們使用剔除來先繪製背面,然後繪製正面。

因此,獲取RGBA紋理,紋理球體著色器,以及以下OpenGL配置

glDisable(GL_DEPTH_TEST);
glEnable(GL_CULL_FACE);
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);

glCullFace(GL_FRONT);
glutSolidSphere(1.0,30,30);

glCullFace(GL_BACK);
glutSolidSphere(1.0,30,30);

值得一提的是,這個特定的紋理影像只包含0或1的alpha值。因此,由於鄰近紋理的alpha值的插值,只有相對較少的片段接收介於0和1之間的alpha值。只有對於這些片段,渲染順序才很重要。如果一個人接受這些片段的潛在渲染偽影,則可以透過啟用深度測試來提高著色器的效能

glEnable(GL_DEPTH_TEST);

請注意,所有alpha值為0的紋理在本特定紋理影像中都是黑色的。實際上,該紋理影像中的顏色是“預乘”它們的alpha值。(這種顏色也稱為“透明度加權”。)因此,對於這個特定的影像,我們實際上應該為預乘的顏色指定混合方程,以避免在混合方程中將顏色與其alpha值再次相乘。因此,著色器的改進(對於這個特定的紋理影像)是採用以下混合規範

glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
半透明球體通常用於徽標和預告片。

自定義顏色混合

[編輯 | 編輯原始碼]

在結束本教程之前,我們不應該省略對所述技術的更實際的應用。左側是帶有半透明藍色海洋的地球影像,我在維基共享資源上找到了它。有一些燈光(或輪廓增強)正在進行,我沒有嘗試複製。相反,我只是嘗試用以下著色器來複制半透明海洋的基本思想,該著色器忽略了紋理貼圖的RGB顏色,並根據alpha值用特定顏色替換它們

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));

    gl_FragColor = texture2D(mytexture, longitudeLatitude);

    if (gl_FragColor.a > 0.5) // opaque 
    {
        gl_FragColor = vec4(0.0, 1.0, 0.0, 1.0); // opaque green
    }
    else // transparent 
    {
        gl_FragColor = vec4(0.0, 0.0, 0.5, 0.7); // semitransparent dark blue
    }
}

當然,為該著色器新增燈光和輪廓增強將會很有趣。也可以更改不透明的綠色顏色,以考慮紋理顏色,例如使用

gl_FragColor = vec4(0.5 * gl_FragColor.r, 2.0 * gl_FragColor.g, 0.5 * gl_FragColor.b, 1.0);

這透過將綠色分量乘以來強調綠色分量,並將紅色和藍色分量乘以來使它們變暗。但是,這會導致綠色過飽和,被鉗制到最大強度。可以透過將綠色分量與最大強度1之間的差值減半來避免這種情況。這個差值是1.0 - gl_FragColor.g;它的一半是0.5 * (1.0 - gl_FragColor.g),與該縮減後的距離到最大強度對應的值為:1.0 - 0.5 * (1.0 - gl_FragColor.g)。因此,為了避免綠色過飽和,我們可以使用(而不是不透明的綠色顏色)

gl_FragColor = vec4(0.5 * gl_FragColor.r, 1.0 - 0.5 * (1.0 - gl_FragColor.g), 0.5 * gl_FragColor.b, 1.0);

在實踐中,人們必須嘗試各種可能性來進行這種顏色轉換。為此,使用數值著色器屬性(例如,上面一行中的因子0.5)特別有用,可以互動地探索各種可能性。

恭喜!您已經完成了這個相當長的教程。我們已經瞭解了

  • 如何將丟棄片段與alpha紋理貼圖結合使用。
  • 如何使用alpha測試來實現相同的效果。
  • 如何將alpha紋理貼圖用於混合。
  • 如何使用alpha紋理貼圖來確定顏色。

進一步閱讀

[編輯 | 編輯原始碼]

如果您還想了解更多


< GLSL 程式設計/GLUT

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