是否可以在没有任何颜色/伽玛校正的情况下将图像发送到金属着色器(来自Scenekit)?
我有一个数据纹理,其中每个通道都对应于我想要测试的特定值。这是一张带有绿色通道映射到国家/地区索引的世界地图。如果该国家/地区处于“活跃”状态,则将其显示为红色;否则,将显示为灰色。
这是我使用的代码示例:
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似乎已转换为线性空间,但我想使用着色器中不变的图像数据。
答案 0 :(得分:4)
金属始终将着色器内的线性RGB用于纹理数据。如果纹理是sRGB而非线性,则读取和采样会从sRGB转换为线性,而写入会从线性转换为sRGB。
纹理像素格式告诉Metal,支持纹理的数据是线性的还是sRGB。大多数像素格式都是线性的,但有些名称以_sRGB
结尾并且是sRGB。
SCNMaterialProperty
和UIImage
在幕后(无双关语)将解释图像数据,并使用源颜色配置文件选择像素格式。如果PNG的颜色配置文件指定的不是线性或sRGB,则可能是转换为其中的一种。如果没有颜色配置文件,则可能是SRGB。
表示非图像数据的PNG应该带有指定其为线性RGB的颜色配置文件。如果您无法修改PNG并将其解释为sRGB,则可能需要遍历CGImage
并使用copy(colorSpace:)
创建一个具有相同数据的新图像对象,并覆盖其颜色配置文件。您将使用CGColorSpace(.genericRGBLinear)
获得所需的色彩空间。
此外,您应该为着色器的采样器使用filter::nearest
。合并相邻国家/地区的索引没有任何意义。
比这更好的是,可能是在应用程序的资源中使用数据文件格式,而不是图像文件格式。然后将其传递到缓冲区而不是纹理中的着色器。