普段はC++からOpen3d使っているのだがひょんなことからPythonから使わざるを得なくなった。色々詰まったので流れをメモしておきたい。
なおAnacondaを使う。初めて使うので設定からやる。Anacondaは開発環境を切り替えて使うためのソフトである。つまり、
・このスクリプトはnumpy 0.5+open3d 0.3以外では動かない。
・別のはnumpy 1.19+open3d 1.12以外では動かない。
・でもどちらも頻繁に使う
みたいな状況で簡単に環境を切り替えるのに便利だという事(らしい)。
Windows 10
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を何もしなくても使えるメリットがある。
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
以下の内容は古い上にcondaとpipを併用する悪い方法なのでインストールは別記事を参照
condaでopen3dをインストールする(改)
以下、古い記事
open3dのPythonモジュールをインストールする。
conda install -c open3d-admin open3d
参考:http://www.open3d.org/docs/release/getting_started.html
インストールされたバージョンによってはサンプルが全く動かないので、バージョンを確認する。pip list で-oオプションを付けると、バージョンアップの必要なライブラリの一覧だけを見られる。
pip list -o
明らかにバージョンが低い場合はアップデートする
pip install -U open3d
参考:https://qiita.com/HyunwookPark/items/242a8ceea656416b6da8
importだけ実行してみてエラーが出なければ(無言なら)成功している。
python -c "import open3d"
一つのファイルに全ての関数やクラスを入れてしまうとあまりに見通しが悪くなるのでファイルを分けたい。
Edit → Preference → File Paths → Data → Scripts にディレクトリを設定する。注意点はディレクトリ名が\modules , \addons ... でなければいけない。
ここではimportしたいので、\modulesディレクトリを作り、そのディレクトリの親ディレクトリを指定する。
\modules\ ディレクトリの中のファイル名(拡張子除)がモジュール名になり、これをimportすれば中の関数を呼び出すことができる。
import test test.call()
このままだと、上記test.pyを更新する度にBlenderを再起動しなければならない。実行する度に再読み込みを行うには再読み込み命令をスクリプトに書く必要がある。importlib.reload( モジュール名 )を呼び出す。
import test ############################# ## リロード import importlib importlib.reload(test) #モジュール名を指定 ## ############################# test.call()
上記test.pyがさらに内部で別のモジュールをimportしていた場合、それらは更新されない。これを更新するためには、testモジュール内部で使用する外部モジュールをimportlib.reloadする必要がある。
import neighbour # 別ファイル neighbour.py import importlib # reload用 # blenderから呼ばれる def call(): importlib.reload(neighbour) # neighbour.pyの再ロード neighbour.hello()
def hello(): print("neighbour.py is updated")
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; }
前回のプログラム
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はジオメトリシェーダから出力する可能性のある最大長点数を指定するはずだが、なぜか前回の
をそのままにしておいても四つ目の頂点を出力できた。何か意味を取り違えているのだろうか。
前回までは頂点を入力にしていたが、三角形を入力する場合はGL_GEOMETRY_INPUT_TYPEにGL_TRIANGLESを指定、GL_GEOMETRY_OUTPUT_TYPEにはGL_TRIANGLE_STRIPを指定する。
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_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(); }
前回のプログラムからプログラムのリンクの部分だけを以下のように書き換える。
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; }
#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(); }
前回のプログラム
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(); }
1頂点入力、1頂点出力のジオメトリシェーダを作って動作確認を行う。
シェーダのコンパイル等は依然作ったprepare_shader.hppを転用する。ただしサンプルとしてはlink_programを使うと読みにくくなるので、ジオメトリシェーダサンプル用に書き直す。
https://suzulang.com/glsl-shader-my-functions-on-cpp
#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; }
#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などを複数実行するときは複数回呼び出す }
#version 460 core in vec4 vColor; out vec4 FragColor; void main() { FragColor = vColor; }
#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を使おうとすると何かとコードが長くなる。状況に合わせて細かい調整ができるところが魅力なのだが練習やテストで毎回書くのは大変なので使いまわせそうなところだけでも関数化しておきたい。
#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; }
#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; }
#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; }
#version 460 core in vec4 vColor; out vec4 FragColor; void main() { FragColor = vColor; }
以下のような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の存在の判定をして構造体を切り替えているだけ。
#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); }
}
#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(); }