深色岩石上一颗多面切割的变石水晶特写,内部呈青绿与品红双色,表面带虹彩反光与裂纹

引言

我叫 Anastasia Gorban,是一名 Unreal Engine 美术,目前担任技术灯光美术(Technical Lighting Artist)。有些读者可能记得我之前在这里发过一篇文章——从那以后我一直在关卡和灯光方向工作,但我也喜欢钻研引擎里更偏技术的那一面,尝试把现实里的美好事物复刻进去。这正是我接下这个项目的原因:我想跳出自己的舒适区,从零搭建一个更复杂的着色器——也就是本文这套写实水晶材质——看看能把它推到什么程度。

水晶世界

这个项目最初是我求职期间的一道测试题——我需要做出一张写实水晶的图,类似变石(alexandrite):内部呈现双色(duochrome),外部带有虹彩(iridescence)。那第一版材质要简单得多,只专注于把变石的观感做准。但当我在三天之内完成测试后,我决定把它发展成一套主材质,能够覆盖不同的水晶变体,并在观感可信的前提下尽可能优化。

我刻意把它做成不透明的——这比半透明更省,而且照样能看起来可信。至于 Substrate(Unreal Engine 5 的材质框架),我此前接触不多,但它已经算不上新功能了,所以我觉得正好借这个机会补上这块短板;Lumen 也一样,我喜欢探索它的能力边界。我的目标是拿到一个接近其他引擎离线渲染效果的实时结果,却完全不做任何渲染。宝石是很自然的题材:它们的光影变化有种让人着迷的东西,而且每一颗都独一无二。

主材质搭建

Substrate 对我来说是片新领域,我就直说了:这套材质严格来说并不一定需要它,因为这里的一切都是用普通的着色器数学运算喂给一个 slab 做出来的,类似的不透明结果在一套标准表面材质上也能复现,而且开销更低。我希望虹彩存在于水晶的外表面,并且与内部彼此独立驱动。虹彩是一层薄膜,会在掠射角下给反射染色;Substrate 让我可以做到这一点而不影响内部的漫反射,而基础着色模型做不到。

整套东西是迭代长出来的,而不是一开始就规划好的。我为测试题做的最早那版没有视差,只有两个按固定距离错开的内部颜色层;没有凹凸 / POM,没有边缘的法线细节,没有内部辉光,也没有把水晶的纹脉与基础色分开的遮罩。它远看还行,但之后每一轮迭代都往物理可信度上加了更多层次,节点图也随之膨胀。

总的来说,最终的主材质结构如下:

  • POM 与凹凸偏移(Bump Offset)——深度阶段
  • 水晶内部(Crystal Insides)——双色内部,每一层都通过调用两个共享函数 MF_InnerLayerUV 与 MF_ColorLerp 构建,并根据视角对高光响应做颜色偏移
  • 外层法线——表面划痕 + 棱边磨损
  • 外层粗糙度——可控的抛光 / 磨砂范围
  • 内部辉光——与内部绑定的自发光层
  • 最终组装——所有模块汇聚到同一个 Substrate Slab BSDF
Unreal 材质图表总览,用彩色分组框标出 Pom、Crystal Insides、Iridescence、Outer Normal 等模块,最终汇聚到 Substrate 输出

下面逐块说明每部分做什么、为什么这么做,以及它暴露了哪些参数。

步骤 1:深度阶段(POM 与凹凸偏移)

在任何颜色被采样之前,材质需要一个能伪造深度的 UV 偏移。我做了两种实现方式:真正的视差遮蔽映射(Parallax Occlusion Mapping,POM,由一张专用高度图驱动,更精确),以及更省成本的凹凸偏移(Bump Offset)节点。一个 Allow POM? 开关决定实际由哪一个驱动后续所有逻辑。这取代了我最初的做法——那时每一层只是一个固定距离值,完全没有基于视角的行为。

深度阶段的两条分支,上方为 Pom 分组,下方为 Bump Offset 分组,各含贴图、参数与开关节点

