聊聊LightProbe原理实现以及对LightProbe数据的修改
0x00 前言
最近工作比较忙,所以文章已经很久没有更新了。这篇小文的主题也是在出差的高铁上想到,因为最近和一些朋友聊天,发现他们中很多人的项目中都使用了多个实时光源。细问之下主要是某些物体,例如角色,在烘焙后的场景中显得不够突出,为了突出角色所以加入了更多的实时光源。但事实上这可能并非一个很好的选择。
0x01 间接光还是直接光
下面这张图片演示了只有直接光照以及加上了间接光照之后的对比。
可以看到,直接光照抵达不到的地方的黑暗的部分要通过间接光照来照亮,而不是为了提高暗部的亮度再加一盏实时灯光。
事实上,如果场景中有大量的灯光——例如如果在上图中为室内增加大量补光来提高室内亮度——
还会造成场景中的明暗对比降低,画面显得更“平”。
这也是很多朋友的项目中场景中存在的一个比较常见的问题,即亮度不够灯光补。补着补着才发现,整个场景已经充斥了太多的灯光了,而场景也因此整体很亮,没有了明暗对比,结果就是视觉效果平的不真实。
相信各位也一定想到了,亮度不够灯光补这个思路的另一个实践——场景内的角色不够亮时也选择使用一个灯光来给角色补光——同样存在着和之前所说的一样的问题。
那么怎么提亮角色才更加合理一些呢?(虽然提亮角色这件事本身就不符合物理规则,但是为了游戏效果显然存在这样的需求)。如上图所示那样,利用间接光来照亮物体是一个不错的思路,不够亮?提高间接光的亮度。
在Unity中如何给动态物体提供间接光?这就引出了下面的主角——LightProbe。
0x02 LightProbe的核心
LightProbe主要解决了如何在动态对象和角色上使用烘焙的照明信息。
其实LightProbe的核心就是球面亮度信号编码和重建。
如果大家了解信号处理方面的知识的话,就会知道只要信号满足一定条件,就可以分解为一系列正弦谐波的和,谐波频率以倍频增长,这就是所谓的傅立叶级数。
而lightprobe也采用了类似的思路,使用了球谐函数来对该球面上的亮度信号进行编码。
同样的,一个原始的亮度信号也可以分解为一系列带缩放参数的基函数之和,而我们只需要知道这些基函数的缩放系数就可以在运行时快速的重建原始的亮度信号了。
但是有一个问题,那就是如果要完美的重建原始光照信号的话,显然需要很多很多甚至是无穷项球谐函数。但是好在LightProbe中保存的主要是一些低频的光照信息,换句话说,它没有高频率变化,所以如果我们通过丢弃所有更高的频率来压缩球体上的频域数据,没有人会注意到。所以这里我们可以只取有限的低频谐函数。
在Unity中,烘焙GI的LightProbe采用了3阶球谐函数(9个参数),实时GI中的LightProbe采用了2阶球谐函数(4个参数)。
OK,信号编码的问题解决了,另一个问题即在运行时如何重建亮度信号。其实使用lightprobe的开销很低,因为只需要将缩放系数与其对应的基函数相乘之后再求和的结果就是近似的原始信号。
接下来我来看看一个Unity中的LightProbe中保存了哪些数据吧。
在Unity中,我们可以使用脚本将场景内的LightProbe保存为一个Asset,并且只要保证使用文本格式进行序列化,我们就可以直接查看其数据内容了。
AssetDatabase.CreateAsset(Instantiate(LightmapSettings.lightProbes), "Assets/lightProbe.asset");
首先能够注意到的是“m_Tetrahedra”部分。
这个其实就是在运行时LightProbe插值时需要用到的四面体数据。因为如果要进行插值,显然要知道需要哪几个点来插值,同时还需要知道每个点的权重各是多少。
在Unity中会根据角色所在的位置,选择四面体,然后使用组成四面体的点进行插值,当然还可以确定每个点的权重。
在靠后的位置,我们还可以找到烘焙后的球谐函数的系数。
可以看到9个参数3个通道所以每一个点总共有27个float数据。
综上,可以看到在使用LightProbe时,计算开销并不大,相对来说比较大的开销主要来自对内存的占用。
0x03 修改LightProbe数据 提亮角色
ok,简单介绍了一下LightProbe的原理以及实现。下面我们还是回到最初的问题,那么怎么提亮一个场景内的角色才更加合理一些呢?
事实上我们可以通过修改烘焙后的LightProbe的数据来实现这样的需求。
可以看到上图中,角色已经和场景融为了一体。虽然这样更加真实和符合物理规则,但是我想对很多人来说这显然不是一个好的效果。角色还是能更加突出的好。
好在Unity提供了获取烘焙后的LightProbe数据的接口:
var probes = LightmapSettings.lightProbes.bakedProbes;
bakedProbes内保存的是一堆“SphericalHarmonicsL2”对象,只要修改SphericalHarmonicsL2的缩放比例就可以修改LightProbe所提供的亮度了。
除了修改亮度之外,有时我们也会想让角色有不同的环境光效果,以更加突出角色。这时我们就可以通过SphericalHarmonicsL2中定义的AddAmbientLight方法来实现了:
probe.AddAmbientLight(color);
提亮和修改环境色之后,我们的角色在场景中就成了下面这样。比实时光更加自然和开销更低。
当然,这里只是抛砖引玉,欢迎大家来讨论。
相关的脚本,可以在这里获取:
https://github.com/chenjd/LightProbeEditor
ref:
https://en.wikipedia.org/wiki/Delaunay_triangulation
https://www.gdcvault.com/play/1015312/Light-Probe-Interpolation-Using-Tetrahedral
https://en.wikipedia.org/wiki/Spherical_harmonics
聊聊LightProbe原理实现以及对LightProbe数据的修改的更多相关文章
- VMware 虚拟化编程(10) — VMware 数据块修改跟踪技术 CBT
目录 目录 前文列表 数据块修改跟踪技术 CBT 为虚拟机开启 CBT CBT 修改数据块偏移量获取函数 QueryChangedDiskAreas changeId 一个 QueryChangedD ...
- MySQL数据库5 - 插入数据,修改数据,删除数据
一.插入数据 1. 所有列都插入值 INSERT [INTO] TABLE_NAME VALUES(V1,V2....Vn); 特点:列值同数,列值同序 eg: insert into users v ...
- 金蝶KIS专业版替换SXS.dll 遭后门清空数据被修改为【恢复数据联系QQ 735330197,2251434429】解决方法 修复工具。
金蝶KIS专业版 替换SXS.dll 遭后门清空数据(凭证被改为:恢复数据联系QQ 735330197,2251434429)恢复解决方法. [客户名称]:山东青岛福隆发纺织品有限公司 [软件名称]: ...
- SQL语句(五)数据的修改
数据的修改 UPDATE 格式 UPDATE 表名 SET 字段名 = 字段值(这个可以是表达式) [WHERE 条件表达式] 关系运算符 (>.<.>=. <=.=.< ...
- fiddler之会话数据的修改
fiddler之会话数据的修改 fiddler记录http的请求,并且针对特定的http请求,可以分析请求数据.修改数据.调试web系统等,功能十分强大.本篇主要讲两种修改的数据的方法,断点和Unlo ...
- SQL Server中数据的修改是如何落盘的?
SQL Server 维护着一个叫做buffer cache的东西, 在buffer cache中SQL Server 读取必须被取回的data pages. 数据在修改时并不是直接写到磁盘上的, 而 ...
- Oracle中用触发器实现自动记录表数据被修改的历史信息
oracle中用触发器实现自动记录表数据被修改的历史信息. 有一些比较重要的表字段每次修改需要做历史记录,以后可以查询这个表中某些字段如何被修改过.由什么改成了什么等,由谁操作,操作时间等. 实例:1 ...
- oracle navicat 可视化操作进行数据的修改
在进行oracle数据库中的数据操作编辑时,需要小心.oracle内置的安全机制是无处不在,并且很有必要存在的. 使用navicat对oracle中数据进行select操作时,查询出的结果是只读的,这 ...
- SQL Server对数据进行修改
SQL Server对数据进行修改,修改数据库中的数据. auto"> <tr style="background:red"> <td>编号 ...
随机推荐
- virtualbox中linux系统与windows实现共享文件夹
最近有一次,需要在linux获取在我windows系统里的安装包,但是呢不论如何也拿不过去. virtualbox虽然提供了双向拖放,但是实在是太不健壮了,感觉基本就没好使过. 于是我想到了用共享文件 ...
- Web微信
一.源代码地址: https://github.com/HuangAm/Webweixin 二.总结: 1.分析Http请求 - 请求方式:get.post等等 - URL:每个请求的url,固定部分 ...
- zoj 3981 Balloon Robot
https://vjudge.net/problem/ZOJ-3981 题意: 有m个座位,其中n个队伍坐在这些位置上,一个队伍一个座位.当一个队A了题之后,他们们会得到气球,假设他们在a时刻A题,但 ...
- ipv4与ipv6的区别
对于计算机网络有一定了解的园园们来说,对这两个概念应该比较熟悉,我也将我知道的一点点小知识分享给大家吧 1.协议使用的广泛程度不同 目前,ipv4得到了广泛的应用,基本上所以的与上网有关的(看电影,玩 ...
- this对象指向
this表示函数运行时,自动生成的一个内部对象,只能在函数内部运行 function test(){ this.x = 1; } 随着使用场景的变化,this的值会发生变化 原则:this指的值调用函 ...
- Pycharm数据库连接错误
简单地总结就一句话: Python2的mysql库为:mysqldb,而Python3的为:pymysql. 当我们使用Pycharm开发项目时,首先需要下载安装相对应的数据库,以及在项目根目录下的s ...
- 《C++ Primer》学习笔记:向vector对象添加元素蕴含的编程假定
练习<C++ Primer>中的3.14节时,当敲入: #include <iostream> #include <string> using namespace ...
- [SCOI 2005]王室联邦
Description “余”人国的国王想重新编制他的国家.他想把他的国家划分成若干个省,每个省都由他们王室联邦的一个成员来管理.他的国家有n个城市,编号为1..n.一些城市之间有道路相连,任意两个不 ...
- 计蒜客 NOIP模拟赛(3) D1T1火山喷发
火山喷发对所有附近的生物具有毁灭性的影响.在本题中,我们希望用数值来模拟这一过程. 在环境里有 nnn 个生物分别具有 A1,A2,⋯,An点生命值,一次火山喷发总计 M轮,每轮造成 1点伤害,等 ...
- UVA - 11107:Life Forms
后缀数组height+二分 #include<cstdio> #include<cstdlib> #include<algorithm> #include<c ...