【节点】[GradientNoise节点]原理解析与实际应用

【Unity Shader Graph 使用与特效实现】专栏-直达

柏林噪声(Perlin Noise)的起源与核心价值

柏林噪声由肯·柏林(Ken Perlin)于1980年发明,属于梯度噪声的一种,在计算机图形学和程序性内容生成中得到了广泛应用。其核心价值在于通过数学算法直接生成纹理信息,相较于传统位图纹理具有以下优势:

  • 无限分辨率:能够动态适应任意显示精度,在4K甚至8K屏幕上依然保持清晰细节,有效避免传统纹理在高分辨率下的模糊问题。
  • 参数化控制:通过调整频率、振幅、偏移等参数,实时改变噪声形态,实现动态效果调整,无需重新生成纹理。
  • 内存零占用:无需存储纹理文件,特别适合移动端等内存受限设备,有助于减少内存消耗并提升应用性能。
  • 动态演化:支持时间维度动画,如云层流动、水面波动等,为场景增添生动感,增强用户体验。

梯度噪声的生成原理

梯度噪声的生成方式基于对伪随机值网格进行采样,并使用平滑插值函数(例如三次或五次插值)对这些值进行插值。其关键思想是创建平滑变化的值,以模拟自然现象的外观,比如地形的起伏或自然纹理(如大理石或木材)中的不规则图案。

算法实现步骤

  • 初始化相关数据:包括排列表(Permutation Table)和梯度表(Gradient Table)等,其中梯度表中元素绝对值的最大值一般为1。排列表用于确定伪随机梯度,梯度表则存储了不同方向的梯度向量。

  • 建立采样空间和参考点:对于一维柏林噪声,采样空间为一个一维坐标轴,轴上整数坐标位置均有一个点;二维柏林噪声的采样空间为一个二维坐标系,横纵坐标为整数的地方均有参考点;三维柏林噪声同理,采样空间为一个三维坐标系,横纵坐标为整数的地方均有参考点。

  • 计算伪随机梯度:根据步骤一的两个表格,对每一个整数坐标点计算其伪随机梯度,该梯度的维数与采样空间维数相同。例如,在二维柏林噪声中,每个整数坐标点都有一个二维的伪随机梯度向量。

  • 插值计算:对于噪声图上的像素,找到它在晶格上对应的点,并求出其参考点的坐标。在一维情况下,参考点为两侧最近的整数点;二维情况下,参考点为组成包围该点的单位正方体的四个点;三维情况下,参考点为组成包围该点的单位立方体的八个点。然后,根据这些参考点的值和插值函数,计算出该像素的值。

  • GradientNoise.cs

    csharp 复制代码
    using System;
    using UnityEngine;
    
    public class GradientNoise
    {
        // 梯度方向查找表
        private static readonly Vector2[] Grad2 = new Vector2[]
        {
            new Vector2(1, 1), new Vector2(-1, 1), new Vector2(1, -1), new Vector2(-1, -1),
            new Vector2(1, 0), new Vector2(-1, 0), new Vector2(0, 1), new Vector2(0, -1)
        };
    
        // 排列查找表
        private static readonly int[] Perm = new int[]
        {
            151,160,137,91,90,15,131,13,201,95,96,53,194,233,7,225,140,36,103,30,69,142,
            8,99,37,240,21,10,23,190,6,148,247,120,234,75,0,26,
            197,62,94,252,219,203,117,35,11,32,57,177,33,88,237,149,56,87,174,20,125,136,
            171,168,68,175,74,165,71,134,139,48,27,166,77,146,158,
            231,83,111,229,122,60,211,133,230,220,105,92,41,55,46,245,40,244,
            102,143,54,65,25,63,161,1,216,80,73,209,76,132,187,208,
            89,18,169,200,196,135,130,116,188,159,86,164,100,109,198,173,
            186,3,64,52,217,226,250,124,123,5,202,38,147,118,126,255,
            82,85,212,207,206,59,227,47,16,58,17,182,189,28,42,
            223,183,170,213,119,248,152,2,44,154,163,70,221,153,101,
            155,167,43,172,9,129,22,39,253,19,98,108,110,79,113,224,232,178,185,
            112,104,218,246,97,228,251,34,242,193,238,210,144,12,191,179,
            162,241,81,51,145,235,249,14,239,107,49,192,214,31,181,199,106,
            157,184,84,204,176,115,121,50,45,127,4,150,254,138,236,205,
            93,222,114,67,29,24,72,243,141,128,195,78,66,215,61,156,180
        };
    
        /// <summary>
        /// 生成2D梯度噪声
        /// </summary>
        /// <param name="uv">输入坐标</param>
        /// <param name="scale">缩放参数</param>
        /// <param name="angleOffset">角度偏移</param>
        /// <returns>噪声值,范围[-1,1]</returns>
        public static float GenerateNoise(Vector2 uv, float scale = 1f, float angleOffset = 0f)
        {
            // 应用缩放
            Vector2 scaledUV = uv * scale;
    
            // 计算整数和小数部分
            int ix = Mathf.FloorToInt(scaledUV.x);
            int iy = Mathf.FloorToInt(scaledUV.y);
            float fx = scaledUV.x - ix;
            float fy = scaledUV.y - iy;
    
            // 应用角度偏移
            if (angleOffset != 0f)
            {
                float angle = angleOffset * Mathf.PI / 180f;
                float cos = Mathf.Cos(angle);
                float sin = Mathf.Sin(angle);
                fx = fx * cos - fy * sin;
                fy = fx * sin + fy * cos;
            }
    
            // 计算四个角点的梯度贡献
            float n00 = DotGridGradient(ix, iy, fx, fy);
            float n10 = DotGridGradient(ix + 1, iy, fx - 1, fy);
            float n01 = DotGridGradient(ix, iy + 1, fx, fy - 1);
            float n11 = DotGridGradient(ix + 1, iy + 1, fx - 1, fy - 1);
    
            // 使用平滑插值函数
            float u = Fade(fx);
            float v = Fade(fy);
    
            // 双线性插值
            float nx0 = Mathf.Lerp(n00, n10, u);
            float nx1 = Mathf.Lerp(n01, n11, u);
            float result = Mathf.Lerp(nx0, nx1, v);
    
            return Mathf.Clamp(result, -1f, 1f);
        }
    
        /// <summary>
        /// 计算网格点的梯度点积
        /// </summary>
        private static float DotGridGradient(int ix, int iy, float x, float y)
        {
            // 获取伪随机梯度
            int index = Perm[(ix + Perm[iy & 255]) & 255];
            Vector2 gradient = Grad2[index % Grad2.Length];
    
            return x * gradient.x + y * gradient.y;
        }
    
        /// <summary>
        /// 5次平滑插值函数
        /// </summary>
        private static float Fade(float t)
        {
            return t * t * t * (t * (t * 6 - 15) + 10);
        }
    
        /// <summary>
        /// 生成带时间动画的噪声
        /// </summary>
        public static float GenerateAnimatedNoise(Vector2 uv, float scale = 1f, float speed = 1f)
        {
            float timeOffset = Time.time * speed;
            return GenerateNoise(uv + new Vector2(timeOffset, timeOffset), scale);
        }
    
        /// <summary>
        /// 生成分形噪声(多个八度叠加)
        /// </summary>
        public static float GenerateFractalNoise(Vector2 uv, float scale = 1f, int octaves = 4, float persistence = 0.5f)
        {
            float total = 0f;
            float frequency = scale;
            float amplitude = 1f;
            float maxValue = 0f;
    
            for (int i = 0; i < octaves; i++)
            {
                total += GenerateNoise(uv, frequency) * amplitude;
                    maxValue += amplitude;
                frequency *= 2f;
                amplitude *= persistence;
            }
    
            return total / maxValue;
        }
    }

