<output id="qn6qe"></output>

    1. <output id="qn6qe"><tt id="qn6qe"></tt></output>
    2. <strike id="qn6qe"></strike>

      亚洲 日本 欧洲 欧美 视频,日韩中文字幕有码av,一本一道av中文字幕无码,国产线播放免费人成视频播放,人妻少妇偷人无码视频,日夜啪啪一区二区三区,国产尤物精品自在拍视频首页,久热这里只有精品12

      【Ray Tracing The Next Week 超詳解】 光線追蹤2-4 Perlin noise

       

       Preface

      為了得到更好的紋理,很多人采用各種形式的柏林噪聲(該命名來自于發明人 Ken Perlin)

      柏林噪聲是一種比較模糊的白噪聲的東西:(引用書中一張圖)

       

      柏林噪聲是用來生成一些看似雜亂無章其實有些變換規律的圖形(更加貼近自然),比如海水、地形、霧等

      當然這里面的原理涉及分形幾何等相關的知識

      例如,2D柏林噪聲可以生成

       

      以及一些網上的總結:

       還有一些其他的圖

       

      是不是看起來自然多了

      那么今天,我們就來領略一下隨機技術帶來的自然之美~

       

       Chapter 4:Perlin Noise

      柏林噪聲有2個關鍵的特點:

      第一,輸入相同的3D點,總能返回相同的隨機值

      第二,簡單快捷,使用一些hack的方法,達到快速近似的效果。

       

       關于隨機數:

      許多人在他們的程序中使用“隨機數產生器”,以使得物體的運動行為更加自然,或者用來生成紋理。隨機數產生器在一些情況下很有用,比如用在模擬自然物體的地方,如地形,海水等。

      自然物體通常是分形的,有各種各樣的層次細節,比如山的輪廓,通過高度區分就有高山(mountain,高度變化大)、山丘(hill,高度變化適中)、巨石(高度變化小) 、石頭(高度變化很小)等。另外,比如草地、海浪、跑動的螞蟻、搖晃的樹枝、風、大理石的花紋等等,這些都呈現出了或大或小的細節變化。Perlin噪聲函數通過噪聲函數來模擬這些自然景觀。

      要構造一個Perlin函數,首先需要一個噪聲函數和一個插值函數

      我們第一步當然是構建一個Perlin的類

      class Perlin
          {
      public:
      
          inline rtvar noise(const rtvec& p)const;
      
          inline static rtvar* randomvalue() { return _randomvalue; }
      
          inline static int* perm_x() { return _perm_x; }
      
          inline static int* perm_y() { return _perm_y; }
      
          inline static int* perm_z() { return _perm_z; }
      
      public:
      
          static rtvar* perlin_generate();
      
          static int* perlin_generate_perm();
      
          static void permute(int* p, int n);

      private: static rtvar* _randomvalue; static int* _perm_x; static int* _perm_y; static int* _perm_z; };

       

      我們來介紹一下,第一個public包含的是和該類相關的成員函數

      第二個public是我們的隨機數生成函數,它們按理說應該和此類無關,但是放在類外,擔心污染命名空間,所以暫時列為靜態函數成員,畢竟它們和Perlin類有很大關系,最后的時候,再把所有的功能性全局函數封裝到3D泛型庫里面

      類數據成員:分別是Perlin隨機函數生成的隨機序列以及三個方向的輔助隨機分量序列

       

      我們如下設置這三個隨機函數

      rtvar * Perlin::perlin_generate()
          {
          rtvar* p = new rtvar[256];
          for (int i = 0; i < 256; ++i)    p[i] = lvgm::rand01();
          return p;
          }
      
      int* Perlin::perlin_generate_perm()
          {
          int * p = new int[256];
          for (int i = 0; i < 256; ++i)    p[i] = i;
          permute(p, 256);
          return p;
          }
      
      void Perlin::permute(int * p, int n)
          {
          for (int i = n - 1; i > 0; --i)
              {
              int target = int(lvgm::rand01() * (i + 1));
              stds swap(p[i], p[target]);
              }
          }

      然后用它們初始化靜態數據成員

      rtvar* Perlin::_randomvalue = Perlin::perlin_generate();
      
      int* Perlin::_perm_x = Perlin::perlin_generate_perm();
      
      int* Perlin::_perm_y = Perlin::perlin_generate_perm();
      
      int* Perlin::_perm_z = Perlin::perlin_generate_perm();

      其中,總隨機序列由第一種方法生成,序列中的每一個元素均為0~1的隨機數

      分量的隨機序列由第二種方法生成,即,初始序列為1-255,之后遍歷整個序列,當前位置和一個隨機生成的位置進行交換,已達到序列隨機化

       

      隨機函數講完了,我們來看一下產生噪聲值的函數

      u,v,w是插值時候用的,目前暫時不用

      參數p為空間某點的位置(未經歸一化或單位化)

      上面的函數也很好懂,就不細說了

       

      我們暫時先不管插值函數,我們先用這個試一下效果

      class noise_texture :public texture
          {
      public:
          noise_texture() {  }
      
          virtual rtvec value(rtvar u, rtvar v, const rtvec& p)const override;
          
      private:
          Perlin _noise;
          };
      
      
      rtvec noise_texture::value(rtvar u, rtvar v, const rtvec& p)const
          {
          return rtvec(1, 1, 1) * _noise.noise(p);
          }

      就是把原來的噪聲值騰個地方,轉個手,沒什么變化

      然后主函數中

      相機參數依然是:(以后默認是這個)

       

      得到的效果是這樣的:

       

      其實還有個中間產品,之前把noise中的最后一行寫成了

      return _randomvalue[_perm_x[i] ^ _perm_y[i] ^ _perm_z[i]];

      結果得到了下圖(未做到完全隨機)

      感覺這個手誤形成圖也挺好看的

       

      第一個圖片看起來有點生硬粗糙,不是很光滑,所以,我們采用線性插值光滑一下

      rtvar Perlin::trilinear_interp(rtvar c[2][2][2], rtvar u, rtvar v, rtvar w)
          {
          rtvar accumulate = 0;
          for (int i = 0; i < 2; ++i)
              for (int j = 0; j < 2; ++j)
                  for (int k = 0; k < 2; ++k)
                      accumulate +=
                      (i * u + (1 - i)*(1 - u))*
                      (j * v + (1 - j)*(1 - v))*
                      (k * w + (1 - k)*(1 - w))*
                      c[i][j][k];
          return accumulate;
          }

      我們把插值函數加入到noise函數中,當然你也可以嘗試其他的插值函數

      inline rtvar Perlin::noise(const rtvec& p)const
          {
          int i = floor(p.x());
          int j = floor(p.y());
          int k = floor(p.z());
      
          rtvar u = p.x() - i;
          rtvar v = p.y() - j;
          rtvar w = p.z() - k;
      
          rtvar list[2][2][2];
          for (int a = 0; a < 2; ++a)
              for (int b = 0; b < 2; ++b)
                  for (int c = 0; c < 2; ++c)
                      list[a][b][c] = _randomvalue[_perm_x[(i + a) & 255] ^ _perm_y[(j + b) & 255] ^ _perm_z[(k + c) & 255]];
          
          return trilinear_interp(list, u, v, w);
          }

       

      我們同時采用了隨機生成函數和插值函數

       

       我們還可以再嘗試一下利用Hermit Cubic來進行舍入插值

       

       

       

      它的頻率依舊有點低,我們可以對參數施加一定的縮放比例,加速它的變化

      也就是圖像中的顏色更迭的太慢(I think)

      class noise_texture :public texture
          {
      public:
          noise_texture() {  }
      
          noise_texture(const rtvar scale);    
      
          virtual rtvec value(rtvar u, rtvar v, const rtvec& p)const override;
          
      private:
          Perlin _noise;
      
          rtvar _scale;
          };
      
      
      
      noise_texture::noise_texture(const rtvar scale)
          :_scale(scale)
          {
          }
      
      rtvec noise_texture::value(rtvar u, rtvar v, const rtvec& p)const
          {
          return rtvec(1, 1, 1) * _noise.noise(_scale * p);
          }

       

      下面是scale 為 15 的圖像

       

      看起來密集多了,顏色變換頻率也快了

      下面是scale 為 1.5 的圖像

       

      顯然,上面的圖像格點還是很明晰的,可能是因為最小值和最大值總是精確地落在整數x / y / z上。 Ken Perlin采用了另一種技巧,將隨機單位向量(而不僅僅是浮點數)放在格點上,并使用點積來移動格子的最小值和最大值。

      所以,首先我們需要將隨機浮點數更改為隨機向量,試一試新的方法

      下面是書上的代碼,你運行之后打不開圖像文件,因為里面是錯的,我們邊看邊數說哪里錯了

      我們需要把數據成員_randomvalue改為static rtvec*

      所以初始化語句也要改

      rtvec * Perlin::_randomvalue = Perlin::perlin_generate();
      
      int * Perlin::_perm_x = Perlin::perlin_generate_perm();
      
      int * Perlin::_perm_y = Perlin::perlin_generate_perm();
      
      int * Perlin::_perm_z = Perlin::perlin_generate_perm();

       

      rtvec * Perlin::perlin_generate()
      {
          rtvec * p = new rtvec[256];
          for (int i = 0; i < 256; ++i)
              p[i] = rtvec(-1 + 2 * lvgm::rand01(), -1 + 2 * lvgm::rand01(), -1 + 2 * lvgm::rand01()).ret_unitization();
          return p;
      }

       且看上面這段代碼, -1 + 2*lvgm::rand01(),返回的區間為-1~1

       

      inline rtvar Perlin::noise(const rtvec& p)const
      {
          int i = floor(p.x());
          int j = floor(p.y());
          int k = floor(p.z());
          rtvar u = p.x() - i;
          rtvar v = p.y() - j;
          rtvar w = p.z() - k;
      
          rtvec list[2][2][2];
          for (int a = 0; a < 2; ++a)
              for (int b = 0; b < 2; ++b)
                  for (int c = 0; c < 2; ++c)
                  {
                      list[a][b][c] = _randomvalue[_perm_x[(i + a) & 255], _perm_y[(j + b) & 255], _perm_z[(k + c) & 255]];
      #ifdef listtest
                      if (list[a][b][c].x() < 0)stds cout << "list.x < 0 " << stds endl;
                      if (list[a][b][c].y() < 0)stds cout << "list.y < 0 " << stds endl;
                      if (list[a][b][c].z() < 0)stds cout << "list.z < 0 " << stds endl;
      #endif
                  }
          return perlin_interp(list, u, v, w);

      上述測試部分可能會輸出信息,因為list中有負值,然后Perlin向量插值就可能會是負值

       

      rtvar Perlin::perlin_interp(rtvec list[2][2][2], rtvar u, rtvar v, rtvar w)
      {
          rtvar uu = u*u*(3 - 2 * u);
          rtvar vv = v*v*(3 - 2 * v);
          rtvar ww = w*w*(3 - 2 * w);
      
          rtvar accumulate = 0;
          for (int i = 0; i < 2; ++i)
              for (int j = 0; j < 2; ++j)
                  for (int k = 0; k < 2; ++k)
                  {
                      rtvec weight(u - i, v - j, w - k);
                      accumulate +=
                          (i*uu + (1 - i) * (1 - uu))*
                          (j*vv + (1 - j) * (1 - vv))*
                          (k*ww + (1 - k) * (1 - ww))*
                          lvgm::dot(list[i][j][k], weight);
      #ifdef accumulatetest
                      if (accumulate < 0)stds cout << "accumulate < 0 " << stds endl;
      #endif
                  }
          return (accumulate);
      }

       

      ****************************** 為什么是“錯”的 ***************************************

       如果noise返回一個負值,那么

      rtvec noise_texture::value(rtvar u, rtvar v, const rtvec& p)const
          {
          return rtvec(1., 1., 1.) *_noise.noise(p);
          }

      它返回的就是一個負值

      bool lambertian::scatter(const ray& rIn, const hitInfo& info, rtvec& attenuation, ray& scattered)const
          {
          rtvec target = info._p + info._n + lvgm::random_unit_sphere();
          scattered = ray{ info._p, target - info._p };
          attenuation = _albedo->value(0.,0.,info._p);
          return true;
          }

      scatter傳出去的attenuation就是負值

      主函數中

      錯誤信息會輸出成功,lerp函數返回含有負值的向量

      gamma校正負值開根號為出現無窮

      你的圖像文件數據讀取會報錯!

      ****************************** 插曲結束 ***************************************

       

      那么如果把隨機生成函數改為

      rtvec * Perlin::perlin_generate()
      {
          rtvec * p = new rtvec[256];
          for (int i = 0; i < 256; ++i)
              p[i] = rtvec(abs(-1 + 2 * lvgm::rand01()), abs(-1 + 2 * lvgm::rand01()), abs(-1 + 2 * lvgm::rand01())).ret_unitization();
          return p;
      }

      還不行,因為noise會返回負值,那么我們把Perlin插值的返回值改為正值即可

      rtvar Perlin::perlin_interp(rtvec list[2][2][2], rtvar u, rtvar v, rtvar w)
      {
          rtvar uu = u*u*(3 - 2 * u);
          rtvar vv = v*v*(3 - 2 * v);
          rtvar ww = w*w*(3 - 2 * w);
      
          rtvar accumulate = 0;
          for (int i = 0; i < 2; ++i)
              for (int j = 0; j < 2; ++j)
                  for (int k = 0; k < 2; ++k)
                  {
                      rtvec weight(u - i, v - j, w - k);
                      accumulate +=
                          (i*uu + (1 - i) * (1 - uu))*
                          (j*vv + (1 - j) * (1 - vv))*
                          (k*ww + (1 - k) * (1 - ww))*
                          lvgm::dot(list[i][j][k], weight);
      #ifdef accumulatetest
                      if (accumulate < 0)stds cout << "accumulate < 0 " << stds endl;
      #endif
                  }
          return abs(accumulate);        //!!!
      }

       

      那么我們的圖片將是這個樣子的

        

      如果我們不改動隨機數生成器,只保證noise函數最后的返回值為正值

      那么也是上面那個圖

       

      改動間還得到了如下圖:

       

      上面兩個圖是因為noise函數中list的值每次取得都和z有關,所以造成了上述線條現象

      不小心重新抄寫的時候將^寫成了逗號,不過改為^也是錯,因為不管怎么選擇下標,該數組中的元素值始終都是-1~1

      真正的解法是value取值的時候對noise的返回值做處理

      下面是Perlin.hpp

       

      /// Perlin.hpp
      
      // -----------------------------------------------------
      // [author]        lv
      // [begin ]        2019.1
      // [brief ]        the Perlin-class for the ray-tracing project
      //                from the 《ray tracing the next week》
      // -----------------------------------------------------
      
      
      #pragma once
      
      namespace rt
      {
      class Perlin
          {
      public:
          inline rtvar noise(const rtvec& p)const;
      
          inline rtvar turb(const rtvec& p, int depth) const;
      
          inline rtvec* randomvalue()const { return _randomvalue; }
      
          inline int* perm_x()const { return _perm_x; }
      
          inline int* perm_y()const { return _perm_y; }
      
          inline int* perm_z()const { return _perm_z; }
      
      public:
      
          static rtvec * perlin_generate();
      
          static void permute(int * p, int n);
      
          static int * perlin_generate_perm();
      
          static rtvar perlin_interp(rtvec list[2][2][2], rtvar u, rtvar v, rtvar w);
      
      private:
          static rtvec * _randomvalue;
      
          static int * _perm_x;
      
          static int * _perm_y;
      
          static int * _perm_z;
          };
      
      
      
      
      rtvec * Perlin::_randomvalue = Perlin::perlin_generate();
      
      int * Perlin::_perm_x = Perlin::perlin_generate_perm();
      
      int * Perlin::_perm_y = Perlin::perlin_generate_perm();
      
      int * Perlin::_perm_z = Perlin::perlin_generate_perm();
      
      rtvec * Perlin::perlin_generate()
          {
          rtvec * p = new rtvec[256];
          for (int i = 0; i < 256; ++i)
              p[i] = rtvec(-1 + 2 * lvgm::rand01(), -1 + 2 * lvgm::rand01(), -1 + 2 * lvgm::rand01()).ret_unitization();
          return p;
          }
      
      int* Perlin::perlin_generate_perm()
          {
          int * p = new int[256];
          for (int i = 0; i < 256; ++i)    p[i] = i;
          permute(p, 256);
          return p;
          }
      
      void Perlin::permute(int* p, int n)
          {
          for (int i = n - 1; i; i--)
              {
              int tar = int(lvgm::rand01() * (i + 1));
              stds swap(p[i], p[tar]);
              }
          }
      
      rtvar Perlin::turb(const rtvec& p, int depth = 7) const 
          {
          rtvar accumulate = 0;
          rtvec t = p;
          rtvar weight = 1.0;
          for (int i = 0; i < depth; i++) 
              {
              accumulate += weight*noise(t);
              weight *= 0.5;
              t *= 2;
              }
          return abs(accumulate);
          }
      
      inline rtvar Perlin::noise(const rtvec& p)const
          {
          int i = floor(p.x());
          int j = floor(p.y());
          int k = floor(p.z());
          rtvar u = p.x() - i;
          rtvar v = p.y() - j;
          rtvar w = p.z() - k;
      
          rtvec list[2][2][2];
          for (int a = 0; a < 2; ++a)
              for (int b = 0; b < 2; ++b)
                  for (int c = 0; c < 2; ++c)
                      {
                      list[a][b][c] = _randomvalue[_perm_x[(i + a) & 255] ^ _perm_y[(j + b) & 255] ^ _perm_z[(k + c) & 255]];
      #ifdef listtest
                      if (list[a][b][c].x() < 0)stds cout << "list.x < 0 " << stds endl;
                      if (list[a][b][c].y() < 0)stds cout << "list.y < 0 " << stds endl;
                      if (list[a][b][c].z() < 0)stds cout << "list.z < 0 " << stds endl;
      #endif
                      }
          return perlin_interp(list, u, v, w);
          }
      
      rtvar Perlin::perlin_interp(rtvec list[2][2][2], rtvar u, rtvar v, rtvar w)
          {
      #ifdef uvwtest
          if (u < 0)stds cout << "u < 0 " << stds endl;
          if (v < 0)stds cout << "v < 0 " << stds endl;
          if (w < 0)stds cout << "w < 0 " << stds endl;
          if (u > 1)stds cout << "u > 1 " << stds endl;
          if (v > 1)stds cout << "v > 1 " << stds endl;
          if (w > 1)stds cout << "w > 1 " << stds endl;
      #endif
          rtvar uu = u*u*(3 - 2 * u);
          rtvar vv = u*v*(3 - 2 * v);
          rtvar ww = u*w*(3 - 2 * w);
      
          rtvar accumulate = 0;
          for (int i = 0; i < 2; ++i)
              for (int j = 0; j < 2; ++j)
                  for (int k = 0; k < 2; ++k)
                      {
                      rtvec weight(u - i, v - j, w - k);
                      accumulate +=
                          (i*uu + (1 - i) * (1 - uu))*
                          (j*vv + (1 - j) * (1 - vv))*
                          (k*ww + (1 - k) * (1 - ww))*
                          lvgm::dot(list[i][j][k], weight);
      #ifdef accumulatetest
                      if (accumulate < 0)stds cout << "accumulate < 0 " << stds endl;
      #endif
                      }
          return accumulate;        
          }
      
      }
      Perlin.hpp

       

        以及noise_texture.hpp中的value函數,如下:

      方可解決noise中返回為負的情況,_scale 為 5 的時候做出的圖如下:

       

       

       

       同樣,我們可以將光線追蹤提高圖片質量的慣用伎倆——采樣,用在噪聲值生成上面,即:使用具有多個相加頻率的復合噪聲。 這通常稱為turbulence

       

      用turb函數來代替noise函數,編者在turb返回的時候取了絕對值,而noise中的負值任由不管,不知為何。。

       

      得到如下圖:

       

      既然,編者已經將turb返回值取了絕對值,我們大可試一下之前的value函數

       

      _scale 為 5 時候

       

       

      看著有點密集,和書上的不太像,把_scale調為3,得到如下圖,看著差不多了

       

      程序紋理的入門是大理石紋理, 基本思想是使顏色與正弦函數成比例,并使用turbulence來調整相位,能使條紋起伏

       

       y = sin(wx + φ)

      _scale就是w值,我實在調不出來書上的紋理

       

       我把_scale的值調成6.3,結果如下:

       

       _scale 值越大,圖像上的正弦曲線波動幅度越小

      如果誰調整出來書上的_scale值了,請于下方評論區留言

       

      我們不妨把value函數中的turb改為原來的noise

       

       

      則得到下面這幅圖

      可以看到比較明顯的格塊狀,所以turb還是好一點

       

      今天就到這兒了,感謝您的閱讀,生活愉快~

       

      posted @ 2019-01-19 12:00  林-兮  閱讀(1713)  評論(6)    收藏  舉報
      主站蜘蛛池模板: 黄色段片一区二区三区| 免费现黄频在线观看国产| 久久久久中文伊人久久久| 国产精品不卡区一区二| 日韩有码av中文字幕| 国产精品成| 免费无码肉片在线观看| 国产成人精品a视频一区| 亚洲a人片在线观看网址| 国产喷水1区2区3区咪咪爱AV| 亚洲精品中文字幕一区二| 欧美最猛黑人xxxx| 日韩人妻无码精品专区综合网 | 99精品久久毛片a片| 亚洲蜜臀av乱码久久| 国产精品久久久久久福利69堂| 亚洲αⅴ无码乱码在线观看性色 | 亚洲综合国产一区二区三区| 变态另类视频一区二区三区| 中国熟女仑乱hd| 久久精品国产99国产精品严洲| 亚洲无人区码一二三四区| 国产中文字幕一区二区| 中文字幕日本六区小电影| 四虎永久在线精品8848a| 国产AV影片麻豆精品传媒| 日本黄页网站免费观看| 亚洲av综合久久成人网| 天天躁日日摸久久久精品| 国产在线一区二区不卡| 日韩V欧美V中文在线| 久久精品国产99久久久古代| 丁香婷婷综合激情五月色| 亚洲精品日本久久一区二区三区| 动漫AV纯肉无码AV电影网| 亚洲一区二区三区av激情| 色爱无码av综合区| 久久夜色国产噜噜亚洲av| 亚洲精品一区二区制服| 亚洲熟妇自偷自拍另类| 亚洲第一二三区日韩国产|