浅析Unity中的Enlighten与混合光照

原文https://www.cnblogs.com/murongxiaopifu/p/8553367.html

0x00 前言

在Unity的5.6版本之前的5.x中,主要使用了Geomerics公司的Enlighten【1】来提供实时全局照明以及烘焙全局照明,在5.6之后Unity引入了新的Lightmapper——Progressive来提供烘焙全局照明并且提供了更多的混合光照模式,但是Enlighten仍然是Unity中全局照明的主要提供者。

logo_enlighten.png

所以,本文就来聊聊Unity5.6以及Unity2017中和Enlighten、混合光照相关的话题吧。

0x01 直接光和间接光

大家都知道在Unity中,我们可以在场景中布置方向光、点光、聚光等类型的光源。但如果只有这些光,则场景内只会受到直接光的影响,而所谓的直接光简单理解就是从光源发出的直接影响物体的光。如果只考虑直接光的影响,则会缺乏很多光影细节,导致视觉效果很“平”。而间接光则描述了光子在物体表面之间的反弹,能够用来增加细节以及真实感。
Local_illumination.JPG
例如上图中,位于天花板灯直射光线之外的区域缺乏光照效果。表现为四壁上没有明暗细节,相反此时直接光范围之外都是均匀的黑色,而整个空间同样也显得十分平。

Global_illumination.JPG
而增加了间接光之后进行渲染,可以看到光被物体表面反弹,彩色光从一个表面转移到另一个表面。表现为红色墙壁和绿色墙壁(在右侧和红墙相对,没在画面内)的颜色反映到了场景中的其他表面上,四壁也不再是均匀的黑色,而有了层次感。

0x02 全局照明和渲染方程

正如前文所说,在Unity中使用直接光是无法模拟光子在物体表面反弹的效果的,因此Unity使用了Enlighten所提供的全局照明(Global illumination)来提供间接光效果。
所谓的全局照明(缩写为GI)是3D计算机图形中使用的一组算法的通用名称,旨在为3D场景添加更逼真的照明。这样的算法不仅考虑直接来自光源的光(直接光),而且还考虑随后来自相同源的光线被场景中的其他表面反射的情况,即间接光。
而全局照明可以用一个称为渲染方程【2】的复杂方程来描述:
屏幕快照 2018-03-11 上午11.10.20.png

渲染方程定义了光线是如何离开表面上某个点的。但是各位也可以看到,这个积分方程太复杂以至于无法快速的计算结果。因此在具体的实现上,往往采用了一些近似的方法。
Unity中所使用的Enlighten采用的近似方法是辐射度算法,即假设存在一组有限的静态元素和仅有漫射光传输来简化计算。上面的渲染方程就可以简化为下面这样:

屏幕快照 2018-03-11 下午12.38.17.png

这样,我们就可以将积分计算变为计算求其他元素的辐射度之和。
其中Bi指的是在i点最终的光,Le是i点本身的光,而两个点之前有多少比例的光会被反弹,则有“form factor”来定义,即公式中的Fij,Lj是J点的光。而pi(输入法问题,先写成p)则是一个和i的材质属性相关的参数。
这样,这个方程就比较好理解了,同时计算相比之前的渲染方程也简单了很多。
而有了简化的方程之后,Enlighten在计算全局光照时,会将场景分组到便于并行处理的system中。接下来,每个system被切割成离散的cluster
屏幕快照 2018-03-11 下午1.32.26.png
在计算光照时,Enlighten将场景视为一组cluster。而场景内的cluster则会以一种层级关联的结构来组织,并且会对其映射的静态几何体的albedo进行采样,之后在Light Transport阶段计算cluster之间的关系以使得光在cluster之间进行传递。
clusterdiagram.png

这样看上去全局照明方案已经完美的解决了间接光的问题,但是预计算实时GI的代价仍然很高,针对移动平台这样相对比较低端的设备,就需要有更加优化的解决方案。所以将静态对象的光照信息烘焙到lightmap上,同时保持对动态对象使用实时光就变成了一个不错的方案。而这就是我们下文中要说的混合光照。

0x03 混合光照

