OpenGL3.3+GLFW+GLEW+GLM小人歩行アニメーションを実現

10094 ワード

現在、ネット上で比較システムを探すチュートリアルは基本的に旧版OpenGLであり、glutベースであるが、このライブラリはすでに前世紀のものである......新版OpenGL 3.3以上のチュートリアルでは、以下の2つを強くお勧めします.
openGL tutorial http://www.opengl-tutorial.org/cn/
learn openGL https://learnopengl-cn.github.io
2つのチュートリアルは1つの理解に重点を置いて、1つのコードは実現して、挿入して一緒に見ることができます.
また、xuhongxuさんに感謝します.
本文はここで原発し,グラフィック学の授業の実験である.
1、環境
  • OpenGL 3.3
  • GLFWは、ウィンドウの作成およびユーザ入力の処理に用いる
  • である.
    OpenGL関数の具体的な実装を決定するための
  • GLEW
  • .
  • GLM,画像関連数学計算ツールライブラリ
  • OSX 10.10

  • 2、ウィンドウとライブラリの初期化
    GLFWウィンドウの初期化:
    if(!glfwInit())
    {
        return -1;
    }
    GLFWwindow* window;
    glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);//OpenGL   3
    glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);//      
    glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE);
    glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);//          
    glfwWindowHint(GLFW_RESIZABLE , GL_FALSE);//       
    glfwWindowHint(GLFW_SAMPLES, 4);
    window = glfwCreateWindow(WIDTH, HEIGHT, "Hello World", NULL, NULL);
    if (!window)
    {
        glfwTerminate();
        return -1;
    }
    glfwMakeContextCurrent(window);//      
    glfwSetKeyCallback(window, key_callback);
    

    GLEWライブラリの初期化:
    glewExperimental = GL_TRUE;
    if (glewInit() != GLEW_OK)
    {
        glfwTerminate();
        return -1;
    }
    

    3、シェーダ
    OpenGL3.3以上のバージョンでは、即時レンダリングモードは使用されず、コアモードが使用され、シェーダで使用される言語はGLSLです.コアモードのワークフローは、下図のようなグラフィックレンダリングラインで、処理中に3 Dのグラフィックを画面に表示される2 D画像に一歩一歩レンダリングします.実験では、自分の頂点シェーダ(vertex shader)とフラグメントシェーダ(fragment shader)を定義する必要があります.
    頂点シェーダの定義は次のとおりです.
    #version 330 core
    
    //     ,   0, C++    ,    
    layout(location = 0) in vec3 vertexPosition_modelspace;
    
    //     ,   1, C++    ,    
    layout(location = 1) in vec3 vertexColor;
    
    //    ,       
    out vec4 fragmentColor;
    
    //    ,MVP  
    uniform mat4 MVP;
    
    void main(){    
    
        //    ,  
        gl_Position =  MVP * vec4(vertexPosition_modelspace,1);
    
        //    ,        
        fragmentColor.xyz = vertexColor;
        fragmentColor.a=0.9;
    }
    

    クリップシェーダの定義は次のとおりです.
    #version 330 core
    
    //  ,          
    in vec4 fragmentColor;
    
    //  
    out vec4 color;
    
    void main(){
    
        color = fragmentColor;
    
    }
    

    コードに頂点シェーダとクリップシェーダをファイルパスでロードし、変数を使用してインデックスを格納します.使用するロード関数loadShadersの主な内容は、ファイルの読み取りや解析などの操作であり、煩雑で貼り付けられない.
    const GLchar* vertexShaderFile = "ColorArrays.vertexshader";
    const GLchar* fragmentShaderFile = "ColorArrays.fragmentshader";
    GLuint programID = loadShaders(vertexShaderFile, fragmentShaderFile);
    

    イメージレンダリングの各ループで、現在のシェーダを描画する前に取得します.
    glUseProgram(programID);
    

    4、VBO、VBO、EBOとcorbuffer
    頂点配列オブジェクト(vertex array object)を作成し、すべての頂点プロパティ呼び出しを格納します.
    GLuint VAO;
    glGenVertexArrays(1, &VAO);
    //      
    glBindVertexArray(VAO);
    

    頂点バッファオブジェクト(vertex buffer object)とインデックスバッファオブジェクト(element buffer object)を作成します.
    GLuint VBO;
    GLuint EBO;
    glGenBuffers(1, &VBO);
    glGenBuffers(1, &EBO);
    

    共通の立方体を作成します.1つの立方体には6つの面があり、各面は2つの三角形で綴られ、合計12の三角形が必要です.静的に画像を作成するには、各三角形の3つの頂点の3 D座標を順番に書く必要があります.煩雑すぎて、重複するデータがたくさんあるので、インデックスを使用して動的に作成します.vertices配列は立方体の8つの頂点を格納し、indices配列は12の三角形に対応する頂点のインデックスを格納する.
    static const GLfloat vertices[] = {
        -1.0f, -1.0f, -1.0f,
        -1.0f, -1.0f, 1.0f,
        -1.0f, 1.0f, -1.0f,
        -1.0f, 1.0f, 1.0f,
        1.0f, -1.0f, -1.0f,
        1.0f, -1.0f, 1.0f,
        1.0f, 1.0f, -1.0f,
        1.0f, 1.0f, 1.0f,
    };
    static const GLuint indices[] = {
            0, 1, 2,
            1, 2, 3,
            1, 0, 5,
            0, 5, 4,
            5, 6, 7,
            4, 5, 6,
            2, 3, 7,
            6, 2, 7,
            1, 5, 3,
            3, 5, 7,
            0, 2, 4,
            2, 4, 6,
    };
    

    オブジェクトをバッファにバインドし、データをバッファにロードします.
    //   arraybuffer 
    glBindBuffer(GL_ARRAY_BUFFER, VBO);
    //        buffer 
    glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_DYNAMIC_DRAW);
    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBO);
    glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_DYNAMIC_DRAW);
    

    頂点のカラーアトリビュートを設定し、同じバッファオブジェクト管理を使用して、カラーをランダムに生成します.
    GLuint colorbuffer;
    glGenBuffers(1, &colorbuffer);
    //8   ,RGB   
    static GLfloat colors[8*3];
    //srand(time(NULL));
    for (int v = 0; v < 8 ; v++)
    {
        colors[3*v+0] = (float)rand()/RAND_MAX;
        colors[3*v+1] = (float)rand()/RAND_MAX;
        colors[3*v+2] = (float)rand()/RAND_MAX;
    }
    glBindBuffer(GL_ARRAY_BUFFER, colorbuffer);
    glBufferData(GL_ARRAY_BUFFER, sizeof(colors), colors, GL_STATIC_DRAW);
    

    位置データと色データは、画像の2つのプロパティです.前に定義した頂点シェーダに渡します.
    glBindBuffer(GL_ARRAY_BUFFER, VBO);
    glVertexAttribPointer(0,3,GL_FLOAT,GL_FALSE,0,(void*)0);
    glEnableVertexAttribArray(0);
    
    glBindBuffer(GL_ARRAY_BUFFER, colorbuffer);
    glVertexAttribPointer(1,3,GL_FLOAT,GL_FALSE,0,(void*)0);
    glEnableVertexAttribArray(1);
    

    最後にVAOのバインドを解除します.
    glBindVertexArray(0);
    

    5、MVPマトリックス
    各物体はもともと自分の局所空間内にあり、座標範囲は-1から1の間にある.
    モデルマトリクスは、平行移動、回転、スケールの演算により、グラフィックのローカル座標を世界座標にマッピングします.
    観察行列はカメラが世界空間を観察する位置と角度を定義する.
    投影(Projection)マトリクスは、世界空間の座標が最終的に画面の前に現れる方法を決定します.
    このため、物体の最終的な位置は、元の位置データに上記3つのマトリクスを加えて変換され、MVPマトリクスの演算はC++コードで行われ、頂点シェーダで最終的な座標データが計算される.
    MVP=projection*view*model;//C++  
    gl_Position =  MVP * vec4(vertexPosition_modelspace,1);//       
    

    観測行列と投影行列は作図全体にわたって統一されている.
    //      uniform  MVP   
    GLuint MatrixID = glGetUniformLocation(programID, "MVP");
    //45°    , 4:3,          0.1 100
    mat4 projection=perspective(45.0f,4.0f/3.0f,0.1f,100.0f);
    //           ,         ,        
    mat4 view=lookAt(vec3(4,2,0),vec3(0,0,3),vec3(0,1,0));
    

    以前定義した汎用立方体を小人の体の各部分の素子として用いて,それぞれモデル行列を設定した.全部で体、頭、左腕、右腕、左足、右足、6つの部品が含まれています.小人の運動は体を中心とし、他の5つの部品は体の位置を基礎として運動する.左腕を例にとると、小人を前に進ませたいので、画像をレンダリングするたびに体を前に少し平行に移動します.身体のモデル行列を得た後,左肩位置の座標を算出した.左腕のシェイプをスケールして変換し、左腕の現在のフレームでの回転幅を回転して変換し、最後に左腕を以前に計算した左肩の位置に移動します.左腕の回転動作は時間とともに変化する.
    //     Z     0.05
    model_body=translate(model_body, vec3(0.0f,0.0f,0.05f));
    //     MVP
    MVP=projection*view*model_body;
    // MVP       
    glUniformMatrix4fv(MatrixID, 1, GL_FALSE, &MVP[0][0]);
    //    
    glDrawElements(GL_TRIANGLES, 12*3, GL_UNSIGNED_INT, (void*)0);
    
    //  
    vec4 shd_left(1.0f,0.0f,0.0f,1.0f);
    //          
    shd_left=model_body*shd_left;
    model_hand_left=mat4(1.0f);
    //         
    model_hand_left=translate(model_hand_left,vec3(shd_left.x+0.1,shd_left.y+0.5,shd_left.z));
    //       
    model_hand_left=rotate(model_hand_left,(float)sin(glfwGetTime()*2)/3,vec3(1.0f,0.0f,0.5f));
    //      ,           
    model_hand_left=translate(model_hand_left,vec3(0.0f,-0.5f,0.0f));
    //               
    model_hand_left=scale(model_hand_left, vec3(0.1f,0.5f,0.1f));
    //     MVP
    MVP=projection*view*model_hand_left;
    //     
    glUniformMatrix4fv(MatrixID, 1, GL_FALSE, &MVP[0][0]);
    //    
    glDrawElements(GL_TRIANGLES, 12*3, GL_UNSIGNED_INT, (void*)0);
    

    他の体の部位は上と似ている.また、小さな人が通った経路に床を描きます.
    //  
    mat4 model_stay=mat4(1.0f);
    model_stay=translate(model_stay, vec3(0.0f,-1.1f,5.0f));
    model_stay=scale(model_stay, vec3(1.0f,0.1f,5.0f));
    
    //...
    //      
    
    //  
    MVP=projection*view*model_stay;
    glUniformMatrix4fv(MatrixID, 1, GL_FALSE, &MVP[0][0]);
    glDrawElements(GL_TRIANGLES, 12*3, GL_UNSIGNED_INT, (void*)0);
    //  
    vec4 neck(0.0f,1.0f,0.0f,1.0f);
    neck=model_body*neck;
    model_head=mat4(1.0f);
    model_head=translate(model_head,vec3(neck.x,neck.y+0.3,neck.z));
    model_head=rotate(model_head,(float)sin(glfwGetTime()*2)/5,vec3(0.0f,0.0f,1.0f));
    model_head=scale(model_head, vec3(0.1f,0.1f,0.1f));
    MVP=projection*view*model_head;
    glUniformMatrix4fv(MatrixID, 1, GL_FALSE, &MVP[0][0]);
    glDrawElements(GL_TRIANGLES, 12*3, GL_UNSIGNED_INT, (void*)0);
    //  
    vec4 shd_left(1.0f,0.0f,0.0f,1.0f);
    shd_left=model_body*shd_left;
    model_hand_left=mat4(1.0f);
    model_hand_left=translate(model_hand_left,vec3(shd_left.x+0.1,shd_left.y+0.5,shd_left.z));
    model_hand_left=rotate(model_hand_left,(float)sin(glfwGetTime()*2)/3,vec3(1.0f,0.0f,0.5f));
    model_hand_left=translate(model_hand_left,vec3(0.0f,-0.5f,0.0f));
    model_hand_left=scale(model_hand_left, vec3(0.1f,0.5f,0.1f));
    MVP=projection*view*model_hand_left;
    glUniformMatrix4fv(MatrixID, 1, GL_FALSE, &MVP[0][0]);
    glDrawElements(GL_TRIANGLES, 12*3, GL_UNSIGNED_INT, (void*)0);
    //  
    vec4 shd_right(-1.0f,0.0f,0.0f,1.0f);
    shd_right=model_body*shd_right;
    model_hand_right=mat4(1.0f);
    model_hand_right=translate(model_hand_right,vec3(shd_right.x-0.1,shd_right.y+0.5,shd_right.z));
    model_hand_right=rotate(model_hand_right,(float)sin(glfwGetTime()*2)/3,vec3(-1.0f,0.0f,-0.5f));
    model_hand_right=translate(model_hand_right,vec3(0.0f,-0.5f,0.0f));
    model_hand_right=scale(model_hand_right, vec3(0.1f,0.5f,0.1f));
    MVP=projection*view*model_hand_right;
    glUniformMatrix4fv(MatrixID, 1, GL_FALSE, &MVP[0][0]);
    glDrawElements(GL_TRIANGLES, 12*3, GL_UNSIGNED_INT, (void*)0);
    //  
    vec4 pp_left(0.5f,-1.0f,0.0f,1.0f);
    pp_left=model_body*pp_left;
    model_leg_left=mat4(1.0f);
    model_leg_left=translate(model_leg_left,vec3(pp_left.x+0.05,pp_left.y,pp_left.z));
    model_leg_left=rotate(model_leg_left,(float)sin(glfwGetTime()*2)/3,vec3(-1.0f,0.0f,0.0f));
    model_leg_left=translate(model_leg_left,vec3(0.0f,-0.5f,0.0f));
    model_leg_left=scale(model_leg_left, vec3(0.1f,0.5f,0.1f));
    MVP=projection*view*model_leg_left;
    glUniformMatrix4fv(MatrixID, 1, GL_FALSE, &MVP[0][0]);
    glDrawElements(GL_TRIANGLES, 12*3, GL_UNSIGNED_INT, (void*)0);
    //  
    vec4 pp_right(-0.5f,-1.0f,0.0f,1.0f);
    pp_right=model_body*pp_right;
    model_leg_right=mat4(1.0f);
    model_leg_right=translate(model_leg_right,vec3(pp_right.x-0.05,pp_right.y,pp_right.z));
    model_leg_right=rotate(model_leg_right,(float)sin(glfwGetTime()*2)/3,vec3(1.0f,0.0f,0.0f));
    model_leg_right=translate(model_leg_right,vec3(0.0f,-0.5f,0.0f));
    model_leg_right=scale(model_leg_right, vec3(0.1f,0.5f,0.1f));
    MVP=projection*view*model_leg_right;
    glUniformMatrix4fv(MatrixID, 1, GL_FALSE, &MVP[0][0]);
    glDrawElements(GL_TRIANGLES, 12*3, GL_UNSIGNED_INT, (void*)0);
    

    6、結果
    成果図gifが大きすぎて貼れないので、ここに突っ込んで見ることができます.