ubuntu18.04 OpenGL开发(显示YUV)

源码参考:https://download.csdn.net/download/weixin_55163060/88382816

安装opengl库

sudo apt install libglu1-mesa-dev freeglut3-dev mesa-common-dev
安装opengl工具包
sudo apt install mesa-utils
检查opengl版本信息(桌面终端执行)
sudo glxinfo | grep "OpenGL version"
显示:OpenGL version string: 3.3 (Compatibility Profile) Mesa 20.0.8
安装glfw窗口管理器
sudo apt-get install cmake xorg-dev libglu1-mesa-dev
wget https://sourceforge.net/projects/glfw/files/glfw/3.3.5/glfw-3.3.5.zip(wget https://github.com/glfw/glfw/releases/download/3.3.5/glfw-3.3.5.zip)
unzip glfw-3.3.5.zip
cd glfw-3.3.5
mkdir build
cd build
cmake -DCMAKE_INSTALL_PREFIX=/usr/local ..
make -j8
sudo make install
【window.c】 
#include <GLFW/glfw3.h>
#include <stdio.h>

int main(void)
{
    GLFWwindow* window;

    /* Initialize the library */
    if (!glfwInit())
        return -1;

    /* Create a windowed mode window and its OpenGL context */
    window = glfwCreateWindow(640, 480, "Hello World", NULL, NULL);
    if (!window)
    {
        glfwTerminate();
        return -1;
    }

    /* Make the window's context current */
    glfwMakeContextCurrent(window);

    /* Loop until the user closes the window */
    while (!glfwWindowShouldClose(window))
    {
        //glClearColor()命令指定了清除背景时用的颜色值,这里的(1,0,0,1)代表RGB中的红色,末尾的1表示不透明度
        glClearColor(1.0, 0.0, 0.0, 1.0);
        /* Render here */
        glClear(GL_COLOR_BUFFER_BIT);

        /* Swap front and back buffers */
        glfwSwapBuffers(window);

        /* Poll for and process events */
        glfwPollEvents();
    }

    glfwTerminate();
    return 0;
}
编译
gcc -o window window.c -lglfw3 -lGL -lX11 -lm -lpthread -ldl
执行显示一个红色窗口
【linmath.h】
一个精简的线性数学库,旨在进行图形编程。 支持vec3,vec4,mat4x4和四元数
【GLAD库】(初始化后调用gl函数编译不会报错)
GLAD是用来管理OpenGL的函数指针的,所以在调用任何OpenGL的函数之前我们需要初始化GLAD。GLAD也可以使OpenGL基础渲染变得简单。
【triangle.c】
//! [code]
#include "glad.h"
#define GLAD_GL_IMPLEMENTATION
#include <GL/gl.h>
//#include "glad.h"
#define GLFW_INCLUDE_NONE
#include <GLFW/glfw3.h>

#include "linmath.h"

#include <stdlib.h>
#include <stddef.h>
#include <stdio.h>

typedef struct Vertex
{
    vec2 pos;
    vec3 col;
} Vertex;

static const Vertex vertices[3] =
{
    { { -0.6f, -0.4f }, { 1.f, 0.f, 0.f } },
    { {  0.6f, -0.4f }, { 0.f, 1.f, 0.f } },
    { {   0.f,  0.6f }, { 0.f, 0.f, 1.f } }
};

static const char* vertex_shader_text =
"#version 330\n"
"uniform mat4 MVP;\n"
"in vec3 vCol;\n"
"in vec2 vPos;\n"
"out vec3 color;\n"
"void main()\n"
"{\n"
"    gl_Position = MVP * vec4(vPos, 0.0, 1.0);\n"
"    color = vCol;\n"
"}\n";

static const char* fragment_shader_text =
"#version 330\n"
"in vec3 color;\n"
"out vec4 fragment;\n"
"void main()\n"
"{\n"
"    fragment = vec4(color, 1.0);\n"
"}\n";

static void error_callback(int error, const char* description)
{
    fprintf(stderr, "Error: %s\n", description);
}

static void key_callback(GLFWwindow* window, int key, int scancode, int action, int mods)
{
    if (key == GLFW_KEY_ESCAPE && action == GLFW_PRESS)
        glfwSetWindowShouldClose(window, GLFW_TRUE);
}

