OpenGL Convert NV12 to RGB24 using shader [closed]

こ雲淡風輕ζ 提交于 2020-02-25 01:24:19

问题


I tried to write an application to display YUV image in OpenGL. I was successfully converted YUV to RGB in C++ using this snippet (source)

static long int crv_tab[256];   
static long int cbu_tab[256];   
static long int cgu_tab[256];   
static long int cgv_tab[256];   
static long int tab_76309[256]; 
static unsigned char clp[1024];   //for clip in CCIR601   

void init_yuv420p_table() 
{   
    long int crv,cbu,cgu,cgv;   
    int i,ind;      
    static int init = 0;

    if (init == 1) return;

    crv = 104597; cbu = 132201;  /* fra matrise i global.h */   
    cgu = 25675;  cgv = 53279;   

    for (i = 0; i < 256; i++)    
    {   
        crv_tab[i] = (i-128) * crv;   
        cbu_tab[i] = (i-128) * cbu;   
        cgu_tab[i] = (i-128) * cgu;   
        cgv_tab[i] = (i-128) * cgv;   
        tab_76309[i] = 76309*(i-16);   
    }   

    for (i = 0; i < 384; i++)   
        clp[i] = 0;   
    ind = 384;   
    for (i = 0;i < 256; i++)   
        clp[ind++] = i;   
    ind = 640;   
    for (i = 0;i < 384; i++)   
        clp[ind++] = 255;

    init = 1;
}

void yuv420sp_to_rgb24(YUV_TYPE type, unsigned char* yuvbuffer,unsigned char* rgbbuffer, int width,int height)   
{
    int y1, y2, u, v;    
    unsigned char *py1, *py2;   
    int i, j, c1, c2, c3, c4;   
    unsigned char *d1, *d2;   
    unsigned char *src_u;
    static int init_yuv420p = 0;

    src_u = yuvbuffer + width * height;   // u

    py1 = yuvbuffer;   // y
    py2 = py1 + width;   
    d1 = rgbbuffer;   
    d2 = d1 + 3 * width;   

    init_yuv420p_table();

    for (j = 0; j < height; j += 2)    
    {    
        for (i = 0; i < width; i += 2)    
        {
            if (type ==  FMT_NV12)
            {
                u = *src_u++;   
                v = *src_u++;      // v紧跟u,在u的下一个位置
            }
            if (type == FMT_NV21)
            {
                v = *src_u++;   
                u = *src_u++;      // u紧跟v,在v的下一个位置
            }

            c1 = crv_tab[v];   
            c2 = cgu_tab[u];   
            c3 = cgv_tab[v];   
            c4 = cbu_tab[u];   

            //up-left   
            y1 = tab_76309[*py1++];    
            *d1++ = clp[384+((y1 + c1)>>16)];     
            *d1++ = clp[384+((y1 - c2 - c3)>>16)];   
            *d1++ = clp[384+((y1 + c4)>>16)];   

            //down-left   
            y2 = tab_76309[*py2++];   
            *d2++ = clp[384+((y2 + c1)>>16)];     
            *d2++ = clp[384+((y2 - c2 - c3)>>16)];   
            *d2++ = clp[384+((y2 + c4)>>16)];   

            //up-right   
            y1 = tab_76309[*py1++];   
            *d1++ = clp[384+((y1 + c1)>>16)];     
            *d1++ = clp[384+((y1 - c2 - c3)>>16)];   
            *d1++ = clp[384+((y1 + c4)>>16)];   

            //down-right   
            y2 = tab_76309[*py2++];   
            *d2++ = clp[384+((y2 + c1)>>16)];     
            *d2++ = clp[384+((y2 - c2 - c3)>>16)];   
            *d2++ = clp[384+((y2 + c4)>>16)];   
        }
        d1  += 3*width;
        d2  += 3*width;
        py1 += width;
        py2 += width;
    }
}

In order to make my application runs smoother, instead of doing the conversion on the CPU, I do it in the fragment shader.

