纹理:GPU中的一块数据结构,YUV数据先经过采样,转成rgb显示。

着色器代码,先通过compile编译成GPU能识别的机器语言,再交由GPU进行显示。

shader着色器,texture纹理,Utility通用工具,context,surface,

vertex shader顶点着色器,fragment shader片元着色器,

实践

1.yuv的字节数

现有宽为w高为h的一个视频,现在定义了一个data[] bytes变量并存储了这个视频的一帧数据。

  • bytes数组中,先连续存储每个像素的灰度信息,y通道占w*h个字节;
  • 然后顺序存储颜色数据,u和v分别占一个字节,每个颜色数据共占2个字节,所以uv通道占(w/2)(h/2)2=w*h/2个字。

2.纹理

2.1 纹理的组成

  • y通道的数据生成一个opengl纹理;
  • uv通道的数据生成一个opengl纹理;
  • 两个纹理作为渲染时的输入,在渲染过程中由GPU组装还原出来视频画面。

yv12是三个纹理是y一个,u一个,v一个;nv12是y一个,uv一个。

2.2 纹理格式

GL_LUMINANCE 和 GL_LUMINANCE_ALPHA 格式的纹理。

  • GL_LUMINANCE 纹理用来加载 NV21 Y Plane 的数据;
  • GL_LUMINANCE_ALPHA 纹理用来加载 UV Plane 的数据。
基本内部格式 所提取的颜色
GL_ALPHA (0,0,0,A)
GL_LUMINANCE (L,L,L,1)--->亮度
GL_LUMINANCE_ALPHA (L,L,L,A)--->透明度
GL_INTENSITY (I,I,I,I)
GL_RGB (R,G,B,1)
GL_RGBA (R,G,B,A)

3. YUV渲染步骤

  • 第一步:初始化上下文
  • 第二步:初始化着色器程序(编译链接着色器程序,生成2个纹理-->确定纹理坐标及对应的顶点坐标-->加载纹理坐标和顶点坐标数据到着色器程序);
  • 第三步:初始化buffer;
  • 第四步:初始化纹理(分别加载NV21的两个Plane数据到2个纹理);
  • 第五步:绘制。

4.代码案例

在OpenGL中,片元着色器最后输出的都是rgba的数据,所以使用OpenGL来渲染YUV数据的关键还是将YUV数据传递给着色器,并在着色器中将YUV转化为RGB。

OpenGL ES 系列教程_hongge372的博客-CSDN博客_opengles教程

第一步:初始化上下文

void initContextGL(const char* hWindow)
{// double dpr = emscripten_get_device_pixel_ratio();// emscripten_set_element_css_size(hWindow, nWmdWidth / dpr, nWndHeight / dpr);// emscripten_set_canvas_element_size(hWindow, nWmdWidth, nWndHeight);// printf("create size success\n");EmscriptenWebGLContextAttributes attrs;emscripten_webgl_init_context_attributes(&attrs);attrs.alpha = 0;
#if MAX_WEBGL_VERSION >= 2attrs.majorVersion = 2;
#endifEMSCRIPTEN_WEBGL_CONTEXT_HANDLE glContext = emscripten_webgl_create_context(hWindow, &attrs);assert(glContext);EMSCRIPTEN_RESULT res=emscripten_webgl_make_context_current(glContext);assert(res == EMSCRIPTEN_RESULT_SUCCESS);assert(emscripten_webgl_get_current_context() == glContext);printf("create context success 1730\n");
}

第二步:初始化着色器程序

