2016-09-18 51 views
0

我遇到了this site並開始學習SDF一段時間了。但是我還是不太明白我的意思這後面的代碼:將GLSL功能轉換爲金屬

float pMod1(inout float p, float size) { 
    float halfsize = size*0.5; 
    float c = floor((p + halfsize)/size); 
    p = mod(p + halfsize, size) - halfsize; 
    return c; 
} 

我將它轉換爲我Metal代碼:

#define _inout(T) T 
... 
float pMod1(_inout (float) p, float size) { 
    float halfsize = size*0.5; 
    float c = floor((p + halfsize)/size); 
    p = mod(p + halfsize, size) - halfsize; 
    return c; 
} 

,但沒有得到預期的結果。但是,如果我更改爲

#define _inout(T) T 
... 
float pMod1(_inout (float) p, float size) { 
    float halfsize = size*0.5; 
    float c = floor((p + halfsize)/size); 
    p = mod(p + halfsize, size) - halfsize; 
    return p; // <-- change from c to p 
} 

然後我得到我的預期。

  1. 我懷疑我的轉換方式inout不完全正確。我從Shadertoy代碼中借用了它,但我不能說服它真的以這種方式工作。

  2. c如何有用?從側面它的代碼註釋:

Many of the operators partition space into cells. An identifier or cell index is returned, if possible. This return value is intended to be optionally used e.g. as a random seed to change parameters of the distance functions inside the cells.

我不明白它的真正含義。有人可以建議一些例子如何使用細胞索引?

UPDATE1:

我改變的代碼:

float pMod1(thread float &p, float size) { 
    float halfsize = size*0.5; 
    float c = floor((p + halfsize)/size); 
    p = mod(p + halfsize, size) - halfsize; 
    return c; 
} 

,現在我得到另一個錯誤信息:

fatal error: unexpectedly found nil while unwrapping an Optional value

從這一行:

command_encoder.setComputePipelineState(cps) 

這裏是MetaView.swift整個代碼:

import MetalKit 

public class MetalView: MTKView, NSWindowDelegate { 

    var queue: MTLCommandQueue! = nil 
    var cps: MTLComputePipelineState! = nil 

    var timer: Float = 0 
    var timerBuffer: MTLBuffer! 

    var mousexBuffer: MTLBuffer! 
    var mouseyBuffer: MTLBuffer! 
    var pos: NSPoint! 
    var floatx: Float! 
    var floaty: Float! 

    required public init(coder: NSCoder) { 
     super.init(coder: coder) 
     self.framebufferOnly = false 
     device = MTLCreateSystemDefaultDevice() 
     registerShaders() 
    } 


    override public func drawRect(dirtyRect: NSRect) { 
     super.drawRect(dirtyRect) 
     if let drawable = currentDrawable { 
      let command_buffer = queue.commandBuffer() 
      let command_encoder = command_buffer.computeCommandEncoder() 
      command_encoder.setComputePipelineState(cps) ///////<-- This line throw an error. 
      command_encoder.setTexture(drawable.texture, atIndex: 0) 
      command_encoder.setBuffer(timerBuffer, offset: 0, atIndex: 1) 
      command_encoder.setBuffer(mousexBuffer, offset: 0, atIndex: 2) 
      command_encoder.setBuffer(mouseyBuffer, offset: 0, atIndex: 3) 
      update() 
      let threadGroupCount = MTLSizeMake(8, 8, 1) 
      let threadGroups = MTLSizeMake(drawable.texture.width/threadGroupCount.width, drawable.texture.height/threadGroupCount.height, 1) 
      command_encoder.dispatchThreadgroups(threadGroups, threadsPerThreadgroup: threadGroupCount) 
      command_encoder.endEncoding() 
      command_buffer.presentDrawable(drawable) 
      command_buffer.commit() 
     } 
    } 