I separate the YUV buffer into Y_Buffer[width*height], U_Buffer[width*height/4] and V_Buffer[width*height/4] from the interleaved UV_Buffer[width*height/2] and pass it to the fragment shader. (I intended to pass the Y_Buffer and the UV_Buffer with the type format GL_RED and GL_RG respectively by using glTexImage2D but somehow when I read the G channel of the UV_Texture in the fragment shader, it always returns 0).

Below is the conversion in the fragment shader:

uniform sampler2D textureY;
uniform sampler2D textureU;
uniform sampler2D textureV;
void main() {
    vec3 yuv, rgb;
    vec3 yuv2r = vec3(1.164, 0.0, 1.596);
    vec3 yuv2g = vec3(1.164, -0.391, -0.813);
    vec3 yuv2b = vec3(1.164, 2.018, 0.0);

    yuv.x = texture(textureY, texCoord).r - 0.0625;
    yuv.y = texture(textureU, texCoord).r - 0.5;
    yuv.z = texture(textureV, texCoord).r - 0.5;

    rgb.x = dot(yuv, yuv2r);
    rgb.y = dot(yuv, yuv2g);
    rgb.z = dot(yuv, yuv2b);

    FragColor = vec4(rgb, 1.0);
}

But all I got is green and pink pixels. I'm quite a newbie in OpenGL. Can anyone point out where I may be wrong? Your help is appreciated.

Update: Add codes that load texture

FILE fp = fopen("nv21.raw", "rb");
unsigned char *yuvBuffer = new unsigned char[width*height*3/2];
fread(yuvBuffer, 1, width*height*3/2, fp);

unsigned char *vuBuffer = &yuvBuffer[width*height];

int bufSize = width*height/4;
unsigned char *uBuffer = new unsigned char[bufSize];
memset(uBuffer, 0, bufSize);
unsigned char *vBuffer = new unsigned char[bufSize];
memset(vBuffer, 0, bufSize);

unsigned char *uPtr, *vPtr;
uPtr = uBuffer;
vPtr = vBuffer;

for (int i = 0; i < bufSize; i++)
{
    *vPtr = *vuBuffer++;
    *uPtr = *vuBuffer++;
}

GLuint textureID[3];

glGenTextures(1, &textureID[0]);
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, textureID[0]);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RED, width, height, 0, GL_RED, GL_UNSIGNED_BYTE, &yuvBuffer[0]);
glBindTexture(0);

glGenTextures(1, &textureID[1]);
glActiveTexture(GL_TEXTURE1);
glBindTexture(GL_TEXTURE_2D, textureID[1]);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RED, width/2, height/2, 0, GL_RED, GL_UNSIGNED_BYTE, uBuffer);
glBindTexture(0);

glGenTextures(1, &textureID[2]);
glActiveTexture(GL_TEXTURE2);
glBindTexture(GL_TEXTURE_2D, textureID[2]);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RED, width/2, height/2, 0, GL_RED, GL_UNSIGNED_BYTE, vBuffer);
glBindTexture(0);

回答1:


By default the texture samplers in the shader program are associated to texture unit 0 (default value is 0).
You have to assign the index of the texture unit to the texture sampler uniforms, by glUniform1i. The texture unit is the binding point between the Sampler and the Texture object. e.g.:

GLint locTexY = glGetUniformLocation(program, "textureY");
GLint locTexU = glGetUniformLocation(program, "textureU");
GLint locTexV = glGetUniformLocation(program, "textureV");
glUseProgram(program);
glUniform1i(locTexY, 0); // corresponds to GL_TEXTURE0
glUniform1i(locTexU, 1); // corresponds to GL_TEXTURE1
glUniform1i(locTexV, 2); // corresponds to GL_TEXTURE2

Since OpenGL 4.20, the Binding points can be set in-shader by Layout Qualifier:

layout(binding = 0) uniform sampler2D textureY;
layout(binding = 1) uniform sampler2D textureU;
layout(binding = 2) uniform sampler2D textureV;


来源:https://stackoverflow.com/questions/60273522/opengl-convert-nv12-to-rgb24-using-shader

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!