1. 程式人生 > 其它 >OpenGL學習筆記--渲染yuv紋理

OpenGL學習筆記--渲染yuv紋理

OpenGL視訊學習資料:https://pan.baidu.com/s/1muWuuuo1_89AijQRNOcJmg
提取碼:xcwn

一般ffmpeg解碼後的資料型別都是I420,即YUV420P,OpenGL沒有提供直接渲染yuv的介面,我們可以通過可程式設計渲染管線,利用多重紋理將Y、U、V紋理分別傳入,在片元著色器GL_FRAGMENT_SHADER中將yuv進行矩陣轉化成RGB,然後進行渲染。

GLSL簡單介紹
OpenGL渲染管線的知識可以參考部落格http://www.cnblogs.com/yyxt/p/4056417.html
頂點著色器和片元著色器是必須的。

GLSL的語法和C語言很類似。每一個Shader程式都有一個main函式,這一點和c語言是一樣的。這裡的變數命名規則保持跟c一樣就行了,注意gl_開頭的變數名是系統內建的變數。
變數型別:
attribute:外部傳入頂點著色器的變數,每一個頂點都會有這兩個屬性。變化率高,用於定義每個點。
varying:用於頂點著色器和片元著色器之間相互傳遞的引數。
uniform:外部傳入片元著色器的變數,變化率較低,對於可能在整個渲染過程沒有改變,只是個常量。
資料型別:
vec2:包含了2個浮點數的向量
vec3:包含了3個浮點數的向量
vec4:包含了4個浮點數的向量
sampler1D:1D紋理著色器
sampler2D:2D紋理著色器
sampler3D:3D紋理著色器
mat2:2*2維矩陣
mat3:3*3維矩陣
mat4:4*4維矩陣
全域性變數:
gl_Position:原始的頂點資料在Vertex Shader中經過平移、旋轉、縮放等數學變換後,生成新的頂點位置(一個四維 (vec4) 變數,包含頂點的 x、y、z 和 w 值)。新的頂點位置通過在Vertex Shader中寫入gl_Position傳遞到渲染管線的後繼階段繼續處理。
gl_FragColor:Fragment Shader的輸出,它是一個四維變數(或稱為 vec4)。gl_FragColor 表示在經過著色器程式碼處理後,正在呈現的畫素的 R、G、B、A 值。
Vertex Shader是作用於每一個頂點的,如果Vertex有三個點,那麼Vertex Shader會被執行三次。Fragment Shader是作用於每個畫素的,一個畫素執行一次。從原始碼中可以看出,畫素的轉換在Fragment Shader中完成。

總結一句話就是頂點著色器搞定位置,片元著色器搞定顏色。

建立YUV著色器
流程圖:
開始
建立頂點著色器和片元著色器 glCreateShader
設定GLSL原始碼 glShaderSource
編譯GLSL原始碼 glCompileShader
建立一個新的程式 glCreateProgram
關聯著色器 glAttachShader
繫結attribute變數 glBindAttribLocation
連結程式 glLinkProgram
獲取uniform變數 glGetUniformLocation
結束
程式碼示例
GLuint prog_yuv;
GLuint texUniformY,texUniformU,texUniformV;
GLuint tex_yuv[3];
enum E_VER_ATTR{ver_attr_ver = 3, ver_attr_tex = 4, ver_attr_num};

struct Texture{
GLuint texID; // glGenTextures分配的ID
GLuint type; // 資料型別如GL_RGB
GLint width;
GLint height;
GLint bpp;
GLubyte* data; // 畫素資料
};

