前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >基础渲染系列(十二)——半透明阴影

基础渲染系列(十二)——半透明阴影

作者头像
放牛的星星
发布2020-07-10 13:38:43
3.2K0
发布2020-07-10 13:38:43
举报
文章被收录于专栏:壹种念头壹种念头

本文重点:

支持cutout阴影 使用抖动 近似半透明名阴影 半透明和cutout阴影之间切换

这是关于渲染的系列教程的第12部分。在上一部分中,我们实现啦渲染半透明表面,但是尚未覆盖它们的阴影。现在,我们来解决这个问题。

本教程是使用Unity 5.5.0f3制作。

(当一个物体Fade的时候,它们的阴影也是)

1 Cutout阴影

当前,我们的透明材质的阴影始终像不透明物体一样被投射,因为这就是我们的着色器所假定的。结果,阴影可能看起来很奇怪,因为实际上你看到是不透明对象的阴影。在定向阴影的情况下,这也可能导致不可见的几何形状阻塞阴影。

(不透明和cutout渲染模式 相同的阴影)

聚光灯或点光源阴影的情况下,也仅获得纯色阴影。

(纯色聚光灯阴影)

1.1 重构My Shadow

为了考虑透明度,我们需要访问阴影投射器着色器通道中的alpha值。这意味着我们需要对反照率纹理进行采样。但是,使用不透明渲染模式时不需要这样做。因此,我们的阴影将需要多个着色器变体。

之前,我们有两个版本的阴影程序。一个版本的立方体阴影贴图是点光源所必需的,而另一个版本是其他光源类型。现在,我们需要混合更多的变体。为了简化操作,我们重写“My Shadow ”包含文件。对所有变体使用插值器,并创建一个顶点和片段程序。

首先,将插值器的定义移出条件块。然后将光向量设置为有条件的。

接下来,编写一个新的顶点程序,其中包含两个不同版本的副本。必须对非立方体代码进行一些调整,以与新的插值器输出配合使用。

对片段程序执行相同的操作。然后注释旧的条件程序。

1.2 剪辑阴影片段

首先要处理cutout阴影。通过丢弃片段来在阴影中切出洞,就像在其他渲染过程中对Cutout渲染模式所做的那样。为此,我们需要材质的色调,反照率纹理和Alpha Cut设置。将它们的变量添加到“My Shadow”的顶部。

当我们使用Cutout渲染模式时,必须对反照率纹理进行采样。实际上,只有在不使用反照率的Alpha值确定平滑度时,才必须这样做。当满足这些条件时,我们需要将UV坐标传递给片段程序。满足这些条件时,将SHADOWS_NEED_UV定义为1。这样,我们可以方便地使用#if SHADOWS_NEED_UV。

将UV坐标添加到顶点输入数据。我们不需要将此作为条件。然后有条件地将UV添加到插值器。

必要时,将UV坐标传递到顶点程序中的插值器中。

将GetAlpha方法从“My Lighting”复制到“My Shadow”。在此,是否对纹理进行采样必须取决于SHADOWS_NEED_UV。因此,请检查该内容,而不是是否定义了_SMOOTHNESS_ALBEDO。下面代码中我标记了差异。

现在,我们可以在片段程序中检索alpha值,并在Cutout渲染模式下使用它进行Clip。

为了使它真正起作用,请将_RENDERING_CUTOUT和_SMOOTHNESS_ALBEDO的着色器功能添加到“My First Lighting Shader”的阴影投射器通道中。

(Cutout 阴影,定向和聚光灯)

1.3 重构 My Lighting

在继续之前,我们还要稍微调整一下“My Lighting”。注意我们如何使用UnityObjectToClipPos转换“My Shadows”中的顶点位置。我们也可以在“My Lighting”中使用此功能,而不用自己执行矩阵乘法。UnityObjectToClipPos函数也执行此乘法,但是使用常数1作为第四个位置坐标,而不是依赖于网格数据。

通过网格提供的数据始终为1,但着色器编译器不知道这一点。结果,肯定是使用常数更为有效。从5.6版开始,当对UNITY_MATRIX_MVP使用未经优化的乘法时,Unity将发出性能警告。

2 局部阴影

