跳轉到內容

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

來自華夏公益教科書,開放的書籍,開放的世界
地球地圖,水是透明的,即水的 Alpha 分量為 0,陸地的 Alpha 分量為 1。

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

它將紋理球體教程的著色器程式碼與截面教程透明度教程中介紹的概念相結合。

如果您還沒有閱讀這些教程,這將是一個很好的閱讀機會。

丟棄透明片段

[編輯 | 編輯原始碼]

讓我們從截面教程中解釋的丟棄片段開始。按照紋理球體教程中描述的步驟,將左側的影像分配到具有以下著色器的球體的材質

import bge
 
cont = bge.logic.getCurrentController()
 
VertexShader = """
   varying vec4 texCoords; // texture coordinates at this vertex
 
   void main()
   {
      texCoords = gl_MultiTexCoord0; // in this case equal to gl_Vertex
      gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;
   }
"""
 
FragmentShader = """
   uniform float cutoff;

   varying vec4 texCoords; 
      // interpolated texture coordinates for this fragment
   uniform sampler2D textureUnit; 
      // a small integer identifying a texture image
 
   void main()
   {
      vec2 longitudeLatitude = vec2(
         (atan(texCoords.y, texCoords.x) / 3.1415926 + 1.0) * 0.5, 
         1.0 - acos(texCoords.z) / 3.1415926);
         // processing of the texture coordinates; 
         // this is unnecessary if correct texture coordinates 
         // are specified within Blender
 
      gl_FragColor = texture2D(textureUnit, longitudeLatitude);

      if (gl_FragColor.a < cutoff)
         // alpha value less than user-specified threshold?
      {
         discard; // yes: discard this fragment
      }
   }
"""
 
mesh = cont.owner.meshes[0]
for mat in mesh.materials:
    shader = mat.getShader()
    if shader != None:
        if not shader.isValid():
            shader.setSource(VertexShader, FragmentShader, 1)
            shader.setSampler('textureUnit', 0)
            shader.setUniform1f('cutoff', cont.owner.get('cutoff'))

此指令碼使用名為cutoff的遊戲屬性來控制相同名稱的統一變數。如果您啟動遊戲,Blender 應該在系統控制檯中(可以透過幫助 > 切換系統控制檯資訊視窗的選單中獲取)抱怨在以下行中缺少浮點數

shader.setUniform1f('cutoff', cont.owner.get('cutoff'))

因為我們還沒有定義遊戲屬性。要做到這一點,請轉到邏輯編輯器並單擊新增遊戲屬性(如果未開啟,請按n開啟屬性)。名稱應為cutoff,型別應為float。將值設定為(任何略微遠離 0 和 1 的值都可以)。

如果您現在啟動遊戲引擎,片段著色器應該讀取 RGBA 紋理並比較 Alpha 值與遊戲屬性cutoff中指定的閾值。如果 Alpha 值小於閾值,則片段將被丟棄,表面將顯示為透明。

由於我們可以透過透明的部分,因此可以像截面教程中描述的那樣停用背面剔除:在資訊視窗的選單中選擇Blender 遊戲引擎,然後(在球體的屬性視窗材質選項卡中)取消選中遊戲設定 > 背面剔除。(注意,即使未在材質選項卡中選中透明度,通常的深度測試也會確保三角形遮擋是正確的。)

透明度教程描述瞭如何使用 Alpha 混合渲染半透明物體。將此與 RGBA 紋理相結合,得到以下片段著色器(頂點著色器與上面相同)

import bge
 
cont = bge.logic.getCurrentController()
 
VertexShader = """
   varying vec4 texCoords; // texture coordinates at this vertex
 
   void main()
   {
      texCoords = gl_MultiTexCoord0; // in this case equal to gl_Vertex
      gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;
   }
"""
 
FragmentShader = """
   uniform float cutoff;

   varying vec4 texCoords; 
      // interpolated texture coordinates for this fragment
   uniform sampler2D textureUnit; 
      // a small integer identifying a texture image
 
   void main()
   {
      vec2 longitudeLatitude = vec2(
         (atan(texCoords.y, texCoords.x) / 3.1415926 + 1.0) * 0.5, 
         1.0 - acos(texCoords.z) / 3.1415926);
 
      gl_FragColor = texture2D(textureUnit, longitudeLatitude);
   }
"""
 
mesh = cont.owner.meshes[0]
for mat in mesh.materials:
    shader = mat.getShader()
    if shader != None:
        if not shader.isValid():
            shader.setSource(VertexShader, FragmentShader, 1)
            shader.setSampler('textureUnit', 0)
            mat.setBlending(bge.logic.BL_SRC_ALPHA, 
                            bge.logic.BL_ONE_MINUS_SRC_ALPHA)

不幸的是,如果您停用背面剔除,即使啟用了透明度 > Z 透明度(在屬性視窗材質選項卡中),您也會在 Blender 2.63 中看到嚴重的渲染偽像。顯然,Blender 2.63 並不總是根據深度對所有三角形進行排序,這在這種情況下是一個問題。但是,將遊戲設定 > Alpha 混合更改為Alpha 排序將告訴 Blender 對該材質的所有三角形進行排序,從而糾正該問題。(這應該只在需要時使用,因為排序成本很高,通常沒有必要)

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

mat.setBlending(bge.logic.BL_ONE, bge.logic.BL_ONE_MINUS_SRC_ALPHA)

半透明球體通常用於徽標和預告片。

混合自定義顏色

[編輯 | 編輯原始碼]

我們不應該在沒有對所介紹技術的更實際應用的情況下結束本教程。左側是地球的影像,帶有半透明的藍色海洋,我在 Wikimedia Commons 上找到的。有一些照明(或輪廓增強),我沒有嘗試再現。相反,我只嘗試使用以下著色器再現半透明海洋的基本思想,該著色器忽略了紋理圖的 RGB 顏色,並根據 Alpha 值用特定顏色替換它們

import bge
 
cont = bge.logic.getCurrentController()
 
VertexShader = """
   varying vec4 texCoords; // texture coordinates at this vertex
 
   void main()
   {
      texCoords = gl_MultiTexCoord0; // in this case equal to gl_Vertex
      gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;
   }
"""
 
FragmentShader = """
   uniform float cutoff;

   varying vec4 texCoords; 
      // interpolated texture coordinates for this fragment
   uniform sampler2D textureUnit; 
      // a small integer identifying a texture image
 
   void main()
   {
      vec2 longitudeLatitude = vec2(
         (atan(texCoords.y, texCoords.x) / 3.1415926 + 1.0) * 0.5, 
         1.0 - acos(texCoords.z) / 3.1415926);
 
      gl_FragColor = texture2D(textureUnit, 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
      }
   }
"""
 
mesh = cont.owner.meshes[0]
for mat in mesh.materials:
    shader = mat.getShader()
    if shader != None:
        if not shader.isValid():
            shader.setSource(VertexShader, FragmentShader, 1)
            shader.setSampler('textureUnit', 0)
            mat.setBlending(bge.logic.BL_SRC_ALPHA, 
                            bge.logic.BL_ONE_MINUS_SRC_ALPHA)

如果您停用背面剔除,您會注意到 Blender 2.63 中存在渲染偽像。希望這個問題會在未來的版本中得到改善。

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

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 紋理圖來確定顏色。

進一步閱讀

[編輯 | 編輯原始碼]

如果您仍然想知道更多


< GLSL 程式設計/Blender

除非另有說明,否則本頁上的所有示例原始碼均授予公有領域。
華夏公益教科書