做游戏的时候,场景里需要一滩流动的岩浆。第一反应是找素材,翻了一圈要么颜色不对,要么动起来像贴图平移,完全不像高温熔岩那样"自己会沸腾、会裂开、会发光"。后来决定用Shader Graph直接从零搓一个,顺手把整个做法的思路和坑都记了下来。这就是这篇"岩浆加速器"的由来——不是指它跑得有多快,而是说这套Shader能让你在场景里快速产出可调的岩浆材质,渲染上也不拖后腿。
1. 为什么用Shader Graph做岩浆:先对比三条传统路径
1.1 传统方案的上限和局限
先说最省事的做法:直接用序列帧贴图。网上能找到不少岩浆特效序列帧,拖进粒子系统或者UV动画里,效果立竿见影。问题是它的循环感很强,看久了会发现流动方向是重复的,而且分辨率固定,贴到地形大面上会糊。序列帧还有一个隐性成本——内存。一套256x256的32帧序列帧大概占2MB,如果场景里有三四处不同大小的岩浆湖,你要准备好几套不同分辨率的图,内存压力立刻上来。
第二种做法是做UV滚动:给一张岩浆贴图加两个方向的滚动速度,用Unlit或标准材质跑。这个方法非常好上手,但真实感很差,因为整个岩浆面是"整体平移",没有局部的开裂、翻滚和热浪扭曲。它适合做急流或者瀑布,做岩浆湖的话一眼就穿帮。
第三种做法是C#脚本驱动,比如用顶点动画修改网格高度、用RenderTexture实时生成噪声贴图再丢给材质。这个方案自由度极高,但是要写不少代码,而且RenderTexture方案在移动端的开销不容小觑,帧率会被拖下来。
1.2 Shader Graph的优势落在哪里
Shader Graph的切入点是"参数化实时合成"。岩浆看起来复杂,但拆开来看就是几样东西——
- 底层是暗色的岩石外壳,偶尔透出红色裂纹
- 表面是流动的液态熔岩,颜色从亮黄渐变到橙红
- 整体有热浪导致的扭曲感,像隔着一层火苗看地面
- 边缘和裂纹处有自发光,暗部不能死黑,要有微弱的热辐射
传统方案要么不动态、要么太贵,而Shader Graph把这几个要素做成了节点,每个要素都有独立参数。调到岩浆还是熔岩、冷却还是沸腾,只需要拖滑块,不需要换贴图、改代码。对项目来说,这是性价比最高的路径。
如果你是第一次碰Shader Graph,建议先把Unity版本理顺。URP工程推荐用Unity 2021 LTS以上版本,节点类型全,主节点支持PBR和Unlit切换,后面不管是调颜色还是并发光都很顺手。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 先把视觉目标拆清楚:岩浆的"图层"解析
做材质之前千万别急着拉节点。先想清楚视觉上需要哪几层,每一层对应哪些Shader节点。
2.1 流动的裂纹:岩浆的骨架层
岩浆湖表面最抢眼的是发光的裂纹网络。它不是均匀铺满的,而是像树枝一样分叉,随着液体流动不断改变走向。在Shader里模拟这种图案,最合适的是噪声类节点,但不是普通的一张白噪图,而是"低频、连续、有分支感"的噪声。
我用的是噪声(倾斜噪声),调整Scale参数把噪声频率压低,让它呈现出大块的分叉结构。这个节点吃二维UV,返回一个0到1之间的值。数值偏大的区域作为裂纹核心,偏小的区域作为岩石外壳。这一步决定了岩浆的整体结构。
2.2 液态起伏:表面波动与沸腾感
裂纹骨架有了,但它是静态的,还得让液态部分动起来。我的做法是叠加一层UV扭曲:先用噪声节点生成扭曲向量,把它加到原始UV上,再用这个扭曲后的UV去采样裂纹噪声和颜色渐变。
听起来绕,但逻辑是"波形影响波形"——第一层噪声负责产生波纹,第二层用波纹去扰动图案。这样裂纹的边缘会出现蠕动,像被热浪推着往外挤,液态区也会有细微的沸腾感。
2.3 发光衰减:从白热中心到暗色外壳
真实岩浆的颜色分布有规律:温度最高的中心是亮白偏黄,向外过渡到橙色、红色,最外层是暗褐色的冷却壳。在Shader里我直接用渐变采样节点,把前面算出来的噪声值作为UV的X轴,让颜色从亮到暗自然过渡。
自发光通道也要跟颜色配合:裂纹中心给很高的Emission强度,外壳几乎不给。这里有个细节——不要用单色Emission,而是让Emission的颜色跟主颜色保持一致但更亮,这样亮部不会发灰。
如果你观察到岩浆表面有一丝丝的白黄丝状流动,那就是在颜色渐变里把高光区间做得极窄。方法是在渐变采样后再接一个Power节点,让数值更集中,高亮部分自然窄成丝状。
3. 节点图搭建实录:从时间节点到PBR主输出
下面是我在Unity URP工程里搭的完整链路,节点名以Unity 2021.3.16f1c1版本的Shader Graph为准。
3.1 建图与基础参数设置
创建Shader Graph有两种方式:在Project窗口右键 -> Create -> Shader Graph -> URP -> Lit Shader Graph,或者在Assets菜单里走相同流程。选Lit而不是Unlit,因为岩浆需要受场景光照影响,还要能投射自发光。
建完之后在Graph Inspector里做三道关键设置:
- Surface Type改成Transparent,让岩浆边缘能做半透明混合,但如果你做的是地面岩浆湖,建议留Opaque再配合自发光,避免排序问题。
- Alpha Clip保留关闭状态,透明混合用常规模式即可
- Render Face选Front,不需要做双面
然后是纹理设定。我准备了两张32位浮点精度的噪声图,一张是透度噪声,一张是扭曲干扰噪声。如果手边没有合适的图,可以用Shader Graph自带的噪声节点直接生成,省掉外部贴图依赖。
3.2 核心链路:UV滚动 → 噪声 → 扭曲 → 二次采样
这是整张图的"发动机"。我把行为拆成四个节点组:
-
时间驱动组:Unity的Time节点输出时间值,乘上可调的
_Speed参数,然后和UV相加。注意是"相加",不是"相乘"——相加是平移,相乘是缩放,这里要的是平移效果。滚动方向可以通过参数的XY分量控制,比如X给0.2、Y给0.3,材料就朝左下斜向流动。 -
噪声生成组:上面滚动后的UV接入**噪声(倾斜噪声)**节点的UV端口,Scale参数先给6到8。数值越大,裂纹分支越密集,越小越粗。
-
扭曲调制组:另一路UV信号进生成噪声,得到扰动值,减去0.5让范围居中在-0.5到0.5,乘上扭曲强度
_DistortStrength,最后加到最开始的UV上。这一组是让裂纹"蠕动"的关键。 -
二次采样组:把扭曲后的UV喂给另一个噪声节点,用一个不同Scale的值(比如12到15),得到高细节的液态扰动。这样一层是骨架、一层是细节,视觉层次比单层噪声丰富得多。
3.3 颜色映射与自发光通道
二次采样得到的噪声值,我会拆成三路用。
一路直接进采样渐变节点,作为主颜色。渐变颜色我按实际参数从"白黄-橙-红-暗褐"走,位置间距要均匀,不然中间色阶会跳。
第二路做自发光:把噪声值先用Power节点的指数值提高,比如Power设置为3到4,把亮部收窄,再乘一个_EmissionStrength参数,输入Emission端口。这里要注意,默认Emission范围是0到1,但实际要表现"发光感",数值给到1.5到2更明显。在URP的Lit Shader里,Emission值大于1会有Bloom的响应,配合后处理Bloom,岩浆的炽热感立刻拉满。
第三路做粗糙度:岩浆表面不是光滑的,液态部分粗糙度略高,外壳部分粗糙度较低,这样高光会集中在液态裂纹里。方法是用Noise值反向后乘一个数值,接Smoothness。
3.4 完整节点说明表
| 节点组 | 节点类型 | 关键端口 | 参数说明 |
|---|---|---|---|
| 时间驱动 | Time | Time | 输出秒数,用于UV滚动 |
| 流动控制 | Add / Multiply | A:UV B:Time | Speed值为流动方向与速度 |
| 基础噪声 | 噪声(倾斜噪声) | UV,Scale | Scale 6~8,控制裂纹粗细 |
| 扭曲控制 | 噪声(倾斜噪声) | UV,Scale | Scale 3~5,低频扰动 |
| 扭曲叠加 | Add / Multiply | 扰动值-0.5后乘强度 | DistortStrength 0.2~0.5 |
| 细节噪声 | 噪声(倾斜噪声) | UV(Distorted),Scale | Scale 12~15,液态细纹 |
| 颜色映射 | 采样渐变 | UV:噪声值 | 白黄→橙红→暗褐 |
| 自发光 | Power / Multiply | 噪声值Power后乘强度 | EmissionStrength 1.2~2.0 |
这套链路下来,最核心的节点数量在20个以内。别小看这个数量,多一个节点就意味着GPU多一次指令,后面优化的时候就知道精简节点多重要了。
4. 给岩浆装上"加速器":流动提速、交互反馈与性能优化
4.1 流动"加速"的三种手法
一开始我只用了一个方向固定的UV滚动。效果能看,但太规律,像条河流而不是岩浆。后来加了两个技巧:
第一个是速度噪声调制——用一个低频噪声去改变UV滚动的速度而不是方向。具体做法是让噪声值乘以一个偏移量,加到Time乘速度的结果上。这样流动会出现"快慢不均"的情况,有的裂纹走得快、有的卡住再突然释放,很像岩浆的间歇性爆发。
第二个是双层滚动叠加:底层用慢速大尺度噪声,上层用快速小尺度噪声,两层颜色分别映射到不同的渐变区段。光靠一层滚动是不够的,因为岩浆的流动特性是多尺度的——大面积缓慢蠕动,同时表面有小股液体快速游走。
第三个是交互加速:给材质加一个_HeatCenter参数,用Distance节点计算片元离热源中心的位置,距离越近,Time的增速越大。这样当角色或物体靠近岩浆时,岩浆会"响应式"地沸腾加速。这个做法在玩法层面很有用,后面第6章会再展开。
4.2 性能上的"加速度":优化前后的对比数据
我在一台骁龙865的测试机上跑了三版。
第一版是最贪心的全噪声版,所有细节都用多个噪声节点实时计算,片元着色器指令数飙到了210条,GPU占用超过12ms,帧数直接跌倒37FPS。跑起来痛不欲生。
第二版把噪声节点数量从5个砍到3个,并把部分噪声改成从贴图采样,指令数降到98条,GPU占用7.2ms,帧数恢复到54FPS。
第三版又做了两个优化:把主颜色渐变从实时计算改成渐变纹理采样(LUT思路),又把扭曲强度改为可调开关(不需要时可以设置为0,编译器会跳过一部分节点),指令数降到62条,GPU占用4.8ms,稳定跑满60FPS。
| 版本 | 噪声节点数 | 片元指令数 | GPU占用 | 帧率 |
|---|---|---|---|---|
| V1 | 5 | 210 | 12.1ms | 37 |
| V2 | 3 | 98 | 7.2ms | 54 |
| V3 | 3 + LUT | 62 | 4.8ms | 60 |
优化核心就一句话:在Shader Graph里,能用纹理预算的不要用实时计算,能合并的节点不要分开。噪声节点虽然写起来爽,但每个都是GPU上的小循环,反复调用代价很高。
4.3 移动端与PC端的配置差异
如果你只做PC,上面的优化已经够了。但涉及到移动端,有三件事必须确认:
渲染精度:移动端很多GPU对高精度浮点的支持是模拟的,Shader Graph里把Precision设为Half能省不少带宽。不过要注意,噪声计算对精度较敏感,如果出现花瓣状带状伪影,就把关键节点的精度改回Full。
纹理压缩:我用的噪声贴图在移动端要转成ASTC格式,不然带宽消耗无法接受。URP管线的导入设置里可以直接选。
批处理:岩浆材质如果用了很多自定义属性,要注意它可能破坏GPU Instance。把不参与实例化的属性挂到MaterialPropertyBlock里,配合动态合批,场景里多个岩浆材质能用同一次Draw Call。
5. 实操中踩过的坑:颜色泛白、接缝撕裂、噪点闪烁的完整排查
5.1 颜色泛白:为什么明明调了渐变却一片灰
第一版跑出来,岩浆整体泛灰白色,完全没有那种炽热的红黄色。排查半天,问题出在采样渐变的UV输入上。我把一个0到1的噪声值直接连到了采样渐变节点,但渐变节点的UV端口会把这个值当作纹理坐标的一部分来做插值,一旦噪声值超出了0到1范围,渐变就会采样到颜色范围以外的区域,结果偏灰白。
解决办法是在进入采样节点前加一个饱和节点(Saturate),把噪声值强制卡到0到1。之后颜色立刻正常。
5.2 接缝撕裂:UV平铺导致的纹理不连续
在大面积地形上铺岩浆材质时,UV平铺会产生明显的接缝。岩浆的裂纹延伸到接缝处会突然截断,像被刀切过。
这个问题的根因是默认的UV是0到1平铺的,一旦贴图拉伸或者重复,接缝处采样结果不连续。规避方法有两个:
一是将_Scale参数换成世界坐标派生的UV。用Position节点的World端口计算世界坐标,再除以一个系数得到世界UV。这样岩浆的裂纹会在地形上连续延伸,不受物体自身UV影响,接缝自然消失。
二是做好发光的羽化控制。在边缘区域让Emission强度衰减到0,即使有轻微接缝也不容易被发现。
5.3 噪点闪烁:移动端的精度与Mipmap问题
在测试机上,岩浆表面出现了不规则的亮点闪烁,尤其是在噪点密集的区域。这个问题是双重的。
第一重是精度问题:移动端的Half精度在噪声高频区域产生了量化噪声,看起来像闪烁。把关键噪声节点的精度改为Full后,闪烁大幅减少。
第二重是Mipmap问题:纹理过滤到Mip层后,高频细节被过度模糊,但低层仍保留细节,造成前后帧之间的采样不一致。解决办法是在噪声贴图的导入设置里勾选Bypass sRGB Sample(把纹理当作线性数据),并把Mipmap的偏差Bias设置成-1,让采样偏向更清晰的层级。
5.4 调试工具的配合用法
Shader Graph自带的主预览窗口只能看单帧,实时动态就得靠Game视图里的调试工具。
我的习惯是三步走:
- 在Shader Graph节点上右键选择"节点预览",单独调每个节点的输出值,确认那一步是错的
- 打开Frame Debugger,看Draw Call用的Shader变体和关键字是否正常
- 用RenderDoc抓帧,直接看GPU执行时的中间计算结果
正常情况下,排查一个效果问题不需要超过二十分钟。超过这个时间,大概率是方向不对,赶紧停下来重新看节点链路。
6. 把岩浆接进场景:粒子配合、光照交互与玩法拓展
6.1 粒子系统补足"喷溅"与"气泡"
Shader能解决的是"面"上的岩浆,但岩浆湖边缘的喷溅、沸腾的气泡、偶尔爆开的火花,Shader单独做会很吃力。我的做法是Shader管表面大效果,粒子系统管小颗粒。
粒子的材质可以复用同一套Shader,但把Surface Type改成Transparent,并打开Alpha Clip。粒子系统的Start Color和Shader的主颜色保持一个色系,差异不要太大,否则视觉效果会"跳戏"。
6.2 光照与反射探针对岩浆的影响
URP的Lit Shader会受场景主灯和反射探针影响。岩浆的自发光很强,但也不能忽略漫反射。实测下来,主灯方向对着岩浆时,如果Smoothness偏高,会出现一团刺眼的高光。我的做法是把Smoothness控制在0.3到0.45之间,让高光有点模糊的光晕感,更接近热辐射。
反射探针建议在岩浆湖周围放两个:一个放高处捕捉大环境,一个贴近岩浆面捕捉地面反射。不用开实时反射,烘焙一次就够了,主要是让岩浆周围的光照看起来通透。
6.3 从"岩浆"到"熔岩加速器":一个玩法的落地思路
本文标题里"加速器"三个字,除了指Shader本身的性能加速,我还在实际玩法中把它做成了字面意义上的"加速装置"。
做法很简单:在岩浆材质里暴露一个_HeatFlowDir向量参数,和场景里的物体交互——当玩家角色站到岩浆附近的石板上时,脚本沿_HeatFlowDir方向给角色一个冲量;同时把_Speed参数临时调高,让岩浆表现得更剧烈。这个联动不需要写专门的Shader逻辑,只需要代码往材质里传参数。
我实测得出的体验数据是:_Speed从0.15提到0.45,玩家能明显感觉到岩浆"活"了;提到0.8以上,视觉上会觉得液体在喷射,但粒子系统如果没跟上,会有一点点突兀。所以视觉速度的加速要配合粒子发射率的提升,手感才稳。
如果想把"加速器"做成关卡机制,还可以在岩浆湖中间放一个机关点,角色踩上去时材质里的_HeatCenter随着距离衰减,让周围的岩浆亮度提高,同时触发一个快速流动的脉冲波。这个效果只需要在Shader里用Distance节点配合Smoothstep,不需要额外的贴图资源。
最后再分享一个我一直在用的习惯:任何参数在材质面板里的命名,一定要写成_开头、驼峰式命名,比如_EmissionStrength、_DistortStrength。这个看起来是小事,但代码里要通过Material.SetFloat修改参数时,命名不一致能把人逼疯。另外一个实用技巧是给每个参数填好Tooltip说明,鼠标悬停就能看到参数是干什么用的、范围大概多少,项目过一周再回来改材质,不用翻笔记就能上手。
以上就是这套"岩浆加速器"Shader的完整拆解。从视觉拆层到节点搭建,从性能优化到场景落地,整套流程可以直接复用到产线项目。希望你拿去用的时候,少走几个我踩过的坑。
