Reputation: 143
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);
Upvotes: 3
Views: 2686
Reputation: 211166
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;
Upvotes: 1