POM 在物理上更正确,但代价也更高(步进次数、阴影步进等等);而当你把多个内部遮罩层层叠加时,凹凸偏移的可扩展性更好。两条分支共用同一个高度来源和同一个 Top Texture Tiling。所谓高度来源,就是顶层的 Top Diffuse Mask 贴图——它在节点图后面还会继续用于顶层的着色。

让两个模块通过同一个平铺参数使用同一张贴图,意味着对顶层的任何改动都会同步作用到用于视差的高度上。这样伪深度永远精确地落在它所偏移的颜色上,两者不会再各自跑偏。POM Height Ratio(默认 0.1)控制效果的深度:调得越高,错觉越强,但在掠射角下容易出现图像游移(swimming)。

一个重要的细节是:我取 POM 的 Offset Only 输出,把它叠加到平铺后的 UV 上——这个平铺 UV 是在 Crystal Insides 模块里单独构建的。如果直接用完整的 Parallax UVs 输出,会把视差的平铺也算进去,等于把所有东西缩放了两次。

Pom 分支的节点近景,Height Map 与 Pom Height Ratio 接入 Parallaxocclusionmapping 节点,右侧展开其属性列表

关于 POM 有一个专门提醒:要让效果达到最强,顶层应当尽可能贴近表面,也就是说 Top Distance 要设得比平时更低。

凹凸偏移是更省的退路,它的深度由 Bump Depth(默认 0.1)设定。由于它采样的是漫反射贴图本身而不是专用高度图,我先把它转换成亮度(对 RGB 做一个加权点积),再送进一个 Power 节点提高对比,从而加深偏移。

Bump Offset 分支的节点近景,漫反射贴图经 Dot 与 Power 提高对比后接入 Bumpoffset 的 Height 输入
黑色背景上并排两颗多面切割的淡紫色水晶,内部带雾状纹理与光斑

步骤 2:水晶内部

这是承载了大部分错觉的模块。我从两个内部平面开始:上层(Top)和底层(Bottom)。用两层而不是一层是刻意的选择。

Crystal Insides 分组的展开视图,分为 Top Layer 与 Bottom Layer 两个区域,各含内部 Uv 函数与配色节点,最后相加合成

对于变石这样的双色宝石来说,内部必须在不同深度上呈现真正不同的颜色,而单层加颜色插值无法令人信服地伪造这一点——颜色必须落在彼此分离的平面上,这样视角移动时,视差才能让一层从另一层背后露出来。

对更简单的宝石来说,一层可能就足够了;而且当水晶离镜头较远、不需要额外深度时,单层同时也是一条更省成本的路径。

黑色背景上并排两颗多面水晶,左侧为深色青绿与品红交织的变石,右侧为柔和的粉色粉晶

在这条管线里,每层的 UV 由两件事共同构建:一个是距离偏移(这一层被推进内部的深度),另一个是畸变(这种推进有多不规则,用来模仿水晶内部的折射)。这些处理被打包进一个函数 MF_InnerLayerUV,它只有四个输入:Distance、Noise Texture、Texture Tiling 和 Distortion Scale——而不是在节点图里留下三份几乎一模一样的副本。

Crystal Insides 顶层近景,Top Distance、噪声贴图、平铺与畸变参数接入 Mf_Innerlayeruv 函数,再与纹理色相加

1——在切线空间里,由相机向量构建出一个反射向量——这就是整个偏移所依赖的、与视角相关的方向,因此偏移的方向真的像是有什么东西在宝石内部弹跳。

2——畸变:反射向量在平面内(X/Y)的分量会被一个 Noise Texture 参数扰动,并由 Distortion Scale 参数缩放。正是它让效果读起来不规则,更接近光线在真实宝石内部被打散的样子,使每一层都像是一道有机的裂隙,从而模仿宝石内部的折射。每一层都有各自的噪声贴图,因为折射会随该层内部几何结构的不同而以不同方式被打散;把这一参数推得更极端,还能模仿红玉髓或琥珀这类宝石。

黑色背景上并排两颗多面水晶,内部呈橙褐与琥珀色的浑浊纹理

3——把这个被扰动过的方向除以反射向量的深度(Z)分量,这样偏移就能保持稳定,而不会在掠射角下发散。

