【程序化天空盒】过程记录01:日月 天空渐变 大气散射
1 日月 SunAndMoon
昼夜的话肯定少不了太阳和月亮,太阳和月亮实现的道理是一样的,只不过是月亮比太阳多了一个需要控制月牙程度(or添加贴图)的细节~
1.1 Sun
太阳的话很简单,直接在shader里实现一个太阳跟随平行光旋转而旋转的样子就行。实现这个效果需要用到Unity内置变量_WorldSpaceLightPos0获取当前平行光的方向,不要被这个参数名字“lightPos”迷惑了,它实际上就是一个归一化的vector(w=0)。接着用Unity内置的distance函数计算当前uv坐标(i.uv.xyz)到上面那个的距离。
如何理解这个“距离”呢?——我们再来复习一遍图形学基础吧:学习齐次坐标时讲到“点与向量的区别”时,例如vector(a, b, c, 0)和point(a, b, c, 1),我们可以把vector看作这个point挪向无穷远处位置。行,那么我们再来看看这个distance计算出来的结果的几何意义是不是就十分简单了——uv坐标越靠近,代表离这个无穷远处的点越近,于是结果越小。
- 我们需要的是一个实心圆(模拟“日月”),如果只将distance的结果与_SunColor相乘并作为片元着色器的结果输出,主要代码及效果如下:
float sun = distance(i.uv.xyz, _WorldSpaceLightPos0);
...
fixed4 col = sun * _SunColor;
- 场景中越靠近远处的“太阳”位置越黑,想要实现一个实心圆就很简单了,补充后代码及效果如下:
float sun = distance(i.uv.xyz, _WorldSpaceLightPos0);
float sunDis = 1 - sun / _sunRadius;
...
fixed4 col = sunDis * _SunColor;
为了实现控制最大化,加入了_sunRadius参数以控制“太阳”的大小,选择除法的原因也很简单——太阳越大意味着白色部分越大,意味着disatance()函数计算结果的权重更加分散,所以是除法。
- 到了这一步你会发现,上图还是缺点什么!
【第一】边界有些模糊,我们想实现的效果是轮廓明显的“日月”,这是因为边界的sunDis数值不够大,导致_sunColor相乘混合的颜色“淡”。如何处理?——简单,直接乘上个合适的倍数就行!
【第二】原有的网格不见了,给人一种黑色“笼罩”整个天空盒的错觉。看到这里你似乎会觉得疑惑:“网格不见了”啥意思?我来举个例子,下面是我将sunDis值分别设置为1,0,-1的效果:
2和3看似都是黑色,其实还是有差别的,他会掩盖网格。而且这个sunDis系数最后是跟类型为Color的_SunColor变量相乘,参考光照计算模型,这里理应将该系数也限制在(0, 1),这里用saturate()就行!
考虑了以上两点后最终的代码:
float sun = distance(i.uv.xyz, _WorldSpaceLightPos0);
float sunDis = saturate((1 - sun / _sunRadius) * 50);
...
fixed4 col = sunDis * _SunColor;
最终效果:
1.2 Moon
太阳做完了,到了月亮部分,它俩一个在光的正方向(太阳)一个在反方向(月亮),这个不难理解吧!所以,关于跟随平行光方向的部分,设置跟Sun一样,只是多了一个负号。
这里我计划实现两种月亮的方法,一种是贴上一个真实的月亮图片,另一个是做一个简单的可以控制的月牙形状。
第一种:月牙
另外,由于月亮是有月牙的~就用两个圆相减的形式做出月牙的效果,相减效果采用给uv.x一个偏移值_CrescentOffset的方法实现的。同样,这里也需要注意只要涉及相减的需要给数值规范到(0, 1)才能确保效果的正确。
下面是月亮部分的代码:
//2.Add Moon
float moon = distance(i.uv.xyz, -_WorldSpaceLightPos0);
float moonDis = saturate((1 - moon / _MoonRadius) * 50);
float crescent = distance(float3(i.uv.x + _CrescentOffset, i.uv.yz), -_WorldSpaceLightPos0);
float crescentDis = saturate((1 - crescent / _MoonRadius) * 50);
moonDis = saturate(moonDis - crescentDis);
fixed4 moonN = moonDis * _MoonColor;
最终效果:
第二种:月亮贴图
贴图主要问题是解决UV坐标系变换问题,因为如果继续用原始的改变光照方向后月亮贴图会变形的问题,我们希望每个角度贴图形状都是圆圆的(参考日常生活中每个角度的月亮都是圆圆的),那么我们就需要一个建立一个4x4的坐标系变换矩阵去实现。
问题又来了:Unity自带的变换矩阵unity_WorldToLight对于平行光是行不通的,只适用于point/spot/烘焙光,所以这里我们需要自己脚本实现变换矩阵。这里我们不难想到shadowmap里也需要求得光源空间的变换矩阵,可以参考我记录的GAMES202作业1中CalcLightMVP()的实现过程去写出这个变换矩阵,还可以跟着这一篇用Unity实现Shadow Map的博客实现这个变换矩阵,道理都是相通的。
更方便的还可以用Unity自带的transform的worldToLocalMatrix获得当前对象的世界空间到local空间的变换矩阵,给场景中的平行光一个子Camera再获取一下这个变换矩阵就行(后面会补充完整脚本内容和shader部分的内容,这里就先放一个效果)。
效果如下,加上了一点后面会做的渐变天空效果:
2 天空主体色 Gradient Sky
1.0版本
完成了日月交替的部分只能说才实现了一小部分,而且天空很单调,为了实现更加酷炫的效果,加点渐变天空颜色。说起渐变,要用上lerp()了!分别给白天天空和晚上天空赋予颜色——基于UV坐标的y 轴值来做天空颜色的渐变,记住还是需要saturate()!最后根据uv坐标(i.uv.y)判断何时昼夜交替。
// 3.gradient sky
fixed3 gradientDay = lerp(_DayBottomColor, _DayTopColor, saturate(i.uv.y));
fixed3 gradientNight = lerp(_NightBottomColor, _NightTopColor, saturate(i.uv.y));
fixed3 gradientSky = lerp(gradientNight, gradientDay, saturate(_WorldSpaceLightPos0.y));
2.0版本
上面的渐变天空简单的lerp做的,很枯燥。参考程序化天空盒实现昼夜变换,我们也来这位大佬的构思思路,做一次分析(毕竟是作品集,所有过程自己实现一遍最好啦!),也天空变化归纳了出来:
参考他提出的思路,地平线渐变用worldPos.y控制,但天空的主体颜色用光方的z和y共同控制,关于天空主体色我的理解:
(纠正一下,感觉早晨不是supper blue而是一种偏绿色的蓝色?颜色可以自行调整的)
即从早晨开始,早晨(非常蓝)-->中午(正常蓝)-->傍晚(紫色)-->深夜(深蓝色),其中,y控制着白天和晚上,z控制白天三种颜色变换、晚上三种颜色交替
白天:
上述思路对应的理解代码为,
// 早午过渡
col = lerp(morningCol, noonCol, smoothstep(0,0.5,z));
// 再把上面的当作午的,进行午傍晚过渡
lerp(col, nightfallCol, smoothstep(0.5,1,z));
同理晚上:
// 早晚过渡
col = lerp(morningCol, nightCol, smoothstep(0,0.5,z));
// 再把上面的当作晚的,进行晚和傍晚的过渡
lerp(col, nightfallCol, smoothstep(0.5,1,z));
再优化一下:我想要清晨和傍晚的颜色持续的少一点,白天的天蓝色维持的久一点,这样就需要更改参数,索性直接多给两个参数,来调节一天内清晨和傍晚的持续时间,最终关于天空颜色早晚变化的参数如下:
3 地平线渐变
跟参考文章不太一样的是,我把渐变天空分为两个部分:半天空渐变色+范围变化的Bloom,虽然这样比较麻烦(叠加了三层orz),但原神有些画面它就是三种颜色叠加的效果,例如下图傍晚的天空:
![](/assets/blank.gif)
索性直接拆成三块做。
3.1 半天空渐变
半天空渐变色有4种颜色,4个颜色变化的时间刚好跟天空主题渐变色对应,
1 晚上:深蓝色+淡色地平线
早晨:浅蓝色+淡色地平线
中午:天蓝色+浅蓝地平线
傍晚:紫色+淡黄色地平线
由于这个是跟主色叠加,是不是应该在天空主体渐变的基础上修改,达到天空主色渐变的感觉:
好,说做就做,以早晨为例,还需要能控制渐变程度等,最后的参数如下:
整个天空颜色很接近了(斗胆),现在就差地平线附近的Bloom。
3.2 范围变化的Bloom
(做了一下午了,起来活动一下继续,orz,,)
再继续观察:
早->午:红色(小)-->黄色(大)-->白色(小)
傍晚->晚上:黄色(大)-->橙黄色(超大)-->黄色(小)
(补充:既然节点4Mie散射开始,那我们把大部分白色的Bloom都交给Mie散射,假设整个发光到节点4就宣告结束。)
【空缺了一部分思路,这里做得比较着急没有记录,放个对比图吧之后补充】
日出日落配上天空颜色变化:
emmm配色什么的感觉挺脏的,然后bloom那里需要给参数调整的,做的时候随便选的,先继续进行下面的步骤,最后再优化。
接下来是加上云、和星空银河,放在下一篇文章吧。
我上面实现渐变天空选择的颜色还是照抄参考里的配色。但是!我认为一个优秀的天空盒,配色一定是要有讲究的!配色的实现方法打算参考这篇文章里提到的将配色数据做成数组形式,像动画关键帧那样呈现出来的方法。
3 大气散射
做的时候没有记录过程,作品搞完后会回来补上。
参考
这里是我搜刮遍了各种网站找到的实现动态天空盒的文章,自己在做的过程中也参考了很多,这里罗列出来希望对看到这篇文章的你有所帮助:
风格化的动态天空球 – WalkingFat
Unity日夜循环天空球(Procedural Skybox) - 知乎
Making a Stylized Skybox Shader
Unity 卡通渲染 程序化天空盒 - 知乎
【unity URP】昼夜循环天空球 - 知乎
程序化天空盒实现昼夜变换 - 知乎 (zhihu.com)
【程序化天空盒】过程记录01:日月 天空渐变 大气散射相关推荐
- 【程序化天空盒】过程记录02:云扰动 边缘光 消散效果
写在前面 写在前面唉,最近筋疲力竭,课题组的东西一堆没做,才刚刚开始带着思考准备练习作品,从去年5月份开始到现在真得学了快一年了,转行学其他的真的好累,,不过还是加油! 下面是做面片云的部分,关于日月 ...
- 【程序化天空盒】过程记录03:镜头光晕 旋转的动态星空
本来说不再记录了,发现实现镜头光晕和星空的时候是有很多需要注意的点的,有必要记录一下. 镜头光晕 URP系列教程-手把手教你在URP中实现镜头光晕效果 | Unity 中文课堂 (u3d.cn) 但是 ...
- 基于URP的程序化天空盒
参考来源: 天空盒教程第 1 部分 |开尔文·范·霍恩 (kelvinvanhoorn.com) [程序化天空盒]过程记录02:云扰动 边缘光 消散效果_九九345的博客-CSDN博客 程序化天空盒实 ...
- 解决Linux 负载过高问题过程记录
解决问题的思路 1.top命令查看该机器的负载状况 2.cd /proc/pid 查看对应高占用程序的位置 3.进入对应程序中查看日志,根据CPU和内存这两个因素分析 4.ps -ajxf 查看进程 ...
- 一次简单的爬虫过程记录:静态网页小说下载
时间:2020年2月14日 环境:windows7 编程语言及版本:Python3.8 IDE:Sublime Text 3 工具:requests,BeautifulSoup,sys 浏览器:Fir ...
- 封装自己专属的真正的纯净版Windows系统过程记录(1)——封装环境准备
文章目录 前言 工具准备 封装环境安装 1. 新建虚拟机 2. 磁盘空间设置 3. 上述完成后,点编辑虚拟机设置,删掉不必要的3个硬件.然后继续点添加,添加一个硬盘设备,也就是物理映射磁盘 4. 添加 ...
- Unity3D游戏制作学习记录01——丛林战争
Unity3D游戏制作学习记录01--丛林战争 Siki学院的视频教程指路牌:http://www.sikiedu.com/course/61. 学业繁忙-和朋友一起跟着siki的丛林战争的教程跟着做 ...
- Linux嵌入式驱动开发07——GPIO驱动过程记录(飞凌开发板)
文章目录 全系列传送门 1. 在/arch/arm/boot/dts/imx6q-pinfunc.h查找 2. 在设备树配置文件中添加设备节点定义以及其引脚定义 3. 修改设备树文件添加配置 4. d ...
- 深度学习-在自带显卡GeForce RTX 2070的研华MIC-770工控机上安装Ubuntu18.04及显卡驱动过程记录
在自带显卡GeForce RTX 2070的研华MIC-770工控机上安装Ubuntu18.04及显卡驱动过程记录 1. 确认工控机是否带有独立显卡及显卡的型号 输入 lspci | grep -i ...
最新文章
- DCN-2655 ssh 远程登陆配置
- php虚拟内存设置,虚拟内存有什么用
- 图的度 知识图谱的一度关系 几度关系
- Python API简单验证
- Troubleshooting(三):网络
- java中为按钮添加图片_如何在Java中为字符串添加双引号?
- python数字排列组合去重_排列组合-生成集合的所有子集
- 外网DNS系统外网访问及邮件系统外网域名访问问题
- eclipse git拉取失败_收藏!工作中Git使用实践和常用命令流程合集
- MATLAB 2018a 安装
- oracle定时任务定时无效
- Matlab使用sort进行排序---2022/04/07
- C语言自学之路六(循环语句详解)
- 阿里云和腾讯云全方位对比
- 关于eclipse IDE安装Darkest Dark Theme主题插件
- 【Excel】数据透视表—按年、季度、月份汇总报表
- word替换妙用小技巧:批量去除多余空格、空行、换行
- python读取桌面上的文件夹怎么加密_python给文件夹加密 怎么样给python文件加密...
- 一起来DIY一个人工智能实验室吧
- Yii2-Admin-Theme 基于layui的通用后台模板
热门文章
- 小睿家庭云可以刷linux系统吗,小睿家庭云刷机方法,刷入openwrt pandorabox padavan方法,刷路由器固件...
- FreeCAD学习笔记——Units、Builtin modules和Workbench creation
- python50道练习题
- 防火墙的长连接和短连接相关命令
- android 6.0.1原生系统源码下载以及源码编译----framework修改
- 肺结节目标检测_四招识别肺结节的良恶性
- JAVA实现简单扫雷游戏
- 讯飞:糖尿病遗传风险预测Coggle挑战赛公开
- OpenCV4经典案例实战教程 笔记
- E07【餐厅】What would you recommend?