柏林噪声(Perlin Noise)的起源与核心价值
柏林噪声由肯·柏林(Ken Perlin)于1980年发明,属于梯度噪声的一种,在计算机图形学和程序性内容生成中得到了广泛应用。其核心价值在于通过数学算法直接生成纹理信息,相较于传统位图纹理具有以下优势:
- 无限分辨率:能够动态适应任意显示精度,在4K甚至8K屏幕上依然保持清晰细节,有效避免传统纹理在高分辨率下的模糊问题。
- 参数化控制:通过调整频率、振幅、偏移等参数,实时改变噪声形态,实现动态效果调整,无需重新生成纹理。
- 内存零占用:无需存储纹理文件,特别适合移动端等内存受限设备,有助于减少内存消耗并提升应用性能。
- 动态演化:支持时间维度动画,如云层流动、水面波动等,为场景增添生动感,增强用户体验。
梯度噪声的生成原理
梯度噪声的生成方式基于对伪随机值网格进行采样,并使用平滑插值函数(例如三次或五次插值)对这些值进行插值。其关键思想是创建平滑变化的值,以模拟自然现象的外观,比如地形的起伏或自然纹理(如大理石或木材)中的不规则图案。
算法实现步骤
-
初始化相关数据:包括排列表(Permutation Table)和梯度表(Gradient Table)等,其中梯度表中元素绝对值的最大值一般为1。排列表用于确定伪随机梯度,梯度表则存储了不同方向的梯度向量。
-
建立采样空间和参考点:对于一维柏林噪声,采样空间为一个一维坐标轴,轴上整数坐标位置均有一个点;二维柏林噪声的采样空间为一个二维坐标系,横纵坐标为整数的地方均有参考点;三维柏林噪声同理,采样空间为一个三维坐标系,横纵坐标为整数的地方均有参考点。
-
计算伪随机梯度:根据步骤一的两个表格,对每一个整数坐标点计算其伪随机梯度,该梯度的维数与采样空间维数相同。例如,在二维柏林噪声中,每个整数坐标点都有一个二维的伪随机梯度向量。
-
插值计算:对于噪声图上的像素,找到它在晶格上对应的点,并求出其参考点的坐标。在一维情况下,参考点为两侧最近的整数点;二维情况下,参考点为组成包围该点的单位正方体的四个点;三维情况下,参考点为组成包围该点的单位立方体的八个点。然后,根据这些参考点的值和插值函数,计算出该像素的值。
-
GradientNoise.cs
csharpusing 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 使用与特效实现】专栏-直达 (欢迎点赞留言探讨,更多人加入进来能更加完善这个探索的过程,🙏)