Unity3D FBM噪声算法代码实现

FBMNoise噪声算法

FBM是Fractal Brownian Motion的简写,中文翻译为分形布朗运动。
FBMNoise是将其他噪声算法用不同的参数输入叠加起来得到的噪声。其可以平滑噪声里的高频区域(即突变区域),使整个噪声变化更平滑。

下图展示了FBM的基本形式,简单来说就是将多个不同频率的噪声按照不同的振幅进行混合。

在这里插入图片描述

代码实现

代码非常简单,这里以perlinNoise柏林噪声为例。
只要在perlinNoise的原始文件中加入这样一个函数即可

        public static float fbmNoise(float x, float y,int layer)
        {
    
    
            float value =0;
            float frequency = 1;
            float amplitude = 0.5f;
            for(int i = 0; i < layer; i++)
            {
    
    
                value += noise(x*frequency,y*frequency) * amplitude;
                frequency *= 2;
                amplitude *= 0.5f;
            }

            return value;
        }

然后在生成时调用该函数

        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 = PerlinNoise.fbmNoise(x / (float) cellSize, y / (float) cellSize,8);
                    texture.SetPixel(x, y, new Color(grayscale, grayscale, grayscale));
                }
            }

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

其他的噪声可以去我之前几期博客看。
需要注意的是层数越高,需要的计算量就越大,如果层数过高,unity可能会卡住。这种fbm噪声,一般是采用预先计算的贴图而不是实时生成的。

效果演示

原始的perlinNoise
在这里插入图片描述

使用了fbm的perlinNoise

在这里插入图片描述
肉眼可见的平滑效果。

再试一下worlyNoise,原始效果
在这里插入图片描述
使用fbm之后
在这里插入图片描述
更多的效果大家可以自己尝试

地形生成

我们同样用perlinNoise的fbm形式来生成一个地形试试

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);
            }
        }
    }
}

把这个脚本挂载到任意场景中的物体上,就可以得到如下效果。效果相当的好
在这里插入图片描述

完整代码

using UnityEngine;

namespace Algorithm
{
    
    
    public static class PerlinNoise
    {
    
    
        static float interpolate(float a0, float a1, float w)
        {
    
    
            //线性插值
            //return (a1 - a0) * w + a0;

            //hermite插值
            return Mathf.SmoothStep(a0, a1, w);
        }


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


        static float dotGridGradient(Vector2 p1, Vector2 p2)
        {
    
    
            Vector2 gradient = randomVector2(p1);
            Vector2 offset = p2 - p1;
            return Vector2.Dot(gradient, offset) / 2 + 0.5f;
        }

        public static float fbmNoise(float x, float y,int layer)
        {
    
    
            float value =0;
            float frequency = 1;
            float amplitude = 0.5f;
            for(int i = 0; i < layer; i++)
            {
    
    
                value += noise(x*frequency,y*frequency) * amplitude;
                frequency *= 2;
                amplitude *= 0.5f;
            }

            return value;
        }

        public static float noise(float x, float y)
        {
    
    
            //声明二维坐标
            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);

            //计算x上的插值
            float v1 = dotGridGradient(leftDown, pos);
            float v2 = dotGridGradient(rightDown, pos);
            float interpolation1 = interpolate(v1, v2, x - (int) x);

            //计算y上的插值
            float v3 = dotGridGradient(leftUp, pos);
            float v4 = dotGridGradient(rightUp, pos);
            float interpolation2 = interpolate(v3, v4, x - (int) x);

            float value = interpolate(interpolation1, interpolation2, y - (int) y);
            return value;
        }
    }
}

生成图片的类

using System.IO;
using UnityEditor;
using UnityEngine;

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

        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.fbmNoise(x / (float) cellSize, y / (float) cellSize,8);
                    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 + "/01PerlinNoise/" + fileName + ".png");
            var binary = new BinaryWriter(file);
            binary.Write(bytes);
            file.Close();
            AssetDatabase.Refresh();
        }
    }
}

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

猜你喜欢

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