    func registerShaders() { 
     queue = device!.newCommandQueue() 
     do { 
      let library = device!.newDefaultLibrary()! 
      let kernel = library.newFunctionWithName("compute")! 
      timerBuffer = device!.newBufferWithLength(sizeof(Float), options: []) 
      mousexBuffer = device!.newBufferWithLength(sizeof(Float), options: []) 
      mouseyBuffer = device!.newBufferWithLength(sizeof(Float), options: []) 
      cps = try device!.newComputePipelineStateWithFunction(kernel) 
     } catch let e { 
      Swift.print("\(e)") 
     } 
    } 

    func update() { 
     timer += 0.01 
     var bufferPointer = timerBuffer.contents() 
     memcpy(bufferPointer, &timer, sizeof(Float)) 
     bufferPointer = mousexBuffer.contents() 
     memcpy(bufferPointer, &floatx, sizeof(NSPoint)) 
     bufferPointer = mouseyBuffer.contents() 
     memcpy(bufferPointer, &floaty, sizeof(NSPoint)) 
    } 

    override public func mouseDragged(event: NSEvent) { 
     pos = convertPointToLayer(convertPoint(event.locationInWindow, fromView: nil)) 
     let scale = layer!.contentsScale 
     pos.x *= scale 
     pos.y *= scale 
     floatx = Float(pos.x) 
     floaty = Float(pos.y) 
     debugPrint("Hello",pos.x,pos.y) 
    } 
} 

UPDATE2: 我懷疑錯誤UPDATE1是因爲我分裂Metal文件的方式。所以我簡化它所有的功能複製到1個Metal文件,現在我產生新的錯誤:

float pMod1(thread float &p, float size) { 
    float halfsize = size*0.5; 
    float c = floor((p + halfsize)/size); 
    p = mod(p + halfsize, size) - halfsize; 
    return c; 
} 

static float map(float3 p) 
{ 
    float size = 10.0; 

    p.x = pMod1(p.x,size);/////<--- this produce the error. 

    float box = fBox(p, float3(1)); 
    float sphere = length(p - float3(1)) - 1; 
    float d = min(box,sphere); 

    float guard = -fBoxCheap(p, float3(size*0.5)); 
    guard = abs(guard) + size*0.1; 

    return min(d,guard); 
} 

錯誤:

Call to pMod1 is ambiguous

回答

2

在金屬中的同等功能是

float pMod1(thread float &p, float size) { 
    float halfsize = size*0.5; 
    float c = floor((p + halfsize)/size); 
    p = mod(p + halfsize, size) - halfsize; 
    return c; 
} 

在爲了修改一個參數,你需要通過引用來傳遞它,就像在C++中一樣。在金屬,你還需要明確限定它作爲thread地址空間中(而不是constantthreadgroup等)

pMod家庭在hg_sdf功能的目的是「摺疊」空間,讓您創建重複定期重複的對象的副本。c值類似於對象索引,指示對象所在的摺疊空間的哪個「分區」。除非您調整對象外觀(通過應用不同的材質或添加表面細節等),否則您可以忽略它。

這一切在Johann Körndorfer's talk here中都有詳細的描述。

更新

爲了調用函數,你需要創建一個臨時變量來存儲您要修改的攪和出部件,因爲你不能在金屬引用傳遞攪和矢量分量。

float px = p.x; 
float c = pMod1(px, size); 
p.x = px; 

由於px被引用傳遞,p.x現在包含已寫入pxpMod1值。

+0

我改變你的建議,但有新的錯誤。請檢查我的** Update1 **的所有細節。 – sooon

+0

不幸的是,你不能通過引用傳遞暈頭轉向的組件。你需要將'p.x'複製到一個局部變量中,將_that_傳入函數,然後在函數調用後將_back_賦值給'p.x'。另外,不要將函數的結果賦給'p.x';返回值是單元格索引,並且與要在參考參數中返回的值完全不同。 – warrenm

+0

因爲返回'c',我怎樣才能在'pMod()'函數中使用'thread float&p'的值?我檢查他們使用相同功能的視頻,但不知何故,他們收回了值「p」。 – sooon