• OpenGL I420渲染(四)


    看了前三节,你学会了渲染RGB;

    本节,是YUV渲染;

    1 YUV类型:

    4:4:4    4:2:2   4:2:0

    444: 一个Y分量,就有一个U,一个V;

    422: 两个Y分量,有一个U,一个V;

    420:四个Y分量,有一个U,一个V;

    这个博客说的很详细:YUV 格式详解-史上最全 - 掘金 (juejin.cn)

    2 我们用ffmpeg从视频中提取一帧YUV;视频就用手机拍一个就行;ffmpeg能给你解析,解码;成I420

    ./ffmpeg -i  XX.mp4 -frames:v 1 out.yuv   (命令),看图 ,数据总量=宽*高*1.5

    3 YUV渲染需要分别将Y,U,V加载到显存;并且有三个采样器来采集纹理中的Y,U,V分量值;

       OpenGL无法做到直接渲染YUV,所以不同的格式的YUV数据,加载到显存后,都要在片元着色器中通过采样器得到YUV的值,然后转成rgb输出


    函数说明

    (1)GLint glGetUniformLocation(GLuint program,const GLchar *name);


      program:指定要查询的程序对象,>>>>>   本例中是着色器程序对象。

      name:要查询其位置序号的统一变量的名称。>>>>>>>本例中是采样器的名字

      描述
      glGetUniformLocation返回一个整数,表示程序对象中特定统一变量的位置(序号);本例中意思就是 :返回着色器程序中的某个采样器的序号;采样器是一个统一性变量;

    (2) glActiveTexture, glBindTexture ,glTexImage2D,glUniform1i

         3.2.1激活纹理单元;3.2.2 绑定纹理对象到激活单元,决定纹理类型;3.2.3 加载数据到显存;3.2.4将采样器序号与纹理单元对应起来

    #include<string>
    #include<fstream>
    #include<sstream>
    #include<iostream>
    #include<stdio.h>

    #include <glad/glad.h>
    #include <GLFW/glfw3.h>

    
    
    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; }
        #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); 
        };
    #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;
    };

    运行结果:


     错误1: 这里如果你的程序颜色是这样的:???

                                                 

     可能是这里写错了:

    glActiveTexture(GL_TEXTURE0);
        glBindTexture(GL_TEXTURE_2D, texIndexarray[0]);
        //使用GL_red表示单通道,glfw3里边没有YUV那个GL属性,会出现未定义;
        glTexImage2D(GL_TEXTURE_2D, 0 , GL_RED, w, h ,0, GL_RED,GL_UNSIGNED_BYTE ,YUVdata);
        glUniform1i(texUniformY, texIndexarray[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, texIndexarray[0]);
        
    
        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, texIndexarray[0]);

    错误2 :颜色这样子???

                                                    

        //---------------------------------------------------------------------------
        glActiveTexture(GL_TEXTURE0);
        glBindTexture(GL_TEXTURE_2D, texIndexarray[0]);
        //使用GL_red表示单通道,glfw3里边没有YUV那个GL属性,会出现未定义;
        glTexImage2D(GL_TEXTURE_2D, 0 , GL_RED, w, h ,0, GL_RED,GL_UNSIGNED_BYTE ,YUVdata);
        glUniform1i(texUniformY, texIndexarray[0]); //texindexarray[0] = 1
        
    
        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, texIndexarray[1]);//texindexarray[1] =2
        
    
        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, texIndexarray[2]);

    错误3,一种很常见的错误,粉绿色:

                                                                      

     这里注意,采样器的统一值,是指纹理单元,不是纹理ID

    //glUseProgram(shaderProgram);  这里未调用着色器程序
        texUniformY = glGetUniformLocation(shaderProgram, "dataY");
        texUniformU = glGetUniformLocation(shaderProgram, "dataU");
        texUniformV = glGetUniformLocation(shaderProgram, "dataV");
    
        ////----------加载数据--------------------------------------------------------
        FILE* fp = fopen("./out.yuv","rb+");//I420
        正常加载YUV
    
        //---------------------------------------------------------------------------
        glActiveTexture(GL_TEXTURE0);//纹理单元是0,那下边指定采样器统一值就应该用0;
        glBindTexture(GL_TEXTURE_2D, texIndexarray[0]);
        //使用GL_red表示单通道,glfw3里边没有YUV那个GL属性,会出现未定义;
        glTexImage2D(GL_TEXTURE_2D, 0 , GL_RED, w, h ,0, GL_RED,GL_UNSIGNED_BYTE ,YUVdata);
        glUniform1i(texUniformY, texIndexarray[0]);   // 使用纹理ID给采样器赋值是不对的         
        
    
        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, texIndexarray[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, texIndexarray[2]);//错
        
        //glUseProgram(0);

    或者单纯的未加载( not use program)着色器程序,也是粉绿色

     错误4:颜色是这样的????颜色大致相同又有点差异;缺了点红粉色;整体有一点点偏绿色

                                                     

    可能是未渲染V分量:既没有加载V分量到显存,也没采样

    glActiveTexture(GL_TEXTURE0);
        glBindTexture(GL_TEXTURE_2D, texIndexarray[0]);
        //使用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 采样器对应着正确的纹理单元
        
    
        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);
    
    -----------------------------------------------------------------
    没有加载V分量的数据到显存;也没在着色器中采样
    void main()
        {
    
        
           yuv.x = texture2D(dataY, TexCoord).r-0.0625;
           yuv.y = texture2D(dataU, 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); 
        };

    错误5 ,可能颜色是这样子,略粉

                                            

    可能的原因是,未将V数据加载到显存,然后采样器还去采了

         glActiveTexture(GL_TEXTURE0);
        glBindTexture(GL_TEXTURE_2D, texIndexarray[0]);
        //使用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 采样器对应着正确的纹理单元
        
    
        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);
        ---------------------------------未将V分量数据上传到显存--------------------------------但是片元着色器去采样了
        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); 
        };

    错误6.偏冷色调

                                 

    可能的原因,也是采样器序号的问题;

    glUseProgram(shaderProgram);
        texUniformY = glGetUniformLocation(shaderProgram, "dataY");//2
        texUniformY = glGetUniformLocation(shaderProgram, "dataU");//0
        texUniformY = glGetUniformLocation(shaderProgram, "dataV");//1
    
        ////----------加载数据--------------------------------------------------------
        FILE* fp = fopen("./out.yuv","rb+");//I420
        正常加载数据
        //---------------------------------------------------------------------------
        glActiveTexture(GL_TEXTURE0);
        glBindTexture(GL_TEXTURE_2D, texIndexarray[0]);
        //使用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 采样器对应着正确的纹理单元
        
    
        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(texUniformY, 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(texUniformY,2);
        
        glUseProgram(0);
  • 相关阅读:
    理解Web路由(浅谈前后端路由与前后端渲染)
    JavaEE开发之SpringMVC中的路由配置及参数传递详解
    Vue2.0 render: h => h(App)的解释
    vue data不可以使用箭头函数的问题解析
    9、响应式数据原理
    项目中的问题
    7-42 整型关键字的散列映射 (25分)
    7-43 字符串关键字的散列映射 (25分)
    7-45 航空公司VIP客户查询 (25分)
    7-44 基于词频的文件相似度 (30分)
  • 原文地址:https://www.cnblogs.com/8335IT/p/16256111.html
Copyright © 2020-2023  润新知