Unity 渲染教程(五):多个光源

这篇Unity渲染教程介绍了如何处理多个光源,包括方向光、点光源和聚光灯,以及如何利用球面谐波函数进行光照近似计算。教程中详细讲解了光源的衰减、混合、范围和类型,以及如何优化渲染批次。此外,还探讨了顶点光照、光源的渲染模式和球面谐波在环境光照中的应用,展示了不同光源和球面谐波对场景光影的影响。
摘要由CSDN通过智能技术生成

对每个物体渲染多个光源的光照效果。

支持不同的光源类型。

使用光源cookie。

计算顶点光照。

在光照计算中添加球面谐波函数(spherical harmonics)的光照。

这是关于渲染基础的系列教程的第五部分。这个系列教程的上一部分讲的使用单一方向光的光照效果。现在我们将要添加对多光源光照的支持。

系列回顾:

Unity 渲染教程(一):矩阵

Unity 渲染教程(二):着色器基础

Unity 渲染教程(三):使用多张纹理贴图

Unity 渲染教程(四):第一个光源

这个系列教程是使用Unity 5.4.0开发的,这个版本目前还是开放测试版本。我使用的是build 5.4.0b21版本。

使用多光源对简单的白色球体进行照明的有趣效果。

导入文件

要为我们的着色器添加对多个光源的支持,我们必须向这个着色器添加更多的渲染通道。这些渲染通道最终将包含几乎相同的代码。为了防止代码重复,我们将把着色器代码移动到导入文件之中。

Unity没有菜单选项来创建着色器导入文件。因此,你必须通过操作系统的文件浏览器手动转到项目的资源文件夹。在与光照着色器相同的文件夹中创建一个名为My Lighting.cginc的纯文本文件。你可以复制我们的着色器文件内容,重命名它,然后清除它里面的内容。

你的第一个导入文件。

将我们的光照着色器的所有代码复制到这个文件里面,从#pragma语句的正下方直到ENDCG。因为这个代码不再直接出现在着色器通道之中,我不再缩进它。

我们现在可以在我们的着色器中导入这个文件,来替换以前的代码。因为这个着色器文件和导入文件在同一个文件夹中,我们可以按文件名直接引用它。

避免重定义错误

正如你已经知道的那样,导入文件本身可以导入其他导入文件。当你导入一些文件,而这些文件又导入相同的其他文件的时候,那么最终会在你的代码里面出现重复的代码。这将导致关于代码重定义的编译器错误。

为了防止这种重定义,通常使用定义检查来保护导入文件。这是一个预处理器检查,看看是否已经做出了某个定义。这个定义仅仅是与导入文件的名称相对应的唯一标识符。你可以定义它是什么,甚至没有。在我们的例子中,我们将使用标识符MY_LIGHTING_INCLUDED。

现在我们可以把我们的导入文件的全部内容放在一个预处理器if块中。条件是MY_LIGHTING_INCLUDED尚未定义。

通常,这个导入文件定义检查的里面的代码不进行缩进。

第二个光源

我们的第二个光源还是一个方向光源。复制主光源并改变它的颜色和旋转,这样的话你可以把这两个光源分开。此外,减少它的强度滑块,比如说是到0.8。Unity将会根据光源的强度来确定这两个光源的哪一个光源是主光源。

两个方向光。

虽然我们的场景中有两个方向光光源,但是场景中物体的渲染表现并没有因为引入了新的光源而产生变化。我们可以通过enable的开关来每次只激活一个光源。在只有一个光源激活的情况下,我们可以看到两个不同光源的不同的光照效果。但是当两个光源都激活的状态下,只有原来的主光源起作用,添加的第二个方向光不起作用。

一次只有一个起效果,不能两个同起作用。

第二个渲染通道

前面的实验中,我们只能看到只能有一个方向光光源生效的照明效果。产生这一结果的原因是,我们的Shader代码中只对一个光源进行了计算(第二个光源被忽略了)。前向基本渲染通道是用于主方向光源。为了渲染一个额外的光源,我们需要一个额外的渲染通道。

复制我们的着色器代码,并将新的光照模式设置为ForwardAdd。Unity将使用这个渲染通道来渲染额外的光源。

