本文主要讲思路和其他教程里没讲的数学逻辑,以及程序化云还有劈开天空的制作思路,代码端会讲的比较简单。

一.上shader
这是天空球的shader,
其中有三个引用的**函数库,**分别是
1.图像处理!
2.UV扭曲
3.程序化纹理的生成!
这三个函数库在在文章的末尾,引用后shader才有效果!
这3个函数库包含了大量常用,但urp里没有的算法,免费分享求点个赞!!
Shader "URP_Skybox3D"
{
Properties
{
[Header(Moon)]
[NoScaleOffset] _MoonCubeMap ("月亮贴图", Cube) = "_Skybox" {}
_MoonRadius ("月亮半径", Range(0, 1)) = 0.05
_MoonMaskRadius("月亮遮罩半径", range(1, 25)) = 10
_MoonSmooth("月亮边缘平滑度", Range(0, 1)) = 0.7
_MoonExposure("月亮曝光度", range(0, 1)) = 1
[Header(Sun)]
_SunRadius ("太阳半径", float) = 1.0
[Header(SkyColor)]
// 基础颜色
_MorningColor("Morning Color", Color) = (1,0.5,0,1)
_NoonColor("Noon Color", Color) = (0,0.5,1,1)
_NightfallColor("Nightfall Color", Color) = (0.5,0,1,1)
_NightColor("Night Color", Color) = (0,0.1,0.2,1)
// 地平线颜色
_MorningHorizonColor("Morning Horizon", Color) = (1,0.3,0,1)
_NoonHorizonColor("Noon Horizon", Color) = (0,0.8,1,1)
_NightfallHorizonColor("Nightfall Horizon", Color) = (0.8,0,1,1)
_NightHorizonColor("Night Horizon", Color) = (0,0.2,0.4,1)
// 时间控制参数
_MorningDuration("Morning Duration", Range(0,1)) = 0.2
_NightfallDuration("Nightfall Duration", Range(0,1)) = 0.8
// 地平线效果参数
_HorizonHeight("Horizon Height", Float) = 0.1
_HorizonIntensity("Horizon Intensity", Range(0,5)) = 1.0
_HorizonPow("Horizon Power", Range(0.1,10)) = 2.0
// 太阳相关
_SumBloomMask ("Sun Bloom Mask", Range(0.01, 10)) = 1
_SumBloomMask1 ("Sun Bloom Mask1", Range(0.01, 10)) = 1
_SumBloomMask2 ("Sun Bloom Mask2", Range(0.01, 10)) = 1
_SumBloomMask3 ("Sun Bloom Mask3", Range(0.01, 10)) = 1
_SunBloomColor ("Sun Bloom Color", Color) = (1, 1, 1, 1)
_SunBloomColor1 ("Sun Bloom Color1", Color) = (1, 1, 1, 1)
_SunBloomColor2 ("Sun Bloom Color2", Color) = (1, 1, 1, 1)
_SunBloomColor3 ("Sun Bloom Color3", Color) = (1, 1, 1, 1)
_SunBloomColor4 ("Sun Bloom Color4", Color) = (1, 1, 1, 1)
// 月亮相关
_MoonBloomColor ("Moon Bloom Color", Color) = (1, 1, 1, 1)
//高天云
_HighCloudTex ("High Cloud Texture", 2D) = "white" {}
_HighCloudSpeed ("High Cloud Speed", Float) = 1.0
//银河
_Galaxy("Galaxy", 2D) = "white" {}
_GalaxyNoise("Galaxy Noise", 2D) = "white" {}
_GalaxyColor("Galaxy Color", Color) = (1, 1, 1, 1)
_GalaxyColor2("Galaxy Color 2", Color) = (1, 1, 1, 1)
_GalaxyColor1("Galaxy Color 1", Color) = (1, 1, 1, 1)
_ColorPos("Color Position", Range(0.0, 1.0)) = 0.5
_ColorPos1("Color Position 1", Range(0.0, 1.0)) = 0.5
_GalaxyNoiseIndensity("GalaxyNoiseIndensity", Range(0.0, 1.0)) = 0.5
_GalaxySpeed("GalaxySpeed", Float) = 1
//星星
_StarNoiseTex("Star Noise Texture", 2D) = "white" {}
_StarRotateSpeed("StarRotateSpeed", Float) = 1
//主云
[Header(Cloud)]
_CloudRange("云范围CloudRange", Range(-1.0 , 1.0)) = 0
_CloudSpeed("云速度CloudSpeed", Range(0 , 1.0)) = 0
[Header(fbmDomian)]
_NoiseScale ("Noise Scale", Float) = 0.2 // 噪声整体缩放
_Octaves ("Octaves", Int) = 5 // 分形叠加层数
_Lacunarity ("Lacunarity", Float) = 3.26 // 频率倍增系数
_Gain ("Gain", Float) = 0.46 // 振幅衰减系数
_Contrast ("Contrast", Range(0.5, 2.0)) = 0.5 // 噪声对比度
_Bias ("Bias", Range(-1, 1)) = -1 // 噪声偏置
_YScale ("Y-axis Scale", Float) = 2.3 // Z轴缩放(控制深度方向细节)
[Header(Voronoi)]
_CellSize ("Cell Size细胞大小", Range(5,30)) = 7 // 细胞大小
_Jitter ("Irregularity细胞不规则度", Range(0.2, 1.0)) = 1 // 细胞不规则度
_EdgeSharpness ("Edge Sharpness边缘锐利度", Range(5, 50)) = 50 // 边缘锐利度
_EdgeThreshold ("Edge Thickness 边缘厚度", Range(0.0, 0.15)) = 0 // 边缘厚度
_DarkenCenter ("Center Darkness细胞中心暗化程度", Range(0, 1)) = 1 // 细胞中心暗化程度
//碎云
[Header(fractus)]
_StretchUVIntensity("碎云的压缩StretchUVIntensity", Float) = 2
_StretchUVSmoothness("碎云朵高度StretchUVSmoothness", Range(1.5 , 12.0)) = 12
_StretchUVstretchScale("碎云边缘的密度StretchUVstretchScale", Float) = 2
_DomainAccount("碎云大小DomainAccount", Float) = 2.6
_DomainCloudIndensity("碎云强度DomainCloudIndensity", Range(0,1)) = 1
_CloudhighlightColor("CloudhighlightColor", Color) = (1, 1, 1, 1)
_CloudmiddleColor("CloudmiddleColor", Color) = (1, 1, 1, 1)
_CloudshadowColor("CloudshadowColor", Color) = (0, 0, 0, 0)
[Header(2thlayer)]
_RotateSunMoon ("第二层太阳月亮旋转RotateSunMoon", Float) = 0 // 第二层太阳月亮旋转
[Header(Cloudx)]
_CloudRangex("云范围CloudRange", Range(-1.0 , 1.0)) = 0
_CloudSpeedx("云速度CloudSpeed", Range(0 , 1.0)) = 0
[Header(fbmDomianx)]
_NoiseScalex ("Noise Scale", Float) = 0.2 // 噪声整体缩放
_Octavesx ("Octaves", Int) = 5 // 分形叠加层数
_Lacunarityx ("Lacunarity", Float) = 3.26 // 频率倍增系数
_Gainx ("Gain", Float) = 0.46 // 振幅衰减系数
_Contrastx ("Contrast", Range(0.5, 2.0)) = 0.5 // 噪声对比度
_Biasx ("Bias", Range(-1, 1)) = -1 // 噪声偏置
_YScalex ("Y-axis Scale", Float) = 2.3 // Z轴缩放(控制深度方向细节)
[Header(Voronoi)]
_CellSizex ("Cell Size细胞大小", Range(5,30)) = 7 // 细胞大小
_Jitterx ("Irregularity细胞不规则度", Range(0.2, 1.0)) = 1 // 细胞不规则度
_EdgeSharpnessx ("Edge Sharpness边缘锐利度", Range(5, 50)) = 50 // 边缘锐利度
_EdgeThresholdx ("Edge Thickness 边缘厚度", Range(0.0, 0.15)) = 0 // 边缘厚度
_DarkenCenterx ("Center Darkness细胞中心暗化程度", Range(0, 1)) = 1 // 细胞中心暗化程度
//碎云
[Header(fractus)]
_StretchUVIntensityx("碎云的压缩StretchUVIntensity", Float) = 2
_StretchUVSmoothnessx("碎云朵高度StretchUVSmoothness", Range(1.5 , 12.0)) = 12
_StretchUVstretchScalex("碎云边缘的密度StretchUVstretchScale", Float) = 2
_DomainAccountx("碎云大小DomainAccount", Float) = 2.6
_DomainCloudIndensityx("碎云强度DomainCloudIndensity", Range(0,1)) = 1
_CloudhighlightColorx("CloudhighlightColor", Color) = (1, 1, 1, 1)
_CloudmiddleColorx("CloudmiddleColor", Color) = (1, 1, 1, 1)
_CloudshadowColorx("CloudshadowColor", Color) = (0, 0, 0, 0)
_LayerMask("LayerMask", CUBE) = "white" {}
_LayerRange("_LayerRange",Range(0,2)) = 2
_LayerColor("_LayerColor",Color) = (1,1,1,1)
}
SubShader
{
Tags {
"RenderType"="Opaque"
"RenderPipeline"="UniversalPipeline"
"Queue"="Geometry"}
Pass
{
HLSLPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Core.hlsl"
#include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Lighting.hlsl"
//uv的数学计算
#include "Assets/Arts/hlsl/UVmath.hlsl"
//图像处理相关
#include "Assets/Arts/hlsl/ImageProcessing.hlsl"
//程序化纹理相关
#include "Assets/Arts/hlsl/Noisegeneration.hlsl"
CBUFFER_START(UnityPerMaterial)
float _SunRadius;
float _MoonRadius;
float _MoonMaskRadius;
float _MoonSmooth;
float _MoonExposure;
// Sky Colors
float4 _MorningColor;
float4 _NoonColor;
float4 _NightfallColor;
float4 _NightColor;
// Horizon Colors
float4 _MorningHorizonColor;
float4 _NoonHorizonColor;
float4 _NightfallHorizonColor;
float4 _NightHorizonColor;
// Timing
float _MorningDuration;
float _NightfallDuration;
// Horizon Effects
float _HorizonHeight;
float _HorizonIntensity;
float _HorizonPow;
float _SumBloomMask;
float _SumBloomMask1;
float _SumBloomMask2;
float _SumBloomMask3;
float4 _SunBloomColor;
float4 _SunBloomColor1;
float4 _SunBloomColor2;
float4 _SunBloomColor3;
float4 _SunBloomColor4;
float4 _MoonBloomColor;
//高天云
float _HighCloudSpeed;
float4 _HighCloudTex_ST;
//银河
float4 _GalaxyColor;
float4 _GalaxyColor2;
float4 _GalaxyColor1;
float _ColorPos;
float _ColorPos1;
float4 _GalaxyNoise_ST;
float4 _Galaxy_ST;
float _GalaxyNoiseIndensity;
float _GalaxySpeed;
//星星
float4 _galaxyColor;
float _galaxyG;
float4 _StarTex_ST;
float4 _StarNoiseTex_ST;
float _StarRotateSpeed;
//主云
float _StretchUVIntensity;
float _StretchUVSmoothness;
float _StretchUVstretchScale;
float _DomainAccount;
float _DomainCloudIndensity;
float _CloudRange;
float _CloudSpeed;
float4 _CloudhighlightColor;
float4 _CloudmiddleColor;
float4 _CloudshadowColor;
//fbm
float _NoiseScale;
int _Octaves;
float _Lacunarity;
float _Gain;
float _Contrast;
float _Bias;
float _YScale;
//voronoi
float _CellSize;
float _Jitter;
float _EdgeSharpness;
float _EdgeThreshold;
float _DarkenCenter;
//2thlayer
float _RotateSunMoon;
//主云
float _StretchUVIntensityx;
float _StretchUVSmoothnessx;
float _StretchUVstretchScalex;
float _DomainAccountx;
float _DomainCloudIndensityx;
float _CloudRangex;
float _CloudSpeedx;
float4 _CloudhighlightColorx;
float4 _CloudmiddleColorx;
float4 _CloudshadowColorx;
//fbm
float _NoiseScalex;
int _Octavesx;
float _Lacunarityx;
float _Gainx;
float _Contrastx;
float _Biasx;
float _YScalex;
//voronoi
float _CellSizex;
float _Jitterx;
float _EdgeSharpnessx;
float _EdgeThresholdx;
float _DarkenCenterx;
float _LayerRange;
float4 _LayerColor;
//float4 _LayerMask_ST;
CBUFFER_END
TEXTURECUBE(_MoonCubeMap);
SAMPLER(sampler_MoonCubeMap);
TEXTURE2D(_HighCloudTex);
SAMPLER(sampler_HighCloudTex);
TEXTURE2D(_GalaxyNoise);
SAMPLER(sampler_GalaxyNoise);
TEXTURE2D(_Galaxy);
SAMPLER(sampler_Galaxy);
TEXTURE2D(_StarNoiseTex);
SAMPLER(sampler_StarNoiseTex);
TEXTURECUBE(_LayerMask);
SAMPLER(sampler_LayerMask);
float4x4 RotateXMatrix(float angle)
{
float sinAngle = sin(angle);
float cosAngle = cos(angle);
// 绕X轴旋转矩阵
return float4x4(
1.0, 0.0, 0.0, 0.0,
0.0, cosAngle, -sinAngle, 0.0,
0.0, sinAngle, cosAngle, 0.0,
0.0, 0.0, 0.0, 1.0
);
}
float2 RotateUV(float2 uv, float2 center, float angle)
{
float2 uvOffset = uv - center;
float2 rotatedUV = float2(
uvOffset.x * cos(angle) - uvOffset.y * sin(angle),
uvOffset.x * sin(angle) + uvOffset.y * cos(angle)
);
return rotatedUV + center;
}
float sphIntersect(float3 rayDir, float3 spherePos, float radius)
{
float3 oc = -spherePos; // 射线起点到球心的向量(假设射线起点在原点)
float b = dot(oc, rayDir);// 向量点积,用于求解二次方程
float c = dot(oc, oc) - radius * radius;// 球方程展开项
float h = b * b - c;//二次方程判别式(b²-4ac,这里a=1简化
if(h < 0.0) return -1.0;// 无交点
h = sqrt(h);
return -b - h;// 视点距离月球表面距离
}
// 计算绕Y轴旋转的矩阵
float4x4 RotationY(float angleDegrees) {
float rad = radians(angleDegrees);
float c = cos(rad);
float s = sin(rad);
// 列主序矩阵:每一行是float4,代表矩阵的一列
return float4x4(
c, 0, s, 0, // 第一列 (x轴分量)
0, 1, 0, 0, // 第二列 (y轴分量)
-s, 0, c, 0, // 第三列 (z轴分量)
0, 0, 0, 1 // 第四列 (齐次坐标分量)
);
}
// 归一化旋转轴:(0.5, 0.5, 0.5) → 单位向量
float3 GetRotationAxis() {
float3 axis = float3(0.5, 0.5, 0.5);
return normalize(axis); // 归一化后为 (1/√3, 1/√3, 1/√3)
}
// 构建绕任意轴旋转的矩阵(罗德里格斯公式)
float4x4 RotationAroundAxis(float angleDegrees) {
float3 axis = GetRotationAxis();
float rad = radians(angleDegrees);
float c = cos(rad);
float s = sin(rad);
float oneMinusC = 1.0 - c;
// 反对称矩阵K的元素
float Kx = axis.x;
float Ky = axis.y;
float Kz = axis.z;
// 构建旋转矩阵(列主序)
return float4x4(
// 第一列
c + Kx*Kx*oneMinusC,
Kx*Ky*oneMinusC - Kz*s,
Kx*Kz*oneMinusC + Ky*s,
0,
// 第二列
Ky*Kx*oneMinusC + Kz*s,
c + Ky*Ky*oneMinusC,
Ky*Kz*oneMinusC - Kx*s,
0,
// 第三列
Kz*Kx*oneMinusC - Ky*s,
Kz*Ky*oneMinusC + Kx*s,
c + Kz*Kz*oneMinusC,
0,
// 第四列(平移分量)
0, 0, 0, 1
);
}
struct appdata
{
float4 vertex : POSITION;
float3 uv : TEXCOORD0;
};
struct v2f
{
float3 uv : TEXCOORD0;
float4 vertex : SV_POSITION;
float3 posWS : TEXCOORD1;
};
v2f vert (appdata v)
{
v2f o;
o.posWS = TransformObjectToWorld(v.vertex.xyz); // 获取世界空间坐标
o.vertex = TransformObjectToHClip(v.vertex.xyz);
o.uv = v.uv;
return o;
}
float4 frag (v2f i) : SV_Target
{
// 对于天空球(包围整个场景的巨大球体),其顶点有两个关键特性:
//
// 天空球的半径极大(远大于场景中其他物体);
// 摄像机通常位于天空球的中心(原点附近)。
//
// 因此,天空球上任意顶点的世界坐标posWS,其方向向量(从原点指向该顶点)近似等于摄像机看向该顶点的视线方向
float3 posWS = normalize(i.posWS.xyz);
Light light = GetMainLight();
// Moon
float3 lDir = normalize(-light.direction);
half moonIntersect = sphIntersect(posWS, lDir, _MoonRadius);
half moonDist = distance(i.uv, lDir);
half moonMask = 1 - smoothstep(_MoonSmooth, 1.0, moonDist * _MoonMaskRadius);
//return moonDist * moonIntersect;
float moonedge = pow(saturate(moonDist * moonIntersect),5)*1000;
// Moon Lighting
// //posWS * moonIntersect 计算射线与月球表面的交点坐标
half3 moonNormal = normalize(lDir - posWS * moonIntersect);//获得球体的法向量
//旋转矩阵,作用是对法向量进行空间旋转,修正方向以匹配立方体贴图的纹理坐标(不使用这个自己定义也行)
half3x3 correctionMatrix = half3x3(
0, -0.2588190451, -0.9659258263,
0.08715574275, 0.9622501869, -0.2578341605,
0.9961946981, -0.08418598283, 0.02255756611
);
moonNormal = mul(correctionMatrix, moonNormal);
float moonNdotL = saturate(dot(moonNormal, lDir)) * 0.5 + 0.5;
half3 moonTex = SAMPLE_TEXTURECUBE(_MoonCubeMap, sampler_MoonCubeMap, moonNormal).rgb;
half3 moonColor = moonMask * moonNdotL * exp2(_MoonExposure) * moonTex+moonedge;
// Sun
half sunDist = distance(i.uv, light.direction);
half sunArea = 1 - smoothstep(0.7, 1.0, sunDist * _SunRadius);
half3 sunColor = float3(sunArea, sunArea, sunArea);
// Sky Gradient---------------------------------------------------------------------------------------------------------------------
float4 dayColor = float4(0,0,0,1);
float4 nightColor = float4(0,0,0,1);
float3 lDirRamp = (light.direction + 1.0) * 0.5;
float4 horizonMask = smoothstep(_HorizonHeight, 0.0, i.uv.y) *
smoothstep(_HorizonHeight, 0.0, -i.uv.y) *
_HorizonIntensity;
float4 dayHorizonColor = float4(0,0,0,0);
float4 nightHorizonColor = float4(0,0,0,0);
float4 daymixColor = float4(0,0,0,0);
float4 nightmixColor = float4(0,0,0,0);
if(lDirRamp.y > 0.5 && lDirRamp.y < 1.0)
{
dayColor = lerp(lerp(_MorningColor, _NoonColor, smoothstep(0,_MorningDuration,lDirRamp.z)),
_NightfallColor, smoothstep(_NightfallDuration, 1.0, lDirRamp.z)) * step(0.5, lDirRamp.y);
dayHorizonColor = lerp(lerp(_MorningHorizonColor, _NoonHorizonColor, smoothstep(0,_MorningDuration,lDirRamp.z)),
_NightfallHorizonColor, smoothstep(_NightfallDuration, 1.0, lDirRamp.z)) *
step(0.5, lDirRamp.y) * pow(saturate(horizonMask), _HorizonPow);
daymixColor = dayHorizonColor + dayColor;
}
else
{
nightColor = lerp(_MorningColor, lerp(_NightColor, _NightfallColor, smoothstep(_NightfallDuration,1.0,lDirRamp.z)),
smoothstep(0.0, _MorningDuration, lDirRamp.z)) *
step(-0.5, -lDirRamp.y);
nightHorizonColor = lerp(_MorningHorizonColor, lerp(_NightHorizonColor,_NightfallHorizonColor,smoothstep(_NightfallDuration,1.0,lDirRamp.z)),
smoothstep(0.0, _MorningDuration, lDirRamp.z)) *
step(-0.5, -lDirRamp.y) * pow(saturate(horizonMask), _HorizonPow);
nightmixColor = nightHorizonColor + nightColor;
}
float4 skyColor = daymixColor + nightmixColor;
//return skyColor;
//太阳月亮Bloom---------------------------------------------------------------------------------------------------------------------
half moonBloomMask = (1 - smoothstep(0, 1, moonDist * 3 )) * smoothstep(0.4, 1, lDir.y);
//return moonBloomMask;
moonColor *= lerp(0.1, 0.5, smoothstep(0.1, 1, posWS.y));
half4 moonBloomColor = moonBloomMask * skyColor * 5 * _MoonBloomColor;
//return moonBloomColor;
float Mask = saturate(dot(light.direction, posWS));
//float Mask1 = lerp(_SumBloomMask1,_SumBloomMask,smoothstep(0, _MorningDuration, lDirRamp.z));
float sunBloomMask = pow(Mask, _SumBloomMask);
float sunBloomMask1 = pow(Mask, _SumBloomMask1);
float sunBloomMask2 = pow(Mask, _SumBloomMask2);
float sunBloomMask3 = pow(Mask, _SumBloomMask3);
half4 sunBloomColor = lerp(lerp(_SunBloomColor1 * sunBloomMask1 + _SunBloomColor3 * (sunBloomMask-sunBloomMask1), _SunBloomColor* sunBloomMask, smoothstep(0, _MorningDuration, lDirRamp.z)),
_SunBloomColor2* sunBloomMask2 + _SunBloomColor4 * (sunBloomMask3 - sunBloomMask2), smoothstep(_NightfallDuration, 1, lDirRamp.z));
//return sunBloomColor;
// 高天云---------------------------------------------------------------------------------------------------------------------
float2 highCloudUV = TRANSFORM_TEX(i.uv.xy, _HighCloudTex);
highCloudUV.x = i.uv.z < 0 ? 1-highCloudUV.x : highCloudUV.x;
highCloudUV.x += _Time.x * 0.1 * _HighCloudSpeed;
highCloudUV.y = smoothstep(0,1,highCloudUV.y);
float highCloudTex = SAMPLE_TEXTURE2D(_HighCloudTex, sampler_HighCloudTex, highCloudUV).a;
highCloudTex = smoothstep(0.004,1,highCloudTex) * abs(i.uv.z);
//主要天空整合---------------------------------------------------------------------------------------------------------------------------
float4 SumColor = saturate(float4(sunColor + moonColor,1) + skyColor * (1-sunBloomMask*0.5) + sunBloomColor + moonBloomColor + highCloudTex);
//银河----------------------------------------------------------------------------------------------------------------------------------
float2 galaxyNoiseUV = TRANSFORM_TEX(i.uv.xz, _GalaxyNoise);
galaxyNoiseUV.x += _Time.x * 0.1 * _GalaxySpeed;
float4 galaxyNoise = SAMPLE_TEXTURE2D(_GalaxyNoise, sampler_GalaxyNoise, galaxyNoiseUV);
galaxyNoise *= _GalaxyNoiseIndensity;
float2 galaxyUV = TRANSFORM_TEX(i.uv.xz, _Galaxy);
galaxyUV += (galaxyNoise.rg - 0.1) * 0.8 * galaxyUV.xy;
float4 galaxy = SAMPLE_TEXTURE2D(_Galaxy, sampler_Galaxy, galaxyUV)* smoothstep(0,1,i.uv.y-0.3);
float4 galaxyColor = lerp(lerp(_GalaxyColor,_GalaxyColor2,smoothstep(0.2,_ColorPos,galaxyUV.x)), _GalaxyColor,smoothstep(_ColorPos1,0.7,galaxyUV.x))
* (galaxy.g - galaxy.r*2) + _GalaxyColor1* galaxy.r ;
galaxyColor *= 0.4;
//星星----------------------------------------------------------------------------------------------------------------------------------
float starNoiseTex = smoothstep(0.4,0.45,SAMPLE_TEXTURE2D(_StarNoiseTex, sampler_StarNoiseTex, i.uv.xz).r);
float Voronoistar = VoronoiCellTime(mul(RotationAroundAxis(_Time.y*_StarRotateSpeed),float4(posWS.x,pow(abs(posWS.y) ,_YScale),posWS.z,1)), 0.01, 0.8, _EdgeThreshold, _EdgeSharpness, _DarkenCenter,_Time.x*_CloudSpeed).r;
Voronoistar = step(Voronoistar,0.15)*starNoiseTex*2;
//银河遮罩内的星星更亮
float starColor = Voronoistar*starNoiseTex;
starColor = Voronoistar *galaxy.g*5 + Voronoistar*(1-galaxy.g)*0.5;
float4 starAndGalaxy = (galaxyColor+float4(starColor, starColor, starColor, 1)) * smoothstep(0,1,lDir.y);
//除了程序化云其他的颜色
float4 c = SumColor + lerp(starAndGalaxy,float4(moonColor,1),moonMask) + float4(highCloudTex,highCloudTex,highCloudTex,1) * skyColor;
//---------------------------------------------------------------------------------------------------------------------
// 程序化云
float3 colorBloom = SaturationAdjustment(moonBloomColor.rgb + sunBloomColor.rgb,0);
float2 cloudUV = i.uv.xz*0.5+0.5;
float Domain3d = DomainNoiseMap3D(mul(RotationY(_Time.x*_CloudSpeed*10) , posWS),_NoiseScale,_YScale,_Octaves,_Lacunarity,_Gain,_Bias,_Contrast);
float Domain3d0 = DomainNoiseMap3D(mul(RotationY(_Time.x*_CloudSpeed*10) , posWS),_NoiseScale,_YScale,_Octaves,_Lacunarity,_Gain+0.5,_Bias,_Contrast);
float Voronoia = VoronoiCellTime(float4(posWS.x,pow(posWS.y ,_YScale),posWS.z,1), _CellSize/100, _Jitter, _EdgeThreshold, _EdgeSharpness, _DarkenCenter,_Time.x*_CloudSpeed).r;
Voronoia = pow(Voronoia,1);
Voronoia = smoothstep (-0.1,1,Voronoia);
float Voronoib = VoronoiCellTime(float3(posWS.x,posWS.y*_YScale*1.5,posWS.z), _CellSize*15/100, _Jitter, _EdgeThreshold, _EdgeSharpness, _DarkenCenter,_Time.x*_CloudSpeed);
Voronoib = pow(Voronoib,0.8);
Voronoib = smoothstep (-0.1,1,Voronoib);
float Voronoii = pow(lerp(Voronoia,Voronoib,0.89),3) + Domain3d0*0.5;
float cloudi = saturate((Voronoii + Domain3d) -_CloudRange);
float cloudrangei = saturate((cloudi-0.35) * 3);
float Voronoic = VoronoiCellTime(float3(posWS.x,pow(posWS.y ,_YScale-0.3),posWS.z), _CellSize/100, _Jitter, _EdgeThreshold, _EdgeSharpness, _DarkenCenter,_Time.x*_CloudSpeed);
Voronoic = pow(Voronoia,1);
Voronoic = smoothstep (-0.1,1,Voronoia);
float Voronoid = VoronoiCellTime(float3(posWS.x,posWS.y*(_YScale-0.3)*1.5,posWS.z), _CellSize*15/100, _Jitter, _EdgeThreshold, _EdgeSharpness, _DarkenCenter,_Time.x*_CloudSpeed);
Voronoid = pow(Voronoib,0.8);
Voronoid = smoothstep (-0.1,1,Voronoib);
float Voronoii0 = pow(lerp(Voronoic,Voronoid,0.88),0.8) + Domain3d0*0.5;
float cloudi0 = saturate((Voronoii0 + Domain3d) -_CloudRange);
float cloudrangei0 = saturate((cloudi0-0.35) * 3);
cloudrangei0 = pow(cloudrangei0,8);
float3 cloudColor = lerp((skyColor+_CloudshadowColor+colorBloom),(skyColor+0.1+_CloudmiddleColor+colorBloom*2),cloudrangei);
cloudColor = lerp(cloudColor,(skyColor+0.1+_CloudhighlightColor+colorBloom*20),cloudrangei0);
float atmosphere = 1-saturate(abs(i.uv.y)-0.6);
cloudColor *= atmosphere;
cloudColor = lerp(skyColor,cloudColor, pow(abs(i.uv.y),0.4));
float2 cloudUV3 = RotateUV(cloudUV , float2(0.5,0.5), _Time.x * _CloudSpeed * 0.05);
cloudUV3 = midStretchUV(cloudUV3,_StretchUVIntensity,_StretchUVSmoothness,_StretchUVstretchScale);
float DomainNoise1 = DomainNoiseMap (cloudUV3 * _DomainAccount*0.2);
DomainNoise1 = pow(DomainNoise1,7);
DomainNoise1 *= _DomainCloudIndensity;
c += saturate(DomainNoise1 - horizonMask);
c = lerp(float4(cloudColor,1),c, saturate(cloudrangei0 + horizonMask) );
//---------------------------------------------------------------------------------------------------------------
//---------------------------------------------------------------------------------------------------------------
// float3 posWS = normalize(i.posWS.xyz);
// Light light = GetMainLight();
//
// Moonx
float3 lDirx = mul(RotateXMatrix(_RotateSunMoon),normalize(light.direction));
half moonIntersectx = sphIntersect(posWS, lDirx, _MoonRadius);
// //return pow(moonIntersect,10);
half moonDistx = distance(i.uv, lDirx);
half moonMaskx = 1 - smoothstep(_MoonSmooth, 1.0, moonDistx * _MoonMaskRadius);
// //return moonMask;
float moonedgex = pow(saturate(moonDistx * moonIntersectx),5)*1000;
// // Moon Lighting
// // //posWS * moonIntersect 计算射线与月球表面的交点坐标
half3 moonNormalx = normalize(lDirx - posWS * moonIntersectx);//获得球体的法向量
// //旋转矩阵,作用是对法向量进行空间旋转,修正方向以匹配立方体贴图的纹理坐标(不使用这个自己定义也行)
// half3x3 correctionMatrix = half3x3(
// 0, -0.2588190451, -0.9659258263,
// 0.08715574275, 0.9622501869, -0.2578341605,
// 0.9961946981, -0.08418598283, 0.02255756611
// );
moonNormal = mul(correctionMatrix, moonNormalx);
float moonNdotLx = saturate(dot(moonNormalx, lDirx)) * 0.5 + 0.5;
half3 moonTexx = SAMPLE_TEXTURECUBE(_MoonCubeMap, sampler_MoonCubeMap, moonNormalx).rgb;
half3 moonColorx = moonMaskx * moonNdotLx * exp2(_MoonExposure) * moonTexx + moonedgex;
// Sun--------------------------------------------------------------------------------------------------------------
half sunDistx = distance(i.uv, -lDirx);
half sunAreax = 1 - smoothstep(0.7, 1.0, sunDistx * _SunRadius);
half3 sunColorx = float3(sunAreax, sunAreax, sunAreax);
// Sky Gradient---------------------------------------------------------------------------------------------------------------------
float4 dayColorx = float4(0,0,0,1);
float4 nightColorx = float4(0,0,0,1);
float3 lDirRampx = (-lDirx + 1.0) * 0.5;
float4 dayHorizonColorx = float4(0,0,0,0);
float4 nightHorizonColorx = float4(0,0,0,0);
float4 daymixColorx = float4(0,0,0,0);
float4 nightmixColorx = float4(0,0,0,0);
//
if(lDirRampx.y > 0.5 && lDirRampx.y < 1.0)
{
dayColorx = lerp(lerp(_MorningColor, _NoonColor, smoothstep(0,_MorningDuration,lDirRampx.z)),
_NightfallColor, smoothstep(_NightfallDuration, 1.0, lDirRampx.z)) * step(0.5, lDirRampx.y);
dayHorizonColorx = lerp(lerp(_MorningHorizonColor, _NoonHorizonColor, smoothstep(0,_MorningDuration,lDirRampx.z)),
_NightfallHorizonColor, smoothstep(_NightfallDuration, 1.0, lDirRampx.z)) *
step(0.5, lDirRampx.y) * pow(saturate(horizonMask), _HorizonPow);
daymixColorx = dayColorx+dayHorizonColorx;
// return daymixColor;
}
else
{
nightColorx = lerp(_MorningColor, lerp(_NightColor, _NightfallColor, smoothstep(_NightfallDuration,1.0,lDirRampx.z)),
smoothstep(0.0, _MorningDuration, lDirRampx.z)) *
step(-0.5, -lDirRampx.y);
nightHorizonColorx = lerp(_MorningHorizonColor, lerp(_NightHorizonColor,_NightfallHorizonColor,smoothstep(_NightfallDuration,1.0,lDirRampx.z)),
smoothstep(0.0, _MorningDuration, lDirRampx.z)) *
step(-0.5, -lDirRampx.y) * pow(saturate(horizonMask), _HorizonPow);
nightmixColorx = nightHorizonColorx + nightColorx;
// return nightmixColor;
}
//
float4 skyColorx = daymixColorx + nightmixColorx;
//return skyColorx;
//太阳月亮Bloom---------------------------------------------------------------------------------------------------------------------
half moonBloomMaskx = (1 - smoothstep(0, 1, moonDistx *3 )) * smoothstep(0.4, 1, -lDir.y);
moonColorx *= lerp(0.1, 0.5, smoothstep(0.1, 1, posWS.y));
half4 moonBloomColorx = moonBloomMaskx * skyColorx * 2* _MoonBloomColor;
//return moonBloomColorx;
float Maskx = saturate(dot(-lDirx, posWS));
//float Mask1 = lerp(_SumBloomMask1,_SumBloomMask,smoothstep(0, _MorningDuration, lDirRamp.z));
float sunBloomMaskx = pow(Maskx, _SumBloomMask);
float sunBloomMask1x = pow(Maskx, _SumBloomMask1);
float sunBloomMask2x = pow(Maskx, _SumBloomMask2);
float sunBloomMask3x = pow(Maskx, _SumBloomMask3);
half4 sunBloomColorx = lerp(lerp(_SunBloomColor1 * sunBloomMask1x + _SunBloomColor3 * (sunBloomMaskx-sunBloomMask1x), _SunBloomColor* sunBloomMaskx, smoothstep(0, _MorningDuration, lDirRampx.z)),
_SunBloomColor2* sunBloomMask2x + _SunBloomColor4 * (sunBloomMask3x - sunBloomMask2x), smoothstep(_NightfallDuration, 1, lDirRampx.z));
//return sunBloomColorx;
//第二层天空整合---------------------------------------------------------------------------------------------------------------------------
float4 SumColorx = saturate(float4(sunColorx + moonColorx,1) + skyColorx * (1-sunBloomMaskx*0.5) + sunBloomColorx + moonBloomColorx + highCloudTex);
//return SumColorx;
//星星---------------------------------------------------------------------------------------------------------
//starColorx = Voronoistar *galaxy.g*5 + Voronoistar*(1-galaxy.g)*0.5;
float4 starAndGalaxyx = (galaxyColor+float4(starColor, starColor, starColor, 1)) * smoothstep(0,1,-lDir.y);
//除了程序化云其他的颜色
float4 cx = SumColorx + lerp(starAndGalaxyx,float4(moonColorx,1),moonMaskx) + float4(highCloudTex,highCloudTex,highCloudTex,1) * skyColor;
//---------------------------------------------------------------------------------------------------------------------
// 程序化云
float3 colorBloomx = SaturationAdjustment(moonBloomColorx.rgb + sunBloomColorx.rgb,0);
float2 cloudUVx = i.uv.xz*0.5+0.5;
float Domain3dx = DomainNoiseMap3D(mul(RotationY(_Time.x*_CloudSpeedx*10) , posWS),_NoiseScalex,_YScalex,_Octavesx,_Lacunarityx,_Gainx,_Biasx,_Contrastx);
float Domain3d0x = DomainNoiseMap3D(mul(RotationY(_Time.x*_CloudSpeedx*10) , posWS),_NoiseScalex,_YScalex,_Octavesx,_Lacunarityx,_Gainx+0.5,_Biasx,_Contrastx);
float Voronoiax = VoronoiCellTime(float4(posWS.x,pow(posWS.y ,_YScalex),posWS.z,1), _CellSizex/100, _Jitterx, _EdgeThresholdx, _EdgeSharpnessx, _DarkenCenterx,_Time.x*_CloudSpeedx).r;
Voronoiax = pow(Voronoiax,1);
Voronoiax = smoothstep (-0.1,1,Voronoiax);
float Voronoibx = VoronoiCellTime(float3(posWS.x,posWS.y*_YScalex*1.5,posWS.z), _CellSizex*15/100, _Jitterx, _EdgeThresholdx, _EdgeSharpnessx, _DarkenCenterx,_Time.x*_CloudSpeedx);
Voronoibx = pow(Voronoibx,0.8);
Voronoibx = smoothstep (-0.1,1,Voronoibx);
float Voronoiix = pow(lerp(Voronoiax,Voronoibx,0.89),3) + Domain3d0x*0.5;
float cloudix = saturate((Voronoiix + Domain3dx) -_CloudRangex);
float cloudrangeix = saturate((cloudix-0.35) * 3);
float Voronoicx = VoronoiCellTime(float3(posWS.x,pow(posWS.y ,_YScalex-0.3),posWS.z), _CellSizex/100, _Jitterx, _EdgeThresholdx, _EdgeSharpnessx, _DarkenCenterx,_Time.x*_CloudSpeedx);
Voronoic = pow(Voronoiax,1);
Voronoic = smoothstep (-0.1,1,Voronoiax);
float Voronoidx = VoronoiCellTime(float3(posWS.x,posWS.y*(_YScalex-0.3)*1.5,posWS.z), _CellSizex*15/100, _Jitterx, _EdgeThresholdx, _EdgeSharpnessx, _DarkenCenterx,_Time.x*_CloudSpeedx);
Voronoidx = pow(Voronoibx,0.8);
Voronoidx = smoothstep (-0.1,1,Voronoibx);
float Voronoii0x = pow(lerp(Voronoicx,Voronoidx,0.88),0.8) + Domain3d0x*0.5;
float cloudi0x = saturate((Voronoii0x + Domain3dx) -_CloudRangex);
float cloudrangei0x = saturate((cloudi0x-0.35) * 3);
cloudrangei0x = pow(cloudrangei0x,8);
float3 cloudColorx = lerp((skyColorx+_CloudshadowColorx+colorBloomx),(skyColorx+0.1+_CloudmiddleColorx+colorBloomx*2),cloudrangeix);
cloudColorx = lerp(cloudColorx,(skyColorx+0.1+_CloudhighlightColorx+colorBloomx*20),cloudrangei0x);
float atmospherex = 1-saturate(abs(i.uv.y)-0.6);
cloudColorx *= atmospherex;
cloudColorx = lerp(skyColorx,cloudColorx, pow(abs(i.uv.y),0.4));
float2 cloudUV3x = RotateUV(cloudUVx , float2(0.5,0.5), _Time.x * _CloudSpeedx * 0.05);
cloudUV3x = midStretchUV(cloudUV3x,_StretchUVIntensityx,_StretchUVSmoothnessx,_StretchUVstretchScalex);
float DomainNoise1x = DomainNoiseMap (cloudUV3x * _DomainAccountx*0.2);
DomainNoise1x = pow(DomainNoise1x,7);
DomainNoise1x *= _DomainCloudIndensityx;
cx += saturate(DomainNoise1x - horizonMask);
cx = lerp(float4(cloudColorx,1),cx, saturate(cloudrangei0x + horizonMask) );
//两层天空的混合
//--------------------------------------------------------------------------------------------------------
float2 screenUV = i.vertex.xy / _ScreenParams.xy;
float4 layermask = SAMPLE_TEXTURECUBE(_LayerMask, sampler_LayerMask, i.posWS);
float auroraNoise = SAMPLE_TEXTURE2D(_StarNoiseTex, sampler_StarNoiseTex,float2(screenUV.x*3,screenUV.y*0.4 - _Time.x)*5);//float2(screenUV.x,screenUV.y*3)
float layermask0 = pow(smoothstep(_LayerRange/2,_LayerRange,layermask.r),2);
float layermask00 = pow(smoothstep(_LayerRange/2,_LayerRange+0.3,layermask.r),2);
float layermask000 = smoothstep(_LayerRange/2-0.01,_LayerRange-0.01,layermask.r);
layermask000 = layermask000 - auroraNoise;
//return layermask000;
layermask = saturate(lerp(layermask0,layermask0-auroraNoise+layermask00,_LayerRange));
float4 layerColor = saturate(layermask000-layermask)*_LayerColor*3;
c = lerp(c,cx,layermask.r) + layerColor;
return c;
}
ENDHLSL
}
}
}
二.制作思路
2.1月球的制作思路
2.1.1shader部分(对比后的数学逻辑进行观看)
// 对于天空球(包围整个场景的巨大球体),其顶点有两个关键特性:
//
// 天空球的半径极大(远大于场景中其他物体);
// 摄像机通常位于天空球的中心(原点附近)。
//
// 因此,天空球上任意顶点的世界坐标posWS,其方向向量(从原点指向该顶点)近似等于摄像机看向该顶点的视线方向
float3 posWS = normalize(i.posWS.xyz);
Light light = GetMainLight();
// Moon
float3 lDir = normalize(-light.direction);
half moonIntersect = sphIntersect(posWS, lDir, _MoonRadius);
half moonDist = distance(i.uv, lDir);
half moonMask = 1 - smoothstep(_MoonSmooth, 1.0, moonDist * _MoonMaskRadius);
//return moonDist * moonIntersect;
float moonedge = pow(saturate(moonDist * moonIntersect),5)*1000;
// Moon Lighting
// //posWS * moonIntersect 计算射线与月球表面的交点坐标
half3 moonNormal = normalize(lDir - posWS * moonIntersect);//获得球体的法向量
//旋转矩阵,作用是对法向量进行空间旋转,修正方向以匹配立方体贴图的纹理坐标,使得月球一直对着视点(不使用这个自己定义也行)
half3x3 correctionMatrix = half3x3(
0, -0.2588190451, -0.9659258263,
0.08715574275, 0.9622501869, -0.2578341605,
0.9961946981, -0.08418598283, 0.02255756611
);
moonNormal = mul(correctionMatrix, moonNormal);
float moonNdotL = saturate(dot(moonNormal, lDir)) * 0.5 + 0.5;
half3 moonTex = SAMPLE_TEXTURECUBE(_MoonCubeMap, sampler_MoonCubeMap, moonNormal).rgb;
half3 moonColor = moonMask * moonNdotL * exp2(_MoonExposure) * moonTex+moonedge;
求出球面与视点的距离
float sphIntersect(float3 rayDir, float3 spherePos, float radius)
{
float3 oc = -spherePos; // 射线起点到球心的向量(假设射线起点在原点)
float b = dot(oc, rayDir);// 向量点积,用于求解二次方程
float c = dot(oc, oc) - radius * radius;// 球方程展开项
float h = b * b - c;//二次方程判别式(b²-4ac,这里a=1简化
if(h < 0.0) return -1.0;// 无交点
h = sqrt(h);
return -b - h;// 视点距离月球表面距离
}
2.1.2数学逻辑
要制作月球,需要采样一个cubemap贴图,要采样一个cubemap贴图,就要先构建一套三维的球面法向坐标,作为采样uv。

