>> >> >> Reference << << << <<<<<<Ref>>>>>>
>> >> >> Indexer << << << <<<<<<Idx>>>>>>
Matched: 0

Tags

    Categories

      Types

        Top Results

          Skybox
          M: 2025-06-01 - ljf12825

          Skybox是一种渲染技术,用于在3D场景中创建远景背景,例如天空、宇宙、城市天际线等
          它本质上是一种把纹理图贴在一个立方体(或球体)内侧的技巧,玩家看不到边界,只能看到包裹在四周的“天空”

          关键特性:

          • 无限远:无论玩家如何移动,天空和总是保持固定的距离,永远不会被靠近(天空盒会跟随相机移动)从而创造出“无限远”的错觉
          • 环境光源:天空盒不仅是视觉背景,其颜色和亮度还会为场景中的物体提供环境光(Ambient Light)和反射光(Reflection),这是实现场景光照统一和真实感的关键

          创建Skybox

          1. 使用现成材质

          菜单栏 -> Window -> Rendering -> Lighting
          SkyboxMaterial

          2. 使用6张图片自定义创建立方体天空盒

          1. 准备图片:需要6张正方形图片,分别对应立方体的六个面(+X, -X, +Y, -Y, +Z, -Z)。通常命名为right, left, top, bottom, front, backpx, nx, py, ny, pz, nz
          • 确保图片的Wrap Mode设置为Clamp,防止边缘接缝处出现拉伸
          1. 创建材质
          • 在Project视图中右键->Create->Material
          • 将新建的材质命名为MySkybox
          • 在材质的Inspector面板中,点击Shader下拉菜单,选择Skybox -> Cubemap
          1. 配置Cubemap
          • 创建Cubemap资源:Project下右键 -> Create -> Rendering -> LegacyCubemap,将六张图拖入Cubemap资源,然后将这个Cubemap资源拖到天空材质球的Cubemap槽位
          1. 应用材质球:同1. 使用现成材质

          3. 使用ProceduralSkybox

          创建Shader文件

          1. Project右键 -> Create -> Shader -> Unlit Shader
          2. 将其命名为ProceduralSkybox
          3. 删除所有代码,替换为
          Shader "Skybox/Procedural Skybox"
          {
              Properties
              {
                  // 顶部颜色(天顶)
                  _SkyColor ("Sky Color", Color) = (0.37, 0.52, 0.73, 1)
                  // 地平线颜色
                  _HorizonColor ("Horizon Color", Color) = (0.89, 0.89, 0.89, 1)
                  // 地面颜色(地平线以下)
                  _GroundColor ("Ground Color", Color) = (0.33, 0.27, 0.21, 1)
                  
                  // 太阳颜色
                  _SunColor ("Sun Color", Color) = (1, 0.8, 0.6, 1)
                  // 太阳大小(半径)
                  _SunSize ("Sun Size", Range(0, 1)) = 0.04
                  // 太阳晕影大小(光晕扩散)
                  _SunGlow ("Sun Glow", Range(0, 10)) = 2.5
                  
                  // 太阳在天空中的方向(通常由脚本绑定方向光的方向来控制)
                  _SunDirection ("Sun Direction", Vector) = (0.3, 0.8, 0.5, 0)
                  
                  // 天空颜色的幂次,控制颜色过渡的陡峭程度
                  _SkyExponent ("Sky Exponent", Range(0, 10)) = 1.5
                  // 地平线的大气厚度效果
                  _AtmosphereThickness ("Atmosphere Thickness", Range(0, 10)) = 1.0
              }
              SubShader
              {
                  Tags
                  {
                      "Queue"="Background"
                      "RenderType"="Background"
                      "PreviewType"="Skybox"
                  }
                  Cull Off // 关闭剔除,因为要从内部渲染
                  ZWrite Off // 关闭深度写入,天空盒永远在最远处
          
                  Pass
                  {
                      CGPROGRAM
                      #pragma vertex vert
                      #pragma fragment frag
                      #include "UnityCG.cginc"
          
                      struct appdata
                      {
                          float4 vertex : POSITION;
                          float3 uv : TEXCOORD0; // 使用三维UV来采样天空球
                      };
          
                      struct v2f
                      {
                          float4 vertex : SV_POSITION;
                          float3 viewDir : TEXCOORD0; // 将视图方向传递给片元着色器
                      };
          
                      // 将属性变量连接到CG代码
                      fixed4 _SkyColor;
                      fixed4 _HorizonColor;
                      fixed4 _GroundColor;
                      fixed4 _SunColor;
                      half _SunSize;
                      half _SunGlow;
                      half4 _SunDirection;
                      half _SkyExponent;
                      half _AtmosphereThickness;
          
                      v2f vert (appdata v)
                      {
                          v2f o;
                          o.vertex = UnityObjectToClipPos(v.vertex);
                          
                          // 获取世界空间下的视图方向(顶点位置)
                          // 对于天空盒,顶点位置就是视图方向
                          o.viewDir = mul((float3x3)unity_ObjectToWorld, v.vertex.xyz);
                          
                          return o;
                      }
          
                      fixed4 frag (v2f i) : SV_Target
                      {
                          // 标准化视图方向
                          float3 viewDir = normalize(i.viewDir);
                          // 标准化太阳方向(确保是单位向量)
                          float3 sunDir = normalize(_SunDirection.xyz);
                          
                          // 1. 计算基础天空颜色(基于视角的垂直分量y)
                          float horizon = dot(viewDir, float3(0, 1, 0)); // 计算与垂直方向的点积
                          // 将horizon从[-1, 1] 映射到 [0, 1] 并应用幂次控制过渡
                          float skyGradient = saturate(horizon * 0.5 + 0.5);
                          // 应用指数控制过渡的平滑度
                          skyGradient = pow(skyGradient, _SkyExponent);
                          
                          // 混合天空和地平线颜色
                          fixed4 skyFinal = lerp(_HorizonColor, _SkyColor, skyGradient);
                          // 混合地平线和地面颜色(对于地平线以下的部分)
                          skyFinal = lerp(_GroundColor, skyFinal, saturate(horizon * _AtmosphereThickness));
                          
                          // 2. 计算太阳
                          // 计算视线方向与太阳方向的夹角
                          float sunDot = dot(viewDir, sunDir);
                          // 平滑步进函数,在sunDot接近1(即视角指向太阳中心)时产生一个圆盘
                          float sunDisk = smoothstep(1.0 - _SunSize, 1.0, sunDot);
                          // 计算太阳光晕,一个更宽更柔和的过渡
                          float sunGlow = pow(saturate(sunDot), _SunGlow);
                          
                          // 结合太阳圆盘和光晕
                          fixed4 sunFinal = (_SunColor * (sunDisk + sunGlow));
                          
                          // 3. 将太阳效果叠加到天空背景上(使用加法混合,因为光是叠加的)
                          fixed4 col = skyFinal + sunFinal;
                          
                          return col;
                      }
                      ENDCG
                  }
              }
              // 防止回退到其他Shader
              Fallback Off
          }
          

          创建并使用材质

          创建一个材质名为Mat_ProceduralSky,在材质Inspector窗口中点击Shader下拉菜单,选择Skybox -/> ProceduralSkybox(Shader第一行创建);调整材质属性,使用该材质作为天空盒材质

          与场景光联动

          为了让太阳的位置与场景中的主方向光(模拟太阳)同步,需要创建一个简单的脚本 SyncSunDirection.cs

          using UnityEngine;
          
          [ExecuteAlways] // execute always include editor mode
          public class SyncSunDirection : MonoBehaviour
          {
              [SerializeField] private Light _sunLight; // directional light
              [SerializeField] private Material _skyboxMaterial; // proceduralskybox
          
              // 在材质中定义的_SunDirection属性的标识符
              private static readonly int SunDirectionID = Shader.PropertyToID("_SunDirection");
          
              void Update()
              {
                  if (_sunLight != null && _skyboxMaterial != null)
                  {
                      // 将光源的forward方向(光照方向的反方向)传递给材质
                      // 因为光源.transform.forward指向光照照射的方向(太阳到物体)
                      // 而天空盒需要的是太阳在天空中的位置方向(物体到太阳),所以取反
                      Vector3 sunDirectionInSky = -_sunLight.transform.forward;
          
                      // 设置材质的向量属性
                      _skyboxMaterial.SetVector(SunDirectionID, new Vector4(
                          sunDirectionInSky.x,
                          sunDirectionInSky.y,
                          sunDirectionInSky.z,
                          0f // 第四个分量通常不用
                      ));
                  }
              }
          }
          

          将该脚本挂在场景中任何物体上,拖入字段
          在编辑器中旋转directional light,天空盒中的太阳位置也会实时更新

          SunDirection1 SunDirection2

          Skybox的类型与属性

          Unity提供了几种不同类型的天空盒Shader,适用于不同的需求和资源类型

          1. 6Sided(Cubemap) 使用6张独立的纹理。最经典和兼容新最好的方式
          • Tint Color:对每个面的纹理进行颜色染色
          • Exposure:调整天空盒的整体亮度。这个值会直接影响环境光的强度
          • Rotation:围绕Y轴旋转整个天空盒,可以调整太阳/云层的位置
          1. Cubemap 使用一个单独的Cubemap资源(.cubemap文件),而不是六张分散的图片。Cube资源可以被引擎优化,性能通常稍好
          • 可以从六张图片生成Cubemap资源,见上所述
          1. Panoramic(LatLong/360°) 使用一张等距柱状投影(Equirectangular)的360°全景图(2:1宽高比)。这是360°相机拍摄的图片格式
          • 优点:只需处理一张纹理,非常方便
          • Mapping:选择投影方式:6 Frames Layout(类正方体)Latitude Longitude Layout(类地球)Automatic通常即可正确识别
          1. Procedural(程序化) 通过Shader算法实时生成天空,无需任何纹理
          • Sun:需要指定一个Directional Light作为太阳。天空盒会根据这个光的方向是是改变天空的颜色、太阳大小、晕影等
          • 控制参数丰富
            • Atmosphere Thickness:大气厚度,影响天空的蓝色程度
            • Ground Color:地平线处的颜色
            • Sun Size / Sun Size Convergence:控制太阳的大小和模糊程度
          • 优点:动态,可以随着游戏时间变化(通过代码控制Directional Light的旋转)
          • 缺点:风格化较强,不如基于图像的天空盒真实

          深入原理与高级应用

          1. 天空盒、环境光与光照烘焙(GI)

          这是天空盒最核心的高级功能之一

          • 环境光(Ambient Light):在Lighting窗口的Environment标签中,Source如果设置为Skybox,那么场景的环境颜色和强度将完全由当前设置的天空盒决定。Unity会通过对天空盒进行采样来计算出平均的环境光颜色。调整Intensity Multiplier可以控制环境光强度
          • 光照烘焙(Baked GI)
            • 当使用Baked Global Illumination时,天空盒是作为一个重要的环境光源被烘焙到光照贴图(Lightmaps)中的
            • 它的颜色和强度会直接影响场景中间接光照的结果
            • 在Lighting窗口中,可以设置Environment Lighting的相关参数,如IntensityAmbient Occlusion,来控制天空盒在烘焙时的影响

          2. 反射探针(Reflection Probes)

          天空盒的另一个核心作用是提供默认的反射源

          • 场景中具有光滑材质的物体(如金属、水面),其反射内容如果没有配置局部Reflection Probes,物体默认使用天空盒作为其反射源
          • 可以创建Refelction Probes,并将其Source设置为Skybox,这样它就会捕获当前场景的天空盒(或自定义的天空盒)生成一个立方体贴图,供周围的物体使用。这对于让动态物体也能融入环境光至关重要

          3. 动态切换天空盒

          using UnityEngine;
          
          public class SkyboxChanger : MonoBehaviour
          {
              public Material[] skyboxMaterials;
              public ReflectionProbe reflectionProbe; // 可选,如果需要更新反射探针
          
              public void ChangeSkybox(int index)
              {
                  if (index < 0 || index >= skyboxMatterials.Length)
                  {
                      Debug.LogError("Index out of range!");
                      return;
                  }
          
                  // 设置渲染设置中的天空盒
                  RenderSettings.skybox = syboxMaterials[index];
          
                  // 立即强制过呢更新环境光照和反射
                  DynamicGI.UpdateEnvironment();
          
                  // 重置反射探针
                  if (refflectionProbe != null) reflectionProbe.RenderProbe();
              }
          }
          

          4. 性能优化与最佳实践

          • 纹理压缩:天空盒纹理通常很大,需要使用合适的压缩格式

            • PC/主机平台:通常使用BCn/HDR压缩(如RGB Crunched DXT5)
            • Android:通常使用ETC2(支持Alpha)或ASTC(更优的压缩比和质量)
            • iOS:通常使用PVRTCASTC
            • HDR:如果天空盒是高动态范围的(非常亮,如太阳),确保导入设置中勾选Generate Mip Maps并设置合适的压缩格式(如BC6H用于HDR Cubemap)
          • Mip Map:通常建议开启,尤其是在有雾效或需要景深效果的场景中,可以改善远处的外观和性能。但对于永远在“无限远”处的天空盒,有时关闭它节省内存也是可行的,但需要测试

          • 分辨率:不要使用过高的分辨率。2048x2048或1024x1024每面对于大多数项目来说已经足够。过高的分辨率会显著增加内存占用和加载时间

          • HDR vs LDR:使用HDR(高动态范围)天空盒可以带来更真实的光照效果,尤其是在配置Post-Processing Stack后处理堆栈中的Tonemapping是,效果惊人。但需要确保项目设置中Color Space为Linear

          5. 常见问题排查

          • 接缝(Seams):确保6张图片的Wrap Mode设置为Clamp,而不是Repeat
          • 天空盒不显式/变紫:检查材质Shader是否正确,纹理是否丢失
          • 光照不正确:更改天空盒后,如果使用了烘焙光照,必须重新烘焙才能更新光照贴图。动态GI则需要调用
          • 移动端性能差:检查纹理压缩格式和分辨率。Panoramic天空盒在部分低端移动设备上可能比6 Sided的更耗性能,因为需要实时进行坐标转换