https://gist.github.com/meki/7e8b462a8d36919d778f
https://www.c-sharpcorner.com/article/word-automation-using-C-Sharp/
ざっくり言うと上記なのだけれど、C++CLIの場合はコピペでは到底動かない。
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; }
ざっくり言うとこのような事をしたい
Pixabayあたりから画像を持ってくる

Image by Gordon Johnson from Pixabay
[Shift+A]→[Mesh]→GridでGridを追加し、分割数を50x50等少し大きめにする。
画像をモディファイアで使うため、冒頭の猫の画像を読み込んでおく

場合によってはUVマップを[S]で少しだけ広げておくといい
[Shift+A]→[Armature]→Single Boneを追加
[Shift]+右クリック で、Grid→Boneの順番に選択し、[Ctrl+P]→[With Empty Groups]で親子関係を作る

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

GridにVertex Weight Editモディファイアを追加
Vertex Weight Edit を以下のように変更する

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

一度オブジェクトモードへ戻り、Armatureを選択して[Pose Mode]へ移行する。
ArmatureをZ方向に持ち上げると、頂点が猫の形に持ち上がる。

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

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

テンキーの[5][1]でXZ平面の平行投影に切り替え、Editモードで[B]の範囲選択などを使っていらない点を削除する。
また、カメラの向きを変えて[C]等を使ってRepeatしてしまったテクスチャの部分を削除する

Edit モードで必要な点を全て選択し、TransformでZをGlobal座標系で0にする
Editモードで[X] → Only Edges & Faces で面を全て消す


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

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

続いて、猫(Grid)を選択し、Object→Duplicationで[Verts]を選択する
碁石が大きすぎるので、[S]で縮める
このままだと各オブジェクトは虚像なので、点群(Grid)を選択した状態で、
[Object]→[Apply]→Make Duplicates Real
を選択し、実体化する。

碁盤はめんどくさいので省略
前回の続き。
テンプレートメタプログラミングは何をやっているかさっぱりわからないので少しでも図解をしておきたい。





可変長引数テンプレートクラスのテスト。以下のテンプレートクラスは、
第一テンプレート引数で指定した型の派生型を
第二以降テンプレート引数から探して返す。
#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; }
普通の感覚だとこんなもんどこに使い道があるのかわからないので使用例を置いておくと、以下の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
指定されていません
#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;}//////////////////////////////////////////////////////// //バッファ作成 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); }////////////////////////////////////////////////////////// 頂点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 }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]); }
#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); }

流れぶった切ってフレームバッファやります。
#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を出力します。あまり長時間走らせていいことのあるプログラムではありません。出力が確認出来たらすぐに画面を閉じてください。
BarkDecidious0107
https://www.textures.com/download/barkdecidious0107/17362?q=bark
[Shift+A]→[Image Texture]→TexturesCom_BarkDecidious0107_seamless_S.jpg
[Shift+A]→[Image Texture]→TexturesCom_Moss0177_1_seamless_S.jpg
[Shift+A]→[Color]→[MixRGB]でテクスチャをMixする
光源のタイプをSunに設定し、Strengthを5にする
テンキーの[7][5]でXY平面の平行投影に切り替える

・ライトの位置をデフォルトから(-6.93 , -9.809)だけ移動する。
・[R]で131.092°回転する。
・テンキーの[3]でYZ平面に移行し、ライトを現在の角度から-13.874°回転する



・[Shift+A]→[Texture]→[Noise Texture]
・[Shift+A]→[Converter]→[ColorRamp]
を追加
・[Shift+A]→[Color]→[Mix]
・[Shift+A]→[Shader]→[Glossy BSDF]
・[Shift+A]→[Shader]→[Mix Shader]
を追加
・[Shift+A]→[Vector]→[Bump]
を追加
・[Shift+A]→[Color]→[Mix]
を追加しMultiplyに設定
・[Shift+A]→[Vector]→[Mapping]
・[Shift+A]→[Input]→[Texture Coordinate]
を追加
マテリアル中心のチュートリアル。約23分
[0:25] レンダラがCyclesになっていることを確認する
1.[X]で最初のCubeを削除する
2.[Shift+A]→[Mesh]→Circle で円を作成
3.[R]→[Y]→[9][0] で90°回転
4.テンキー[1]→[5]でY方向の平行投影にする
5.Editモードへ移行し、[E]→[Esc]→で押し出し→[G][X]でX方向に限定し[Ctrl]を押しながらマウスを動かし押し出しを行う。これを二回行う。
6.[Z]を押してワイヤフレーム表示に切り替え、[B]で右辺を選択する。
7.5と同じ事を繰り返して、右側にも伸ばす
8.Loop Cutで一区画を8分割する


