Unity 随机 生成地形 (PerlinNoise 柏林噪声)

2023-11-08 06:50

本文主要是介绍Unity 随机 生成地形 (PerlinNoise 柏林噪声),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

Unity 随机 生成地形 (PerlinNoise 柏林噪声)

  • 程序化地图生成
    • 地图显示模块
    • 柏林噪声地图模块
      • 柏林噪声效果显示
    • 彩色地图模块
      • 色彩地图效果显示
    • 基础网格生成规则
    • 网格地图模块
      • 网格地图效果显示
    • 程序化地图管理模块
    • 地图管理 菜单编辑
    • 代码搭载
    • 效果展示

程序化地图生成

地图显示模块

三种不同的地图格式数据生成:DrawNoiseMap() 噪声地图DrawTexture() 色彩地图DrawMesh() 网格地图
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;/// <summary>
/// 地图显示
/// 将噪声贴图转换为纹理 
/// </summary>
public class MapDisplay_ZH : MonoBehaviour
{[Header("纹理渲染")]public Renderer _TextureRender;[Header("网格数据")]public MeshFilter _MeshFilter;[Header("网格渲染")]public MeshRenderer _MeshRender;/// <summary>/// 噪声地图绘制/// </summary>/// <param 柏林噪声="_NoiseMap"></param>public void DrawNoiseMap(float[,] _NoiseMap){//地图宽度int _Width = _NoiseMap.GetLength(0);//地图高度int _Height = _NoiseMap.GetLength(1);//根据传入值  确定渲染纹理长宽Texture2D _Texture = new Texture2D(_Width, _Height);//获取噪波数组中的所有值Color[] _ColourMap = new Color[_Width * _Height];//设置每个像素点的颜色for (int y = 0; y < _Width; y++){for (int x = 0; x < _Height; x++){//色彩取样// Color 是一维数组 噪声是二维数组//获取当前像素点所在位置 : Y值 乘 地图宽度 再加上 X  就是当前像素在噪声地图中的位置//当前取样点的颜色 //由于只想要 黑白色域  所以使用 Color.black 和 Color.white_ColourMap[y * _Width + x] = Color.Lerp(Color.black, Color.white, _NoiseMap[x, y]);}}//色彩传递//纹理贴图赋值_Texture.SetPixels(_ColourMap);_Texture.Apply();//主纹理贴图赋值_TextureRender.sharedMaterial.mainTexture = _Texture;//地图大小赋值_TextureRender.transform.localScale = new Vector3(_Width, 1, _Height);}/// <summary>/// Texture 地图绘制/// </summary>/// <param 图像="_Texture"></param>public void DrawTexture(Texture2D _Texture){//渲染贴图赋予_TextureRender.sharedMaterial.mainTexture = _Texture;//渲染物体大小设置_TextureRender.transform.localScale = new Vector3(_Texture.width, 1, _Texture.height);}/// <summary>/// 网格地形绘制/// </summary>/// <param 地形网格="_MeshData"></param>/// <param 地形贴图="_Texture2D"></param>internal void DrawMesh(MeshData _MeshData, Texture2D _Texture2D){_MeshFilter.sharedMesh = _MeshData.CreteMesh();_MeshRender.sharedMaterial.mainTexture = _Texture2D;}
}

柏林噪声地图模块

