Конвертировать GLSL-функции в Metal

Я наткнулся этот сайт и начать изучать 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 полезно? в коде со стороны это комментируют:

Многие операторы делят пространство на ячейки. Идентификатор
или возвращается индекс ячейки, если это возможно. Это возвращаемое значение
предназначен для использования по желанию, например как случайное семя, чтобы изменить
параметры функций расстояния внутри ячеек.

Я не понимаю, что это на самом деле значит. Может кто-нибудь предложить несколько примеров, как использовать индекс ячейки?

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

и теперь я получаю еще одно сообщение об ошибке:

фатальная ошибка: неожиданно найден ноль при развертывании необязательного значения

из этой строки:

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 неоднозначно

0

Решение

Эквивалентная функция в металле

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 адресное пространство (в отличие от constant, threadgroup, так далее.)

Цель pMod Семейство функций в hg_sdf заключается в «сворачивании» пространства, что позволяет создавать дубликаты объектов, которые повторяются через равные промежутки времени. c значение похоже на индекс объекта, указывающий, в каком «разделе» сложенного пространства находится объект. Вы можете игнорировать его, если не настраиваете внешний вид объекта (применяя другой материал или добавляя детали поверхности и т. д.)

Все это довольно подробно описано в Иоганн Кёрндорфер говорит здесь.

Обновить

Чтобы вызвать функцию, вам нужно создать временную переменную для хранения извилистого компонента, который вы хотите изменить, так как вы не можете передавать извитые векторные компоненты по ссылке в Metal.

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

поскольку px был передан по ссылке, p.x теперь содержит значение, которое было написано px внутри pMod1,

2

Другие решения

Других решений пока нет …

По вопросам рекламы [email protected]