gpt4 book ai didi

ios - 将具有 UInt8 组件类型的纹理传递给 Metal 计算着色器

转载 作者:IT王子 更新时间:2023-10-29 05:35:43 26 4
gpt4 key购买 nike

我有一个以编程方式生成的图像,我想将此图像作为纹理发送到计算着色器。我生成此图像的方式是将每个 RGBA 分量计算为 UInt8值,并将它们组合成一个 UInt32并将其存储在图像的缓冲区中。我使用以下代码来执行此操作:

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()

哪里RGBA32是一个小结构,它进行移位和创建 UInt32值(value)。这个图像结果很好,因为我可以将它转换为 UIImage并将其保存到我的照片库中。

当我尝试将此图像作为纹理发送到计算着色器时出现问题。下面是我的着色器代码:

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

此代码的问题是我的纹理类型是 uint ,它是 32 位的,我想通过附加 4 个 8 位值来生成 32 位像素,就像我在 CPU 上所做的那样。但是,我似乎无法在 Metal 上执行此操作,因为没有 byte我可以将其附加在一起并组成一个 uint32 的类型.所以,我的问题是,在 Metal 计算着色器上处理 2D 纹理和设置 32 位像素的正确方法是什么?

奖励问题:另外,我已经看到带有 texture2d<float, access::read> 的示例着色器代码作为输入纹理类型。我假设它代表一个介于 0.0 和 1.0 之间的值,但与值介于 0 和 255 之间的无符号整数相比,它有什么优势?

编辑:澄清一下,着色器的输出纹理,environmentMap , 具有与输入纹理完全相同的属性(宽度、高度、像素格式等)。为什么我认为这是违反直觉的,因为我们正在设置 uint4作为一个像素,这意味着它由 4 个 32 位值组成,而每个像素应该是 32 位。使用当前代码,{255, 127, 63, 255}结果与 {2550, 127, 63, 255} 完全相同,这意味着在写入输出纹理之前,这些值会以某种方式被限制在 0-255 之间。但这是非常违反直觉的。

最佳答案

其中的魔法比您似乎熟悉的要多一些,所以我会尝试阐明。

首先,根据设计,Metal 中纹理的存储格式与您读取/采样时获得的类型之间存在松散的联系。您可以在 .bgra8Unorm 中拥有纹理格式化,当通过绑定(bind)为 texture2d<float, access::sample> 的纹理采样时会给你一个float4其组件按 RGBA 顺序排列。从这些打包字节到带有混合组件的浮点向量的转换遵循 Metal 着色语言规范中指定的详细记录的转换规则。

还有一种情况是,当写入存储为(例如)每个组件 8 位的纹理时,值将被限制以适合底层存储格式。这进一步受到纹理是否为 norm 的影响。类型:如果格式包含 norm ,这些值被解释为好像它们指定了 0 到 1 之间的值。否则,您读取的值不会被标准化。

一个例子:如果一个纹理是.bgra8Unorm并且给定像素包含字节值 [0, 64, 128, 255] ,然后在请求 float 的着色器中读取时组件,你会得到 [0.5, 0.25, 0, 1.0]当你取样时。相比之下,如果格式为 .rgba8Uint , 你会得到 [0, 64, 128, 255] .纹理的存储格式对其内容在采样时的解释方式具有重要影响。

我假设你的纹理的像素格式类似于 .rgba8Unorm .如果是这种情况,您可以通过这样编写内核来实现您想要的:

kernel void updateEnvironmentMap(texture2d<float, access::read> currentFrameTexture [[texture(0)]],
texture2d<float, access::read> coordinateConversionTexture [[texture(1)]],
texture2d<float, access::write> environmentMap [[texture(2)]]
uint2 gid [[thread_position_in_grid]])
{
const float4 pixel(255, 127, 63, 255);
environmentMap.write(pixel * (1 / 255.0), gid);
}

相比之下,如果您的纹理格式为 .rgba8Uint , 你会通过这样写得到同样的效果:

kernel void updateEnvironmentMap(texture2d<float, access::read> currentFrameTexture [[texture(0)]],
texture2d<float, access::read> coordinateConversionTexture [[texture(1)]],
texture2d<float, access::write> environmentMap [[texture(2)]]
uint2 gid [[thread_position_in_grid]])
{
const float4 pixel(255, 127, 63, 255);
environmentMap.write(pixel, gid);
}

我知道这是一个玩具示例,但我希望通过上述信息,您可以弄清楚如何正确存储和采样值以实现您想要的。

关于ios - 将具有 UInt8 组件类型的纹理传递给 Metal 计算着色器,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/47738441/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com