问题
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