1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179
| Shader "Custom/URP/ProceduralSkybox" { Properties { // 太阳参数 _SunColor ("太阳颜色", Color) = (1.0, 0.95, 0.8, 1) _SunIntensity ("太阳强度", Range(1, 50)) = 15.0 _SunSize ("太阳大小", Range(0.001, 0.1)) = 0.04 _SunBloom ("太阳光晕大小", Range(0.01, 0.5)) = 0.15
// 大气参数(物理近似) _RayleighCoeff ("瑞利系数", Range(0, 5)) = 1.0 // 越大越蓝 _MieCoeff ("米散射系数", Range(0, 5)) = 0.3 // 越大日周围越白 _MieG ("Mie g 值", Range(0.5, 0.99)) = 0.76 // 前向散射强度
// 天空颜色 _ZenithColor ("天顶颜色", Color) = (0.05, 0.15, 0.5, 1) _HorizonColor ("地平线颜色", Color) = (0.4, 0.6, 0.8, 1) _GroundColor ("地面颜色(天空盒下半球)", Color) = (0.2, 0.18, 0.15, 1)
// 时间参数(通过脚本传入) _SunAltitude ("太阳高度角(弧度)", Range(-1.57, 1.57)) = 0.5 _SunAzimuth ("太阳方位角(弧度)", Range(0, 6.28)) = 0.0 }
SubShader { Tags { "RenderType" = "Background" "Queue" = "Background" "PreviewType" = "Skybox" }
Cull Off ZWrite Off
Pass { HLSLPROGRAM #pragma vertex SkyboxVert #pragma fragment SkyboxFrag #include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Core.hlsl"
CBUFFER_START(UnityPerMaterial) float4 _SunColor; float _SunIntensity; float _SunSize; float _SunBloom; float _RayleighCoeff; float _MieCoeff; float _MieG; float4 _ZenithColor; float4 _HorizonColor; float4 _GroundColor; float _SunAltitude; float _SunAzimuth; CBUFFER_END
struct Attributes { float4 positionOS : POSITION; }; struct Varyings { float4 positionHCS : SV_POSITION; float3 rayDir : TEXCOORD0; };
Varyings SkyboxVert(Attributes input) { Varyings output; // 天空盒顶点变换:剥离位移,只保留旋转(天空盒不随相机位移) output.positionHCS = TransformObjectToHClip(input.positionOS.xyz); // 将顶点方向作为射线方向(物体空间 -> 世界空间旋转部分) output.rayDir = TransformObjectToWorld(input.positionOS.xyz); return output; }
// 瑞利相位函数 float PhaseRayleigh(float cosTheta) { return 3.0 / (16.0 * 3.14159) * (1.0 + cosTheta * cosTheta); }
// Henyey-Greenstein 米散射相位函数 float PhaseMie(float cosTheta, float g) { float g2 = g * g; float num = (1.0 - g2) * (1.0 + cosTheta * cosTheta); float den = (2.0 + g2) * pow(1.0 + g2 - 2.0 * g * cosTheta, 1.5); return 3.0 / (8.0 * 3.14159) * num / den; }
// 快速大气散射近似(不做全光线步进,使用解析近似) // 足够用于天空盒,不需要完整的光线步进积分 float3 CalculateSkyColor(float3 rayDir, float3 sunDir) { float cosTheta = dot(rayDir, sunDir); float elevation = rayDir.y; // -1 到 1(-1=正下,1=正上)
// ---- 瑞利散射(蓝天)---- // β_R(λ) ∝ 1/λ^4,RGB 分量近似比例 float3 betaRayleigh = float3(5.5e-6, 13.0e-6, 22.4e-6) * _RayleighCoeff * 1e6;
// 大气厚度近似(光程长度随仰角的变化) float opticalDepth = max(0.0, 1.0 / (max(elevation, 0.03) + 0.1)); opticalDepth = min(opticalDepth, 20.0); // 防止地平线处爆炸
// 瑞利散射颜色(天空基础蓝色) float3 rayleigh = betaRayleigh * PhaseRayleigh(cosTheta) * opticalDepth;
// ---- 米散射(太阳周围白色晕圈)---- float betaMie = 21e-6 * _MieCoeff * 1e6; float3 mie = betaMie * PhaseMie(cosTheta, _MieG) * opticalDepth * 0.5;
// ---- Beer-Lambert 透射率 ---- float3 extinction = exp(-(betaRayleigh + betaMie) * opticalDepth);
// ---- 组合 ---- // 入射太阳光颜色(经过大气衰减) float3 sunTransmit = exp(-(betaRayleigh + betaMie) * max(0.0, 1.0 / (max(sunDir.y, 0.01) + 0.1)));
float3 scatter = (rayleigh * float3(0.55, 0.75, 1.0) + mie * float3(1.0, 0.95, 0.85)) * sunTransmit * 20.0;
return scatter; }
half4 SkyboxFrag(Varyings input) : SV_Target { float3 rayDir = normalize(input.rayDir);
// 从高度角/方位角计算太阳方向 float3 sunDir = float3( cos(_SunAltitude) * sin(_SunAzimuth), sin(_SunAltitude), cos(_SunAltitude) * cos(_SunAzimuth) ); sunDir = normalize(sunDir);
// ---- 大气散射颜色 ---- float3 skyColor = CalculateSkyColor(rayDir, sunDir);
// ---- 天顶到地平线渐变(叠加在散射之上)---- float elevation = rayDir.y; float zenithT = saturate(elevation); float horizT = smoothstep(-0.05, 0.3, elevation); float3 gradient = lerp(_GroundColor.rgb, _HorizonColor.rgb, horizT); gradient = lerp(gradient, _ZenithColor.rgb, zenithT * zenithT);
// 散射与渐变混合 float3 sky = gradient + skyColor;
// ---- 太阳圆盘 ---- float cosAngle = dot(rayDir, sunDir); // 太阳圆盘(硬边缘 + smoothstep 抗锯齿) float sunDisk = smoothstep(_SunSize, _SunSize - 0.002, acos(saturate(cosAngle))); // 太阳光晕(柔和辉光) float sunBloom = pow(max(cosAngle, 0.0), 1.0 / max(_SunBloom, 0.001));
float3 sunContrib = _SunColor.rgb * _SunIntensity * (sunDisk + sunBloom * 0.2);
// 日落时太阳颜色变暖(太阳接近地平线时更偏红) float sunElevation = sunDir.y; float3 sunsetTint = lerp(float3(1.0, 0.4, 0.1), float3(1.0, 0.95, 0.8), saturate(sunElevation * 3.0)); sunContrib *= sunsetTint;
// 夜晚:太阳低于地平线时淡出 float daytime = smoothstep(-0.1, 0.1, sunDir.y); sky *= daytime; sunContrib *= daytime;
float3 finalColor = sky + sunContrib;
// 简单曝光(确保颜色范围合理) finalColor = 1.0 - exp(-finalColor * 0.5);
return half4(finalColor, 1.0); } ENDHLSL } } }
|