【问题标题】:Colour/chroma texture is stretched when converting from YUV-NV12 to RGB using OpenGL ES使用 OpenGL ES 从 YUV-NV12 转换为 RGB 时,颜色/色度纹理被拉伸
【发布时间】:2020-10-29 16:04:36
【问题描述】:

我有一个 YUV-NV12 格式图像数据的字节缓冲区。当我尝试将其转换为 RGB 时,我得到一个带有拉伸颜色(色度)层的输出,如下图所示。

我关注了这个很棒的answer,它指导将 YUV-NV21 转换为 RGB。由于 NV-12 只是具有翻转 U 和 V 数据的 NV-21,我应该做的唯一更改是替换片段着色器中的 u 和 v 值。

顶点着色器:

precision mediump float;
uniform mat4 uMVPMatrix;
attribute vec4 vPosition;
attribute vec4 vTextureCoordinate;
varying vec2 position;

void main()
{
   gl_Position = uMVPMatrix * vPosition;
   position = vTextureCoordinate.xy;
}

片段着色器:

precision mediump float;
varying vec2 position;
uniform sampler2D uTextureY;
uniform sampler2D uTextureUV;

void main() 
{
    float y, u, v;
    y = texture2D(uTextureY, position).r;
    u = texture2D(uTextureUV, position).a - 0.5;
    v = texture2D(uTextureUV, position).r - 0.5;

    float r, g, b;
    r = y + 1.13983 * v;
    g = y - 0.39465 * u - 0.58060 * v;
    b = y + 2.03211 * u;

    gl_FragColor = vec4(r, g, b, 1.0);
}

将图像数据拆分并放入 2 个ByteBuffer 中,即mYBuffer 和mUVBuffer。 mSourceImage 只是一个Buffer,其中包含byte 数据的图像数据。

ByteBuffer bb = (ByteBuffer) mSourceImage;
if (bb == null) {
    return;
}
int size = mWidth * mHeight;

bb.position(0).limit(size);
mYBuffer = bb.slice();
bb.position(size).limit(bb.remaining());
mUVBuffer = bb.slice();

生成纹理:

GLES20.glGenTextures(2, mTexture, 0);

for(int i = 0; i < 2; i++) {
    GLES20.glActiveTexture(GLES20.GL_TEXTURE0 + i);
    GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, mTexture[i]);

    GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MIN_FILTER, GLES20.GL_LINEAR);
    GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MAG_FILTER, GLES20.GL_LINEAR);
    GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_S, GLES20.GL_CLAMP_TO_EDGE);
    GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_T, GLES20.GL_CLAMP_TO_EDGE);
}

将缓冲区数据传递给纹理:

mTextureYHandle = GLES20.glGetUniformLocation(mProgramId, "uTextureY");
mTextureUVHandle = GLES20.glGetUniformLocation(mProgramId, "uTextureUV");

GLES20.glActiveTexture(GLES20.GL_TEXTURE0);
GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, mTexture[0]);
GLES20.glTexImage2D(GLES20.GL_TEXTURE_2D, 0, GLES20.GL_LUMINANCE, mWidth, mHeight, 0, GLES20.GL_LUMINANCE, GLES20.GL_UNSIGNED_BYTE, mYBuffer);
GLES20.glUniform1i(mTextureYHandle, 0);

GLES20.glActiveTexture(GLES20.GL_TEXTURE1);
GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, mTexture[1]);
GLES20.glTexImage2D(GLES20.GL_TEXTURE_2D, 0, GLES20.GL_LUMINANCE_ALPHA, mWidth / 2, mHeight / 2, 0, GLES20.GL_LUMINANCE_ALPHA, GLES20.GL_UNSIGNED_BYTE, mUVBuffer);
GLES20.glUniform1i(mTextureUVHandle, 1);

我不明白为什么我会得到这样的输出。任何帮助将不胜感激。

【问题讨论】:

    标签: android opengl-es yuv


    【解决方案1】:

    没关系,这是我的代码中的一个小错误。

    分割字节缓冲区时,我使用bb.position(size).limit(bb.remaining()) 作为UV 缓冲区。由于某种原因,bb.remaining() 在获得一些帧后变为 0(这实际上是相机预览)。所以我把它改成了bb.position(size).limit(size + size / 2)。

    也是我通过阅读this所做的假设,

    我应该做的唯一更改是替换片段着色器中的 u 和 v 值

    似乎是错误的。观察到GL20.GL_LUMINANCE_ALPHA 将始终将U 字节放入纹理的A 组件中,并将V 字节放入R、G、B 组件中(您可以使用其中任何一个)。因此,无需在片段着色器中交换 u 和 v 值(我已使用正确的片段着色器代码编辑了我的问题)。

    我会保留这个问题,希望这对将来的人有所帮助。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2014-02-25
      • 1970-01-01
      • 2014-02-26
      • 2013-06-10
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多