Unity3D Worley噪声算法代码实现

Worley Noise噪声算法

Worley噪声的算法过程如下。
首先需要创建一个2D’网格’,'网格’由’格子’构成,这里的’格子’并不是对应单个像素的,而是一个’格子’里包含有许多个像素。
(如图所示的一个格子,一个格子由9个像素组成,而’网格’由无数个这种’格子’组成,一个格子由多少像素组成对最后的效果有很大影响)
在这里插入图片描述
1.每个格子会关联一个随机生成的点,这里称为特征点。这个点必须在格子内部。
2.对于每个像素,计算离它最近的特征点的距离,作为该像素的灰度值。

那么如何找到离该像素最近的特征点是哪个呢?只要循环遍历该像素所在的格子为中心的九宫格即可。使用简单的计算即可证明离该像素最近的特征点也不会到九宫格的位置外面。

代码实现

所用的hash函数如下,这个hash函数给定一个点返回另一个点,也就是给定一个’格子’然后返回在该格子里的一个特征点
hash函数可以自己设计,只要满足以下条件
1.对同样的输入有同样的输出
2.粗略的满足统计上的随机性
3.该点每个分量在[0,1]之间 (因此,用三角函数是一个很好的选择)

        static Vector2 hash(Vector2 p)
        {
    
    
            float random = Mathf.Sin(666 + p.x * 5678 + p.y * 1234) * 4321;
            return new Vector2(p.x+Mathf.Sin(random)/2+0.5f, p.y+Mathf.Cos(random)/2+0.5f);
        }

然后就是前面说的,一个九宫格循环,查找最近点
返回值也可以反相(即用1减去自身),worly噪声生成的图片无论是正相还是反相都相当有艺术性。

public static float noise(float x, float y)
        {
    
    
            float distance = float.MaxValue;
            for (int Y = -1; Y <= 1; Y++)
            {
    
    
                for (int X = -1; X <= 1; X++)
                {
    
    
                    Vector2 cellPoint = hash(new Vector2((int) x + X, (int) y + Y));
                    distance = Mathf.Min(distance, Vector2.Distance(cellPoint, new Vector2(x, y)));
                }
            }

            return 1-distance;
        }

这样worly noise的代码就写完了。
接着我们再创建一个c#脚本基于这个函数来生成一张纹理。
创建一个Texture2D然后在一个二重循环里用SetPixel函数来设置对应像素点的颜色,因为我们要创建一个灰度图,所以rgb分量全都一样。
需要注意我们传入参数的时候对坐标值除以了16,这就代表一个’格子’里有16个像素。
最后还写了一个函数把创建好的纹理保存成图片。
把这个类挂载在一个物体上就可以生成一张noise噪声图并贴在物体上,同时保存该图在Assets文件夹下。

    public class CreateWorlyNoise : MonoBehaviour
    {
    
    
        [Range(1, 512)] public int cellSize=16;

        private void Start()
        {
    
    
            Texture2D texture = new Texture2D(512, 512);
            this.GetComponent<Renderer>().material.mainTexture = texture;
            for (int y = 0; y < texture.height; y++)
            {
    
    
                for (int x = 0; x < texture.width; x++)
                {
    
    
                    float grayscale = WorlyNoise.noise(x / (float) cellSize, y / (float) cellSize);
                    texture.SetPixel(x, y, new Color(grayscale, grayscale, grayscale));
                }
            }

            texture.Apply();
            saveTexture2D(texture, "tex");
        }

        void saveTexture2D(Texture2D texture, string fileName)
        {
    
    
            var bytes = texture.EncodeToPNG();
            var file = File.Create(Application.dataPath + "/04WorlyNoise/" + fileName + ".png");
            var binary = new BinaryWriter(file);
            binary.Write(bytes);
            file.Close();
            AssetDatabase.Refresh();
        }
    }

效果演示

效果如下,其实根据前面的算法大家也可以猜到,既然有查找最近点的操作,那么肯定会生成很多圆形。
(如果调一下颜色其实也很像一些细胞组织)
在这里插入图片描述
反相之后
在这里插入图片描述

地形生成

我们同样用worly noise来生成一个地形试试

using Algorithm;
using UnityEngine;

public class CreateTerrain : MonoBehaviour
{
    
    
    
    void Start()
    {
    
    
        for (int z = 0; z < 128; z++)
        {
    
    
            for (int x = 0; x < 128; x++)
            {
    
    
                float grayscale = ValueNoise.noise(x / (float) 16, z / (float) 16);
                var cube=GameObject.CreatePrimitive(PrimitiveType.Cube);
                int cubeY = (int) (grayscale * 10);
                cube.transform.position = new Vector3(x, cubeY, z);
                cube.GetComponent<Renderer>().material.color=new Color(0,grayscale,0);
            }
        }
    }
}

把这个脚本挂载到任意场景中的物体上,就可以得到如下效果。
在这里插入图片描述
重复性太强,看来不适合做地形生成

完整代码

public static class WorlyNoise
    {
    
    
        public static float noise(float x, float y)
        {
    
    
            float distance = float.MaxValue;
            for (int Y = -1; Y <= 1; Y++)
            {
    
    
                for (int X = -1; X <= 1; X++)
                {
    
    
                    Vector2 cellPoint = hash(new Vector2((int) x + X, (int) y + Y));
                    distance = Mathf.Min(distance, Vector2.Distance(cellPoint, new Vector2(x, y)));
                }
            }

            return 1-distance;
        }

        static Vector2 hash(Vector2 p)
        {
    
    
            float random = Mathf.Sin(666 + p.x * 5678 + p.y * 1234) * 4321;
            return new Vector2(p.x+Mathf.Sin(random)/2+0.5f, p.y+Mathf.Cos(random)/2+0.5f);
        }
       
    }

生成图片的类

using System.IO;
using UnityEditor;
using UnityEngine;

namespace Algorithm
{
    
    
    public class CreateWorlyNoise : MonoBehaviour
    {
    
    
        [Range(1, 512)] public int cellSize=16;

        private void Start()
        {
    
    
            Texture2D texture = new Texture2D(512, 512);
            this.GetComponent<Renderer>().material.mainTexture = texture;
            for (int y = 0; y < texture.height; y++)
            {
    
    
                for (int x = 0; x < texture.width; x++)
                {
    
    
                    float grayscale = WorlyNoise.noise(x / (float) cellSize, y / (float) cellSize);
                    texture.SetPixel(x, y, new Color(grayscale, grayscale, grayscale));
                }
            }

            texture.Apply();
            saveTexture2D(texture, "tex");
        }

        void saveTexture2D(Texture2D texture, string fileName)
        {
    
    
            var bytes = texture.EncodeToPNG();
            var file = File.Create(Application.dataPath + "/04WorlyNoise/" + fileName + ".png");
            var binary = new BinaryWriter(file);
            binary.Write(bytes);
            file.Close();
            AssetDatabase.Refresh();
        }
    }
}

另外代码也传到github仓库里了,大家也可以关注一下哦~
我的github

猜你喜欢

转载自blog.csdn.net/o83290102o5/article/details/117425429
今日推荐