// 載入YUV著色器
void loadYUVShader(){
GLuint vs = glCreateShader(GL_VERTEX_SHADER);
GLuint fs = glCreateShader(GL_FRAGMENT_SHADER);

char szVS[] = " \
attribute vec4 verIn; \
attribute vec2 texIn; \
varying vec2 texOut; \
\
void main(){ \
gl_Position = verIn; \
texOut = texIn; \
} \
";
const GLchar* pszVS = szVS;
GLint len = strlen(szVS);
glShaderSource(vs, 1, (const GLchar**)&pszVS, &len);

char szFS[] = " \
varying vec2 texOut; \
uniform sampler2D tex_y; \
uniform sampler2D tex_u; \
uniform sampler2D tex_v; \
\
void main(){ \
vec3 yuv; \
vec3 rgb; \
yuv.x = texture2D(tex_y, texOut).r; \
yuv.y = texture2D(tex_u, texOut).r - 0.5; \
yuv.z = texture2D(tex_v, texOut).r - 0.5; \
rgb = mat3( 1, 1, 1, \
0, -0.39465, 2.03211, \
1.13983, -0.58060, 0) * yuv; \
gl_FragColor = vec4(rgb, 1); \
} \
";
const GLchar* pszFS = szFS;
len = strlen(szFS);
glShaderSource(fs, 1, (const GLchar**)&pszFS, &len);

glCompileShader(vs);
glCompileShader(fs);

//#ifdef _DEBUG
GLint iRet = 0;
glGetShaderiv(vs, GL_COMPILE_STATUS, &iRet);
glGetShaderiv(fs, GL_COMPILE_STATUS, &iRet);
//#endif

prog_yuv = glCreateProgram();

glAttachShader(prog_yuv, vs);
glAttachShader(prog_yuv, fs);

glBindAttribLocation(prog_yuv, ver_attr_ver, "verIn");
glBindAttribLocation(prog_yuv, ver_attr_tex, "texIn");

glLinkProgram(prog_yuv);

//#ifdef _DEBUG
glGetProgramiv(prog_yuv, GL_LINK_STATUS, &iRet);
//#endif

glValidateProgram(prog_yuv);

texUniformY = glGetUniformLocation(prog_yuv, "tex_y");
texUniformU = glGetUniformLocation(prog_yuv, "tex_u");
texUniformV = glGetUniformLocation(prog_yuv, "tex_v");


static const GLfloat vertices[] = {
-1.0f, -1.0f,
1.0f, -1.0f,
-1.0f, 1.0f,
1.0f, 1.0f,
};

static const GLfloat textures[] = {
0.0f, 1.0f,
1.0f, 1.0f,
0.0f, 0.0f,
1.0f, 0.0f,
};

// reverse
//static const GLfloat textures[] = {
// 0.0f, 0.0f,
// 1.0f, 0.0f,
// 0.0f, 1.0f,
// 1.0f, 1.0f,
//};

glVertexAttribPointer(ver_attr_ver, 2, GL_FLOAT, GL_FALSE, 0, vertices);
glEnableVertexAttribArray(ver_attr_ver);

glVertexAttribPointer(ver_attr_tex, 2, GL_FLOAT, GL_FALSE, 0, textures);
glEnableVertexAttribArray(ver_attr_tex);

glGenTextures(3, tex_yuv);
for (int i = 0; i < 3; i++){
glBindTexture(GL_TEXTURE_2D, tex_yuv[i]);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
}
}

// 繪畫YUV資料
void drawYUV(Texture* tex){
glUseProgram(prog_yuv);

glMatrixMode(GL_PROJECTION);
glLoadIdentity();

int w = tex->width;
int h = tex->height;
int y_size = w*h;
GLubyte* y = tex->data;
GLubyte* u = y + y_size;
GLubyte* v = u + (y_size>>2);

glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, tex_yuv[0]);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RED, w, h, 0, GL_RED, GL_UNSIGNED_BYTE, y);
glUniform1i(texUniformY, 0);

glActiveTexture(GL_TEXTURE1);
glBindTexture(GL_TEXTURE_2D, tex_yuv[1]);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RED, w/2, h/2, 0, GL_RED, GL_UNSIGNED_BYTE, u);
glUniform1i(texUniformU, 1);

glActiveTexture(GL_TEXTURE2);
glBindTexture(GL_TEXTURE_2D, tex_yuv[2]);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RED, w/2, h/2, 0, GL_RED, GL_UNSIGNED_BYTE, v);
glUniform1i(texUniformV, 2);

glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
}


這裡只是貼出了關鍵的載入YUV著色器,和繪畫YUV資料關鍵的程式碼
————————————————
版權宣告:本文為CSDN博主「ithewei」的原創文章,遵循CC 4.0 BY-SA版權協議,轉載請附上原文出處連結及本宣告。
原文連結:https://blog.csdn.net/GG_SiMiDa/article/details/74474780