[MetalKit]28-Using-MetalKit-part-2-3^2使用MetalKit18

蘋果API搬運工發表於2017-12-14

本系列文章是對 metalkit.org 上面MetalKit內容的全面翻譯和學習.

MetalKit系統文章目錄


是的,正如標題所示,我們又有一個和數學有關的帖子了.有一天我在想,當我們通勤時間長達一小時左右,沒有網際網路沒有膝上型電腦,只有一臺iPad時,我們能做什麼.幸運的是,現在iPad有了神奇的Swift Playgrounds應用了.

讓我們以一個全新的playground開始,只執行基本的計算核心.因為當前版本的Swift Playgrounds不支援編輯Auxiliary Source Files輔助資原始檔,就是我們通常存放SwiftMetal檔案的地方,所以我們將不得不在playground主頁面寫程式碼,還好不太複雜.我們要做的是修改我們的MetalView初始化器,給它輸入一個額外的引數-我們的著色器/核心程式碼.然後我們開始生成程式碼,只需給這個長的字串新增幾行就好.

讓我們以一個亮藍色的背景顏色開始:

let shader =
"#include <metal_stdlib>\n" +
"using namespace metal;" +
"kernel void k(texture2d<float,access::write> o[[texture(0)]]," +
"              uint2 gid[[thread_position_in_grid]]) {" +
"   float3 color = float3(0.5, 0.8, 1.0);" +
"   o.write(float4(color, 1.0), gid);" +
"}"
複製程式碼

如果你現在執行playground,輸出影像會像這樣:

chapter18_1.png

下一步,我們繪製一個漸變.我們將當前畫素座標劃分到螢幕尺寸上,得到UV-一對 (0-1) 之間的浮點數.然後將固定的顏色與Y相乘-UV的垂直分量會給我們一個漸變:

"   int width = o.get_width();" +
"   int height = o.get_height();" +
"   float2 uv = float2(gid) / float2(width, height);" +
"   color *= uv.y;" +
複製程式碼

輸出影像會像這樣:

chapter18_2.png

接下來我們換個更好的背景.一個看起來像日落的平滑漸變.我們可以用mix來混合顏色.我們告訴函式垂直混合顏色,用Y分量來切換顏色:

"   float3 color = mix(float3(1.0, 0.6, 0.1), float3(0.5, 0.8, 1.0), sqrt(1 - uv.y));" +
複製程式碼

輸出影像會像這樣:

chapter18_3.png

從這裡,我們就能畫一個黑色的洞.我將用距離函式(length)在螢幕中間 (0.5, 0.5) 畫黑色來實現,並在外面新增越來越多的顏色,直到螢幕角落達到最大值.把最後一行替換為:

"   float2 q = uv - float2(0.5);" +
"   color *= length(q);" +
複製程式碼

輸出影像會像這樣:

chapter18_4.png

下一步,我們用smootstep來繪製一個圓形,裡面填充上黑色,外面藍色,在r(r + 0.01) 之間用混合色.用下面替換最後一行:

"   float r = 0.2;" +
"   color *= smoothstep(r, r + 0.01, length(q));" +
複製程式碼

輸出影像會像這樣:

chapter18_5.png

如果我們對圓形邊緣不滿,可以用數學函式比如cosatan2讓它 凹凸不平 .我們產生了9個凸起(頻率),凸起高度(振幅)是0.1:

"   float r = 0.2 + 0.1 * cos(atan2(q.x, q.y) * 9.0);" +
複製程式碼

輸出影像會像這樣:

chapter18_6.png

新增X座標到餘弦相位,產生一個彎曲效果:

"   float r = 0.2 + 0.1 * cos(atan2(q.x, q.y) * 9.0 + 20.0 * q.x);" +
複製程式碼

輸出影像會像這樣:

chapter18_7.png

你可以新增一個很小的值如0.1到餘弦中來旋轉它們:

"   float r = 0.2 + 0.1 * cos(atan2(q.x, q.y) * 9.0 + 20.0 * q.x + 1.0);" +
複製程式碼

輸出影像會像這樣:

chapter18_8.png

你覺得這看起來像棕櫚樹的樹冠了麼,我也覺得像!我們可以用abs來畫樹幹,這個函式給我們水平/垂直距離而不是歐幾里得距離(對一個給定的點)如長度,所以讓我們用X距離在原有基礎上再新增幾行程式碼(我們將重用rcolor):

"   r = 0.015;" +
"   color *= smoothstep(r, r + 0.002, abs(q.x));" +
複製程式碼

輸出影像會像這樣:

chapter18_9.png

我們可以用另一個Y軸的smoothstep來移除不需要的樹幹部分:

"   color *= 1.0 - (1.0 - smoothstep(r, r + 0.002, abs(q.x))) * smoothstep(0.0, 0.1, q.y);" +
複製程式碼

輸出影像會像這樣:

chapter18_10.png

因為樹冠和樹幹都用到了q,修改這個值將會移動所有的影像:

"   float2 q = uv - float2(0.67, 0.29);" +
複製程式碼

輸出影像會像這樣:

chapter18_11.png

通過引入一個sin函式我們可以彎曲樹幹.頻率太小彎曲不夠,但頻率太高又彎曲太多,所以2.0正好. 2.5振幅將樹幹的基準向螢幕邊緣移動到正好的距離(把符號從 + 改成 - 會把基準向另一邊移動):

"   color *= 1.0 - (1.0 - smoothstep(r, r + 0.002, abs(q.x - 0.25 * sin(2.0 * q.y)))) * smoothstep(0.0, 0.1, q.y);" +
複製程式碼

輸出影像會像這樣:

chapter18_12.png

樹幹又太光滑了.再用cos來新增些不規則變化.高的頻率低的振幅看上去正是我們想要的:

"   r = 0.015 + 0.002 * cos (120.0 * q.y);" +
複製程式碼

輸出影像會像這樣:

chapter18_13.png

還有,樹幹根部通常會改變地面附近的形狀,所以exp函式正是我們需要的,因為他在開始時增長緩慢,然後向著天空快速增長.我們用衰減因子為 -50.0:

"   r = 0.015 + 0.002 * cos (120.0 * q.y) + exp(-50.0 * (1.0 - uv.y));" +
複製程式碼

輸出影像會像這樣:

chapter18_14.png

我們可以用sqrt來得到一個更大的數(當用於小數時),用來增強第二種顏色的表現.日落即將完成:

"   float3 color = mix(float3(1.0, 0.6, 0.1), float3(0.5, 0.8, 1.0), sqrt(1 - uv.y));" +
複製程式碼

最終iPad上的圖片應該看起來像:

chapter18_15.png

總結,我們看到了如何用sqrt來塑造形狀的過渡,用cos來在形狀在建立凸起和凹陷,用exp來創造麴線,用smoothstep來處理閾值/臨界點,abs來獲得對稱性,mix來獲得混合.又在通勤路上了?為什麼不來看看這個漂亮的三葉草是怎麼建立的呢:

chapter18_16.png

我要再次感謝Inigo Quilez,因為他激勵我寫下更多的關於用數學繪圖的文章.本教程中的數學都歸功於他.

原始碼source code 已釋出在Github上.

下次見!

相關文章