int main(void)
{
    glfwSetErrorCallback(error_callback);

    if (!glfwInit())
        exit(EXIT_FAILURE);

    glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
    glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
    glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);

    GLFWwindow* window = glfwCreateWindow(640, 480, "OpenGL Triangle", NULL, NULL);
    if (!window)
    {
        glfwTerminate();
        exit(EXIT_FAILURE);
    }

    glfwSetKeyCallback(window, key_callback);

    glfwMakeContextCurrent(window);
    if (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress))
    {
        return -1;
    }
    glfwSwapInterval(1);

    // NOTE: OpenGL error checks have been omitted for brevity

    GLuint vertex_buffer;
    glGenBuffers(1, &vertex_buffer);
    glBindBuffer(GL_ARRAY_BUFFER, vertex_buffer);
    glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);

    const GLuint vertex_shader = glCreateShader(GL_VERTEX_SHADER);
    glShaderSource(vertex_shader, 1, &vertex_shader_text, NULL);
    glCompileShader(vertex_shader);

    const GLuint fragment_shader = glCreateShader(GL_FRAGMENT_SHADER);
    glShaderSource(fragment_shader, 1, &fragment_shader_text, NULL);
    glCompileShader(fragment_shader);

    const GLuint program = glCreateProgram();
    glAttachShader(program, vertex_shader);
    glAttachShader(program, fragment_shader);
    glLinkProgram(program);

    const GLint mvp_location = glGetUniformLocation(program, "MVP");
    const GLint vpos_location = glGetAttribLocation(program, "vPos");
    const GLint vcol_location = glGetAttribLocation(program, "vCol");

    GLuint vertex_array;
    glGenVertexArrays(1, &vertex_array);
    glBindVertexArray(vertex_array);
    glEnableVertexAttribArray(vpos_location);
    glVertexAttribPointer(vpos_location, 2, GL_FLOAT, GL_FALSE,
                          sizeof(Vertex), (void*) offsetof(Vertex, pos));
    glEnableVertexAttribArray(vcol_location);
    glVertexAttribPointer(vcol_location, 3, GL_FLOAT, GL_FALSE,
                          sizeof(Vertex), (void*) offsetof(Vertex, col));

    while (!glfwWindowShouldClose(window))
    {
        int width, height;
        glfwGetFramebufferSize(window, &width, &height);
        const float ratio = width / (float) height;

        glViewport(0, 0, width, height);
        glClear(GL_COLOR_BUFFER_BIT);

        mat4x4 m, p, mvp;
        mat4x4_identity(m);
        mat4x4_rotate_Z(m, m, (float) glfwGetTime());
        mat4x4_ortho(p, -ratio, ratio, -1.f, 1.f, 1.f, -1.f);
        mat4x4_mul(mvp, p, m);

        glUseProgram(program);
        glUniformMatrix4fv(mvp_location, 1, GL_FALSE, (const GLfloat*) &mvp);
        glBindVertexArray(vertex_array);
        glDrawArrays(GL_TRIANGLES, 0, 3);

        glfwSwapBuffers(window);
        glfwPollEvents();
    }

    glfwDestroyWindow(window);

    glfwTerminate();
    exit(EXIT_SUCCESS);
}

//! [code]
编译
gcc -o triangle glad.c triangle.c -lglfw3 -lGL -lX11 -lm -lpthread -ldl
执行程序显示旋转的三角形
【YUV描述】
一般RGB用于渲染,YUV用于传输。
YUV4:4:4:完全采用表示每个像素点都有一个Y,U,V。一个YUV占 8+8+8 = 24bits,3个字节。
YUV4:2:2: 就是2:1的水平取样,垂直完全采样,表示水平的两个像素有两个Y但是只有一个U一个V的采用格式。一个YUV占 8+4+4 = 16bits 2个字节。
YUV4:2:0:就是2:1的水平取样,2:1的垂直采样,表示上下左右四个像素点有4个Y但是只取一个U和一个V,一个YUV占 8+2+2 = 12bits 1.5个字节
planar的YUV格式表示先连续存储所有像素点的Y,再紧接着存储所有的U,再就是V。Y、U和V组件存储为三个独立的数组中。
packed的YUV格式表示每个像素点的YUV是连续交替存储的,先存储像素点1的YUV再存在像素点2的YUV像素点。Y、U和V组件存储在一个数组中。每个像素点的Y,U,V是连续交错存储的
YUV420P(YU12和YV12)格式
YU12:安卓的模式。存储顺序是先存Y,再存U,最后存V。YYYYYYYY UUVV (I420格式)
YV12:存储顺序是先存Y,再存V,最后存U。YYYYYYYY VVUU
YUV420SP(NV12和NV21)格式
NV12 是 IOS 中有的模式,它的存储顺序是先存 Y 分量,再 UV 进行交替存储。
NV21 是 安卓 中有的模式,它的存储顺序是先存 Y 分量,在 VU 交替存储
【h264转YUV命令】
ffmpeg -i C:\Users\Administrator\Desktop\test.h264 -y -an -frames 1 -s 1920x1080 out.yuv
pause
【GLSL】
OpenGL着色语言(OpenGL Shading Language)是用来在OpenGL中着色编程的语言,也即开发人员写的短小的自定义程序,他们是在图形卡的GPU (Graphic Processor Unit图形处理单元)上执行的,代替了固定的渲染管线的一部分,使渲染管线中不同层次具有可编程性。比如:视图转换、投影转换等。GLSL(GL Shading Language)的着色器代码分成2个部分:Vertex Shader(顶点着色器)和Fragment(片断着色器),有时还会有Geometry Shader(几何着色器)。负责运行顶点着色的是顶点着色器。它可以得到当前OpenGL 中的状态,GLSL内置变量进行传递。GLSL其使用C语言作为基础高阶着色语言,避免了使用汇编语言或硬件规格语言的复杂性。
【yuv_show.cpp】
#include<string>
#include<fstream>
#include<sstream>
#include<iostream>
#include<stdio.h>
#include "glad.h"
#include <GLFW/glfw3.h>

typedef unsigned char BYTE;
const unsigned int SCR_WIDTH = 500;
const unsigned int SCR_HEIGHT = 600;
const int len = 1920*1080 * 3/2;
BYTE YUVdata [len];

unsigned int VBO = 0;
unsigned int VAO = 0;
unsigned int EBO = 0;
unsigned int texturePIC = 0;
int shaderProgram = 0;

GLuint texIndexarray[3];
GLuint texUniformY = 99;
GLuint texUniformU = 99;
GLuint texUniformV = 99;

void LoadPicture()
{

    
    glGenTextures(3, texIndexarray);//生成三个纹理索引
    
    glBindTexture(GL_TEXTURE_2D, texIndexarray[0]);
    //为bind的纹理设置环绕,过滤方式
    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);

    glBindTexture(GL_TEXTURE_2D, texIndexarray[1]);
    //为bind的纹理设置环绕,过滤方式
    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);
    glBindTexture(GL_TEXTURE_2D, texIndexarray[2]);
    //为bind的纹理设置环绕,过滤方式
    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);
    

    
    //使用着色器程序,返回采样器的序号
    glUseProgram(shaderProgram);//该语句必须要有;安装 指定着色器程序
    texUniformY = glGetUniformLocation(shaderProgram, "dataY");
    texUniformU = glGetUniformLocation(shaderProgram, "dataU");
    texUniformV = glGetUniformLocation(shaderProgram, "dataV");

    ----------加载数据--------------------------------------------------------
    FILE* fp = fopen("./out.yuv","rb+");//I420
    int returns  =fread(YUVdata,1,len,fp);
    int w = 1920;
    int h = 1080;
    int ysize = w*h;
    int uvsize = w * h / 4;

    void* uptr = &YUVdata[ysize];
    void* vptr = &YUVdata[ysize * 5 / 4];


    //---------------------------------------------------------------------------
    glActiveTexture(GL_TEXTURE0);
    glBindTexture(GL_TEXTURE_2D, texIndexarray[0]);// texindexarray[0] =1
    //使用GL_red表示单通道,glfw3里边没有YUV那个GL属性;
    glTexImage2D(GL_TEXTURE_2D, 0 , GL_RED, w, h ,0, GL_RED,GL_UNSIGNED_BYTE ,YUVdata);
    glUniform1i(texUniformY,0);                //通过 glUniform1i 的设置,保证每个 uniform 采样器对应着正确的纹理单元;注意这里不能用tesindexarray[0];
    

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

    glActiveTexture(GL_TEXTURE2);
    glBindTexture(GL_TEXTURE_2D, texIndexarray[2]);
    glTexImage2D(GL_TEXTURE_2D, 0, GL_RED, w/2, h/2, 0, GL_RED, GL_UNSIGNED_BYTE,vptr);
    glUniform1i(texUniformV,2);
    
    glUseProgram(0);
}


