Unity 渲染教程(七):阴影

本文详细介绍了Unity中阴影的原理和实现,包括平行光阴影的渲染过程,阴影贴图的生成,以及阴影质量问题。通过深度贴图、阴影贴图、级联阴影和阴影偏移等概念,阐述了Unity如何处理物体间的遮挡关系,以及如何提高阴影质量。此外,还讨论了阴影投射和接收的实现,包括支持不同光源类型(如平行光、聚光灯、点光源)的阴影,并探讨了阴影贴图的采样和抗锯齿问题。文章以一个简单的Unity场景为例,展示了启用和禁用阴影的效果,以及如何在着色器中添加对阴影的支持。
摘要由CSDN通过智能技术生成

平行光阴影

虽然我们的光照着色器现在能够产生相当真实的结果,但是它孤立地评估每个表面片段。它假定来自每个光源的光线最终到达到每个表面片段。但只在这些光线不被其他东西阻挡的情况下,这个判断才是正确的。

一些光线被其他东西阻挡。

当一个物体位于光源和另外一个物体之间的时候,这个物体可以阻止部分或全部光线到达另外一个物体。照亮第一个物体的光线不再可用于照亮第二个物体。结果就是,第二个物体至少有部分区域未被照亮。不亮的区域位于第一个物体的阴影之中。为了描述这一点,我们经常说第一个物体在第二个物体上投射阴影。

在现实的情况中,在完全照明和完全阴影的空间之间存在一个过渡区域,这个过渡区域被称为半阴影区。半阴影区的存在,是因为所有光源都有一个体积。光源具有体积导致的结果就是,存在一些仅有部分光源可见的区域,这意味着它们被部分地遮蔽。光源越大,表面距离阴影投射提的距离越远,被遮蔽的区域也就越大。

带有半阴影区的阴影。

Unity不支持半阴影。Unity支持的是软阴影,但软阴影是一种阴影滤波技术,而不是半阴影的模拟。

启用阴影

如果场景没有阴影的话,很难看到对象之间的空间关系。为了说明这一点,我创建了一个简单的场景,在这个简单的场景厘米那有几个拉伸的立方体。我在这些立方体上方放了四排球。中间两行是浮动的球体,而最外面的两行通过圆柱体将球体与它们下方的立方体相连。

这些对象具有Unity的默认材质。整个场景有两个方向光,一个是默认的方向光,和一个略弱的黄色方向光。这些光源是在以前的教程中使用过的相同的光源。

目前,整个项目范围内的阴影都被禁用了。我们在之前的教程中设置过这一点。环境光的强度也设置为零,这使得我们更容易能看到阴影。

两个方向光,没有阴影没有环境光的效果。

阴影是项目级别的质量设置的一部分,通过编辑/项目设置/质量可以找到具体的设置项。 我们将在高质量等级中启用阴影。高质量等级意味着支持硬阴影和软阴影、使用高分辨率、稳定的适配投影、150的投影距离以及四个级联阴影贴图。

阴影的质量设置。

还要确保两个方向光源都设置为投射软阴影。它们的分辨率应该取决于质量设置。

对每个光源投射的阴影进行设置。

当两个方向光源投射阴影的时候,所有对象之间的空间关系变得更加清楚。整个场景也变得更加真实,看起来也更加有趣。

带有阴影的场景效果

阴影贴图

Unity是如何将这些阴影添加到场景中取得?标准着色器显然有一些方法来确定光源发出的光线是否被阻挡。

你可以通过在场景中投射一条射线,从射线是否能到达表面片段来判断一个点是否在阴影中。如果这条射线在到达表面片段之前命中了某物,那么光源往这个方向发出的光线就会被阻挡。这是物理引擎可以做到的,但是对于每个表面片段和每个光源都这样做是非常不切实际的。而且在得到这些结果之后,你必须将得到的结果通过某种方式传递到图形处理器之中。

有几种技术能够支持实时阴影。每种技术都有它自己的优点和缺点。Unity使用的是现在最常见的技术,即阴影贴图。这意味着Unity以纹理方式存储阴影信息。我们现在将研究下Unity的阴影具体是如何实现的。

通过“窗口/帧调试器”打开帧调试器,启用阴影,并查看渲染步骤中的层次结构。查看启用阴影的帧与不启用阴影的帧之间的差异。

启用阴影的帧与不启用阴影的帧对于渲染过程的差异

当禁用阴影的时候,所有的物体都会照常进行渲染。我们已经很熟悉这个过程了。但是当启用阴影的时候,渲染过程变得更加复杂。渲染阶段变多了,还多了非常多的绘制调用。所以渲染阴影是非常昂贵的!

渲染到深度贴图

当启用平行光阴影的渲染选项的时候,Unity开始在渲染过程加入一个深度渲染Pass。所得到的结果会放一张与屏幕分辨率相匹配的纹理贴图中去。这个深度渲染Pass会渲染整个场景,但是仅记录每个片段的深度信息。这是与图形处理器用来确定片段与先前渲染的片段的深度相对关系相同的信息。