天空球与月球示意图
就要先构建一套三维的球面法向坐标,作为采样uv
后面就是如何计算出这套采样uv的数学逻辑
天空球上任意一点P,要构建它在球面上的法向,可以将视点和点P相连,这条线与月球的交点为P’。P点在球面上的法向=P’在月球表面的法向。
P’的法向=P’- C;(C是月球的球心点)
P’ - C = vector(VP’) - vector(VC);
已知vector(VC)指的是-light.direction。
此时,只需要求出 vector(VP’)就可以求出P’- C的值了。
此时,需要引入两个方程
1. 射线方程:任意一点P:P = O + t*d;
2. 球面方程:球面上任意一点P,满足|P-C|2 = r2;
已知
射线方程的O为视点(在unity的天空球中,视点=世界坐标原点);
d=1(在unity的天空球中,视点到天空球表面的距离为1);
C点坐标为-light.direction;
r为自定义的球面半径;
现在将射线方程带入球面方程,即可求出t的值;也就是视点到球面的距离;
(在程序中,就是这个函数float sphIntersect(float3 rayDir, float3 spherePos, float radius))
vector(VP’) = t * vector(VP);
现在求出了vector(VP’),vector(VC),就可以求出P’ - C ,也就是P’的法向,P点在球面上的法向。也就是采样月球的uv。

