『コントロールに描画』 でコントロールを置けるようにしたので、
PropertyGrid を使って三角形の大きさ・位置・カラーを変更できるようにしてみます。
作った時の環境。GLEW を導入してない方は こちら 。
大体こんな感じで作ります。
前回作った GLSLApp2.zip を元に作るのでダウンロード。
GLSLForm.h をクリックしてデザイナを開き、フォーム右 ( Panel2 ) に propertyGrid を配置します。
作成された propertyGrid1 のプロパティで Dock = Fill に設定すると下のような見た目に。
PropertyGrid はクラスのメンバ変数を直接表示・変更できるコントロールです。
シェーダに渡したい「大きさ・位置・カラー」をメンバ変数に持つクラスを作成して PropertyGrid に設定します。
プロジェクト名を右クリックして「 追加>新しい項目>クラス 」を選択。
「 C++ クラス 」で追加ボタンを押し、クラス名に GLSLProperty を入力して完了。
生成された GLSLProperty.h, GLSLProperty.cpp にメンバ変数とその get/set プロパティを追加。( 赤字 )
[GLSLProperty.h] #pragma once using namespace System::Drawing; ref class GLSLProperty { public: GLSLProperty(void); private: float m_size; Point m_position; Color m_faceColor; public: property float Size { float get() { return m_size; } void set(float value) { m_size = value; } }; property Point Position { Point get() { return m_position; } void set(Point value) { m_position = value; } }; property Color FaceColor { Color get() { return m_faceColor; } void set(Color value) { m_faceColor = value; } }; };
[GLSLProperty.cpp] #include "GLSLProperty.h" GLSLProperty::GLSLProperty(void) { m_size = 1.0; m_position = Point(0, 0); m_faceColor = Color::Red; }
PropertyGrid にクラスの内容を表示するには SelectedObject を使います。
GLSLForm.h で GLSLProperty オブジェクトを生成し、propertyGrid1->SelectedObject に設定。
[GLSLForm.h] #include "GLSLMain.h" #include "GLSLView.h" #include "GLSLProperty.h" : private: GLSLMain* m_pGL; GLSLView^ m_view; GLSLProperty^ m_property; public: GLSLForm(void) { InitializeComponent(); // //TODO: ここにコンストラクター コードを追加します // m_view = gcnew GLSLView(); m_view->Dock = System::Windows::Forms::DockStyle::Fill; m_view->SizeChanged += gcnew System::EventHandler(this, &GLSLForm::GLSLForm_SizeChanged); m_view->Paint += gcnew System::Windows::Forms::PaintEventHandler(this, &GLSLForm::GLSLForm_Paint); this->splitContainer1->Panel1->Controls->Add(m_view); m_pGL = new GLSLMain((void*)m_view->Handle); m_property = gcnew GLSLProperty(); propertyGrid1->SelectedObject = m_property; }
頂点シェーダに「大きさ・位置」パラメータを追加します。
[basic.vert] #version 400 layout (location = 0) in vec3 VertexPosition; uniform float uSize; uniform vec3 uPosition; void main() { gl_Position = vec4(VertexPosition * uSize + uPosition, 1.0); }
ピクセルシェーダに「カラー」パラメータを追加します。
[basic.frag] #version 400 layout (location = 0) out vec4 FlagColor; uniform vec3 uFaceColor; void main (void) { FlagColor = vec4(uFaceColor, 1); }
PropertyGrid の値をシェーダに反映させるクラスを追加します。
プロジェクト名を右クリックして「 追加>新しい項目>クラス 」を選択。
「 C++ クラス 」で追加ボタンを押し、クラス名に GLSLShaderParam を入力, 仮想デストラクターをチェック, マネージを外して完了。
生成された GLSLShaderParam.h, GLSLShaderParam.cpp にシェーダパラメータと対応するメンバ変数を持たせます。
これはそのままシェーダに設定するので、vec3 は float[3] など型を揃えて。
外部からこれらの値を設定する Set 関数と、シェーダに適用する Use 関数も追加します。
[GLSLShaderParam.h] #pragma once #include <gl/glew.h> class GLSLShaderParam { public: GLSLShaderParam(void); virtual ~GLSLShaderParam(void); private: float m_position[3]; float m_size; float m_faceColor[3]; public: void SetPosition(float x, float y, float z){ m_position[0] = x; m_position[1] = y; m_position[2] = z; } void SetSize(float siz){ m_size = siz; } void SetFaceColor(float r, float g, float b){ m_faceColor[0] = r; m_faceColor[1] = g; m_faceColor[2] = b; } void Use(GLuint program); };
[GLSLShaderParam.cpp] #include "GLSLShaderParam.h" GLSLShaderParam::GLSLShaderParam(void) { SetPosition(0.0f, 0.0f, 0.0f); SetSize(1.0f); SetFaceColor(1.0f, 0.0f, 0.0f); } GLSLShaderParam::~GLSLShaderParam(void) { } void GLSLShaderParam::Use(GLuint program) { GLint index; if ((index = glGetUniformLocation(program, "uPosition")) >= 0) glUniform3f(index, m_position[0], m_position[1], m_position[2]); if ((index = glGetUniformLocation(program, "uSize")) >= 0) glUniform1f(index, m_size); if ((index = glGetUniformLocation(program, "uFaceColor")) >= 0) glUniform3f(index, m_faceColor[0], m_faceColor[1], m_faceColor[2]); }
シェーダを適用する際、パラメータを反映できるように Use 関数を変更します。( 赤字 )
引数に GLSLShaderParam を渡して Use 関数をコール。
[GLSLShader.h] #pragma once #include <gl/glew.h> #include "GLSLShaderParam.h" class GLSLShader { public: GLSLShader(const char* name); virtual ~GLSLShader(void); void Use(GLSLShaderParam* param); private: GLuint LoadFromFile(const char* filename, GLenum shadertype); private: GLuint m_program; };
[GLSLShader.cpp] void GLSLShader::Use(GLSLShaderParam* param) { if (m_program) { glUseProgram(m_program); if (param) { param->Use(m_program); } } }
パラメータ設定クラスは GLMain が保持して生成・破棄を行います。
描画時にシェーダに渡して使いますが、外部からパラメータを設定できるよう Get 関数も用意。
[GLMain.h] #pragma once #include#include "GLSLShader.h" #include "GLSLShaderParam.h" class GLSLMain { public: GLSLMain(void* hWnd); virtual ~GLSLMain(void); void GLSLMain::Render(int w, int h); GLSLShaderParam* GetShaderParam(){ return m_pShaderParam; } private: GLSLShader* m_pShader; GLSLShaderParam* m_pShaderParam; HWND m_hWnd; HDC m_hDC; HGLRC m_hRC; };
[GLMain.cpp] #include#include "GLSLMain.h" GLSLMain::GLSLMain(void* hWnd) { : wglMakeCurrent(m_hDC, m_hRC); glewInit(); m_pShader = new GLSLShader("basic"); m_pShaderParam = new GLSLShaderParam(); wglMakeCurrent(m_hDC, nullptr); } GLSLMain::~GLSLMain(void) { wglMakeCurrent(nullptr, nullptr); if (m_pShaderParam) delete m_pShaderParam; if (m_pShader) delete m_pShader; if (m_hRC) wglDeleteContext(m_hRC); if (m_hDC) ReleaseDC(m_hWnd, m_hDC); } void GLSLMain::Render(int w, int h) { wglMakeCurrent(m_hDC, m_hRC); glViewport(0, 0, w, h); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); if (m_pShader) m_pShader->Use(m_pShaderParam); glBegin(GL_TRIANGLES); glVertex3f( 0.7f, -0.5f, 0.0f ); glVertex3f( 0.0f, 0.5f, 0.0f ); glVertex3f( -0.7f, -0.5f, 0.0f ); glEnd(); glFlush(); SwapBuffers(m_hDC); wglMakeCurrent(m_hDC, 0); }
GLSLProperty に、GLSLShaderParam を設定するメソッドを追加します。
Size はそのまま使いますが、Position は整数なので適当な倍率をかけて float にします。( ここでは 1/100 )
FaceColor は 0〜255 → 0.0〜1.0 に変換。
[GLSLProperty.h] public: void Set(GLSLShaderParam* param);
[GLSLProperty.cpp] void GLSLProperty::Set(GLSLShaderParam* param) { if (param == nullptr) return; param->SetSize(m_size); param->SetPosition(m_position.X * 0.01f, m_position.Y * 0.01f, 0.0f); param->SetFaceColor(m_faceColor.R / 255.0f, m_faceColor.G / 255.0f, m_faceColor.B / 255.0f); }
デザイナで propertyGrid1 を選択し、PropertyValueChanged イベントを追加します。
PropertyGrid の値が編集されると呼ばれるので、ここでシェーダパラメータの更新と再描画を行わせます。
[GLSLForm.h] private: System::Void propertyGrid1_PropertyValueChanged(System::Object^ s, System::Windows::Forms::PropertyValueChangedEventArgs^ e) { if (m_pGL) { m_property->Set(m_pGL->GetShaderParam()); m_pGL->Render(m_view->ClientSize.Width, m_view->ClientSize.Height); } }
C# で使って便利だなと思っていた PropertyGrid ですが、C++ で使う良いサンプルが無かったのでやってみました。
パラメータ設定クラスを PropertyGrid に直接設定して書き換えられたら良さそうなものなんですが、
マネージ型でないといけなかったり、自作のクラスやデータ型を表示させるには面倒な設定が必要だったり…。
なので今回は Drawing クラスの Color, Point 型を利用し、実際にシェーダで使う値に変換する形にしています。
いちおう自作の Vector3 型を表示させるのもやってみたので、次の機会に紹介できたらと思います。
これもサンプルがあまり無かったのですが、SlimDX の実装が参考になりました。
まあ面倒なので、別に PropertyGrid じゃなくてコントロール追加で良かった気も。