为了同时支持“Fade”和“Transprant”渲染模式的阴影,需要将其关键字添加到阴影或阴影投射器通道的着色器功能中。像其他pass一样,渲染功能现在具有四个可能的状态。

这两种模式是半透明的,而不是cutoff。因此,它们的阴影也应该是半透明的。在这种情况下,让我们在“My Shadows”中定义一个方便的SHADOWS_SEMITRANSPARENT宏。

现在需要调整SHADOWS_NEED_UV的定义,因此在半透明阴影的情况下也可以定义它。

2.1 抖动

阴影贴图包含到阻挡光线的表面的距离。光线被阻挡了一定距离,或者没有被阻挡。因此,没有办法指定光被半透明表面部分阻挡。

我们能做的就是将阴影表面的一部分剪掉。这也是我们为cutoff阴影所做的。但是,除了基于阈值进行裁剪外,我们还可以统一裁剪片段。例如,如果一个表面让一半的光通过。总而言之,生成的阴影将显示为完整阴影的一半。

不必总是使用相同的模式。依靠alpha值,我们可以使用带有更多或更少孔的图案。而且,如果我们混合这些模式,则可以创建阴影密度的平滑过渡。基本上,我们仅使用两种状态来近似渐变。这种技术被称为抖动(Dither)。

Unity包含我们可以使用的抖动模式图集。它包含4 x 4像素的16种不同图案。它以完全空的模式开始。每个连续的图案填充一个附加像素,直到填充了七个像素。然后反转,直到所有像素都被填充。

(Unity使用的抖动图案)

2.2 VPOS

要对我们的阴影应用抖动模式,我们需要对其进行采样。不能使用网格的UV坐标,因为它们在阴影空间中不一致。相反,我们需要使用片段的屏幕空间坐标。从光的角度渲染阴影贴图时,这会使图案与阴影贴图对齐。

通过在片段程序中添加带有VPOS语义的参数,可以访问片段的屏幕空间位置。这些坐标不是由顶点程序显式输出的,但是GPU可以使它们可供我们使用。

遗憾的是,VPOS和SV_POSITION语义不能很好地发挥作用。在某些平台上,它们最终映射到相同的位置语义上。因此,我们不能在Interpolators结构中同时使用两者。但幸运的是,我们只需要在顶点程序中使用SV_POSITION,而在片段程序中仅需要VPOS。因此,可以为每个程序使用单独的结构。

首先,将Interpolators重命名为InterpolatorsVertex并相应地调整MyShadowVertexProgram。不要调整MyShadowFragmentProgram。

然后创建一个新的Interpolators结构以用于片段程序。它是其他结构的副本,不同之处在于当需要半透明阴影时,它应包含UNITY_VPOS_TYPE vpos:VPOS而不是float4 position:SV_POSITION。HLSLSupport中定义了UNITY_VPOS_TYPE宏。它通常是float4,但Direct3D 9除外,后者需要将其设置为float2。

我们在片段程序中是否需要位置?

顶点程序需要输出其变换后的位置,但是我们不必在片段程序中访问它。因此,从技术上讲,我们可以将其排除在结构之外。但是,由于该结构的所有其他字段都是有条件的,因此可能会导致一个空结构。编译器不能总是处理这些错误,因此我们将位置保留在其中以防止发生错误。

2.3 抖动中

要访问Unity的抖动模式纹理,请将_DitherMaskLOD变量添加到“My Shadows”中。不同的图案存储在3D纹理的图层中,因此其类型必须是sampler3D而不是sampler2D。

如果需要半透明阴影,请在MyShadowFragmentProgram中对此纹理进行采样。这是通过tex3D函数完成的,该函数需要3D坐标。第三个坐标应在0–1范围内,并用于选择3D切片。因为有16个图案,所以第一个图案的Z坐标为0,第二个图案的坐标为0.0625,第三个为0.128,依此类推。让我们从始终选择第二种模式开始。

当应丢弃片段时,抖动纹理的Alpha通道为零。因此,从中减去一个较小的值,然后使用该值进行裁剪。

要实际看到它,我们必须对其进行缩放。为了使外观更好看,请将其放大100倍,方法是将位置乘以0.01。聚光灯下的阴影使我们可以很好地对其进行观察。

(fade模式下 统一的抖动)

