Металл: как прикрепить изображение к шейдеру без преобразования цвета / гаммы?

Можно ли отправить изображение в металлический шейдер (из набора сцен) без какой-либо коррекции цвета / гаммы?

У меня есть текстура данных, где каждый канал соответствует определенным значениям, которые я хочу проверить. Это карта мира с отображением зеленого канала в индекс страны. Страна будет отображаться красным, если она «активна», в противном случае - серым.

Вот пример кода, который я использую:

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, который я использую для текстуры, кажется, преобразован в линейное пространство, но я хочу использовать данные изображения без изменений в шейдере.

2
0
832
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Металл всегда использует линейный RGB в шейдерах для данных текстуры. Если текстура sRGB, а не линейная, операции чтения и выборки преобразуются из sRGB в линейные, а записи преобразуются из линейных в sRGB.

Формат пикселей текстуры - это то, что сообщает Metal, являются ли данные, поддерживающие текстуру, линейными или sRGB. Большинство форматов пикселей являются линейными, но некоторые из них имеют имена, оканчивающиеся на _sRGB, и являются sRGB.

За кулисами (без каламбура) SCNMaterialProperty и UIImage будут интерпретировать данные изображения и использовать исходный цветовой профиль для выбора формата пикселей. Если PNG имеет цветовой профиль, который указывает что-то иное, кроме линейного или sRGB, он, вероятно, преобразуется в один из них. Если у него нет цветового профиля, вероятно, это SRGB.

PNG, представляющий данные, не относящиеся к изображению, должен иметь цветовой профиль, указывающий, что он находится в линейном RGB. Если вы не можете изменить PNG и он интерпретируется как sRGB, вам может потребоваться пройти через CGImage и использовать copy(colorSpace:) для создания нового объекта изображения с теми же данными и переопределения его цветового профиля. Вы получите желаемое цветовое пространство, используя CGColorSpace(.genericRGBLinear).

Кроме того, вы, вероятно, должны использовать filter::nearest для семплера вашего шейдера. Нет смысла объединять индексы соседних стран.

Однако лучше всего, вероятно, было бы использовать формат файла данных в ресурсах вашего приложения, а не формат файла изображения. Затем передайте его шейдеру в буфере, а не в текстуре.

Привет, Кен. У меня несколько связанная с этим проблема. Я использую rgba16Float в качестве моего MTKView colorPixelFormat вместо стандартного bgra8Unorm. Теперь все стало ярче. Текстуры. Чистый цвет фона. Все ярче, как будто я поправил гамму. Вы хоть понимаете, что здесь происходит? Ссылка: stackoverflow.com/questions/54256198/…

dugla 19.01.2019 17:10

Другие вопросы по теме