// Equirectangular skybox with a blurred nadir, for hiding whoever was carrying the camera. // Drop-in replacement for Skybox/Panoramic (mono, latitude-longitude layout). // // REQUIRED: the source texture must have mip maps enabled, or nothing will blur. // RenderTexture: tick "Mip Maps" on the asset (auto-generate is on by default). // Still image: tick "Generate Mip Maps" in the import settings. Shader "Skybox/PanoramicNadirBlur" { Properties { [NoScaleOffset] _MainTex ("Equirectangular Texture", 2D) = "grey" {} _Tint ("Tint Color", Color) = (0.5, 0.5, 0.5, 0.5) [Gamma] _Exposure ("Exposure", Range(0, 8)) = 1.0 _Rotation ("Rotation", Range(0, 360)) = 0 [Header(Nadir Blur)] _BlurStartAngle ("Blur Starts (deg below horizon)", Range(0, 90)) = 40 _BlurFullAngle ("Fully Blurred (deg below horizon)", Range(0, 90)) = 65 _BlurStrength ("Blur Strength (mip level)", Range(0, 10)) = 6 } SubShader { Tags { "Queue" = "Background" "RenderType" = "Background" "PreviewType" = "Skybox" } Cull Off ZWrite Off Pass { CGPROGRAM #pragma vertex vert #pragma fragment frag #pragma target 3.0 #include "UnityCG.cginc" sampler2D _MainTex; half4 _Tint; half _Exposure; float _Rotation; float _BlurStartAngle; float _BlurFullAngle; float _BlurStrength; float3 RotateAroundYInDegrees(float3 vertex, float degrees) { float alpha = degrees * UNITY_PI / 180.0; float sina, cosa; sincos(alpha, sina, cosa); float2x2 m = float2x2(cosa, -sina, sina, cosa); return float3(mul(m, vertex.xz), vertex.y).xzy; } struct appdata_t { float4 vertex : POSITION; UNITY_VERTEX_INPUT_INSTANCE_ID }; struct v2f { float4 vertex : SV_POSITION; float3 texcoord : TEXCOORD0; UNITY_VERTEX_OUTPUT_STEREO }; v2f vert(appdata_t v) { v2f o; UNITY_SETUP_INSTANCE_ID(v); UNITY_INITIALIZE_OUTPUT(v2f, o); UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(o); float3 rotated = RotateAroundYInDegrees(v.vertex.xyz, _Rotation); o.vertex = UnityObjectToClipPos(rotated); o.texcoord = v.vertex.xyz; return o; } // Matches Unity's Skybox/Panoramic mapping so orientation is unchanged. inline float2 ToRadialCoords(float3 coords) { float3 nc = normalize(coords); float latitude = acos(nc.y); float longitude = atan2(nc.z, nc.x); float2 sphereCoords = float2(longitude, latitude) * float2(0.5 / UNITY_PI, 1.0 / UNITY_PI); return float2(0.5, 1.0) - sphereCoords; } fixed4 frag(v2f i) : SV_Target { float3 dir = normalize(i.texcoord); float2 uv = ToRadialCoords(dir); // Elevation in degrees: +90 straight up, 0 at the horizon, -90 straight down. float elevation = degrees(asin(dir.y)); // 0 above the start angle, ramping to 1 by the full angle. float t = 1.0 - smoothstep(-_BlurFullAngle, -_BlurStartAngle, elevation); // Sampling a coarser mip is the cheap way to blur: no extra taps. float lod = t * _BlurStrength; half4 tex = tex2Dlod(_MainTex, float4(uv, 0.0, lod)); half3 col = tex.rgb * _Tint.rgb * unity_ColorSpaceDouble.rgb * _Exposure; return half4(col, 1.0); } ENDCG } } Fallback Off }