跳轉到內容

GLSL 程式設計/Unity/天空盒

來自華夏公益教科書
從摩天大樓的視角。只要背景是靜態的並且足夠遠,它就適合作為天空盒。

本教程介紹瞭如何使用立方體貼圖將 **環境貼圖作為背景** 進行渲染。

它基於 “反射表面”部分。如果你還沒有閱讀過該教程,現在是閱讀的好時機。

在背景中渲染天空盒

[編輯 | 編輯原始碼]

“反射表面”部分 所述,天空盒可以被認為是一個無限大的、紋理化的盒子,它包圍著一個場景。有時,天空盒(或天空圓頂)透過足夠大的紋理化模型實現,這些模型近似於無限大的盒子(或圓頂)。但是,“反射表面”部分 介紹了立方體貼圖的概念,它實際上代表了一個無限大的盒子;因此,我們不需要對有限尺寸的盒子或圓頂進行近似。相反,我們可以渲染任何填充螢幕的模型(無論它是盒子、圓頂還是蘋果樹,只要它覆蓋整個背景即可),在頂點著色器中計算從相機到光柵化表面點的檢視向量(就像我們在 “反射表面”部分 中所做的那樣),然後在片段著色器中使用此檢視向量在立方體貼圖中進行查詢(而不是 “反射表面”部分 中的反射檢視向量)

         #ifdef FRAGMENT
 
         void main()
         {
            gl_FragColor = textureCube(_Cube, viewDirection);
         }
 
         #endif

為了獲得最佳效能,我們當然應該渲染一個只有少數頂點的模型,並且每個畫素都應該只被光柵化一次。因此,渲染包圍相機(或整個場景)的立方體的內部是可以的。

完整的著色器程式碼

[編輯 | 編輯原始碼]

著色器應該附加到一個材質,該材質應該附加到包圍相機的立方體。在著色器程式碼中,我們使用 ZWrite Off 停用寫入深度緩衝區,這樣就不會有任何物體被天空盒遮擋。(參見 “逐片段操作”部分 中對深度測試的描述。)使用 Cull Front 啟用正面剔除,這樣就只有立方體的“內部”會被光柵化。(參見 “切除”部分。)Tags { "Queue" = "Background" } 這行程式碼指示 Unity 在渲染其他物體之前渲染此通道。

Shader "GLSL shader for skybox" {
   Properties {
      _Cube ("Environment Map", Cube) = "" {}
   }
   SubShader {
      Tags { "Queue" = "Background" }
      
      Pass {   
         ZWrite Off
         Cull Front

         GLSLPROGRAM
 
         // User-specified uniform
         uniform samplerCube _Cube;   
 
         // The following built-in uniforms 
         // are also defined in "UnityCG.glslinc", 
         // i.e. one could #include "UnityCG.glslinc" 
         uniform vec3 _WorldSpaceCameraPos; 
            // camera position in world space
         uniform mat4 _Object2World; // model matrix
 
         // Varying
         varying vec3 viewDirection;
 
         #ifdef VERTEX
 
         void main()
         {            
            mat4 modelMatrix = _Object2World;
 
            viewDirection = vec3(modelMatrix * gl_Vertex 
               - vec4(_WorldSpaceCameraPos, 1.0));
 
            gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;
         }
 
         #endif
 
 
         #ifdef FRAGMENT
 
         void main()
         {
            gl_FragColor = textureCube(_Cube, viewDirection);
         }
 
         #endif
 
         ENDGLSL
      }
   }
}

Unity 的天空盒系統的著色器程式碼

[編輯 | 編輯原始碼]

上面的著色器演示瞭如何透過渲染一個帶有特定著色器的圍繞相機的立方體來渲染天空盒。這是一種非常通用的方法。但是,Unity 有它自己的天空盒系統,不需要任何遊戲物體:你只需在主選單中將帶有天空盒著色器的材質指定到 **編輯 > 渲染設定 > 天空盒材質**,Unity 會處理剩下的工作。不幸的是,我們不能將我們的著色器用於此係統,因為我們必須在由頂點紋理座標指定的職位進行立方體紋理查詢。這實際上比計算檢視方向更容易。以下是程式碼

Shader "GLSL shader for skybox" {
   Properties {
      _Cube ("Environment Map", Cube) = "" {}
   }
   SubShader {
      Tags { "Queue" = "Background" }
      
      Pass {   
         ZWrite Off
         Cull Off

         GLSLPROGRAM
 
         #extension GL_NV_shadow_samplers_cube : enable

         // User-specified uniform
         uniform samplerCube _Cube;   
  
         // Varying
         varying vec3 texCoords;
 
         #ifdef VERTEX
 
         void main()
         {            
            texCoords = vec3(gl_MultiTexCoord0);
            gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;
         }
 
         #endif
 
 
         #ifdef FRAGMENT
 
         void main()
         {
            gl_FragColor = textureCube(_Cube, texCoords);
         }
 
         #endif
 
         ENDGLSL
      }
   }
}

如上所述,你應該使用此著色器建立一個材質,並將該材質拖放到 **編輯 > 渲染設定 > 天空盒材質**。無需將材質附加到任何遊戲物體。

恭喜你完成了另一個教程!我們已經瞭解了

  • 如何渲染天空盒。
  • 如何在 Unity 中不使用遊戲物體渲染天空盒。

進一步閱讀

[編輯 | 編輯原始碼]

如果你還想了解更多


< GLSL 程式設計/Unity

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