ぬの部屋(仮)
nu-no-he-ya
  •    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
           
  • Blenderで先頭の形が変わらない矢印を作る

    1.矢印の胴部分(名前 body)を作る

    *注意 originをオブジェクトの根元にする

    2.矢印の頭部分(名前 head)を作る

    *注意 originをオブジェクトの根元にする

    3.headをbodyの子にする

    [Shift]+右クリックで head,bodyの順に選択し、[Ctrl+P]→”Object”でheadをbodyの子に設定する

    4.headに「Limit Scale」Constraintを追加し以下のように設定する

    5.bodyの移動・回転・伸縮にheadが位置だけ追従する

    6.参考サイト

    https://blender.stackexchange.com/questions/88028/create-an-arrow-of-adjustable-length-but-constant-head-dimension

    C++のテンプレートメタ関数is_assignableの動作

    書籍 C++テンプレートテクニック 第2版 (επιστημη, 高橋 晶 著)

    P130、is_assignable関数の挙動。文章だけだとわかりづらいので図にしてみた。

    C++のテンプレートメタ関数has_iteratorの動作

    書籍 C++テンプレートテクニック 第2版 (επιστημη, 高橋 晶 著)

    P125、has_iterator関数の挙動(C++11版)。文章だけだとわかりづらいので図にしてみた。

    C++CLIでMicrosoft Word文書を開く

    前回は書き出す方をやったので、今回は読み込む方をやる。

    #include "pch.h"
    
    using namespace System;
    
    int main(array<::System::String ^> ^args)
    {
    
      Microsoft::Office::Interop::Word::Application^ 
        word = gcnew Microsoft::Office::Interop::Word::Application();
      word->Visible = false;
    
      Object^ oMissing = ::System::Reflection::Missing::Value;
      Object^ oTrue = true;
      Object^ oFalse = false;
    
      // Word文書を開く
      System::String^ fname = "C:\\test\\out.docx";
      Microsoft::Office::Interop::Word::Document^
      document = word->Documents->Open(fname,
        oMissing, oMissing, oMissing,
        oMissing, oMissing, oMissing,
        oMissing, oMissing, oMissing,
        oMissing, oMissing, oMissing,
        oMissing, oMissing, oMissing);
    
      for each(Microsoft::Office::Interop::Word::Paragraph^ par in document->Paragraphs)
      {
        Microsoft::Office::Interop::Word::Range^ ran = par->Range;
    
        System::String^ str = ran->Text;
    
        System::Console::WriteLine(str);
    
      }
    
      // 文書を閉じる
      ((Microsoft::Office::Interop::Word::_Document^)(document))->Close(oFalse, oMissing, oMissing);
      ((Microsoft::Office::Interop::Word::_Application^)word)->Quit(oMissing, oMissing, oMissing);
    
      return 0;
    }
    

    C++CLIでMicrosoft Word文書を作成する

    参考文献

    https://gist.github.com/meki/7e8b462a8d36919d778f

    https://www.c-sharpcorner.com/article/word-automation-using-C-Sharp/

    ざっくり言うと上記なのだけれど、C++CLIの場合はコピペでは到底動かない。

    C++CLIで動かす場合の注意

    引数

    引数は全てObject^型でなければならない

      Object^ oMissing = ::System::Reflection::Missing::Value;
    
      Object^ oTrue = true;
      Object^ oFalse = false;
    
      Object^ filename = ::System::IO::Directory::GetCurrentDirectory() + "\\out.docx";
    

    引数は一切省略できない

    https://gist.github.com/meki/7e8b462a8d36919d778f

    ではSaveAs2の引数はファイル名のみだが、C++CLIでは以下のように全て指定する。

      Object^ filename = ::System::IO::Directory::GetCurrentDirectory() + "\\out.docx";
      document->SaveAs2(
        filename,
        oMissing, oMissing, oMissing, oMissing,
        oMissing, oMissing, oMissing, oMissing,
        oMissing, oMissing, oMissing, oMissing, 
        oMissing, oMissing, oMissing, oMissing
      );
    

    名前空間と型

      using namespace Microsoft::Office::Interop::Word;
    

    これはやらない方がいい。

    Microsoft::Office::Interop::Word::Systemというのがあり、using namespaceをするとそれと衝突してコンパイルが通らなくなる。

    参照の追加

    ソースコード

    #include "pch.h"
    
    using namespace System;
    
    /// <summary>
    /// 文書の末尾位置を取得する.
    /// </summary>
    /// <returns></returns>
    int getLastPosition(Microsoft::Office::Interop::Word::Document^ document)
    {
      return document->Content->End - 1;
    }
    
    /// <summary>
    /// 文書の末尾にテキストを追加する.
    /// </summary>
    void addTextSample(
      Microsoft::Office::Interop::Word::Document^ document, 
      Microsoft::Office::Interop::Word::WdColorIndex color, 
      System::String^ text)
    {
      
      Object^ before = getLastPosition(document);
      Object^ dce = document->Content->End - 1;
    
      Microsoft::Office::Interop::Word::Range^ rng = document->Range(dce, dce);
      rng->Text += text;
    
      Object^ after = getLastPosition(document);
    
      document->Range(before, after)->Font->ColorIndex = color;
    }
    
    
    int main(array<::System::String ^> ^args)
    {
    
      Microsoft::Office::Interop::Word::Application^ 
        word = gcnew Microsoft::Office::Interop::Word::Application();
    
      word->Visible = false;
    
      Object^ oMissing = ::System::Reflection::Missing::Value;
    
      Microsoft::Office::Interop::Word::Document^ 
        document = word->Documents->Add(oMissing, oMissing, oMissing, oMissing);
    
      Object^ oTrue = true;
      Object^ oFalse = false;
    
      ////////////////////////////////////////
      // テキストを追加
      addTextSample(document, Microsoft::Office::Interop::Word::WdColorIndex::wdGreen, "Hello, ");
      addTextSample(document, Microsoft::Office::Interop::Word::WdColorIndex::wdRed, "World");
      ////////////////////////////////////////
    
    
    
      // 名前を付けて保存
      Object^ filename = ::System::IO::Directory::GetCurrentDirectory() + "\\out.docx";
      document->SaveAs2(
        filename,
        oMissing, oMissing, oMissing, oMissing,
        oMissing, oMissing, oMissing, oMissing,
        oMissing, oMissing, oMissing, oMissing, 
        oMissing, oMissing, oMissing, oMissing
      );
    
      // 文書を閉じる
      ((Microsoft::Office::Interop::Word::_Document^)(document))->Close(oFalse, oMissing, oMissing);
      ((Microsoft::Office::Interop::Word::_Application^)word)->Quit(oMissing, oMissing, oMissing);
    
        return 0;
    }
    

    実行結果

    Blender で碁盤ぽいものの上に碁石ぽいものを配置する

    結果画像

    ざっくり言うとこのような事をしたい

    手順

    画像の入手

    Pixabayあたりから画像を持ってくる

    Image by Gordon Johnson from Pixabay

    頂点の準備

    1.BlenderでGridを追加する

    [Shift+A]→[Mesh]→GridでGridを追加し、分割数を50×50等少し大きめにする。

    2.画像の読み込み

    画像をモディファイアで使うため、冒頭の猫の画像を読み込んでおく

    (1)
    (3)

    (2)

    画像からウェイトペイントを作成

    3.GridをUV展開

    場合によってはUVマップを[S]で少しだけ広げておくといい

    4.BoneとVertexGroupを追加

    [Shift+A]→[Armature]→Single Boneを追加

    [Shift]+右クリック で、Grid→Boneの順番に選択し、[Ctrl+P]→[With Empty Groups]で親子関係を作る

    この作業で、”Bone”という名前の、空のVertex Groupができる。

    5.Vertex Groupに画像の値を反映したWeightを作成

    GridにVertex Weight Editモディファイアを追加

    Vertex Weight Edit を以下のように変更する

    Weight Paintモードへ行き、猫の形にPaintされていることを確認し、Vertex Weight EditモディファイアをApplyする

    ウェイトペイントされた頂点を抜き出す

    6.Pose Modeで頂点の選別

    一度オブジェクトモードへ戻り、Armatureを選択して[Pose Mode]へ移行する。

    ArmatureをZ方向に持ち上げると、頂点が猫の形に持ち上がる。

    猫を適度に持ち上げたら、Gridを選択し、モディファイアへ行き、ArmatureをApplyする。

    ※作業が終わったら、Armatureは[X]で削除して良い

    7.不要な頂点の削除

    テンキーの[5][1]でXZ平面の平行投影に切り替え、Editモードで[B]の範囲選択などを使っていらない点を削除する。

    また、カメラの向きを変えて[C]等を使ってRepeatしてしまったテクスチャの部分を削除する

    Edit モードで必要な点を全て選択し、TransformでZをGlobal座標系で0にする

    8.面と辺を削除

    Editモードで[X] → Only Edges & Faces で面を全て消す

    碁石の配置

    9.碁石の作成

    (必要なら)レイヤーを変えて、[Shift+A]→[Mesh]→UV Shpere で球を追加し、[S][Z]でつぶして碁石っぽいものを作る。

    10.Duplication

    [Shift]キーを押しながら、
    ① 碁石
    ② 猫の頂点群
    の順に右ボタンクリックで選択し
    [Ctrl + P] → Object で 猫の腸点群(Grid)を親、碁石(Sphere)を子に設定する

    続いて、猫(Grid)を選択し、Object→Duplicationで[Verts]を選択する

    碁石が大きすぎるので、[S]で縮める

    このままだと各オブジェクトは虚像なので、点群(Grid)を選択した状態で、

    [Object]→[Apply]→Make Duplicates Real

    を選択し、実体化する。

    後書き

    碁盤はめんどくさいので省略

    C++ 可変長引数テンプレートクラスを試す(2)

    前回の続き。

    テンプレートメタプログラミングは何をやっているかさっぱりわからないので少しでも図解をしておきたい。

    C++ 可変長引数テンプレートクラスを試す(1)

    可変長引数テンプレートクラスのテスト。以下のテンプレートクラスは、

    第一テンプレート引数で指定した型の派生型を

    第二以降テンプレート引数から探して返す。

    ソースコード

    #include <iostream>
     
    ///////////////////////////////////////////////////////// ///////////////////////////////////////////////////////// // テスト用のクラス一覧 class AAA { public: void print() { std::cout << "AAA" << std::endl;} }; class BBB { public: void print() { std::cout << "BBB" << std::endl;} }; class CCC { public: void print() { std::cout << "CCC" << std::endl;} }; class AAADerived :public AAA { public: void print() { std::cout << "AAADerived" << std::endl; } }; class CCCDerived :public CCC { public: void print() { std::cout << "CCCDerived" << std::endl; } }; class DUMMY { public: void print() { std::cout << "指定されていません" << std::endl; } };
    ///////////////////////////////////////////////////////// ///////////////////////////////////////////////////////// // クラス選択クラスの本体 template<class Base,class Head, class... Args> struct Search { using type_t = typename std::conditional < std::is_base_of<Base, Head>::value, //判断 Head, typename Search<Base, Args...>::type_t>::type; };
     
    // クラス選択クラスの本体(特殊化)
    
    template<class Base, class Head> struct Search<Base,Head> { using type_t = typename std::conditional < std::is_base_of<Base, Head>::value, //判断 Head, DUMMY>::type; }; ///////////////////////////////////////////////////////// ///////////////////////////////////////////////////////// // クラス選択クラス // 第一テンプレート引数に指定したクラスから派生したクラスを、 // 第二以降テンプレート引数から選択する template<class Base, class... Args> struct TypeDerived { using type_t = typename Search<Base,Args...>::type_t; };
     
    //////////////////////////////////////////////////////// //////////////////////////////////////////////////////// // 動作確認 int main() { using T = TypeDerived< AAA,//このクラスの派生クラスを探す BBB, CCCDerived, AAADerived >::type_t; T obj; obj.print(); int i; std::cin >> i; }

    実行結果

    AAADerived

    用途

    普通の感覚だとこんなもんどこに使い道があるのかわからないので使用例を置いておくと、以下のABCクラスでは、AAA,BBB,CCC(及びその派生クラス)をどんな順番で指定してもちゃんと動作する。普通テンプレートに指定する型の順番は(当たり前だが)決まっている。だから何だといわれると困るのでもう一つ、クラス指定が足りなかった場合も、自動的にDUMMYが指定されるのでエラーにならない。

    template<class... T>
    class ABC {
    public:
      typename TypeDerived<AAA, T...>::type_t a;
      typename TypeDerived<BBB, T...>::type_t b;
      typename TypeDerived<CCC, T...>::type_t c;
    
    };
    
    int main()
    {
    
      std::cout << "obj1 ------" << std::endl;
      ABC<AAA, CCCDerived,BBB> obj1;
      obj1.a.print();
      obj1.b.print();
      obj1.c.print();
      std::cout << std::endl;
    
      std::cout << "obj2 ------" << std::endl;
      ABC<BBB,  CCC, AAADerived> obj2;
      obj2.a.print();
      obj2.b.print();
      obj2.c.print();
      std::cout << std::endl;
    
      std::cout << "obj3 ------" << std::endl;
      ABC<AAADerived, BBB> obj3;
      obj3.a.print();
      obj3.b.print();
      obj3.c.print();
      std::cout << std::endl;
    
      int i;
      std::cin >> i;
    
    }
    

    結果

    obj1 ——
    AAA
    BBB
    CCCDerived

    obj2 ——
    AAADerived
    BBB
    CCC

    obj3 ——
    AAADerived
    BBB
    指定されていません

    glslとFrameBufferでオブジェクトのIDをレンダリング

    要点

    • R32UIのテクスチャをフレームバッファに関連づける
    • 頂点ごとに用意したIDバッファを頂点バッファとともに有効にする
      (この時glVertexAttribIPointerを使う)
    • フラグメントシェーダで、レンダリング時に色ではなくIDをテクスチャに書き込む
    • glGetTexImageでテクスチャの内容を取得する。この時上下反転をする。

    ソースコード(C++)

    #pragma comment(lib,"glew32.lib")
    
    #include <gl/glew.h>
    #include <GL/glut.h>
    
    #include <vector>
    
    #include <vector>
    #include <sstream>
    #include <fstream>
    #include <algorithm>
    
    
    //////////////////////////////////////////// //フレームバッファへの描画サイズ int width; int height; //フレームバッファ関連のバッファID GLuint ID_fbo; GLuint ID_texture_output; GLuint ID_depth; //////////////////////////////////////////// // 二次元の三角形を四個 GLfloat vtx[4 * 3 * 2]; // 各頂点に設定するID GLuint vID[4 * 3]; //描画するオブジェクト用のバッファのID GLuint ID_Triangles; GLuint ID_Triangle_Index; //////////////////////////////////////////// // シェーダのプログラムID GLuint ID_program_disp; GLuint ID_program_idrender;

    /////////////////////////////////////////// //テクスチャ(ID_texture_output)に書き込んだデータを取り出す領域を用意 std::vector<GLuint> texdata;
    void prepare_shaders(GLuint* programID,const char* fn_vertex, const char* fn_fragment); void prepare_databuffer(); void prepare_framebuffer(); void display(void); //マウスでクリックした位置のオブジェクトIDをコンソールに出力 void mouse(int button, int state, int x, int y) { if (button != GLUT_LEFT_BUTTON || state != GLUT_DOWN) return; display(); unsigned int id = texdata[width * y + x]; printf("---- %d\n", id); } //ウィンドウサイズ固定用 void keepwindowsize(int w, int h) { glutReshapeWindow(width, height); } /////////////////////////////////////////////////// // エントリポイント int main(int argc, char* argv[]) { glutInit(&argc, argv); glutInitDisplayMode(GLUT_RGBA); glutCreateWindow(argv[0]); glutDisplayFunc(display); glutMouseFunc(mouse); //フレームバッファで書き込む先の画像サイズを500x500にする width = 500; height = 500; //IDの書き出し先 texdata.resize(width * height, 0); //ウィンドウサイズをフレームバッファの描画サイズと同じにする glutInitWindowSize(width, height); //ウィンドウサイズは固定にしておきたい glutReshapeFunc(keepwindowsize); // glewの初期化 glewInit(); // シェーダ準備 prepare_shaders(&ID_program_disp,"vertex_disp.shader","fragment_disp.shader"); prepare_shaders(&ID_program_idrender,"vertex_id.shader", "fragment_id.shader"); //フレームバッファ準備 prepare_framebuffer(); //データ準備 prepare_databuffer(); glutMainLoop(); return 0; }
     
    void display(void) { {
    //以後の描画はフレームバッファに対して行われる glBindFramebuffer(GL_FRAMEBUFFER, ID_fbo); glUseProgram(ID_program_idrender); glClearColor(0, 0, 0, 1); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); glMatrixMode(GL_PROJECTION); glLoadIdentity(); glMatrixMode(GL_MODELVIEW); glLoadIdentity(); glViewport(0, 0, width, height); glLoadIdentity(); // 頂点バッファを有効化 glEnableVertexAttribArray(0); glBindBuffer(GL_ARRAY_BUFFER, ID_Triangles); glVertexAttribPointer( 0, // シェーダ内のlayoutとあわせる 2, // 1要素の要素数(x,y)で2要素 GL_FLOAT, // タイプ GL_FALSE, // 正規化しない(データが整数型の時) 0, // ストライド (void*)0 // 配列バッファオフセット ); // IDバッファを有効化 glEnableVertexAttribArray(1); glBindBuffer(GL_ARRAY_BUFFER, ID_Triangle_Index); glVertexAttribIPointer(//バッファが整数の時はglVertexAttribIPointerを使う 1, // シェーダ内のlayoutとあわせる 1, // 1要素の要素数 GLuintが一つで1要素 GL_UNSIGNED_INT, // タイプ 0, // ストライド (void*)0 // 配列バッファオフセット ); glDrawArrays(GL_TRIANGLES, 0, 12); glDisableVertexAttribArray(1); glDisableVertexAttribArray(0); /////////////////////////////////////////
    //テクスチャのデータを取り出す

      glBindTexture(GL_TEXTURE_2D, ID_texture_output); glGetTexImage(GL_TEXTURE_2D, 0, GL_RED_INTEGER, GL_UNSIGNED_INT, &texdata[0]);
        //texdataはデータのIDが入ったwidht*height個のGLuint型配列
    //上下反転 座標系がウィンドウズとOpenGLで上下逆になっている for (int ya = 0; ya < height / 2; ya++) { int yb = height - ya - 1; if (ya == yb)break; for (size_t x = 0; x < width; x++) { int pa = ya * width + x; int pb = yb * width + x; std::swap(texdata[pa], texdata[pb]); } } glFlush();
    } {
    //ここから先は画面に出力する glBindFramebuffer(GL_FRAMEBUFFER, 0); glUseProgram(ID_program_disp); glClearColor(0, 0, 0, 1); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); glMatrixMode(GL_PROJECTION); glLoadIdentity(); glMatrixMode(GL_MODELVIEW); glLoadIdentity(); glViewport(0, 0, width, height);//ウィンドウサイズをこれに固定している // 頂点バッファ glEnableVertexAttribArray(0); glBindBuffer(GL_ARRAY_BUFFER, ID_Triangles); glVertexAttribPointer( 0, // シェーダ内のlayoutとあわせる 2, // 1要素の要素数(x,y)で2要素 GL_FLOAT, // タイプ GL_FALSE, // 正規化しない(データが整数型の時) 0, // ストライド (void*)0 // 配列バッファオフセット ); glDrawArrays(GL_TRIANGLES, 0, 12); glDisableVertexAttribArray(0); glFlush();
    } }
    void prepare_databuffer() {
    //////////////////////////////////////////////////////// // 頂点作成 { int ofs_obj = 3 * 2;//一つのオブジェクトのサイズは二次元3頂点 int ofs_vtx = 2;//一つの頂点のサイズは二次元 //三角形1 vtx[(0 * ofs_obj) + (0 * ofs_vtx) + (0)] = 0.0; //頂点1 vtx[(0 * ofs_obj) + (0 * ofs_vtx) + (1)] = 1.0; vtx[(0 * ofs_obj) + (1 * ofs_vtx) + (0)] = 1.0; //頂点2 vtx[(0 * ofs_obj) + (1 * ofs_vtx) + (1)] = -1.0; vtx[(0 * ofs_obj) + (2 * ofs_vtx) + (0)] = -1.0; //頂点3 vtx[(0 * ofs_obj) + (2 * ofs_vtx) + (1)] = -1.0; //三角形2 vtx[(1 * ofs_obj) + (0 * ofs_vtx) + (0)] = 0.0; //頂点1 vtx[(1 * ofs_obj) + (0 * ofs_vtx) + (1)] = 0.7; vtx[(1 * ofs_obj) + (1 * ofs_vtx) + (0)] = 0.7; //頂点2 vtx[(1 * ofs_obj) + (1 * ofs_vtx) + (1)] = -0.7; vtx[(1 * ofs_obj) + (2 * ofs_vtx) + (0)] = -0.7; //頂点3 vtx[(1 * ofs_obj) + (2 * ofs_vtx) + (1)] = -0.7; //三角形3 vtx[(2 * ofs_obj) + (0 * ofs_vtx) + (0)] = 0.0; //頂点1 vtx[(2 * ofs_obj) + (0 * ofs_vtx) + (1)] = 0.4; vtx[(2 * ofs_obj) + (1 * ofs_vtx) + (0)] = 0.4; //頂点2 vtx[(2 * ofs_obj) + (1 * ofs_vtx) + (1)] = -0.4; vtx[(2 * ofs_obj) + (2 * ofs_vtx) + (0)] = -0.4; //頂点3 vtx[(2 * ofs_obj) + (2 * ofs_vtx) + (1)] = -0.4; //三角形4 vtx[(3 * ofs_obj) + (0 * ofs_vtx) + (0)] = 0.0; //頂点1 vtx[(3 * ofs_obj) + (0 * ofs_vtx) + (1)] = 0.2; vtx[(3 * ofs_obj) + (1 * ofs_vtx) + (0)] = 0.2; //頂点2 vtx[(3 * ofs_obj) + (1 * ofs_vtx) + (1)] = -0.2; vtx[(3 * ofs_obj) + (2 * ofs_vtx) + (0)] = -0.2; //頂点3 vtx[(3 * ofs_obj) + (2 * ofs_vtx) + (1)] = -0.2;
     }
      ////////////////////////////////////////////////////////
    
    // 頂点ID作成 { int ofs_obj = 3;//一つのオブジェクトのサイズは3頂点×1番号 //三角形1 vID[(0 * ofs_obj) + 0 ] = 1; //頂点1 vID[(0 * ofs_obj) + 1 ] = 1; //頂点2 vID[(0 * ofs_obj) + 2 ] = 1; //頂点3 //三角形2 vID[(1 * ofs_obj) + 0 ] = 2; //頂点1 vID[(1 * ofs_obj) + 1 ] = 2; //頂点2 vID[(1 * ofs_obj) + 2 ] = 2; //頂点3 //三角形3 vID[(2 * ofs_obj) + 0 ] = 3; //頂点1 vID[(2 * ofs_obj) + 1 ] = 3; //頂点2 vID[(2 * ofs_obj) + 2 ] = 3; //頂点3 //三角形4 vID[(3 * ofs_obj) + 0 ] = 4; //頂点1 vID[(3 * ofs_obj) + 1 ] = 4; //頂点2 vID[(3 * ofs_obj) + 2 ] = 4; //頂点3 }
    //////////////////////////////////////////////////////// //バッファ作成 glGenBuffers(1, &ID_Triangles); glBindBuffer(GL_ARRAY_BUFFER, ID_Triangles); glBufferData( GL_ARRAY_BUFFER, 4 * 3 * 2 * sizeof(GLfloat), vtx, GL_STATIC_DRAW); glGenBuffers(1, &ID_Triangle_Index); glBindBuffer(GL_ARRAY_BUFFER, ID_Triangle_Index); glBufferData( GL_ARRAY_BUFFER, 4 * 3 * sizeof(GLuint), vID, GL_STATIC_DRAW); }
    void prepare_framebuffer() { ////////////////////////////////////////////////////// //フレームバッファの作成 // 削除はglDeleteFrameBuffersを使用する glGenFramebuffers(1, &ID_fbo); glBindFramebuffer(GL_FRAMEBUFFER, ID_fbo); ////////////////////////////////////////////////////// { //テクスチャバッファの作成 (ID記録用) // 削除はglDeleteTextures glGenTextures(1, &ID_texture_output); glBindTexture(GL_TEXTURE_2D, ID_texture_output); //テクスチャを転送する。 //ただし最後がnullptrなので実質転送しない glTexImage2D(GL_TEXTURE_2D, 0, GL_R32UI, width, height, 0, GL_RED_INTEGER, GL_UNSIGNED_INT, nullptr); //テクスチャのフィルタリングの設定 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); } ////////////////////////////////////////////////////// // デプスバッファの作成 // レンダーバッファの一種なので、削除は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); ////////////////////////////////////////////////////// //描画先の設定 // 「GL_COLOR_ATTACHMENT0」 が ID_texture_outputのテクスチャを表すようにする glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, ID_texture_output, 0); /////////////////////////////////////////////////// //描画バッファのリストをセットする //この作業によってフラグメントシェーダの(layout=0) out の出力先がテクスチャになる glBindFramebuffer(GL_FRAMEBUFFER, ID_fbo); GLenum DrawBuffers[1] = { GL_COLOR_ATTACHMENT0}; glDrawBuffers(1, DrawBuffers); // "1"はDrawBuffersのサイズです。 /////////////////////////////////////////////////// // フレームバッファのエラーチェック。 if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) return;/*エラーデータ*/ }
    //シェーダのコンパイルとリンクを行う関数 void prepare_shaders(GLuint* programID,const char* fn_vertex, const char* fn_fragment){ GLint Result = GL_FALSE; int InfoLogLength; ////////////////////////////////////////////// ////////////////////////////////////////////// //頂点シェーダ // シェーダを作ります // (作るといっても宣言みたいなもの) GLuint VertexShaderID = glCreateShader(GL_VERTEX_SHADER); //////////////////////////////////////////// // ファイルから頂点シェーダを読み込みます。 std::string VertexShaderCode; std::ifstream VertexShaderStream(fn_vertex, std::ios::in); if (VertexShaderStream.is_open()) { std::stringstream sstr; sstr << VertexShaderStream.rdbuf(); VertexShaderCode = sstr.str(); VertexShaderStream.close(); } //////////////////////////////////////////// // 頂点シェーダをコンパイルします。 printf("Compiling shader : %s\n", fn_vertex); char const* VertexSourcePointer = VertexShaderCode.c_str(); glShaderSource(VertexShaderID, 1, &VertexSourcePointer, NULL); glCompileShader(VertexShaderID); //////////////////////////////////////////// // エラーチェック // 頂点シェーダをチェックします。 glGetShaderiv(VertexShaderID, GL_COMPILE_STATUS, &Result); glGetShaderiv(VertexShaderID, GL_INFO_LOG_LENGTH, &InfoLogLength); if (Result == FALSE) { std::vector<char> VertexShaderErrorMessage(InfoLogLength); glGetShaderInfoLog(VertexShaderID, InfoLogLength, NULL, &VertexShaderErrorMessage[0]); fprintf(stdout, "%s\n", &VertexShaderErrorMessage[0]); } ////////////////////////////////////////////// ////////////////////////////////////////////// //フラグメントシェーダ // シェーダを作ります。 GLuint FragmentShaderID = glCreateShader(GL_FRAGMENT_SHADER); ///////////////////////////////////////////// // ファイルからフラグメントシェーダを読み込みます。 std::string FragmentShaderCode; std::ifstream FragmentShaderStream(fn_fragment, std::ios::in); if (FragmentShaderStream.is_open()) { std::stringstream sstr; sstr << FragmentShaderStream.rdbuf(); FragmentShaderCode = sstr.str(); FragmentShaderStream.close(); } ///////////////////////////////////////////// // フラグメントシェーダをコンパイルします。 printf("Compiling shader : %s\n", fn_fragment); char const* FragmentSourcePointer = FragmentShaderCode.c_str(); glShaderSource(FragmentShaderID, 1, &FragmentSourcePointer, NULL); glCompileShader(FragmentShaderID); ///////////////////////////////////////////// // フラグメントシェーダをチェックします。 glGetShaderiv(FragmentShaderID, GL_COMPILE_STATUS, &Result); glGetShaderiv(FragmentShaderID, GL_INFO_LOG_LENGTH, &InfoLogLength); if (Result == GL_FALSE) { std::vector<char> FragmentShaderErrorMessage(InfoLogLength); glGetShaderInfoLog(FragmentShaderID, InfoLogLength, NULL, &FragmentShaderErrorMessage[0]); fprintf(stdout, "%s\n", &FragmentShaderErrorMessage[0]); } //////////////////////////////////////////// //////////////////////////////////////////// // プログラムのリンク //////////////////////////////////////// // プログラムをリンクします。 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]); }

    シェーダ (ID書き込み用)

    頂点シェーダ

    #version 460 core
    
    layout (location = 0) in vec3 aPos;
    layout (location = 1) in uint aID;
    
    out flat uint index;
    
    uniform mat4 gl_ModelViewMatrix;
    uniform mat4 gl_ProjectionMatrix;
    
    void main()
    {
      gl_Position = gl_ProjectionMatrix * gl_ModelViewMatrix * vec4(aPos, 1.0);
      index = aID;
      
    }
    

    フラグメントシェーダ

    #version 460 core
    
    layout(location = 0) out uint UIntIndex;
    
    in flat uint index;
    
    void main()
    {
      UIntIndex = index;
    
    } 
    

    シェーダ (表示用)

    頂点シェーダ

    #version 460 core
    
    layout (location = 0) in vec2 aPos;
    
    uniform mat4 gl_ModelViewMatrix;
    uniform mat4 gl_ProjectionMatrix;
    
    out float c;
    
    void main()
    {
      gl_Position = gl_ProjectionMatrix * gl_ModelViewMatrix * vec4(aPos,0.0, 1.0);
      c = abs(aPos.y);
    }
    

    フラグメントシェーダ

    #version 460 core
    
    out vec4 FragColor;
      
    in float c;
    
    void main()
    {
      FragColor = vec4(c,c,c,1);
    } 
    

    実行結果

    マウスで三角形をクリックすると、
    コンソールに三角形のIDが表示される

    OpenGL+フレームバッファ

    流れぶった切ってフレームバッファやります。

    ソースコード

    #pragma warning(disable:4996)
    
    #pragma comment(lib,"glew32.lib")
    
    #include <gl/glew.h>
    #include <GL/glut.h>
    
    #include <vector>
    
    
    void init(void);
    void display(void);
    
    //フレームバッファへの描画サイズ
    int width;
    int height;
    
    GLuint ID_fbo;
    GLuint ID_texture_output;
    GLuint ID_depth;
    
    
    void prepare_framebuffer() {
    
    ////////////////////////////////////////////////////// //フレームバッファの作成 // 削除はglDeleteFrameBuffersを使用する glGenFramebuffers(1, &ID_fbo); glBindFramebuffer(GL_FRAMEBUFFER, ID_fbo);
    ////////////////////////////////////////////////////// //テクスチャバッファの作成 // 削除はglDeleteTextures glGenTextures(1, &ID_texture_output); glBindTexture(GL_TEXTURE_2D, ID_texture_output); //テクスチャを転送する。 //ただし最後が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);
    ////////////////////////////////////////////////////// // デプスバッファの作成 // レンダーバッファの一種なので、削除は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);
    ////////////////////////////////////////////////////// //描画先の設定 // 「GL_COLOR_ATTACHMENT0」 が ID_texture_outputのテクスチャを表すようにする glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, ID_texture_output, 0);
    /////////////////////////////////////////////////// // フレームバッファのエラーチェック。 if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) return;/*エラーデータ*/ } /////////////////////////////////////////////////// // エントリポイント int main(int argc, char* argv[]) { glutInit(&argc, argv); glutInitDisplayMode(GLUT_RGBA); glutCreateWindow(argv[0]); glutDisplayFunc(display); glewInit(); // glewの初期化 //フレームバッファで書き込む先の画像サイズを100x100にする width = 100; height = 100; //フレームバッファ準備 prepare_framebuffer(); glutMainLoop(); return 0; } //ファイル書き込み用の関数 void pnmP3_Write(const char* const fname, const int vmax, const int width, const int height, const unsigned char* const p); void display(void)
    {
    //以後の描画はフレームバッファに対して行われる glBindFramebuffer(GL_FRAMEBUFFER, ID_fbo); glClearColor(1, 0, 0, 1); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); glViewport(0, 0, width, height); glLoadIdentity(); //普通の描画処理 glBegin(GL_QUADS); glColor3d(1, 0, 0); glVertex2d(-0.7, -0.7); glColor3d(0, 1, 0); glVertex2d( 0.7, -0.7); glColor3d(0, 0, 1); glVertex2d( 0.7, 0.7); glColor3d(1, 1, 1); glVertex2d(-0.7, 0.7); glEnd(); //テクスチャ(ID_texture_output)に書き込んだデータを取り出す領域を用意 std::vector<GLubyte> texdata; texdata.resize(width * height * 3); //テクスチャのデータを取り出す glBindTexture(GL_TEXTURE_2D, ID_texture_output); glGetTexImage(GL_TEXTURE_2D, 0, GL_RGB, GL_UNSIGNED_BYTE, &texdata[0] ); glFlush(); //ファイル書き出し pnmP3_Write("C:\\test\\ret.ppm", 255, width, height, &texdata[0]);
    //ここから先は画面に出力する glBindFramebuffer(GL_FRAMEBUFFER, 0); glClearColor(0, 0, 0, 1); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); glViewport(0, 0, width, height);//ここのwidthとheightは別にこの値でなくてもいい。ウィンドウサイズに合わせるのが普通。 glLineWidth(5); glBegin(GL_LINES); glColor3d(1, 0, 0); glVertex2d(-0.7, -0.7); glColor3d(0, 0, 1); glVertex2d(0.7, 0.7); glColor3d(0, 1, 0); glVertex2d(0.7, -0.7); glColor3d(1, 1, 1); glVertex2d(-0.7, 0.7); glEnd(); glFlush();
    } //! @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 char* const fname, const int vmax, const int width, const int height, const unsigned char* const p) { // PPM ASCII FILE* fp = fopen(fname, "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); }

    出力結果

    C:\test\ret.ppm
    画面表示

    注意

    このプログラムは描画が走るたびにC:\test\ret.ppmを出力します。あまり長時間走らせていいことのあるプログラムではありません。出力が確認出来たらすぐに画面を閉じてください。