4——Distance:到这里,结果会按该层的 Distance 值缩放——Top Distance 用 200,Bottom Distance 用 400。Distance 决定了相机旋转时每一层的 UV 移动多远,而两个值之间的落差正是把上层与底层在视觉上分开、而不是塌缩成一团平的决定因素。对于不需要太多深度的宝石,可以把 Distance 值调低,让顶层更贴近表面——这也是依赖 POM 的宝石推荐采用的做法。

5——最后,把缩放后的偏移叠加到该层自己的平铺 UV 上,再乘以该层的 Texture Tiling 输入。这样一来,上层和底层各有一套自己的平铺——比如当水晶内部需要更致密的晶体晶格时就能用上。如前所述,深度阶段引入的 Top Texture Tiling 与缩放顶层漫反射用的是同一个值,所以两者不会跑偏。

Mf_Innerlayeruv 函数内部图表,相机向量变换到切线空间求出反射向量,再受噪波与畸变参数扰动后输出 Uv

每一层 MF_InnerLayerUV 的输出,随后会与步骤 1(POM 与凹凸偏移)得到的 UV 偏移相加,把每层的深度偏移与畸变,和基础的视差阶段合成一个最终 UV。这个 UV 就是在 MF_ColorLerp 函数里用来采样该层 Diffuse Mask 贴图的坐标。

在颜色方面,材质会经过这样一条管线:把漫反射遮罩的红色通道单独取出,喂给 MF_ColorLerp,由它在该通道的取值上,于 MAIn Color 与 Veins Color 之间做插值。

Mf_Colorlerp 函数内部图表,漫反射贴图取样后取红通道作为 Alpha,在主体色与纹理色之间插值

Main Color 越深,半透明的错觉就越强,因为接近黑色的漫反射几乎不贡献什么,读起来就像是空心的体积。在双层设置里,上层最好保持更暗,让底层的颜色透出来。如果整颗石头都想偏半透明,那两层都要做得偏暗。颜色越亮,这一层就越发乳白、越发不透明。

黑色背景上并排两颗深红色的多面水晶,内部有暗红纹理与明显白色反光

在此基础上,我给底层加了第二遍配色,因为真实水晶内部的颜色和纹理并不会随深度保持均匀——色带(zoning)、多色性(pleochroism)和色散(dispersion)都会让宝石随你看向内部的方位而变化颜色。每一层只有一组 Main/Veins 无法完全再现这一点,所以底层多了一块第三色区,配有它自己的 Veins Color 2 和 Diffuse Mask 2,通过一个低指数的菲涅尔项混合进来——正对表面时它接近 0,越靠近轮廓边缘越强。

Main Color 与第一遍保持一致。这里混合主体色是没有意义的,因为真正承载内部颜色的是纹脉,填充色只是纹脉所处的介质。这样就能把额外的颜色集中在宝石中心,并向边缘逐渐淡出,而不是均匀铺满整层。

Crystal Insides 底层近景,多组颜色与遮罩经 Mf_Colorlerp 混合,并用菲涅尔控制第二次配色的叠加
黑色背景上并排两颗多面水晶,呈桃色与蓝紫交织的暖色调

到此为止,两层一直是各自独立的。上层的结果单独作为 Top Layer 输出;把上层与底层相加,则得到 Two Layers 输出。随后由 Allow Second Layer? 开关在两者之间选择。

Crystal Insides 的收尾节点,上层结果经 Power 输出为 Top Layer,两层相加得到 Two Layers,由开关选择

关于贴图本身还有一点要说明:我在这里用到的绝大多数漫反射贴图和遮罩都是用 Nano Banana 生成的。免费的好水晶贴图很难找,而自己生成能得到我刚好需要的纹样,并且完全掌控它们的观感,而不用将就手边能找到的东西。

十二张水晶内部纹理的拼图阵列,涵盖蓝、棕、紫、绿、灰白、粉白等多种色调与裂纹结构

步骤 3:虹彩

我的变石需要一层虹彩外衣,因为这种色彩变化是它的一大特征;但对于本身没有虹彩的宝石,它可以被关掉。像石英、红宝石、蓝宝石这类透明的单色宝石不会出现表面虹彩,而蛋白石、拉长石、月光石和菊石则会有——所以把虹彩做成可选层,能让这套主材质适用于各种水晶类型。

