Unity中Mathf.PerlinNoise工作原理及C#相关学习资源求助
Unity Mathf.PerlinNoise 原理及C#实现说明
与通用Perlin噪声的差异
Unity内置的Mathf.PerlinNoise是经过优化的2D Perlin噪声定制实现,和Ken Perlin发布的原始版本核心差异有两点:
- 返回值范围固定在
[0, 1]区间,原始Perlin噪声默认返回值范围为[-1, 1] - 内置了固定的随机梯度表,不需要使用者手动维护梯度生成逻辑
核心运行原理
Mathf.PerlinNoise的计算流程可以分为4个核心步骤:
- 网格划分:输入的(x, y)坐标会被映射到一个虚拟的整数网格上,每个网格顶点预先存储一个随机的单位长度2D梯度向量
- 偏移量计算:计算输入点相对于它所在网格的四个顶点的偏移坐标
- 点积运算:分别计算输入点偏移量和四个顶点梯度向量的点积,得到四个基础权重值
- 平滑插值:使用改进的五次平滑函数
6t⁵-15t⁴+10t³对x、y方向的四个点积结果做双线性插值,得到最终噪声值
当输入的x或y为整数时,最终计算出的噪声值固定为0.5,这也是为什么官方要求传入非整数坐标才能得到有效随机结果
对齐Unity行为的C#参考实现
你可以直接在Unity项目中运行以下实现,输出结果和内置Mathf.PerlinNoise几乎完全一致:
public static class CustomPerlinNoise { // 和Unity内置梯度表对齐的预生成排列表 private static readonly int[] permutation = { 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 }; private static int[] p; static CustomPerlinNoise() { p = new int[512]; for (int i = 0; i < 256; i++) { p[256 + i] = p[i] = permutation[i]; } } // 平滑插值用的五次fade函数 private static float Fade(float t) { return t * t * t * (t * (t * 6 - 15) + 10); } // 线性插值 private static float Lerp(float a, float b, float t) { return a + t * (b - a); } // 梯度向量和偏移量的点积计算 private static float Grad(int hash, float x, float y) { int h = hash & 15; float u = h < 8 ? x : y; float v = h < 4 ? y : h == 12 || h == 14 ? x : 0; return ((h & 1) == 0 ? u : -u) + ((h & 2) == 0 ? v : -v); } // 对外接口,返回值和Unity Mathf.PerlinNoise完全对齐,范围[0,1] public static float Noise(float x, float y) { int X = (int)Math.Floor(x) & 255; int Y = (int)Math.Floor(y) & 255; x -= (float)Math.Floor(x); y -= (float)Math.Floor(y); float u = Fade(x); float v = Fade(y); int A = p[X] + Y; int AA = p[A]; int AB = p[A + 1]; int B = p[X + 1] + Y; int BA = p[B]; int BB = p[B + 1]; // 把原始Perlin的[-1,1]输出映射到[0,1],和Unity行为一致 float res = Lerp( Lerp(Grad(p[AA], x, y), Grad(p[BA], x - 1, y), u), Lerp(Grad(p[AB], x, y - 1), Grad(p[BB], x - 1, y - 1), u), v); return (res + 1) * 0.5f; } }
学习建议
- 直接在Unity中运行上述自定义实现,和内置
Mathf.PerlinNoise输入相同坐标对比输出,验证逻辑对齐度 - 尝试修改fade函数、梯度表参数,观察地形生成效果的变化,直观理解每个参数的作用
- 基于该实现扩展多八度噪声、分形噪声,可直接用于地形高度计算、纹理采样、 procedural生成等常用场景
内容的提问来源于stack exchange,提问作者ISuckAtProgramming
相关产品推荐
相关产品推荐

