OpenGL 使用着色器将 NV12 转换为 RGB24
OpenGL Convert NV12 to RGB24 using shader
我尝试编写一个应用程序在 OpenGL 中显示 YUV 图像。我使用此代码段 (source)
在 C++ 中成功将 YUV 转换为 RGB
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;
}
}
为了让我的应用程序运行更流畅,我没有在 CPU 上进行转换,而是在片段着色器中进行转换。
我把YUV buffer分成了Y_Buffer[width*height], U_Buffer[width*height/4] and V_Buffer[width*height/4 ] 从交错的 UV_Buffer[width*height/2] 传递给片段着色器。 (我打算通过使用 glTexImage2D 分别传递类型为 format GL_RED 和 GL_RG 的 Y_Buffer 和 UV_Buffer 但不知何故当我在片段着色器中读取 UV_Texture 的 G 通道时,它总是 returns 0)。
下面是片段着色器中的转换:
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);
}
但我得到的只是绿色和粉色像素。我是 OpenGL 的新手。谁能指出我可能错在哪里?感谢您的帮助。
更新:添加加载纹理的代码
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);
默认情况下,着色器程序中的纹理采样器关联到纹理单元 0(默认值为 0)。
您必须通过 glUniform1i
. The texture unit is the binding point between the Sampler and the Texture 对象将纹理单元的索引分配给纹理采样器制服。例如:
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
从 OpenGL 4.20 开始,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;
我尝试编写一个应用程序在 OpenGL 中显示 YUV 图像。我使用此代码段 (source)
在 C++ 中成功将 YUV 转换为 RGBstatic 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;
}
}
为了让我的应用程序运行更流畅,我没有在 CPU 上进行转换,而是在片段着色器中进行转换。
我把YUV buffer分成了Y_Buffer[width*height], U_Buffer[width*height/4] and V_Buffer[width*height/4 ] 从交错的 UV_Buffer[width*height/2] 传递给片段着色器。 (我打算通过使用 glTexImage2D 分别传递类型为 format GL_RED 和 GL_RG 的 Y_Buffer 和 UV_Buffer 但不知何故当我在片段着色器中读取 UV_Texture 的 G 通道时,它总是 returns 0)。
下面是片段着色器中的转换:
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);
}
但我得到的只是绿色和粉色像素。我是 OpenGL 的新手。谁能指出我可能错在哪里?感谢您的帮助。
更新:添加加载纹理的代码
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);
默认情况下,着色器程序中的纹理采样器关联到纹理单元 0(默认值为 0)。
您必须通过 glUniform1i
. The texture unit is the binding point between the Sampler and the Texture 对象将纹理单元的索引分配给纹理采样器制服。例如:
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
从 OpenGL 4.20 开始,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;