黑色背景上并排两颗多面水晶,左侧带蓝紫色纹理,右侧接近浅灰白色

内部效果跑通后,我为外部单独做了一套由视角驱动的颜色偏移,输入到高光响应(F0)里,从而读起来像一层薄膜涂层。把相机向量变换到切线空间,与像素法线做点积,得到一个标准的视角相关项——正对表面时为 1,掠射角时降到 0——再经过饱和处理并取幂(Iridescence Blend Intensity)来塑造衰减曲线。

把这个结果乘上 Wave Scale(默认 6.28 ≈ 2π),再送进 Sine:薄膜干涉在视角上是周期性的,正弦正好代表这种周期性,把一条平滑的渐变变成重复的色带,Wave Scale 越大色带越多。这个正弦值会被重映射到 [0,1],用作两个 Iridescent Color 参数之间插值的 alpha,再乘上总体的 Iridescence Intensity。

Iridescence 分组的节点近景,视角点乘结果经强度塑形后乘上 Wave Scale 送入 Sine,形成周期性虹彩色带
左侧为两颗带虹彩的材质球预览,右侧为材质实例参数列表,含 Bottom 颜色、虹彩与粗糙度分组

在这套设置里,虹彩由它自己的开关控制:Allow Iridescence?。

步骤 4:外层法线与外层粗糙度

这两块是和虹彩一起加进来的,目的是让表面带上抛光宝石会有的细微划痕与擦痕,从而显得真实。这里混合了两个法线来源:一个是 Outer Normal Texture(由 Outer Normal Intensity 缩放),负责铺满整个表面的细密划痕;另一个是 Edge Normal Texture(配合 Edge Normal Strength),只在刻面相交处起作用,让棱边的高光更锐利。

Outer Normal 分组节点,外层法线贴图与几何边缘遮罩分两路处理后,经角度校正法线混合输出
两张变石水晶的表面特写,内部交织青绿与品红色,表面可见细密划痕与高光

至于边缘遮罩,我复用了 Fab 上一个免费水晶资产自带的 emissive 贴图——它的自发光恰好是沿几何棱边分布的,所以我直接取它的绿色通道当作 Geometry Edge Mask。对这个特定的网格来说,它本质上就是一张免费的曲率图;代价是它被烘焙死在这份资产上,换到任意几何体上就失效了(通用做法是自己烘焙一张真正的曲率 / 空腔贴图,或者用基于法线导数的运行时曲率项)。

Unreal 内容浏览器截图,显示 Crystal 材质实例、Moonstone_07 静态网格与四张配套纹理

粗糙度只用一张 Outer Roughness Texture,被重映射到由美术控制的最小 / 最大粗糙度区间内。

Outer Roughness 分组节点,粗糙度贴图与最小最大参数一起接入 Qmf_Roughnessadjustments 函数

步骤 5:内部辉光

水晶会散射光线、让光穿过自身并在内部折射,因此光看起来是在石头里聚集、弹跳,而不只是从表面反射出去。在不透明材质里并没有真正的透射可以依靠,所以用一个自发光层来替代那部分内部光,制造出光线在水晶内部折射并积聚的错觉。

它被一个菲涅尔项遮罩,用 Edge Shadow 指数来做内部的边缘压暗,从而让辉光集中在水晶内部。这个遮罩会去乘 Glow Color 和 Emissive Intensity。

Glow 分组节点,辉光颜色与强度相乘后经菲涅尔遮罩输出,左侧连接单层与双层的选取逻辑

Allow Inner Glow? 开关决定辉光的来源:

  • 开启时,光来自底层,读起来像是从内部向上折射出来的光;
  • 关闭时,只来自顶层,是一种纯粹的表面辉光。
黑色背景上并排两颗多面水晶,呈蓝紫色调,内部有羽状纹理

需要说明的是:上面的描述是相互独立的——一颗水晶可以带两层却没有内部辉光;但 Allow Inner Glow? 并非如此——内部辉光源自底层,所以启用它本身就要求该层存在。

