繁体   English   中英

可以同时处理RGB和YUV纹理的Metal Shader Function

[英]Metal Shader Function that can deal with both RGB and YUV textures

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

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

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

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

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

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

以下是用于将 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

金属着色器 function 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);
}

很好的参考然后你可以构建管道或变体来专门处理你需要的东西,就像这里

#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;
}

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM