zoukankan      html  css  js  c++  java
  • 【OpenGL】GLSL学习笔记(持续更新)

    由于这里的知识点很细碎又不是很多,所以我边学OpenGl一边把需要用到的GLSL知识写上去。

    0.概念和初始化:

    着色器分为顶点着色器(Vertex Shader)和片元着色器(Fragment Shader),语法类似C++,OpenGL对每一个顶点都执行一次顶点着色器,对所有片元执行片元着色器(片元可以狭隘的理解为像素)。

    初始化:

    In C++:

    GLuint programID = LoadShaders( "TransformVertexShader.vertexshader", "ColorFragmentShader.fragmentshader" );

    获得到的是链接的着色器句柄。但是LoadShaders却需要我们自己编写。

    GLuint LoadShaders(const char * vertex_file_path,const char * fragment_file_path){
    
        // Create the shaders
        GLuint VertexShaderID = glCreateShader(GL_VERTEX_SHADER);
        GLuint FragmentShaderID = glCreateShader(GL_FRAGMENT_SHADER);
    
        // Read the Vertex Shader code from the file
        std::string VertexShaderCode;
        std::ifstream VertexShaderStream(vertex_file_path, std::ios::in);
        if(VertexShaderStream.is_open()){
            std::stringstream sstr;
            sstr << VertexShaderStream.rdbuf();
            VertexShaderCode = sstr.str();
            VertexShaderStream.close();
        }else{
            printf("Impossible to open %s. Are you in the right directory ? Don't forget to read the FAQ !
    ", vertex_file_path);
            getchar();
            return 0;
        }
    
        // Read the Fragment Shader code from the file
        std::string FragmentShaderCode;
        std::ifstream FragmentShaderStream(fragment_file_path, std::ios::in);
        if(FragmentShaderStream.is_open()){
            std::stringstream sstr;
            sstr << FragmentShaderStream.rdbuf();
            FragmentShaderCode = sstr.str();
            FragmentShaderStream.close();
        }
    
        GLint Result = GL_FALSE;
        int InfoLogLength;
    
    
        // Compile Vertex Shader
        printf("Compiling shader : %s
    ", vertex_file_path);
        char const * VertexSourcePointer = VertexShaderCode.c_str();
        glShaderSource(VertexShaderID, 1, &VertexSourcePointer , NULL);
        glCompileShader(VertexShaderID);
    
        // Check Vertex Shader
        glGetShaderiv(VertexShaderID, GL_COMPILE_STATUS, &Result);
        glGetShaderiv(VertexShaderID, GL_INFO_LOG_LENGTH, &InfoLogLength);
        if ( InfoLogLength > 0 ){
            std::vector<char> VertexShaderErrorMessage(InfoLogLength+1);
            glGetShaderInfoLog(VertexShaderID, InfoLogLength, NULL, &VertexShaderErrorMessage[0]);
            printf("%s
    ", &VertexShaderErrorMessage[0]);
        }
    
    
    
        // Compile Fragment Shader
        printf("Compiling shader : %s
    ", fragment_file_path);
        char const * FragmentSourcePointer = FragmentShaderCode.c_str();
        glShaderSource(FragmentShaderID, 1, &FragmentSourcePointer , NULL);
        glCompileShader(FragmentShaderID);
    
        // Check Fragment Shader
        glGetShaderiv(FragmentShaderID, GL_COMPILE_STATUS, &Result);
        glGetShaderiv(FragmentShaderID, GL_INFO_LOG_LENGTH, &InfoLogLength);
        if ( InfoLogLength > 0 ){
            std::vector<char> FragmentShaderErrorMessage(InfoLogLength+1);
            glGetShaderInfoLog(FragmentShaderID, InfoLogLength, NULL, &FragmentShaderErrorMessage[0]);
            printf("%s
    ", &FragmentShaderErrorMessage[0]);
        }
    
    
    
        // Link the program
        printf("Linking program
    ");
        GLuint ProgramID = glCreateProgram();
        glAttachShader(ProgramID, VertexShaderID);
        glAttachShader(ProgramID, FragmentShaderID);
        glLinkProgram(ProgramID);
    
        // Check the program
        glGetProgramiv(ProgramID, GL_LINK_STATUS, &Result);
        glGetProgramiv(ProgramID, GL_INFO_LOG_LENGTH, &InfoLogLength);
        if ( InfoLogLength > 0 ){
            std::vector<char> ProgramErrorMessage(InfoLogLength+1);
            glGetProgramInfoLog(ProgramID, InfoLogLength, NULL, &ProgramErrorMessage[0]);
            printf("%s
    ", &ProgramErrorMessage[0]);
        }
    
        
        glDetachShader(ProgramID, VertexShaderID);
        glDetachShader(ProgramID, FragmentShaderID);
        
        glDeleteShader(VertexShaderID);
        glDeleteShader(FragmentShaderID);
    
        return ProgramID;
    }
    LoadShaders

    大意是通过文件流获取两个着色器代码,然后分别编译这两个代码得到两个着色器句柄,最后把创建一个进程把这两个句柄合一块,最后把这个进程链接到主程序中。

    由于此代码是如此的枯燥,所以最好封装成一个库。

    1.传入属性(Attribute):

    In GLSL,Vertex Shader:

    layout(location = 0) in vec3 vertexPosition_modelspace;

    我们要知道,我们传进去的数据是顶点属性,0代表一个位置,OpenGL确保至少有16个包含4分量的顶点属性可用,但是有些硬件或许允许更多的顶点属性,你可以查询GL_MAX_VERTEX_ATTRIBS来获取具体的上限。

    in vec3表示输入,使用vec3这种类型,后面的是名字。

    In C++:

    第一步创建一个VBO把顶点数据拷进去:

    GLuint vertexbuffer;
        glGenBuffers(1, &vertexbuffer);
        glBindBuffer(GL_ARRAY_BUFFER, vertexbuffer);
        glBufferData(GL_ARRAY_BUFFER, sizeof(g_vertex_buffer_data), g_vertex_buffer_data, GL_STATIC_DRAW);

    参数应该很好懂,最后一个函数glBufferData的第四个参数是GL_STATIC_DRAW,代表静态数据,不能修改,可以提高性能。

    第二步:

    glEnableVertexAttribArray(0);
            glBindBuffer(GL_ARRAY_BUFFER, vertexbuffer);
            glVertexAttribPointer(
                0,                  // attribute. No particular reason for 0, but must match the layout in the shader.
                3,                  // size
                GL_FLOAT,           // type
                GL_FALSE,           // normalized?
                0,                  // stride
                (void*)0            // array buffer offset
            );

    一步步解释。首先得知道,我们向着色器里传进去的是顶点属性(Vertex Attribute),之前说过它的数量是有限制的。

    第一行glEnableVertexAttribArray(0)指定的是启用location=0的顶点属性,此时在GLSL中可以用layout(location = 0)接收到。

    第二行把vertexbuffer——我们定义的顶点缓冲,之前我把顶点类型为GLfloat的一维数组,其中每连续三个元素代表一个顶点——绑定到当前上下文的GL_ARRAY_BUFFER中,在使用前要绑定,这时我们就可以使用——

    第三行glVertexAttribPointer,它用于指定顶点数组的数据格式和位置。首先是位置,第一个参数,这里填的0,对应layout的0;3代表三个元素一个顶点;GL_FLOAT代表我们的类型;GL_FALSE代表不进行标准化(向量标准化就是把(1,10,1,0)变成(0.1,1,0.1,0));0代表步长(此处等效于4),最后一个是数组偏移量,因为我们从数组下标为0开始定义顶点,所以填0。

    为什么要指定这些呢?因为OpenGL只知道array buffer里的二进制数据,并不知道这些数据的意义,所以当我们要把这些数据传给着色器的时候,必须要事先指定类型和位置。

    最后,随手关门好习惯,记得关闭顶点属性和销毁顶点数组缓冲区。

    glDisableVertexAttribArray(0);//关闭位置为0的顶点属性
    glDeleteBuffers(1, &vertexbuffer);//销毁顶点数组缓冲

    2.传入变量(Variable)

    使用uniform关键字来实现,适用于所有着色器统一传入的常量。

    In GLSL,Vertex Shader

    使用uniform关键字传入一个矩阵:

    uniform mat4 MVP;

    注意,虽然说是变量,但uniform传入的变量实际上不可修改。

    In C++:

    GLuint MatrixID = glGetUniformLocation(programID, "MVP");

    通过glGetUniformLocation获取指定名字的uniform的句柄,programID是我们之前提到的着色器句柄。

    glUniformMatrix4fv(MatrixID, 1, GL_FALSE, &MVP[0][0]);

    通过glUniformMatrix4fv传入数据,MatrixID是之前获取到的句柄,1代表数组数量或者矩阵的数量,GL_FALSE指定矩阵是列优先矩阵,最后传入数据开头的指针。

    除了传入矩阵,还有以下函数来对uniform进行装载(没有重载太蛋疼了):

    这是获取uniform地址,返回一个句柄,接下来的装载将使用这个句柄。

  • 相关阅读:
    爬取校花网的视频
    实现爬取图片
    MongoDB简介和安装
    WebSocket实现简易聊天室
    WEB service基础
    Flask之CBV,flash,Flask-Session,WTForms,DBUtils
    Flask之session,路由,配置,蓝图, before/after_request
    Flask之request,response,Jinja2
    Linux下的docker
    Linux下docker配置镜像加速后重启docker服务失败
  • 原文地址:https://www.cnblogs.com/dudujerry/p/13542466.html
Copyright © 2011-2022 走看看