【问题标题】:Metal Shader Function that can deal with both RGB and YUV textures可以同时处理 RGB 和 YUV 纹理的 Metal Shader Function
【发布时间】:2022-01-22 09:43:37
【问题描述】:

我正在尝试自学 iPhone 上的计算机图形基础知识和 Apple 的 Metal API。我正在尝试做一些非常基本的事情,但我有点卡住了。

我想要做的只是“纹理一个四边形”。基本上,我制作了一个矩形,并且我有一个覆盖矩形的图像纹理。对于图像纹理仅来自已知格式的图像的基本情况,我可以使其工作,但我无法弄清楚如何使我的代码更通用并能够处理不同的格式。

例如,有时图像纹理来自图像文件,经过解码后,像素数据为 RGB 格式。有时,我的图像纹理实际上来自以 YUV 格式存储数据的视频帧。

理想情况下,我想创建某种“采样器”对象或函数,它可以将特定纹理坐标的 RGB 颜色传回给我。在我准备渲染的代码中,这是使用哪种格式的上下文的部分,因此它将有足够的信息来确定应该使用哪种类型的采样器。例如,在视频帧的情况下,它知道它正在处理一个视频帧,因此它创建了一个 YUV 采样器并将相关数据传递给它。然后从我只想读取颜色的着色器代码中,它可以只要求某些特定坐标处的颜色,YUV 采样器会做适当的工作来计算正确的 RGB 颜色。如果我传入一个 RGB 采样器,它只会读取 RGB 数据而不进行任何类型的计算。

我认为这真的很简单吗?我觉得这对于处理不同格式或颜色空间或其他任何纹理的图形代码来说必须是一个常见问题?我错过了什么明显的东西吗?

如何在不编写所有着色器的一堆版本的情况下做到这一点?

【问题讨论】:

  • 您了解过 Accelerate 的转换功能吗? developer.apple.com/documentation/accelerate/conversion YCbCr 是使用vImageConvert_AnyToAny() 支持的格式:developer.apple.com/documentation/accelerate/…
  • @JeshuaLacock - 这需要制作图像缓冲区的副本吗?理想情况下,我希望能够在不分配更多内存并将所有新像素值放入新内存的情况下做到这一点。如果可能的话,我想要一些可以即时完成的东西。比如,当我请求某个位置的颜色时,转换只针对该颜色?
  • 我的建议将在预处理中完成。如果您想即时更改色彩空间,您只需要实现色彩空间转换功能。我在自己的着色器中执行此操作,将 RGB 更改为 HSL,调整,然后返回 RGB。

标签: ios iphone graphics metal yuv


【解决方案1】:

这里是用于将 RGBA 转换为 YUVA 的函数,反之亦然。

float4 rgba2yuva(float4 rgba)
{

    float4 yuva = float4(0.0);

    yuva.x = rgba.r * 0.299 + rgba.g * 0.587 + rgba.b * 0.114;
    yuva.y = rgba.r * -0.169 + rgba.g * -0.331 + rgba.b * 0.5 + 0.5;
    yuva.z = rgba.r * 0.5 + rgba.g * -0.419 + rgba.b * -0.081 + 0.5;
    yuva.w = rgba.a;

    return yuva;
}

float4 yuva2rgba(float4 yuva)
{

    float4 rgba = float4(0.0);

    rgba.r = yuva.x * 1.0 + yuva.y * 0.0 + yuva.z * 1.4;
    rgba.g = yuva.x * 1.0 + yuva.y * -0.343 + yuva.z * -0.711;
    rgba.b = yuva.x * 1.0 + yuva.y * 1.765 + yuva.z * 0.0;
    rgba.a = yuva.a;

    return rgba;
}

我从这里改编了代码:https://github.com/libretro/glsl-shaders/blob/master/nnedi3/shaders/

简单的 OpenGL 着色器很容易移植到 Metal。我几乎只是将数据类型vec4 更改为float4。如果你想要半个版本,只需将float4 替换为half4。

