zoukankan      html  css  js  c++  java
  • 【OpenGL】学习笔记#2

    第一个OpenGL程序

    现在,让我们画一个三角形。

    之前说过,我们用GLFW来简化繁琐的创建窗口的操作。

    所以第一步,让我们创建窗口:

    // Initialise GLFW
        if( !glfwInit() )
        {
            fprintf( stderr, "Failed to initialize GLFW
    " );
            getchar();
            return -1;
        }
    
        glfwWindowHint(GLFW_SAMPLES, 4);
        glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
        glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
        glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE); // To make MacOS happy; should not be needed
        glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
    
        // Open a window and create its OpenGL context
        window = glfwCreateWindow( 1024, 768, "Tutorial 02 - Red triangle", NULL, NULL);
        if( window == NULL ){
            fprintf( stderr, "Failed to open GLFW window. If you have an Intel GPU, they are not 3.3 compatible. Try the 2.1 version of the tutorials.
    " );
            getchar();
            glfwTerminate();
            return -1;
        }
        glfwMakeContextCurrent(window);

    不用记住这些东西,很简单,让我们继续往下看:

    由于我们要使用高级的OpenGL接口,同时要保证可扩展性,我们使用GLEW来确保我们可以使用高级的函数。

    初始化GLEW:

    // Initialize GLEW
        glewExperimental = true; // Needed for core profile
        if (glewInit() != GLEW_OK) {
            fprintf(stderr, "Failed to initialize GLEW
    ");
            getchar();
            glfwTerminate();
            return -1;
        }

    首先,我们生成一个VAO:

    GLuint VertexArrayID;//顶点缓冲文件号,VAO
        glGenVertexArrays(1, &VertexArrayID);//生成1个顶点缓冲区,并返回文件号
        glBindVertexArray(VertexArrayID);//将缓冲文件绑定到当前OpenGL上下文中

    接着,从我们的着色器文件里加载着色器程序:

    // Create and compile our GLSL program from the shaders
        GLuint programID = LoadShaders( "SimpleVertexShader.vertexshader", "SimpleFragmentShader.fragmentshader" );//加载顶点着色器和片元着色器

    定义三角形的顶点

    static const GLfloat g_vertex_buffer_data[] = { //定义顶点
            -1.0f, -1.0f, 0.0f,
             1.0f, -1.0f, 0.0f,
             0.0f,  1.0f, 0.0f,
        };

    生成VBO同时把我们的顶点数据放进去

    GLuint vertexbuffer;//定义顶点缓冲文件号
        glGenBuffers(1, &vertexbuffer);//同上
        glBindBuffer(GL_ARRAY_BUFFER, vertexbuffer);//绑定这个顶点缓冲到当前上下文,同时指定文件类型为数组缓冲(告诉OpenGL类型)
        glBufferData(GL_ARRAY_BUFFER, sizeof(g_vertex_buffer_data), g_vertex_buffer_data, GL_STATIC_DRAW);//输入VBO数据

    主循环:

    do{
    
            // Clear the screen
            glClear( GL_COLOR_BUFFER_BIT );//清除屏幕
    
            // Use our shader
            glUseProgram(programID);//使用着色器
    
            // 1rst attribute buffer : vertices
            glEnableVertexAttribArray(0);//启用顶点属性(0)
            //glBindBuffer(GL_ARRAY_BUFFER, vertexbuffer); //没用 
            glVertexAttribPointer(
                0,                  // attribute 0. 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
            );
    
            // Draw the triangle !
            glDrawArrays(GL_TRIANGLES, 0, 3); // 3 indices starting at 0 -> 1 triangle
    
            glDisableVertexAttribArray(0);//关闭顶点属性(0)
    
            // Swap buffers
            glfwSwapBuffers(window);//双缓冲
            glfwPollEvents();//滚动事件
    
        } // Check if the ESC key was pressed or the window was closed
        while( glfwGetKey(window, GLFW_KEY_ESCAPE ) != GLFW_PRESS &&
               glfwWindowShouldClose(window) == 0 );//强大的GLFW

    经过注释,相信有窗体基础的都看得懂。

    这样,我们的程序就完成了。

    相信细心的朋友已经发现了,VAO完全没用啊!

    是的,没用,几细演细一下啦

    接下来是着色器:

    它们的语法和C++很像,请看顶点着色器:

    #version 330 core
    
    // Input vertex data, different for all executions of this shader.
    layout(location = 0) in vec3 vertexPosition_modelspace;
    
    void main(){
    
        gl_Position.xyz = vertexPosition_modelspace;
        gl_Position.w = 1.0;
    
    }

    第一行指定了最低版本

    接下来它指定了一个数字,可以找到在主循环里我们指定顶点数组的属性和类型的时候:

    glVertexAttribPointer(
                0,                  // attribute 0. 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
            );

    这个第一个参数0就是我们在着色器里写的0,代表我们将调用这个属性。

    main函数里就对这个顶点进行处理。

    接下来看片元着色器:

    #version 330 core
    
    // Ouput data
    out vec3 color;
    
    void main()
    {
    
        // Output color = red 
        color = vec3(1,0,0);
    
    }

    out代表片元着色器将这个颜色数据输出,所以我们的三角形将会是红色的!(这是一个特殊情况,每个片元着色器都应当有一个out vec3/vec4来指定这个片元的颜色)

    在程序里有一个LoadShader,它并不是官方接口(为什么不是?),所以我们要自己编写它(并最好封装成一个库!)

    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 ? 
    ", 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;
    }

    这段代码非常无聊,仅仅是文件流获取两种着色器代码,再分别编译,最后链接到程序里,返回文件ID——所以我们以后不会再见到它,仅仅把他放到一个库里。

    最后回到我们的第一个程序,用C++编写程序一定不要忘记清理我们定义的VAO(OpenGL不知道它是VAO,只知道它是一个顶点数组)和VBO还有程序链接的着色器,以及GLFW的窗口:

    // Cleanup VBO
        glDeleteBuffers(1, &vertexbuffer);//必要的清理工作
        glDeleteVertexArrays(1, &VertexArrayID);
        glDeleteProgram(programID);
    
        // Close OpenGL window and terminate GLFW
        glfwTerminate();//窗口拜拜

    伴随着窗口的销毁,我们的第一个程序也完成了。

     so beautiful !

  • 相关阅读:
    Extjs Ext.ux.IFrame的用法 以及父子窗口间函数相互调用
    Android ADB server didn't ACK * failed to start daemon * 简单有效的解决方案
    Java sun.misc.Unsafe类的学习笔记
    Java 并发编程学习笔记 理解CLH队列锁算法
    深入理解Java虚拟机 -- 读书笔记(1):JVM运行时数据区域
    Java并发编程学习笔记 深入理解volatile关键字的作用
    JVM Client Server启动设置
    双重检查锁定与延迟初始化
    Tomcat 添加为系统服务 开机自动启动
    配置TOMCAT 修改默认ROOT路径
  • 原文地址:https://www.cnblogs.com/dudujerry/p/13534415.html
Copyright © 2011-2022 走看看