使用混合光照和直接使用烘焙的lightmap有什么区别呢?简单来说,选择“混合”烘焙模式,会将标记为静态的GameObject受到的来自混合光源的光照信息保存为Lightmap。 然而,与标记为“烘焙”的灯光不同,混合光源也会为场景中的非静态(动态)GameObject提供实时的直接光照。

在Unity5.x的版本中,光源就已经有了混合模式的选择。但是直到Unity5.6版本,混合模式又变成了4种子模式。而到了Unity2017版本,则变成了3种子模式——shadowmask的两种模式的设置被放到了Quality Setting内的Shadow下。如下图所示,分别来自Unity5.5、Unity5.6以及Unity2017.3三个版本的Lighting窗口截图。
merge_from_ofoct-4.jpg
可以看到Unity5.6之后的版本,混合光照的模式增加了Baked IndirectDistance ShadowmaskShadowmaskSubtractive这些子模式(2017版本后Distance Shadowmask的设置放到了Quality Setting中)。
首先我们可以先来看看Subtractive模式,因为Subtractive模式也是5.6之前Unity所使用的混合光照模式。
例如在Unity5.5中,我们如果将光源设置为mixed来开启混合光照,和在5.6之后选择Subtractive模式开启混合光照是一样的。
那么现在就可以安心的使用混合光照了吗?
我们可以来看看一个在这种模式下常见的问题:
屏幕快照 2018-03-11 下午8.50.42.png

即使用点光开启mixed模式时,左下角的动态对象cube并没有产生由点光产生的实时阴影。
这个听上去并不符合预期,因为mixed光照模式下对动态对象显然要提供实时的光照效果,也就是预期应该是像下图这样产生点光造成的阴影。
屏幕快照 2018-03-11 下午8.48.50.png

那么这是为什么呢?这其实是因为在5.6之前的混合光照,以及5.6之后使用subtractive模式的混合光照只能在"只有"一个方向光(Directional light)的时候才能正常工作。
subtractive模式的另一个问题就是烘焙的结果中没有高光,这是因为这种模式不仅仅烘焙了间接光,而且它还会烘焙直接光,因此也就无法提供高光效果了。所以可以看到在上面的截图中,并没有高光的效果。

正是由于这种混合光照模式的不足,所以Unity5.6之后又引入了更多的混合光照模式,以满足大家的需求。
但是,作为开销最小的一种混合光照模式,subtractive模式仍然有被保留的必要。在一些对性能要求高过对效果的要求时,subtractive模式仍然不失为一个不错的选择。

0x04 混合光照新模式-Shadowmask

Unity的新的混合光照机制的一个重要功能便是对Shadowmask【3】的支持了。混合光照开启shadowmask模式的情况下,Unity会额外生成一套shadowmask贴图,它每个纹素的4个通道可以分别用来保存4盏不同的光源。
屏幕快照 2018-03-12 下午2.30.17.png
如图,这张shadowmask保存了2盏光源的信息,一个主方向光和一个点光。
Shadowmask的主要功能本质上十分容易理解,它解決了之前subtractive模式下无法完成的事情。Shadowmask不仅仅可以处理方向光,而且还可以正确的处理点光和聚光在混合光照条件下的表现。

屏幕快照 2018-03-12 下午2.49.50.png
可以看到左下角的动态对象cube产生了由mixed模式下的点光产生的实时阴影。
同时,由于没有对直接光进行烘焙,因此能够在运行时提供正确的高光效果。

而有些朋友如果打开Mixed Lighting的子mode选择菜单的话,在5.6版本中还会看到另一个和shadowmask类似的选项——Distance Shadowmask。
屏幕快照 2018-03-12 下午2.53.27.png
那么Distance Shadowmask和Shadowmask又有什么区别呢?
这就和阴影距离(Shadow Distance)以及混合光照下阴影是如何产生的有关了。
我们在Edit->Project Settings->Quality设置中可以找到Shadow Distance的设置,在实时光模式下,只有在Shadow Distance范围内的物体才会有阴影的影响。

