ProRes 422视频转Metal着色器:YpCbCr转RGB画面发紫问题
解决ProRes 422视频在Metal着色器里YpCbCr转RGB偏紫的问题
1. 先修正纹理格式,这是偏紫的核心原因
你当前创建Metal纹理用的是.bgrg422(RGB类格式),但从AVPlayer获取的像素缓冲区是kCVPixelFormatType_422YpCbCr8(YUV格式),格式完全不匹配导致数据解析错误,这是画面偏紫的主要原因。
把纹理创建的格式改成与像素缓冲区对应的YUV格式:
CVMetalTextureCacheCreateTextureFromImage(nil, textureCache, pixelBuffer, nil, .ycbcr422, width, height, 0, &cvTexture)
2. 编写正确的Metal片段着色器转换逻辑
格式匹配后,需要在着色器里正确解析YUV数据并转换为RGB。ProRes通常采用BT.709转换标准,同时要注意YUV范围的归一化处理:
#include <metal_stdlib> using namespace metal; fragment float4 ycbcr_to_rgb(texture2d<half, access::sample> ycbcrTex [[texture(0)]], float2 texCoord [[texture_coord(0)]]) { // 采样YUV纹理:packed 422格式每个纹理像素存储两个Y分量、一个Cb、一个Cr half4 ycbcrData = ycbcrTex.sample(sampler(coord::normalized, address::clamp_to_edge, filter::linear), texCoord); // 根据纹理坐标的奇偶性选取对应的Y分量 half y = fract(texCoord.x * ycbcrTex.get_width()) < 0.5 ? ycbcrData.x : ycbcrData.z; half cb = ycbcrData.y; half cr = ycbcrData.w; // 将YUV从视频标准范围(Y:16-235,CbCr:16-240)转换为0-1的浮点范围 y = (y - 16.0/255.0) / (219.0/255.0); cb = (cb - 128.0/255.0) / (224.0/255.0); cr = (cr - 128.0/255.0) / (224.0/255.0); // 采用BT.709标准转换为RGB half r = y + 1.5748 * cr; half g = y - 0.1873 * cb - 0.4681 * cr; half b = y + 1.8556 * cb; // 限制RGB值在0-1区间,避免颜色溢出 r = clamp(r, 0.0, 1.0); g = clamp(g, 0.0, 1.0); b = clamp(b, 0.0, 1.0); return float4(r, g, b, 1.0); }
3. 额外检查项
- 打印
CVPixelBufferGetPixelFormatType(pixelBuffer)确认像素格式确实是kCVPixelFormatType_422YpCbCr8,避免格式混淆。 - 确保纹理宽高与像素缓冲区宽高完全一致,防止拉伸导致颜色错位。
- 采样器坐标模式设置为
normalized,避免使用像素坐标导致采样位置错误。
内容的提问来源于stack exchange,提问作者JoeBayLD
相关产品推荐
相关产品推荐