void render()
{
    glBindVertexArray(VAO);
    glUseProgram(shaderProgram);
    glDrawElements(GL_TRIANGLES,6,GL_UNSIGNED_INT,0);
    //glDrawArrays(GL_TRIANGLE_FAN,0,4);也可
    glUseProgram(0);
    glBindVertexArray(0);
}

void initmodule()
{
    //做个一模型;正方形;映射了顶点坐标和纹理坐标的对应关系
    float vertexs[] = {
        //顶点坐标-------纹理坐标(屏幕坐标翻转)
        1.0f,  1.0f, 0.0f,  1.0f, 0.0f,   
        1.0f, -1.0f, 0.0f,  1.0f, 1.0f,   
        -1.0f, -1.0f, 0.0f,  0.0f, 1.0f,   
        -1.0f,  1.0f, 0.0f,  0.0f, 0.0f    
        
        
    };
    //一个正方形是由两个三角形得来的;记录顶点的索引顺序
    unsigned int indexs[] = {
        0,1,3,
        1,2,3,
    };
    
    //做VAO
    glGenVertexArrays(1,&VAO);
    glBindVertexArray(VAO);

    //做VBO

    glGenBuffers(1, &VBO);
    glBindBuffer(GL_ARRAY_BUFFER, VBO);
    //创建显存空间
    glBufferData(GL_ARRAY_BUFFER,sizeof(vertexs), vertexs, GL_STATIC_DRAW);
    
    //设置索引缓冲
    glGenBuffers(1,&EBO);
    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER,EBO);
    glBufferData(GL_ELEMENT_ARRAY_BUFFER,sizeof(indexs),indexs,GL_STATIC_DRAW);    //加载纹理图片,生成纹理
    LoadPicture();
    
    //设置第0个锚点,3个点,不需要归一化,跨度5个float可以读下一个点
    glVertexAttribPointer(0,3,GL_FLOAT,GL_FALSE,5*sizeof(float),(void*)0);
    //打开顶点
    glEnableVertexAttribArray(0);
    //纹理属性设置,纹理在第一个锚点上(指定顶点数据)
    glVertexAttribPointer(1,2, GL_FLOAT, GL_FALSE, 5 * sizeof(float), (void*)(3 * sizeof(float)));
    //打开纹理
    glEnableVertexAttribArray(1);

    //解除绑定VBO
    glBindBuffer(GL_ARRAY_BUFFER,0);

    //解绑VAO
    glBindVertexArray(0);

    

}

void initshader(const char* verpath,const char* fragpath)
{
    //编译shader,并记录shaderID
    std::string VerCode("");
    std::string fregCode("");
    //读文件
    std::ifstream  vShaderFile;
    std::ifstream  fShaderFile;

    vShaderFile.exceptions(std::ifstream::failbit | std::ifstream::badbit);
    fShaderFile.exceptions(std::ifstream::failbit | std::ifstream::badbit);
    
    try
    {
        vShaderFile.open(verpath);
        fShaderFile.open(fragpath);

        std::stringstream vsstream, fsstream;
        vsstream << vShaderFile.rdbuf();
        fsstream << fShaderFile.rdbuf();
        VerCode = vsstream.str();
        fregCode = fsstream.str();
        
    }
    catch (const std::exception&)
    {
        std::cout << "read file error" << std::endl;
    }

    const char* vshader = VerCode.c_str();
    const char* fshader = fregCode.c_str();

    //shader 编译连接
    unsigned int vertexID = 0, fragID = 0;
    char infoLog[512];//存储错误信息
    int  successflag = 0;
    vertexID = glCreateShader(GL_VERTEX_SHADER);
    glShaderSource(vertexID,1,&vshader,NULL );
    glCompileShader(vertexID);
    //获取编译是否成功
    glGetShaderiv(vertexID,GL_COMPILE_STATUS,&successflag);
    if (!successflag)
    {
        glGetShaderInfoLog(vertexID,512,NULL,infoLog);
        std::string errstr(infoLog);
        std::cout << "v shader err"<<infoLog;
    }
    //frag
    fragID = glCreateShader(GL_FRAGMENT_SHADER);
    glShaderSource(fragID, 1, &fshader, NULL);
    glCompileShader(fragID);
    //获取编译是否成功
    glGetShaderiv(fragID, GL_COMPILE_STATUS, &successflag);
    if (!successflag)
    {
        glGetShaderInfoLog(fragID, 512, NULL, infoLog);
        std::string errstr(infoLog);
        std::cout << "f shader err"<<infoLog;
    }
    //链接
    shaderProgram = glCreateProgram();
    glAttachShader(shaderProgram,vertexID);
    glAttachShader(shaderProgram,fragID);
    
    glBindAttribLocation(shaderProgram, 0, "aPos");
    glBindAttribLocation(shaderProgram, 1, "texCoord");

    glLinkProgram(shaderProgram);
    glGetProgramiv(shaderProgram,GL_LINK_STATUS,&successflag);
    if (!successflag)
    {
        glGetShaderInfoLog(shaderProgram, 512, NULL, infoLog);
        std::string errstr(infoLog);
        std::cout << "link error";
    }

    //编译完成后,可以把中间的步骤程序删除
    glDeleteShader(vertexID);
    glDeleteShader(fragID);
}
void processInput(GLFWwindow *window)
{
    if (glfwGetKey(window, GLFW_KEY_ESCAPE) == GLFW_PRESS)
    {
        //将窗口设置为关闭,跳出循环
        glfwSetWindowShouldClose(window, true);
    }
}

