在Unity中手写Perlin Noise

本文介绍了 Perlin Noise 的原理及用途,并提供了 Unity 中的代码实现。Perlin Noise 常用于游戏开发中生成自然景观,如地形、水、火焰等。
前言:Unity中是内置了PerlinNoise的,之所以我想要自己实现是因为自己实现一次可以更深地理解其背后原理。
###(一)、Perlin噪声介绍:
柏林噪声是一个主要用于程序生成随机内容的算法,本质上噪声就是一个随机数生成器。 其在游戏开发领域,常用于生成波形,起伏不平的材质或纹理。例如用柏林噪声来生成我的世界的地形,水,火焰以及云。
说到随机可能你会想到Unity里不是有Random类可以给我们提供随机数吗,为什么还需要Perlin Noise来生成一个噪声值。这是因为通过Random类获取的随机数它太随机了,毫无规律可言,两个相邻产生的随机数之间没有联系从而可能造成两个值相差过大,这也让它在PCG领域的应用不如Perlin Noise。拿Perlin Noise最典型的应用举例,Minecraft里面的无限大地形就是通过Perlin Noise来实现的,正是其平滑的特点让生成的地形贴近于自然,并且其伪随机性也在游戏开发中大放异彩,Minecraft中你输入相同的Seed(种子)总会得到一样的地形。

(二)、Perlin噪声原理:

  • 一维柏林噪声:在X轴向上每个整数坐标随机生成一个数(范围为-1~1),我们称这个数为Gradient,译为梯度或者斜率。然后我们对相邻两个整数之间使用梯度进行插值计算,使得相邻两点之间平滑过渡。平滑度取决于所选用的插值函数,老版的柏林噪声使用f(t)=3t^2-2t^3,改进后的柏林噪声使用f(t)=t^3(t(t6-15)+10)。
  • 二维柏林噪声:获取一个点P(x,y),然后得到P点周围最近的四个点A(i, j)、B(i+1, j)、C(i, j+1)、D(i+1, j+1);随后获取ABCD四点的二维梯度值G(A)、G(B)、G(C)、G(D),并且算出ABCD到P点的向量AP、BP、CP以及DP。接着,将G(A)与AP进行点乘,计算出A点对于P点的梯度贡献值,然后分别算出其余三个点对P点的梯度贡献值,使用缓和曲线(ease curves)来计算它们的权重和。
  • 三维柏林噪声:同二维,计算8个顶点对P点的梯度贡献值
  • 二维下有4个,三维下有8个,n维下有2^n个,所以复杂度为O(2^n)
所以柏林噪声算法中一个重要部分就是梯度的计算。所以Perlin噪声也叫Gradient噪声(基于梯度的噪声)。

(三)、Perlin的代码实现:

