2016-09-18 64 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