采样月球的uv
通过这个数据取采样月球的Cubemap,即可获得月球在天空球上的显示效果。
2.2天空球渐变的制作思路
float4 dayColor = float4(0,0,0,1);
float4 nightColor = float4(0,0,0,1);
float3 lDirRamp = (light.direction + 1.0) * 0.5;
float4 horizonMask = smoothstep(_HorizonHeight, 0.0, i.uv.y) *
smoothstep(_HorizonHeight, 0.0, -i.uv.y) *
_HorizonIntensity;
float4 dayHorizonColor = float4(0,0,0,0);
float4 nightHorizonColor = float4(0,0,0,0);
float4 daymixColor = float4(0,0,0,0);
float4 nightmixColor = float4(0,0,0,0);
if(lDirRamp.y > 0.5 && lDirRamp.y < 1.0)
{
dayColor = lerp(lerp(_MorningColor, _NoonColor, smoothstep(0,_MorningDuration,lDirRamp.z)),
_NightfallColor, smoothstep(_NightfallDuration, 1.0, lDirRamp.z)) * step(0.5, lDirRamp.y);
dayHorizonColor = lerp(lerp(_MorningHorizonColor, _NoonHorizonColor, smoothstep(0,_MorningDuration,lDirRamp.z)),
_NightfallHorizonColor, smoothstep(_NightfallDuration, 1.0, lDirRamp.z)) *
step(0.5, lDirRamp.y) * pow(saturate(horizonMask), _HorizonPow);
daymixColor = dayHorizonColor + dayColor;
}
else
{
nightColor = lerp(_MorningColor, lerp(_NightColor, _NightfallColor, smoothstep(_NightfallDuration,1.0,lDirRamp.z)),
smoothstep(0.0, _MorningDuration, lDirRamp.z)) *
step(-0.5, -lDirRamp.y);
nightHorizonColor = lerp(_MorningHorizonColor, lerp(_NightHorizonColor,_NightfallHorizonColor,smoothstep(_NightfallDuration,1.0,lDirRamp.z)),
smoothstep(0.0, _MorningDuration, lDirRamp.z)) *
step(-0.5, -lDirRamp.y) * pow(saturate(horizonMask), _HorizonPow);
nightmixColor = nightHorizonColor + nightColor;
}
float4 skyColor = daymixColor + nightmixColor;
这一段主要通过light.direction进行插值,获得不同时间点的天空球固有色的变化;
通过i.uv.y(世界空间的y轴)作为mask,将地平线颜色和天空球固有色进行插值;
2.3太阳月亮的bloom的制作思路
太阳坐标点为(light.direction),求出任意一点距离与太阳坐标点的距离,在进行pow即可获得bloom,月亮的bloom同理
2.4四个面云的制作思路
float2 highCloudUV = TRANSFORM_TEX(i.uv.xy, _HighCloudTex);
highCloudUV.x = i.uv.z < 0 ? 1-highCloudUV.x : highCloudUV.x;
highCloudUV.x += _Time.x * 0.1 * _HighCloudSpeed;
highCloudUV.y = smoothstep(0,1,highCloudUV.y);
float highCloudTex = SAMPLE_TEXTURE2D(_HighCloudTex, sampler_HighCloudTex, highCloudUV).a;
highCloudTex = smoothstep(0.004,1,highCloudTex) * abs(i.uv.z);
依据世界空间坐标在xy平面经过变换后采样云朵的贴图;

