学习笔记06GI

实际引擎中的全局光照,并不是真正实时的,分为两种,第二种是比较常用的,就是lightmap,烘焙好间接光照,然后进行一个采样。

预计算实时GI,他这个也是通过预计算然后再使用算好的数据,这个预计算结果不一定是保存在纹理中。

这两个东西,是可以同时使用的,也可以单独用一个。

烘焙系统视觉效果上作用区别不大。

就是他们都是烘焙,结果基本上是一样的,只是烘焙的过程,采用的算法等等的可能不同,就比如渐进式烘焙,这个他是先烘焙你摄像机看到的部分,然后在其他,enlighten就有点不一样,但是对于我们来说

区别真的不大。

这里第一个就是只烘焙间接光,第二个就是阴影的烘焙相关的。

灯光mode为烘焙,那么这个灯只对静态物体起作用,如果是mixed或者是realtime的,那就对静态和动态都起作用。

首先上边三种模式看第一个单词mixed,也就是这仨都是针对mixed mode的光的。为啥呢?

因为bake的光,他是直接和间接,阴影都进行烘焙,没得商量,所以这里的选项根本不会对bake光源有任何的控制。

然后实时光照,根本不鸟这个bake GI。如果实时光照匹配这个BakeGI的话,根本没有间接光。

然后这三个模式既然是针对mixed的,mixed是一个实时和烘焙的混合。

对于baked indirect和shadowmask这俩,都是直接光是实时的,间接光是烘焙的。

对于subtractive,静态物体,两个光都是烘焙的,非静态物体,直接光是实时的,间接光是烘焙的。

关于他们的阴影,baked indirect和 subtractive没啥特殊的,实时光就实时阴影,间接光就烘焙阴影。而shadowmask就不一样了,它是有俩的,另一个是distance shadowmask

第三个就是间接光和直接光都进行一个烘焙。

打开unity,调整光方向,让他从右边口射入。

关于场景里面的三个球,他们都是自发光的。

打开烘焙界面。

然后这里有几个设置,上边标记的那俩就是预计算实时GI和烘焙GI。

烘焙之前,我们需要勾选这里的static。

打开实时GI关闭烘焙GI的效果。

这个是关闭了实时GI的效果。

这个效果上和刚才是类似的。直接光照是可以实时修改的。

直接光和间接光都进行一个烘焙,需要把光照的模式修改再进行烘焙。

烘焙完成之后,再修改直接光的方向等就不会有任何反应了。

这时候直接看上去和subtractive没啥区别。

如果我们这个把灯关掉,直接光是会消失的。这个是shadowmask的作用。

意思是:这个他会把直接光和间接光烘焙到场景中,但是不同于subtractive的是,他会把直接光和间接光分开进行烘焙,

这里可以看到。

如果修改灯光强度,那么这里的光照效果会变暗,变亮,阴影的亮暗会受影响,但是如果我们修改光的方向,阴影并不会发生移动。就是因为他是烘焙死的,位置不会变的。

如果我们切换成distance shadow mask。

他会把直接光变成一个实时光,阴影是实时的。

当我们把距离拉远(上边project setting里面有一个shadow distance控制距离分界),会变成烘焙的阴影。

这里有相关的设置。

 

============================================================================================================

首先创建一个shader。.

搭建一个场景,这里本身柱子和平面都是unity的standard材质,所以应该有阴影的,这里看不到是因为距离远,因为刚刚的设置:

修改一下:

三个对象都勾选成static。

这里烘焙之后,由于我们使用的shader就是一个unlit的shader,他里面根本没有什么投射阴影,接受阴影等代码,所以就是上边这样的效果。

要想让他融入这个全局光照,我们需要给他添加很多东西。

这一点如何实现,我们可以参考unity自带的shader里面怎么写的。

这个打开的话,是表面着色器,我们拽到unity里面获取一下像素片元着色器的代码,然后再看。

这个着色器是实现了GI的,所以我们就参考它的代码,

找到像素着色器,会有这么一个函数调用,这个函数其实就是实现GI的核心函数。

找到实现。

这里就是嵌套调用了一下。

由于我们这里要自己写GI,那么这些函数我们待会也要自己写,所以把他们复制到一个头文件中,待会自己写,自己调用就好了。

