跳轉到內容

Cg 程式設計/Unity/透明紋理

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

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

它將“紋理球體”部分的著色器程式碼與在“切除”部分“透明度”部分中介紹的概念相結合。

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

丟棄透明片段

[編輯 | 編輯原始碼]

讓我們從“切除”部分中解釋的丟棄片段開始。按照“紋理球體”部分中描述的步驟,並將左側的影像分配給帶有以下著色器的球體的材質

Shader "Cg texturing with alpha discard" {
   Properties {
      _MainTex ("RGBA Texture Image", 2D) = "white" {} 
      _Cutoff ("Alpha Cutoff", Float) = 0.5
   }
   SubShader {
      Pass {    
         Cull Off // since the front is partially transparent, 
            // we shouldn't cull the back

         CGPROGRAM
 
         #pragma vertex vert  
         #pragma fragment frag 
 
         uniform sampler2D _MainTex;    
         uniform float _Cutoff;
 
         struct vertexInput {
            float4 vertex : POSITION;
            float4 texcoord : TEXCOORD0;
         };
         struct vertexOutput {
            float4 pos : SV_POSITION;
            float4 tex : TEXCOORD0;
         };
 
         vertexOutput vert(vertexInput input) 
         {
            vertexOutput output;
 
            output.tex = input.texcoord;
            output.pos = UnityObjectToClipPos(input.vertex);
            return output;
         }

         float4 frag(vertexOutput input) : COLOR
         {
            float4 textureColor = tex2D(_MainTex, input.tex.xy);  
            if (textureColor.a < _Cutoff)
               // alpha value less than user-specified threshold?
            {
               discard; // yes: discard this fragment
            }
            return textureColor;
         }
 
         ENDCG
      }
   }
   Fallback "Unlit/Transparent Cutout"
}

片段著色器讀取 RGBA 紋理並將 alpha 值與使用者指定的閾值進行比較。如果 alpha 值小於閾值,則丟棄該片段,表面看起來透明。

請注意,discard 指令在某些平臺上速度相當慢,尤其是在移動裝置上。因此,混合通常是一個更有效的替代方案。

“透明度”部分描述瞭如何使用 alpha 混合渲染半透明物體。將此與 RGBA 紋理相結合,得到以下程式碼

Shader "Cg texturing with alpha blending" {
   Properties {
      _MainTex ("RGBA Texture Image", 2D) = "white" {} 
   }
   SubShader {
      Tags {"Queue" = "Transparent"}

      Pass {	
         Cull Front // first render the back faces
         ZWrite Off // don't write to depth buffer 
            // in order not to occlude other objects
         Blend SrcAlpha OneMinusSrcAlpha 
            // blend based on the fragment's alpha value
         
         CGPROGRAM
 
         #pragma vertex vert  
         #pragma fragment frag 
 
         uniform sampler2D _MainTex;
 
         struct vertexInput {
            float4 vertex : POSITION;
            float4 texcoord : TEXCOORD0;
         };
         struct vertexOutput {
            float4 pos : SV_POSITION;
            float4 tex : TEXCOORD0;
         };
 
         vertexOutput vert(vertexInput input) 
         {
            vertexOutput output;
 
            output.tex = input.texcoord;
            output.pos = UnityObjectToClipPos(input.vertex);
            return output;
         }

         float4 frag(vertexOutput input) : COLOR
         {
            return tex2D(_MainTex, input.tex.xy);  
         }
 
         ENDCG
      }

      Pass {	
         Cull Back // now render the front faces
         ZWrite Off // don't write to depth buffer 
            // in order not to occlude other objects
         Blend SrcAlpha OneMinusSrcAlpha 
            // blend based on the fragment's alpha value
         
         CGPROGRAM
 
         #pragma vertex vert  
         #pragma fragment frag 
 
         uniform sampler2D _MainTex;
 
         struct vertexInput {
            float4 vertex : POSITION;
            float4 texcoord : TEXCOORD0;
         };
         struct vertexOutput {
            float4 pos : SV_POSITION;
            float4 tex : TEXCOORD0;
         };
 
         vertexOutput vert(vertexInput input) 
         {
            vertexOutput output;
 
            output.tex = input.texcoord;
            output.pos = UnityObjectToClipPos(input.vertex);
            return output;
         }

         float4 frag(vertexOutput input) : COLOR
         {
            return tex2D(_MainTex, input.tex.xy);  
         }
 
         ENDCG
      }
   }
   Fallback "Unlit/Transparent"
}

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