2.5银河的制作思路
世界空间坐标的xz平面,通过噪声贴图扰动后,采样一张银河的贴图;
float2 galaxyNoiseUV = TRANSFORM_TEX(i.uv.xz, _GalaxyNoise);
galaxyNoiseUV.x += _Time.x * 0.1 * _GalaxySpeed;
float4 galaxyNoise = SAMPLE_TEXTURE2D(_GalaxyNoise, sampler_GalaxyNoise, galaxyNoiseUV);
galaxyNoise *= _GalaxyNoiseIndensity;
float2 galaxyUV = TRANSFORM_TEX(i.uv.xz, _Galaxy);
galaxyUV += (galaxyNoise.rg - 0.1) * 0.8 * galaxyUV.xy;
float4 galaxy = SAMPLE_TEXTURE2D(_Galaxy, sampler_Galaxy, galaxyUV)* smoothstep(0,1,i.uv.y-0.3);
float4 galaxyColor = lerp(lerp(_GalaxyColor,_GalaxyColor2,smoothstep(0.2,_ColorPos,galaxyUV.x)), _GalaxyColor,smoothstep(_ColorPos1,0.7,galaxyUV.x))
* (galaxy.g - galaxy.r*2) + _GalaxyColor1* galaxy.r ;
galaxyColor *= 0.4;

