Выпуск Swift Metal Shader - PullRequest
       115

Выпуск Swift Metal Shader

0 голосов
/ 06 октября 2019

Я часами пытался заставить следующий шейдер, написанный на opengl, работать в metal swift: https://github.com/gl-transitions/gl-transitions/blob/master/transitions/SimpleZoom.glsl Я использую метод ядра в функции шейдера, ниже приведен один выходной кадр полученного видео. Рамка изображения при увеличении

Я также написал несколько других шейдеров, которые успешно работают, но остановились на этом на 5+ часов.

Текущий код:

#include <metal_stdlib>
using namespace metal;
float2 zoom(float2 uv, float amount)

{
    return 0.5f + ((uv - 0.5f) * (1.0f - amount));
}

float4 getColor(texture2d<float, access::sample> tex2d, float2 uv)

{
    constexpr sampler sampler2d(coord::normalized,

                                address::clamp_to_edge,

                                filter::linear,

                                mip_filter::linear

                                );

    return tex2d.sample(sampler2d, float2(uv.x, 1.0f - uv.y));
}

float4 transition(texture2d<float, access::sample> fromTexture,
                  texture2d<float, access::sample> toTexture,
                  float nQuick,
                  float progress,
                  float2 uv
                  )
{

    uv.x /= fromTexture.get_width();
    uv.y /= fromTexture.get_height();
    uv.y = 1.0f - uv.y;

    float4 fromColor = getColor(fromTexture, zoom(uv, smoothstep(0.0f, nQuick, progress)));
    float4 toColor = getColor(toTexture, uv);

    return mix (fromColor, toColor, smoothstep(nQuick-0.2f, 1.0f, progress));
}



kernel void transition_simplezoom(texture2d<float, access::sample> inTexture [[ texture(0) ]],

                            texture2d<float, access::sample> inTexture2 [[ texture(1) ]],

                            texture2d<float, access::write> outTexture [[ texture(2) ]],

                            device const float *progress [[ buffer(1) ]],

                            device float *result [[buffer(0)]],

                            device const float *zoom_quickness [[buffer(2)]],

                            uint2 gid [[ thread_position_in_grid ]])
{
    float zoomQuickness = *zoom_quickness;

    float prog = *progress;

    prog = 1.0 - prog;

    float2 ngid = float2(gid);``

    float nQuick = clamp(zoomQuickness, 0.2, 1.0);

    return outTexture.write(transition(inTexture, inTexture2, nQuick, prog, float2(ngid)),

                            gid);
}

Диспетчеризация групп потоков:

guard let commandBuffer = commandQueue.makeCommandBuffer(), let computeCommandEncoder = commandBuffer.makeComputeCommandEncoder() else {
            return nil
        }

        // Set the compute pipeline state for the command encoder.
        computeCommandEncoder.setComputePipelineState(computePipelineState)

        // Set the input and output textures for the compute shader.
        computeCommandEncoder.setTexture(inputTexture, index: 0)
        computeCommandEncoder.setTexture(inputTexture1, index: 1)
        computeCommandEncoder.setTexture(inputTexture2, index: 2)
let threadGroupCount = MTLSizeMake(1, 1, 1)

        let threadGroups: MTLSize = {
            MTLSizeMake(Int(1280) / threadGroupCount.width, Int(720) / threadGroupCount.height, 1)
        }()
computeCommandEncoder.dispatchThreadgroups(threadGroups, threadsPerThreadgroup: threadGroupCount)

Ожидаемый результат: https://gl -transitions.com / editor / SimpleZoom

Текущий результат скод выше

1 Ответ

1 голос
/ 06 октября 2019

При преобразовании этого шейдера я старался максимально приблизиться к духу и структуре оригинала. Но, поскольку между GLSL и MSL есть существенные различия, мне пришлось взять на себя некоторые свободы:

  • Предположим, что униформа и другие глобалы будут поступать в виде constant буферов
  • Передавайте параметры из фрагментного шейдера в служебные функции, а не обращайтесь к ним как к глобальным

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

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

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

float4 getColor(texture2d<float, access::sample> tex2d, float2 uv) {
    constexpr sampler sampler2d(coord::normalized,
                                address::clamp_to_edge,
                                filter::linear,
                                mip_filter::linear);

    return tex2d.sample(sampler2d, float2(uv.x, 1.0f - uv.y));
}

float2 zoom(float2 uv, float amount) {
    return 0.5f + ((uv - 0.5f) * (1.0f - amount));
}

float4 transition (texture2d<float, access::sample> fromTexture,
                   texture2d<float, access::sample> toTexture,
                   float nQuick,
                   float progress,
                   float2 uv)
{
    float4 fromColor = getColor(fromTexture, zoom(uv, smoothstep(0.0f, nQuick, progress)));
    float4 toColor = getColor(toTexture, uv);
    return mix(fromColor, toColor, smoothstep(nQuick - 0.2f, 1.0f, progress));
}

vertex VertexOut textured_vertex(VertexIn in [[stage_in]]) {
    VertexOut out;
    out.position = float4(in.position, 0.0f, 1.0f);
    out.texCoords = in.texCoords;
    return out;
}

fragment float4 zoomed_textured_fragment(VertexOut in [[stage_in]],
                                         constant float& zoom_quickness [[buffer(0)]],
                                         constant float& progress       [[buffer(1)]],
                                         texture2d<float, access::sample> fromTexture [[texture(0)]],
                                         texture2d<float, access::sample> toTexture   [[texture(1)]])
{
    float nQuick = clamp(zoom_quickness, 0.2 , 1.0);
    return transition(fromTexture, toTexture, nQuick, progress, in.texCoords);
}

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

var zoomSpeed: Float = 0.5
renderCommandEncoder.setFragmentBytes(&zoomSpeed, length: MemoryLayout<Float>.size, index: 0)

renderCommandEncoder.setFragmentBytes(&progress, length: MemoryLayout<Float>.size, index: 1)

, где progress - это Float var, который изменяется со временем для выполнения анимации масштабирования.

...