混合一 一減源alpha

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

使用自定義顏色混合

[編輯 | 編輯原始碼]

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

Shader "Cg semitransparent colors based on alpha" {
   Properties {
      _MainTex ("RGBA Texture Image", 2D) = "white" {} 
   }
   SubShader {
      Tags {"Queue" = "Transparent"}

      Pass {	
         Cull Front // first render the back faces
         ZWrite Off // don't write to depth buffer 
            // in order not to occlude other objects
         Blend SrcAlpha OneMinusSrcAlpha 
            // blend based on the fragment's alpha value
         
         CGPROGRAM
 
         #pragma vertex vert  
         #pragma fragment frag 
 
         uniform sampler2D _MainTex;
 
         struct vertexInput {
            float4 vertex : POSITION;
            float4 texcoord : TEXCOORD0;
         };
         struct vertexOutput {
            float4 pos : SV_POSITION;
            float4 tex : TEXCOORD0;
         };
 
         vertexOutput vert(vertexInput input) 
         {
            vertexOutput output;
 
            output.tex = input.texcoord;
            output.pos = UnityObjectToClipPos(input.vertex);
            return output;
         }

         float4 frag(vertexOutput input) : COLOR
         {
            float4 color =  tex2D(_MainTex, input.tex.xy);  
            if (color.a > 0.5) // opaque back face?
            {
               color = float4(0.0, 0.0, 0.2, 1.0); 
                  // opaque dark blue
            }
            else // transparent back face?
            {
               color = float4(0.0, 0.0, 1.0, 0.3); 
                  // semitransparent green
            }
            return color;
         }
 
         ENDCG
      }

      Pass {	
         Cull Back // now render the front faces
         ZWrite Off // don't write to depth buffer 
            // in order not to occlude other objects
         Blend SrcAlpha OneMinusSrcAlpha 
            // blend based on the fragment's alpha value
         
         CGPROGRAM
 
         #pragma vertex vert  
         #pragma fragment frag 
 
         uniform sampler2D _MainTex;
 
         struct vertexInput {
            float4 vertex : POSITION;
            float4 texcoord : TEXCOORD0;
         };
         struct vertexOutput {
            float4 pos : SV_POSITION;
            float4 tex : TEXCOORD0;
         };
 
         vertexOutput vert(vertexInput input) 
         {
            vertexOutput output;
 
            output.tex = input.texcoord;
            output.pos = UnityObjectToClipPos(input.vertex);
            return output;
         }

         float4 frag(vertexOutput input) : COLOR
         {
            float4 color = tex2D(_MainTex, input.tex.xy);  
            if (color.a > 0.5) // opaque front face?
            {
               color = float4(0.0, 1.0, 0.0, 1.0); 
                  // opaque green
            }
            else // transparent front face
            {
               color = float4(0.0, 0.0, 1.0, 0.3); 
                  // semitransparent dark blue
            }
            return color;
        }
 
         ENDCG
      }
   }
   Fallback "Unlit/Transparent"
}

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

color = float4(0.5 * color.r, 2.0 * color.g, 0.5 * color.b, 1.0);

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

color = float4(0.5 * color.r, 1.0 - 0.5 * (1.0 - color.g), 0.5 * color.b, 1.0);

實際上,人們必須嘗試此類顏色轉換的各種可能性。為此,使用數字著色器屬性(例如上面行中的因子 0.5)對於以互動方式探索可能性特別有用。

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

  • 如何將丟棄片段與 alpha 紋理貼圖結合使用。
  • 如何將 alpha 紋理貼圖用於混合。
  • 如何將 alpha 紋理貼圖用於確定顏色。

進一步閱讀

[編輯 | 編輯原始碼]

如果您還想了解更多資訊

< Cg 程式設計/Unity

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