У меня проблемы с рендерингом полупрозрачных спрайтов в металле.Я прочитал этот вопрос , этот вопрос , этот и эту тему на форумах Apple, и еще несколько, ноЯ не могу заставить его работать, поэтому прочитайте, прежде чем пометить этот вопрос как дубликат .
Моя справочная текстура имеет четыре строки и четыре столбца.Ряды полностью насыщены красным, зеленым, синим и черным соответственно.Непрозрачность столбцов варьируется от 100% непрозрачных до 25% непрозрачных (1, 0,75, 0,5, 0,25 альфа, в этом порядке).
Вкл. Pixelmator (где я его создал), этовыглядит следующим образом:
Если я вставлю полностью непрозрачный белый фон перед экспортом, он будет выглядеть следующим образом:
... Однако , когда я накладываю текстуру на четырехугольник в металле, и рендеринг его после очистки фона до непрозрачного белого (255,255, 255, 255), я получаю это:
... что явно на 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), я получаю это:
Обратите внимание, как верхний ряд постепенно темнеет вправо. Это должен быть один и тот же цвет на всем протяжении , поскольку он смешивает два цвета, которые имеют одинаковый компонент RGB (255, 0, 0).
Я сократил свое приложение до минимума и поместил демонстрационный проект на Github ;Полная установка Metal доступна в исходном коде репозитория.Возможно, что-то, о чем я не упомянул, вызывает это, но я не могу понять, что ...
Редактировать:
Как подсказывает@KenThomases в комментариях, я изменил значение свойства MTKView
colorPixelFormat
со значения по умолчанию .bgra8Unorm
на bgra8Unorm_srgb
и установил для свойства colorSpace
то же самое, что и view.window?.colorSpace?.cgColorSpace
. Теперь полупрозрачные фрагменты выглядят гораздо менее темными, но все еще не ожидаемого цвета:
(верхстрока должна быть полностью «невидимой» на красном фоне слева направо.)
Приложение
Я наткнулся на AppleДокументы по использованию отладчика шейдеров , поэтому я решил взглянуть на то, что происходит во фрагментном шейдере, когда мое приложение рисует один из верхних правых фрагментов спрайта (который, как полагают, полностью насыщен красным на 25% непрозрачности).
Интересно, что значение, возвращаемое фрагментным шейдером (к которому затем будет применено альфа-смешение, основываясь на текущем цвете цветового буфера и коэффициентах / функциях смешения), составляет [0.314, 0.0, 0.0, 0.596]
:
Это значение RGBA, по-видимому, совершенно не зависит от того, * MTKTextureLoader.Option.SRGB
true
, false
или отсутствует .
Обратите внимание, что компонент красный (0.314
) и альфа компонента (0.596
) не равны , хотя (если я не ошибаюсь) они должны быть , для полностью насыщенного красного с предварительно умноженным альфа.
Полагаю, это означает, что я сузил проблему до стадии загрузки текстуры ...?
Возможно, мне стоит отказаться от удобного MTKTextureLoader
и испачкать руки ...?