创建头文件的方式,就是随便创建一个文件,shader就可以,然后打开in explorer,然后修改后缀即可。

参考人家的

写好之后,include到自己的shader中,注意放的位置和include的写法。

这里如果文件移动了。shader啥的没有刷新,可以右键reimport一下,刷新一下。

=======================================================================================

GI了解补充

基于 Unity 引擎的游戏开发进阶之 全局光照 - 知乎 (zhihu.com)

光照探针,那个是针对非静态物体的。他会把一个物体的发光给吸收到小球里面,然后小球来照亮周围的物体。注意这里的发光物体必须是静态的。

它可以有很多的小球,这个小球根据和物体的距离不同,小球里面存储的光照是不同的。

然后该物体周围的物体会吸收三个小球的光。选择最近的三个小球进行吸收。

为了得到更好的效果,我们这里可以对探针以及物体做一些处理。

对于探针,球的数量要多一些,然后根据距离进行一个分布。由近到远。因为极端情况就三个小球,周围的所有的物体吸光都是吸相同的三个小球,也就是吸到的亮度是一样的,这样效果就不合理了

远的近的应该被不同亮度照亮的。所以要多一些球,这样距离远的物体就吸距离远的球,距离远的球他的亮度会低一些,这样符合正常的效果。

对于物体,要尽可能的小一些,极端情况,一个巨大的大地板,然后它吸哪三个小球,是由他的中心点决定的,中心点距离哪三个球最近,那么整个板子就被相同的光照亮,这也是不合理的,因为地板很远的一部分

和距离发光物体近的一部分,应该具有不同的亮度。所以把物体弄的小一些,无论哪一部分距离发光物体的距离都是差不多的,然后被照亮的强度相同,没有太大的问题。

实时GI,这个实时指的并不是完全的实时,主要是指的可以实时改变灯光的亮度,颜色。然后效果上也会由实时的改动。

他针对的是静态物体。其实他这个提前计算的是:全局光照的那些光线路径,而具体的照明结果是实时计算的。

Bake GI,针对静态物体,就是直接把所有的光照结果直接烘焙到贴图上,也就是烘焙之后,这些光都是板上钉钉的,不会随着光的属性修改而实时改变的。

相关操作:

创建探针。然后直接烘焙就行了。

如果光源为baked,那么lighting里面是否开启real time GI都无影响。因为一切照亮都是从烘焙到贴图中得到的。同时非静态物体不会被照亮。

如果光源为mixed,会照亮非静态物体。

lighting中选择baked GI,直接光照是实时改变的,包括位置和颜色等,间接光照不能由任何的改动。

lighting中选择 real time GI,直接光照是可以实时改变的,没有间接光照。其实lighting中选择了实时GI,再光源处就只有realtime一个选项。

如果光源为real time,会照亮非静态物体,

lighting中选择baked GI,直接光照没问题,没有间接光照。

lighting中选择realtimeGI,直接光照没问题,间接光照颜色亮度可以实时修改。

所以有用的搭配:

Baked 搭配 baked GI。

Mixed 搭配 baked GI。

Realtime 搭配 realtime GI。

也就是这个老哥说的三种情况。

基于 Unity 引擎的游戏开发进阶之 全局光照 - 知乎 (zhihu.com)

对于光源模式的三种:realtime,baked,mixed。

这里说的就更清楚了,mixed其实就是把间接光,利用烘焙来生成,然后直接光,就和普通情况一样,完全实时的。

而实时光,就是每一帧计算一次,实时光不会进行具体颜色的烘焙,会进行路径的烘焙,所以选择实时光的时候,弄烘焙GI是没有间接光效果的,就类似它没有烘焙你却想要显示它的烘焙结果,当然显示不出来了。

而选择实时GI的时候,就是每一帧计算一次,根据已经烘焙好的路径进行实时计算,实时计算颜色强度等。

写给美术看的Unity全局光照技术(理论篇) - 知乎 (zhihu.com)

Unity学习——预计算实时GI(全局光照) - 知乎 (zhihu.com)

关于这个勾选static的,其实只需要勾选lightmap static就够了,外面static的勾选相当于一个总开关,它勾选了,里面这些都勾选上。

===========================================================================================

