OpenTKの手始めとして、前回触れたGameWindowとGLControlという二つのプログラミングスタイルをベースとしたスケルトンを作ってみましょう。(注)
注:メインテーマであるBCCSkeltonがそうですが、私は標準の「ドンガラ(Skelton)」を作り、それに機能を実装するスタイルのプログラミングを好んでいるということでしょう。また、両者で共通するGLクラス関係のメソッドも共有化できます。
繰り返しになりますが、(ウィンドウ自体にOpenTKがビルトインされた)GameWindowと、(OpenTKをウィンドウコントロールとして利用する)GLControlのメリデメはこちらを参照して下さい。↓に私が使っているGameWindowとGLControlのスケルトンを載せておきます。二つとも同じカラフルな2D三角形を描画するだけの簡単なプログラムですが、相違点と共通点があります。(なお、GLControlの場合の定期的な描画更新はユーザーがTimer等を使って実施してください。また、GLControlへのキー入力はやや処理が面倒ですので省略しています。)
【相違点】
GameWindowはウィンドウそのものなので、そのまま、または派生させて生成し、後はウィンドメッセージウループで呼び出される(組み込みの)割込みメソッド("Onナンチャラ")をオーバーライドして使っています。特に注意すべきは、再描画するメソッドがOnRenderFrameで、その直前に呼ばれるOnUpdateFrameで再描画前の処理を行う、ということでしょうか?
一方、GLControlは他のウィンドウコントロールと同じく、メインウィンドウ(Form)のOnLoad処理(WM_CREATE)でメインウィンドウの子として生成します。そしてこのコントロールの生成時、サイズ変更時、再描画時の割込みメソッドに処理を書いてゆきます。なお、GameWindowはGLUTの"Idle処理"と同じように特段何をしなくとも再描画処理が呼ばれますが、GLControlは意図的にWM_PAINTメッセージを出さないと再描画しないようなので、グラフィック処理時にInvalidateやRefreshメソッドを使って再描画要求を出す必要があります。
【共通点】
先ず、両方とも生成時にOpenGLの初期処理をOnLoadで行うところでしょうか?↓では画面消去色の設定と深度チェックを有効にしています。
次にOpenGLのお作法通り、サイズ変更時(生成時を含む)にビューポートと投影処理の設定を行います。↓では共に透視投影で処理しています。
最後は描画処理ですが、先ず画面消去、モデルビューモードで視点・視野設定を行い、GL命令等で描画を行い、ダブルバッファーの交換を行います。
さて、では実際のコードをご覧ください。なお、次回からは実際に
GameWindow版スケルトンを使ってGLUTでやってきたプリミティブ等を移植
してみようと思います。
【GameWindow_Base.cpp】
/////////////////////////////////////////////
// GameWindow_Base.cpp
// OpenTKをGameWindowで使う場合のスケルトン
// Copyrignt (c) 2024 byYsama
/////////////////////////////////////////////
using System;
using OpenTK; //OpenTKを使用する際に必須
using OpenTK.Graphics; //OpenTKのグラフィックを使用する際に必須
using OpenTK.Graphics.OpenGL; //OpenGLを使用する際に必要
using OpenTK.Input; //OpenTKで入力を行う際に必要(例:Keyboiard)
namespace GameWindow_Base
{
//アプリケーションクラス
public class App
{
[STAThread]
public static void Main()
{
gWindow gw= new gWindow();
gw.Run();
}
}
class gWindow : GameWindow //GameWindowクラスからユーザーウィンドウを派生させる
{
public gWindow() : base(640, 480, GraphicsMode.Default, "OpenTK GameWindow") //既定引数
{
/* 解説:マウスやキーによる割り込みの場合、次のようにして対応するメソッドを実装する。
//Mouse割込み
this.MouseDown += gW_MouseDown;
this.MouseMove += gW_MouseMove;
this.MouseUp += gW_MouseUp;
//Key割込み
this.KeyUp += gW_KeyUp;
this.KeyDown += gW_KeyDown;
this.KeyPress += gW_KeyPress;
*/
}
//ウィンドウの起動時に実行される
protected override void OnLoad(EventArgs e)
{
base.OnLoad(e); //既定処理
GL.ClearColor(Color4.Black); //画面消去色の設定
GL.Enable(EnableCap.DepthTest); //深度チェックを有効化する
}
//ウィンドウのサイズが変更された場合に実行される
protected override void OnResize(EventArgs e)
{
base.OnResize(e); //既定処理
//ビューポートの設定
GL.Viewport(0, 0, ClientRectangle.Width, ClientRectangle.Height);
//視野空間の設定
SetProjection();
}
//ウィンドウイベントが更新された場合に実行される(OnRenderFrameの前に実行される)
protected override void OnUpdateFrame(FrameEventArgs e)
{
base.OnUpdateFrame(e);
//ESCキーが押されたら終了
if(Keyboard.GetState().IsKeyDown(Key.Escape))
{
Exit();
}
}
//ウィンドウ描画が更新された場合に実行される
protected override void OnRenderFrame(FrameEventArgs e)
{
base.OnRenderFrame(e); //既定処理
GL.Clear(ClearBufferMask.ColorBufferBit | ClearBufferMask.DepthBufferBit);
SetModelView(); //モデルビューモードで視点を設定して原点(0, 0, 0)を見る
//以下に描画処理を行う
GL.Begin(PrimitiveType.Quads);
GL.Color4(Color4.White); //色名で持定
GL.Vertex3(-1.0f, 1.0f, 0.0f);
GL.Color4(new float[] { 1.0f, 0.0f, 0.0f, 1.0f }); //配列で特定
GL.Vertex3(-1.0f, -1.0f, 0.0f);
GL.Color4(0.0f, 1.0f, 0.0f, 1.0f); //4つの引数に単精度実数型で特定
GL.Vertex3(1.0f, -1.0f, 0.0f);
GL.Color4((byte)0, (byte)0, (byte)255, (byte)255); //byte型で特定
GL.Vertex3(1.0f, 1.0f, 0.0f);
GL.End();
//バッファー変換
SwapBuffers();
}
private void SetProjection()
{
//視野空間の設定
GL.MatrixMode(MatrixMode.Projection);
//透視投影
Matrix4 projection = Matrix4.CreatePerspectiveFieldOfView((float)Math.PI / 4, (float)Width / (float)Height, 0.1f, 64.0f); //glPerspective(fov, aspect, near, far);
GL.LoadMatrix(ref projection);
}
void SetModelView(float x = 0.0f, float y = 0.0f, float z = 5.0f)
{
GL.MatrixMode(MatrixMode.Modelview); //モデルビューを選択
//視点を設定して原点(0, 0, 0)を見る
Vector3 eye = new Vector3(x, y, z);
Matrix4 modelview = Matrix4.LookAt(eye, Vector3.Zero, Vector3.UnitY); //gluLookAt(0, 0, 5, 0, 0, 0, 0, 1, 0);
GL.LoadMatrix(ref modelview); //現在の変換マトリックスに設定
}
void SetLight(float x = 50.0f, float y = 50.0f, float z = 10.0f)
{
//光源の使用
GL.Enable(EnableCap.Lighting);
//ライト0の設定
GL.Enable(EnableCap.Light0);
float[] pos = new float[] {x, y, z, 0.0f };
GL.Light(LightName.Light0, LightParameter.Position, pos);
}
}
}
【GLControl_Base.cpp】
///////////////////////////////////////////
// GLControl_Base.cpp
// OpenTKをGLControlで使う場合のスケルトン
// Copyrignt (c) 2024 byYsama
///////////////////////////////////////////
using System;
using System.Drawing;
using System.Windows.Forms;
using OpenTK; //OpenTKを使用する際に必須
using OpenTK.Graphics; //OpenTKのグラフィックを使用する際に必須
using OpenTK.Graphics.OpenGL; //OpenGLを使用する際に必要
//using OpenTK.Input; //OpenTKで入力を行う際に必要(例:Keyboiard)-マウスを使う場合Formsのマウスイベントと干渉するので注意
namespace GLControl_Base
{
//アプリケーションクラス
public class App
{
[STAThread]
public static void Main()
{
Application.Run(new MainForm());
}
}
public class MainForm : Form
{
private GLControl glControl; //GLControlへのポインター
public MainForm()
{
this.Text = "OpenTK_GLControl_Base";
this.ClientSize = new Size(640, 480);
this.MinimumSize = new Size(480, 360);
this.Load += new EventHandler(Form_Load);
}
private void Form_Load(object sender, EventArgs e)
{
//Formのコントロールレイアウトロジック保留
SuspendLayout();
//GLControl関連
glControl = new GLControl();
glControl.Size = new Size(this.ClientSize.Width, this.ClientSize.Height);
glControl.Location = new Point(0, 0);
glControl.Load += new EventHandler(glControl_Load);
glControl.Resize += new EventHandler(glControl_Resize);
glControl.Paint += new PaintEventHandler(glControl_Paint);
/* 解説:マウスによる割り込みの場合、次のようにして対応するメソッドを実装する。
glControl.MouseDown += glc_MouseDown;
glControl.MouseMove += glc_MouseMove;
glControl.MouseUp += glc_MouseUp;
*/
Controls.Add(glControl);
//Formのコントロールレイアウトロジック再開
ResumeLayout();
}
private void glControl_Load(object sender, EventArgs e)
{
GL.ClearColor(glControl.BackColor); //画面消去色の設定
GL.Enable(EnableCap.DepthTest); //深度バッファの使用
}
private void glControl_Resize(object sender, EventArgs e)
{
glControl.Dock = DockStyle.Fill; //All the control's edges are docked to the all edges of its containing control and sized appropriately.
glControl_SetProjection();
glControl_SetModelView(); //ModelViewの設定
//glControl_SetLight(); //ライトの設定
glControl.Refresh(); //再描画
}
private void glControl_Paint(object sender, PaintEventArgs e)
{
//色と深度バッファーを初期化
GL.Clear(ClearBufferMask.ColorBufferBit | ClearBufferMask.DepthBufferBit);
//モデルビューにして視点を設定
glControl_SetModelView();
//以下に描画処理を行う
GL.Begin(PrimitiveType.Quads);
GL.Color4(Color4.White); //色名で持定
GL.Vertex3(-1.0f, 1.0f, 0.0f);
GL.Color4(new float[] { 1.0f, 0.0f, 0.0f, 1.0f }); //配列で特定
GL.Vertex3(-1.0f, -1.0f, 0.0f);
GL.Color4(0.0f, 1.0f, 0.0f, 1.0f); //4つの引数に単精度実数型で特定
GL.Vertex3(1.0f, -1.0f, 0.0f);
GL.Color4((byte)0, (byte)0, (byte)255, (byte)255); //byte型で特定
GL.Vertex3(1.0f, 1.0f, 0.0f);
GL.End();
//バッファー変換
glControl.SwapBuffers();
}
private void glControl_SetProjection()
{
//ビューポートの設定
GL.Viewport(0, 0, glControl.Size.Width, glControl.Size.Height);
//視野空間の設定
GL.MatrixMode(MatrixMode.Projection);
//透視投影-尚、glControl.AspectRatioは、(float)glControl.Width / (float)glControl.Heightと等価
Matrix4 projection = Matrix4.CreatePerspectiveFieldOfView((float)Math.PI / 4, glControl.AspectRatio, 0.1f, 64.0f);
GL.LoadMatrix(ref projection);
}
void glControl_SetModelView(float x = 0.0f, float y = 0.0f, float z = 5.0f)
{
GL.MatrixMode(MatrixMode.Modelview); //モデルビューを選択
//視点を設定して原点(0, 0, 0)を見る
Vector3 eye = new Vector3(x, y, z);
Matrix4 modelview = Matrix4.LookAt(eye, Vector3.Zero, Vector3.UnitY); //gluLookAt(0, 0, 5, 0, 0, 0, 0, 1, 0);
GL.LoadMatrix(ref modelview); //現在の変換マトリックスに設定
}
void glControl_SetLight(float x = 50.0f, float y = 50.0f, float z = 10.0f)
{
//光源の使用
GL.Enable(EnableCap.Lighting);
//ライト0の設定
GL.Enable(EnableCap.Light0);
float[] pos = new float[] {x, y, z, 0.0f };
GL.Light(LightName.Light0, LightParameter.Position, pos);
}
}
}

















