Визуализация различных цветовых форматов текстуры в металле - PullRequest
0 голосов
/ 09 сентября 2018

Я использую MTKTextureLoader для загрузки предоставленных пользователем изображений в текстуры для рендеринга. Я отрисовываю эти предоставленные текстуры в промежуточную текстуру, а затем отрисовываю промежуточную текстуру для рисования MTKView. И промежуточная текстура, и нарисованный объект имеют одинаковый цветовой формат.

Я столкнулся с некоторыми проблемами с определенными изображениями. Все изображения являются файлами PNG, но кажется, что я могу получить различные базовые данные из MTKTextureLoader.

Первый выпуск:

Я загружаю PNG с альфой и без альфы. Это, кажется, фактор, но это не ясно на 100%. Оба свойства текстуры выглядят одинаково.

PNG с альфа:

Texture: <BronzeMtlTexture: 0x1015484b0>
    label = 512x512.png 
    textureType = MTLTextureType2D 
    pixelFormat = MTLPixelFormatBGRA8Unorm_sRGB 
    width = 512 
    height = 512 
    depth = 1 
    arrayLength = 1 
    mipmapLevelCount = 10 
    sampleCount = 1 
    cpuCacheMode = MTLCPUCacheModeDefaultCache 
    storageMode = MTLStorageModeManaged 
    resourceOptions = MTLResourceCPUCacheModeDefaultCache MTLResourceStorageModeManaged  
    usage = MTLTextureUsageShaderRead  
    framebufferOnly = 0 
    purgeableState = MTLPurgeableStateNonVolatile 
    parentTexture = <null> 
    parentRelativeLevel = 0 
    parentRelativeSlice = 0 
    buffer = <null> 
    bufferOffset = 0 
    bufferBytesPerRow = 0 
    iosurface = 0x0 
    iosurfacePlane = 0
    label = 512x512.png

PNG без альфа:

Texture: <BronzeMtlTexture: 0x10164a9b0>
    label = 016 - jKsgTpt.png 
    textureType = MTLTextureType2D 
    pixelFormat = MTLPixelFormatBGRA8Unorm_sRGB 
    width = 1685 
    height = 815 
    depth = 1 
    arrayLength = 1 
    mipmapLevelCount = 11 
    sampleCount = 1 
    cpuCacheMode = MTLCPUCacheModeDefaultCache 
    storageMode = MTLStorageModeManaged 
    resourceOptions = MTLResourceCPUCacheModeDefaultCache MTLResourceStorageModeManaged  
    usage = MTLTextureUsageShaderRead  
    framebufferOnly = 0 
    purgeableState = MTLPurgeableStateNonVolatile 
    parentTexture = <null> 
    parentRelativeLevel = 0 
    parentRelativeSlice = 0 
    buffer = <null> 
    bufferOffset = 0 
    bufferBytesPerRow = 0 
    iosurface = 0x0 
    iosurfacePlane = 0
    label = 016 - jKsgTpt.png

В приведенном выше случае PNG с альфа-каналом загружается с заменой его компонентов R & B. Есть ли способ обнаружить это, чтобы я мог правильно настроить шейдер по мере необходимости?

Второй выпуск:

Один из PNG, который я тестировал с завершенной загрузкой как MTLPixelFormatRGBA16Unorm. Моя промежуточная текстура и рисование MTKView обычно MTLPixelFormatBGRA8Unorm. Это можно обнаружить, но как мне правильно отрендерить эту текстуру до промежуточной текстуры? В этом случае я получаю очень расплывчатую картинку.


Мне кажется, что мне не хватает некоторых нюансов MTKTextureLoader или что, возможно, это не было предназначено для использования так, как я хочу.


Обновление 1

Я не делаю ничего особенного с загрузчиком текстур. Там не так много для настройки:

let textureLoader = MTKTextureLoader(device: metalDevice)

let options: [MTKTextureLoader.Option:Any] = [
    .generateMipmaps : true,
    .SRGB: true
]

textureLoader.newTexture(URL: url, options: options) { (texture, error) in
    // Store the texture here
}

Как показано в первом выпуске, я получу две разные текстуры, которые помечены как BGRA8, но, как правило, пиксели с прозрачностью имеют свои пиксели в порядке RGBA. Во втором выпуске у меня есть один конкретный PNG, который загружается в RGBA16.


