Unity 动态天气系统(基于URP)

需求:光照二十四小时实时变化,昼夜系统。

思路:程序化天空盒,绑定一系列参数到配置文件。

实现:

一,程序化天空盒

1,太阳(月亮)随时间旋转:采样贴图贴在天空盒上,直接用天空盒UV会有拉伸问题,采样的UV需要C#传矩阵到Shader。

创建太阳,月亮,灯光的引用

[SerializeField] private Transform m_sunTransform = null;
[SerializeField] private Transform m_moonTransform = null;
[SerializeField] private Transform m_directionLight = null;

传到Shader里采样

private Material skyMat = null;
......
internal static readonly int SunMatrix = Shader.PropertyToID("_SunMatrix");
internal static readonly int MoonMatrix = Shader.PropertyToID("_MoonMatrix");
......
skyMat.SetMatrix(SunMatrix, m_sunTransform.worldToLocalMatrix);
skyMat.SetMatrix(MoonMatrix, m_moonTransform.worldToLocalMatrix);

天空球Shader

uniform float4x4 _SunMatrix;
uniform float4x4 _MoonMatrix;
......

o.vertex = UnityObjectToClipPos(v.vertex);
o.SunPos = mul((float3x3)_SunMatrix, v.vertex.xyz);
o.MoonPos = mul((float3x3)_MoonMatrix, v.vertex.xyz);
......

half4 sunColor = tex2D(_SunTex, i.SunPos + 0.5);
half4 moonColor = tex2D( _MoonTex, i.MoonPos + 0.5);
......

采样出来图像有些许拉伸,UV加0.5。在朝向对面也会生成了一个图像,需要做个mask。

C#部分

internal static readonly int UpDirectionMatrix = Shader.PropertyToID("_UpDirectionMatrix");
internal static readonly int SunDirection = Shader.PropertyToID("_SunDirection");
internal static readonly int MoonDirection = Shader.PropertyToID("_MoonDirection");
......
skyMat.SetMatrix(UpDirectionMatrix, transform.worldToLocalMatrix);
skyMat.SetVector(SunDirection,transform.InverseTransformDirection(-m_sunTransform.forward));
skyMat.SetVector(MoonDirection,transform.InverseTransformDirection(-m_moonTransform.forward));

Shader部分

uniform float4x4 _UpDirectionMatrix;
......
o.WorldPos = normalize(mul((float3x3)unity_WorldToObject, v.vertex.xyz));
o.WorldPos = normalize(mul((float3x3)_UpDirectionMatrix, o.WorldPos));
......
float3 viewDir = normalize(i.WorldPos);
float sunCosTheta = dot(viewDir, _SunDirection);
float moonCosTheta = dot(viewDir, _MoonDirection);

half4 sunColor = tex2D(_SunTex, i.SunPos + 0.5) * saturate(sunCosTheta);
half4 moonColor = tex2D( _MoonTex, i.MoonPos + 0.5) * saturate(moonCosTheta);

白天时,灯光朝向与太阳一致,晚上时,灯光朝向与月亮一致,太阳朝向与Y轴的夹角大于0为白天,小于0为夜晚。太阳和月亮朝向相反。

m_moonTransform.forward = -m_sunTransform.forward;
m_sunElevation = Vector3.Dot(-m_sunTransform.forward, Vector3.up);

m_directionLight.localRotation=Quaternion.LookRotation(m_sunElevation >= 0.0f ? m_sunTransform.forward : m_moonTransform.forward);

现在旋转m_sunTransform已经能够看到灯光随太阳(月亮)正常变化了,然后加入时间。

[SerializeField,Range(0,24)] private float dayTime = 8;
[SerializeField,Range(0,3600)] private float timeScale = 1;
......
private void Update()
{
    dayTime += Time.deltaTime/3600 * timeScale;
    if (dayTime > 24)
    {
        dayTime = 0;
    }
    m_sunTransform.localRotation=Quaternion.Euler(dayTime * 15,300,240);
}

2,大气散射:理论网上有很多了,这里直接写实现。

C#部分

public float wavelengthR = 680;
public float wavelengthG = 550;
public float wavelengthB = 450;
public float molecularDensity = 2.545f;
public float rayleigh = 1.5f;
    