我们现在看到第二个光源的效果了,但是没有主光源的效果。Unity会渲染这两个光源,但是在后面执行的加法渲染通道的渲染结果会覆盖在它之前执行的基础渲染通道得到的渲染结果。这是错误的。为第二个方向光进行渲染计算的加法渲染通道应该将其渲染结果累加到原有的由基础渲染通道完成的主方向光的渲染结果之上,而不是替换原有的主方向光源的渲染结果。我们可以通过改变加法渲染通道的混合模式来让图形处理器将加法渲染通道的渲染结果加到基础渲染通道的渲染结果之上。

新旧像素数据的混合方法由两个因子确定。新旧数据与对应因子相乘,然后相加到一起来得到最终的混合结果。默认的混合模式是不对新旧像素数据进行混合,这相当于 One Zero的混合模式(混合结果 = 0x旧的颜色+1x新的颜色)。使用默认混合模式的渲染通道会使用新的渲染结果替换帧缓存中的当前像素中的任何数据。为了把当前的渲染结果累加到当前像素的帧缓存上,我们需要将混合模式设置为One One。这种混合模式被称为添加模式。

两个光源叠加到一起的效果。

在一个物体第一次被渲染的时候,图形处理器会检查当前的这个物体片段(fragment)是否被其他物体的片段遮挡。这个片段遮挡判断所使用的距离信息存在图形处理器的深度缓冲区中,这个深度缓冲区也称作Z缓冲区。因此,每个像素会有其对应的颜色和深度信息。这个深度信息表示每个像素上当前绘制的最前面的物体距摄像机的距离。这个机制有点像声纳。

如果我们想要渲染的片段没有被其他片段遮挡,那么它当前是最接近相机的表面的片段。图形处理器继续运行片段着色程序。它产生的结果会覆盖当前像素的颜色,并在深度缓冲中记录当前的物体深度。

对于一个像素,如果当前像素上当前准备绘制的物体片段的深度比深度缓存中保存的深度更远,那么在这个像素上,当前物体被其他物体遮挡。在这种情况下,当前这个像素上,我们不会看到正在准备渲染的这个物体,这个物体不会被渲染。

那么如何处理半透明对象呢?

深度缓冲方法仅适用于完全不透明的对象。半透明对象需要不同的方法。 我们将在未来的教程中处理这些半透明的对象。

这个过程会对第二个光源重复一遍,除了那些现在我们添加的已经存在的东西以外。再次,片段程序只有在没有什么东西在我们正在渲染的片段前面的情况下运行。如果是这样的话,我们最终得到的深度与之前的一样,因为它是对同一个对象进行渲染得到的信息。因此,我们最终记录完全相同的深度值。

因为写入深度缓冲区两次是没有必要的,让我们禁用它。这是使用ZWrite Off语句来关闭着色器。

绘制调用的批次

要更好地了解发生了什么,你可以启用游戏视图右上角的状态统计面板。我们关心的是绘制批次数量(Batches)和合批节省的批次数量(Saved By Batching)。这两个数字反映了当前场景的绘制调用情况。

我们先在只有主光源被激活的情况下观察。

五个渲染批次,一共七个。

因为我们有六个物体,你会期望有六个渲染批次。但是,启用动态批次处理之后,所有三个立方体都会合并为一个批次。所以在动态批次处理节省了两个批次之后,你认为我们将会有4个渲染批次。但是显示仍有5个渲染批次。

额外的批次处理是由动态阴影引起的。让我们通过完全禁用质量设置中的阴影,通过“编辑/项目设置/质量”来消除它。 请确保你调整了当前在编辑器中使用的质量设置。

不再有阴影,那么只有四个渲染批次。

为什么我还有一个额外的渲染批次?

你可能是在渲染环境的立方体贴图。这是另一个绘制调用。我们在前面的教程中已经禁用了它。

关闭阴影与立方体贴图的设置之后,你可能需要触发一下统计信息更新(比如通过点击游戏视图)来查看最新的批次信息。现在绘制批次应该是4,节省的批次应该是2。接着,我们激活第二个方向光光源来看看有什么变化。

两个光源,一共十二次批次。

因为每个物体现在被渲染两次,我们最终有十二个渲染批次,而不是六个渲染批次。这是预期得到结果。你可能不会期望到的是,动态批次处理不再工作。 不幸的是,unity只能对最多受到一个方向光影响的物体进行动态合批的优化处理。使用第二个方向光光源会使动态合批这个优化失效。

帧调试器

要更好地了解场景的渲染方式,现在可以使用帧调试器。通过窗口/帧调试器打开它。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

码农老K

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值