簡體   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