『コントロールに描画』 でコントロールを置けるようにしたので、
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 じゃなくてコントロール追加で良かった気も。