这个模块为基础模块:返回值在0.01.0之间。(返回值可能略低于0.0或超过1.0)关键性代码: Mathf.PerlinNoise()
using System.Collections;
using System.Collections.Generic;
using UnityEngine;/// <summary>
/// 柏林噪声地图生成
/// </summary>
public static class Noise_ZH
{/// <summary>/// 柏林噪声地图生成方法/// </summary>/// <param 地图宽度="_MapWidth"></param>/// <param 地图高度="_MapHeight"></param>/// <param 地图大小="_Scale"></param>/// <param 种子="_Seed"></param>/// <param 抵消="_Octaves"></param>/// <param 持续="_Persistance"></param>/// <param 空隙="_Lacunarity"></param>/// <param 偏移抵消="_Offset"></param>/// /// <returns></returns>public static float[,] GenerateNoiseMap(int _MapWidth, int _MapHeight, float _Scale, int _Seed, int _Octaves, float _Persistance, float _Lacunarity, Vector2 _Offset){//噪声地图float[,] _NoiseMap = new float[_MapWidth, _MapHeight];//地图种子随机System.Random _Prng = new System.Random(_Seed);//抵消Vector2[] _OctaveOffsets = new Vector2[_Octaves];for (int i = 0; i < _Octaves; i++){float _OffsetX = _Prng.Next(-10000, 10000) + _Offset.x;float _OffsetY = _Prng.Next(-10000, 10000) + _Offset.y;_OctaveOffsets[i] = new Vector2(_OffsetX, _OffsetY);}//避免地图  不存在if (_Scale <= 0){_Scale = 0.0001f;}//地图放大响应float _HalfWidth = _MapWidth / 2;float _HalfHeight = _MapHeight / 2;//最大噪声高度float _MaxNoiseHeight = float.MinValue;//最小噪声高度float _MinNoiseHeight = float.MaxValue;for (int y = 0; y < _MapHeight; y++){for (int x = 0; x < _MapWidth; x++){//振幅float _Amplitude = 1;//频率float _Frequency = 1;//噪波高度float _NoiseHeight = 0;for (int i = 0; i < _Octaves; i++){//地图单元取整float _SampleX = (x - _HalfWidth) / _Scale * _Frequency + _OctaveOffsets[i].x;float _SampleY = (y - _HalfHeight) / _Scale * _Frequency + _OctaveOffsets[i].y;//根据传入参数 生成2D柏林噪声float _PerlinValue = Mathf.PerlinNoise(_SampleX, _SampleY) * 2 - 1;//噪波高度等于 柏林噪声 乘于 振幅_NoiseHeight += _PerlinValue * _Amplitude;//振幅变更_Amplitude *= _Persistance;//频率变更_Frequency *= _Lacunarity;}//限值操作if (_NoiseHeight > _MaxNoiseHeight){_MaxNoiseHeight = _NoiseHeight;}else if (_NoiseHeight < _MinNoiseHeight){_MinNoiseHeight = _NoiseHeight;}//传入地图数据_NoiseMap[x, y] = _NoiseHeight;}}//噪声贴图 最大值最小值 限定输出//图像叠加for (int y = 0; y < _MapHeight; y++){for (int x = 0; x < _MapWidth; x++){_NoiseMap[x, y] = Mathf.InverseLerp(_MinNoiseHeight, _MaxNoiseHeight, _NoiseMap[x, y]);}}//返回地图数据return _NoiseMap;}}

柏林噪声效果显示

请添加图片描述

彩色地图模块

色彩地图模块:
TextureFromColourMap() 纹理生成模块
TextureFromHeightMap() 高度生成模块
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
/// <summary>
/// 纹理生成器
/// </summary>
public static class TextureGenerator_ZH
{/// <summary>/// 颜色地图的纹理/// </summary>/// <param 色彩地图="_ColourMap"></param>/// <param 宽度="_Width"></param>/// <param 高度="_Height"></param>/// <returns></returns>public static Texture2D TextureFromColourMap(Color[] _ColourMap, int _Width, int _Height){Texture2D _Texture = new Texture2D(_Width, _Height);//绘制模式 变更//点过滤-纹理像素变得块状近距离//边缘块状显示//_Texture.filterMode = FilterMode.Point;_Texture.filterMode = FilterMode.Point;//纹理坐标缠绕模式//将纹理夹紧到边缘的最后一个像素_Texture.wrapMode = TextureWrapMode.Clamp;//色彩传递//纹理贴图赋值_Texture.SetPixels(_ColourMap);//必须要应用一下不然不响应_Texture.Apply();return _Texture;}/// <summary>/// 高度贴图纹理/// </summary>/// <param 高度贴图="_HeightMap"></param>/// <returns></returns>public static Texture2D TextureFromHeightMap(float[,] _HeightMap){//地图宽度int _Width = _HeightMap.GetLength(0);//地图高度int _Height = _HeightMap.GetLength(1);根据传入值  确定渲染纹理长宽//Texture2D _Texture = new Texture2D(_Width, _Height);//获取噪波数组中的所有值Color[] _ColourMap = new Color[_Width * _Height];//设置每个像素点的颜色for (int y = 0; y < _Width; y++){for (int x = 0; x < _Height; x++){//色彩取样// Color 是一维数组 噪声是二维数组//获取当前像素点所在位置 : Y值 乘 地图宽度 再加上 X  就是当前像素在噪声地图中的位置//当前取样点的颜色 //由于只想要 黑白色域  所以使用 Color.black 和 Color.white_ColourMap[y * _Width + x] = Color.Lerp(Color.black, Color.white, _HeightMap[x, y]);}}色彩传递纹理贴图赋值//_Texture.SetPixels(_ColourMap);//_Texture.Apply();return TextureFromColourMap(_ColourMap, _Width, _Height);}
}

色彩地图效果显示

请添加图片描述

基础网格生成规则

当三角网格数据绘制顺序为逆时针时:
123134  法线方向为朝上。
当三角网格数据绘制顺序为顺时针时:
143132  法线方向为朝下

请添加图片描述

法线方向:法线(normal line),是指始终垂直于某平面的直线。在几何学中,法线指平面上垂直于曲线在某点的切线的一条线。法线也应用于光学的平面镜反射上。
如果法线方向背对视角方向,那么该网格三角面将不可视。

请添加图片描述

网格地图模块

网格地图模块:MeshData 网格类AddTrianle()为关键性方法进行三角形网格绘制逻辑CreteMesh() 网格数据填充方法
using System.Collections;
using System.Collections.Generic;
using UnityEngine;/// <summary>
/// 网格生成器
/// </summary>
public static class MeshGenerator_ZH
{/// <summary>/// 地形网格生成/// </summary>/// <param 高度地图="_HeightMap"></param>public static MeshData GenerateTerrainMesh(float[,] _HeightMap){//宽度int _Width = _HeightMap.GetLength(0);//高度int _Height = _HeightMap.GetLength(1);//网格划分float _TopLeftX = (_Width - 1) / -2f;float _TopLeftZ = (_Height - 1) / -2f;//网格数据MeshData _MeshData = new MeshData(_Width, _Height);//三角形 绘制序号int _VertexIndex = 0;//网格数据填充for (int y = 0; y < _Height; y++){for (int x = 0; x < _Width; x++){//顶点数据填充_MeshData._Vertices[_VertexIndex] = new Vector3(_TopLeftX + x, _HeightMap[x, y], _TopLeftZ + y);//UV 数据填充_MeshData._UVs[_VertexIndex] = new Vector2(x / (float)_Width, y / (float)_Height);//剔除 行 列 最边缘 的顶点  不计入网格渲染计算if (x < _Width - 1 && y < _Height - 1){_MeshData.AddTrianle(_VertexIndex, _VertexIndex + _Width + 1, _VertexIndex + _Width);_MeshData.AddTrianle(_VertexIndex + _Width + 1, _VertexIndex, _VertexIndex + 1);}//序号增加_VertexIndex++;}}return _MeshData;}
}/// <summary>
/// 网格数据
/// </summary>
public class MeshData
{//顶点数据public Vector3[] _Vertices;//绘制序列public int[] _Triangles;//UV 数据public Vector2[] _UVs;//三角 序号int _TriangleIndex;public MeshData(int _MeshWidth,int _MeshHeight){//网格顶点数据_Vertices = new Vector3[_MeshWidth * _MeshHeight];//UV 数据_UVs = new Vector2[_MeshWidth * _MeshHeight];//三角形绘制序列_Triangles = new int[(_MeshWidth - 1) * (_MeshHeight - 1) * 6];}/// <summary>/// 网格三角形绘制/// </summary>/// <param 顶点A="_A"></param>/// <param 顶点B="_B"></param>/// <param 顶点C="_C"></param>public void AddTrianle(int _A,int _B,int _C){//例如:// 1 2 3// 4 5 6// 7 8 9// 绘制 逻辑为  124 245 235 356 457 578 568 689  右手定则  逆时针 法线方向朝上//注意 绘制的的方向要统一   统一顺时针绘制  或者逆时针绘制_Triangles[_TriangleIndex] = _C;_Triangles[_TriangleIndex+1] = _B;_Triangles[_TriangleIndex+2] = _A;_TriangleIndex += 3;}/// <summary>/// 新建网格/// </summary>/// <returns></returns>public Mesh CreteMesh(){//网格数据Mesh _Mesh = new Mesh();//顶点数据赋予_Mesh.vertices = _Vertices;//三角形序列赋予_Mesh.triangles = _Triangles;//UV 数据 赋予_Mesh.uv = _UVs;//使用默认法线_Mesh.RecalculateNormals();//数据返回return _Mesh;}
}

网格地图效果显示

请添加图片描述

请添加图片描述

程序化地图管理模块

用作管理地图模块化生成
using System.Collections;
using System.Collections.Generic;
using UnityEngine;/// <summary>
///  地图管理
/// </summary>
public class MapGenertor_ZH : MonoBehaviour
{/// <summary>/// 地图显示类型枚举/// </summary>public enum DrawMap { NoiseMap, ColourMap, MeshMap }[Header("地图绘制类型")]public DrawMap _DrawMap = DrawMap.NoiseMap;[Header("地图宽度")]public int _MapWidth;[Header("地图高度")]public int _MapHeight;[Header("地图大小")]public float _NoiseScale;[Header("八度")]public int _Octaves;[Header("持续")][Range(0, 1)]public float _Persistance;[Header("空隙")]public float _Lacunarity;[Header("地图种子")]public int _Seed;[Header("偏移")]public Vector2 _Offset;[Header("地形区域")]public TerrainType[] _Regions;[Header("自动更新布尔")]public bool _AutoUpdate;/// <summary>/// 地图生成 调用/// </summary>public void GenerateMap(){//参数传递 float[,] _NoiseMap = Noise_ZH.GenerateNoiseMap(_MapWidth, _MapHeight, _NoiseScale, _Seed, _Octaves, _Persistance, _Lacunarity, _Offset);//色彩地图Color[] _ColourMap = new Color[_MapWidth * _MapHeight];//根据地形高度进行色彩赋予for (int y = 0; y < _MapHeight; y++){for (int x = 0; x < _MapWidth; x++){//获取当前地图高度值float _CurrentHeight = _NoiseMap[x, y];//进行地形区域判定for (int i = 0; i < _Regions.Length; i++){if (_CurrentHeight <= _Regions[i]._Height){//色彩赋值_ColourMap[y * _MapWidth + x] = _Regions[i]._Colour;break;}}}}//查找 MapDisplay_ZH 搭载物体MapDisplay_ZH _Display = FindObjectOfType<MapDisplay_ZH>();//根据不同类型进行不同地图绘制if (_DrawMap == DrawMap.NoiseMap){//进行柏林噪声地形绘制_Display.DrawTexture(TextureGenerator_ZH.TextureFromHeightMap(_NoiseMap));}else if (_DrawMap == DrawMap.ColourMap){//进行色彩贴图地形绘制_Display.DrawTexture(TextureGenerator_ZH.TextureFromColourMap(_ColourMap, _MapWidth, _MapHeight));}else if (_DrawMap == DrawMap.MeshMap){//进行网格地形绘制_Display.DrawMesh(MeshGenerator_ZH.GenerateTerrainMesh(_NoiseMap), TextureGenerator_ZH.TextureFromColourMap(_ColourMap, _MapWidth, _MapHeight));}}/// <summary>/// 当脚本被加载或检查器在值被修改时调用此函数时(仅在编辑器调用中)/// </summary>private void OnValidate(){//保证初始化显示if (_MapWidth < 1){_MapWidth = 1;}if (_MapHeight < 1){_MapHeight = 1;}if (_Lacunarity < 1){_Lacunarity = 1;}if (_Octaves < 0){_Octaves = 0;}}
}//序列化
[System.Serializable]
/// <summary>
/// 地形类型
/// </summary>
public struct TerrainType
{//名称public string _Name;//高度public float _Height;//颜色public Color _Colour;
}

地图管理 菜单编辑

需要在 根目录创建一个 Editor 文件夹并把代码文件放进去才能执行
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEditor;
/// <summary>
/// 菜单编辑
/// </summary>
[CustomEditor(typeof(MapGenertor_ZH))]
public class MapGeneratorEditor_ZH : Editor
{public override void OnInspectorGUI(){MapGenertor_ZH _MapGen = (MapGenertor_ZH)target;//绘制内置检查器//如果发现变更if (DrawDefaultInspector()){//如果更新布尔为 Ture  就持续更新地图if (_MapGen._AutoUpdate){_MapGen.GenerateMap();}}//在 MapGenertor_ZH 上创建更新按钮if (GUILayout.Button("Generate")){_MapGen.GenerateMap();}}
}

代码搭载

请添加图片描述
请添加图片描述

请添加图片描述

效果展示

根据地图种子的值来进行地图随机变换

请添加图片描述

请添加图片描述
请添加图片描述
请添加图片描述

暂时先这样吧,如果有时间的话就会更新,实在看不明白就留言,看到我会回复的。
路漫漫其修远兮,与君共勉。

这篇关于Unity 随机 生成地形 (PerlinNoise 柏林噪声)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/368441

相关文章

AI一键生成 PPT

AI一键生成 PPT 操作步骤 作为一名打工人,是不是经常需要制作各种PPT来分享我的生活和想法。但是,你们知道,有时候灵感来了,时间却不够用了!😩直到我发现了Kimi AI——一个能够自动生成PPT的神奇助手!🌟 什么是Kimi? 一款月之暗面科技有限公司开发的AI办公工具,帮助用户快速生成高质量的演示文稿。 无论你是职场人士、学生还是教师,Kimi都能够为你的办公文

pdfmake生成pdf的使用

实际项目中有时会有根据填写的表单数据或者其他格式的数据,将数据自动填充到pdf文件中根据固定模板生成pdf文件的需求 文章目录 利用pdfmake生成pdf文件1.下载安装pdfmake第三方包2.封装生成pdf文件的共用配置3.生成pdf文件的文件模板内容4.调用方法生成pdf 利用pdfmake生成pdf文件 1.下载安装pdfmake第三方包 npm i pdfma

poj 1258 Agri-Net(最小生成树模板代码)

感觉用这题来当模板更适合。 题意就是给你邻接矩阵求最小生成树啦。~ prim代码:效率很高。172k...0ms。 #include<stdio.h>#include<algorithm>using namespace std;const int MaxN = 101;const int INF = 0x3f3f3f3f;int g[MaxN][MaxN];int n

poj 1287 Networking(prim or kruscal最小生成树)

题意给你点与点间距离,求最小生成树。 注意点是,两点之间可能有不同的路,输入的时候选择最小的,和之前有道最短路WA的题目类似。 prim代码: #include<stdio.h>const int MaxN = 51;const int INF = 0x3f3f3f3f;int g[MaxN][MaxN];int P;int prim(){bool vis[MaxN];

poj 2349 Arctic Network uva 10369(prim or kruscal最小生成树)

题目很麻烦,因为不熟悉最小生成树的算法调试了好久。 感觉网上的题目解释都没说得很清楚,不适合新手。自己写一个。 题意:给你点的坐标,然后两点间可以有两种方式来通信:第一种是卫星通信,第二种是无线电通信。 卫星通信:任何两个有卫星频道的点间都可以直接建立连接,与点间的距离无关; 无线电通信:两个点之间的距离不能超过D,无线电收发器的功率越大,D越大,越昂贵。 计算无线电收发器D

hdu 1102 uva 10397(最小生成树prim)

hdu 1102: 题意: 给一个邻接矩阵,给一些村庄间已经修的路,问最小生成树。 解析: 把已经修的路的权值改为0,套个prim()。 注意prim 最外层循坏为n-1。 代码: #include <iostream>#include <cstdio>#include <cstdlib>#include <algorithm>#include <cstri

【生成模型系列(初级)】嵌入(Embedding)方程——自然语言处理的数学灵魂【通俗理解】

【通俗理解】嵌入(Embedding)方程——自然语言处理的数学灵魂 关键词提炼 #嵌入方程 #自然语言处理 #词向量 #机器学习 #神经网络 #向量空间模型 #Siri #Google翻译 #AlexNet 第一节:嵌入方程的类比与核心概念【尽可能通俗】 嵌入方程可以被看作是自然语言处理中的“翻译机”,它将文本中的单词或短语转换成计算机能够理解的数学形式,即向量。 正如翻译机将一种语言

poj 3723 kruscal,反边取最大生成树。

题意: 需要征募女兵N人,男兵M人。 每征募一个人需要花费10000美元,但是如果已经招募的人中有一些关系亲密的人,那么可以少花一些钱。 给出若干的男女之间的1~9999之间的亲密关系度,征募某个人的费用是10000 - (已经征募的人中和自己的亲密度的最大值)。 要求通过适当的招募顺序使得征募所有人的费用最小。 解析: 先设想无向图,在征募某个人a时,如果使用了a和b之间的关系

Thymeleaf:生成静态文件及异常处理java.lang.NoClassDefFoundError: ognl/PropertyAccessor

我们需要引入包: <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-thymeleaf</artifactId></dependency><dependency><groupId>org.springframework</groupId><artifactId>sp

前端-06-eslint9大变样后,如何生成旧版本的.eslintrc.cjs配置文件

目录 问题解决办法 问题 最近在写一个vue3+ts的项目,看了尚硅谷的视频,到了配置eslintrc.cjs的时候我犯了难,因为eslint从9.0之后重大更新,跟以前完全不一样,但是我还是想用和老师一样的eslintrc.cjs文件,该怎么做呢? 视频链接:尚硅谷Vue项目实战硅谷甄选,vue3项目+TypeScript前端项目一套通关 解决办法 首先 eslint 要