ぬの部屋(仮)
nu-no-he-ya
  • 1234567
    891011121314
    15161718192021
    22232425262728
    2930     
           
        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      
         12
    3456789
    10111213141516
    17181920212223
    2425262728  
           
      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
           
       1234
    567891011
    12131415161718
    19202122232425
    26272829   
           
    1234567
    891011121314
    15161718192021
    22232425262728
    293031    
           
        123
    45678910
    11121314151617
    18192021222324
    25262728293031
           
      12345
    6789101112
    13141516171819
    20212223242526
    27282930   
           
          1
    2345678
    9101112131415
    16171819202122
    23242526272829
    3031     
        123
    45678910
    11121314151617
    18192021222324
    252627282930 
           
     123456
    78910111213
    14151617181920
    21222324252627
    28293031   
           
         12
    3456789
    10111213141516
    17181920212223
    24252627282930
    31      
       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     
       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    
           
        123
    45678910
    11121314151617
    18192021222324
    25262728293031
           
      12345
    6789101112
    13141516171819
    20212223242526
    27282930   
           
          1
    2345678
    9101112131415
    16171819202122
    23242526272829
    3031     
        123
    45678910
    11121314151617
    18192021222324
    252627282930 
           
     123456
    78910111213
    14151617181920
    21222324252627
    28293031   
           
     123456
    78910111213
    14151617181920
    21222324252627
    28      
           
         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     
       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
           
  • AnacondaでPythonを使う(目的 Open3d)

    前置き

    普段はC++からOpen3d使っているのだがひょんなことからPythonから使わざるを得なくなった。色々詰まったので流れをメモしておきたい。

    なおAnacondaを使う。初めて使うので設定からやる。Anacondaは開発環境を切り替えて使うためのソフトである。つまり、

    ・このスクリプトはnumpy 0.5+open3d 0.3以外では動かない。

    ・別のはnumpy 1.19+open3d 1.12以外では動かない。

    ・でもどちらも頻繁に使う

    みたいな状況で簡単に環境を切り替えるのに便利だという事(らしい)。

    環境

    Windows 10

    Anaconda

    インストール

    https://www.anaconda.com/products/individualから、一番下の方にある「Anaconda Installers」の64-Bit Graphical Installer (457 MB)あたりをクリックしてダウンロード・インストールする

    参考:https://www.python.jp/install/anaconda/windows/install.html

    仮想環境構築

    まず仮想環境を作り、その環境に必要なライブラリをインストールする。

    AnacondaをインストールするとAnaconda PowerShell Prompt(Anaconda 3)というのがスタートメニューに追加されるので起動する。

    あるいはAnaconda Promptの方でもいい。lsとかが使えなくてやや面倒だが、activateを何もしなくても使えるメリットがある。

    1.環境作成

    conda create -n 環境名
    

    例(環境名devo3dを作成):

    conda create -n devo3d
    

    環境が作成できたか確認:

    conda info -e
    

    作成した環境(devo3d)へ移行:

    Anaconda Promptを使っているなら以下のactivateで環境を移行できるが、PowerShellを使っている場合はactivateがそのままでは動かないのでもう一手間必要になる

    activate devo3d
    

    ※PowerShellを使っている場合はactivateの前に以下を実行。ただし何をやっているのか著者は理解していないのでちゃんと調べてからやった方が良い。

    conda install -n root -c pscondaenvs pscondaenvs

    参考:

    https://qiita.com/nabehide/items/097553ccd51543ee31fb

    (2022年12月 情報更新記事)

    以下の内容は古い上にcondaとpipを併用する悪い方法なのでインストールは別記事を参照

    condaでopen3dをインストールする(改)

    以下、古い記事


    2.Open3d インストール

    open3dのPythonモジュールをインストールする。

    conda install -c open3d-admin open3d

    参考:http://www.open3d.org/docs/release/getting_started.html

    3.バージョン確認

    インストールされたバージョンによってはサンプルが全く動かないので、バージョンを確認する。pip list で-oオプションを付けると、バージョンアップの必要なライブラリの一覧だけを見られる。

    pip list -o

    明らかにバージョンが低い場合はアップデートする

    pip install -U open3d

    参考:https://qiita.com/HyunwookPark/items/242a8ceea656416b6da8

    4.稼働確認

    importだけ実行してみてエラーが出なければ(無言なら)成功している。

    python -c "import open3d"

    別ファイルのBlender Pythonスクリプトをimportして使い回す

    一つのファイルに全ての関数やクラスを入れてしまうとあまりに見通しが悪くなるのでファイルを分けたい。

    自分のスクリプトを入れるディレクトリの設定

    Edit → Preference → File Paths → Data → Scripts にディレクトリを設定する。注意点はディレクトリ名が\modules , \addons ... でなければいけない。

    ここではimportしたいので、\modulesディレクトリを作り、そのディレクトリの親ディレクトリを指定する。

    \modules\  ディレクトリの中のファイル名(拡張子除)がモジュール名になり、これをimportすれば中の関数を呼び出すことができる。

    外部にモジュールを置き、blenderスクリプトから呼び出す

    import test
    
    test.call()
    

    更新できるようにする(メインのファイル)

    このままだと、上記test.pyを更新する度にBlenderを再起動しなければならない。実行する度に再読み込みを行うには再読み込み命令をスクリプトに書く必要がある。importlib.reload( モジュール名 )を呼び出す。

    import test
    
    #############################
    ## リロード
    import importlib
    importlib.reload(test) #モジュール名を指定
    ##
    #############################
    
    test.call()
    

    更新できるようにする(関連するファイル)

    上記test.pyがさらに内部で別のモジュールをimportしていた場合、それらは更新されない。これを更新するためには、testモジュール内部で使用する外部モジュールをimportlib.reloadする必要がある。

    modules/test.py

    import neighbour # 別ファイル neighbour.py
    import importlib # reload用
    
    # blenderから呼ばれる
    def call():
    
      importlib.reload(neighbour) # neighbour.pyの再ロード
      neighbour.hello()
    

    modules/neighbour.py

    def hello():
      print("neighbour.py is updated")
    

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

    freeglutをカプセル化できる。glutSetWindowDataにthisポインタを渡し、コールバック関数内でglutGetWindowDataでthisを取得しそのメンバを呼び出す。

    #include <Windows.h>
    #include <gl/GL.h>
    #include <gl/freeglut.h>
    #include <cstdio>
    
    #pragma comment(lib,"opengl32.lib")
    #pragma comment(lib,"freeglut.lib")
    
    
    class CfreeglutWindow {
    
    
      // コールバック関数から呼ばれるメンバ関数群 //////////////////////////
      static void mouse_callback(int button, int state, int x, int y) {
        CfreeglutWindow* pwin = (CfreeglutWindow*)glutGetWindowData();
        pwin->mouse(button, state, x, y);
      }
      static void motion_callback(int x, int y) {
        CfreeglutWindow* pwin = (CfreeglutWindow*)glutGetWindowData();
        pwin->motion(x, y);
      }
      static void mouseWheel_callback(int button, int dir, int x, int y) {
        CfreeglutWindow* pwin = (CfreeglutWindow*)glutGetWindowData();
        pwin->mouse(button, dir, x, y);
      }
    
      static void disp_callback() {
        CfreeglutWindow* pwin = (CfreeglutWindow*)glutGetWindowData();
        pwin->disp();
      }
      static void key_callback(unsigned char key, int x, int y) {
        CfreeglutWindow* pwin = (CfreeglutWindow*)glutGetWindowData();
        pwin->key(key,x,y);
      }
      //普通のメンバ変数
      int width;
      int height;
      float xpos,ypos;
    public:
    
      CfreeglutWindow() {
        xpos = 0.0;
        ypos = 0.0;
      }
    
      void init(int argc, char** argv, const char* title) {
        glutInit(&argc, argv);
        glutInitWindowPosition(100, 50);
        glutInitWindowSize(400, 300);
        glutInitDisplayMode(GLUT_SINGLE | GLUT_RGBA);
    
        width = 400;
        height = 300;
    
        glutCreateWindow(title);
        glutDisplayFunc(disp_callback);
        glutMouseFunc(mouse_callback);
        glutMotionFunc(motion_callback);
        glutMouseWheelFunc(mouseWheel_callback);
        glutKeyboardFunc(key_callback);
    
        glutSetWindowData(this);
    
    
      }
      void loop() {
        glutMainLoop();
      }
    
    
      // 描画
      void disp(void) {
        glClearColor(0, 0, 0, 0);
        glClear(GL_COLOR_BUFFER_BIT);
        glViewport(0, 0, width, height);
    
        float v = 0.7;
    
        glPushMatrix();
        glLoadIdentity();
        glTranslatef(xpos, ypos, 0.0);
        glBegin(GL_TRIANGLES);
        glColor3d(1, 1, 1);
        glVertex2f(-v, -v);
        glColor3d(0, 1, 1);
        glVertex2f( v, -v);
        glColor3d(0, 0, 1);
        glVertex2f( v,  v);
        glEnd();
        glPopMatrix();
    
        glFlush();
        glutSwapBuffers();
      }
      // 普通キーの入力
      void key(unsigned char key, int x, int y) {
        if (key == 'a') {
          xpos -= 0.2;
        }
        if (key == 's') {
          xpos += 0.2;
        }
        glutPostRedisplay();
      }
    
    
      // クリック
      void mouse(int button, int state, int x, int y) {
    
        if (button == GLUT_LEFT_BUTTON && state == GLUT_DOWN) {
          ypos += 0.2;
        }
        if (button == GLUT_RIGHT_BUTTON && state == GLUT_DOWN) {
          ypos -= 0.2;
        }
        glutPostRedisplay();
    
      }
      //ドラッグ
      void motion(int x, int y){}
      // ホイール
      void mouseWheel(int button, int dir, int x, int y)
      {
        if (dir > 0){}
        else{}
        return;
      }
    };
    
    int main(int argc, char** argv)
    {
      CfreeglutWindow cfw;
    
      cfw.init(argc, argv, "freeglut");
    
      cfw.loop();
    
      return 0;
    }
    

    GLSLを試す (9) Geometry Shader(5)- 三角形一枚から三角形二枚

    前回のプログラム

    https://suzulang.com/glsl-9-geometry-shader-4-triangle-to-triangle

    の、C++側をそのままにして、ジオメトリシェーダを以下のものに置き換える。

    #version 460
    
    layout (triangles) in; //入力は三角形
    layout (triangle_strip) out; //出力も三角形
    
    layout (max_vertices = 4) out; //四点出力
    
    //geometry shaderへの入力は配列で受け取らなければいけない
    //ただし名前はvertex shader側のoutと一致させる
    in vec4 vertexColor[]; 
    
    // 出力は普通。名前はfragment shader側のinと一致させる
    out vec4 vColor;
    
    void main()
    {
    
    
        gl_Position = gl_in[0].gl_Position;
        vColor = vertexColor[0];
        EmitVertex();   
    
        gl_Position = gl_in[1].gl_Position;
        vColor = vertexColor[1]*0.2;
        EmitVertex();
    
        gl_Position = gl_in[2].gl_Position;
        vColor = vertexColor[2]*0.2;
        EmitVertex();
    
        //第四頂点
        gl_Position = vec4(-0.8,0.8,0,1);
        vColor = vec4(1,1,1,1);
        EmitVertex();
    
        EndPrimitive();
    }
    

    実行結果

    C++側では三頂点しか指定していないのに三角形が二枚出力されている。

    疑問

    GL_GEOMETRY_VERTICES_OUTはジオメトリシェーダから出力する可能性のある最大長点数を指定するはずだが、なぜか前回の

    glProgramParameteri(*ProgramID, GL_GEOMETRY_VERTICES_OUT, 3);

    をそのままにしておいても四つ目の頂点を出力できた。何か意味を取り違えているのだろうか。

    GLSLを試す (9) Geometry Shader(4)- 三角形一枚から三角形一枚

    前回までは頂点を入力にしていたが、三角形を入力する場合はGL_GEOMETRY_INPUT_TYPEにGL_TRIANGLESを指定、GL_GEOMETRY_OUTPUT_TYPEにはGL_TRIANGLE_STRIPを指定する。

    https://suzulang.com/glsl-9-geometry-shader-1

    C++側 ジオメトリシェーダの設定

    //! @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);
        glProgramParameteri(*ProgramID, GL_GEOMETRY_INPUT_TYPE, GL_TRIANGLES);//ジオメトリシェーダには三角形が入力される
        glProgramParameteri(*ProgramID, GL_GEOMETRY_OUTPUT_TYPE, GL_TRIANGLE_STRIP);//ジオメトリシェーダからは三角形が出力される
    
        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 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);
            glDrawArrays(GL_TRIANGLES, 0, 3);
    
        }
        glFlush();
    }
    

    ジオメトリシェーダ

    ジオメトリシェーダだけをこのように変更する

    #version 460
    
    layout (triangles) in; //入力は三角形
    layout (triangle_strip) out; //出力も三角形
    
    layout (max_vertices = 3) out;
    
    //geometry shaderへの入力は配列で受け取らなければいけない
    //ただし名前はvertex shader側のoutと一致させる
    in vec4 vertexColor[]; 
    
    // 出力は普通。名前はfragment shader側のinと一致させる
    out vec4 vColor;
    
    void main()
    {
    
    
        gl_Position = gl_in[0].gl_Position;
        vColor = vertexColor[0];
        EmitVertex();
    
        gl_Position = gl_in[1].gl_Position;
        vColor = vertexColor[1]*0.1;
        EmitVertex();
    
        gl_Position = gl_in[2].gl_Position;
        vColor = vertexColor[2]*0.1;
        EmitVertex();
    
        EndPrimitive();
    }
    

    GLSLを試す (9) Geometry Shader(3)- 頂点一つから三角形を生成

    C++側(シェーダの設定)

    前回のプログラムからプログラムのリンクの部分だけを以下のように書き換える。

    https://suzulang.com/glsl-9-geometry-shader-1

    //! @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);
        glProgramParameteri(*ProgramID, GL_GEOMETRY_OUTPUT_TYPE, GL_TRIANGLE_STRIP);
    
        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;
    }
    

    Geometry Shader

    #version 460
    
    layout (points) in; //入力は頂点単位
    layout (triangle_strip) out; //出力も頂点単位
    
    layout (max_vertices = 3) out;
    
    //geometry shaderへの入力は配列で受け取らなければいけない
    //ただし名前はvertex shader側のoutと一致させる
    in vec4 vertexColor[]; 
    
    // 出力は普通。名前はfragment shader側のinと一致させる
    out vec4 vColor;
    
    void main()
    {
    
        // 第一頂点
        gl_Position = gl_in[0].gl_Position;
        vColor = vertexColor[0];
        EmitVertex();   
    
        // 第二頂点
        gl_Position = gl_in[0].gl_Position;
        gl_Position.x += 0.2;
        vColor = vertexColor[0]*0.5;
        EmitVertex();
    
        // 第三頂点
        gl_Position = gl_in[0].gl_Position;
        gl_Position.x += 0.2;
        gl_Position.y += 0.2;
        vColor = vertexColor[0]*0.2;
        EmitVertex();
    
        EndPrimitive();
    }
    

    GLSLを試す (9) Geometry Shader(2)- 頂点を増やす

    前回のプログラム

    https://suzulang.com/glsl-9-geometry-shader-1

    C++側、vertex shader,fragment shaderはそのまま、geometry shaderだけを書き換える。

    #version 460
    
    layout (points) in; //入力は頂点単位元々
    layout (points) out; //出力も頂点単位
    
    layout (max_vertices = 3) out;
    
    //geometry shaderへの入力は配列で受け取らなければいけない
    //ただし名前はvertex shader側のoutと一致させる
    in vec4 vertexColor[]; 
    
    // 出力は普通。名前はfragment shader側のinと一致させる
    out vec4 vColor;
    
    void main()
    {
    
        // 元々入ってきた頂点
        gl_Position = gl_in[0].gl_Position;
        gl_PointSize=20.0;
        vColor = vertexColor[0];
        EmitVertex(); // 頂点をラスタライザへ送る
        
        // ジオメトリシェーダで生成する頂点
        gl_Position = gl_in[0].gl_Position; // 元々の頂点の座標を取得
        gl_Position.x += 0.2;               // 少し右へずらす
        gl_PointSize=10.0;                  // 頂点サイズも変える
        vColor = vertexColor[0];            // 色は同じものを使う
        EmitVertex();                       // 頂点をラスタライザへ送る
    
        EndPrimitive();
    }
    

    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();
    
    }