弄好了cginc之后,我们就要把一些代码拷贝到我们的cginc中。

弄完之后改名字。改成我们自己的,不会重定义冲突。

然后一一来看:

这个inout其实就是相当于传引用。会对gi参数进行修改。

第一个是直接光照,那么里面只需要光的方向和颜色就行了,第三个舍弃了。不做存储了。

然后间接光照,

玩这个UnityGI,我们需要加上头文件,注意光照相关的就加上边这俩头文件就行了,他俩还会链接到其他的头文件,所以基本上光照相关的都会包含进来。

写上我们自己的gi。

还有一个参数,这个主要就是物体的表面属性了。

然后还有一个GIInput

UnityLight就是刚刚的直接光照结构体。 atten是衰减。

lightMapUV,GI中的间接光照,无论是bake GI还是realtime GI都是会采样纹理贴图的。这个变量就是采样贴图的纹理坐标。

看后面注释,xy分类是bake GI的,zw是realtime GI的。

剩下的都是和探针相关的,先不涉及。

到现在为止,三波输入都搞定了。

====================================================================================================================

对于上边还缺了一个lightmapUV的变量写入,这个是一个重头戏。

他这个变量只有在bake GI或者realtime GI的情况下才会使用。

所以这里我们要进行一个判断。根据什么判断?也就是根据是否开启bake GI或者realtime GI

上边的意思就是,我们先#pragma multi_...之后,他就会弄出来下面那些宏供我们判断。

可以搞这么一个小测试。

对这俩进行开关的时候,输出的颜色就黑白变化了。

判断条件说了,那么这个lightmapUV到底从哪搞来,其实就是一个模型的二套UV。第一套UV都是模型表面那些采样。第二套UV是专门和烘焙相关的。所以从哪来?就是从appdata中获取。

appdata中的语义写成TEXCOORD1即可。这就会传入第二套UV。

第二套UV,DCC软件可以生成,DCC软件不生成,在模型导入引擎的时候,一般也会默认勾选帮我们生成第二套UV。

注意了,这里我们还有一种方式,就是appdata得到了uv之后,直接传到v2f,然后到了像素里面再对lightmapUV进行赋值,这样中途v2f可把float4改成float2。

但是这样就大错特错了,因为顶点shader里面执行这个计算要比像素shader里面执行这个计算高效!

在模型的面板中,我们是可以看到这个相关的信息的。

到这里基本上把参数准备就完成了。

==============================================================================================================================

首先解决一个报错,之前giInput.light写错了,他需要的是一个直接光的结构体,我们把之前写过的gi给放在上边,然后这里直接把gi的light传进去就行了。

有时候他会要求这种初始化,因为下面他作为输出参数使用,所以初始化一下。

当我们计算完成gi之后,继续观察unity里面shader GI咋写的,他又干了啥:

用了一下上边的这个函数。依旧是复制自己头文件里面。

里面调用了UnityLambertLight:

这个就是最基本的兰伯特模型。

在lightingLambert还有一个判断宏,定义如下:

现在效果是一个黑色,因为我们没有对o里面的法线进行初始化,导致Lambert计算的时候是0.

把灯光和模式都设置一下,就可以看到效果,直接光已经ok了,但是间接光还是有些问题的。

=================================================================================================================

效果不对,我们就利用中间的结果进行一点点的输出来看问题在哪:

直接把间接光照给输出出来。

这里的结果是正确的,正确的烘焙效果,带有阴影。

注意这里的什么是正确的,其实就想象间接照明他的效果,这里虽然是间接,他也不会是均匀的,因为直接光照射其他物体是有方向偏重的,所以其他物体反射到球身上也是有方向偏重的。

另外这里直接光是没有阴影的,因为直接光的部分,就是一个纯Lambert,我们没有做投射阴影的东西。

那只能是这个lightLambert的问题了。

这里有问题,这个变量根本没有初始化。这里初始化为1即可。

最终效果。

直接光加上间接光。

目前还未对间接光计算的黑盒做一个剖析,所以下面:

其实核心就是这个函数了。

这里套了两层,其实可以简化成这一句。

置空。

其实这里除了几个if之外,这里的data.light是我们场景中的主平行光。就是把它的颜色考虑一个衰减,同时把间接光考虑一下AO。