public float kr = 8.4f;
public float km = 1.2f;
public float mie = 1.0f;
public float mieDistance = 1.0f;
public float scattering = 0.25f;
public float luminance = 1.5f;
public float exposure = 2.0f;
public Color rayleighColor ;
public Color mieColor ;
public Color scatteringColor;
public float fogScatteringScale = 1.0f;
......
internal static readonly int ScatteringModeID = Shader.PropertyToID("_ScatteringMode");
internal static readonly int Kr = Shader.PropertyToID("_Kr");
internal static readonly int Km = Shader.PropertyToID("_Km");
internal static readonly int Rayleigh = Shader.PropertyToID("_Rayleigh");
internal static readonly int Mie = Shader.PropertyToID("_Mie");
internal static readonly int MieDistance = Shader.PropertyToID("_MieDepth");
internal static readonly int Scattering = Shader.PropertyToID("_Scattering");
internal static readonly int Luminance = Shader.PropertyToID("_Luminance");
internal static readonly int Exposure = Shader.PropertyToID("_Exposure");
internal static readonly int RayleighColor = Shader.PropertyToID("_RayleighColor");
internal static readonly int MieColor = Shader.PropertyToID("_MieColor");
internal static readonly int ScatteringColor = Shader.PropertyToID("_ScatteringColor");
internal static readonly int FogScatteringScale = Shader.PropertyToID("_FogScatteringScale");
......
skyMat.SetInt(ScatteringModeID,(int)m_scatteringMode);
skyMat.SetFloat(Kr, kr * 1000f);
skyMat.SetFloat(Km, km * 1000f);
skyMat.SetVector(Rayleigh, ComputeRayleigh() * rayleigh);
skyMat.SetVector(Mie, ComputeMie() * mie);
skyMat.SetFloat(MieDistance, mieDistance);
skyMat.SetFloat(Scattering, scattering * 60f);
skyMat.SetFloat(Luminance, luminance);
skyMat.SetFloat(Exposure, exposure);
skyMat.SetColor(RayleighColor, rayleighColor);
skyMat.SetColor(MieColor, mieColor);
skyMat.SetColor(ScatteringColor, scatteringColor);
skyMat.SetFloat(FogScatteringScale,fogScatteringScale);
......
private Vector3 ComputeRayleigh()
{
    Vector3 rayleigh = Vector3.one;
    Vector3 lambda = new Vector3(wavelengthR, wavelengthG, wavelengthB) * 1e-9f;
    float n = 1.0003f;
    float pn = 0.035f;
    float n2 = n * n;
    float N = molecularDensity * 1E25f;
    float temp = (8.0f * Mathf.PI * Mathf.PI * Mathf.PI * ((n2 - 1.0f) * (n2 - 1.0f))) / (3.0f * N) * ((6.0f + 3.0f * pn) / (6.0f - 7.0f * pn));

    rayleigh.x = temp / Mathf.Pow(lambda.x, 4.0f);
    rayleigh.y = temp / Mathf.Pow(lambda.y, 4.0f);
    rayleigh.z = temp / Mathf.Pow(lambda.z, 4.0f);

    return rayleigh;
}
    
private Vector3 ComputeMie()
{
    Vector3 mie;
        
    float c = (0.6544f * 5.0f - 0.6510f) * 10f * 1e-9f;
    Vector3 k = new Vector3(686.0f, 678.0f, 682.0f);

    mie.x = (434.0f * c * Mathf.PI * Mathf.Pow((4.0f * Mathf.PI) / wavelengthR, 2.0f) * k.x);
    mie.y = (434.0f * c * Mathf.PI * Mathf.Pow((4.0f * Mathf.PI) / wavelengthG, 2.0f) * k.y);
    mie.z = (434.0f * c * Mathf.PI * Mathf.Pow((4.0f * Mathf.PI) / wavelengthB, 2.0f) * k.z);

    return mie;
}
......
public enum ScatteringMode
{
    Automatic,
    Custom
}

Shader部分