可以通过以0.0625为步长增加Z坐标来检查所有16种抖动模式。阴影被完全裁剪为0,并在0.9375处完全渲染。

(改变抖动模式)

2.4 近似半透明

代替使用统一的图案,我们必须基于表面的alpha值来选择抖动图案。当完全不透明度达到0.9375时,将alpha值乘以该因子,然后将其用作Z坐标。

(基于alpha的抖动)

现在,抖动根据表面的不透明度而变化。为了使其看起来更像是真实的阴影,我们需要缩小图案大小。Unity使用系数0.25,因此我们也将使用它。

(缩放抖动)

这看起来好多了,但并不完美。抖动的明显程度取决于阴影图的分辨率。分辨率越高,图案越小且不那么明显。

抖动对于柔和的定向阴影效果更好。屏幕空间过滤将抖动的片段弄脏到不再明显的程度。结果是逼近实际的半透明阴影。

(带有抖动的方向阴影 硬VS软)

不幸的是,抖动在视觉上不稳定。当物体移动时,你会获得非常明显的影子游泳。不仅沿着边缘,而且跨越整个阴影!

(抖动 游泳)

如何在半透明的表面上接收阴影呢?

Unity不支持在半透明表面上投射阴影。因此,使用“Fade”或“Transparent”渲染模式的材质将不会接收阴影。但cutoff效果很好。

3 优化半透明阴影

考虑到半透明阴影的局限性,你可以不使用它们。通过其“Mesh Renderer”组件的“Cast Shadows”模式完全禁用对象的阴影。但是,对于半透明的对象来说,cutout阴影可能效果很好。例如,当其表面的很大一部分完全不透明时。因此,让我们可以在两种类型的阴影之间进行选择。

为了支持此选择,请将阴影功能添加到新的关键字_SEMITRANSPARENT_SHADOWS的阴影投射过程中。

在“My Shadows”中,仅当设置了_SEMITRANSPARENT_SHADOWS着色器关键字时,才定义SHADOWS_SEMITRANSPARENT。

如果未启用新的着色器功能,那么我们应该后退至cutout阴影。我们可以通过手动定义_RENDERING_CUTOUT来实现。

因为尚未启用新的着色器功能,所以现在在使用“Fade”或“Transparent”渲染模式时会得到cutout阴影。

(带有cutout阴影的 Fade模式)

3.1 半透明 可切换化

要再次启用半透明阴影,我们必须为其添加一个选项到我们的自定义着色器UI中。因此,向MyLightingShaderGUI中添加DoSemitransparentShadows方法。

仅在使用Fade或Transparent模式时才需要显示此选项。我们知道在DoRenderingMode内部使用哪种模式。因此,如果需要,请在此方法的末尾调用DoSemitransparentShadows。

由于这是一个二进制选择,因此我们可以使用切换按钮来表示它。因为标签Semitransparent Shadows的宽度比Unity默认检查器窗口的宽度宽,所以我将其缩写。为了清楚起见,我给它提供了一个简短的工具提示。

(半透明阴影复选框)

与其他关键字一样,检查用户是否进行更改并相应地设置关键字。

3.2 为阴影显示 Alpha Cutoff

使用Cutoff阴影时,我们可能想更改Alpha抠像阈值。当前,它仅在使用Cutout渲染模式时显示在我们的UI中。但是,当不使用半透明阴影时,现在还必须在“Fade”和“Transparent”模式下可以访问它。通过在适当的时候在DoSemitransparentShadows中将shouldShowAlphaCutoff设置为true来支持这一点。

(当需要的时候Alpha cutoff会出现)

下一章:延迟着色。

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2020-05-17,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 壹种念头 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 1 Cutout阴影
    • 1.1 重构My Shadow
      • 1.2 剪辑阴影片段
        • 1.3 重构 My Lighting
        • 2 局部阴影
          • 2.1 抖动
            • 2.2 VPOS
              • 2.3 抖动中
                • 2.4 近似半透明
                • 3 优化半透明阴影
                  • 3.1 半透明 可切换化
                    • 3.2 为阴影显示 Alpha Cutoff
                    相关产品与服务
                    图像处理
                    图像处理基于腾讯云深度学习等人工智能技术,提供综合性的图像优化处理服务,包括图像质量评估、图像清晰度增强、图像智能裁剪等。
                    领券
                    问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档