2.6星星的制作思路
float starNoiseTex = smoothstep(0.4,0.45,SAMPLE_TEXTURE2D(_StarNoiseTex, sampler_StarNoiseTex, i.uv.xz).r);
float Voronoistar = VoronoiCellTime(mul(RotationAroundAxis(_Time.y*_StarRotateSpeed),float4(posWS.x,pow(abs(posWS.y) ,_YScale),posWS.z,1)), 0.01, 0.8, _EdgeThreshold, _EdgeSharpness, _DarkenCenter,_Time.x*_CloudSpeed).r;
Voronoistar = step(Voronoistar,0.15)*starNoiseTex*2;
通过反转voronoi纹理,并进行step后,乘以一个噪声贴图 从而获得星星的效果;

常规的voronoi图

反转+step+乘以一个噪声纹理后的效果
2.7程序化云的制作思路
这里主要讲思路
2.4.1求出云的mask
先获得两组voroni图
float Voronoia = VoronoiCellTime(float4(posWS.x,pow(posWS.y ,_YScale),posWS.z,1), _CellSize/100, _Jitter, _EdgeThreshold, _EdgeSharpness, _DarkenCenter,_Time.x*_CloudSpeed).r;
Voronoia = pow(Voronoia,1);
Voronoia = smoothstep (-0.1,1,Voronoia);
//return Voronoia;
float Voronoib = VoronoiCellTime(float3(posWS.x,posWS.y*_YScale*1.5,posWS.z), _CellSize*15/100, _Jitter, _EdgeThreshold, _EdgeSharpness, _DarkenCenter,_Time.x*_CloudSpeed);
Voronoib = pow(Voronoib,0.8);
Voronoib = smoothstep (-0.1,1,Voronoib);


