Metal:如何在不进行 color/gamma 转换的情况下将图像附加到着色器?
Metal: How to attach image to shader without color/gamma conversion?
是否可以在不进行任何 color/gamma 校正的情况下将图像发送到金属着色器(来自 scenekit)?
我有一个数据纹理,其中每个通道对应于我希望能够测试的特定值。这是一张世界地图,绿色通道映射到国家索引。如果该国家/地区 'active',则该国家/地区将呈现红色,否则将呈现灰色。
这是我使用的代码示例:
sphereNode = SCNNode(geometry: sphereGeometry)
scene.rootNode.addChildNode(sphereNode)
let program = SCNProgram()
program.vertexFunctionName = "sliceVertex"
program.fragmentFunctionName = "sliceFragment"
sphereNode.geometry?.firstMaterial?.program = program
let imageProperty = SCNMaterialProperty(contents: UIImage(named: "art.scnassets/Textures/earth-data-map-2k.png")!)
mageProperty.mipFilter = SCNFilterMode.none
sphereNode.geometry?.firstMaterial?.setValue(imageProperty, forKey: "dataTexture")
片段着色器:
fragment float4 sliceFragment(
Vertex in [[stage_in]],
texture2d<float, access::sample> dataTexture [[texture(0)]]
){
constexpr sampler quadSampler(coord::normalized, filter::linear, address::repeat);
float4 color = dataTexture.sample(quadSampler, in.texCoords);
int index = int(color.g * 255.0);
float active = index == 81 ? 1.0 : 0.0;
float3 col = mix(float3(color.g,color.g,color.g), float3(1.0,0.0,0.0), active);
return float4(col.r, col.g, col.b, 1);
}
问题是我用于纹理的 UIImage 似乎被转换为线性 space 但我想在着色器中使用未更改的图像数据。
Metal 始终在着色器中使用线性 RGB 来处理纹理数据。如果纹理是 sRGB 而不是线性,则读取和采样从 sRGB 转换为线性,写入从线性转换为 sRGB。
纹理像素格式告诉 Metal 支持纹理的数据是线性的还是 sRGB。大多数像素格式是线性的,但有些名称以 _sRGB
结尾并且是 sRGB。
在幕后(没有双关语意),SCNMaterialProperty
和 UIImage
将解释图像数据并使用源颜色配置文件来选择像素格式。如果 PNG 的颜色配置文件指定了线性或 sRGB 以外的其他内容,则它可能正在转换为其中之一。如果它没有颜色配置文件,它可能被认为是 SRGB。
表示非图像数据的 PNG 应带有指定其为线性 RGB 的颜色配置文件。如果您无法修改 PNG 并且它被解释为 sRGB,您可能需要通过 CGImage
并使用 copy(colorSpace:)
创建一个具有相同数据的新图像对象并覆盖其颜色配置文件。您将使用 CGColorSpace(.genericRGBLinear)
.
获得所需的颜色 space
此外,您大概应该使用 filter::nearest
作为着色器的采样器。合并相邻的国家指数没有意义。
不过,比这更好的可能是在您的应用程序资源中使用数据文件格式而不是图像文件格式。然后将它传递给缓冲区中的着色器而不是纹理。
是否可以在不进行任何 color/gamma 校正的情况下将图像发送到金属着色器(来自 scenekit)?
我有一个数据纹理,其中每个通道对应于我希望能够测试的特定值。这是一张世界地图,绿色通道映射到国家索引。如果该国家/地区 'active',则该国家/地区将呈现红色,否则将呈现灰色。
这是我使用的代码示例:
sphereNode = SCNNode(geometry: sphereGeometry)
scene.rootNode.addChildNode(sphereNode)
let program = SCNProgram()
program.vertexFunctionName = "sliceVertex"
program.fragmentFunctionName = "sliceFragment"
sphereNode.geometry?.firstMaterial?.program = program
let imageProperty = SCNMaterialProperty(contents: UIImage(named: "art.scnassets/Textures/earth-data-map-2k.png")!)
mageProperty.mipFilter = SCNFilterMode.none
sphereNode.geometry?.firstMaterial?.setValue(imageProperty, forKey: "dataTexture")
片段着色器:
fragment float4 sliceFragment(
Vertex in [[stage_in]],
texture2d<float, access::sample> dataTexture [[texture(0)]]
){
constexpr sampler quadSampler(coord::normalized, filter::linear, address::repeat);
float4 color = dataTexture.sample(quadSampler, in.texCoords);
int index = int(color.g * 255.0);
float active = index == 81 ? 1.0 : 0.0;
float3 col = mix(float3(color.g,color.g,color.g), float3(1.0,0.0,0.0), active);
return float4(col.r, col.g, col.b, 1);
}
问题是我用于纹理的 UIImage 似乎被转换为线性 space 但我想在着色器中使用未更改的图像数据。
Metal 始终在着色器中使用线性 RGB 来处理纹理数据。如果纹理是 sRGB 而不是线性,则读取和采样从 sRGB 转换为线性,写入从线性转换为 sRGB。
纹理像素格式告诉 Metal 支持纹理的数据是线性的还是 sRGB。大多数像素格式是线性的,但有些名称以 _sRGB
结尾并且是 sRGB。
在幕后(没有双关语意),SCNMaterialProperty
和 UIImage
将解释图像数据并使用源颜色配置文件来选择像素格式。如果 PNG 的颜色配置文件指定了线性或 sRGB 以外的其他内容,则它可能正在转换为其中之一。如果它没有颜色配置文件,它可能被认为是 SRGB。
表示非图像数据的 PNG 应带有指定其为线性 RGB 的颜色配置文件。如果您无法修改 PNG 并且它被解释为 sRGB,您可能需要通过 CGImage
并使用 copy(colorSpace:)
创建一个具有相同数据的新图像对象并覆盖其颜色配置文件。您将使用 CGColorSpace(.genericRGBLinear)
.
此外,您大概应该使用 filter::nearest
作为着色器的采样器。合并相邻的国家指数没有意义。
不过,比这更好的可能是在您的应用程序资源中使用数据文件格式而不是图像文件格式。然后将它传递给缓冲区中的着色器而不是纹理。