将 GLSL 函数转换为 Metal
Convert GLSL functions to 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
}
然后我得到了我所期望的。
我怀疑我的转换方式 inout
不完全正确。我从一些 Shadertoy
代码中借用了它,但我不相信它真的可以那样工作。
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.
我不明白它的真正含义。有人可以建议一些如何使用单元格索引的示例吗?
更新 1:
我把代码改成:
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)
}
}
更新2:
我怀疑 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
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
地址 space 中(与 constant
、threadgroup
等相反)
hg_sdf 中的 pMod
系列函数的目的是 "fold" space,允许您创建定期重复的对象副本。 c
值就像一个对象索引,指示对象所在的 "partition" 个折叠 space 中。您可以忽略它,除非您正在调整对象外观(通过应用不同的 material 或添加表面细节等)
Johann Körndorfer's talk here.
中对此进行了非常详尽的描述
更新
为了调用该函数,您需要创建一个临时变量来存储要修改的调出组件,因为在 Metal 中无法通过引用传递调出矢量组件。
float px = p.x;
float c = pMod1(px, size);
p.x = px;
由于 px
是通过引用传递的,因此 p.x
现在包含在 pMod1
.
中写入 px
的值
我遇到了 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
}
然后我得到了我所期望的。
我怀疑我的转换方式
inout
不完全正确。我从一些Shadertoy
代码中借用了它,但我不相信它真的可以那样工作。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.
我不明白它的真正含义。有人可以建议一些如何使用单元格索引的示例吗?
更新 1:
我把代码改成:
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)
}
}
更新2:
我怀疑 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
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
地址 space 中(与 constant
、threadgroup
等相反)
hg_sdf 中的 pMod
系列函数的目的是 "fold" space,允许您创建定期重复的对象副本。 c
值就像一个对象索引,指示对象所在的 "partition" 个折叠 space 中。您可以忽略它,除非您正在调整对象外观(通过应用不同的 material 或添加表面细节等)
Johann Körndorfer's talk here.
中对此进行了非常详尽的描述更新
为了调用该函数,您需要创建一个临时变量来存储要修改的调出组件,因为在 Metal 中无法通过引用传递调出矢量组件。
float px = p.x;
float c = pMod1(px, size);
p.x = px;
由于 px
是通过引用传递的,因此 p.x
现在包含在 pMod1
.
px
的值