zoukankan      html  css  js  c++  java
  • 图形学噪声解析

    新博客:https://yinl.fun
    欢迎关注,同步更新

    Prelin噪声

    Prelin噪声由Ken Perlin在1983年提出,因参与制作迪士尼的动画电影《电子世界争霸战》,但不满于当时那种非常不自然的纹理效果,而提出了Perlin噪声
    本篇文章借鉴各路大神的文章总结而来:

    此文章源码在我的个人GitHub上,本文章的仅仅实现了Prelin噪声中的最简单版本。上图先~

    利用Unity实现2D Prelin噪声

    candycat的文章非常好,不过没有Unity的具体实现代码,而则卷大明的文章中只给出了一部分的代码。最后发现了Unity自带2D噪声,所以结合多方文章最终完成了最简单的Prelin噪声。

    如果你懂GLSL的pixel shaders,那你就可以直接去阅读由iq大神写的:Voronoise。而这里我只是用Unity代码还原冯乐乐博客中利用shaderToy编写的代码

    在开始正文之前,我们得知道几个数学小知识。图形学中涉及矩阵计算的东西有太多,在ShaderToy中都有封装,而Unity并没有,所以我们得实现几个图形学函数

    Floor:向下取整

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

    Fract:取小数部分

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

    Sin:正弦函数

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

    其实这些函数在Unity中也有实现,不过我这里要实现2D的噪声,所以得处理2D情况下的操作。接下来就是正片了~
    首先还原冯乐乐博客中的梯度函数

    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实现的方式

  • 相关阅读:
    windows server 2008 x64 上asp.net 调用 word, excel 出错的解决方式
    在Azure VM上架设Surv-U FTP 服务器
    关于windows event log 的若干记录
    postman-变量/环境/过滤等
    每秒处理10万高并发订单的乐视集团支付系统架构分享
    Chai.js断言库API中文文档【转载】
    SharePoint 2013 如何打包一个应用
    SharePoint 2013 如何获取当前站点对应的“应用目录”中的“适用于SharePoint的应用程序”列表
    一个吊丝android个人开发者的逆袭之路
    开发者说说广告的事
  • 原文地址:https://www.cnblogs.com/SHOR/p/9398231.html
Copyright © 2011-2022 走看看