• 代码编译【OpenGL】Shader概述代码编译


    文章结束给大家来个程序员笑话:[M]

        这篇文章述讲了Shader是如何编译和链接,终最在OpenGL程序中应用的。当然,不解了这些我们仍然可以畸形作工,但是作为初学者,解了这些会让我更能明确自己在嘛干。。。

        

    综述

        哈,鼎鼎名大的Shader终究让我给见到了……之前在习学Unity3D的时候就被群里的大牛耳闻目睹说Shader如何如何要重,在现终究轮到自己领教了。吐槽终了,进入正题。

        Shader的编译器被内嵌到OpenGL库的部内,而且必须在运行OpenGL程序时才能编译。在现还没有可以前提编译Shader的工具。在最新的OpenGL4.1中像好正在改善。

        在现的习学中,我应用的是这个程教供给的一个载入shader的代码。代码不长,功能不全,只能同时载入vertex shader和fragment shader(这里是保存在两个独自的件文里,后缀别分的vertexshader和fragmentshader,后缀不要重,即便是txt也可以,要重的是内容应用的是GLSL法语),但是对于初学者够用了。(实际上,我们平日须要最少两个shader。)代码如下:

    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::string Line = "";
            while(getline(VertexShaderStream, Line))
                VertexShaderCode += "n" + Line;
            VertexShaderStream.close();
        }
    
        // 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::string Line = "";
            while(getline(FragmentShaderStream, Line))
                FragmentShaderCode += "n" + Line;
            FragmentShaderStream.close();
        }
    
        GLint Result = GL_FALSE;
        int InfoLogLength;
    
        // Compile Vertex Shader
        printf("Compiling shader : %sn", 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);
        std::vector VertexShaderErrorMessage(InfoLogLength);
        glGetShaderInfoLog(VertexShaderID, InfoLogLength, NULL, &VertexShaderErrorMessage[0]);
        fprintf(stdout, "%sn", &VertexShaderErrorMessage[0]);
    
        // Compile Fragment Shader
        printf("Compiling shader : %sn", 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);
        std::vector FragmentShaderErrorMessage(InfoLogLength);
        glGetShaderInfoLog(FragmentShaderID, InfoLogLength, NULL, &FragmentShaderErrorMessage[0]);
        fprintf(stdout, "%sn", &FragmentShaderErrorMessage[0]);
    
        // Link the program
        fprintf(stdout, "Linking programn");
        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);
        std::vector ProgramErrorMessage( max(InfoLogLength, int(1)) );
        glGetProgramInfoLog(ProgramID, InfoLogLength, NULL, &ProgramErrorMessage[0]);
        fprintf(stdout, "%sn", &ProgramErrorMessage[0]);
    
        glDeleteShader(VertexShaderID);
        glDeleteShader(FragmentShaderID);
    
        return ProgramID;
    }

        为了举例,我们以下都应用下面两个shader。

        第一个shader是一个顶点着色器,vertex shader,件文名为ExampleShader.vertexshader。

    #version 400
    in vec3 VertexPosition;
    in vec3 VertexColor;
    out vec3 Color;
    void main()
        Color = VertexColor;
        gl_Position = vec4( VertexPosition, 1.0 );
    }

        这里简单解释一下。它接受两个输入和一个输出,并应用输入VertexPosition给gl_position赋值,应用VertexColor给输出Color赋值,而Color将会传递给下面的片段着色器。

        

        第二个是片段着色器,fragment shader,件文名为ExampleShader.fragmentshader。

    #version 400
    in vec3 Color;
    out vec4 FragColor;
    void main() {
       FragColor = vec4(Color, 1.0);
    }

        顶点着色器会在每个顶点上调用一次,而片段着色器则会在每个像素上调用一次。

        每日一道理
    信念是巍巍大厦的栋梁,没有它,就只是一堆散乱的砖瓦;信念是滔滔大江的河床,没有它,就只有一片泛滥的波浪;信念是熊熊烈火的引星,没有它,就只有一把冰冷的柴把;信念是远洋巨轮的主机,没有它,就只剩下瘫痪的巨架。

        ----------------------------------------------------------------分割线--------------------------------------------------------------------

        

    编译一个Shader

        


        


        

    如上图所示,一个shader的编译过程主要分为3个部分:首先创建一个shader对象(Shader Object)。例如下面别分创建了两个shader对象:
        // Create the shaders
        GLuint VertexShaderID = glCreateShader(GL_VERTEX_SHADER);
        GLuint FragmentShaderID = glCreateShader(GL_FRAGMENT_SHADER);

    然后将shader源代码(source code)复制到shader对象中。由于这里是从件文里读入代码,因此先将源代码别分读入到一个string类型的变量里(VertexShaderCode和FragmentShaderCode),再把指针传递给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::string Line = "";
            while(getline(VertexShaderStream, Line))
                VertexShaderCode += "n" + Line;
            VertexShaderStream.close();
        }

        char const * VertexSourcePointer = VertexShaderCode.c_str();
        glShaderSource(VertexShaderID, 1, &VertexSourcePointer , NULL);

    最后,编译shader。平日我们须要检查一下shader是否编译成功,不成功的话再打出错误信息,这通过两个变量Result和InfoLogLength来实现:
        GLint Result = GL_FALSE;
        int InfoLogLength;

        glCompileShader(VertexShaderID);
    
        // Check Vertex Shader
        glGetShaderiv(VertexShaderID, GL_COMPILE_STATUS, &Result);
        glGetShaderiv(VertexShaderID, GL_INFO_LOG_LENGTH, &InfoLogLength);
        std::vector VertexShaderErrorMessage(InfoLogLength);
        glGetShaderInfoLog(VertexShaderID, InfoLogLength, NULL, &VertexShaderErrorMessage[0]);
        fprintf(stdout, "%sn", &VertexShaderErrorMessage[0]);

    上面的筛选的vertex shader的编译过程,当然fragment shader的编译是一样的。

    链接一个Shader

        

    在我们编译完成shader后,正式在OpenGL管道中应用它们之前,我们还须要链接它们。这主要是为解了决不同shader之间的输入和输出匹配问题。例如这里,ExampleShader.vertexshader里的输出Color须要和ExampleShader.fragmentshader里的输入Color相匹配。

        


        

    和编译shader类似,我们首先须要创建一个shader program object。
        GLuint ProgramID = glCreateProgram();
    
    
    然后将之前创建好的shader object附加给它。
        glAttachShader(ProgramID, VertexShaderID);
        glAttachShader(ProgramID, FragmentShaderID);

    最后,进行链接。
        glLinkProgram(ProgramID);

    和之前须要检查编译状态类似,我们也须要检查链接状态,如果链接不成功,就打出提示信息。
        // Check the program
        glGetProgramiv(ProgramID, GL_LINK_STATUS, &Result);
        glGetProgramiv(ProgramID, GL_INFO_LOG_LENGTH, &InfoLogLength);
        std::vector ProgramErrorMessage( max(InfoLogLength, int(1)) );
        glGetProgramInfoLog(ProgramID, InfoLogLength, NULL, &ProgramErrorMessage[0]);
        fprintf(stdout, "%sn", &ProgramErrorMessage[0]);

    删除一个Shader

    在我们把一个shader对象附加一个program对象后,我们就可以删除它了,以便释放它占用的资源和句柄。注意,如果一个shader已经被附加到一个program对象(比如在现),那么实际上它并没有立刻被删除,而是出于一种挂起状态。当它从附加的program对象上被卸载时,才会真正从内存中删除。
        glDeleteShader(VertexShaderID);
        glDeleteShader(FragmentShaderID);

    指定应用一个Shader Program

    为了在OpenGL管道里真正应用我们创建好的shader program对象,我们须要应用glUseProgram( ProgramID);函数,ProgramID就是上面函数的返回值,它供给了一个Shadr program对象的句柄。在一个OpenGL程序中,我们可以应用多个shader programs,我们可以通过应用glUseProgram在OpenGL管道里进行切入和切除,以便选择不同的程序。

    删除一个Shader Program

    当我们不再须要一个shader program时,可以通过调用glDeleteProgram(ProgramID)来删除一个shader program,以释放OpenGL内存。注意,如果当前的shader program正在被应用,那么它并不会立刻被删除,而是出于一种挂起状态,直到程序不再应用它(可能切换到另一个Shader Program)。这时,之前附加的shader对象就会真正被删除了(如果之前调用过glDeleteShader的话)。

    备注

    你可能还是不懂我在讲什么,如果这样,可以到 这个网站上看一下第二节课的内容,可能会有所帮助。

        


        


    文章结束给大家分享下程序员的一些笑话语录: 警告
    有一个小伙子在一个办公大楼的门口抽着烟,一个妇女路过他身边,并对他 说, “你知道不知道这个东西会危害你的健康?我是说, 你有没有注意到香烟 盒上的那个警告(Warning)?”
    小伙子说,“没事儿,我是一个程序员”。
    那妇女说,“这又怎样?”
    程序员说,“我们从来不关心 Warning,只关心 Error”

  • 相关阅读:
    c语言进阶2-变量的作用域与无参函数
    《Linux 性能及调优指南》3.2 CPU瓶颈
    《Linux 性能及调优指南》3.1 确认瓶颈
    马哥Linux base学习笔记
    《Linux 性能及调优指南》1.6 了解Linux性能指标
    Linux性能及调优指南1.2之Linux内存架构
    《Linux 性能及调优指南》1.1 Linux进程管理
    分布式系统的Raft算法
    《马哥出品高薪linux运维教程》wingkeung学习笔记-linux基础入门课程
    Systemd 入门教程:实战篇
  • 原文地址:https://www.cnblogs.com/jiangu66/p/3033367.html
Copyright © 2020-2023  润新知