【讨论】:

  • 那么,这看起来很适合即时转换东西,但这也意味着使用这些函数的代码知道颜色的格式?从纹理中读取颜色值后,我们需要添加某种 if 语句。我想知道是否有一种方法可以抽象出来,以便函数可以在不需要条件逻辑的情况下读取颜色。使用您的函数,使用这些辅助函数的代码看起来像这样。 ``` float4 color = texture.sample(sampler, uv);颜色=(isYUV?yuva2rgba(颜色)颜色); ```
  • 我不会担心一个简单的比较会减慢 Metal 的速度以使其稍微显着。但是,如果您不想每次绘制都进行逻辑检查,则可以有两个着色器调用,一个用于 RGB 值,另一个用于 YUV。对于抽象,当然你也可以编写一个带有开关的函数来处理你想要支持的所有颜色空间颜色转换。
  • 是的,添加一个 if 语句可能没什么大不了的,但是您仍然必须将足够的元数据传递给需要读取颜色的每个事物,以便它知道哪个版本的要调用的辅助函数?就像,您必须将 RGB 纹理和亮度/色度纹理传递给片段着色器,以便它可以同时执行两个分支?随着您的代码变得越来越复杂,这个问题似乎真的会失控,如果除了不同的颜色模型之外,您还必须处理不同的颜色空间和传递函数,情况会变得更糟?
  • 您有责任了解您正在处理的色彩空间。没有什么神奇的,只是给它一个图像,它知道它所在的颜色空间或金属的任何类似的东西。我错过了什么吗?
  • 如果您想要为每种类型的纹理颜色空间使用单独的着色器,那么可以,您将根据着色器的预期传递不同的纹理。如果您只想动态转换颜色空间,那么不,只需为其提供 RGB 或任何您的基本工作颜色空间。
【解决方案2】:

金属着色器功能 ARK,现在可以使用@Jeshua Lacock 在两者之间进行转换。

// tweak your color offsets as desired
#include <metal_stdlib>
using namespace metal;

kernel void YUVColorConversion(texture2d<uint, access::read> yTexture [[texture(0)]],
                               texture2d<uint, access::read> uTexture [[texture(1)]],
                               texture2d<uint, access::read> vTexture [[texture(2)]],
                               texture2d<float, access::write> outTexture [[texture(3)]],
                               uint2 gid [[thread_position_in_grid]])
{
    float3 colorOffset = float3(0, -0.5, -0.5);
    float3x3 colorMatrix = float3x3(
                                    float3(1, 1, 1),
                                    float3(0, -0.344, 1.770),
                                    float3(1.403, -0.714, 0)
                                    );

    uint2 uvCoords = uint2(gid.x / 2, gid.y / 2);
    
    float y = yTexture.read(gid).r / 255.0;
    float u = uTexture.read(uvCoords).r / 255.0;
    float v = vTexture.read(uvCoords).r / 255.0;

    float3 yuv = float3(y, u, v);

    float3 rgb = colorMatrix * (yuv + colorOffset);

    outTexture.write(float4(float3(rgb), 1.0), gid);
}

良好的参考 here ,然后您可以构建管道或变体来专门处理您需要的内容,例如 here

#include <metal_stdlib>
#include <simd/simd.h>
#include <metal_texture>
#include <metal_matrix>
#include <metal_geometric>
#include <metal_math>
#include <metal_graphics>
#include "AAPLShaderTypes.h"

using namespace metal;

// Variables in constant address space.
constant float3 lightPosition = float3(0.0, 1.0, -1.0);

// Per-vertex input structure
struct VertexInput {
    float3 position [[attribute(AAPLVertexAttributePosition)]];
    float3 normal   [[attribute(AAPLVertexAttributeNormal)]];
    half2  texcoord [[attribute(AAPLVertexAttributeTexcoord)]];
};

// Per-vertex output and per-fragment input
typedef struct {
    float4 position [[position]];
    half2  texcoord;
    half4  color;
} ShaderInOut;

// Vertex shader function
vertex ShaderInOut vertexLight(VertexInput in [[stage_in]],
                               constant AAPLFrameUniforms& frameUniforms [[ buffer(AAPLFrameUniformBuffer) ]],
                               constant AAPLMaterialUniforms& materialUniforms [[ buffer(AAPLMaterialUniformBuffer) ]]) {
    ShaderInOut out;
    
    // Vertex projection and translation
    float4 in_position = float4(in.position, 1.0);
    out.position = frameUniforms.projectionView * in_position;
    
    // Per vertex lighting calculations
    float4 eye_normal = normalize(frameUniforms.normal * float4(in.normal, 0.0));
    float n_dot_l = dot(eye_normal.rgb, normalize(lightPosition));
    n_dot_l = fmax(0.0, n_dot_l);
    out.color = half4(materialUniforms.emissiveColor + n_dot_l);

    // Pass through texture coordinate
    out.texcoord = in.texcoord;
    
    return out;
}

// Fragment shader function
fragment half4 fragmentLight(ShaderInOut in [[stage_in]],
                             texture2d<half>  diffuseTexture [[ texture(AAPLDiffuseTextureIndex) ]]) {
    constexpr sampler defaultSampler;
    
    // Blend texture color with input color and output to framebuffer
    half4 color =  diffuseTexture.sample(defaultSampler, float2(in.texcoord)) * in.color;
    
    return color;
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2016-07-08
    • 2016-04-24
    • 2017-04-23
    • 2012-05-20
    • 1970-01-01
    • 2012-03-08
    • 2013-06-10
    • 1970-01-01
    相关资源
    最近更新 更多