#define PI 3.1415926535
#define Pi316 0.0596831
#define Pi14 0.07957747
#define MieG float3(0.4375f, 1.5625f, 1.5f)
......
uniform float  _FogScatteringScale;
uniform int    _ScatteringMode;
uniform float  _Kr;
uniform float  _Km;
uniform float3 _Rayleigh;
uniform float3 _Mie;
uniform float _MieDistance;
uniform float  _Scattering;
uniform float  _Luminance;
uniform float  _Exposure;
uniform float4 _RayleighColor;
uniform float4 _MieColor;
uniform float4 _ScatteringColor;
......
float zenith = acos(saturate(dot(float3(0.0, 1.0, 0.0), viewDir))) * _FogScatteringScale;
float z = (cos(zenith) + 0.15 * pow(93.885 - ((zenith * 180.0f) / PI), -1.253));
float SR = _Kr / z;
float SM = _Km / z;
float3 fex = exp(-(_Rayleigh * SR  + _Mie * SM));
float sunset = clamp(dot(float3(0.0, 1.0, 0.0), _SunDirection), 0.0, 0.5);
float3 Esun = _ScatteringMode == 0 ? lerp(fex, (1.0 - fex), sunset) : _ScatteringColor;

//Sun Inscatter
float  rayPhase = 2.0 + 0.5 * pow(sunCosTheta, 2.0);
float  miePhase = MieG.x / pow(MieG.y - MieG.z * sunCosTheta, 1.5);
float3 BrTheta  = Pi316 * _Rayleigh * rayPhase * _RayleighColor;
float3 BmTheta  = Pi14  * _Mie * miePhase * _MieColor * sunRise;
float3 BrmTheta = (BrTheta + BmTheta) / (_Rayleigh + _Mie);
float3 sunInScatter = BrmTheta * Esun * _Scattering * (1.0 - fex);
sunInScatter *= sunRise;

//Moon Inscatter
rayPhase = 2.0 + 0.5 * pow(moonCosTheta, 2.0);
miePhase = MieG.x / pow(MieG.y - MieG.z * moonCosTheta, 1.5);
BrTheta  = Pi316 * _Rayleigh * rayPhase * _RayleighColor;
BmTheta  = Pi14  * _Mie * miePhase * _MieColor * moonRise;
BrmTheta = (BrTheta + BmTheta) / (_Rayleigh + _Mie);
Esun = _ScatteringMode == 0 ? (1.0 - fex) : _ScatteringColor;
float3 moonInScatter = BrmTheta * Esun * _Scattering * 0.1 * (1.0 - fex);
moonInScatter *= 1.0 - sunRise;

BrmTheta = BrTheta / (_Rayleigh + _Mie);
float3 skyLuminance = BrmTheta * _ScatteringColor * _Luminance * (1.0 - fex);

值用float,颜色用Color,是一个定制,无法满足随时间变化而变化,用AnimationCurve和Gradient替换,AnimationCurve.Evaluate(Time time),Gradient.Evaluate(Time time)。然后参数可以存到ScriptableObject里,创建多个ScriptableObject存不同的天气参数,用来实现不同天气的切换,这里我就不再写了。

  • 10
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Unity 2022 是一款强大的游戏开发引擎,而URP(Universal Render Pipeline)是其提供的一种轻量级渲染管线。在Unity 2022中,我们可以结合URP和HighlightPlus高亮插件来实现游戏中的高亮效果。 HighlightPlus是Unity Asset Store上提供的一款高亮插件,它可以帮助我们在游戏中实现各种高亮效果,比如在交互对象处显示边框、发光效果等。而URP则可以作为渲染管线,实现高性能的渲染效果。 使用HighlightPlus插件需要先导入到Unity项目中,然后在需要高亮的物体上添加HighlightEffect组件。我们可以在组件的属性面板中设置各种高亮效果的参数,比如边框的颜色、宽度,发光的颜色、强度等。可以根据项目需求自定义高亮效果,并在游戏运行时动态控制高亮状态。 整合URP和HighlightPlus,我们可以在游戏中使用URP作为渲染管线,保证游戏的性能和效果。同时,通过HighlightPlus插件,我们可以实现物体的高亮效果,提升游戏的可交互性和视觉效果。在Unity 2022中,基于URP的使用HighlightPlus高亮插件的操作与以往版本的Unity相似,只需要正确导入并设置相关组件的属性即可。 综上所述,Unity 2022基于URP使用HighlightPlus高亮插件,可以为游戏增加各种高亮效果,提升游戏的可玩性和视觉效果,并通过URP保证游戏的性能和效果。这是一个非常有用和方便的功能,开发者可以根据需要灵活使用。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值