void framebuffer_size_callback(GLFWwindow* window, int width, int height)
{
    glViewport(0, 0, width, height);
}

int main()
{
    //glfw初始化
    glfwInit();
    glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
    glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
    glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);

    //glfw创建窗口
    GLFWwindow* window = glfwCreateWindow(500, 600, "LearnOpenGL", NULL, NULL);
    if (window == NULL)
    {
        printf("创建窗口失败");
        //终止
        glfwTerminate();
        return -1;
    }
    //显示窗口
    glfwMakeContextCurrent(window);

    //设置回调,当窗口大小调整后将调用该回调函数
    glfwSetFramebufferSizeCallback(window, framebuffer_size_callback);

    // glad初始化
    if (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress))
    {
        printf("加载失败");
        return -1;
    }
    initshader("vertexShader.glsl", "fragmentShader.glsl");//先编译着色器
    initmodule();
    
    
    // 使用循环达到循环渲染效果
    while (!glfwWindowShouldClose(window))
    {
        //自定义输入事件
        processInput(window);

        glClearColor(0.0f,0.0f,0.0f,1.0f);
        glClear(GL_COLOR_BUFFER_BIT);
        render();
        
        //交互缓冲区,否则显示空白
        glfwSwapBuffers(window);
        //输入输出事件,否则无法对窗口进行交互
        glfwPollEvents();
    }

    //终止渲染 关闭并清理glfw本地资源
    glfwTerminate();
    return 0;
}
【vertexShader.glsl】
#version 330 core
layout(location = 0) in vec3 aPos;
layout(location = 1) in vec2 texCoord; 

out vec2 TexCoord;
void main()
{
   gl_Position = vec4(aPos.x,aPos.y,aPos.z,1.0);
   TexCoord = texCoord;
};
【fragmentShader.glsl】
#version 330 core
layout(location = 0) out vec4 FragColor;
in vec2 TexCoord;
uniform sampler2D dataY;
uniform sampler2D dataU;
uniform sampler2D dataV;
vec3 yuv;
vec3 rgb;
void main()
{
   yuv.x = texture2D(dataY, TexCoord).r-0.0625;
   yuv.y = texture2D(dataU, TexCoord).r-0.5;
   yuv.z = texture2D(dataV, TexCoord).r-0.5;

   rgb = mat3(1,              1,      1,     
            0,       -0.18732, 1.8556,    
            1.57481, -0.46813,      0) * yuv;   
    FragColor = vec4(rgb.x, rgb.y,rgb.z,1); 
};
【编译】
g++ -o yuv_show glad.c yuv_show.cpp -lglfw3 -lGL -lX11 -lm -lpthread -ldl
【运行】
1、报错:v shader err0:2(1): error: shader output explicit location requires GL_ARB_separate_shader_objects extension or GLSL 4.20
原因:fragmentShader.glsl和vertexShader.glsl搞反了
2、报错:段错误
解决方法:sudo ./yuv_show
注:render()函数中增加LoadPicture()并修改其为读取不同的yuv数据可以实现视频流的播放。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

悠哉无忧

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值