而在混合光照-Shadowmask模式下,静态物体和静态物体之间的阴影是通过shadowmask贴图来产生的。而对于静态物体接收的动态物体产生的阴影,则只有在shadow distance范围内动态物体才会产生实时的shadow map阴影。而动态物体之间的阴影,同样需要在Shadow Distance范围内来产生。至于动态物体如果要接收静态物体的阴影,则需要借助light probe的帮助。
屏幕快照 2018-03-12 下午3.36.27.png
如上图所示,在静态的墙和地面之间,由shadowmask提供了阴影。
至于那三个动态物体cube,在没有light probe的情况下,左下角的cube无法接受到静态墙体的阴影。而右边的两个cube之间则有实时的shadow map阴影,并且它们也在静态墙壁上投下了实时的shadow map阴影。

而如果使用Distance Shadowmask模式的话,各位猜猜哪一种阴影产生的方式会发生变化呢?
答案其实很简单,首先肯定和Shadow Distance相关,其次肯定是和实时的shadow map和烘焙好的shadowmask之间的切换相关。嗯,答案呼之欲出——在distance shadowmask模式下,处于shadow distance范围内的静态物体也会实时的产生shadow map阴影,这一点和动态物体一样,因此无论是静态物体之间还是静态物体和动态物体之间都会产生实时的shadow map阴影。而一旦超出了shadow distance的范围,则静态物体的阴影会从shadowmask中获取。

如上图所示,可以看到静态的墙和地面之间的阴影边缘锐利了,不像之前从Shadowmask获取的阴影,由于分辨率的原因可能会更加模糊,此时已经是实时的Shadow Map阴影了,因此边缘更加清晰锐利。而且左下角的cube已经可以接受到静态墙体的实时阴影了。

不过正如我刚才提到的,Distance Shadowmask和Shadow Distance以及两种阴影产生的方式有关,那么就有可能会出现一种瑕疵~即在Shadow Distance的内外,会有两种不同的阴影,而Shadowmask产生的阴影由于分辨率的原因往往更加模糊,例如下面这样:
屏幕快照 2018-03-12 下午4.51.36.png
可以看到墙壁产生了两种不同的阴影。因此这个问题各位在开发过程中可能也要关注一下。

另外,Distance Shadowmask和Shadowmask相比,性能上的开销显然要更大一些,这也不难想象,毕竟还有很多静态物体也会产生实时阴影嘛。那么我们可以简单的对比一下两者在Draw Call上的开销:
merge_from_ofoct-6.jpg
可以看到,右侧的Distance Shadowmask的drawcall开销要远远高于shadowmask。因此,在移动设备上采用shadowmask而不是开销更高的Distance Shadowmask对于追求性能的情况更加适用。当然,Unity2017之后,Distance Shadowmask和Shadowmask的设置已经移到了Quality Setting中了,这意味着我们可以在运行时动态的切换这两种模式了。

void OnTriggerEnter(Collider other)
{if(QualitySettings.shadowmaskMode == ShadowmaskMode.Shadowmask){QualitySettings.shadowmaskMode = ShadowmaskMode.DistanceShadowmask;}else{QualitySettings.shadowmaskMode = ShadowmaskMode.Shadowmask;}
}

当然,无论是Distance Shadowmask还是Shadowmask,它们都可以在Shadow Distance之外提供来自Shadowmask的阴影,虽然较之实时阴影要模糊一些。因此,对于需要远方也要产生阴影效果的项目,Shadowmask模式是一个很好的选择。

0x05 混合光照新模式-Baked Indirect

下面还剩下最后一个混合光照模式——Baked Indirect【4】。事实上这个混合模式的名字就已经十分直白了——它只烘焙间接光,其他的全部是实时的。Shadowmask贴图?不存在的。
因此我们有了实时光、实时阴影等等。所以,在Shadow Distance的范围之内和实时光下效果一样,阴影是实时的shadow map,并且在shadow distance之外不会有阴影产生——哪怕是分辨率比较低的模糊阴影也没有。
屏幕快照 2018-03-12 下午5.24.54.png

可以看到在Shadow Distance范围内的墙的阴影十分清晰,但是在范围之外则已经没有阴影了。同时,这种混合模式的开销也很大,因为它只烘焙了间接光,其他的全部是实时的。

