[MetalKit]22-Using-MetalKit-part-15使用MetalKit15

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

MetalKit系統文章目錄git


第13部分末尾,咱們說過讓咱們的行星看起來更真實有兩種方法:添加紋理,或添加一些噪聲到plante顏色中.咱們在第14部分已經展現了添加噪聲.這周咱們看看紋理採樣.紋理很是有用,由於比起爲每一個頂點計算顏色,它能夠給表面提供更好的細節.github

讓咱們比第13部分Part 13開始,由於咱們再也不須要噪聲代碼了.首先,在MetalView.swift中移除mouseDown函數,咱們已經再也不須要它了.同時移除mouseBufferpos變量,同時移除代碼中對它們的引用.而後,建立一個新的紋理對象:swift

var texture: MTLTexture!
複製代碼

下一步,將這行(可能你已經在前面清理中移除過了):ide

commandEncoder.setBuffer(mouseBuffer, offset: 0, atIndex: 2)
複製代碼

替換爲:函數

commandEncoder.setTexture(texture, atIndex: 1)
複製代碼

同時改變timer的緩衝器索引,從1改成0:post

commandEncoder.setBuffer(timerBuffer, offset: 0, atIndex: 0)
複製代碼

我在Resources文件夾添加一張圖片名爲texture.jpg,你能夠用本身的圖片代替.讓咱們建立一個函數來加載並使用這張圖片做爲紋理:學習

func setUpTexture() {
    let path = NSBundle.mainBundle().pathForResource("texture", ofType: "jpg")
    let textureLoader = MTKTextureLoader(device: device!)
    texture = try! textureLoader.newTextureWithContentsOfURL(NSURL(fileURLWithPath: path!), options: nil)
}
複製代碼

下一步,在咱們的init函數調用這個函數:ui

override public init(frame frameRect: CGRect, device: MTLDevice?) {
    super.init(frame: frameRect, device: device)
    registerShaders()
    setUpTexture()
}
複製代碼

如今,清理咱們Shaders.metal中的內核,只保留下面幾行:編碼

kernel void compute(texture2d<float, access::write> output [[texture(0)]],
                    texture2d<float, access::read> input [[texture(1)]],
                    constant float &timer [[buffer(1)]],
                    uint2 gid [[thread_position_in_grid]])
{
    float4 color = input.read(gid);
    gid.y = input.get_height() - gid.y;
    output.write(color, gid);
}
複製代碼

你會首次注意到,咱們從 [[texture(1)]] 屬性拿到了input紋理,由於這個屬性就是咱們設置到命令編碼器裏時的索引.同時,訪問權限設置爲read.而後咱們將其讀取到color變量,然而,它倒是上下顛倒的.爲了修復這個問題,在下一行咱們爲每一個像素反轉Y軸.輸出圖片看起來應該像這樣:

chapter15_1.png

若是你打開圖片並與咱們的輸出比較,你會發現它已經旋轉到正確朝向了.下一步,咱們要找回咱們的行星及周圍的黑暗天空.用下面一大塊代碼替換output行:

int width = input.get_width();
int height = input.get_height();
float2 uv = float2(gid) / float2(width, height);
uv = uv * 2.0 - 1.0;
float radius = 0.5;
float distance = length(uv) - radius;
output.write(distance < 0 ? color : float4(0), gid);
複製代碼

這段代碼看起來很熟悉,由於前些章節咱們已經討論過如何建立行星及周圍的黑色空間.輸出圖片看起來應該像這樣:

chapter15_2.png
如今很好!下一步咱們讓行星轉動起來.用下面一大塊代碼替換 output行:

uv = fmod(float2(gid) + float2(timer * 100, 0), float2(width, height));
color = input.read(uint2(uv));
output.write(distance < 0 ? color : float4(0), gid);
複製代碼

這段代碼看起來又很熟悉,由於前些章節咱們已經討論過如何使用timer讓行星動起來.輸出圖片看起來應該像這樣:

chapter15_3.gif

這看起來就比較傻了!輸出的圖像看起來像一我的舉着火把緊貼牆壁走在黑暗的洞穴裏.用下面的代碼替換最後三行:

uv = uv * 2;
radius = 1;
constexpr sampler textureSampler(coord::normalized, address::repeat, min_filter::linear, mag_filter::linear, mip_filter::linear );
float3 norm = float3(uv, sqrt(1.0 - dot(uv, uv)));
float pi = 3.14;
float s = atan2( norm.z, norm.x ) / (2 * pi);
float t = asin( norm.y ) / (2 * pi);
t += 0.5;
color = input.sample(textureSampler, float2(s + timer * 0.1, t));
output.write(distance < 0 ? color : float4(0), gid);
複製代碼

首先,咱們縮小紋理尺寸到原來的一半,並設置半徑爲1來匹配行星對象的尺寸和紋理尺寸.而後,神奇的地方來了.讓咱們引入sampler採樣器.sampler採樣器是一個包含了各類渲染狀態的對象,讓紋理來配置:座標,尋址方式(這裏設置爲repeat)和過濾方法(設置爲linear).下一步,計算球面上每一個點的normal法線.最後,咱們用採樣來計算color而不是像前面同樣直接讀取.還有一件事要作,在內核參數列表中,讓咱們也把紋理訪問權限由read改成sample.將這一行:

texture2d<float, access::read> input [[texture(1)]],
複製代碼

替換爲:

texture2d<float, access::sample> input [[texture(1)]],
複製代碼

輸出圖像看起來應該像這樣:

chapter15_4.gif

這就是咱們所說的真實的行星表面!還要感謝 Chris的幫助. 源代碼source code 已發佈在Github上.

下次見!

相關文章
相關標籤/搜索