Mam obraz, który generuję programowo i chcę wysłać ten obraz jako teksturę do modułu cieniującego Compute. Sposób generowania tego obrazu polega na tym, że obliczam każdy z komponentów RGBA jako wartości UInt8
i łączę je w UInt32
i zapisuję w buforze obrazu. Czynię to z następującego fragmentu kodu:Przekazywanie tekstur o typie komponentu UInt8 do shadera obliczeniowego Metal
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()
gdzie RGBA32
jest trochę struct, który nie do przesuwania i tworzenia wartości UInt32
. Obraz ten okazuje się świetny, ponieważ mogę go przekonwertować na UIImage
i zapisać go w mojej bibliotece zdjęć.
Problem pojawia się, gdy próbuję wysłać ten obraz jako teksturę do modułu cieniującego Compute. Poniżej jest mój kod shaderów:
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);
}
Problem z tym kodem jest, że rodzaj moich tekstur jest uint
, który jest 32-bitów, a chcę, aby wygenerować 32-bitowy pikselach samo jak ja na Procesor, dołączając 4 wartości 8-bitowe. Jednak nie mogę tego zrobić na Metalie, ponieważ nie ma takiego typu, który mogę dodać do siebie i utworzyć uint32
. Moje pytanie brzmi: jaki jest prawidłowy sposób obsługi tekstur 2D i ustawiania 32-bitowych pikseli na shaderze obliczeniowym Metal?
Dodatkowe pytanie: Widziałem również przykładowe kody modułu cieniującego z texture2d<float, access::read>
jako typ tekstury wejściowej. Zakładam, że reprezentuje ona wartość między 0.0 a 1.0, ale jaką ma to zaletę, która ma nad unsigned int z wartościami od 0 do 255?
Edycja: Aby wyjaśnić, tekstura wyjściowa modułu cieniującego, environmentMap
, ma dokładnie takie same właściwości (szerokość, wysokość, format pikselu itp.), Co tekstury wejściowe. Dlaczego uważam, że jest to sprzeczne z intuicją, to ustawienie uint4
jako piksela, co oznacza, że składa się z 4 32-bitowych wartości, podczas gdy każdy piksel powinien mieć 32 bity. Przy obecnym kodzie {255, 127, 63, 255}
ma dokładnie taki sam wynik, jak {2550, 127, 63, 255}
, co oznacza, że wartości zostają w jakiś sposób zaciśnięte między 0-255 przed zapisaniem w teksturach wyjściowych. Jest to jednak skrajnie sprzeczne z intuicją.
Dzięki Warren, to wyjaśniło wiele rzeczy. Ponadto, jeśli ktoś chce uzyskać więcej informacji na ten temat, należy zapoznać się z rozdziałem 7.7 dotyczącym teksturowania i reguł konwersji w kategorii cieniowania metalu. – halileohalilei