0x06 总结

ok,让我们对这几种混合模式做一个小的总结吧~可以简单的归纳为下面这个表格【5】。
屏幕快照 2018-03-12 下午6.09.33.png

Ref

【1】https://www.siliconstudio.co.jp/middleware/enlighten/en/
【2】https://en.wikipedia.org/wiki/Rendering_equation
【3】https://docs.unity3d.com/Manual/LightMode-Mixed-BakedIndirect.html
【4】https://docs.unity3d.com/Manual/LightMode-Mixed-Shadowmask.html
【5】https://docs.google.com/spreadsheets/d/18R663xpccuyRns1kOvGAiqj0qU9QFMbbXyrOCQMsU5w/edit#gid=1748986211

 

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/246236.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

聊聊LightProbe原理实现以及对LightProbe数据的修改

原文链接https://www.cnblogs.com/murongxiaopifu/p/8997720.html 0x00 前言 最近工作比较忙,所以文章已经很久没有更新了。这篇小文的主题也是在出差的高铁上想到,因为最近和一些朋友聊天,发现他们中很多人的项目中都使用了多个实时光源。…

3D游戏的照明设计理论,第3部分:三点照明法的异端与误区

https://zhuanlan.zhihu.com/p/87997570 这是有关如何处理游戏照明的系列文章的一部分。第一部分是关于灯具,第二部分是关于光的形式材料。 在第一部分中,我们首先从文化角度考虑了灯光-灯光在整个历史上对不同的人意味着不同的事物,并且在照…

3D游戏的照明设计理论,第4部分:如何在游戏引擎中照亮游戏世界

从更一般和更概念的角度来看,这是有关我如何处理游戏照明的系列文章的一部分。我在Unity中构建了大部分示例,但这通常适用于任何3D游戏引擎,其中大多数具有类似的照明工具。 我们开始思考了有关光照的文化和概念,在第一部分。在第…

unity shader 变种(多重编译 multi_compile)

一、定义 在unity中我们可以通过使用#pragma multi_compile或#pragma shader_feature指令来为shader创建多个稍微有点区别的shader变体。这个Shader被称为宏着色器(mega shader)或者超着色器(uber shader)。实现原理:…

AndroidStudio导出aar文件给Unity使用

AndroidStudio导出aar文件给Unity使用 本文参考 :http://www.devacg.com/?post548 Demo地址:https://github.com/JulyNine/AndroidToUnity 一、用Android Studio创建个空工程 注意:包名要与Unity中工程的包名不一致,不然打包时…

Unity C# Job System介绍(四) 并行化Job和故障排除(完结)

并行化job ParallelFor jobs​docs.unity3d.com 当调度Jobs时,只能有一个job来进行一项任务。在游戏中,非常常见的情况是在一个庞大数量的对象上执行一个相同的操作。这里有一个独立的job类型叫做IJobParallelFor来处理此类问题。ParallelFor jobs当调…

C# Job System

概述 设计目的:简单安全地使用多线程,随便就能写出高性能代码 收益:FPS更高,电池消耗更低(Burst编译器) 并行性:C# Job System和Unity Native Job System共享工作线程worker threads&#xf…

Unity游戏开发——C#特性Attribute与自动化

这篇文章主要讲一下C#里面Attribute的使用方法及其可能的应用场景。 比如你把玩家的血量、攻击、防御等属性写到枚举里面。然后界面可能有很多地方要根据这个枚举获取属性的描述文本。 比如你做网络框架的时候,一个协议号对应一个类的处理或者一个方法。 比如你做…

Unity c#中Attribute用法详解

举两个例子,在变量上使用[SerializeFiled]属性,可以强制让变量进行序列化,可以在Unity的Editor上进行赋值。 在Class上使用[RequireComponent]属性,就会在Class的GameObject上自动追加所需的Component。 以下是Unity官网文档中找…

走进LWRP(Universal RP)的世界

走进LWRP(Universal RP)的世界 原文:https://connect.unity.com/p/zou-jin-lwrp-universal-rp-de-shi-jie LWRP自Unity2018发布以来,进入大家视野已经有一段时间了,不过对于广大Unity开发者来说,依然相对…

