圖形學噪聲解析

新博客:https://yinl.fun
歡迎關注,同步更新html

Prelin噪聲

Prelin噪聲由Ken Perlin在1983年提出,因參與制做迪士尼的動畫電影《電子世界爭霸戰》,但不滿於當時那種很是不天然的紋理效果,而提出了Perlin噪聲
本篇文章借鑑各路大神的文章總結而來:git

此文章源碼在個人我的GitHub上,本文章的僅僅實現了Prelin噪聲中的最簡單版本。上圖先~github

利用Unity實現2D Prelin噪聲

candycat的文章很是好,不過沒有Unity的具體實現代碼,而則卷大明的文章中只給出了一部分的代碼。最後發現了Unity自帶2D噪聲,因此結合多方文章最終完成了最簡單的Prelin噪聲。函數

若是你懂GLSL的pixel shaders,那你就能夠直接去閱讀由iq大神寫的:Voronoise。而這裏我只是用Unity代碼還原馮樂樂博客中利用shaderToy編寫的代碼動畫

在開始正文以前,咱們得知道幾個數學小知識。圖形學中涉及矩陣計算的東西有太多,在ShaderToy中都有封裝,而Unity並無,因此咱們得實現幾個圖形學函數spa

Floor:向下取整.net

private static Vector2 Floor(Vector2 p)
{
    return new Vector2(Mathf.Floor(p.x), Mathf.Floor(p.y));
}

Fract:取小數部分3d

private static Vector2 Fract(Vector2 p)
{
    return p - Floor(p);
}

Sin:正弦函數code

private static Vector2 Sin(Vector2 p)
{
    return new Vector2(Mathf.Sin(p.x), Mathf.Sin(p.y));
}

其實這些函數在Unity中也有實現,不過我這裏要實現2D的噪聲,因此得處理2D狀況下的操做。接下來就是正片了~
首先還原馮樂樂博客中的梯度函數htm

private static Vector2 Hash22(Vector2 p)
{
    p = new Vector2(Vector2.Dot(p, new Vector2(127.1f, 311.7f)),
                    Vector2.Dot(p, new Vector2(269.5f, 183.3f)));

    return new Vector2(-1, -1) + 2.0f * Fract(Sin(p) * 43758.5453123f);
}

而後還原緩和曲線,這裏利用新提出的緩和曲線,使其在二階的狀況下也能知足連續性,公式以下:

\(s(t)=6*t^5-15*t^4+10*t^3\)

// 一階
private static float GetEaseCurves(float t)
{
    return t * t * t * (t * (t * 6 - 15) + 10);
}
// 二階
private static Vector2 GetEaseCurves(Vector2 p)
{
    return new Vector2(GetEaseCurves(p.x), GetEaseCurves(p.y));
}

這裏將馮樂樂中的-1.0f改成Unity中可計算的Vector(-1, -1)
接下來是生成prelin噪聲的主函數了

public static float prelin_noise(Vector2 p)
{
    Vector2 pi = Floor(p);
    Vector2 pf = p - pi;

    Vector2 w = GetEaseCurves(pf);

    float corner1 = Vector2.Dot(Hash22(pi + Vector2.zero), pf - Vector2.zero);
    float corner2 = Vector2.Dot(Hash22(pi + Vector2.right), pf - Vector2.right);
    float corner3 = Vector2.Dot(Hash22(pi + Vector2.up), pf - Vector2.up);
    float corner4 = Vector2.Dot(Hash22(pi + Vector2.one), pf - Vector2.one);

    return Mathf.Lerp(Mathf.Lerp(corner1, corner2, w.x),
                      Mathf.Lerp(corner3, corner4, w.x),
                      w.y);
}

到這裏咱們完成了2D Prelin噪聲的構建,加下來就是怎麼利用噪聲生成貼圖並轉成PNG存儲起來~
下面的代碼生成了一個噪聲的貼圖而且返回

private int width = 512;  // 貼圖寬度
private int height = 512; // 貼圖高度
private float xOrg = 0f;  // 寬度偏移起點
private float yOrg = 0f;  // 高度偏移起點
private float scale = 15f; // 週期

private Texture2D CreateTexture()
{
      Texture2D tex = new Texture2D(width, height);
      Color[] pix = new Color[width * height];

      float y = 0f;
      while (y < height)
      {
          float x = 0f;
          while (x < width)
          {
              float xCoord = xOrg + x / width * scale;
              float yCoord = yOrg + y / height * scale;
              float sample = PrelinNoise.prelin_noise(new Vector2(xCoord, yCoord));
              pix[(int)y * width + (int)x] = new Color(sample, sample, sample);
              x++;
          }
          y++;
      }

      tex.SetPixels(pix);
      tex.Apply();

      return tex;
}

下面的函數將貼圖轉換成PNG圖片存儲到特定位置

private bool SaveTexture(Texture2D tex, string path)
{
      if (File.Exists(path))
      {
          Debug.LogWarning("已有文件");
          return false;
      }

      if (tex == null)
      {
          Debug.LogWarning("貼圖爲空");
          return false;
      }

      // 貼圖轉換爲PNG圖片
      byte[] texData = tex.EncodeToPNG();

      // 若是沒有目錄則建立目錄
      int index = path.LastIndexOf('/');
      string dir = path.Remove(index);
      if (!Directory.Exists(dir))
      {
          Directory.CreateDirectory(dir);
      }

      // 貼圖存儲
      File.WriteAllBytes(path, texData);

      return true;
  }

後續還會陸續更新Sample噪聲,分型噪聲等利用Unity實現的方式

相關文章
相關標籤/搜索