ShaderGraph实战:程序化生成动态岩石行星材质 1. 项目概述从ShaderGraph到一颗岩石星球最近在捣鼓Unity的ShaderGraph想做个不那么“玩具”的玩意儿于是就有了这个制作岩石行星的想法。这玩意儿听起来挺唬人好像得是AAA大作里才有的东西但实际上用ShaderGraph这种可视化工具从零开始搭建一个带地形、大气、甚至云层效果的星球整个过程充满了逻辑拆解的乐趣远没有想象中那么复杂。它本质上是一个基于球体的、通过多层噪声叠加来模拟复杂地表形态并辅以光照和后期效果的着色器项目。如果你对Unity的ShaderGraph有基础了解想深入理解程序化纹理生成、噪声应用以及复杂材质效果的组合逻辑那么这个项目会是一个绝佳的实战案例。它能帮你把那些分散的节点知识串联成一个有明确视觉目标的完整管线。最终你将得到的不是一个静态的贴图球而是一个动态的、可实时调整参数的星球材质。你可以随时改变山脉的高度、海洋的深度、雪线的高度甚至大气的厚度和颜色亲眼看着星球地貌在你指尖演化。这对于独立开发者制作太空游戏背景、科幻场景概念验证或者单纯想做出一个炫酷的个人作品集展示都极具价值。整个过程我们会避开那些深奥的数学公式专注于用ShaderGraph的节点去“画”出我们想要的每一个细节。2. 核心思路与节点蓝图设计制作一个逼真的岩石行星不能靠蛮力堆贴图得有一套清晰的“施工蓝图”。我的核心思路是分层构建逐级细化。想象一下造物主创造星球先有个球体几何基础然后用地壳板块运动弄出大陆和海洋的轮廓基础噪声层接着在陆地上雕刻出山脉、丘陵、峡谷多频次噪声叠加最后撒上植被、积雪、沙石基于高度的颜色/纹理混合。在ShaderGraph里我们完全可以用节点网络来模拟这个过程。整个着色器的输入是模型表面的UV坐标和位置信息输出是最终的颜色。蓝图可以分解为以下几个关键阶段基础地形生成这是核心。我们将使用Simplex Noise在ShaderGraph里通常是Gradient Noise作为基础。单一噪声太平滑我们需要用“分形布朗运动”的思路即用多个不同频率Frequency和振幅Amplitude的噪声叠加。高频噪声决定细节小石头、裂缝低频噪声决定大形貌大陆板块、巨型山脉。通过调节这些层的权重我们可以得到从温和丘陵到险峻山峰的各种地形。高度图与颜色映射上一步我们得到了一个代表地形高度的灰度值0到1。接下来我们需要一个“色卡”根据高度值来分配颜色。例如高度最低的区域比如值0-0.2是深海用深蓝色。稍高一点0.2-0.25是浅海/大陆架用浅蓝色。陆地部分0.25-0.6是岩石和土壤用棕色、灰色渐变。高海拔0.6-0.8可以考虑加入浅灰色模拟裸露岩石。最高处0.8-1.0用白色模拟积雪。 在ShaderGraph中这通常通过一个Color Ramp节点来实现你可以自由拖动色标来定义这个映射关系这是调整星球外观最直观、最有效的手段之一。光照与法线增强没有光照的星球只是一个彩色球。我们需要让地形有立体感。这里有两个关键生成法线贴图ShaderGraph提供了Normal From Height节点可以直接根据我们计算出的高度图哪怕是程序化实时生成的来计算出对应的法线贴图。这能让光照根据地形起伏产生正确的明暗变化瞬间让星球“站”起来。大气散射模拟这是让星球看起来“真实”而非“塑料”的灵魂一笔。简单来说我们可以在星球边缘视角方向与表面法线接近垂直的地方叠加上一层渐变的颜色通常是淡蓝色或根据恒星颜色来定模拟光线穿过大气层的效果。这可以通过计算视角方向与表面法线的点积来实现值越小边缘大气效果越强。云层效果可选但推荐一个静止的星球略显沉闷。加上一层缓慢飘动的云层能极大增加生动感。云层可以作为一个独立的、半透明的层来处理使用另一套噪声通常是更柔和、棉絮状的噪声比如Perlin Noise生成云图并让其根据时间缓慢平移和形变叠加在星球表面颜色之上。注意在开始连接节点前强烈建议先在纸上或绘图软件里画出这个节点流程的草图。明确数据如高度值、颜色、法线的流向这能帮你在大脑中理清逻辑避免在复杂的节点网络中迷失。我的习惯是先搭建主干高度生成-颜色映射再添加分支法线、大气最后做特效云层。每个阶段完成后都预览一下中间结果确保每一步都符合预期。3. 核心节点网络搭建与参数详解现在我们进入ShaderGraph编辑器开始真正的“搭积木”。我会按照蓝图分模块讲解关键节点的连接和参数意义。3.1 构建分形噪声地形生成器这是最核心的模块。我们创建一个Subgraph子图是个好主意将其命名为“FractalTerrainHeight”这样主图会更清晰。输入我们需要一个Vector2类型的UV输入命名为“BaseUV”。通常我们会使用物体的Position节点输出经过转换后得到球面UV但为了简单起见初期可以直接使用UV节点假设模型UV已展开。更高级的做法是使用Triplanar Mapping三平面映射来避免球体极点处的纹理拉伸但入门阶段用UV即可。第一层噪声添加一个Gradient Noise节点。将其UV端口连接到BaseUV。Scale这是频率。设为较低的值如5。这决定了大陆板块的尺度。输出得到一个介于0-1之间的灰度值我们称其为noise_low_freq。第二层噪声细节层再添加一个Gradient Noise节点。UV同样连接BaseUV。Scale设为较高的值如30。这用于生成中等规模的山脉丘陵。我们需要降低它的影响力所以将其输出连接到一个Multiply节点另一个乘数输入设为0.3这个值叫振幅。得到noise_mid_freq。第三层噪声高频细节复制第二个噪声节点。Scale设为更高的值如100。用于模拟岩石表面的微小凹凸。用Multiply节点将其振幅降得更低比如0.1。得到noise_high_freq。叠加使用Add节点将noise_low_freq,noise_mid_freq,noise_high_freq依次相加。此时总和可能超过1我们需要用Clamp节点或Saturate节点将其限制在0-1范围内。这个最终输出就是我们的“高度值”Height。实操心得噪声的Scale和振幅参数需要反复调试。一个技巧是频率翻倍振幅减半。例如如果你的基础层Scale5振幅1那么下一层可以设为Scale10振幅0.5再下一层Scale20振幅0.25。这样叠加出来的分形效果最自然。当然艺术创作不必拘泥多调调滑块观察预览球体的变化找到你觉得最像“行星”的那组参数。3.2 配置颜色渐变与表面纹理得到高度值后我们将其输入到一个Color Ramp节点。创建色标在Color Ramp的属性栏里你会看到一个渐变条。点击下方可以添加色标。根据之前的设计从左0到右1大致添加5个色标。位置0.0深蓝色RGB约 0, 0.1, 0.3。位置0.2浅蓝色RGB约 0.2, 0.5, 0.8。位置0.25沙滩黄/浅褐色RGB约 0.8, 0.7, 0.5。位置0.6岩石灰/深褐色RGB约 0.4, 0.35, 0.3。位置0.8浅灰色RGB约 0.7, 0.7, 0.7。位置1.0白色RGB 1,1,1。平滑过渡色标之间默认是线性过渡这有时会显得生硬。你可以选择色标在属性里将其模式改为“Smooth”让颜色过渡更自然模拟不同地质区域之间的交融。输出Color Ramp节点的输出直接连接到主输出节点的Base Color。此时你应该能看到一个有着基本海洋陆地分布的彩色球体了。3.3 生成动态法线与光照集成仅有颜色是平的我们需要立体感。生成法线在ShaderGraph中搜索Normal From Height节点。将我们之前计算出的“高度值”即分形噪声叠加并Clamp后的那个值连接到其Height输入。Strength这是一个关键参数控制法线强度的“强度”。值越大地形起伏在光照下显得越陡峭。对于行星尺度一个较小的值如0.02到0.1通常就够了太大的话会显得表面过于崎岖像小行星。Sampling保持默认的Central Difference即可。输出法线将Normal From Height节点的输出连接到主输出节点的Normal端口。光照模型在Master Stack主堆栈的Graph Settings中确保你的Material设置为“Lit”。这样它才会响应场景中的灯光。你可以放一个Directional Light平行光来模拟太阳调整光的角度观察星球表面的明暗变化是否随着地形起伏而自然。注意事项Normal From Height节点是根据高度差来计算法线的如果你的高度图变化非常剧烈高频噪声权重过大生成的法线可能会很“碎”导致光照闪烁。如果遇到这种情况可以尝试先对高度值进行一次轻微的Blur模糊处理或者降低高频噪声的振幅再输入给法线生成节点。3.4 实现简易大气散射效果大气效果是点睛之笔能让星球瞬间融入太空环境。计算边缘因子我们需要知道当前像素位于星球的“边缘”还是“正面”。获取View Direction视图方向和Normal我们刚生成的法线节点。使用Dot Product点积节点连接两者。点积结果在1正面法线与视线同向到-1背面之间变化在边缘区域接近0。为了得到一个0中心到1边缘的值我们可以用公式(1 - dot(N, V)) * 0.5。更常用的一个技巧是使用Fresnel Effect节点它本质上就是计算1 - dot(Normal, View Dir)并提供了指数Power参数来控制边缘效果的衰减范围。用Fresnel节点更方便将指数设为3-5值越大边缘光晕越细。创建大气颜色用一个Color节点定义大气颜色通常是淡蓝0.4, 0.6, 1.0或根据恒星颜色设定为橙红色。混合将计算出的边缘因子Fresnel输出作为Alpha使用Lerp线性插值节点在A端口输入星球原本的Base Color在B端口输入大气颜色。这样在星球中心显示原本颜色在边缘逐渐混合进大气色。输出将Lerp的结果连接到最终的Base Color取代之前直接连Color Ramp的线路。4. 云层动画与细节深化技巧一个静态的星球已经不错了但动态的云层能让它“活”过来。4.1 创建动态云层遮罩云噪声使用Simple Noise或Voronoi节点模式设为Cells来生成云朵状图案。Simple Noise的Scale调大如50看起来更绵软Voronoi的Scale调小如10细胞状结构经过处理可以模拟破碎的云层。动画要让云动起来关键是让噪声的UV随时间偏移。创建一个Time节点输出其Sine Time或直接使用Time乘以一个很小的速度系数如0.1得到一个二维的偏移向量Vector2。将这个偏移向量与BaseUV相加再输入给噪声节点的UV。这样噪声图案就会缓慢移动。阈值化原始的噪声是平滑的灰度图。我们需要把它变成“非黑即白”的云朵形状。使用Remap节点或Step节点。例如将噪声输出连接到一个Step节点的Edge端口Step的In端口给一个固定值如0.5。这样噪声值大于0.5的地方输出1白色云小于0.5的地方输出0黑色透明。为了得到更柔和的云边缘可以用Smoothstep代替Step并定义两个阈值如0.4和0.6。多层云只使用一层云会显得单调。可以创建两套不同的噪声不同Scale不同移动速度用Multiply或Screen混合模式叠加在一起这样云层会更丰富有远近层次感。4.2 云层与星球表面的合成混合模式云层应该是半透明且叠加在星球表面的。我们将处理好的云遮罩一个0-1的值作为Alpha连接到主输出节点的Alpha端口如果你的渲染管线支持透明度。同时将云的颜色通常是白色或浅灰色与星球Base Color进行混合。更真实的混合简单的Alpha混合可能让云看起来像贴在表面。一个更高级的技巧是将云遮罩也作为一个轻微的高度扰动加到星球本身的高度计算中在颜色映射之前这样云层所在区域的地形会有微妙的起伏感模拟云层投射的阴影或体积感这需要更复杂的节点设计属于进阶技巧。性能考量云层动画和额外的噪声计算会增加着色器负担。在移动平台或需要大量实例化的场景中可以考虑将云层效果作为一个可开关的选项使用Branch节点或Keyword或者使用更简化的云纹理平铺。4.3 材质参数化与实时调整ShaderGraph最大的优势之一就是可以轻松暴露参数到材质面板。选中任何节点的参数如噪声的Scale、Color Ramp的色标位置、大气颜色的RGB值右键选择“Convert to Property”。这样它就会出现在Blackboard黑板上并可以在材质实例中进行调节。我强烈建议暴露以下参数_TerrainFrequency(Vector1): 控制基础噪声频率调节大陆板块大小。_TerrainAmplitude(Vector1): 控制地形整体起伏强度。_SnowHeight(Vector1): 控制Color Ramp中白色积雪开始的阈值位置。_OceanDepth(Vector1): 控制深海颜色的范围。_AtmosphereColor(Color): 大气层颜色。_AtmospherePower(Vector1): 控制Fresnel效果的指数调节大气边缘的宽度。_CloudSpeed(Vector1): 云层移动速度。_CloudCoverage(Vector1): 通过调节Step/Smoothstep的阈值来控制云的多少。这样你无需修改着色器资源只需在场景中选中材质球拖动这些滑块就能实时看到星球地貌、气候的演变这个过程极具成就感也是进行艺术导演的绝佳方式。5. 常见问题排查与性能优化指南在实际操作中你肯定会遇到一些预期之外的效果。这里记录了几个我踩过的坑和解决方案。5.1 视觉问题排查表问题现象可能原因排查与解决思路星球表面全是条纹或规则图案使用的噪声Scale值过低或UV采样出了问题。检查噪声节点的Scale值尝试提高到10以上。确保UV输入正确如果是用Position尝试先归一化。可以添加一个Tiling And Offset节点在噪声UV前打乱一下平铺。地形看起来像“泡泡”或过于平滑噪声层数不够或高频细节层的振幅太低。增加噪声叠加层数4-5层。提高高频噪声层的振幅但不要超过低频层。检查Normal From Height的Strength是否过小。颜色过渡生硬有明显的色圈Color Ramp的色标之间是线性过渡且色标位置过于集中。将Color Ramp中关键色标如海洋到陆地的混合模式改为“Smooth”。适当拉开色标之间的距离给过渡留出空间。也可以考虑在高度值输入Color Ramp前用Power节点对其进行非线性重塑压缩或拉伸某些高度区间的范围。大气效果在整个球体上都可见太假Fresnel节点的Power值太小或计算方式有误。增大Fresnel的Power值尝试5-8。确保计算Fresnel所用的Normal是经过法线贴图扰动后的最终法线而不是顶点的原始法线。云层移动时出现闪烁或抖动用于云动画的Time节点增量过大或UV偏移计算精度问题。降低云层移动速度系数。确保Time节点输出的是平滑的时间如Sine Time。对于非常慢的移动可以使用Time节点的Time输出乘以一个极小的系数如0.01。极点处纹理严重拉伸或扭曲使用了标准的UV映射而球体模型在极点处的UV是汇聚的。这是球面映射的固有难题。解决方案是使用Triplanar Mapping。为你的噪声计算创建三套基于世界坐标X、Y、Z平面的投影然后根据顶点法线进行混合。ShaderGraph有现成的Triplanar节点搜索并应用它来代替简单的UV输入能极大改善极地区域的质量。5.2 性能优化要点噪声节点数量每一个Gradient/Simple Noise节点都有性能开销。评估你的地形需要多少细节。对于远景星球3层噪声可能就够了对于需要拉近查看的星球可以考虑4-5层。过多的层数如7-8层收益递减但开销线性增长。复杂的颜色渐变Color Ramp节点色标数量过多也会增加计算量。通常5-7个色标足以模拟丰富的地表。避免使用超过10个色标。云层效果动态云层尤其是多层混合的云是性能消耗大户。如果目标是移动端考虑使用静态的云层贴图或者大幅简化云层噪声的复杂度减少一层提高Step阈值减少云量。使用Subgraph将复杂的功能模块如分形地形生成器、大气计算封装成Subgraph。这不仅使主图整洁更重要的是Subgraph在内部会被优化和复用。如果同一套计算逻辑在主图中被多次调用例如地形高度被用于颜色和法线将其放在Subgraph里能避免重复计算。精度选择在Graph Settings中可以考虑将Precision从Full浮点切换到Half半精度。对于颜色、UV偏移等计算半精度通常足够且能提升性能尤其是在移动平台上。但对于高度计算这种需要较高精度的可能仍需保持浮点。5.3 从星球到星系思路拓展完成一个基础星球后你可以以此为模块进行更多拓展多星球系统创建材质变体调整参数地形频率、主色调、大气颜色快速生成风格迥异的岩石行星、冰巨星、沙漠星球。动态地貌通过脚本控制材质参数可以实现“星球 terraforming”的动画效果例如雪线缓缓下降、海洋面积扩大。星环效果使用一个独立的、带有Alpha裁剪的平面着色器配合噪声生成星环的颗粒感将其放置在星球周围。背景星空使用一个简单的CubeMap或由程序化噪声生成的星空球罩作为场景的背景。这个项目的魅力在于它像一套乐高。你掌握了基础模块噪声、颜色映射、光照、混合的搭建方法后就可以自由组合创造出属于你自己的浩瀚星海。每一次参数调整都是对虚拟世界的一次地质改造这种即时的反馈和创造感正是图形编程最吸引人的地方。