Unity 2017 Game Optimization 读书笔记(1)Scripting Strategies Part 1

1.Obtain Components using the fastest method Unity有多种Getcomponet的方法&#xff1a; GetComponent(string), GetComponent<T>() GetComponent(typeof(T)) 哪种效率最高会跟随Unity版本的变化而变化&#xff0c;对于Unity 2017&#xff0c;本书作者的测试是Ge…

C# 多态相关的文章

一 C# 多态的实现 封装、继承、多态&#xff0c;面向对象的三大特性&#xff0c;前两项理解相对容易&#xff0c;但要理解多态&#xff0c;特别是深入的了解&#xff0c;对于初学者而言可能就会有一定困难了。我一直认为学习OO的最好方法就是结合实践&#xff0c;封装、继承在…

C++ 虚函数和虚表

几篇写的不错的文章&#xff0c;本文是整合了这几篇文章&#xff0c;感谢这些大佬 https://www.jianshu.com/p/00dc0d939119 https://www.cnblogs.com/hushpa/p/5707475.html https://www.jianshu.com/p/91227e99dfd7 多态: 多态是面相对象语言一个重要的特性,多态即让同一…

Unity 2017 Game Optimization 读书笔记(2)Scripting Strategies Part 2

1. Share calculation output 和上一个Tip很像&#xff0c;可以缓存计算结果或者各种信息&#xff0c;避免多次重复的计算&#xff0c;例如在场景里查找一个物体&#xff0c;从文件读取数据&#xff0c;解析Json等等。 容易忽略的点是常常在基类了实现了某个方法&#xff0c;在…

Unity 2017 Game Optimization 读书笔记(3)Scripting Strategies Part 3

1.Avoid retrieving string properties from GameObjects 通常来讲&#xff0c;从C#的object中获取string 属性没有额外的内存开销&#xff0c;但是从Unity中的Gameobject获取string属性不一样&#xff0c;这会产生上一篇讲到的 Native-Managed Bridge&#xff08;Native内存和…

Unity 2017 Game Optimization 读书笔记(4)Scripting Strategies Part 4

1.Avoid Find() and SendMessage() at runtime SendMessage() 方法和 GameObject.Find() 相关的一系列方法都是开销非常大的。SendMessage()函数调用的耗时大约是一个普通函数调用的2000倍&#xff0c;GameObject.Find() 则和场景的复杂度相关&#xff0c;场景越复杂&#xff0…

Unity HDRP中的光照烘焙测试(Mixed Lighing )和间接光

部分内容摘抄自&#xff1a;https://www.cnblogs.com/murongxiaopifu/p/8553367.html 直接光和间接光 大家都知道在Unity中&#xff0c;我们可以在场景中布置方向光、点光、聚光等类型的光源。但如果只有这些光&#xff0c;则场景内只会受到直接光的影响&#xff0c;而所谓的…

聊聊Unity项目管理的那些事:Git-flow和Unity

感谢原作者https://www.cnblogs.com/murongxiaopifu/p/6086849.html 0x00 前言 目前所在的团队实行敏捷开发已经有了一段时间了。敏捷开发中重要的一个话题便是如何对项目进行恰当的版本管理。项目从最初使用svn到之后的Git One Track策略再到现在的GitFlow策略&#xff0c;中…

聊聊网络游戏同步那点事

写的非常好的一篇博文&#xff0c;转载自https://www.cnblogs.com/murongxiaopifu/p/6376234.html 0x00 前言 16年年底的时候我从当时的公司离职&#xff0c;来到了目前任职的一家更专注于游戏开发的公司。接手的是一个platform游戏项目&#xff0c;基本情况是之前的团队完成…

Unity 2017 Game Optimization 读书笔记 Dynamic Graphics(1)

The Rendering Pipeline 渲染表现差有可能取决于CPU端&#xff08;CPU Bound&#xff09;也有可能取决于GPU(GPU Bound).调查CPU-bound的问题相对简单&#xff0c;因为CPU端的工作就是从硬盘或者内存中加载数据并且调用图形APU指令。想找到GPU-bound的原因会困难很多&#xff…