/*顶点着色器代码*/
static const char g_pGLVS[] =              ///<普通纹理渲染顶点着色器
{"precision mediump float;""attribute vec4 position; ""attribute vec4 texCoord; ""varying vec4 pp; ""void main() ""{ ""    gl_Position = position; ""    pp = texCoord; ""} "
};/*像素着色器代码*/
const char* g_pGLFS =              ///<YV12片段着色器
{"precision mediump float;""varying vec4 pp; ""uniform sampler2D Ytexture; ""uniform sampler2D Utexture; ""uniform sampler2D Vtexture; ""void main() ""{ ""    float r,g,b,y,u,v; ""    y=texture2D(Ytexture, pp.st).r; ""    u=texture2D(Utexture, pp.st).r; ""    v=texture2D(Vtexture, pp.st).r; ""    y=1.1643*(y-0.0625); ""    u=u-0.5; ""    v=v-0.5; ""    r=y+1.5958*v; ""    g=y-0.39173*u-0.81290*v; ""    b=y+2.017*u; ""    gl_FragColor=vec4(r,g,b,1.0); ""} "
};
GLuint initShaderProgram()
{///< 顶点着色器相关操作GLuint nVertexShader   = glCreateShader(GL_VERTEX_SHADER);const GLchar* pVS  = g_pGLVS;GLint nVSLen       = static_cast<GLint>(strlen(g_pGLVS));glShaderSource(nVertexShader, 1, (const GLchar**)&pVS, &nVSLen);GLint nCompileRet;glCompileShader(nVertexShader);glGetShaderiv(nVertexShader, GL_COMPILE_STATUS, &nCompileRet);if(0 == nCompileRet){return SR_FAIL;}///< 片段着色器相关操作GLuint nFragmentShader = glCreateShader(GL_FRAGMENT_SHADER);const GLchar* pFS = g_pGLFS;GLint nFSLen = static_cast<GLint>(strlen(g_pGLFS));glShaderSource(nFragmentShader, 1, (const GLchar**)&pFS, &nFSLen);glCompileShader(nFragmentShader);glGetShaderiv(nFragmentShader, GL_COMPILE_STATUS, &nCompileRet);if(0 == nCompileRet){return SR_FAIL;}///<program相关GLuint nShaderProgram = glCreateProgram();glAttachShader(nShaderProgram, nVertexShader);glAttachShader(nShaderProgram, nFragmentShader);glLinkProgram(nShaderProgram);GLint nLinkRet;glGetProgramiv(nShaderProgram, GL_LINK_STATUS, &nLinkRet);if(0 == nLinkRet){return SR_FAIL;}glDeleteShader(nVertexShader);glDeleteShader(nFragmentShader);return nShaderProgram;}

功能描述:loadShader 方法通过 glCreateShader 方法创建了一个着色器;如果着色器创建成功则加载着色器源代码,并编译着色器。编译完成后检查编译情况。若编译成功则返回着色器的 ID,反之删除着色器并且打印错误信息。

GLenum type为着色器的类型,分为:GL_VERTEX_SHADER顶点着色器,GL_FRAGMENT_SHADER片段着色器。

功能描述:createProgram 方法通过调用 loadShader 方法分别加载顶点着色器与片元着色器的源代码进 GPU,并分别进行编译。

第三步:初始化buffer

GLuint g_vertexPosBuffer;
GLuint g_vertexPosBuffer;
GLuint g_texturePosBuffer;
const GLfloat g_Vertices[] = {-1.0f,  1.0f,-1.0f, -1.0f,1.0f,  1.0f,1.0f,  1.0f,-1.0f, -1.0f,1.0f, -1.0f,
};const GLfloat g_TexCoord[] = {0.0f, 0.0f,0.0f, 1.0f,1.0f, 0.0f,1.0f, 0.0f,0.0f, 1.0f,1.0f, 1.0f,
};
void initBuffers()
{GLuint vertexPosBuffer;glGenBuffers(1, &vertexPosBuffer);//创建缓冲区对象glBindBuffer(GL_ARRAY_BUFFER, vertexPosBuffer);//将该缓冲区对象绑定到管线上glBufferData(GL_ARRAY_BUFFER, sizeof(g_Vertices), g_Vertices, GL_STATIC_DRAW);glBindBuffer(GL_ARRAY_BUFFER, NULL);g_vertexPosBuffer=vertexPosBuffer;GLuint texturePosBuffer;glGenBuffers(1, &texturePosBuffer);glBindBuffer(GL_ARRAY_BUFFER, texturePosBuffer);glBufferData(GL_ARRAY_BUFFER, sizeof(g_TexCoord), g_TexCoord, GL_STATIC_DRAW);glBindBuffer(GL_ARRAY_BUFFER, NULL);g_texturePosBuffer=texturePosBuffer;}

第四步:初始化纹理

GLuint g_ShaderProgram;
GLuint g_Texture2D[3];
#define Y 0
#define U 1
#define V 2
void initTexture()
{glUseProgram(g_ShaderProgram);GLuint          nTexture2D[3];        ///<< YUV三层纹理数组glGenTextures(3, nTexture2D);for(int i = 0; i < 3; ++i){glBindTexture(GL_TEXTURE_2D, nTexture2D[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);glBindTexture(GL_TEXTURE_2D, NULL);}GLuint          nTextureUniform[3];nTextureUniform[Y] = glGetUniformLocation(g_ShaderProgram, "Ytexture");nTextureUniform[U] = glGetUniformLocation(g_ShaderProgram, "Utexture");nTextureUniform[V] = glGetUniformLocation(g_ShaderProgram, "Vtexture");glUniform1i(nTextureUniform[Y], 0);glUniform1i(nTextureUniform[U], 1);glUniform1i(nTextureUniform[V], 2);g_Texture2D[0]=nTexture2D[0];g_Texture2D[1]=nTexture2D[1];g_Texture2D[2]=nTexture2D[2];glUseProgram(NULL);
}

功能描述:将纹理图象空间映射到帧缓冲图象空间(把纹理像素映射成像素)

//纹理过滤函数glTexParameteri()glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP);param1: GL_TEXTURE_2D: 操作2D纹理.param2: GL_TEXTURE_WRAP_S: S方向上的贴图模式.param3: GL_CLAMP: 将纹理坐标限制在0.0,1.0的范围之内.如果超出了会如何呢.不会错误,只是会边缘拉伸填充.glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP);param2:这里同上,只是它是T方向glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);param2:GL_TEXTURE_MAG_FILTER: 放大过滤param3:GL_LINEAR: 线性过滤, 使用距离当前渲染像素中心最近的4个纹素加权平均值.glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_NEAREST);param2:GL_TEXTURE_MIN_FILTER: 缩小过滤param2:GL_LINEAR_MIPMAP_NEAREST: 使用GL_NEAREST对最接近当前多边形的解析度的两个层级贴图进行采样,然后用这两个值进行线性插值.

第四步 渲染YUV到纹理

int RenderFrame( unsigned char* pFrameData, int nFrameWidth, int nFrameHeight)
{if(pFrameData==NULL||nFrameWidth<=0||nFrameHeight<=0){return -1;}glClearColor(0.8f,0.8f,1.0f,1.0);glClear(GL_COLOR_BUFFER_BIT);glActiveTexture(GL_TEXTURE0);glBindTexture(GL_TEXTURE_2D, g_Texture2D[Y]);glTexImage2D(GL_TEXTURE_2D, 0, GL_LUMINANCE, nFrameWidth, nFrameHeight, 0, GL_LUMINANCE, GL_UNSIGNED_BYTE, pFrameData);glActiveTexture(GL_TEXTURE1);glBindTexture(GL_TEXTURE_2D,g_Texture2D[U] );glTexImage2D(GL_TEXTURE_2D, 0, GL_LUMINANCE, nFrameWidth / 2, nFrameHeight / 2, 0, GL_LUMINANCE, GL_UNSIGNED_BYTE, pFrameData + nFrameWidth * nFrameHeight * 5 / 4);glActiveTexture(GL_TEXTURE2);glBindTexture(GL_TEXTURE_2D, g_Texture2D[V]);glTexImage2D(GL_TEXTURE_2D, 0, GL_LUMINANCE, nFrameWidth / 2, nFrameHeight / 2, 0, GL_LUMINANCE, GL_UNSIGNED_BYTE, pFrameData + nFrameWidth * nFrameHeight);glUseProgram(g_ShaderProgram);//将所有顶点数据上传至顶点着色器的顶点缓存
#ifdef DRAW_FROM_CLIENT_MEMORYglVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 20, pos_and_color);glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 20, (void*)(pos_and_color+2));GLint nPosLoc = glGetAttribLocation(g_ShaderProgram, "position");glVertexAttribPointer(nPosLoc, 2, GL_FLOAT, GL_FALSE, 0, g_Vertices);GLint nTexcoordLoc = glGetAttribLocation(g_ShaderProgram, "texCoord");glVertexAttribPointer(nTexcoordLoc, 2, GL_FLOAT, GL_FALSE, 0, g_TexCoord);
#elseglBindBuffer(GL_ARRAY_BUFFER, g_vertexPosBuffer);GLint nPosLoc = glGetAttribLocation(g_ShaderProgram, "position");glEnableVertexAttribArray(nPosLoc);glVertexAttribPointer(nPosLoc, 2, GL_FLOAT, GL_FALSE, 0, 0);glBindBuffer(GL_ARRAY_BUFFER, NULL);glBindBuffer(GL_ARRAY_BUFFER, g_texturePosBuffer);GLint nTexcoordLoc = glGetAttribLocation(g_ShaderProgram, "texCoord");glEnableVertexAttribArray(nTexcoordLoc);glVertexAttribPointer(nTexcoordLoc, 2, GL_FLOAT, GL_FALSE, 0, 0);glBindBuffer(GL_ARRAY_BUFFER, NULL);
#endifglDrawArrays(GL_TRIANGLES, 0, 6);glDisableVertexAttribArray(nPosLoc);glDisableVertexAttribArray(nTexcoordLoc);#ifdef EXPLICIT_SWAPemscripten_webgl_commit_frame();
#endif#ifdef REPORT_RESULTREPORT_RESULT(0);
#endifglUseProgram(NULL);return SR_OK;
}
void glTexImage2D(int target,  // 目标纹理,此处必须为GL_TEXTURE_2Dint level,   // 执行细节级别,0是最基本的图像级别,n表示第N级贴图细化级别int internalformat,  // 指定纹理中的颜色组件int width,  // 指定纹理图像的宽度,int height, //  指定纹理图像的高度,int border, // 指定边框的宽度。必须为0int format, // 像素数据的颜色格式int type, // 指定像素数据的数据类型java.nio.Buffer pixels // 指定内存中指向图像数据的指针
);

internalformat这个参数指定纹理中的颜色组件,可选的值如下:

  • 通常使用GL_RGBA这种颜色组件,它会单独保存R,G,B,A四个数据;
  • 在渲染YUV数据的时候,使用GL_LUMINANCE和GL_LUMINANCE_ALPHA。使用GL_LUMINANCE时,可以将Y分量存储到像素的各个通道内,这样在着色器中,我们可以通过R,G,B任意一个分量来获取到Y值。U,V分量同理
  • 使用GL_LUMINANCE_ALPHA的时候,首先存储亮度,然后是alpha值,利用这一点可以将U值存储到像素的A通道,V值存储到R,G,B通道

完整代码:

#include <GLES2/gl2.h>
// #include <EGL/egl.h>
// #include <EGL/eglext.h>
#include <emscripten.h>
// #include <emscripten/bind.h>
// #include <emscripten/val.h>
#include <emscripten/html5.h>// #include  <X11/Xlib.h>
// #include  <X11/Xatom.h>
// #include  <X11/Xutil.h>#include <string.h>
#include <stdio.h>
#include <iostream>
#include <assert.h>#define SR_OK 0;
#define SR_FAIL 1;#define Y 0
#define U 1
#define V 2/// esCreateWindow flag - RGB color buffer
#define ES_WINDOW_RGB           0
/// esCreateWindow flag - ALPHA color buffer
#define ES_WINDOW_ALPHA         1
/// esCreateWindow flag - depth buffer
#define ES_WINDOW_DEPTH         2
/// esCreateWindow flag - stencil buffer
#define ES_WINDOW_STENCIL       4
/// esCreateWindow flat - multi-sample buffer
#define ES_WINDOW_MULTISAMPLE   8GLuint flags=ES_WINDOW_RGB;
GLuint g_ShaderProgram;
GLuint g_Texture2D[3];
GLuint g_vertexPosBuffer;
GLuint g_texturePosBuffer;/*顶点着色器代码*/
static const char g_pGLVS[] =              ///<普通纹理渲染顶点着色器
{"precision mediump float;""attribute vec4 position; ""attribute vec4 texCoord; ""varying vec4 pp; ""void main() ""{ ""    gl_Position = position; ""    pp = texCoord; ""} "
};/*像素着色器代码*/
const char* g_pGLFS =              ///<YV12片段着色器
{"precision mediump float;""varying vec4 pp; ""uniform sampler2D Ytexture; ""uniform sampler2D Utexture; ""uniform sampler2D Vtexture; ""void main() ""{ ""    float r,g,b,y,u,v; ""    y=texture2D(Ytexture, pp.st).r; ""    u=texture2D(Utexture, pp.st).r; ""    v=texture2D(Vtexture, pp.st).r; ""    y=1.1643*(y-0.0625); ""    u=u-0.5; ""    v=v-0.5; ""    r=y+1.5958*v; ""    g=y-0.39173*u-0.81290*v; ""    b=y+2.017*u; ""    gl_FragColor=vec4(r,g,b,1.0); ""} "
};const GLfloat g_Vertices[] = {-1.0f,  1.0f,-1.0f, -1.0f,1.0f,  1.0f,1.0f,  1.0f,-1.0f, -1.0f,1.0f, -1.0f,
};const GLfloat g_TexCoord[] = {0.0f, 0.0f,0.0f, 1.0f,1.0f, 0.0f,1.0f, 0.0f,0.0f, 1.0f,1.0f, 1.0f,
};#ifdef __cplusplus
extern "C" {
#endif
EMSCRIPTEN_KEEPALIVE
void initBuffers()
{GLuint vertexPosBuffer;glGenBuffers(1, &vertexPosBuffer);//创建缓冲区对象glBindBuffer(GL_ARRAY_BUFFER, vertexPosBuffer);//将该缓冲区对象绑定到管线上glBufferData(GL_ARRAY_BUFFER, sizeof(g_Vertices), g_Vertices, GL_STATIC_DRAW);glBindBuffer(GL_ARRAY_BUFFER, NULL);g_vertexPosBuffer=vertexPosBuffer;GLuint texturePosBuffer;glGenBuffers(1, &texturePosBuffer);glBindBuffer(GL_ARRAY_BUFFER, texturePosBuffer);glBufferData(GL_ARRAY_BUFFER, sizeof(g_TexCoord), g_TexCoord, GL_STATIC_DRAW);glBindBuffer(GL_ARRAY_BUFFER, NULL);g_texturePosBuffer=texturePosBuffer;}
EMSCRIPTEN_KEEPALIVE
void initTexture()
{glUseProgram(g_ShaderProgram);GLuint          nTexture2D[3];        ///<< YUV三层纹理数组glGenTextures(3, nTexture2D);for(int i = 0; i < 3; ++i){glBindTexture(GL_TEXTURE_2D, nTexture2D[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);glBindTexture(GL_TEXTURE_2D, NULL);}GLuint          nTextureUniform[3];nTextureUniform[Y] = glGetUniformLocation(g_ShaderProgram, "Ytexture");nTextureUniform[U] = glGetUniformLocation(g_ShaderProgram, "Utexture");nTextureUniform[V] = glGetUniformLocation(g_ShaderProgram, "Vtexture");glUniform1i(nTextureUniform[Y], 0);glUniform1i(nTextureUniform[U], 1);glUniform1i(nTextureUniform[V], 2);g_Texture2D[0]=nTexture2D[0];g_Texture2D[1]=nTexture2D[1];g_Texture2D[2]=nTexture2D[2];glUseProgram(NULL);
}
EMSCRIPTEN_KEEPALIVE
GLuint initShaderProgram()
{///< 顶点着色器相关操作GLuint nVertexShader   = glCreateShader(GL_VERTEX_SHADER);const GLchar* pVS  = g_pGLVS;GLint nVSLen       = static_cast<GLint>(strlen(g_pGLVS));glShaderSource(nVertexShader, 1, (const GLchar**)&pVS, &nVSLen);GLint nCompileRet;glCompileShader(nVertexShader);glGetShaderiv(nVertexShader, GL_COMPILE_STATUS, &nCompileRet);if(0 == nCompileRet){return SR_FAIL;}///< 片段着色器相关操作GLuint nFragmentShader = glCreateShader(GL_FRAGMENT_SHADER);const GLchar* pFS = g_pGLFS;GLint nFSLen = static_cast<GLint>(strlen(g_pGLFS));glShaderSource(nFragmentShader, 1, (const GLchar**)&pFS, &nFSLen);glCompileShader(nFragmentShader);glGetShaderiv(nFragmentShader, GL_COMPILE_STATUS, &nCompileRet);if(0 == nCompileRet){return SR_FAIL;}///<program相关GLuint nShaderProgram = glCreateProgram();glAttachShader(nShaderProgram, nVertexShader);glAttachShader(nShaderProgram, nFragmentShader);glLinkProgram(nShaderProgram);GLint nLinkRet;glGetProgramiv(nShaderProgram, GL_LINK_STATUS, &nLinkRet);if(0 == nLinkRet){return SR_FAIL;}glDeleteShader(nVertexShader);glDeleteShader(nFragmentShader);return nShaderProgram;}
EMSCRIPTEN_KEEPALIVE
void initContextGL(const char* hWindow)
{// double dpr = emscripten_get_device_pixel_ratio();// emscripten_set_element_css_size(hWindow, nWmdWidth / dpr, nWndHeight / dpr);// emscripten_set_canvas_element_size(hWindow, nWmdWidth, nWndHeight);// printf("create size success\n");EmscriptenWebGLContextAttributes attrs;emscripten_webgl_init_context_attributes(&attrs);attrs.alpha = 0;
#if MAX_WEBGL_VERSION >= 2attrs.majorVersion = 2;
#endifEMSCRIPTEN_WEBGL_CONTEXT_HANDLE glContext = emscripten_webgl_create_context(hWindow, &attrs);assert(glContext);EMSCRIPTEN_RESULT res=emscripten_webgl_make_context_current(glContext);assert(res == EMSCRIPTEN_RESULT_SUCCESS);assert(emscripten_webgl_get_current_context() == glContext);printf("create context success 1730\n");
}
EMSCRIPTEN_KEEPALIVE
int init(const char* hWindow)
{ //初始化ContextinitContextGL(hWindow);//初始化着色器程序g_ShaderProgram=initShaderProgram();//初始化bufferinitBuffers();//初始化纹理initTexture();return 1;
}
EMSCRIPTEN_KEEPALIVE
int RenderFrame( unsigned char* pFrameData, int nFrameWidth, int nFrameHeight)
{if(pFrameData==NULL||nFrameWidth<=0||nFrameHeight<=0){return -1;}glClearColor(0.8f,0.8f,1.0f,1.0);glClear(GL_COLOR_BUFFER_BIT);glActiveTexture(GL_TEXTURE0);glBindTexture(GL_TEXTURE_2D, g_Texture2D[Y]);glTexImage2D(GL_TEXTURE_2D, 0, GL_LUMINANCE, nFrameWidth, nFrameHeight, 0, GL_LUMINANCE, GL_UNSIGNED_BYTE, pFrameData);glActiveTexture(GL_TEXTURE1);glBindTexture(GL_TEXTURE_2D,g_Texture2D[U] );glTexImage2D(GL_TEXTURE_2D, 0, GL_LUMINANCE, nFrameWidth / 2, nFrameHeight / 2, 0, GL_LUMINANCE, GL_UNSIGNED_BYTE, pFrameData + nFrameWidth * nFrameHeight * 5 / 4);glActiveTexture(GL_TEXTURE2);glBindTexture(GL_TEXTURE_2D, g_Texture2D[V]);glTexImage2D(GL_TEXTURE_2D, 0, GL_LUMINANCE, nFrameWidth / 2, nFrameHeight / 2, 0, GL_LUMINANCE, GL_UNSIGNED_BYTE, pFrameData + nFrameWidth * nFrameHeight);glUseProgram(g_ShaderProgram);//将所有顶点数据上传至顶点着色器的顶点缓存
#ifdef DRAW_FROM_CLIENT_MEMORYglVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 20, pos_and_color);glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 20, (void*)(pos_and_color+2));GLint nPosLoc = glGetAttribLocation(g_ShaderProgram, "position");glVertexAttribPointer(nPosLoc, 2, GL_FLOAT, GL_FALSE, 0, g_Vertices);GLint nTexcoordLoc = glGetAttribLocation(g_ShaderProgram, "texCoord");glVertexAttribPointer(nTexcoordLoc, 2, GL_FLOAT, GL_FALSE, 0, g_TexCoord);
#elseglBindBuffer(GL_ARRAY_BUFFER, g_vertexPosBuffer);GLint nPosLoc = glGetAttribLocation(g_ShaderProgram, "position");glEnableVertexAttribArray(nPosLoc);glVertexAttribPointer(nPosLoc, 2, GL_FLOAT, GL_FALSE, 0, 0);glBindBuffer(GL_ARRAY_BUFFER, NULL);glBindBuffer(GL_ARRAY_BUFFER, g_texturePosBuffer);GLint nTexcoordLoc = glGetAttribLocation(g_ShaderProgram, "texCoord");glEnableVertexAttribArray(nTexcoordLoc);glVertexAttribPointer(nTexcoordLoc, 2, GL_FLOAT, GL_FALSE, 0, 0);glBindBuffer(GL_ARRAY_BUFFER, NULL);
#endifglDrawArrays(GL_TRIANGLES, 0, 6);glDisableVertexAttribArray(nPosLoc);glDisableVertexAttribArray(nTexcoordLoc);#ifdef EXPLICIT_SWAPemscripten_webgl_commit_frame();
#endif#ifdef REPORT_RESULTREPORT_RESULT(0);
#endifglUseProgram(NULL);return SR_OK;
}
#ifdef __cplusplus
}
#endif

OpenGL/OpenGL ES入门:渲染YUV数据实践相关推荐

  1. Android音视频学习系列(六) — 掌握视频基础知识并使用OpenGL ES 2.0渲染YUV数据

    系列文章 Android音视频学习系列(一) - JNI从入门到精通 Android音视频学习系列(二) - 交叉编译动态库.静态库的入门 Android音视频学习系列(三) - Shell脚本入门 ...

  2. Unity 渲染YUV数据 ---- 以Unity渲染Android Camera数据为例子

    1 背景 一般Unity都是RGB直接渲染的,但是总有特殊情况下,需要渲染YUV数据.比如,Unity读取Android的Camera YUV数据,并渲染.本文就基于这种情况,来展开讨论. Unity ...

  3. 【使用D3D11直接渲染YUV数据】

    使用D3D11直接渲染YUV数据 最初学习D3D11时,采取的是直接渲染RGB数据的方式,因为采集的时候采出来的是YUV420格式,需要利用libyuv库把YUVI420转成RGB格式.但是在实际项目 ...

  4. android 渲染yuv数据,Android opengl渲染yuv420例子

    [实例简介] Android下使用OpenGL渲染yuv420p图像并显示.例子中提供了两种类型,一种使用GLSurfaceView在onDrawframe中调用native方法绘制,另外一种使用EG ...

  5. Android音视频学习系列(五) — 掌握音频基础知识并使用AudioTrack、OpenSL ES渲染PCM数据

    系列文章 Android音视频学习系列(一) - JNI从入门到精通 Android音视频学习系列(二) - 交叉编译动态库.静态库的入门 Android音视频学习系列(三) - Shell脚本入门 ...

  6. Android用surface直接显示yuv数据(三)

    本文用Java创建UI并联合JNI层操作surface来直接显示yuv数据(yv12),开发环境为Android 4.4,全志A23平台. package com.example.myyuvviewe ...

  7. Android OpenGL ES 学习(十一) –渲染YUV视频以及视频抖音特效

    OpenGL 学习教程 Android OpenGL ES 学习(一) – 基本概念 Android OpenGL ES 学习(二) – 图形渲染管线和GLSL Android OpenGL ES 学 ...

  8. Android平台上基于OpenGl渲染yuv视频

    本篇文章已授权微信公众号 guolin_blog (郭霖)独家发布 更多音视频开发文章,请看:音视频开发专栏 介绍一个自己刚出炉的音视频播放录制开源项目 前言 这是我音视频专栏的第一篇实例解析,也算是 ...

  9. android 视频播放滤镜,用openGL ES+MediaPlayer 渲染播放视频+滤镜效果

    之前曾经写过用SurfaceView,TextureView+MediaPlayer 播放视频,和 ffmpeg avi解码后SurfaceView播放视频,今天再给大家来一篇openGL ES+Me ...

最新文章

  1. 关于ASPNET在IIS一些问题的经验总结
  2. 塔勒布: 2019-nCoV的系统风险:一份笔记
  3. git 创建新分支,合并分支等问题
  4. 面向对象的程序设计之原型模式
  5. 新一代組合創新架構師_學習地圖
  6. echart php mysql简书_echarts-自定义构建
  7. 2016猴年春节有感
  8. jQuery——入门基础(获取元素、样式、属性,选择集、过滤器、样式类)
  9. Java读取excel文件 将Excel文件变为二维String数组 .
  10. C语言实现文件类型统计程序,C语言实现文件类型统计函数
  11. 残差曲线意义_生存曲线(三):统计分析方法这么多,到底选哪个?
  12. 第一百六十四节,jQuery,常规选择器
  13. linux火狐怎么清除缓存文件,火狐浏览器如何设置_火狐浏览器怎样清除缓存
  14. 移动通信原理、技术与系统——概述
  15. python地区房价数据分析_数据分析——房价分析
  16. DropdownMenu 下拉菜单的使用
  17. Cesium 三维球转动监听事件(相机监听事件)并且获取当前中心点位置
  18. win7 查看php版本信息,window_Windows系统版本怎么看?2种查看windows版本的方法介绍,前段时间微软正式开始向Win7和 - phpStudy...
  19. 使用2019.2 Terrain Tools更新来加速Terrain Material绘画
  20. 如何解决Windows10启动QQ时报错无法访问个人文件夹?

热门文章

  1. mini2440裸机音乐播放器(非常久曾经的笔记)
  2. Zookeeper小解
  3. 计算机系统维护博客,关于Windows系统简单日常维护
  4. 博客搬家系列(一)-简介
  5. 植物大战僵尸java 7723_植物大战僵尸BT版
  6. 【Linux】线程互斥
  7. Win10系统设置IP无法保存解决方式
  8. 使按钮变灰不可用方法
  9. 使用C++实现谷歌身份验证器(Google Authenticator)
  10. android 解锁mac,论一台笔记本三个不同系统的玩法,解锁正确姿势