283 lines
11 KiB
GLSL
283 lines
11 KiB
GLSL
Shader "GAME/StylizedSky"
|
|
{
|
|
// Fully procedural stylized skybox: vertical sky gradient, sun + glow, moon, twinkling
|
|
// stars and FBM clouds — all animated. Static art-direction lives in these Properties;
|
|
// everything that changes with the time of day is pushed as GLOBALS by TimeOfDayManager
|
|
// (mirrors how StylizedGrass reads the global "_Season"). No textures, no geometry.
|
|
Properties
|
|
{
|
|
[Header(Clouds static tuning)][Space]
|
|
_CloudScale("Cloud Scale", Float) = 1.0
|
|
_CloudSoftness("Cloud Softness", Range(0.01, 1)) = 0.08
|
|
_CloudShadingStrength("Cloud Shading Strength", Range(0, 4)) = 2.5
|
|
_CloudHorizonFade("Cloud Horizon Fade", Range(0.02, 0.6)) = 0.05
|
|
_CloudDetail("Cloud Detail (billow lumps)", Range(0, 1)) = 0.7
|
|
|
|
[Header(Sun static tuning)][Space]
|
|
_SunSize("Sun Size", Range(0.002, 0.2)) = 0.04
|
|
_SunEdgeSoftness("Sun Edge Softness", Range(0.001, 0.1)) = 0.015
|
|
_SunGlowFalloff("Sun Glow Falloff", Range(1, 2000)) = 400
|
|
_SunGlowStrength("Sun Glow Strength", Range(0, 3)) = 0.6
|
|
|
|
[Header(Moon static tuning)][Space]
|
|
_MoonSize("Moon Size", Range(0.002, 0.2)) = 0.05
|
|
_MoonEdgeSoftness("Moon Edge Softness", Range(0.001, 0.1)) = 0.012
|
|
|
|
[Header(Stars static tuning)][Space]
|
|
_StarDensity("Star Density", Float) = 60
|
|
_StarThreshold("Star Threshold", Range(0.8, 0.999)) = 0.93
|
|
_StarSize("Star Size", Range(0.01, 0.5)) = 0.09
|
|
_StarTwinkleSpeed("Star Twinkle Speed", Float) = 3
|
|
|
|
[Header(Gradient shaping)][Space]
|
|
_HorizonSharpness("Horizon Sharpness", Range(0.2, 8)) = 2.5
|
|
_GroundSharpness("Ground Sharpness", Range(0.2, 8)) = 3.0
|
|
}
|
|
|
|
SubShader
|
|
{
|
|
Tags
|
|
{
|
|
"RenderType" = "Background"
|
|
"Queue" = "Background"
|
|
"RenderPipeline" = "UniversalPipeline"
|
|
"PreviewType" = "Skybox"
|
|
}
|
|
|
|
Pass
|
|
{
|
|
Name "StylizedSky"
|
|
Cull Off
|
|
ZWrite Off
|
|
ZTest LEqual
|
|
|
|
HLSLPROGRAM
|
|
#pragma target 3.0
|
|
#pragma vertex vert
|
|
#pragma fragment frag
|
|
|
|
#include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Core.hlsl"
|
|
|
|
// ---------------------------------------------------------------------------------
|
|
// Static per-material tuning
|
|
// ---------------------------------------------------------------------------------
|
|
CBUFFER_START(UnityPerMaterial)
|
|
float _CloudScale;
|
|
float _CloudSoftness;
|
|
float _CloudShadingStrength;
|
|
float _CloudHorizonFade;
|
|
float _CloudDetail;
|
|
|
|
float _SunSize;
|
|
float _SunEdgeSoftness;
|
|
float _SunGlowFalloff;
|
|
float _SunGlowStrength;
|
|
|
|
float _MoonSize;
|
|
float _MoonEdgeSoftness;
|
|
|
|
float _StarDensity;
|
|
float _StarThreshold;
|
|
float _StarSize;
|
|
float _StarTwinkleSpeed;
|
|
|
|
float _HorizonSharpness;
|
|
float _GroundSharpness;
|
|
CBUFFER_END
|
|
|
|
// ---------------------------------------------------------------------------------
|
|
// Time-of-day globals (written every frame by TimeOfDayManager via SetGlobalX).
|
|
// Declared OUTSIDE the CBUFFER so they resolve to the global values, exactly like
|
|
// StylizedGrass reads "_Season".
|
|
// ---------------------------------------------------------------------------------
|
|
float4 _SkyZenithColor;
|
|
float4 _SkyHorizonColor;
|
|
float4 _SkyGroundColor;
|
|
|
|
float4 _SkySunDirection; // xyz = world direction TOWARD the sun (normalized)
|
|
float4 _SkySunColor;
|
|
float4 _SkyMoonDirection; // xyz = world direction TOWARD the moon (normalized)
|
|
float4 _SkyMoonColor;
|
|
|
|
float4 _SkyCloudColor;
|
|
float4 _SkyCloudShadowColor;
|
|
float4 _SkyCloudParams; // x = coverage threshold, y = opacity, zw = wind offset
|
|
float _SkyStarOpacity;
|
|
|
|
// ---------------------------------------------------------------------------------
|
|
// Noise
|
|
// ---------------------------------------------------------------------------------
|
|
float hash21(float2 p)
|
|
{
|
|
p = frac(p * float2(123.34, 345.45));
|
|
p += dot(p, p + 34.345);
|
|
return frac(p.x * p.y);
|
|
}
|
|
|
|
float valueNoise(float2 p)
|
|
{
|
|
float2 i = floor(p);
|
|
float2 f = frac(p);
|
|
float2 u = f * f * (3.0 - 2.0 * f);
|
|
float a = hash21(i);
|
|
float b = hash21(i + float2(1, 0));
|
|
float c = hash21(i + float2(0, 1));
|
|
float d = hash21(i + float2(1, 1));
|
|
return lerp(lerp(a, b, u.x), lerp(c, d, u.x), u.y);
|
|
}
|
|
|
|
float fbm(float2 p)
|
|
{
|
|
float v = 0.0;
|
|
float amp = 0.5;
|
|
[unroll]
|
|
for (int i = 0; i < 5; i++)
|
|
{
|
|
v += amp * valueNoise(p);
|
|
p *= 2.02;
|
|
amp *= 0.5;
|
|
}
|
|
return v;
|
|
}
|
|
|
|
// Billow (turbulence) FBM: abs() folds each octave into rounded lumps, producing the
|
|
// cauliflower/cumulus structure that plain value-noise FBM cannot.
|
|
float billowFbm(float2 p)
|
|
{
|
|
float v = 0.0;
|
|
float amp = 0.5;
|
|
[unroll]
|
|
for (int i = 0; i < 5; i++)
|
|
{
|
|
v += amp * abs(2.0 * valueNoise(p) - 1.0);
|
|
p *= 2.03;
|
|
amp *= 0.5;
|
|
}
|
|
return v;
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------------
|
|
// Sky pieces
|
|
// ---------------------------------------------------------------------------------
|
|
|
|
// Vertical zenith -> horizon -> ground ramp driven by view height (dir.y in [-1,1]).
|
|
half3 SampleSkyGradient(float height)
|
|
{
|
|
float up = pow(saturate(height), 1.0 / _HorizonSharpness);
|
|
half3 sky = lerp(_SkyHorizonColor.rgb, _SkyZenithColor.rgb, up);
|
|
float down = pow(saturate(-height), 1.0 / _GroundSharpness);
|
|
sky = lerp(sky, _SkyGroundColor.rgb, down);
|
|
return sky;
|
|
}
|
|
|
|
// Planar projection of the sky onto a cloud sheet. Adding a constant to the height
|
|
// (instead of dividing by a clamped near-zero) removes the hard horizon singularity
|
|
// that smeared clouds into streaks, keeping puffs readable all the way down.
|
|
float2 CloudPlaneUV(float3 dir)
|
|
{
|
|
float2 wind = _SkyCloudParams.zw;
|
|
return (dir.xz / (dir.y + 0.28)) * _CloudScale + wind;
|
|
}
|
|
|
|
// Returns cloud coverage in .x and a fake self-shadow lighting term in .y. Big soft
|
|
// masses (value FBM) are broken into rounded cumulus lumps by billow FBM, then a tight
|
|
// smoothstep gives defined edges. The lighting term darkens the sun-shadowed underside
|
|
// so the puffs read as volumetric rather than flat.
|
|
float2 SampleClouds(float3 dir)
|
|
{
|
|
float2 uv = CloudPlaneUV(dir);
|
|
float shape = fbm(uv * 0.6);
|
|
float billow = billowFbm(uv * 1.9);
|
|
float density = lerp(shape, shape * (0.5 + billow), _CloudDetail);
|
|
|
|
float coverage = _SkyCloudParams.x;
|
|
float cloud = smoothstep(coverage, coverage + _CloudSoftness, density);
|
|
|
|
float2 lightDir = normalize(_SkySunDirection.xz + 1e-4);
|
|
float lit = fbm((uv + lightDir * 0.22) * 0.6);
|
|
float lighting = saturate((shape - lit) * _CloudShadingStrength + 0.55);
|
|
|
|
return float2(cloud, lighting);
|
|
}
|
|
|
|
// Sparse twinkling star field, faded below the horizon and multiplied by night opacity.
|
|
float SampleStars(float3 dir)
|
|
{
|
|
if (dir.y <= 0.0) return 0.0;
|
|
float2 uv = (dir.xz / (dir.y + 0.35)) * _StarDensity;
|
|
float2 id = floor(uv);
|
|
float2 gv = frac(uv) - 0.5;
|
|
float n = hash21(id);
|
|
if (n < _StarThreshold) return 0.0;
|
|
|
|
float twinkle = 0.6 + 0.4 * sin(_Time.y * _StarTwinkleSpeed + n * 100.0);
|
|
float spark = smoothstep(_StarSize, 0.0, length(gv));
|
|
float horizon = smoothstep(0.0, 0.15, dir.y);
|
|
return spark * twinkle * horizon;
|
|
}
|
|
|
|
// Sharp disc + soft power-falloff glow for a celestial body.
|
|
void SampleBody(float3 dir, float3 bodyDir, float size, float edge,
|
|
out float disc, out float glow)
|
|
{
|
|
float d = dot(dir, bodyDir);
|
|
float inner = cos(size);
|
|
float outer = cos(size + edge);
|
|
disc = smoothstep(outer, inner, d);
|
|
glow = pow(saturate(d), _SunGlowFalloff);
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------------
|
|
// Vertex / Fragment
|
|
// ---------------------------------------------------------------------------------
|
|
struct Attributes
|
|
{
|
|
float4 positionOS : POSITION;
|
|
};
|
|
|
|
struct Varyings
|
|
{
|
|
float4 positionCS : SV_POSITION;
|
|
float3 dirWS : TEXCOORD0;
|
|
};
|
|
|
|
Varyings vert(Attributes IN)
|
|
{
|
|
Varyings OUT;
|
|
OUT.positionCS = TransformObjectToHClip(IN.positionOS.xyz);
|
|
OUT.dirWS = TransformObjectToWorldDir(IN.positionOS.xyz, false);
|
|
return OUT;
|
|
}
|
|
|
|
half4 frag(Varyings IN) : SV_Target
|
|
{
|
|
float3 dir = normalize(IN.dirWS);
|
|
|
|
half3 col = SampleSkyGradient(dir.y);
|
|
|
|
col += SampleStars(dir) * _SkyStarOpacity;
|
|
|
|
float moonDisc, moonGlowUnused;
|
|
SampleBody(dir, _SkyMoonDirection.xyz, _MoonSize, _MoonEdgeSoftness, moonDisc, moonGlowUnused);
|
|
col += _SkyMoonColor.rgb * moonDisc;
|
|
|
|
float sunDisc, sunGlow;
|
|
SampleBody(dir, _SkySunDirection.xyz, _SunSize, _SunEdgeSoftness, sunDisc, sunGlow);
|
|
col += _SkySunColor.rgb * (sunGlow * _SunGlowStrength);
|
|
col += _SkySunColor.rgb * sunDisc;
|
|
|
|
float2 clouds = SampleClouds(dir);
|
|
float horizonFade = smoothstep(0.0, _CloudHorizonFade, dir.y);
|
|
float cloudAlpha = clouds.x * horizonFade * _SkyCloudParams.y;
|
|
half3 cloudCol = lerp(_SkyCloudShadowColor.rgb, _SkyCloudColor.rgb, clouds.y);
|
|
col = lerp(col, cloudCol, cloudAlpha);
|
|
|
|
return half4(col, 1.0);
|
|
}
|
|
ENDHLSL
|
|
}
|
|
}
|
|
|
|
Fallback Off
|
|
}
|