using System.Collections.Generic; using UnityEngine; using System; public static class Noise { /// <summary> /// 用于获得伪随机梯度的排列表,有Perlin本人提供 /// </summary> private static readonly int[] perm = { 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, 151 }; /// <summary> /// 余弦插值 /// </summary> /// <param name="a"></param> /// <param name="b"></param> /// <param name="t"></param> /// <returns></returns> private static float Cos_Interpolate(float a, float b, float t) { double ft = t * Math.PI; t = (float)((1 - Math.Cos(ft)) * 0.5); return a * (1 - t) + t * b; } public static float perlinNoise(float x,float y) { List<Vector2> gradientList = CalculateGradient(); Vector2 pos = new Vector2(x, y); Vector2 rightUp = new Vector2((int)x + 1, (int)y + 1); Vector2 rightDown = new Vector2((int)x + 1, (int)y); Vector2 leftUp = new Vector2((int)x, (int)y + 1); Vector2 leftDown = new Vector2((int)x, (int)y); float v1 = DotGridGradient(gradientList[GradientIndex((int)leftDown.x, (int)leftDown.y)], GetDisVec(pos, leftDown)); float v2 = DotGridGradient(gradientList[GradientIndex((int)rightDown.x, (int)rightDown.y)], GetDisVec(pos, rightDown)); float interpolation1 = Cos_Interpolate(v1, v2, x - (int)x); float v3 = DotGridGradient(gradientList[GradientIndex((int)leftUp.x, (int)leftUp.y)], GetDisVec(pos, leftUp)); float v4 = DotGridGradient(gradientList[GradientIndex((int)rightUp.x, (int)rightUp.y)], GetDisVec(pos, rightUp)); float interpolation2 = Cos_Interpolate(v3, v4, x - (int)x); return Cos_Interpolate(interpolation1, interpolation2, y - (int)y); } /// <summary> /// 获取各个点的伪随机梯度 /// </summary> /// <param name="x">点的X坐标</param> /// <param name="y">点的Y坐标</param> /// <returns></returns> private static int GradientIndex(int x,int y) { return perm[(x + perm[y & 255]) & 255] & 7;//&7是为了保证取梯度向量时不越界 } /// <summary> /// 计算伪随机向量 /// </summary> /// <returns></returns> private static List<Vector2> CalculateGradient() { //可以采用Perlin在论文中给出的12个向量 //我们这里采用在单位圆上分布均匀的单位向量 List<Vector2> tempList = new List<Vector2>(); for(int i = 0; i < 8; i++) { // Vector2 vec = new Vector2(Mathf.Cos(Mathf.PI/4 * i), Mathf.Sin(Mathf.PI/4 * i)); tempList.Add(vec); } return tempList; } /// <summary> /// 获得各顶点到指定点的方向向量 /// </summary> /// <param name="p1">指定点</param> /// <param name="p2">分别为四个顶点</param> /// <returns></returns> private static Vector2 GetDisVec(Vector2 p1,Vector2 p2) { return p1 - p2; } /// <summary> /// 将梯度向量和个方向向量进行点乘 /// </summary> /// <param name="gradient"></param> /// <param name="dirVec"></param> /// <returns></returns> private static float DotGridGradient(Vector2 gradient,Vector2 dirVec) { return Vector2.Dot(gradient, dirVec) / 2 + 0.5f; } }
读者可以将代码粘贴进Unity里面自行测试,得到的噪声值和Unity内置的进行比较。当然效果没有内置的好,毕竟内置的肯定是经过优化的,并且Perlin Noise本身就有很多的实现,插值方式,列表选择等都会影响其结果。
内容概要:本文围绕VMware虚拟化环境在毕业设计中的应用,重点探讨其在网络安全与AI模型训练两大领域的实践价值。通过搭建高度隔离、可复现的虚拟化环境,解决传统物理机实验中存在的环境配置复杂、攻击场景难还原、GPU资源难以高效利用等问题。文章详细介绍了嵌套虚拟化、GPU直通(passthrough)、虚拟防火墙等核心技术,并结合具体场景提供实战操作流程与代码示例,包括SQL注入攻防实验中基于vSwitch端口镜像的流量捕获,以及PyTorch分布式训练中通过GPU直通实现接近物理机性能的模型训练效果。同时展望了智能化实验编排、边缘虚拟化和绿色计算等未来发展方向。; 适合人群:计算机相关专业本科高年级学生或研究生,具备一定虚拟化基础、网络安全或人工智能背景,正在进行或计划开展相关方向毕业设计的研究者;; 使用场景及目标:①构建可控的网络安全实验环境,实现攻击流量精准捕获与WAF防护验证;②在虚拟机中高效开展AI模型训练,充分利用GPU资源并评估性能损耗;③掌握VMware ESXi命令行与vSphere平台协同配置的关键技能; 阅读建议:建议读者结合VMware实验平台动手实践文中提供的esxcli命令与网络拓扑配置,重点关注GPU直通的硬件前提条件与端口镜像的混杂模式设置,同时可延伸探索自动化脚本编写与能效优化策略。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值