本节实现Unity日夜循环天空球♥(´∀`)人
概述
Unity中有两种天空盒,一种是纹理类(6面、立方体贴图、全景),另一种是程序类。本文实现了程序化天空盒(其实就是100%纯手写的天空盒=-=)
本文中的天空盒主要参考了小黄人的分享(日常告白→
做吧,根据你的需要做魔法改变,和时间系统相关联,repo在这里("·ω·)"→
天空盒设置
确保相机中的Clear Flags设置为天空盒模式,然后将天空盒组件挂在任意物体下,并添加使用天空盒着色器的材质。
太阳和月亮图画
Unity 内置变量 _WorldSpaceLightPos0 存储定向光的方向。这样就可以通过改变定向光的旋转来旋转天空球unity 天空盒动态变化,形成昼夜效果。
首先,光所指向的正负方向就是我们画太阳和月亮的地方!
计算uv坐标上天空球体上的坐标与_WorldSpaceLightPos0的距离,根据距离画出这个值。得到的是一个从中心到边缘亮度递减的圆形效果(距离方向坐标越近,数值越小)2d游戏素材,可以使用 saturate 再次对球面区域的颜色进行处理,将球体乘以一个大数, 并将数字返回 1 以获得清晰的边界。
// sun
float sun = distance(i.uv.xyz, _WorldSpaceLightPos0);
float sunDisc = 1 - (sun / _SunRadius);
sunDisc = saturate(sunDisc * 50);
月亮的画法也是一样,两个球就可以达到月食/日食的效果——
// moon
float moon = distance(i.uv.xyz, -_WorldSpaceLightPos0); //日月方向相反
float moonDisc = 1 - (moon / _MoonRadius);
moonDisc = saturate(moonDisc * 50);
float crescentMoon = distance(float3(i.uv.x + _MoonOffset, i.uv.yz), -_WorldSpaceLightPos0);
float crescentMoonDisc = 1 - (crescentMoon / _MoonRadius);
crescentMoonDisc = saturate(crescentMoonDisc * 50);
moonDisc = saturate(moonDisc - crescentMoonDisc);
渐变天空
天气有两种,白天和黑夜。
使用lerp函数形成颜色渐变,根据uv坐标确定昼夜不交替时的天空颜色,根据saturate函数和世界坐标的y值确定昼夜交替。
// gradient day sky
float3 gradientDay = lerp(_DayBottomColor, _DayTopColor, saturate(i.uv.y));
float3 gradientNight = lerp(_NightBottomColor, _NightTopColor, saturate(i.uv.y));
float3 skyGradients = lerp(gradientNight, gradientDay,saturate(_WorldSpaceLightPos0.y));
星空/云图
星空和云彩是用噪声贴图+截止制作的。
在“天空”纹理中,不能直接使用 uv 坐标。在uv坐标下,emmm感觉附着在一个巨大的球体上。在这里游戏图片素材,繁星点点的天空和云彩只会出现在“天空”中,屏幕上的表演只会出现在屏幕上。上半部分。所以这里是使用世界坐标来实现的,在xz平面上显示纹理,使用y轴坐标来控制上半部分的显示范围。
用于天空贴图的坐标如下处理。用clamp控制世界坐标y值在0到最大值之间(我不知道y轴最大值怎么取,但是大于10000的时候没有区别,所以我用这个数...接下来就是熟悉的cutoff操作,使用step设置一个cutoff阈值,突出亮部,关键代码如下:
float2 skyuv = i.worldPos.xz / clamp(i.worldPos.y, 0, 500);
float3 stars = tex2D(_Stars, skyuv + float2(_StarsSpeed, _StarsSpeed) * _Time.x);
stars = step(_StarsCutoff, stars);
同样的方法,添加云彩(和星星一样,只是改变纹理),效果如下:
云优化添加噪声图
当前的云只是单层纹理,看起来很有层次感,可以看到明显的纹理图案。接下来,通过添加噪声图来丰富云的形状。
这里添加了两种纹理,一种用于在原始纹理上叠加丰富的云图案,另一种用于创建更明显的失真效果。
float distort = tex2D(_DistortTex, (skyuv + (_Time.x * _DistortionSpeed)) * _DistortScale);
float noise = tex2D(_CloudNoise, ((skyuv + distort ) - (_Time.x * _CloudSpeed)) * _CloudNoiseScale);
float finalNoise = saturate(noise) * 3 * saturate(i.worldPos.y);
cloud = saturate(step(_CloudCutoff, cloud * finalNoise));
蓬松的!
云的边缘现在太清晰了,所以让我们让云看起来更蓬松,添加一个参数,在一个截止范围内平滑值,使云在边缘有点模糊。
cloud = saturate(smoothstep(_CloudCutoff * cloud, _CloudCutoff * cloud + _Fuzziness, finalNoise));
这似乎更接近现实。
云层
复制并粘贴上一层云并添加另一层以提供不同的截止范围。效果非常好。可以制作浓密或稀疏的云层,多层也方便以后添加不同的颜色。
cloud = saturate(smoothstep(_CloudCutoff * cloud, _CloudCutoff * cloud + _Fuzziness, finalNoise));
float cloudSec = saturate(smoothstep(_CloudCutoff * cloud, _CloudCutoff * cloud + _FuzzinessSec, finalNoise));
颜色加
给上层和下层的云层赋予不同的颜色。这里按照白天和黑夜添加两组颜色,然后根据白天和黑夜交替更换云色。
星空优化为晚上出现
星空与云不同,它只出现在夜晚。使用 saturate 和世界坐标 y 轴返回 1 或 0 作为乘数。
stars = step(_StarsCutoff, stars) * saturate(-_WorldSpaceLightPos0.y);
云的背后
目前星云叠加效果如下,星云出现在云层前。
决定只画没有云的星星。
stars *= (1 - cloud);
地平线
作为最后一步,添加地平线。可以根据uv.y画一条水平线。然后还是根据白天和黑夜来确定不同的地平线颜色。地平线的另一个作用是模糊上下边界,使云与地面的边界不那么明显。在这里unity 天空盒动态变化,绘制了一条额外的线以赋予它明亮的颜色来阻挡它。
float3 horizon = abs((i.uv.y * _HorizonIntensity) - _HorizonHeight);
horizon = saturate((1 - horizon)) * (_HorizonColorDay * saturate(-_WorldSpaceLightPos0.y) + _HorizonColorNight * saturate(_WorldSpaceLightPos0.y));
时间脚本
功能如下:
结束了!
本期素材小黄人的梦幻天空球
Catlike 编码 · 时钟
ps:感觉是step、smoothstep、saturate、clamp、lerp这五个函数负责shader中的绘制...