即便没有勾选 Allow Second Layer?,只要开启内部辉光,实际上就会把第二层拉进结果里,因为没有底层,光就无从谈起。

步骤 6:最终组装

所有内容都汇聚到一个 Substrate Slab BSDF – Simple 节点上:Diffuse Albedo 来自 Crystal Insides 的结果(单层还是双层取决于开关),F0 来自一个固定的 Specular 值或虹彩,Roughness 和 Normal 直接来自各自的模块,Emissive Color 来自 Glow。BSDF 上的其余输入一律保持关闭。材质是不透明的,通过分层漫反射来伪造内部深度,而不是付出真正半透明与次表面散射的代价。

材质图表的最终组装部分,单层/双层、虹彩、粗糙度、法线、辉光与两个开关一起接入 Substrate Slab Bsdf 节点

在当前这套设置里,功能开关严格来说并不算优化手段。它们确实裁掉了一部分指令,但主要目的是让参数集合保持整齐,避免材质实例里一团乱麻。

这样就能去掉某一类水晶根本用不到的控制项,让配置粉晶的美术不必在一堆与之无关的虹彩和内部辉光参数里挣扎。

左侧为粉色与深色两颗水晶球预览,右侧为按八个分组排列的材质实例完整参数列表

性能与优化

这套 Substrate 材质的指令数大约在 445(所有开关关闭)到 517(全部开启)之间,而一个空的 Substrate Slab BSDF 本身就占 326 条指令。我把同样的设置在一套标准的非 Substrate 材质上重建了一遍,结果只有 291–369 条指令。所以如果目标纯粹是以最低成本交付这颗不透明水晶,那标准材质才是正确选择。

唯一失去的是纯粹形态的虹彩,因为标准着色模型不允许你直接给绝缘体的高光反射上色,于是那套在 Substrate 里驱动 F0 的薄膜效果就无处安放。你可以退而求其次,把虹彩色混进 Base Color 来近似,但那样得到的是一层带色偏的表面,而不是反射上真正的彩色高光。调整 Specular、Metallic 之类的参数也有助于达到类似效果。

结语

整个过程大约花了我两周,其中包括为打下基础的那道测试题投入的三天。如果一开始就有清晰的计划,本可以更快,但我是一边做一边发明新的逻辑层。灯光也占了相当一部分时间:为了做出这种观感,我需要好几个能控制光通道和 gobo 效果的光源,还必须修改 Megascans 默认的岩石材质来加上闪烁和位移细分。

另外还有一套小型的 PCG 设置,用来把水晶散布到另一块岩石的表面。最主要的挑战其实就是在层层叠叠的逻辑模块里不迷失,并搞清楚哪一种「伪造」能让我最接近真实。这样的材质是一堆冒充真实光学现象的「作弊」,难的地方在于判断哪一种作弊值得加,以及在它们越堆越多时让每一个都保持清晰可读。

我的建议是:先想清楚你希望材质最终呈现出什么,再大致想一下怎么搭建它,但对意料之外的思路保持开放,因为往往正是那些思路才能真正带你去到你想要的观感。始终把不同的使用场景放在心上,不要死守唯一一种变体。

与此同时,一定要认真把第一个具体版本做扎实,因为它会给你一个可以继续生长的地基。不断测试、质疑你的节点图,在往上叠加之前先确认某个节点真的在做你以为它在做的事,也不要害怕删掉那些没有贡献的部分。

差不多就是这样。非常感谢 80 Level 给我这次梳理这套材质原理的机会——把一个最初只有三个节点的东西拆解透彻,真的很有成就感。如果这类水晶主题的资产你也有兴趣,本站收录了面向

Unity 的 Better Crystals 晶体着色器,以及一整套 水晶矿场场景资产,可以对照参考。祝大家的项目顺利!

Muddy Materials-虚幻4K高精度泥泞道路环境材质素材包

声明:本站所有文章,如无特殊说明或标注,均为本站原创发布。任何个人或组织,在未征得本站同意时,禁止复制、盗用、采集、发布本站内容到任何网站、书籍等各类媒体平台。如若本站内容侵犯了原著者的合法权益,可联系我们进行处理。