本系列文章是對 metalkit.org 上面MetalKit內容的全面翻譯和學習.git
MetalKit系統文章目錄github
是的,正如標題所示,咱們又有一個和數學有關的帖子了.有一天我在想,當咱們通勤時間長達一小時左右,沒有互聯網沒有筆記本電腦,只有一臺iPad
時,咱們能作什麼.幸運的是,如今iPad
有了神奇的Swift Playgrounds
應用了.swift
讓咱們以一個全新的playground開始,只運行基本的計算內核.由於當前版本的Swift Playgrounds
不支持編輯Auxiliary Source Files輔助資源文件
,就是咱們一般存放Swift
和Metal
文件的地方,因此咱們將不得不在playground主頁面寫代碼,還好不太複雜.咱們要作的是修改咱們的MetalView
初始化器,給它輸入一個額外的參數-咱們的着色器/內核代碼.而後咱們開始生成代碼,只需給這個長的字符串添加幾行就好.函數
讓咱們以一個亮藍色的背景顏色開始:post
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,輸出圖像會像這樣:學習
下一步,咱們繪製一個漸變.咱們將當前像素座標劃分到屏幕尺寸上,獲得UV-一對 (0-1) 之間的浮點數.而後將固定的顏色與Y相乘-UV
的垂直份量會給咱們一個漸變:ui
" int width = o.get_width();" +
" int height = o.get_height();" +
" float2 uv = float2(gid) / float2(width, height);" +
" color *= uv.y;" +
複製代碼
輸出圖像會像這樣:spa
接下來咱們換個更好的背景.一個看起來像日落的平滑漸變.咱們能夠用mix來混合顏色.咱們告訴函數垂直混合顏色,用Y份量來切換顏色:翻譯
" float3 color = mix(float3(1.0, 0.6, 0.1), float3(0.5, 0.8, 1.0), sqrt(1 - uv.y));" +
複製代碼
輸出圖像會像這樣:3d
從這裏,咱們就能畫一個黑色的洞.我將用距離函數(length)在屏幕中間 (0.5, 0.5) 畫黑色來實現,並在外面添加愈來愈多的顏色,直到屏幕角落達到最大值.把最後一行替換爲:
" float2 q = uv - float2(0.5);" +
" color *= length(q);" +
複製代碼
輸出圖像會像這樣:
下一步,咱們用smootstep來繪製一個圓形,裏面填充上黑色,外面藍色,在r和 (r + 0.01) 之間用混合色.用下面替換最後一行:
" float r = 0.2;" +
" color *= smoothstep(r, r + 0.01, length(q));" +
複製代碼
輸出圖像會像這樣:
若是咱們對圓形邊緣不滿,能夠用數學函數好比cos和atan2讓它 凹凸不平 .咱們產生了9個凸起(頻率),凸起高度(振幅)是0.1:
" float r = 0.2 + 0.1 * cos(atan2(q.x, q.y) * 9.0);" +
複製代碼
輸出圖像會像這樣:
添加X座標到餘弦相位,產生一個彎曲效果:
" float r = 0.2 + 0.1 * cos(atan2(q.x, q.y) * 9.0 + 20.0 * q.x);" +
複製代碼
輸出圖像會像這樣:
你能夠添加一個很小的值如0.1到餘弦中來旋轉它們:
" float r = 0.2 + 0.1 * cos(atan2(q.x, q.y) * 9.0 + 20.0 * q.x + 1.0);" +
複製代碼
輸出圖像會像這樣:
你以爲這看起來像棕櫚樹的樹冠了麼,我也以爲像!咱們能夠用abs來畫樹幹,這個函數給咱們水平/垂直距離而不是歐幾里得距離(對一個給定的點)如長度,因此讓咱們用X距離在原有基礎上再添加幾行代碼(咱們將重用r和color):
" r = 0.015;" +
" color *= smoothstep(r, r + 0.002, abs(q.x));" +
複製代碼
輸出圖像會像這樣:
咱們能夠用另外一個Y軸的smoothstep來移除不須要的樹幹部分:
" color *= 1.0 - (1.0 - smoothstep(r, r + 0.002, abs(q.x))) * smoothstep(0.0, 0.1, q.y);" +
複製代碼
輸出圖像會像這樣:
由於樹冠和樹幹都用到了q,修改這個值將會移動全部的圖像:
" float2 q = uv - float2(0.67, 0.29);" +
複製代碼
輸出圖像會像這樣:
經過引入一個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);" +
複製代碼
輸出圖像會像這樣:
樹幹又太光滑了.再用cos來添加些不規則變化.高的頻率低的振幅看上去正是咱們想要的:
" r = 0.015 + 0.002 * cos (120.0 * q.y);" +
複製代碼
輸出圖像會像這樣:
還有,樹幹根部一般會改變地面附近的形狀,因此exp函數正是咱們須要的,由於他在開始時增加緩慢,而後向着天空快速增加.咱們用衰減因子爲 -50.0:
" r = 0.015 + 0.002 * cos (120.0 * q.y) + exp(-50.0 * (1.0 - uv.y));" +
複製代碼
輸出圖像會像這樣:
咱們能夠用sqrt來獲得一個更大的數(當用於小數時),用來加強第二種顏色的表現.日落即將完成:
" float3 color = mix(float3(1.0, 0.6, 0.1), float3(0.5, 0.8, 1.0), sqrt(1 - uv.y));" +
複製代碼
最終iPad上的圖片應該看起來像:
總結,咱們看到了如何用sqrt來塑造形狀的過渡,用cos來在形狀在建立凸起和凹陷,用exp來創造麴線,用smoothstep來處理閾值/臨界點,abs來得到對稱性,mix來得到混合.又在通勤路上了?爲何不來看看這個漂亮的三葉草是怎麼建立的呢:
我要再次感謝Inigo Quilez,由於他激勵我寫下更多的關於用數學繪圖的文章.本教程中的數學都歸功於他.
源代碼source code 已發佈在Github上.
下次見!