跳转到内容

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

除非另有说明,本页的所有示例源代码都授予公有领域。
华夏公益教科书