第一个if,里面判断的宏是:

shadows_screen是硬件上支持与否的一个判断。后面是静态烘焙是否打开。

其实这个是distance shadermask情况下才有的,这里我们的球的shader并不能投射实时阴影,因为投射阴影这个要在shader里面写出来。我们并没有写。

所以没法使用这个效果,它过渡也看不出来。

这个也暂时跳过。

这里首先进行一个采样贴图,采样的是烘焙的光照贴图,采样的map是看不到实现的,unity没有开放出来。

然后烘焙贴图的采样结果都是一个RGBM 的格式,是一个编码之后的,我们需要解码。

深入了解Unity的光照贴图(一) - 知乎

编码的目的是存储一些超过1的数值。

我们甚至可以直接把这一部分拿出来,这样就不必走gi那些,简化了shader。

但多数还是用Unity内部的那个,并且在里面根据需要做一些修改。

这一坨和directional mode有关系的,表示定向光烘焙模式,开启后表示生成的烘焙贴图会多生成一组烘焙贴图。

shadermask会生成三组,subtractive是两组,其中的一组是定向光,作用就是在shader表面呈现光照方向的效果,比如法线贴图。

无论开启与否,下面就是把刚刚采样到的颜色给到间接光的diffuse。

这个realtime GI类似的,也是采样解码。

==============================================================================================

上节,再说distance shadowmask的时候有一个阴影混合过渡,我们当时没有写阴影,所以没有测试,这里补充上。

直接从之前的shader中复制即可。

然后就有投影了,还差个接受阴影。

这里就加上attenuation那一句,这个atten有两种含义,一种是光照衰减,一种是实时阴影的值。

这里我们要做过渡效果,其实就是对这个值进行一个处理,那么就是传递到giInput,然后到

这里会对giInput传递过来的atten进行一个修改。实现过渡效果。

然后采样阴影还差点东西,但是不用上边的方式,上边方式atten得到的只是采样后的阴影,我们想要的是采样后的阴影加上光照的衰减。

这个可以参考unity内部的shader:

这里补充上这两句就可以实现我们想要的。实现了阴影之间的过渡。包括投射的包括接受的,从实时到烘焙的过渡。

可以看一下上边两句具体干了啥:

这里就相当于一个宏,干了俩宏的事情。 

其实就是把之前的光照衰减和实时阴影的采样进行了一个合并,就是一块写了。

这一句也是两个的结合。

=========================================================================================================================

准备这样一个场景,然后把灯的模式改成baked。然后地板是static,球是非static。

这样烘焙之后,因为baked模式,所以一切灯都是烘焙的,同时只有静态物体接受灯光。那么结果就是球根本没收到灯光。

 

如果想要让这个球收到灯光。

我们想要让这个动态物体获得光照的影响,那就可以用探针。

可以创建一个空对象,然后直接add component,找到探针。

我们调大这个空物体的scale,其实就有一个包围盒被放大。

然后这里好像包围盒上就直接有探针。

绿色就偏多一些。

上边能这么干,是因为使用的是内置的standard shader,他都弄好了,使用我们自己的shader就不行了,怎么才能行呢?

(附上我们自己的材质,就会是黑色的,因为场景内的光都是baked的,而且动态物体,所以没有东西能照亮它)

还是参考unity内部的shader。

有这么一段。

粘贴到我们自己里面。

这里首先需要此对象没有开启静态烘焙,也就是这个物体时动态的??然后俩条件,讲了半天没听明白。

然后这里写了一句o.sh,我们需要在我们自己的o里面也定义一个sh;

再往下,一个条件判断后,它进行了一个函数的调用:

里面的参数就是4盏灯,float4变量表示同时记录四盏灯的x,4个y,4个z,这样合起来一共四个灯。(逐顶点照明最多支持四盏灯)

然后传入四个颜色,四个衰减,世界坐标和法线。

修改一下。入乡随俗。

当计算完这个sh之后,怎么使用它?就是在frag里面:

给giInput做一个赋值即可。

然后解决报错就出现了光照探针的效果。

这里还测试了一个什么逐顶点光,就是上边写的那一坨。没看懂,

大佬文章,从PBR到GI。

Unity PBR Standard Shader 实现详解 (三)全局光照函数计算 - 知乎

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值