Альфа-смешивание в металле не дает ожидаемых результатов - PullRequest
3 голосов
/ 10 апреля 2019

У меня проблемы с рендерингом полупрозрачных спрайтов в металле.Я прочитал этот вопрос , этот вопрос , этот и эту тему на форумах Apple, и еще несколько, ноЯ не могу заставить его работать, поэтому прочитайте, прежде чем пометить этот вопрос как дубликат .

Моя справочная текстура имеет четыре строки и четыре столбца.Ряды полностью насыщены красным, зеленым, синим и черным соответственно.Непрозрачность столбцов варьируется от 100% непрозрачных до 25% непрозрачных (1, 0,75, 0,5, 0,25 альфа, в этом порядке).

Вкл. Pixelmator (где я его создал), этовыглядит следующим образом:

enter image description here

Если я вставлю полностью непрозрачный белый фон перед экспортом, он будет выглядеть следующим образом:

enter image description here

... Однако , когда я накладываю текстуру на четырехугольник в металле, и рендеринг его после очистки фона до непрозрачного белого (255,255, 255, 255), я получаю это:

enter image description here

... что явно на 1042 * темнее , чем должно быть внепрозрачные фрагменты (ярко-белый сзади должен «просочиться»).

Детали реализации

Я импортировал файл png в Xcode как актив текстуры в каталоге ресурсов моего приложения, и ввремя выполнения, я загружаю его, используя MTKTextureLoader.Опция .SRGB, похоже, не имеет значения.

Код шейдера, насколько я могу судить, не делает ничего фантастического, но для справки:

#include <metal_stdlib>
using namespace metal;

struct Constants {
    float4x4 modelViewProjection;
};

struct VertexIn {
    float4 position  [[ attribute(0) ]];
    float2 texCoords [[ attribute(1) ]];
};

struct VertexOut {
    float4 position [[position]];
    float2 texCoords;
};

vertex VertexOut sprite_vertex_transform(device VertexIn *vertices [[buffer(0)]],
                                         constant Constants &uniforms [[buffer(1)]],
                                         uint vertexId [[vertex_id]]) {

    float4 modelPosition = vertices[vertexId].position;
    VertexOut out;

    out.position = uniforms.modelViewProjection * modelPosition;
    out.texCoords = vertices[vertexId].texCoords;

    return out;
}

fragment float4 sprite_fragment_textured(VertexOut fragmentIn [[stage_in]],
                                         texture2d<float, access::sample> tex2d [[texture(0)]],
                                         constant Constants &uniforms [[buffer(1)]],
                                         sampler sampler2d [[sampler(0)]]) {

    float4 surfaceColor = tex2d.sample(sampler2d, fragmentIn.texCoords);

    return surfaceColor;
}

На стороне приложения я использую следующие (довольно стандартные) коэффициенты наложенияи операции над моим дескриптором прохода рендеринга:

descriptor.colorAttachments[0].rgbBlendOperation = .add
descriptor.colorAttachments[0].alphaBlendOperation = .add

descriptor.colorAttachments[0].sourceRGBBlendFactor = .one
descriptor.colorAttachments[0].sourceAlphaBlendFactor = .sourceAlpha

descriptor.colorAttachments[0].destinationRGBBlendFactor = .oneMinusSourceAlpha
descriptor.colorAttachments[0].destinationAlphaBlendFactor = .oneMinusSourceAlpha

(я пытался изменить sourceRGBBlendFactor с .one на .sourceAlpha, чтобы сделать его немного темнее.)


Если вместо этого я отображаю изображение на красном фоне (255, 0, 0, 255), я получаю это:

enter image description here

Обратите внимание, как верхний ряд постепенно темнеет вправо. Это должен быть один и тот же цвет на всем протяжении , поскольку он смешивает два цвета, которые имеют одинаковый компонент RGB (255, 0, 0).

Я сократил свое приложение до минимума и поместил демонстрационный проект на Github ;Полная установка Metal доступна в исходном коде репозитория.Возможно, что-то, о чем я не упомянул, вызывает это, но я не могу понять, что ...


Редактировать:

Как подсказывает@KenThomases в комментариях, я изменил значение свойства MTKView colorPixelFormat со значения по умолчанию .bgra8Unorm на bgra8Unorm_srgb и установил для свойства colorSpace то же самое, что и view.window?.colorSpace?.cgColorSpace. Теперь полупрозрачные фрагменты выглядят гораздо менее темными, но все еще не ожидаемого цвета:

enter image description here

(верхстрока должна быть полностью «невидимой» на красном фоне слева направо.)


Приложение

Я наткнулся на AppleДокументы по использованию отладчика шейдеров , поэтому я решил взглянуть на то, что происходит во фрагментном шейдере, когда мое приложение рисует один из верхних правых фрагментов спрайта (который, как полагают, полностью насыщен красным на 25% непрозрачности).

Интересно, что значение, возвращаемое фрагментным шейдером (к которому затем будет применено альфа-смешение, основываясь на текущем цвете цветового буфера и коэффициентах / функциях смешения), составляет [0.314, 0.0, 0.0, 0.596]:

enter image description here

Это значение RGBA, по-видимому, совершенно не зависит от того, * MTKTextureLoader.Option.SRGB true, false или отсутствует .

Обратите внимание, что компонент красный (0.314) и альфа компонента (0.596) не равны , хотя (если я не ошибаюсь) они должны быть , для полностью насыщенного красного с предварительно умноженным альфа.

Полагаю, это означает, что я сузил проблему до стадии загрузки текстуры ...?

Возможно, мне стоит отказаться от удобного MTKTextureLoader и испачкать руки ...?

1 Ответ

5 голосов
/ 10 апреля 2019

Что ж, получается, что проблема действительно в стадии загрузки текстуры, но не в каком-либо куске кода, который я мог бы подправить (по крайней мере, если придерживаться MTKTextureLoader).

Похоже, мне нужно было внести некоторые изменения в Инспектора атрибутов моего каталога активов в XCode (но, по крайней мере, теперь я могу пометить свой исходный вопрос с помощью Xcode: на шаг ближе к бронзовому значку!).

В частности , мне пришлось изменить атрибут Интерпретация набора текстур из значения по умолчанию "Цвета" на "Цвета (без предварительного умножения) ) "

enter image description here

Очистите, эти наборы текстур каталога ресурсов разработаны с учетом более традиционных форматов текстурных изображений, таких как, например, TGA, не PNG (который официально предварительно не умножается согласно спецификации).

Я как-то ожидал, что MTKTextureLoader будет достаточно умен, чтобы сделать это для меня во время загрузки. Очевидно, что это не та информация, которую можно надежно прочитать (например, из метаданных / заголовка PNG-файла).


Теперь моя эталонная текстура визуализируется во всей своей яркой красе:

enter image description here

В качестве последнего, более строгого теста я могу подтвердить, что все 4 цвета «исчезают» на эквивалентном фоне RGB, независимо от непрозрачности текселей:

enter image description here

...