Ich habe ein Bild, das ich programmgesteuert generieren und ich möchte dieses Bild als Textur an einen Compute Shader senden. Die Art, wie ich dieses Bild erzeuge, besteht darin, dass ich jede RGBA-Komponente als UInt8
Werte berechne und sie zu einer UInt32
kombiniere und sie im Puffer des Bildes abspeichere. Ich tue dies mit dem folgenden Code:Übergeben Texturen mit UInt8 Komponententyp zu Metal Compute Shader
guard let cgContext = CGContext(data: nil,
width: width,
height: height,
bitsPerComponent: 8,
bytesPerRow: 0,
space: CGColorSpaceCreateDeviceRGB(),
bitmapInfo: RGBA32.bitmapInfo) else {
print("Unable to create CGContext")
return
}
guard let buffer = cgContext.data else {
print("Unable to create textures")
return
}
let pixelBuffer = buffer.bindMemory(to: RGBA32.self, capacity: width * height)
let heightFloat = Float(height)
let widthFloat = Float(width)
for i in 0 ..< height {
let latitude = Float(i + 1)/heightFloat
for j in 0 ..< width {
let longitude = Float(j + 1)/widthFloat
let x = UInt8(((sin(longitude * Float.pi * 2) * cos(latitude * Float.pi) + 1)/2) * 255)
let y = UInt8(((sin(longitude * Float.pi * 2) * sin(latitude * Float.pi) + 1)/2) * 255)
let z = UInt8(((cos(latitude * Float.pi) + 1)/2) * 255)
let offset = width * i + j
pixelBuffer[offset] = RGBA32(red: x, green: y, blue: z, alpha: 255)
}
}
let coordinateConversionImage = cgContext.makeImage()
wo RGBA32
ein wenig Struktur ist, die die Verschiebung tut und die Schaffung der UInt32
Wert. Dieses Bild ist gut, da ich es in UIImage
konvertieren und in meiner Fotogalerie speichern kann.
Das Problem tritt auf, wenn ich versuche, dieses Bild als Textur an einen Compute Shader zu senden. Unten ist mein Shader-Code:
kernel void updateEnvironmentMap(texture2d<uint, access::read> currentFrameTexture [[texture(0)]],
texture2d<uint, access::read> coordinateConversionTexture [[texture(1)]],
texture2d<uint, access::write> environmentMap [[texture(2)]]
uint2 gid [[thread_position_in_grid]])
{
const uint4 pixel = {255, 127, 63, 255};
environmentMap.write(pixel, gid);
}
Das Problem mit diesem Code ist, dass die Art meiner Texturen uint
ist, die 32-Bit ist, und ich möchte 32-Bit-Pixel die gleiche Weise, die ich auf das tun generieren CPU, durch Anhängen von 4 8-Bit-Werten. Allerdings kann ich das anscheinend nicht auf Metal machen, da es keinen byte
Typ gibt, den ich einfach zusammenfügen kann und einen uint32
zusammensetze. Also, meine Frage ist, was ist der richtige Weg, um 2D-Texturen zu behandeln und 32-Bit-Pixel auf einem Metal Compute Shader zu setzen?
Bonus Frage: Ich habe auch Beispiel Shader-Codes mit texture2d<float, access::read>
als Eingabe Textur-Typ gesehen. Ich gehe davon aus, dass es einen Wert zwischen 0.0 und 1.0 darstellt, aber welchen Vorteil hat das gegenüber einem vorzeichenlosen int mit Werten zwischen 0 und 255?
Bearbeiten: Um zu klären, hat die Ausgabestruktur des Shaders, environmentMap
, genau die gleichen Eigenschaften (Breite, Höhe, PixelFormat usw.) wie die Eingabetexturen. Warum ich denke, dass dies gegenläufig ist, ist, dass wir ein uint4
als ein Pixel setzen, was bedeutet, dass es aus 4 32-Bit-Werten besteht, während jedes Pixel 32-Bit sein sollte. Mit diesem aktuellen Code hat {255, 127, 63, 255}
das exakt gleiche Ergebnis wie {2550, 127, 63, 255}
, was bedeutet, dass die Werte irgendwie zwischen 0-255 geklemmt werden, bevor sie in die Ausgangstextur geschrieben werden. Aber das ist extrem kontraintuitiv.
Dank Warren, das hat eine Menge Dinge geklärt. Wenn jemand weitere Informationen dazu wünscht, ist das Kapitel 7.7 Texturadressierungs- und Konvertierungsregeln der Metal Shading Language Specification der richtige Ort dafür. – halileohalilei