将两者相互混合,获得一套大概的云朵mask

此时,云朵mask显得太过规律,需要在加上一个FBMnoise,打破这种规律感
float Voronoii = pow(lerp(Voronoia,Voronoib,0.89),3) + Domain3d0*0.5;

FBMnoise

此时,云朵的mask大概出来了,通过调整,可以求出大概的云朵范围
float cloudi = saturate((Voronoii + Domain3d) -_CloudRange);
float cloudrangei = saturate((cloudi-0.35) * 3);

黑色为云,白色为天空球
通过将生成纹理的uv坐标的y轴进行缩短后再进行纹理采样,并调整对比度,即可获得云朵阴影部分的mask。

由于采样uv的y轴进行了缩短,此时的mask会再视觉上显得更低,展现出云朵上方被天空求照亮的感觉。
将两个mask进行混合,再设置云朵颜色,并混合之前的效果,即可获得最终的云朵效果

这种程序化云朵可以通过调整生成FBMnoise的参数进而调整云朵的粗糙的
(材质中的_Lacunarity ("Lacunarity", Float) = 3.26 // 频率倍增系数,该参数可以调整云朵的粗糙度)

也可以调整voronoi纹理的参数去调整云朵的形状,数量,对比

(Voronoi图的相关参数
[Header(Voronoi)]
_CellSize ("Cell Size细胞大小", Range(5,30)) = 7 // 细胞大小
_Jitter ("Irregularity细胞不规则度", Range(0.2, 1.0)) = 1 // 细胞不规则度
_EdgeSharpness ("Edge Sharpness边缘锐利度", Range(5, 50)) = 50 // 边缘锐利度
_EdgeThreshold ("Edge Thickness 边缘厚度", Range(0.0, 0.15)) = 0 // 边缘厚度
_DarkenCenter ("Center Darkness细胞中心暗化程度", Range(0, 1)) = 1 // 细胞中心暗化程度)
2.8劈开天空的制作思路
首先制作一个sdf图,该图必须是cubemap格式的,用以作为切分两个天空的mask

将这个sdf图作为溶解算法里的mask图,通过step即可获得一个可以调整的mask
使用之前的方式,再做一个天空球,两个天空球通过这个sdf图生成的mask,便可拥有两个天空。

但此时的天空球过于单调,战神里这段切开天空的边缘有着极光火焰云的效果

所以可以通过溶解算法里将溶解边缘提亮的办法去获得溶解边缘的高光,但这里的火焰似乎有着从上往下的方向感,所以我们可以使用屏幕空间uv去采样一个预计算好的noise贴图,再与溶解边缘的高光部分相乘,叠加后进而制作出这种边缘极光火焰云的效果。
//两层天空的混合
//--------------------------------------------------------------------------------------------------------
float2 screenUV = i.vertex.xy / _ScreenParams.xy;
float4 layermask = SAMPLE_TEXTURECUBE(_LayerMask, sampler_LayerMask, i.posWS);
//return layermask;
float auroraNoise = SAMPLE_TEXTURE2D(_StarNoiseTex, sampler_StarNoiseTex,float2(screenUV.x*3,screenUV.y*0.4 - _Time.x)*5);//float2(screenUV.x,screenUV.y*3)
float layermask0 = pow(smoothstep(_LayerRange/2,_LayerRange,layermask.r),2);
float layermask00 = pow(smoothstep(_LayerRange/2,_LayerRange+0.3,layermask.r),2);
float layermask000 = smoothstep(_LayerRange/2-0.01,_LayerRange-0.01,layermask.r);
layermask000 = layermask000 - auroraNoise;
//return layermask000;
layermask = saturate(lerp(layermask0,layermask0-auroraNoise+layermask00,_LayerRange));
float4 layerColor = saturate(layermask000-layermask)*_LayerColor*3;
c = lerp(c,cx,layermask.r) + layerColor;//c是第一层天空的颜色 cx是第二层天空的颜色

大功告成!!
2.9 其他的一些小trick和优化策略
1. 第二层天空,可以将生成天空球的相关坐标,如light.direction通过罗德里格斯旋转矩阵,进行旋转,进而实现第二层天空和第一层天空的分别控制,也可以传入一个空物体,将空物体的坐标作为生成天空球的相关坐标;
2. 程序化云朵在靠近太阳/月亮时。他的亮部会变大,可以将云朵部分的mask加上太阳/月亮的bloom部分,用以制作这个效果
3. 大量的程序化纹理会消耗过多的计算量,可以将其放到computeshader中进行计算后再传进来。
相关的函数库
通过网盘分享的文件:urp函数库
链接: https://pan.baidu.com/s/1ebLQqaKwr9TgCGo5cFtCXA?pwd=2333 提取码: 2333
里面包括各种
图像处理的函数,如Kawase模糊,散景模糊,饱和度调整等
uv处理函数,如三平面映射,陡峭视差uv扭曲等
程序化纹理生成函数,如白噪声,2d到3dvoronoi,Perlin Noise,Wave Noise等等