这个数据对应于片段在裁剪空间中的Z坐标。裁剪空间是定义相机可以看到的区域的空间。深度信息存储为0到1范围内的值。当查看纹理贴图的时候,离相机比较近的纹理像素显示为比较暗的点。离相机比较近的纹理像素显示为比较亮的点。

在相机的近平面设置为5的时候的深度纹理贴图

什么是裁剪空间?

裁剪空间决定了相机可以看到的区域。当你在场景视图中选择主摄像机的时候,你将在主摄像机的前面看到一个金字塔线框,这个金字塔线框就是指示这个摄像机可以看到的区域。

近平面值很大的时候,相机的视图。

在裁剪空间中,这个金字塔是一个规则的立方体。模型-视图-投影矩阵用于将网格顶点转换到这个空间之中。它被称为裁剪空间,因为在这个金字塔之外的所有内容都会被剪裁掉,因为这个金字塔之外的所有内容都是不可见的。

这个信息实际上与阴影没有直接关系,但Unity会在稍后使用这个信息。

渲染到阴影贴图之中

Unity渲染的下一个内容是第一个光源的阴影贴图。稍后,它将渲染第二个光源的阴影贴图。

再一次,整个场景都被渲染了一遍,并且同样也是只有深度信息被存储在纹理贴图之中。然而,这一次是从光源的位置来渲染整个场景。最为特别的是,光源作为摄像机。这意味着深度值告诉我们的是在光线射到某物之前到底行进了有多远。这可以用来确定是否有东西被遮挡住!

会对法线贴图有什么影响么?

阴影贴图记录的是实际几何体的深度信息。法线贴图会给物体添加粗糙表面的错觉,而阴影贴图会忽略它们。因此,阴影不受法线贴图的影响。

因为我们使用的是平行光源,他们的相机是正交的。因此,没有透视投影,并且与光源相机的确切位置没有关系。Unity将定位光源相机的位置,以便光源相机看到正常相机视图中的所有对象。

两张阴影贴图,分别是四个视点下看到的结果。

事实上, Unity不只对每个光源都要渲染整个场景一次。而是对每个光源都要渲染整个场景四次!纹理贴图被分成四个象限,每个象限从不同的角度进行渲染。这是因为我们选择使用四个级联阴影。 如果你要切换到两个级联阴影,场景将对每个光源渲染整个场景两次。如果没有级联阴影的话,它只是对每个光源渲染整个场景一次。当我们观察阴影的质量的时候,我们将看到为什么Unity会这么做。

收集阴影

我们现在有从相机的视角得到的场景的深度信息。我们也有从每个光源的视角得到的场景的深度信息。当然,这些数据存储在不同的裁剪空间之中,但我们知道这些空间的相对位置和方向。所以我们可以从一个空间转换到另一个空间中去。这允许我们从两个视角来比较深度的测量。从概念上讲,我们有两个向量应该在同一点结束。如果他们这样确实在同一点结束的话,从相机和光源的视角都可以看到这一点,所以这一点应该被照亮。如果光源发出的射线在到达这一点之前结束,则意味着光源发出的射线被阻挡,也就意味着该点被遮蔽了。

当现场相机无法看到一个点的时候怎么办呢?

这些点隐藏在更靠近相机的其他点的后面。场景深度纹理只包含那些最靠近相机的点。因此,没有必要来浪费时间去评估这些隐藏的点上。

对于每个光源而言的场景空间的阴影。Unity通过渲染覆盖整个视图的单个四边形来创建这些纹理。它使用Hidden /Internal-ScreenSpaceShadows这个着色器进行这个pass的渲染。从场景相机和光源的深度纹理贴图中进行每个片段的采样、进行比较、并将最终的阴影值渲染为屏幕空间的阴影贴图。被照亮的纹理像素设置为1,阴影贴图的纹理像素设置为0。此时,Unity还可以执行过滤,来创建那些柔和的阴影。

为什么Unity在渲染和收集阴影之间交替?

每个光源需要自己的屏幕空间的阴影贴图。但从光艳的角度渲染的阴影贴图可以重复使用。

对阴影贴图进行采样

最后,Unity完成了对阴影的渲染。现在场景被正常渲染,只是有一个变化。 光源的颜色会乘以存储在阴影贴图中的值。当这个光源应该被遮挡的时候,这会消除这个光源的影响。

每个被渲染的片段都会对阴影贴图进行采样。 最终会被其他物体隐藏的片段会放在后面进行绘制。 因此,这些片段可以最终接收到对它们进行隐藏的对象投射的阴影。当通过帧调试器单步调试的时候,你可以看到这一点。你还可以看到阴影出现在实际投射这些阴影的对象之前。当然,这些

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

码农老K

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

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

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

打赏作者

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

抵扣说明:

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

余额充值