跳至內容

GLSL 程式設計/Unity/紋理層

來自華夏公益教科書,開放的世界,開放的書籍
人皮膚的層

本教程介紹了 **多紋理**,即在著色器中使用多個紋理影像。

它擴充套件了 “紋理球體”部分 的著色器程式碼到多個紋理,並展示了一種組合它們的方法。如果您還沒有閱讀過該教程,這是一個很好的機會去閱讀它。

表面層

[編輯 | 編輯原始碼]

許多真實的表面(例如左邊影像中所示的人類皮膚)由幾層不同顏色、透明度、反射率等組成。如果最頂層是不透明的,並且不透射任何光線,那麼對於渲染表面來說並不重要。然而,在許多情況下,最頂層是(半)透明的,因此表面的精確渲染必須考慮到多層。

事實上,包含在 Phong 反射模型中的鏡面反射(見 “鏡面高光”部分)通常對應於一個反射光線的透明層:人體皮膚上的汗水、水果上的蠟、嵌入色素顆粒的透明塑膠等。另一方面,漫反射對應於最頂層透明層下方的層。

照亮這種分層表面不需要層的幾何模型:它們可以用一個單一的、無限薄的多邊形網格來表示。然而,光照計算必須為不同的層計算不同的反射,並且必須考慮光在層之間傳遞的情況(光進入層時和光退出層時)。這種方法的例子包括 Nvidia 的“Dawn”演示(見“GPU Gems”一書的第 3 章,可在 線上 獲取)和 Nvidia 的“Human Head”演示(見“GPU Gems 3”一書的第 14 章,也可在 線上 獲取)。

對這些過程的完整描述超出了本教程的範圍。只需說,層通常與紋理影像相關聯以指定其特徵。在這裡,我們只展示如何使用兩種紋理以及一種特定方式來組合它們。事實上,該示例與層無關,因此說明了多紋理有比表面層更多的應用。

未點亮地球的地圖。
陽光照射的地球的地圖。

亮暗地球

[編輯 | 編輯原始碼]

由於人類活動,地球的陰暗面並非完全黑暗。相反,人造燈游標記了城市的位置和範圍,如左圖所示。因此,地球的漫射光照不應該僅僅使陽光照射面的紋理影像變暗,而應該實際將其與陰暗面的紋理影像混合。請注意,陽光照射的地球比陰暗面的人造燈光亮得多;然而,我們降低了這種對比度以展示夜間紋理。

著色器程式碼擴充套件了 “紋理球體”部分 中的程式碼到兩個紋理影像,並使用 “漫反射”部分 中描述的計算方法來處理單個方向光源

根據這個公式,漫射光照的級別 levelOfLighting 是 max(0, N·L)。然後,我們根據 levelOfLighting 混合白天紋理和夜晚紋理的顏色。這可以透過將白天顏色乘以 levelOfLighting,並將夜晚顏色乘以 1.0 - levelOfLighting,然後將它們加起來以確定片段的顏色來實現。或者,可以使用內建 GLSL 函式 mixmix(a, b, w) = b*w + a*(1.0-w)),這可能更有效。因此,片段著色器可能是

         #ifdef FRAGMENT
 
         void main()
         {
            vec4 nighttimeColor = _Color 
               * texture2D(_MainTex, vec2(textureCoordinates));    
            vec4 daytimeColor = _LightColor0 
               * texture2D(_DecalTex, vec2(textureCoordinates));    
            gl_FragColor = 
               mix(nighttimeColor, daytimeColor, levelOfLighting);
               // = daytimeColor * levelOfLighting 
               // + nighttimeColor * (1.0 - levelOfLighting)
        }
 
         #endif

請注意,這種混合與在 “透明度”部分 中討論的 alpha 混合非常相似,除了我們在片段著色器中執行混合,並使用 levelOfLighting 而不是紋理的 alpha 分量(即不透明度),該紋理應該混合到另一個紋理的“上面”。事實上,如果 _DecalTex 指定了一個 alpha 分量(見 “透明紋理”部分),我們可以使用這個 alpha 分量來混合 _DecalTex_MainTex 上。這實際上是 Unity 的標準 Decal 著色器所做的,它對應於一個層,該層在可見的透明層的頂部部分透明。

完整著色器程式碼

[編輯 | 編輯原始碼]

