C#使うならやっぱしフォームじゃなく,WPFでしょ!
…ということで,今回はWPF上にOpenGLのビューを作成するサンプルを作ってみました。
C#でOpenGL (3)
★ 1.はじめに…
★ 2.WPFでOpenGL
WPFでOpenGLのビューを表示するに当たって,一番の問題はレンダリングコンテキストの作成です。
WPFをかじったことがある方ならわかると思いますが,WPFはそもそもHWNDやHDCといったハンドルを意識しないように設計されています。
そうなんです!そこが問題なんです。
レンダリングコンテキストを作成するためにはwglCreateContext()メソッドを使うのですが,まずこのメソッドの引数にHDCが必要です。さらにHDCを取得するためにHWNDが必要になります。そんなわけでWPFですんなりと実装できないようになっちゃってます。
wglCreateContext()とか古い関数使わなくて済むようにMicrosoftの方でC#のコードを用意してくれれば、一番早いんですが…残念ながら用意されていないようです。
そんなわけで困るんですが、一応HWNDを取得できるような関数を用意しくれているみたいなのでそれを使います。
System.Windows.Interop.WindowInteropHelperというクラスがあり、このクラスのメンバにHandleプロパティというのがあります。このプロパティを取得すればウィンドウハンドルを取得できます。
このプロパティなんですが,Initializeイベントで呼び出すと、ウィンドウがまだ初期化されていないためか、うまく取得できないのでSourceInitializedイベントで呼び出すようにすれば良いみたいです。色々とググって調べてみたんですが、大体SourceInitializedで取得しているコードが多かったです。そんなわけで、周りの人にあわせてSourceInitializedイベントでウィンドウハンドルを取得することにします。
サンプルコードでは,以下のようにWindowSourceInitialized()メソッドでウィンドウハンドルを取得しています。
WPFではUIと同じスレッドで描画処理をする必要があるのですが,DispatcherTimerを使えばUIと同じスレッドで処理できるので,タイマーにはDispatcherTimerを使います。
最後に,Closedイベントが発生したときにレンダリングコンテキストを破棄する処理を追加しておきます。今回は,描画用のクラスを作ったので,IDisposableインターフェースを継承して,ClosedイベントでDispose()メソッドを呼び出すようにしました。
描画クラスですが,コンストラクタでピクセルフォーマットの設定などをするメソッドを呼び出し,レンダリングコンテキストを作成しています
今回はWPFでOpenGLのビューを表示する方法を紹介してみました。WPF上でもOpenGLを一応使えるので,ツール作成などに使えるのではないかなぁ〜と思います。
WPFをかじったことがある方ならわかると思いますが,WPFはそもそもHWNDやHDCといったハンドルを意識しないように設計されています。
そうなんです!そこが問題なんです。
レンダリングコンテキストを作成するためにはwglCreateContext()メソッドを使うのですが,まずこのメソッドの引数にHDCが必要です。さらにHDCを取得するためにHWNDが必要になります。そんなわけでWPFですんなりと実装できないようになっちゃってます。
wglCreateContext()とか古い関数使わなくて済むようにMicrosoftの方でC#のコードを用意してくれれば、一番早いんですが…残念ながら用意されていないようです。
そんなわけで困るんですが、一応HWNDを取得できるような関数を用意しくれているみたいなのでそれを使います。
System.Windows.Interop.WindowInteropHelperというクラスがあり、このクラスのメンバにHandleプロパティというのがあります。このプロパティを取得すればウィンドウハンドルを取得できます。
このプロパティなんですが,Initializeイベントで呼び出すと、ウィンドウがまだ初期化されていないためか、うまく取得できないのでSourceInitializedイベントで呼び出すようにすれば良いみたいです。色々とググって調べてみたんですが、大体SourceInitializedで取得しているコードが多かったです。そんなわけで、周りの人にあわせてSourceInitializedイベントでウィンドウハンドルを取得することにします。
サンプルコードでは,以下のようにWindowSourceInitialized()メソッドでウィンドウハンドルを取得しています。
00035: /// <summary> 00036: /// 初期化処理 00037: /// </summary> 00038: /// <param name="sender"></param> 00039: /// <param name="e"></param> 00040: private void WindowSourceInitialized(object sender, EventArgs e) 00041: { 00042: //ウィンドウハンドル取得 00043: IntPtr hWnd = new WindowInteropHelper(this).Handle; 00044: 00045: //描画クラスの作成 00046: renderer = new GLRenderer(hWnd); 00047: renderer.Initialized(); 00048: renderer.SizeChanged(RenderSize);//一度リサイズしておく 00049: 00050: //タイマー 00051: timer = new DispatcherTimer(); 00052: timer.Tick += new EventHandler(DispatcherTimerTick); 00053: timer.Interval = new TimeSpan(0, 0, 0, 0, 16); // 16msecで描画 00054: timer.Start(); 00055: }続いて描画処理ですが,描画処理はタイマーイベントで16msec毎に描画するようにします。16msecなのは,60FPSになるようにするため1/60=0.016secだからです。
WPFではUIと同じスレッドで描画処理をする必要があるのですが,DispatcherTimerを使えばUIと同じスレッドで処理できるので,タイマーにはDispatcherTimerを使います。
最後に,Closedイベントが発生したときにレンダリングコンテキストを破棄する処理を追加しておきます。今回は,描画用のクラスを作ったので,IDisposableインターフェースを継承して,ClosedイベントでDispose()メソッドを呼び出すようにしました。
描画クラスですが,コンストラクタでピクセルフォーマットの設定などをするメソッドを呼び出し,レンダリングコンテキストを作成しています
00042: /// <summary> 00043: /// コンストラクタ 00044: /// </summary> 00045: /// <param name="hWnd"></param> 00046: public GLRenderer(IntPtr hWnd) 00047: { 00048: size = new Size(); 00049: 00050: //ピクセルフォーマットの設定 00051: SetupPixelFormat(hWnd); 00052: }SetupPixelFormatメソッドの中身ですが,ピクセルフォーマットの設定とレンダリングコンテキストの作成を行っています。
00147: /// <summary> 00148: /// ピクセルフォーマットの設定 00149: /// </summary> 00150: /// <param name="hWnd">ウィンドウハンドル</param> 00151: private void SetupPixelFormat(IntPtr hWnd) 00152: { 00153: // PIXELFORMATDESCRIPTORの設定 00154: Gdi.PIXELFORMATDESCRIPTOR pfd = new Gdi.PIXELFORMATDESCRIPTOR(); 00155: pfd.dwFlags = Gdi.PFD_SUPPORT_OPENGL | Gdi.PFD_DRAW_TO_WINDOW | Gdi.PFD_DOUBLEBUFFER; 00156: pfd.iPixelType = Gdi.PFD_TYPE_RGBA; 00157: pfd.cColorBits = 32; 00158: pfd.cAlphaBits = 8; 00159: pfd.cDepthBits = 24; 00160: 00161: // デバイスコンテキストハンドルの取得 00162: hDC = User.GetDC(hWnd); 00163: 00164: // ピクセルフォーマットの選択 00165: int pixelFormat = Gdi.ChoosePixelFormat(this.hDC, ref pfd); 00166: if ( pixelFormat == 0 ) 00167: throw new Exception("Error : Cann't Find A Suitable PixelFormat."); 00168: 00169: // ピクセルフォーマットの設定 00170: if (!Gdi.SetPixelFormat(hDC, pixelFormat, ref pfd)) 00171: throw new Exception("Error : Cann't Set The PixelFormat."); 00172: 00173: // レンダリングコンテキストの生成 00174: hRC = Wgl.wglCreateContext(hDC); 00175: if (hRC == IntPtr.Zero) 00176: throw new Exception("Error : Cann't Create A GLRendering Context."); 00177: 00178: // レンダリングコンテキストカレントにする 00179: Wgl.wglMakeCurrent(hDC, hRC); 00180: 00181: // GLエラーのチェック 00182: int error = Gl.glGetError(); 00183: if (error != Gl.GL_NO_ERROR) 00184: throw new Exception("GL Error : " + error.ToString()); 00185: 00186: }
今回はWPFでOpenGLのビューを表示する方法を紹介してみました。WPF上でもOpenGLを一応使えるので,ツール作成などに使えるのではないかなぁ〜と思います。
★ Download
本ソースコードおよびプログラムを使用したことによる如何なる損害も製作者は責任を負いません。
本ソースコードおよびプログラムは自己責任でご使用ください。
プログラムの作成にはMicrosoft Visual Studio 2008 SP1 Professionalを用いています。
本ソースコードおよびプログラムは自己責任でご使用ください。
プログラムの作成にはMicrosoft Visual Studio 2008 SP1 Professionalを用いています。