ShaderGraph中的GradientNoise节点

在Unity的ShaderGraph中,GradientNoise节点是用于生成柏林噪声的重要组成部分。

节点功能概述

GradientNoise节点允许开发者在Shader内部创建自定义渐变,并通过采样渐变节点(Sample Gradient Node)实现基于时间或参数的渐变颜色提取。该节点的核心价值在于:

  • 灵活颜色控制:通过可视化编辑定义多色标渐变,替代硬编码颜色值,使颜色过渡更加自然和多样化。
  • 高效复用:定义一次渐变后可在Shader中多次采样,减少重复配置,提高开发效率。
  • 动态效果支持:配合时间参数实现颜色过渡动画,无需外部脚本驱动,使动画效果更加流畅和自然。

端口与参数详解

  • Scale:控制噪点图的大小,Scale越大,噪点图越密集。通过调整Scale参数,可以改变噪声的细节程度,从而影响最终效果的外观。
  • Angle Offset:用于控制伪随机数偏移这些点,可以生成细胞簇。通过调整Angle Offset参数,可以改变噪声的形态,创造出不同的图案效果。
  • Cell Density:控制这些像元的比例以及由此产生的噪声。通过调整Cell Density参数,可以改变噪声的密度和分布,从而影响最终效果的细节和复杂度。

实际应用示例:溶解效果

我们将使用GradientNoise节点来创建溶解效果,它在制作流动性强、连续变化的效果(比如熔岩灯、墨滴等)方面表现出色。溶解效果常用于角色消失、物体破碎等场景,为游戏增添动态和戏剧性。