Обновление 2

Настройка трубопровода:

let pipelineDescriptor = MTLRenderPipelineDescriptor()
pipelineDescriptor.vertexFunction = self.library.makeFunction(name: "instance_vertex")
pipelineDescriptor.fragmentFunction = self.library.makeFunction(name: "instance_fragment")
pipelineDescriptor.colorAttachments[0].pixelFormat = newTexture.pixelFormat
pipelineDescriptor.colorAttachments[0].isBlendingEnabled = true
pipelineDescriptor.colorAttachments[0].rgbBlendOperation = .add
pipelineDescriptor.colorAttachments[0].alphaBlendOperation = .add
pipelineDescriptor.colorAttachments[0].sourceRGBBlendFactor = .sourceAlpha
pipelineDescriptor.colorAttachments[0].sourceAlphaBlendFactor = .sourceAlpha
pipelineDescriptor.colorAttachments[0].destinationRGBBlendFactor = .oneMinusSourceAlpha
pipelineDescriptor.colorAttachments[0].destinationAlphaBlendFactor = .oneMinusSourceAlpha

newTexture в этом случае текстура загружается из MTKTextureLoader.

Настройка рендера:

let renderPassDescriptor = MTLRenderPassDescriptor()
renderPassDescriptor.colorAttachments[0].texture = canvasTexture
renderPassDescriptor.colorAttachments[0].loadAction = .clear
renderPassDescriptor.colorAttachments[0].clearColor = MTLClearColor(
    red: Double(red),
    green: Double(green),
    blue: Double(blue),
    alpha: Double(alpha)
)
renderPassDescriptor.colorAttachments[0].storeAction = .store

let encoder = commandBuffer.makeRenderCommandEncoder(descriptor: renderPassDescriptor)!

canvasTexture было сделано с тем же типом текстуры, что и MTKView. Я пробовал BGRA8 и BGRA8 SRGB, в зависимости от того, какой флаг загрузчика для SRGB был установлен выше в загрузчике.

Визуализация:

encoder.setRenderPipelineState(pipelineState)
encoder.setVertexBuffer(vertexBuffer, offset: 0, index: 0)
encoder.setVertexBuffer(uniformBuffer, offset: memorySize * offset, index: 1)
encoder.setFragmentTexture(newTexture, index: 0)
encoder.drawPrimitives(type: .triangleStrip, vertexStart: 0, vertexCount: 4)

Фрагмент шейдера:

fragment half4 face_instance_fragment(VertexOut v [[stage_in]], texture2d<float, access::sample> texture [[ texture(0) ]])
{
    constexpr sampler textureSampler(mag_filter::linear,
                                     min_filter::linear,
                                     s_address::clamp_to_edge,
                                     t_address::clamp_to_edge,
                                     r_address::clamp_to_edge);

    return (half4)texture.sample(textureSampler, v.texturePosition);
}

Добавление .zyxw к сэмплеру выше исправит цвета одной текстуры, но нарушит другую, как я знаю, цвета правильные, просто в неправильном порядке.

1 Ответ

0 голосов
/ 10 сентября 2018

На этот вопрос будет трудно ответить, не увидев код (как приложение, так и шейдер) и не выяснив, что вы наблюдаете и как. Например, как вы определяете, что PNG без альфа имеет свои компоненты R и B поменялись местами?

В любом случае шейдерам не нужно заботиться о порядке компонентов в формате пикселей. Считывания / выборки из текстуры всегда возвращают компонент R в компоненте .r вывода, компонент G в .g, компонент B в .b и альфа в .a, независимо от основного формата пикселей .

Аналогично, шейдерам не нужно заботиться о том, является ли пиксельный формат текстуры sRGB или нет. Шейдеры всегда работают с линейным RGBA. Metal выполняет автоматическое преобразование текстур sRGB и значений шейдеров.

Формат пикселей влияет на то, какой тип данных используется для чтения, выборки и записи. Нормализованные (со знаком или без знака) пиксельные форматы используют half или float. Пиксельные форматы с плавающей точкой также используют half или float. Целочисленные пиксельные форматы без знака используют ushort или uint. Целочисленные пиксельные форматы со знаком используют short или int. Глубинные (с или без трафарета) пиксельные форматы используют float.

...