将GLSL函数转换为Metal

时间:2016-09-18 13:37:41

标签: c++ glsl metal

我遇到this site并开始研究SDF一段时间了。但是我仍然不明白这个代码背后的想法:

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如何有用?在旁边的代码中评论:

  3.   

    许多运营商将空间划分为单元格。标识符   如果可能,返回或单元格索引。这个返回值是   意图任选地使用,例如,作为随机种子来改变   细胞内距离函数的参数。

    我不明白它的真正含义。有人可以提出一些如何使用细胞指数的例子吗?

    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;
    }
    

    现在我收到另一条错误消息:

      

    致命错误:在解包可选值时意外发现nil

    从这一行:

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

    错误:

      

    pMod1的调用含糊不清

1 个答案:

答案 0 :(得分:2)

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;
}

为了修改参数,您需要通过引用传递它,就像在C ++中一样。在Metal中,您还需要明确将其限定为thread地址空间(而不是constantthreadgroup等)。

hg_sdf中pMod函数族的目的是“折叠”空间,允许您创建以固定间隔重复的对象的副本。 c值类似于对象索引,指示对象所在的折叠空间的“分区”。除非您调整对象外观(通过应用不同的材质或添加表面细节等),否则可以忽略它。 )

这在Johann Körndorfer's talk here中已经非常详尽地描述了。

<强>更新

为了调用该函数,您需要创建一个临时变量来存储您想要修改的调出组件,因为您无法通过Metal中的引用传递混合的矢量组件。

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

由于引用传递pxp.x现在包含写入pxpMod1的值。