ぬの部屋(仮)
nu-no-he-ya
  •   12345
    6789101112
    13141516171819
    20212223242526
    27282930   
           
          1
    2345678
    9101112131415
    16171819202122
    23242526272829
    3031     
       1234
    567891011
    12131415161718
    19202122232425
    262728293031 
           
     123456
    78910111213
    14151617181920
    21222324252627
    282930    
           
         12
    3456789
    10111213141516
    17181920212223
    24252627282930
    31      
       1234
    567891011
    12131415161718
    19202122232425
    2627282930  
           
    1234567
    891011121314
    15161718192021
    22232425262728
    293031    
           
    1234567
    891011121314
    15161718192021
    22232425262728
           
           
        123
    45678910
    11121314151617
    18192021222324
    25262728293031
           
     123456
    78910111213
    14151617181920
    21222324252627
    28293031   
           
          1
    2345678
    9101112131415
    16171819202122
    23242526272829
    30      
       1234
    567891011
    12131415161718
    19202122232425
    262728293031 
           
     123456
    78910111213
    14151617181920
    21222324252627
    282930    
           
         12
    3456789
    10111213141516
    17181920212223
    24252627282930
    31      
      12345
    6789101112
    13141516171819
    20212223242526
    2728293031  
           
    1234567
    891011121314
    15161718192021
    22232425262728
    2930     
           
        123
    45678910
    11121314151617
    18192021222324
    25262728293031
           
      12345
    6789101112
    13141516171819
    20212223242526
    27282930   
           
          1
    2345678
    9101112131415
    16171819202122
    23242526272829
    3031     
         12
    3456789
    10111213141516
    17181920212223
    242526272829 
           
      12345
    6789101112
    13141516171819
    20212223242526
    2728293031  
           
          1
    2345678
    9101112131415
    16171819202122
    23242526272829
    3031     
        123
    45678910
    11121314151617
    18192021222324
    252627282930 
           
     123456
    78910111213
    14151617181920
    21222324252627
    28293031   
           
          1
    2345678
    9101112131415
    16171819202122
    23242526272829
    30      
       1234
    567891011
    12131415161718
    19202122232425
    262728293031 
           
    1234567
    891011121314
    15161718192021
    22232425262728
    293031    
           
         12
    3456789
    10111213141516
    17181920212223
    24252627282930
           
      12345
    6789101112
    13141516171819
    20212223242526
    2728293031  
           
    1234567
    891011121314
    15161718192021
    22232425262728
    2930     
           
        123
    45678910
    11121314151617
    18192021222324
    25262728293031
           
        123
    45678910
    11121314151617
    18192021222324
    25262728   
           
     123456
    78910111213
    14151617181920
    21222324252627
    28293031   
           
         12
    3456789
    10111213141516
    17181920212223
    24252627282930
    31      
       1234
    567891011
    12131415161718
    19202122232425
    2627282930  
           
    1234567
    15161718192021
    293031    
           
         12
    3456789
    10111213141516
           
      12345
    6789101112
    13141516171819
    20212223242526
    2728293031  
           
          1
    2345678
    9101112131415
    16171819202122
    23242526272829
    3031     
        123
    45678910
    11121314151617
    18192021222324
    252627282930 
           
     123456
    78910111213
    14151617181920
    21222324252627
    28293031   
           
          1
    2345678
    9101112131415
    16171819202122
    23242526272829
    30      
       1234
    567891011
    12131415161718
    19202122232425
    262728293031 
           
    1234567
    891011121314
    15161718192021
    22232425262728
    293031    
           
        123
    45678910
    11121314151617
    18192021222324
    25262728293031
           
      12345
    6789101112
    13141516171819
    20212223242526
    27282930   
           
        123
    45678910
    11121314151617
    18192021222324
    252627282930 
           
     123456
    78910111213
    14151617181920
    21222324252627
    28293031   
           
       1234
    567891011
    12131415161718
    19202122232425
    2627282930  
           
    1234567
    891011121314
    15161718192021
    22232425262728
    293031    
           
         12
    3456789
    10111213141516
    17181920212223
    24252627282930
           
      12345
    6789101112
    13141516171819
    20212223242526
    2728293031  
           
      12345
    6789101112
    13141516171819
    20212223242526
    2728     
           
          1
    2345678
    9101112131415
    16171819202122
    23242526272829
    3031     
     123456
    78910111213
    14151617181920
    21222324252627
    282930    
           
         12
    3456789
    10111213141516
    17181920212223
    24252627282930
    31      
    1234567
    891011121314
    15161718192021
    22232425262728
    293031    
           
        123
    45678910
    11121314151617
    18192021222324
    252627282930 
           
     123456
    78910111213
    14151617181920
    21222324252627
    28293031   
           
     123456
    78910111213
    14151617181920
    21222324252627
    28293031   
           
       1234
    567891011
    12131415161718
    19202122232425
    262728293031 
           
     123456
    78910111213
    14151617181920
    21222324252627
    282930    
           
         12
    3456789
    10111213141516
    17181920212223
    24252627282930
    31      
      12345
    6789101112
    13141516171819
    20212223242526
    2728293031  
           
    1234567
    891011121314
    15161718192021
    22232425262728
    2930     
           
        123
    45678910
    11121314151617
    18192021222324
    25262728293031
           
      12345
    6789101112
    13141516171819
    20212223242526
    27282930   
           
          1
    2345678
    9101112131415
    16171819202122
    23242526272829
    3031     
          1
    2345678
    9101112131415
    16171819202122
    232425262728 
           
       1234
    567891011
    12131415161718
    19202122232425
    262728293031 
           
    1234567
    891011121314
    15161718192021
    22232425262728
    293031    
           
         12
    3456789
    10111213141516
    17181920212223
    24252627282930
           
      12345
    6789101112
    13141516171819
    20212223242526
    2728293031  
           
    1234567
    891011121314
    15161718192021
    22232425262728
    2930     
           
        123
    45678910
    11121314151617
    18192021222324
    25262728293031
           
  • GLSLを試す (9) Geometry Shader(1)

    1頂点入力、1頂点出力のジオメトリシェーダを作って動作確認を行う。

    シェーダのコンパイル等は依然作ったprepare_shader.hppを転用する。ただしサンプルとしてはlink_programを使うと読みにくくなるので、ジオメトリシェーダサンプル用に書き直す。

    https://suzulang.com/glsl-shader-my-functions-on-cpp

    各シェーダ

    default.vert

    #version 460 core
    
    layout (location = 0) in vec3 coord;
    layout (location = 1) in vec3 incolor;
    
    out vec4 vertexColor;
    
    uniform mat4 ModelViewMatrix;
    uniform mat4 ProjectionMatrix;
    
    void main()
    {
      gl_Position = ProjectionMatrix * ModelViewMatrix * vec4(coord, 1.0);
      vertexColor = vec4(incolor, 1.0);
      gl_PointSize=30.0;
    }
    

    default.geom

    #version 460
    
    layout (points) in; //入力は頂点単位
    layout (points) out; //出力も頂点単位
    layout (max_vertices = 1) out; // 最大出力頂点数
    
    //geometry shaderへの入力は配列で受け取らなければいけない
    //ただし名前はvertex shader側のoutと一致させる
    in vec4 vertexColor[]; 
    
    // 出力は普通。名前はfragment shader側のinと一致させる
    out vec4 vColor;
    
    void main()
    {
        // gl_inにデータが入っている。今回は頂点ごとの処理なので要素数1.
        // つまりgl_in[0]のみが有効
        
        gl_Position = gl_in[0].gl_Position;
        gl_PointSize=30.0;
        vColor = vertexColor[0];
        EmitVertex(); // ラスタライザに頂点を送る
        EndPrimitive();//プリミティブの区切り。GL_LINE_STRIPなどを複数実行するときは複数回呼び出す
    }
    

    default.frag

    #version 460 core
    
    in vec4 vColor;
    
    out vec4 FragColor;
    
    void main()
    {
      FragColor = vColor;
    } 
    

    C++側

    #include <iostream>
    
    #include <Windows.h>
    #include "prepare_shader.hpp"
    #include <gl/GL.h>
    #include <gl/GLU.h>
    #include <gl/freeglut.h>
    
    #pragma comment(lib,"glew32.lib")
    
    //ウィンドウの幅と高さ
    int width, height;
    
    GLuint buf_points;
    GLuint buf_colors;
    
    GLfloat mproj[16];
    GLfloat mmodel[16];
    GLuint programID;
    const char* vtxfile = "default.vert";
    const char* fragfile = "default.frag";
    const char* geomfile = "default.geom";
    
    
    //! @brief プログラムのリンクを行う(geometryシェーダ使用時)。
    //! @details ジオメトリシェーダがある場合はglLinkProgramの前に設定を行わなければいけない。
    //! 別に関数を用意した方がわかりやすいのでここではこれを使う
    GLSLCompileCond link_program_with_geometry(
        GLuint* ProgramID,
        GLuint vertexShaderID,
        GLuint geometryShaderID,
        GLuint fragmentShaderID,
        std::string* error = nullptr) {
    
        GLint Result = GL_FALSE;
        int InfoLogLength;
    
        ////////////////////////////////////////
        // プログラムをリンクします。
        *ProgramID = glCreateProgram();
    
        glAttachShader(*ProgramID, vertexShaderID);
        glAttachShader(*ProgramID, geometryShaderID);
        glAttachShader(*ProgramID, fragmentShaderID);
    
    
        ////////////////////////////////////////
        // ジオメトリシェーダを使うときの設定をここでする
        // この作業は glAttachShaderとglLinkProgramの間に入れる
        glProgramParameteri(*ProgramID, GL_GEOMETRY_VERTICES_OUT, 3);//ジオメトリシェーダからの最大出力頂点数
        glProgramParameteri(*ProgramID, GL_GEOMETRY_INPUT_TYPE, GL_POINTS);//ジオメトリシェーダには頂点が入力される
        glProgramParameteri(*ProgramID, GL_GEOMETRY_OUTPUT_TYPE, GL_POINTS);//ジオメトリシェーダからは頂点が出力される
    
        glLinkProgram(*ProgramID);
    
        ////////////////////////////////////////
        // プログラムをチェックします。
        glGetProgramiv(*ProgramID, GL_LINK_STATUS, &Result);
        glGetProgramiv(*ProgramID, GL_INFO_LOG_LENGTH, &InfoLogLength);
        std::vector<char> ProgramErrorMessage((std::max)(InfoLogLength, int(1)));
        glGetProgramInfoLog(*ProgramID, InfoLogLength, NULL, &ProgramErrorMessage[0]);
    
        if (error) {
            *error = &ProgramErrorMessage[0];
    
            return GLSLCompileCond::LINK_ERROR;
        }
    
        return GLSLCompileCond::SUCCESS;
    }
    
    
    void init() {
    
        std::vector<GLfloat> colors;
        std::vector<GLfloat> points;
    
        GLfloat v = 0.5;
    
        push_3(colors, 1, 0, 0);
        push_3(points, v, 0, 0);
    
        push_3(colors, 0, 1, 0);
        push_3(points, 0, v, 0);
    
        push_3(colors, 0, 0, 1);
        push_3(points, 0, 0, 0);
    
        prepare_buffer(&buf_points, points.data(), points.size() * sizeof(GLfloat), GL_STATIC_DRAW);
        prepare_buffer(&buf_colors, colors.data(), colors.size() * sizeof(GLfloat), GL_STATIC_DRAW);
    
        GLuint vtxShader;
        GLuint flagShader;
        GLuint geomShader;
    
        std::string verr;
        std::string ferr;
        std::string gerr;
        prepare_shader_byfile(&vtxShader, GL_VERTEX_SHADER, vtxfile, &verr);
        prepare_shader_byfile(&flagShader, GL_FRAGMENT_SHADER, fragfile, &ferr);
        prepare_shader_byfile(&geomShader, GL_GEOMETRY_SHADER, geomfile, &gerr);//ジオメトリシェーダのコンパイル
    
        std::cout << verr << std::endl;
        std::cout << ferr << std::endl;
        std::cout << gerr << std::endl;
    
        std::string linkerr;
        link_program_with_geometry(
            &programID,
            vtxShader,
            flagShader,
            geomShader,
            &linkerr
        );
        std::cout << linkerr << std::endl;
    
    
    
        loadidentity44(mproj);
        loadidentity44(mmodel);
    }
    
    
    //描画関数
    void disp(void) {
    
        glViewport(0, 0, width, height);
    
        glClearColor(0.2, 0.2, 0.2, 1);
        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
    
        glUseProgram(programID);
        GLuint loc_ModelViewMatrix = glGetUniformLocation(programID, "ModelViewMatrix");
        GLuint loc_ProjectionMatrix = glGetUniformLocation(programID, "ProjectionMatrix");
    
    
        glUniformMatrix4fv(loc_ModelViewMatrix, 1, GL_FALSE, mmodel);
        glUniformMatrix4fv(loc_ProjectionMatrix, 1, GL_FALSE, mproj);
    
        {
            glEnable(GL_VERTEX_PROGRAM_POINT_SIZE);
    
            auto bindbufP = EnableAndBindFArrayBuffer(0, buf_points, 3, GL_FLOAT);
            auto bindbufC = EnableAndBindFArrayBuffer(1, buf_colors, 3, GL_FLOAT);
    
            glDrawArrays(GL_POINTS, 0,3);
    
        }
        glFlush();
    }
    
    //ウィンドウサイズの変化時に呼び出される
    void reshape(int w, int h) {
        width = w; height = h;
    
        disp();
    }
    
    //エントリポイント
    int main(int argc, char** argv)
    {
        glutInit(&argc, argv);
        glutInitWindowPosition(100, 50);
        glutInitWindowSize(500, 500);
        glutInitDisplayMode(GLUT_SINGLE | GLUT_RGBA);
    
        glutCreateWindow("sample");
        glutDisplayFunc(disp);
        glutReshapeFunc(reshape);
    
        glewInit();
    
        init();
    
        glutMainLoop();
    
        return 0;
    }
    

    GLSLのシェーダ関係の処理を関数化(C++)

    GLSLを使おうとすると何かとコードが長くなる。状況に合わせて細かい調整ができるところが魅力なのだが練習やテストで毎回書くのは大変なので使いまわせそうなところだけでも関数化しておきたい。

    prepare_shader.hpp

    #pragma once
    
    #include <string>
    #include <vector>
    #include <fstream>
    #include <sstream>
    #include <algorithm>
    #include <functional>
    
    #include <gl/glew.h>
    #pragma comment(lib,"glew32.lib")
    
    
    enum class GLSLCompileCond {
        SUCCESS = 0,
        FILE_OPEN_ERROR = 1,
        COMPILE_ERROR = 2,
        LINK_ERROR = 3
    };
    
    //! @brief シェーダをソースコードからコンパイルする
    //! @param [out] ShaderID 作成したシェーダのID
    //! @param [in] shader_type GL_VERTEX_SHADER , GL_FRAGMENT_SHADER , GL_GEOMETRY_SHADER のいずれか
    //! @param [in] sourcecode シェーダのソースコード
    //! @param [out] error エラー出力先。エラーの内容を確認しないならnullptrを指定
    GLSLCompileCond prepare_shader_bytext(
        GLuint* ShaderID,
        const GLenum shader_type, 
        const char* sourcecode,
        std::string* error = nullptr) {
    
        //////////////////////////////////////////////
          // シェーダを作ります
          // (作るといっても宣言みたいなもの)
        *ShaderID = glCreateShader(shader_type);
    
        glShaderSource(*ShaderID, 1, &sourcecode, NULL);
        glCompileShader(*ShaderID);
    
        ////////////////////////////////////////////
        // エラーチェック
        GLint Result = GL_FALSE;
        int InfoLogLength;
    
        // 頂点シェーダをチェックします。
        glGetShaderiv(*ShaderID, GL_COMPILE_STATUS, &Result);
        glGetShaderiv(*ShaderID, GL_INFO_LOG_LENGTH, &InfoLogLength);
        if (Result == GL_FALSE) {
            if (error) {
                std::vector<char> ShaderErrorMessage(InfoLogLength);
                glGetShaderInfoLog(*ShaderID, InfoLogLength, NULL, &ShaderErrorMessage[0]);
                *error = &ShaderErrorMessage[0];
            }
            return GLSLCompileCond::COMPILE_ERROR;
        }
    
        return GLSLCompileCond::SUCCESS;
    }
    //! @brief ファイルからシェーダを読み込む
    //! @param [out] ShaderID シェーダの出力先
    //! @param [in] shader_type GL_VERTEX_SHADER , GL_FRAGMENT_SHADER , GL_GEOMETRY_SHADERのいずれか
    //! @param [in] shader_file_path シェーダファイルのパス
    //! @param [out] エラーメッセージ出力先
    GLSLCompileCond prepare_shader_byfile(
        GLuint* ShaderID,
        const GLenum shader_type,
        const char* shader_file_path,
        std::string* error = nullptr) {
    
    
        ////////////////////////////////////////////
        // ファイルから頂点シェーダを読み込みます。
        // 注意 ここはSTLのifstreamとかstringstreamの使い方の話で、
        // OpenGL命令は一つも無い。
        std::string ShaderCodeText;
        std::ifstream fileinput(shader_file_path, std::ios::in);
        if (fileinput.is_open())
        {
            std::stringstream sstr;
            sstr << fileinput.rdbuf();
            ShaderCodeText = sstr.str();
            fileinput.close();
        }
        else {
            if(error)
                *error = "file open failed";
            return GLSLCompileCond::FILE_OPEN_ERROR;
        }
        ////////////////////////////////////////////
        // 頂点シェーダをコンパイルします。
        return prepare_shader_bytext(ShaderID,shader_type,ShaderCodeText.c_str(),error);
    
    }
    //! @brief シェーダをリンクする
    //! @param [out] ProgramID プログラムIDの保存先
    //! @param [in] ShaderIDs シェーダ一覧
    //! @param [in] geometryprogramparameter ジオメトリシェーダの設定をする関数
    //! @param [out] error エラーメッセージ出力先
    //! @return エラー状態
    GLSLCompileCond link_program(
        GLuint* ProgramID,
        const std::vector<GLuint> ShaderIDs,
        std::function<void(GLuint)> geometryprogramparameter,
        std::string* error = nullptr) {
    
        GLint Result = GL_FALSE;
        int InfoLogLength;
    
        ////////////////////////////////////////
        // プログラムをリンクします。
        *ProgramID = glCreateProgram();
    
        for (GLuint shader : ShaderIDs) {
            glAttachShader(*ProgramID, shader);
        }
    
        if(geometryprogramparameter)
            geometryprogramparameter(*ProgramID);
    
        glLinkProgram(*ProgramID);
    
        ////////////////////////////////////////
        // プログラムをチェックします。
        glGetProgramiv(*ProgramID, GL_LINK_STATUS, &Result);
        glGetProgramiv(*ProgramID, GL_INFO_LOG_LENGTH, &InfoLogLength);
        std::vector<char> ProgramErrorMessage((std::max)(InfoLogLength, int(1)));
        glGetProgramInfoLog(*ProgramID, InfoLogLength, NULL, &ProgramErrorMessage[0]);
    
        if (error) {
            *error = &ProgramErrorMessage[0];
    
            return GLSLCompileCond::LINK_ERROR;
        }
    
        return GLSLCompileCond::SUCCESS;
    }
    //! @brief データを転送
    //! @param [in,out] buffer_name バッファ名へのポインタ
    //! @param [in] data バッファの配列の先頭アドレス
    //! @param [in] databytes データの大きさ
    //! @param [in] usage GL_STATIC_DRAWまたはGL_DYNAMIC_DRAW
    bool prepare_buffer(
        GLuint *buffer_name,
        const void* data,
        const size_t databytes,
        const GLenum usage
        ) {
        
        if (*buffer_name == 0) {
            glGenBuffers(1, buffer_name);
        }
        else {
            glDeleteBuffers(1, buffer_name);
            *buffer_name = 0;
            glGenBuffers(1, buffer_name);
        }
        if (*buffer_name != 0) {
            glBindBuffer(GL_ARRAY_BUFFER, *buffer_name);
            glBufferData(
                GL_ARRAY_BUFFER,
                databytes,
                data,
                usage);
            return true;
        }
        return false;
    
    }
    class EnableAndBindFArrayBuffer {
        GLuint m_layout;
    public:
        //! @brief バッファを有効にする
        //! @param [in] layout
        //! @param [in] buffername
        //! @param [in] element_count
        //! @param [in] datatype
        //! @param [in] normalized
        //! @param [in] stride
        //! @param [in] offset 
        EnableAndBindFArrayBuffer(
            const GLuint layout,
            const GLuint buffername,
            const GLuint element_count,
            const GLenum datatype,
            const GLboolean normalized = false,
            const GLsizei stride = 0,
            const void* offset = nullptr
        ) {
            glEnableVertexAttribArray(layout);
            glBindBuffer(GL_ARRAY_BUFFER, buffername);
            glVertexAttribPointer(
                layout,
                element_count,
                datatype,
                normalized,
                stride,
                offset
            );
            m_layout = layout;
        }
    
        //! @brief デストラクタで終了処理
        ~EnableAndBindFArrayBuffer() {
            glDisableVertexAttribArray(m_layout);
        }
    };
    //! @brief コンテナにデータをpush_backする(4要素)
    template<typename Container>
    void push_4(
        Container& container,
        const typename Container::value_type a,
        const typename Container::value_type b,
        const typename Container::value_type c,
        const typename Container::value_type d) {
        container.push_back(a);
        container.push_back(b);
        container.push_back(c);
        container.push_back(d);
    }
    
    //! @brief コンテナにデータをpush_backする(3要素)
    template<typename Container>
    void push_3(
        Container& container, 
        const typename Container::value_type a, 
        const typename Container::value_type b, 
        const typename Container::value_type c) {
        container.push_back(a);
        container.push_back(b);
        container.push_back(c);
    }
    
    //! @brief コンテナにデータをpush_backする(2要素)
    template<typename Container>
    void push_2(
        Container& container, 
        const typename Container::value_type a,
        const typename Container::value_type b
        ) {
        container.push_back(a);
        container.push_back(b);
    }
    //! @brief 単位行列
    //! @param [out] p 4x4行列
    template<typename T>
    void loadidentity44(T& p) {
        p[0] = 1;
        p[1] = 0;
        p[2] = 0;
        p[3] = 0;
    
        p[4] = 0;
        p[5] = 1;
        p[6] = 0;
        p[7] = 0;
    
        p[8] = 0;
        p[9] = 0;
        p[10] = 1;
        p[11] = 0;
    
        p[12] = 0;
        p[13] = 0;
        p[14] = 0;
        p[15] = 1;
    }
    

    使い方

    main.cpp

    #include <iostream>
    
    #include <Windows.h>
    #include "prepare_shader.hpp"
    #include <gl/GL.h>
    #include <gl/GLU.h>
    #include <gl/freeglut.h>
    
    #pragma comment(lib,"glew32.lib")
    
    //ウィンドウの幅と高さ
    int width, height;
    
    GLuint buf_points;
    GLuint buf_colors;
    
    GLfloat mproj[16];
    GLfloat mmodel[16];
    GLuint programID;
    const char* vtxfile = "default.vert";
    const char* fragfile = "default.frag";
    
    
    // シェーダとデータの設定
    void init() {
    
        std::vector<GLfloat> colors;
        std::vector<GLfloat> points;
    
        GLfloat v = 0.8;
    
        push_3(colors, 0, 0, 1);//色 RGB
        push_3(points, 0, 0, 0);//座標 XYZ
    
        push_3(colors, 0, 1, 0);
        push_3(points, 0, v, 0);
    
        push_3(colors, 1, 0, 0);
        push_3(points, v, 0, 0);
    
        prepare_buffer(&buf_points, points.data(), points.size() * sizeof(GLfloat), GL_STATIC_DRAW);
        prepare_buffer(&buf_colors, colors.data(), colors.size() * sizeof(GLfloat), GL_STATIC_DRAW);
    
        GLuint vtxShader;
        GLuint flagShader;
    
        std::string verr;
        std::string ferr;
        prepare_shader_byfile(&vtxShader, GL_VERTEX_SHADER, vtxfile, &verr);
        prepare_shader_byfile(&flagShader, GL_FRAGMENT_SHADER, fragfile, &ferr);
    
        std::cout << verr << std::endl;
        std::cout << ferr << std::endl;
    
        std::string linkerr;
        link_program(&programID, { vtxShader ,flagShader }, nullptr, &linkerr);
    
        loadidentity44(mproj);
        loadidentity44(mmodel);
    }
    
    
    //描画関数
    void disp(void) {
    
        glViewport(0, 0, width, height);
    
        glClearColor(0.2, 0.2, 0.2, 1);
        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
    
        glUseProgram(programID);
        GLuint loc_ModelViewMatrix = glGetUniformLocation(programID, "ModelViewMatrix");
        GLuint loc_ProjectionMatrix = glGetUniformLocation(programID, "ProjectionMatrix");
    
    
        glUniformMatrix4fv(loc_ModelViewMatrix, 1, GL_FALSE, mmodel);
        glUniformMatrix4fv(loc_ProjectionMatrix, 1, GL_FALSE, mproj);
    
        {
            glEnable(GL_VERTEX_PROGRAM_POINT_SIZE);
    
            auto bindbufP = EnableAndBindFArrayBuffer(0, buf_points, 3, GL_FLOAT);
            auto bindbufC = EnableAndBindFArrayBuffer(1, buf_colors, 3, GL_FLOAT);
    
            glDrawArrays(GL_POINTS, 0,3);
    
        }
        glFlush();
    }
    
    
    //ウィンドウサイズの変化時に呼び出される
    void reshape(int w, int h) {
        width = w; height = h;
    
        disp();
    }
    
    //エントリポイント
    int main(int argc, char** argv)
    {
        glutInit(&argc, argv);
        glutInitWindowPosition(100, 50);
        glutInitWindowSize(500, 500);
        glutInitDisplayMode(GLUT_SINGLE | GLUT_RGBA);
    
        glutCreateWindow("sample");
        glutDisplayFunc(disp);
        glutReshapeFunc(reshape);
    
        glewInit();
    
        init();
    
        glutMainLoop();
    
        return 0;
    }
    

    default.vert

    #version 460 core
    
    layout (location = 0) in vec3 coord;
    layout (location = 1) in vec3 incolor;
    
    out vec4 vColor;
    
    uniform mat4 ModelViewMatrix;
    uniform mat4 ProjectionMatrix;
    
    void main()
    {
      gl_Position = ProjectionMatrix * ModelViewMatrix * vec4(coord, 1.0);
      vColor = vec4(incolor, 1.0);
      gl_PointSize=30.0;
    }
    

    default.frag

    #version 460 core
    
    in vec4 vColor;
    
    out vec4 FragColor;
    
    void main()
    {
      FragColor = vColor;
    } 
    

    std::vectorもstd::arrayもローカル変数の型として同じように扱うためにresizeの呼び出しを切り替える

    問題

    以下のようなcopy関数を作りたい場合、テンプレート引数Tがarrayの時はresizeできない一方で、vectorの時はresizeしなければならない。

    これを同じように扱えるようにしたい。具体的にはresize関数の有無で処理を分けたい。

    #include <iostream>
    
    #include <vector>
    #include <array>
    
    
    template<typename T>
    T copy(T& data) {
    
      T tmp;
    
      // vectorの時はresizeしないと要素数 0 で落ちる
      // arrayの時はresizeが存在しないのでコンパイルできない
      //tmp.resize(3);
    
      tmp[0] = data[0];
      tmp[1] = data[1];
      tmp[2] = data[2];
    
      return tmp;
    }
    int main()
    {
    
      std::vector<float> vec{ 1.0f,1.0f,1.0f };
      std::array<float, 3> ary{ 2.0f,2.0f,2.0f };
    
      auto dup_vec = copy(vec); //これをしたい時はresizeを有効に
      auto dup_ary = copy(ary); //これをしたい時はresizeを無効に
    
      printf("%lf %lf %lf\n", dup_vec[0], dup_vec[1], dup_vec[2]);
      printf("%lf %lf %lf\n", dup_ary[0], dup_ary[1], dup_ary[2]);
    
      getchar();
    
    }
    

    解決

    やっていることはメンバ関数resizeの存在の判定をして構造体を切り替えているだけ。

    has_resize.hpp

    #include <type_traits>
    
    namespace szl {
    
      struct has_resize_impl {
    
    
        // Tにresizeメンバ関数がある場合はこちらが有効(実体化はしない)
        // 戻り値がstd::true_typeの関数宣言
        template<typename T>
        static auto check_has_resize (T& o)
          ->decltype(
            o.resize(0), 
            std::true_type()
          );
    
    
        // Tにresizeがない場合はこちらが有効(実体化はしない)
        // 戻り値がstd::false_typeの関数宣言
        static auto check_has_resize(...)
          ->decltype(
            std::false_type()
          );
      };
    
      //プライマリテンプレート
      template<typename T>
      struct _myresize_;
    
    
      //! @brief resizeがある場合呼び出される
      template<>
      struct _myresize_<std::true_type> {
        template<typename T>
        static void resize_func(T& t, const size_t size) {
          if (size > t.size()) {
            t.resize(size);
          }
        }
      };
    
    
      //!j @brief resizeがない場合に呼び出される。何もしない。
      template<>
      struct _myresize_<std::false_type> {
        template<typename T>
        static void resize_func(T& t, const size_t size) {
        }
      };
    
    
      //! @brief resize関数があるオブジェクトに対してのみresizeを実行する
      //! @param [in,out] obj 配列型のオブジェクト
      //! @param [in] 
      template<typename T>
      void resize_if_needed(T& obj, const size_t size) {
    
        // resizeを持つオブジェクトなら std::true_type
        // resizeを持たないオブジェクトなら std::false_type
        using type = decltype(has_resize_impl::check_has_resize(obj));
    
        // resizeあり/なしバージョンの関数を呼び出す
        _myresize_<type>::resize_func(obj, size);
      }
    }
    

    copy_generalの実装と使い方

    #include <iostream>
    
    #include <vector>
    #include <array>
    
    #include"has_resize.hpp"
    
    
    template<typename T>
    T copy_general(T& data) {
    
      T tmp;
    
      //型に応じてresizeをする関数としない関数を切り替える
      szl::resize_if_needed(tmp, 3);
    
      tmp[0] = data[0];
      tmp[1] = data[1];
      tmp[2] = data[2];
    
      return tmp;
    }
    int main()
    {
    
      std::vector<float> vec{ 1.0f,1.0f,1.0f };
      std::array<float, 3> ary{ 2.0f,2.0f,2.0f };
    
      auto dup_vec = copy_general(vec);
      auto dup_ary = copy_general(ary);
    
      printf("%lf %lf %lf\n", dup_vec[0], dup_vec[1], dup_vec[2]);
      printf("%lf %lf %lf\n", dup_ary[0], dup_ary[1], dup_ary[2]);
    
      getchar();
    
    }
    

    JP Procedural Stylized Rocks Tutorial [Blender 2.8] を試す(2)

    作業

    1. 3D View上での表示を変更する。

    2.変形

    Editモードに入り、ループカット(Ctrl+R)で切れ目を入れ、Z方向に4だけ移動する。底面を選択し縮小した後、Editモードを抜ける。

    3.モディファイアを適用する領域を設定

    VertexGroupの作成

    [a]で全ての頂点を選択し、[+]ボタンでVertexGroupを作成し名前を「Deform」として[Assign]する。

    モディファイアにVertexGroupを指定

    4.モディファイアを適用しない領域を指定

    現在、”Deform”グループには全ての頂点が登録されているので、「グループから除外したい頂点を選択し」[Remove]ボタンを押す。

    5.他のオブジェクトにモディファイアをコピー

    モディファイアが多いので他のオブジェクトを用意しモディファイア群だけを複製する。

    「①新しいモデル」「②Rockのモディファイア群を設定したモデル」の順番に選択し、[Ctrl+L]→Modifiresを選択しコピーする。

    JP Procedural Stylized Rocks Tutorial [Blender 2.8] を試す(1)

    なおここではBlender 2.90で試す。

    手順

    Step.0 [Edit]モードへ行き、初期のCubeを5倍にする。

    以降、以下のようにモディファイア(とObject Data Properties)を編集していく。

    Step.1 Subdivision Surfaceモディファイア

    Step.2 Displaceモディファイア(一つ目)

    BaseDisplacementNoiseテクスチャを以下のように設定

    Step.3 Displaceモディファイア(二つ目)

    SecondaryDisplacementNoiseテクスチャを設定。殆どBaseDisplacementNoiseと同じで、sizeを3.0に変更する

    Step.4 Decimateモディファイア(一つ目)

    Step.5 Decimateモディファイア(二つ目)

    Step.6 ObjectDataProperties

    Step.7 Smoothモディファイア

    Step.8 Bevelモディファイア

    Step.9 WeightNormal

    これは追加するだけで、パラメータの変更はしない。

    Step.10 Triangulate

    続き

    形状はこれで(ほぼ)完了。めずらしくチュートリアルに極めて近い物ができた。次回は残っているライティングとかもやってみる。

    glActiveTextureでテクスチャの下地の色をテクスチャで設定

    glColor3dで下地の色を設定

    テクスチャはポリゴンに貼るが、そのポリゴンの色が何色かで張り付けた結果に違いが出る。

    以下は、テクスチャを一枚用意し、それをglColor3dでポリゴンの色を指定した上に張り付けた結果。

    テクスチャ:https://pixabay.com/ja/photos/%E9%B3%A5-%E3%83%96%E3%83%AB%E3%83%BC%E3%83%90%E3%83%BC%E3%83%89-%E9%B3%A5png-1232416/

    //描画関数
    void disp(void) {
    
        glViewport(0, 0, winwidth, winheight);
    
        glClearColor(0.2, 0.2, 0.2, 1);
        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
    
        glEnable(GL_CULL_FACE);
        double v = 0.95;
    
    
        //下地の色を設定
        glColor3d(1, 0, 0);
        //テクスチャを有効化
        glBindTexture(GL_TEXTURE_2D, texname);
        glEnable(GL_TEXTURE_2D);
        glBegin(GL_QUADS);
    
        glTexCoord2d(0, 1);
        glVertex2d(-v, -v);
    
        glTexCoord2d(1, 1);
        glVertex2d(v, -v);
    
        glTexCoord2d(1, 0);
        glVertex2d(v, v);
    
        glTexCoord2d(0, 0);
        glVertex2d(-v, v);
    
        glEnd();
        glFlush();
    }
    
    

    glActiveTexture

    glActiveTextureを使うと、下地の色用のテクスチャと、張り付けたい画像のテクスチャを同時に指定できる。「同時に指定できる」とはつまり、普通glBindTextureを一度実行したらほかのテクスチャは無効になるが、glActiveTextureを使えば二つのglBindTextureを有効にできるという意味。

    OpenGLにはテクスチャスロットのような概念がある。

    glActiveTexture(GL_TEXTURE0);

    で、「これからスロット名”GL_TEXTURE0″の設定をする」という意味になる。

    そして、一つのスロットで一度に使えるテクスチャは一枚だけで、これは「最後にglBindTextureで指定したテクスチャ」だが、一度に複数のスロットを使えるので、各スロットで glEnable(GL_TEXTURE_2D)でそのスロットを有効にしてやれば、テクスチャを複数使うことができる。

    #include <iostream>
    
    #include <Windows.h>
    #include <gl/glew.h>// glActiveTextureに必要
    #include <gl/GL.h>
    #include <gl/GLU.h>
    #include <gl/freeglut.h>
    
    #pragma comment(lib,"glew32.lib")
    
    #define STB_IMAGE_IMPLEMENTATION
    #include "stb_image.h"
    
    // freeglut:
    // http://freeglut.sourceforge.net/
    
    //ウィンドウの幅と高さ
    int winwidth, winheight;
    
    
    
    GLuint texname_img;
    GLuint texname_block;
    
    
    //下地用のテクスチャ
    void init_tex_block() {
    
        unsigned char pixels[] = {
            255,  0,  0,
              0,255,  0,
              0,  0,255,
            255,255,255
        };
    
        glGenTextures(1, &texname_block);
        glActiveTexture(GL_TEXTURE0);
        glBindTexture(GL_TEXTURE_2D, texname_block);
        glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
    
        glTexImage2D(
            GL_TEXTURE_2D, 0, GL_RGB, 2, 2,
            0, GL_RGB, GL_UNSIGNED_BYTE, pixels
        );
    }
    //画像読み込み,テクスチャとしてロード
    //pngの読み込みに STBライブラリを使用
    //https://suzulang.com/stb-library-image-read/
    //https://github.com/nothings/stb
    void init_tex_img()
    {
        unsigned char* pixels;
        int bpp;
        int imgwidth;
        int imgheight;
    
        // ファイルを読み込み、画像データを取り出す
        //   最後の引数でピクセルあたりのバイト数を強制できる
        pixels = stbi_load(R"(C:\Users\szl\Desktop\bird-1232416_640.png)", &imgwidth, &imgheight, &bpp, 0);
    
        glGenTextures(1, &texname_img);
        glActiveTexture(GL_TEXTURE1);
        glBindTexture(GL_TEXTURE_2D, texname_img);
        glPixelStorei(GL_UNPACK_ALIGNMENT, 4);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
    
        glTexImage2D(
            GL_TEXTURE_2D, 0, GL_RGBA, imgwidth, imgheight,
            0, GL_RGBA, GL_UNSIGNED_BYTE, pixels
        );
    
    
    
        // メモリ上の画像データを破棄
        stbi_image_free(pixels);
    
    
    }


    //描画関数
    void disp(void) { glViewport(0, 0, winwidth, winheight); glClearColor(0.2, 0.2, 0.2, 1); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); glEnable(GL_CULL_FACE); double v = 0.95;
        glActiveTexture(GL_TEXTURE0);
        glEnable(GL_TEXTURE_2D);
        glBindTexture(GL_TEXTURE_2D,texname_block);
        glActiveTexture(GL_TEXTURE1);
        glEnable(GL_TEXTURE_2D);
        glBindTexture(GL_TEXTURE_2D, texname_img);
        glBegin(GL_QUADS);
        glMultiTexCoord2f(GL_TEXTURE0, 0, 1);
        glMultiTexCoord2f(GL_TEXTURE1, 0, 1);
        glVertex2d(-v, -v);
    
        glMultiTexCoord2f(GL_TEXTURE0, 1, 1);
        glMultiTexCoord2f(GL_TEXTURE1, 1, 1);
        glVertex2d(v, -v);
    
        glMultiTexCoord2f(GL_TEXTURE0, 1, 0);
        glMultiTexCoord2f(GL_TEXTURE1, 1, 0);
        glVertex2d(v, v);
    
        glMultiTexCoord2f(GL_TEXTURE0, 0, 0);
        glMultiTexCoord2f(GL_TEXTURE1, 0, 0);
        glVertex2d(-v, v);
    
        glEnd();
    
        glActiveTexture(GL_TEXTURE1);
        glDisable(GL_TEXTURE_2D);
        glActiveTexture(GL_TEXTURE0);
        glDisable(GL_TEXTURE_2D);
    
    
        glFlush();
    }
    
    //ウィンドウサイズの変化時に呼び出される
    void reshape(int w, int h) {
        winwidth = w; 
        winheight = h;
    
        disp();
    }
    
    
    
    //エントリポイント
    int main(int argc, char** argv)
    {
        glutInit(&argc, argv);
        glutInitWindowPosition(100, 50);
        glutInitWindowSize(640, 480);
        glutInitDisplayMode(GLUT_SINGLE | GLUT_RGBA);
        glutCreateWindow("sample");
        glutDisplayFunc(disp);
        glutReshapeFunc(reshape);
    
        glewInit();
    
        init_tex_img();
        init_tex_block();
    
        glutMainLoop();
    
        return 0;
    }
    

    OpenGLで裏面と表面で別の色を指定する

    glMaterialfvで着色する場合、第一引数にGL_FRONTかGL_BACKを指定して切り替える


    glColor3fで着色する場合、カリングを使い裏面のみ描画、表面のみ描画を切り替えて二回レンダリングする。

    ポリゴン描画関数本体

      //ポリゴンを描画
      void render() {
        float v = 0.7;
        glBegin(GL_QUADS);
    
        glVertex3f(-v, -v, 0.0);
    
        glVertex3f(v, -v, 0.0);
    
        glVertex3f(v, v, 0.0);
    
        glVertex3f(-v, v, 0.0);
        glEnd();
    
      }
    

    glColor3*で描画

      void draw_by_color() {
        glEnable(GL_CULL_FACE);//カリングを有効化
    
        glCullFace(GL_FRONT);
        glColor4f(1, 0, 0, 1);
        render();
    
        glCullFace(GL_BACK);
        glColor4f(0, 1, 0, 1);
        render();
    
      }
    

    glMaterialfvで描画

    void draw_by_material() {
      glEnable(GL_LIGHTING);
      glEnable(GL_LIGHT0);
    
      GLfloat white[4]{ 1,1,1,1 };
      GLfloat red[4]{ 1,0,0,1 };
      GLfloat green[4]{ 0,1,0,1 };
    
      GLfloat pos[4]{ 0,0,0,1 };
    
      glLightModeli(GL_LIGHT_MODEL_TWO_SIDE, 1);//両面に光を当てる
      glLightfv(GL_LIGHT0, GL_POSITION, pos);//ライト位置設定
      glLightfv(GL_LIGHT0, GL_AMBIENT, white);//環境光色設定
    
      glMaterialfv(GL_FRONT, GL_AMBIENT, red);//表面の材質
      glMaterialfv(GL_BACK, GL_AMBIENT, green);//裏面の材質
    
      render();//オブジェクトのレンダリング
    
      glDisable(GL_LIGHTING);
      glDisable(GL_LIGHT0);
    }
    

    描画関数呼び出し

      void draw()
      {
    
        /////////////////////
        // 描画
        glViewport(0, 0, width, height);
    
        glClearColor(0.3, 0.3, 0.3, 1);
        glClear(GL_COLOR_BUFFER_BIT);
    
    
        Sleep(1);
        angle++;
    
        glPushMatrix();
        glRotated(angle, 1, 0, 0);
    
        //draw_by_color();//この二つの関数切り替え
        draw_by_material();
    
        glPopMatrix();
        //
        /////////////////////
    
        glFlush();
        glfwSwapBuffers(_window);
    
      }
    

    glFramebufferTexture2D に渡す GL_COLOR_ATTACHMENT0 は何か

    以前フレームバッファをやってみたときに、glFramebufferTexture2D関数を使ったが、第二引数に与えたGL_COLOR_ATTACHMENT0について言及しなかった(気がする)。当時は調べて納得はしていたが改めて読み返して混乱したのでここで明確にしておきたい。

    glGenFramebuffers(1, &framebuffer);
    glBindFramebuffer(GL_FRAMEBUFFER, framebuffer);
    
    /* ... */
    
    glGenTextures(1, texture0);
    glGenTextures(1, texture1);
    
    /* ... */
    
    glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, texture0, 0);
    glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT1, GL_TEXTURE_2D, texture1, 0);
    

    このように指定し、フラグメントシェーダ側で以下のように出力する。

    #version 460 core
    
    layout (location = 0) out vec4 DefaultOut;// 0、つまりGL_COLOR_ATTACHMENT0
    layout (location = 1) out vec4 RedOnly; // 1、つまりGL_COLOR_ATTACHMENT1
    
    in vec4 vertexColor;
    
    void main()
    {
      DefaultOut = vertexColor;
      RedOnly = vec4(vertexColor[0],0,0,1);
    
    }
    

    つまり、GL_COLOR_ATTACHMENT0はフラグメントシェーダのlocationを指定している。

    サンプル全体

    #include <memory>
    #include <iostream>
    #include <vector>
    #include <algorithm>
    #include <array>
    
    #include<windows.h>
    #include <gl/glew.h>
    #include "CglfwBase.hpp"
    
    #pragma warning(disable:4996)
    
    #pragma comment(lib,"opengl32.lib")
    #pragma comment(lib,"glew32.lib")
    
    
    //! @brief PPM(RGB各1byte,カラー,テキスト)を書き込む
    //! @param [in] fname ファイル名
    //! @param [in] vmax 全てのRGBの中の最大値
    //! @param [in] width 画像の幅
    //! @param [in] height 画像の高さ
    //! @param [in] p 画像のメモリへのアドレス
    //! @details RGBRGBRGB....のメモリを渡すと、RGBテキストでファイル名fnameで書き込む
    void pnmP3_Write(const TCHAR* const fname, const int vmax, const int width, const int height, const unsigned char* const p) { // PPM ASCII
    
      
      FILE* fp = _wfopen(fname, L"wb");
      fprintf(fp, "P3\n%d %d\n%d\n", width, height, vmax);
    
      size_t k = 0;
      for (size_t i = 0; i < (size_t)height; i++) {
        for (size_t j = 0; j < (size_t)width; j++) {
          fprintf(fp, "%d %d %d ", p[k * 3 + 0], p[k * 3 + 1], p[k * 3 + 2]);
          k++;
        }
        fprintf(fp, "\n");
      }
    
      fclose(fp);
    }
    //! @brief フレームバッファに関する変数と機能をまとめる
    class FramebufferTest {
    public:
      GLuint ID_texture_0; // テクスチャ1
      GLuint ID_texture_1; // テクスチャ2
    
      GLuint ID_fbo;// フレームバッファ
    
      int width, height;//テクスチャのサイズ
    private:
      GLuint ID_depth;
    
    
      //! @brief テクスチャを一枚作成し、フレームバッファに関連付ける
      void generate_texture(GLuint *texname, GLenum attachment) {
        glGenTextures(1, texname);
    
        glBindTexture(GL_TEXTURE_2D, *texname);
    
        //テクスチャを転送する。
        //ただし最後がnullptrなので実質転送しない
        glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, nullptr);
    
        //テクスチャのフィルタリングの設定
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
    
        //描画先の設定
        // attachmentへの書き込みで、texnameに書き込まれるようにする
        glFramebufferTexture2D(GL_FRAMEBUFFER, attachment, GL_TEXTURE_2D, *texname, 0);
    
      }

    public:
      // フレームバッファオブジェクトの作成と設定を行う
      // 途中で描画先のテクスチャも作成する
      void prepare(int WIDTH,int HEIGHT) {
        width = WIDTH;
        height = HEIGHT;
    
        //////////////////////////////////////////////////////
        //フレームバッファの作成
        // 削除はglDeleteFrameBuffersを使用する
        glGenFramebuffers(1, &ID_fbo);
        glBindFramebuffer(GL_FRAMEBUFFER, ID_fbo);
    
    
    
        //////////////////////////////////////////////////////
        //テクスチャバッファの作成
        // 削除はglDeleteTextures
        generate_texture(&ID_texture_0, GL_COLOR_ATTACHMENT0);
        generate_texture(&ID_texture_1, GL_COLOR_ATTACHMENT1);
    
        // fragment shaderの
        //  layout (location = 0) ・・・ GL_COLOR_ATTACHMENT0
        //  layout (location = 1) ・・・ GL_COLOR_ATTACHMENT1
        // が書き込み先であることを教える(?)
        GLenum DrawBuffers[2] = { GL_COLOR_ATTACHMENT0 , GL_COLOR_ATTACHMENT1 };
        glDrawBuffers(2, DrawBuffers); // "2"はDrawBuffersのサイズです。

    ////////////////////////////////////////////////////// // デプスバッファの作成 // レンダーバッファの一種なので、削除はglDeleteRenderbuffersを使用する glGenRenderbuffers(1, &ID_depth); glBindRenderbuffer(GL_RENDERBUFFER, ID_depth); glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT, width, height); glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, ID_depth); /////////////////////////////////////////////////// // フレームバッファのエラーチェック。 if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) return;/*エラーデータ*/ }
    };
    
    
    class MyWindow :public MyWindowBase {
    
      int width;
      int height;
      FramebufferTest myfb;
    
      GLuint vertexbufferName;
      GLuint colorbufferName;
    
    public:
    
    
      //! @brief glew,glfwの設定 フレームバッファの初期化、シェーダの作成、データの作成
      virtual void setting()override {
    
        glewInit();
        glfwGetFramebufferSize(_window, &width, &height);
    
        //フレームバッファの初期化
        myfb.prepare(width, height);
    
        //シェーダに渡す描画データの作成
        prepare_data();
    
        //シェーダの作成
        prepare_shaders();
      }
    
    
      //! @brief キーイベント。フレームバッファによって書き込んだテクスチャデータをファイルへ出力
      virtual void key(int key, int scancode, int action, int mods)override {  
    
        //テクスチャ(ID_texture_output)に書き込んだデータを取り出す領域を用意
        std::vector<GLubyte> texdata;
        texdata.resize(myfb.width*myfb.height * 3);
    
        ////////////////////////////////////////////////////
        //テクスチャのデータを取り出す
        glBindTexture(GL_TEXTURE_2D, myfb.ID_texture_0);
        glGetTexImage(GL_TEXTURE_2D, 0, GL_RGB, GL_UNSIGNED_BYTE, &texdata[0]);
        // 取り出したテクスチャをファイルに書き出す
        pnmP3_Write(LR"(C:\test\texture0.ppm)", 255, width, height, texdata.data());
    
        ////////////////////////////////////////////////////
        //テクスチャのデータを取り出す
        glBindTexture(GL_TEXTURE_2D, myfb.ID_texture_1);
        glGetTexImage(GL_TEXTURE_2D, 0, GL_RGB, GL_UNSIGNED_BYTE, &texdata[0]);
        // 取り出したテクスチャをファイルに書き出す
        pnmP3_Write(LR"(C:\test\texture1.ppm)", 255, width, height, texdata.data());
      }
    
    
      //! @brief 描画関数
      virtual void draw()override
      {
        //以後の描画はフレームバッファに対して行われる
        glBindFramebuffer(GL_FRAMEBUFFER, myfb.ID_fbo);
    
        draw_core();
    
        //以後の描画は画面に対して行われる
        glBindFramebuffer(GL_FRAMEBUFFER, 0);
    
        draw_core();
    
        glfwSwapBuffers(_window);
    
      }
    
    
      //! @brief 描画関数本体
    void draw_core() { ///////////////////// // 描画 glViewport(0, 0, width, height); glClearColor(0.3, 0.3, 0.3, 1); glClear(GL_COLOR_BUFFER_BIT); glEnable(GL_BLEND); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); glUseProgram(ProgramID); //gl_PointSizeを有効にする glEnable(GL_VERTEX_PROGRAM_POINT_SIZE); // 頂点バッファ glEnableVertexAttribArray(0); glBindBuffer(GL_ARRAY_BUFFER, vertexbufferName); glVertexAttribPointer( 0, // 属性0 3, // 1要素の要素数 x y z GL_FLOAT, // タイプ GL_FALSE, // 正規化しない(データが整数型の時) 0, // ストライド (void*)0 // 配列バッファオフセット ); // カラーバッファ glEnableVertexAttribArray(1); glBindBuffer(GL_ARRAY_BUFFER, colorbufferName); glVertexAttribPointer( 1, // 属性1 4, // 1要素の要素数 r g b a GL_FLOAT, // タイプ GL_FALSE, // 正規化しない(データが整数型の時) 0, // ストライド (void*)0 // 配列バッファオフセット ); // 5頂点を描く glDrawArrays(GL_POINTS, 0, 5); glDisableVertexAttribArray(0); glDisableVertexAttribArray(1); // ///////////////////// glFlush(); } GLuint VertexShaderID; GLuint FragmentShaderID; GLuint ProgramID;
      //! @brief シェーダの作成
    //! シェーダコードを直接埋め込んで、compileshaderを呼び出している

    void prepare_shaders() { const char* vshader = R"(
    #version 460 core
    
    layout (location = 0) in vec3 aPos;
    layout (location = 1) in vec3 incolor;
    
    out vec4 vertexColor;
    
    uniform mat4 gl_ModelViewMatrix;
    uniform mat4 gl_ProjectionMatrix;
    
    void main()
    {
      gl_Position = gl_ProjectionMatrix * gl_ModelViewMatrix * vec4(aPos, 1.0);
      vertexColor = vec4(incolor, 1.0);
      gl_PointSize=30.0; // 頂点のサイズ(ピクセル)
    }
    
    )";
        VertexShaderID = compileshader(GL_VERTEX_SHADER, vshader);
    
        const char* fshader = R"(
    
    #version 460 core
    
    layout (location = 0) out vec4 DefaultOut;// 0、つまりGL_COLOR_ATTACHMENT0
    layout (location = 1) out vec4 RedOnly; // 1、つまりGL_COLOR_ATTACHMENT1
    
    //out vec4 FragColor;
      
    in vec4 vertexColor;
    
    void main()
    {
      DefaultOut = vertexColor;
      RedOnly = vec4(vertexColor[0],0,0,1);
    
    }
    )";
    
        FragmentShaderID = compileshader(GL_FRAGMENT_SHADER, fshader);
    
    
        GLint Result = GL_FALSE;
        int InfoLogLength;
    
        ////////////////////////////////////////
        // プログラムをリンクします。
        fprintf(stdout, "Linking program\n");
        ProgramID = glCreateProgram();
        glAttachShader(ProgramID, VertexShaderID);
        glAttachShader(ProgramID, FragmentShaderID);
        glLinkProgram(ProgramID);
    
        ////////////////////////////////////////
        // プログラムをチェックします。
        glGetProgramiv(ProgramID, GL_LINK_STATUS, &Result);
        glGetProgramiv(ProgramID, GL_INFO_LOG_LENGTH, &InfoLogLength);
        std::vector<char> ProgramErrorMessage((std::max)(InfoLogLength, int(1)));
        glGetProgramInfoLog(ProgramID, InfoLogLength, NULL, &ProgramErrorMessage[0]);
        fprintf(stdout, "%s\n", &ProgramErrorMessage[0]);
    
    
      }
      //! @brief シェーダに渡す描画データの作成
      void prepare_data() {
    
        using point_t = std::array<GLfloat, 3>; //モデルの座標用 x y z
        using color_t = std::array<GLfloat, 4>;//テクスチャ座標用 r g b a
    
        /////////////////////////////////
        //バッファとデータ
        point_t vertex[5];
        color_t color[5];
        const int elemcount = 5;
        //////////////////////////////////////////
        vertex[0] = { -0.7, 0.7, 0 };
        vertex[1] = { -0.7,-0.7, 0 };
        vertex[2] = { 0.7,-0.7, 0 };
        vertex[3] = { 0.7, 0.7, 0 };
        vertex[4] = { 0  , 0  , 0 };
        color[0] = { 1.0,0.0,0.0,1.0 };
        color[1] = { 0.0,1.0,0.0,1.0 };
        color[2] = { 0.0,0.0,1.0,1.0 };
        color[3] = { 1.0,0.0,1.0,1.0 };
        color[4] = { 1.0,1.0,1.0,1.0 };
        //////////////////////////////////////////
        // バッファの転送
    
        glGenBuffers(1, &vertexbufferName);
        glBindBuffer(GL_ARRAY_BUFFER, vertexbufferName);
        glBufferData(GL_ARRAY_BUFFER, 3 * elemcount * sizeof(GLfloat), vertex, GL_STATIC_DRAW);
    
        glGenBuffers(1, &colorbufferName);
        glBindBuffer(GL_ARRAY_BUFFER, colorbufferName);
        glBufferData(GL_ARRAY_BUFFER, 4 * elemcount * sizeof(GLfloat), color, GL_STATIC_DRAW);
      }
      //! @brief シェーダをコンパイル・リンク
      GLuint compileshader(GLenum type,const char* VertexShaderCode) {
    
        GLuint shaderID;
    
        //////////////////////////////////////////////
          // シェーダを作ります
          // (作るといっても宣言みたいなもの)
        shaderID = glCreateShader(type);
    
        ////////////////////////////////////////////
        // 頂点シェーダをコンパイルします。
        printf("Compiling shader... \n");
        glShaderSource(shaderID, 1, &VertexShaderCode, NULL);
        glCompileShader(shaderID);
    
        ////////////////////////////////////////////
        // エラーチェック
        GLint Result = GL_FALSE;
        int InfoLogLength;
    
        // 頂点シェーダをチェックします。
        glGetShaderiv(shaderID, GL_COMPILE_STATUS, &Result);
        glGetShaderiv(shaderID, GL_INFO_LOG_LENGTH, &InfoLogLength);
        if (Result == FALSE) {
          std::vector<char> VertexShaderErrorMessage(InfoLogLength);
          glGetShaderInfoLog(shaderID, InfoLogLength, NULL, &VertexShaderErrorMessage[0]);
          fprintf(stdout, "%s\n", &VertexShaderErrorMessage[0]);
        }
    
        return shaderID;
      }
    
    
      virtual void framebuffer_size(int _width, int _height)override
      {
        width = _width;
        height = _height;
      }
    
    
    };
    
    
    int main()
    {
    
      MyWindow win;
    
      win.init(600,600,"glfw window");
      win.setting();
    
      win.loop();
    
    }
    

    出力

    画面出力ができたら、キーを何か押すとc:\test\texture0.ppmとtexture1.ppmを出力する。

    画像が逆転しているのはOpenGLは左下原点なので仕方がない。

    画面表示
    C:\test\texture0.ppm
    C:\test\texture1.ppm

    GLFWのコールバック関数でメンバ変数を呼び出す

    glfwSetWindowUserPointerでthisポインタを指定し、非メンバ関数であるコールバック関数の第一引数からthisを取り出せるようにすれば、間接的にだが自作クラスのメンバ関数を呼び出す事ができる。

    CglfwBase.hpp

    #pragma once
    
    #pragma comment(lib,"glfw3.lib")
    #include <gl/glfw/glfw3.h>
    
    class MyWindowBase {
    protected:
      GLFWwindow* _window;
    protected:
      // コールバック関数から呼ばれるメンバ関数群 //////////////////////////
    virtual void key(int key, int scancode, int action, int mods) {} virtual void mouse_button(int button, int action, int mods) {} virtual void mouse_move(double xpos, double ypos) {} virtual void mouse_wheel(double xoffset, double yoffset) {} virtual void framebuffer_size(int width, int height) {} ////////////////////////////////////////////// ////////////////////////////////////////////// virtual void setting() {} virtual void draw() {}
    protected:
    
    
      // GLFWに指定するコールバック関数(staticメンバ関数) //////////////////////////
      // GLFWのコールバック関数にはこれを指定する。glfwSetWindowUserPointerで指定したthisポインタを
      // glfwGetWindowUserPointerで取得し、メンバ関数を呼び出す
    
    static void key_callback(GLFWwindow* pwin, int key, int scancode, int action, int mods) { MyWindowBase* myw = static_cast<MyWindowBase*>(glfwGetWindowUserPointer(pwin)); myw->key(key, scancode, action, mods); } static void mouse_button_callback(GLFWwindow* pwin, int button, int action, int mods) { MyWindowBase* myw = static_cast<MyWindowBase*>(glfwGetWindowUserPointer(pwin)); myw->mouse_button(button, action, mods); } static void mouse_move_callback(GLFWwindow* pwin, double xpos, double ypos) { MyWindowBase* myw = static_cast<MyWindowBase*>(glfwGetWindowUserPointer(pwin)); myw->mouse_move(xpos, ypos); } static void mouse_wheel_callback(GLFWwindow* pwin, double xoffset, double yoffset) { MyWindowBase* myw = static_cast<MyWindowBase*>(glfwGetWindowUserPointer(pwin)); myw->mouse_wheel(xoffset, yoffset); } static void framebuffer_size_callback(GLFWwindow* pwin, int width, int height) { MyWindowBase* myw = static_cast<MyWindowBase*>(glfwGetWindowUserPointer(pwin)); myw->framebuffer_size(width, height); } static void draw_callback(GLFWwindow* pwin) { MyWindowBase* myw = static_cast<MyWindowBase*>(glfwGetWindowUserPointer(pwin)); myw->draw(); }
    public:
    
      // ここでGLFWの初期化を行う /////////////////////////////////
      int init(int winwidth,int winheight,const char* wintitle) {
    
    // GLFW の初期化 if (glfwInit() == GL_FALSE) { // 初期化に失敗したら終了 return 1; }
    // ウィンドウを作成 _window = glfwCreateWindow( winwidth, //width winheight, //height wintitle,//title NULL, //monitor NULL //share ); //////////////////////////////////////////////////////////////////////////////// // ウィンドウを作成できなければ終了 if (_window == nullptr) { glfwTerminate(); return 1; }
     
        ////////////////////////////////////////////////////////////////////////////////
        // 自身のポインタをGLFWwindowオブジェクトにセット
        glfwSetWindowUserPointer(_window, this);

    //////////////////////////////////////////// // コールバック関数の設定 glfwSetKeyCallback(_window, MyWindowBase::key_callback); glfwSetMouseButtonCallback(_window, MyWindowBase::mouse_button_callback); glfwSetScrollCallback(_window, MyWindowBase::mouse_wheel_callback); glfwSetCursorPosCallback(_window, MyWindowBase::mouse_move_callback); glfwSetFramebufferSizeCallback(_window, MyWindowBase::framebuffer_size_callback); glfwSetWindowRefreshCallback(_window, MyWindowBase::draw_callback); glfwMakeContextCurrent(_window); }
      // イベントループ /////////////////////////////////
    void loop() { while (glfwWindowShouldClose(_window) == GL_FALSE) { draw(); // イベント取得 glfwPollEvents(); } glfwTerminate(); }
    };
    

    main.cpp

    #include <memory>
    #include <iostream>
    
    #include<windows.h>
    #include "CglfwBase.hpp"
    
    #pragma comment(lib,"opengl32.lib")
    
    class MyWindow :public MyWindowBase {
    
      int width;
      int height;
    public:
      virtual void setting()override {
        glfwGetFramebufferSize(_window, &width, &height);
      }
    
    
      virtual void key(int key, int scancode, int action, int mods)override {
        // https://www.glfw.org/docs/latest/group__keys.html#ga99aacc875b6b27a072552631e13775c7
    
        // action ... GLFW_PRESS , GLFW_REPEAT , GLFW_RELEASE
    
        //特殊キー
        if (key == GLFW_KEY_UP && action == GLFW_PRESS) {
        }
        if (key == GLFW_KEY_DOWN && action == GLFW_PRESS) {
        }
        if (key == GLFW_KEY_LEFT && action == GLFW_PRESS) {
        }
        if (key == GLFW_KEY_RIGHT && action == GLFW_PRESS) {
        }
    
        if (key >= GLFW_KEY_A && key <= GLFW_KEY_Z) {
          if (action == GLFW_PRESS) {
            const char* key_name = glfwGetKeyName(key, 0);
            printf("key - %s\n", key_name);
          }
        }
      }
    
      virtual void mouse_button(int button, int action, int mods)override {
        double xpos, ypos;
        glfwGetCursorPos(_window, &xpos, &ypos);
    
        if (button == GLFW_MOUSE_BUTTON_LEFT && action == GLFW_PRESS) {
          printf("mouse L pressed\n");
        }
        if (button == GLFW_MOUSE_BUTTON_LEFT && action == GLFW_RELEASE) {
          printf("mouse L released\n");
        }
        if (button == GLFW_MOUSE_BUTTON_RIGHT && action == GLFW_PRESS) {
        }
        if (button == GLFW_MOUSE_BUTTON_RIGHT && action == GLFW_RELEASE) {
        }
        if (button == GLFW_MOUSE_BUTTON_MIDDLE && action == GLFW_PRESS) {
        }
    
        draw();
      }
    
      virtual void mouse_move(double xpos, double ypos)override {
        printf("mouse is moving\n");
        draw();
      }
      // マウスホイールを処理するコールバック関数
      virtual void mouse_wheel(double xoffset, double yoffset)override
      {
        if (yoffset < 0) {
        }
        if (yoffset > 0) {
        }
        draw();
    
      }
      virtual void framebuffer_size(int _width, int _height)override
      {
        width = _width;
        height = _height;
      }
    
      virtual void draw()override
      {
    
        /////////////////////
        // 描画
        glViewport(0, 0, width, height);
    
        glClearColor(0.3, 0.3, 0.3, 1);
        glClear(GL_COLOR_BUFFER_BIT);
    
        glEnable(GL_BLEND);
        glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
    
        float v = 0.7;
        glBegin(GL_TRIANGLES);
        glColor3f(0, 0, 1);
        glVertex3f(-v, -v, 0.0);
        glColor3f(1, 0, 1);
        glVertex3f(v, -v, 0.0);
        glColor3f(1, 1, 1);
        glVertex3f(v, v, 0.0);
        glEnd();
    
        //
        /////////////////////
    
        glFlush();
        glfwSwapBuffers(_window);
    
      }
    };
    
    
    int main()
    {
    
      MyWindow win;
    
      win.init(600,600,"glfw window");
      win.setting();
    
      win.loop();
    
    }