著色器屬性的名稱選擇與回退著色器的屬性名稱一致 - 在這種情況下是 Decal 著色器(注意,回退 Decal 著色器和標準 Decal 著色器似乎以相反的方式使用這兩個紋理)。此外,還引入了另一個屬性 _Color,它與夜晚紋理的紋理顏色(按分量)相乘,以控制其整體亮度。此外,光源的顏色 _LightColor0 也與白天紋理的顏色(按分量)相乘,以考慮彩色光源。

Shader "GLSL multitexturing of Earth" {
   Properties {
      _DecalTex ("Daytime Earth", 2D) = "white" {}
      _MainTex ("Nighttime Earth", 2D) = "white" {} 
      _Color ("Nighttime Color Filter", Color) = (1,1,1,1)
   }
   SubShader {
      Pass {	
         Tags { "LightMode" = "ForwardBase" } 
            // pass for the first, directional light 

         GLSLPROGRAM

         uniform sampler2D _MainTex;
         uniform sampler2D _DecalTex;
         uniform vec4 _Color; 

         // The following built-in uniforms (except _LightColor0) 
         // are also defined in "UnityCG.glslinc", 
         // i.e. one could #include "UnityCG.glslinc" 
         uniform mat4 _Object2World; // model matrix
         uniform mat4 _World2Object; // inverse model matrix
         uniform vec4 _WorldSpaceLightPos0; 
            // direction to or position of light source
         uniform vec4 _LightColor0; 
            // color of light source (from "Lighting.cginc")
         
         varying float levelOfLighting; 
            // level of diffuse lighting computed in vertex shader
         varying vec4 textureCoordinates;

         #ifdef VERTEX
         
         void main()
         {				
            mat4 modelMatrix = _Object2World;
            mat4 modelMatrixInverse = _World2Object; // unity_Scale.w 
               // is unnecessary because we normalize vectors
            
            vec3 normalDirection = normalize(vec3(
               vec4(gl_Normal, 0.0) * modelMatrixInverse));
            vec3 lightDirection;

            if (0.0 == _WorldSpaceLightPos0.w) // directional light?
            {
               lightDirection = normalize(vec3(_WorldSpaceLightPos0));
            } 
            else // point or spot light
            {
               lightDirection = vec3(0.0, 0.0, 0.0); 
                  // ignore other light sources
            }

            levelOfLighting = 
               max(0.0, dot(normalDirection, lightDirection));
            textureCoordinates = gl_MultiTexCoord0;
            gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;
         }
         
         #endif

         #ifdef FRAGMENT
         
         void main()
         {
            vec4 nighttimeColor = _Color 
               * texture2D(_MainTex, vec2(textureCoordinates));    
            vec4 daytimeColor = _LightColor0 
               * texture2D(_DecalTex, vec2(textureCoordinates));    
            gl_FragColor = 
               mix(nighttimeColor, daytimeColor, levelOfLighting);
               // = daytimeColor * levelOfLighting 
               // + nighttimeColor * (1.0 - levelOfLighting)
        }
         
         #endif

         ENDGLSL
      }
   } 
   // The definition of a fallback shader should be commented out 
   // during development:
   // Fallback "Decal"
}

執行此著色器時,請確保在場景中啟用了方向光源。

恭喜!您已經完成了關於基本紋理的最後一個教程。我們已經瞭解了

  • 表面層如何影響材料的外觀(例如人皮、打蠟的水果、塑膠等)
  • 在紋理化代表地球的球體時,如何考慮陰暗面的人造燈光。
  • 如何在著色器中實現這種技術。
  • 這與將 alpha 紋理混合到第二個不透明紋理上的關係。

進一步閱讀

[編輯 | 編輯原始碼]

如果您仍然想了解更多

  • 關於基本紋理,您應該閱讀 “紋理球體”部分
  • 關於漫反射,您應該閱讀 “漫反射”部分
  • 關於 alpha 紋理,您應該閱讀 “透明紋理”部分
  • 關於高階皮膚渲染,您可以閱讀 Randima Fernando(編輯)於 2004 年由 Addison-Wesley 出版、由 Curtis Beeson 和 Kevin Bjorke 撰寫的“GPU Gems”一書中的第 3 章“‘Dawn’ 演示中的皮膚”,該書可在 線上 獲取,以及 Hubert Nguyen(編輯)於 2007 年由 Addison-Wesley 出版、由 Eugene d’Eon 和 David Luebke 撰寫的“GPU Gems 3”一書中的第 14 章“逼真的即時皮膚渲染的高階技術”,該書也可在 線上 獲取。


< GLSL 程式設計/Unity

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