实现步骤

  • 创建PBRGraph:在Project面板右键 --> Create --> Shader --> PBRGraph,并重命名(如RPAShaderGraph)。PBRGraph是Unity中用于创建基于物理渲染(PBR)的Shader的图形化工具。
  • 添加GradientNoise节点:在"PBRGraph"面板空白处右键 --> 选择"Create Note" --> 创建GradientNoise节点。GradientNoise节点将用于生成溶解效果的噪声图。
  • 连接时间节点:创建"Time"节点,并将其连接到GradientNoise节点的Angle Offset参数上,以实现动态效果。通过时间节点的变化,可以控制噪声的形态变化,使溶解效果具有时间上的动态性。
  • 调整Scale参数:根据需要调整Scale参数,以控制噪点图的大小和密度。Scale参数越大,噪点图越密集,溶解效果越明显;Scale参数越小,噪点图越稀疏,溶解效果越柔和。
  • 添加颜色节点:创建"Color"颜色节点和"Multiply"乘法节点,将GradientNoise节点的输出和Color颜色节点的输出连接到Multiply乘法节点的输入上,以修改颜色。通过颜色节点的设置,可以改变溶解效果的颜色,使其与场景中的其他元素相协调。
  • 添加圆形遮罩:添加"Ellipse"圆形节点,将其输入节点的宽高调整为0.8,然后将输出节点连接到"PBR Master"主节点的Alpha输入上,以实现圆形遮罩效果。通过圆形遮罩的设置,可以限制溶解效果的范围,使其只在特定的区域内发生。
  • 设置表面属性:在Master主节点的"设置"按钮中,将"Surface"属性设置为"Transparent",以得到被遮罩的效果。通过表面属性的设置,可以使物体在溶解过程中逐渐变得透明,增强溶解效果的视觉表现。

GradientNoise节点的应用案例

地形高度图生成

在3D地形生成中,GradientNoise节点可以创建自然起伏的地形,模拟山脉、山谷等地形特征‌。通过调整Scale参数,可以控制地形细节的层次,实现从宏观地貌到微观细节的丰富变化。

云层与雾效果

对于2D/3D体积效果,GradientNoise节点能够模拟天空中的云层或场景中的雾‌。通过噪声控制透明度,可以创建出逼真的体积云和动态雾效,为场景增添氛围感。

水面波纹扰动

在动态水面效果中,GradientNoise节点可以让水面法线随时间扰动,模拟波光粼粼的效果‌。这种技术常用于创建湖泊、海洋等动态水体,增强场景的真实感。

材质表面细节

为金属、岩石等材质添加细微的表面起伏,可以增强材质的真实感‌。GradientNoise节点能够为PBR材质添加程序化的粗糙度或法线贴图,使材质表面看起来更加自然。

火焰效果实现

在制作火焰效果时,GradientNoise节点常与Voronoi节点结合使用。通过Tiling And Offset节点对GradientNoise进行偏移移动,再结合颜色和透明度设置,可以模拟出逼真的火苗动态效果。

梦幻星球效果

在创建星球等科幻场景时,GradientNoise节点可用于生成星球表面的随机图案。通过结合菲涅尔效果,可以创造出具有独特视觉风格的梦幻星球。

性能优化与拓展应用

  • 性能优化:在ShaderGraph中,可以通过调整噪声的复杂度和使用更高效的插值函数来优化性能。例如,降低噪声的细节程度可以减少计算量,提高渲染速度;使用更高效的插值函数可以减少计算时间,提升性能。
  • 拓展应用:除了溶解效果,GradientNoise节点还可以用于地形生成、云层模拟、动态特效、材质细节等多个领域。在地形生成中,可以使用GradientNoise节点创建自然的地形起伏;在云层模拟中,可以使用GradientNoise节点生成自然的云层形态;在动态特效中,可以使用GradientNoise节点创建各种动态效果,如火焰、烟雾等;在材质细节中,可以使用GradientNoise节点添加自然的纹理细节,如木材的纹理、大理石的纹理等。

【Unity Shader Graph 使用与特效实现】专栏-直达 (欢迎点赞留言探讨,更多人加入进来能更加完善这个探索的过程,🙏)

相关推荐
winlife_7 小时前
全程用 AI 做一款商业级手游 · EP0 立项:能做到吗、怎么做、边界在哪
人工智能·unity·ai编程·游戏开发·商业化·mcp·funplay
SmalBox1 天前
【节点】[DynamicNoise节点]原理解析与实际应用
unity3d·游戏开发·图形学
晓杰'1 天前
从0到1实现Balatro游戏后端(7):Boss Blind与特殊规则实现
后端·websocket·typescript·node.js·游戏开发·项目实战·nestjs
晓杰'1 天前
从0到1实现Balatro游戏后端(6):Blind关卡状态设计与回合推进实现
后端·websocket·typescript·游戏开发·项目实战·nestjs·状态管理
SmalBox2 天前
【节点】[Checkerboard节点]原理解析与实际应用
unity3d·游戏开发·图形学
郝学胜-神的一滴3 天前
中级OpenGL教程 008:精准控制高光光斑大小与强度
c++·unity·godot·three.js·图形学·opengl·unreal
avi91113 天前
Unity 商业插件之(五)课外2 - Zenject的一些小Tips(学习备忘)
unity·游戏开发·团结引擎
SmalBox3 天前
【节点】[TriangleWave节点]原理解析与实际应用
unity3d·游戏开发·图形学
UWA3 天前
5 月刊|GPM 2.0 实现全场景可视化溯源、多维度数据解析与根因精准定位
性能优化·游戏开发·uwa