【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 !\n", 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\n", 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\n", &VertexShaderErrorMessage[0]);
    }



    // Compile Fragment Shader
    printf("Compiling shader : %s\n", 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\n", &FragmentShaderErrorMessage[0]);
    }



    // Link the program
    printf("Linking program\n");
    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\n", &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地址,返回一個句柄,接下來的裝載將使用這個句柄。

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM