[MetalKit]Using MetalKit part 2*3^2使用MetalKit18

本系列文章是對 http://metalkit.org 上面MetalKit內(nèi)容的全面翻譯和學(xué)習(xí).

MetalKit系統(tǒng)文章目錄


是的,正如標(biāo)題所示,我們又有一個和數(shù)學(xué)有關(guān)的帖子了.有一天我在想,當(dāng)我們通勤時間長達(dá)一小時左右,沒有互聯(lián)網(wǎng)沒有筆記本電腦,只有一臺iPad時,我們能做什么.幸運(yùn)的是,現(xiàn)在iPad有了神奇的Swift Playgrounds應(yīng)用了.

讓我們以一個全新的playground開始,只運(yùn)行基本的計算內(nèi)核.因為當(dāng)前版本的Swift Playgrounds不支持編輯Auxiliary Source Files輔助資源文件,就是我們通常存放SwiftMetal文件的地方,所以我們將不得不在playground主頁面寫代碼,還好不太復(fù)雜.我們要做的是修改我們的MetalView初始化器,給它輸入一個額外的參數(shù)-我們的著色器/內(nèi)核代碼.然后我們開始生成代碼,只需給這個長的字符串添加幾行就好.

讓我們以一個亮藍(lán)色的背景顏色開始:

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);" +
"}"

如果你現(xiàn)在運(yùn)行playground,輸出圖像會像這樣:

chapter18_1.png

下一步,我們繪制一個漸變.我們將當(dāng)前像素坐標(biāo)劃分到屏幕尺寸上,得到UV-一對(0-1)之間的浮點數(shù).然后將固定的顏色與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來混合顏色.我們告訴函數(shù)垂直混合顏色,用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

從這里,我們就能畫一個黑色的洞.我將用距離函數(shù)(length)在屏幕中間(0.5, 0.5)畫黑色來實現(xiàn),并在外面添加越來越多的顏色,直到屏幕角落達(dá)到最大值.把最后一行替換為:

"   float2 q = uv - float2(0.5);" +
"   color *= length(q);" +

輸出圖像會像這樣:

chapter18_4.png

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

"   float r = 0.2;" +
"   color *= smoothstep(r, r + 0.01, length(q));" +

輸出圖像會像這樣:

chapter18_5.png

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

"   float r = 0.2 + 0.1 * cos(atan2(q.x, q.y) * 9.0);" +

輸出圖像會像這樣:

chapter18_6.png

添加X坐標(biāo)到余弦相位,產(chǎn)生一個彎曲效果:

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

輸出圖像會像這樣:

chapter18_7.png

你可以添加一個很小的值如0.1到余弦中來旋轉(zhuǎn)它們:

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

輸出圖像會像這樣:

chapter18_8.png

你覺得這看起來像棕櫚樹的樹冠了么,我也覺得像!我們可以用abs來畫樹干,這個函數(shù)給我們水平/垂直距離而不是歐幾里得距離(對一個給定的點)如長度,所以讓我們用X距離在原有基礎(chǔ)上再添加幾行代碼(我們將重用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函數(shù)我們可以彎曲樹干.頻率太小彎曲不夠,但頻率太高又彎曲太多,所以2.0正好. 2.5振幅將樹干的基準(zhǔn)向屏幕邊緣移動到正好的距離(把符號從+改成-會把基準(zhǔn)向另一邊移動):

"   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來添加些不規(guī)則變化.高的頻率低的振幅看上去正是我們想要的:

"   r = 0.015 + 0.002 * cos (120.0 * q.y);" +

輸出圖像會像這樣:

chapter18_13.png

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

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

輸出圖像會像這樣:

chapter18_14.png

我們可以用sqrt來得到一個更大的數(shù)(當(dāng)用于小數(shù)時),用來增強(qiáng)第二種顏色的表現(xiàn).日落即將完成:

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

最終iPad上的圖片應(yīng)該看起來像:

chapter18_15.png

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

chapter18_16.png

我要再次感謝Inigo Quilez,因為他激勵我寫下更多的關(guān)于用數(shù)學(xué)繪圖的文章.本教程中的數(shù)學(xué)都?xì)w功于他.

源代碼source code 已發(fā)布在Github上.
下次見!

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容