堪比科幻大片的地形渲染效果:Mapbox进阶教程之创建地形遮罩着色器

  • A+
所属分类:地图服务
广告也精彩

Creating a terrain mask shader哇塞!这个地形渲染效果真是比科幻大片里还好看~上面这么酷炫的地形效果,怎么实现呢?这位开发者 Baran Kahyaoglu 就分享了他的实践经历!一起看看他是怎么实现的吧~堪比科幻大片的地形渲染效果:Mapbox进阶教程之创建地形遮罩着色器堪比科幻大片的地形渲染效果:Mapbox进阶教程之创建地形遮罩着色器在这篇博文中,我们将讨论如何使用 ShaderGraph 创建一个遮罩着色器(MaskShader),并将其应用在我们的 Mapbox Unity SDK 地形上。温馨提示:要使用ShaderGraph 来完成这个任务,你需要提前了解在如何在 URP 项目中使用 Mapbox Unity SDK(ShaderGraph不支持标准的渲染管道,只适用于URP/HDRP)。我在我的科幻地图项目中使用了类似的效果来创建一个六边形的地图,之后有很多人来询问我怎么实现。科幻地图项目和地图遮罩着色器正如你所猜测的那样,当你想在另一个对象上放置一张地图时,比如桌子或 AR表面,这是一个常见的情况。有很多不同的方法可以做到这一点,但在这篇文章中,我们将使用遮罩纹理和 ShaderGraph 来实现基于着色器的遮罩。请问,什么是遮罩 (Musk)?堪比科幻大片的地形渲染效果:Mapbox进阶教程之创建地形遮罩着色器根据google的说法,"纹理遮罩 (Texture Musk)是一种灰度纹理,用于限制底层元素的效果"。或者换句话说,它是一种纹理,它决定了底层事物在哪里可见或不可见。我想,它的细节取决于实现,但它们都或多或少是相同的,无论是 photoshop 遮罩、3d 着色器、UI 遮罩等。那遮罩和遮罩着色器如何工作?堪比科幻大片的地形渲染效果:Mapbox进阶教程之创建地形遮罩着色器遮罩纹理像素的亮度将被用作对应的基础纹理像素的不透明度,在我们的例子中,基础 "效果 "将是 Mapbox Unity SDK 中的卫星图像网格,遮罩将是黑白色的六边形 jpg ,"+"部分将是我们的着色器, 用来告诉GPU 是否绘制卫星图像像素。让我们开始吧 !前两个元素,底图和遮罩应该是相当容易的。在这篇文章中,我们可以使用 Mapbox Unity SDK 中的 ZoomableMap 演示场景。这是一个自上而下的地图,我们可以平移和缩放,因此它正是我们需要的。堪比科幻大片的地形渲染效果:Mapbox进阶教程之创建地形遮罩着色器对于遮罩,我们将使用一个非常基本的六边形图像,如上图所示。你可以在这里找到这个图像。但可以肯定的是,着色器(shader) 是所有魔法的所在。现在我们将深入研究这个问题。我将快速地介绍一下基础知识。首先,我们已经决定使用 URP 和 ShaderGraph 来做这件事,所以让我们确保这两个包已经加载,并且项目设置已经设置为正确使用 URP。然后让我们在项目窗口的上下文菜单中选择 Create Shader PBR Graph 来创建一个新的着色器资产。并新建一个材质来使用该着色器。Shader 目前都是空的,但不要紧,我们一会儿就会说到。最后,选择场景中的 Map 对象,在Abstract Map script,General Other settings 下找到 Tile Material 字段,在那里设置你的新材质。顾名思义,这将告诉 Mapbox Unity SDK 使用我们的新材质来制作地形网格。地图设置现在应该是这样的如果你现在运行场景,你应该得到一个白色/灰色的视图。不过这是预料之中的,因为我们的着色器都是空的,它只是在网格上渲染默认颜色。让颜色出现吧!所以让我们从简单的东西开始。让我们创建一个新的 Texture 字段;你可以把它命名为任何你想要的名字,但这有一点很重要;Reference 必须是 _BaseMap 。Mapbox Unity SDK 将使用这个名字将纹理传递给 GPU(详情请点击查看文章 “使用 Mapbox Unity SDK 与 URP 结合使用”),所以如果它在代码和 shader 中不匹配,它将无法工作。然后对该纹理进行采样,并将其用于 Albedo 通道。我还将平滑度降为零,因为我讨厌平滑度(所以只是视觉偏好)。堪比科幻大片的地形渲染效果:Mapbox进阶教程之创建地形遮罩着色器遮罩着色器图形颜色通道——现在你应该在网格上应用了图像。颜色现在可以工作了,让我们开始尝试同样的 alpha 。创建一个新的 Texture 字段,Reference 不是很重要,但我们以后会需要记住它。取样并将其用于 Alpha,如下图所示。我们的材质 Surface 默认设置为Opaque,让我们把它改为透明。最后,在你的项目中找到材质,将遮罩纹理设置为黑白六边形图像。又是一张疯狂的截图,我想一次把所有的东西都显示出来......嗯,我们现在的地图上有多个六边形。但是为什么呢?当我们像这样取样遮罩纹理时,我指的是使用默认的 UV 值来取样节点的 "那个",我们实际上是在 UV 空间中进行取样。请记住,地图包含了一堆正方形的瓷砖,每个瓷砖都有一个独立的网格、纹理,当然还有从(0,0)到(1,1)的 UV 空间。遮罩和紫外线空间:在左边你可以看到各个瓷砖的UV坐标,在右边是我们使用这些坐标应用遮罩时的情况。我们只是想要一个六边形在一个特定的地方,在世界空间的一个特定的地方。所以我们应该以某种方式将当前像素的位置(相对于遮罩位置)映射到 UV 坐标上。或者如果我们反过来想,在我们想放置遮罩的地方会有一个 UV 空间/平方(0-1),我们需要将当前像素的位置转换到这个空间。相对顶点位置:我们想要的一个十六进制掩码。我们需要检查所有像素的位置。虽然图中只显示了6个像素,但着色器会对地形网格上的所有像素起作用。红色是不可见的,蓝色是可见的。实际这个操作上并不难;首先,我们从像素世界空间位置减去蒙版中心位置,以找到相对位置(上图中的线)。然后我们将矢量除以蒙版比例,因为该矩形可以是任意大小,对吗?如果足够大,它也将包含那些红点。这将为我们提供 UV 空间坐标。最后,我们将旋转此 UV 向量,因为您的 table/environment/surface 可能并不总是与遮罩纹理对齐。想象有一个用于矩形桌子的矩形遮罩,但是它在场景中旋转。堪比科幻大片的地形渲染效果:Mapbox进阶教程之创建地形遮罩着色器上面是整个计算过程,但我也会在下面一一讲解。在使用图形编辑器完成所有这些时间之后,我仍然不确定我是否喜欢它们。但我必须说,它们具有极高的可读性(直到某些时候)。第一部分是定位。我们从像素位置减去蒙版位置,然后将其除以蒙版比例。现在,遮罩区域内的任何值都应在[-0.5,0.5]范围内。堪比科幻大片的地形渲染效果:Mapbox进阶教程之创建地形遮罩着色器我们将此位置转换为 vector2 并放下Y轴。我们在 Z 平面上工作,因此 Y 轴在这种情况下是无用的。然后,我们从[-0.5,0.5]重新映射为[0,1],将其移至 UV 范围。在这里使用 remap 有点太过了,可以对其进行优化。但是为了简化起见,我更喜欢这么做。堪比科幻大片的地形渲染效果:Mapbox进阶教程之创建地形遮罩着色器我们将其绕[0.5,0.5]旋转,因为这是 UV 空间和蒙版纹理的中心。在此之前,还要从 ccw 转换为 cw ,然后将度数转换为弧度。堪比科幻大片的地形渲染效果:Mapbox进阶教程之创建地形遮罩着色器最后,我们使值饱和(clamp(0,1))。这将启用/禁用纹理重复。但是,只有将纹理资源也设置为 Repeatmode 时,它才起作用。否则,无论此选项是什么,它都不会重复。堪比科幻大片的地形渲染效果:Mapbox进阶教程之创建地形遮罩着色器现在它在技术上应该可以工作了,但是还有一个小问题。我们在材质设置中设置了参数模板位置,模板比例等,但是当我们运行项目时,Mapbox SDK 会克隆该材质并为每个图块创建该材质的实例,因为它们都必须具有不同的颜色图(卫星图像)。现在它们都有不同的材质,如果您想调整一些数字并在运行时对其进行样式设置,则必须一一选择所有数字,然后分别更改值。具有不同比例值的图块,有点像图中这样的风格所以它现在应该可以正常工作,并且这完全是可选的,但是我真的很想要一个可以调整所有数字的脚本。全局着色器变量为此,我们将使用全局着色器变量。它们是着色器的静态全局变量,您可以在任何地方进行设置,并且它们在实体的所有实例之间共享。这正是我们想要的蒙版位置,比例等字段的内容。堪比科幻大片的地形渲染效果:Mapbox进阶教程之创建地形遮罩着色器我们需要做的第一件事是禁用检查器对这些参数的支持。检查器会覆盖代码设置的值,因此,如果它们 exposed 在检查器中,则我们的代码将根本无法工作。您可以在着色器图形黑板上找到它。对于每个参数,应该有一个 Exposed 复选框。如果您取消选中该选项,则该参数将不会出现在检查器中。然后,我们需要一个C#脚本来获取和设置值;public class ShaderController : MonoBehaviour { public Texture2D MaskTexture; public float MaskScale; public float MaskRotation; public bool RepeatMask; private void Update() { SetValues(); } private void OnValidate() { SetValues(); } public void SetValues() { Shader.SetGlobalTexture("_MaskTexture", MaskTexture); Shader.SetGlobalFloat("_MaskScale", MaskScale); Shader.SetGlobalFloat("_MaskRotation", MaskRotation); Shader.SetGlobalVector("_ReferencePosition", transform.position); Shader.SetGlobalInt("_RepeatMask", RepeatMask ? 1 : 0); } private void OnDrawGizmos() { var v1 = transform.position + Quaternion.Euler(0, MaskRotation, 0) * new Vector3(- MaskScale/2, 0, - MaskScale/2); var v2 = transform.position + Quaternion.Euler(0, MaskRotation, 0) * new Vector3(- MaskScale/2, 0, + MaskScale/2); var v3 = transform.position + Quaternion.Euler(0, MaskRotation, 0) * new Vector3(+ MaskScale/2, 0, + MaskScale/2); var v4 = transform.position + Quaternion.Euler(0, MaskRotation, 0) * new Vector3(+ MaskScale/2, 0, - MaskScale/2); Gizmos.DrawLine(v1, v2); Gizmos.DrawLine(v2, v3); Gizmos.DrawLine(v3, v4); Gizmos.DrawLine(v4, v1); } }它应该简单明了。OnValidate 在大多数情况下就足够了,但是此脚本使用对象自身的位置作为遮罩位置,OnValidate 并且不会在位置更改时触发,因此我也添加了 Updatecall。如我之前所说,这是完全可选的,但是如果您喜欢像我一样尝试使用各种值,则可能需要它。地形蒙版着色器我认为这就是我们的遮罩着色器!这几乎是我在所有项目中使用的,包括我一开始提到的科学幻想曲地图。在下面发布该图的最终完整视图;堪比科幻大片的地形渲染效果:Mapbox进阶教程之创建地形遮罩着色器Karan 的技术分析对你有什么启发吗?如果你也在使用 Mapbox Unity SDK,不如联系 Max (mapbox_max)哦,拉你进专门的 Mapbox Unity 技术群与大家畅所欲言~本文翻译自Creating a terrain mask shader, 来自作者 Baran 个人博客(网址链接)。欢迎大家戳下面的 Mapbox 熊猫,进入 Mapbox.cn 留下你的问题、建议、产品想法等,我们会在 1- 3 个工作日内回复你哦!
点击查看详情

  • 我的微信
  • 这是我的微信扫一扫
  • weinxin
  • 我的微信公众号
  • 我的微信公众号扫一扫
  • weinxin
广告也精彩
avatar

发表评论

您必须登录才能发表评论!