1. 程式人生 > >Book of Shaders 04 - 網格噪聲:Worley Noise

Book of Shaders 04 - 網格噪聲:Worley Noise

# 0x00 思路 假設要生成 4 個網格,可以先在空間中指定 4 個特徵點。對於每個畫素點,計算它到最近特徵點的距離,將這個距離當作結果值輸出。 ```c #ifdef GL_ES precision mediump float; #endif uniform vec2 u_resolution; void main() { vec2 st = gl_FragCoord.xy/u_resolution.xy; // 先指定 4 個特徵點 vec2 point[4]; point[0] = vec2(0.83, 0.75); point[1] = vec2(0.60, 0.07); point[2] = vec2(0.28, 0.64); point[3] = vec2(0.31, 0.26); // 計算畫素點到 4 個特徵點的最小距離 float m_dist = 1.0; for (int i = 0; i < 4; i++) { float dist = distance(st, point[i]); m_dist = min(m_dist, dist); } // 輸出結果 vec3 color = vec3(0.0); color += m_dist; gl_FragColor = vec4(color, 1.0); } ``` 效果展示: ![](https://img2020.cnblogs.com/blog/1890896/202010/1890896-20201004181539017-947699335.png) # 0x01 優化 迴圈對著色器很不友好,遍歷次數過多的迴圈會顯著降低著色器的效能。在 Steven Worley 發表的一篇論文[《A Cellular Texture Basis Function》](http://www.rhythmiccanvas.com/research/papers/worley.pdf)中描述了優化的方法。我們可以將空間分割成網格,每個網格對應一個特徵點,每個畫素點只計算到相鄰網格中的特徵點的距離。這樣,每個畫素點就只需要計算到九個特徵點的距離,它自身所在的網格的特徵點和相鄰的八個網格的特徵點。 ![](https://img2020.cnblogs.com/blog/1890896/202010/1890896-20201004181549653-1058228308.png) 另外,還可以改用每個網格的整數座標來構造隨機的特徵點。省去了手動指定特徵點的麻煩,同時也帶來了更多的隨機性。 ```c #ifdef GL_ES precision mediump float; #endif uniform vec2 u_resolution; vec2 random2(vec2 pos) { float x = dot(pos, vec2(127.1, 311.7)); float y = dot(pos, vec2(269.5, 183.3)); return fract(sin(vec2(x, y)) * 43758.5453); } void main() { vec2 st = gl_FragCoord.xy / u_resolution.xy; st *= 9.0; vec2 i = floor(st); vec2 f = fract(st); float m_dist = 1.0; for (int y = -1; y <= 1; y++) { for (int x = -1; x <= 1; x++) { // 相鄰格子 vec2 neighbor = vec2(float(x), float(y)); // 生成隨機特徵點 vec2 point = random2(i + neighbor); // 計算距離 float dist = length(neighbor + point - f); // 更新最短距離 m_dist = min(m_dist, dist); } } vec3 color = vec3(0.0); color += m_dist; gl_FragColor = vec4(color, 1.0); } ``` 效果展示: ![](https://img2020.cnblogs.com/blog/1890896/202010/1890896-20201004181603179-606239478.png) # 0x02 擴充套件 Inigo Quilez 寫了一篇文章,提出了他稱之為 [Voro Noise](https://www.iquilezles.org/www/articles/voronoise/voronoise.htm) 的噪聲,可以將常規噪聲和網格噪聲組合在一起。 在 Voro Noise 中,額外使用兩個引數,不妨稱之為 u 和 v。其中,u 用來決定最終的噪聲更像常規噪聲還是網格噪聲,簡單來說:當 u 接近 0 時,生成的噪聲更接近常規噪聲;當 u 接近 1 時,生成的噪聲更接近網格噪聲。v 提供類似常規噪聲中的線性插值和網格噪聲中最短距離值的功能(ps:最短距離的演算法是非連續的,在 iq 的另一篇文章 [Smooth Voronoi](https://www.iquilezles.org/www/articles/smoothvoronoi/smoothvoronoi.htm) 中提供瞭解決這一問題的辦法)。 ```c // 初版插值方法。其中,64 是一個連續性比較好的值,詳見 Smooth Voronoi 一文。 // float ww = pow( 1.0 - smoothstep(0.0, 1.414, sqrt(d)), 64.0 - 63.0 * v); // 在初版基礎上提高函式的階數以獲得更平滑的表現。 // 64.0 - 63.0 * v => 1.0 + 63.0 * pow(1.0 - v, 4.0) float ww = pow( 1.0 - smoothstep(0.0, 1.414, sqrt(d)), 1.0 + 63.0 * pow(1.0 - v, 4.0)); ``` 完整程式碼: ```c #ifdef GL_ES precision mediump float; #endif uniform vec2 u_resolution; uniform vec2 u_mouse; vec3 random3( vec2 p ) { float x = dot(p, vec2(127.1, 311.7)); float y = dot(p, vec2(269.5, 183.3)); float z = dot(p, vec2(419.2, 371.9)); return fract(sin(vec3(x, y, z)) * 43758.5453); } float voroNoise(in vec2 x, float u, float v ) { vec2 i = floor(x); vec2 f = fract(x); float k = 1.0 + 63.0 * pow(1.0 - v, 4.0); // 下面兩個引數用於計算加權平均值,wa 統計總值,wt 統計總單位數 float wa = 0.0; float wt = 0.0; // 擴大搜索範圍,進一步提高連續性 for (int y = -2; y <= 2; y++) { for (int x = -2; x <= 2; x++) { // 相鄰格子 vec2 neighbor = vec2(float(x), float(y)); // 隨機生成 point,其中 point.xy 表示特徵點,point.z 表示該點的灰度值 vec3 point = random3(i + neighbor) * vec3(u, u, 1.0); // 根據距離計算貢獻值 float dist = length(neighbor - f + point.xy); float ww = pow(1.0 - smoothstep(0.0, 1.414, dist), k); wa += point.z * ww; wt += ww; } } return wa/wt; } void main() { vec2 st = gl_FragCoord.xy/u_resolution.xy; st *= 10.0; // 用滑鼠位置控制 voroNoise 中的 u 和 v float n = voroNoise(st, u_mouse.x/u_resolution.x, u_mouse.y/u_resolution.y); gl_FragColor = vec4(vec3(n), 1.0); } ``` 效果展示: u 從 0 到 1 ![](https://img2020.cnblogs.com/blog/1890896/202010/1890896-20201004181618951-940316300.gif) v 從 0 到 1 ![](https://img2020.cnblogs.com/blog/1890896/202010/1890896-20201004181633083-1670461438.gif) uv 一起變化 ![](https://img2020.cnblogs.com/blog/1890896/202010/1890896-20201004181651668-1084879614.gif)
參考資料: - [1] [The Book of Shaders](https://thebookofshaders.com/) - [2] [Worley noise - Wikipedia](https://en.wikipedia.org/wiki/Worley_noise) - [3] [Voro noise - iq](https://www.iquilezles.org/www/articles/voronoise/voronoise.htm) - [4] [Smooth voronoi - iq](https://www.iquilezles.org/www/articles/smoothvoronoi/smoothvoro