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

相关文章

Java使用Javassist动态生成HelloWorld类

《Java使用Javassist动态生成HelloWorld类》Javassist是一个非常强大的字节码操作和定义库,它允许开发者在运行时创建新的类或者修改现有的类,本文将简单介绍如何使用Javass... 目录1. Javassist简介2. 环境准备3. 动态生成HelloWorld类3.1 创建CtC

Python从Word文档中提取图片并生成PPT的操作代码

《Python从Word文档中提取图片并生成PPT的操作代码》在日常办公场景中,我们经常需要从Word文档中提取图片,并将这些图片整理到PowerPoint幻灯片中,手动完成这一任务既耗时又容易出错,... 目录引言背景与需求解决方案概述代码解析代码核心逻辑说明总结引言在日常办公场景中,我们经常需要从 W

Unity新手入门学习殿堂级知识详细讲解(图文)

《Unity新手入门学习殿堂级知识详细讲解(图文)》Unity是一款跨平台游戏引擎,支持2D/3D及VR/AR开发,核心功能模块包括图形、音频、物理等,通过可视化编辑器与脚本扩展实现开发,项目结构含A... 目录入门概述什么是 UnityUnity引擎基础认知编辑器核心操作Unity 编辑器项目模式分类工程

C#使用Spire.XLS快速生成多表格Excel文件

《C#使用Spire.XLS快速生成多表格Excel文件》在日常开发中,我们经常需要将业务数据导出为结构清晰的Excel文件,本文将手把手教你使用Spire.XLS这个强大的.NET组件,只需几行C#... 目录一、Spire.XLS核心优势清单1.1 性能碾压:从3秒到0.5秒的质变1.2 批量操作的优雅

Python使用python-pptx自动化操作和生成PPT

《Python使用python-pptx自动化操作和生成PPT》这篇文章主要为大家详细介绍了如何使用python-pptx库实现PPT自动化,并提供实用的代码示例和应用场景,感兴趣的小伙伴可以跟随小编... 目录使用python-pptx操作PPT文档安装python-pptx基础概念创建新的PPT文档查看

C#和Unity中的中介者模式使用方式

《C#和Unity中的中介者模式使用方式》中介者模式通过中介者封装对象交互,降低耦合度,集中控制逻辑,适用于复杂系统组件交互场景,C#中可用事件、委托或MediatR实现,提升可维护性与灵活性... 目录C#中的中介者模式详解一、中介者模式的基本概念1. 定义2. 组成要素3. 模式结构二、中介者模式的特点

在ASP.NET项目中如何使用C#生成二维码

《在ASP.NET项目中如何使用C#生成二维码》二维码(QRCode)已广泛应用于网址分享,支付链接等场景,本文将以ASP.NET为示例,演示如何实现输入文本/URL,生成二维码,在线显示与下载的完整... 目录创建前端页面(Index.cshtml)后端二维码生成逻辑(Index.cshtml.cs)总结

Python实现数据可视化图表生成(适合新手入门)

《Python实现数据可视化图表生成(适合新手入门)》在数据科学和数据分析的新时代,高效、直观的数据可视化工具显得尤为重要,下面:本文主要介绍Python实现数据可视化图表生成的相关资料,文中通过... 目录前言为什么需要数据可视化准备工作基本图表绘制折线图柱状图散点图使用Seaborn创建高级图表箱线图热

SQLServer中生成雪花ID(Snowflake ID)的实现方法

《SQLServer中生成雪花ID(SnowflakeID)的实现方法》:本文主要介绍在SQLServer中生成雪花ID(SnowflakeID)的实现方法,文中通过示例代码介绍的非常详细,... 目录前言认识雪花ID雪花ID的核心特点雪花ID的结构(64位)雪花ID的优势雪花ID的局限性雪花ID的应用场景

Django HTTPResponse响应体中返回openpyxl生成的文件过程

《DjangoHTTPResponse响应体中返回openpyxl生成的文件过程》Django返回文件流时需通过Content-Disposition头指定编码后的文件名,使用openpyxl的sa... 目录Django返回文件流时使用指定文件名Django HTTPResponse响应体中返回openp