9.XY平面の端のあたりから右に5、上に5いったところの四角形を選択し、押し出して枝にする
10.枝の先端を[S]で0.710倍に縮小する
11.反対側から左に5、XY平面の直下の四角形4枚を押し出し枝にする。
12.[E]押し出しとS[拡大縮小]を使って木の両端を閉じる
※ 動画では単位系がcmになっている
13.Smoothシェーディングに切り替える
14.Subdivision Surfaceモディファイア追加
15.Displaceモディファイアを設定する。
16.UV展開する

17.マテリアルを作成し、マテリアル名を「Tree Bark」とする

以後、マテリアル設定
Blenderは頂点Colorを持つPLYには対応している。一方でFace ColorのPLYには対応していないらしい。ざっと調べた限りでは。
あたりから頂点色を持つPLYファイルを用意する。
Blenderで普通に[File]→[Import]→Stanford (.ply) で読み込む
Vertex Paintモードに移行すると、PLYから読み込んだ頂点色を確認できる。
[Shift+A]→[Input]→Attribute
のノードを追加し、Vertex Colorsの名前を設定する
Blender 2.8 の Eevee でも全く同じようにできる。

#pragma once //基本的なデータタイプ #include <vcg/complex/complex.h> class MyFace; class MyVertex; class MyEdge; struct MyUsedTypes : public vcg::UsedTypes< vcg::Use<MyVertex>::AsVertexType, vcg::Use<MyFace>::AsFaceType, vcg::Use<MyEdge>::AsEdgeType> {}; //頂点型 class MyVertex : public vcg::Vertex< MyUsedTypes, vcg::vertex::Coord3f, vcg::vertex::Color4b, vcg::vertex::Normal3f, vcg::vertex::VFAdj, vcg::vertex::VEAdj, vcg::vertex::Qualityf, vcg::vertex::BitFlags, vcg::vertex::Mark> { //ここにメンバ変数を追加できる }; //面型 class MyFace : public vcg::Face < MyUsedTypes, vcg::face::VertexRef, vcg::face::Normal3f, vcg::face::FFAdj, vcg::face::EFAdj, vcg::face::Mark, vcg::face::VFAdj, vcg::face::BitFlags > { }; //エッジ型 class MyEdge : public vcg::Edge< MyUsedTypes, vcg::edge::VertexRef, vcg::edge::BitFlags, vcg::edge::EVAdj, vcg::edge::EFAdj> { }; //3Dモデル型 class MyMesh : public vcg::tri::TriMesh< std::vector<MyVertex>, std::vector<MyFace>, std::vector<MyEdge> > { };
//メッシュ定義 MyMesh mesh; //PLYファイル読み込み vcg::tri::io::ImporterPLY<MyMesh>::Open(mesh, plypathname); //全てのfaceにDELETEDフラグを立てる for (int i = 0; i < mesh.face.size(); i++) { vcg::tri::Allocator<MyMesh>::DeleteFace(mesh, mesh.face[i]); } // DELETED フラグのついたMyFaceオブジェクトを削除する vcg::tri::Allocator<MyMesh>::CompactFaceVector(mesh);
VCGの三角形の削除は、MyFaceオブジェクトにDELETEDフラグを立てる事で行う。
DeleteFace staticメンバ関数でDELETEDフラグを立てるのだが、この時、対象の三角形(上ではmesh.face[i])に既にDELETEDフラグが立っているとassertで落ちる。まあ既に削除された事になっているモノを操作しようとしているのだから合理的ではあるのだが。
そして、本当の削除はその後CompactFaceVectorで行う。
この時注意は、CompactFaceVectorを使う前も、「内部的には三角形数==0 」 という扱いになっている。だから、
int fc = mesh.FN();
とした場合、fc == 0になる。
ところが
int fc = mesh.face.size()
とすると、削除する前の三角形の数を取得することになる。この
という状況がVCGできにはまずいので、一連の処理が終わったらCompactFaceVectorを必ず呼び出す必要がある。

VCG LibraryのBall Pivotingでメッシュ生成
VCG Library ターゲットの周辺のオブジェクトを取得