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数据的修改的更多相关文章

  1. VMware 虚拟化编程(10) — VMware 数据块修改跟踪技术 CBT

    目录 目录 前文列表 数据块修改跟踪技术 CBT 为虚拟机开启 CBT CBT 修改数据块偏移量获取函数 QueryChangedDiskAreas changeId 一个 QueryChangedD ...

  2. MySQL数据库5 - 插入数据,修改数据,删除数据

    一.插入数据 1. 所有列都插入值 INSERT [INTO] TABLE_NAME VALUES(V1,V2....Vn); 特点:列值同数,列值同序 eg: insert into users v ...

  3. 金蝶KIS专业版替换SXS.dll 遭后门清空数据被修改为【恢复数据联系QQ 735330197,2251434429】解决方法 修复工具。

    金蝶KIS专业版 替换SXS.dll 遭后门清空数据(凭证被改为:恢复数据联系QQ 735330197,2251434429)恢复解决方法. [客户名称]:山东青岛福隆发纺织品有限公司 [软件名称]: ...

  4. SQL语句(五)数据的修改

    数据的修改 UPDATE 格式 UPDATE 表名 SET 字段名 = 字段值(这个可以是表达式) [WHERE 条件表达式] 关系运算符 (>.<.>=. <=.=.< ...

  5. fiddler之会话数据的修改

    fiddler之会话数据的修改 fiddler记录http的请求,并且针对特定的http请求,可以分析请求数据.修改数据.调试web系统等,功能十分强大.本篇主要讲两种修改的数据的方法,断点和Unlo ...

  6. SQL Server中数据的修改是如何落盘的?

    SQL Server 维护着一个叫做buffer cache的东西, 在buffer cache中SQL Server 读取必须被取回的data pages. 数据在修改时并不是直接写到磁盘上的, 而 ...

  7. Oracle中用触发器实现自动记录表数据被修改的历史信息

    oracle中用触发器实现自动记录表数据被修改的历史信息. 有一些比较重要的表字段每次修改需要做历史记录,以后可以查询这个表中某些字段如何被修改过.由什么改成了什么等,由谁操作,操作时间等. 实例:1 ...

  8. oracle navicat 可视化操作进行数据的修改

    在进行oracle数据库中的数据操作编辑时,需要小心.oracle内置的安全机制是无处不在,并且很有必要存在的. 使用navicat对oracle中数据进行select操作时,查询出的结果是只读的,这 ...

  9. SQL Server对数据进行修改

    SQL Server对数据进行修改,修改数据库中的数据. auto"> <tr style="background:red"> <td>编号 ...

随机推荐

  1. android studio 何如修改报名

    1. 重命名办法,网上很多见 2. 对于需要重新修改包名的级别的 a. 修改package 和 gradle 的包名,对应一致. b. 修改R 所在包名,使用crtl+n修改R文件的路径 c. 手动首 ...

  2. Linux中的重启命令

    1.系统重启: shutdowm    该命令安全地将系统关机. 有些用户会使用直接断掉电源的方式来关闭linux,这是十分危险的.因为linux与windows不同,其后台运行着许多进程,所以强制关 ...

  3. ZOJ-2913 Bus Pass---BFS进阶版

    题目链接: https://vjudge.net/problem/ZOJ-2913 题目大意: 问哪个区域到公交路线上所有区域的最大距离最小 思路: 这里要求出到底是哪个区域到某些指定区域的最大距离最 ...

  4. 分析ajax请求抓取今日头条关键字美图

    # 目标:抓取今日头条关键字美图 # 思路: # 一.分析目标站点 # 二.构造ajax请求,用requests请求到索引页的内容,正则+BeautifulSoup得到索引url # 三.对索引url ...

  5. 判断字符串的后缀.endswith()

    可以用str.endswith('.jpg')来判断字符串是否以jpg结尾,返回True或者False

  6. https原理通俗了解

    摘要:本文尝试一步步还原HTTPS的设计过程,以理解为什么HTTPS最终会是这副模样.但是这并不代表HTTPS的真实设计过程.在阅读本文时,你可以尝试放下已有的对HTTPS的理解,这样更利于" ...

  7. 一文了解安卓APP逆向分析与保护机制

    "知物由学"是网易云易盾打造的一个品牌栏目,词语出自汉·王充<论衡·实知>.人,能力有高下之分,学习才知道事物的道理,而后才有智慧,不去求问就不会知道."知物 ...

  8. linux samba服务配置

    1.下载 wget+rpm或yum install 2.配置/etc/samba/smb.conf cat smb.conf | grep setsebool 执行终端打印出来的字符串 setsebo ...

  9. [LeetCode] Find Bottom Left Tree Value 寻找最左下树结点的值

    Given a binary tree, find the leftmost value in the last row of the tree. Example 1: Input: 2 / \ 1 ...

  10. Spring MVC 知识点记忆

    1.Dao  用的 @Repository 2.Handler 用的 @Controller 3. @Autowired 消除了对get set方法 4. @RequestMapping(value= ...