MoNo.OpenGL.MGL

OpenGL のAPIを .NET (C#/F#) から利用しやすくするための薄いラッパーです。

OpenGL の関数はC言語形式なので、そのままでは .NET から利用しづらいと感じることがあります。より .NET から利用しやすくするため下記の点を改善するラッパーとして MGL を用意しました。

  • MoNo.RAIL のデータ型の利用
    Vector3dHmMatrix3d といった MoNo.RAIL で用意されているプリミティブなデータ型に対応させました。これにより、例えば OpenGL では3次元ベクトルを長さ3の double / float 配列として入出力する仕様だが、ラッパーにより Vector3d などのデータ型で入出力できるようになりました。
  • プロパティによる状態の get/set
    OpenGL では状態の取得と設定が別々の関数になっていますが(例えば glIsEnabled() と glEnable(), glDisable() など)、.NET では get と set はひとつのプロパティにまとめたほうが自然であり使いやすくなります。例えば GL_DEPTH_TEST の有効/無効状態を MGL.DepthTestEnabled というプロパティでラップすることによって使いやすくしました。
  • 頂点配列の描画の簡略化
    頂点配列に関する OpenGL のAPIは非常に柔軟性が高く設計されていますが、その分だけ利用にあたっては定型的なコードが増えてしまいがちで気軽に利用することが出来ません。MGL では例えば PointNormal3f などといった MoNo.RAIL で用意されているデータ型の配列を受け取るラッパー関数群 MGL.DrawArrays() を備えており、簡便に頂点配列を利用することができます。
  • その他
    テクスチャ、シェーダー、FBO、VBO といった機能もラップすることでより簡便に利用できるようになっています。

プロパティ

有効/無効状態の取得と設定
MGL Property OpenGL constant
LightingEnabled GL_LIGHTING
DepthTestEnabled GL_DEPTH_TEST
StencilTestEnabled GL_STENCIL_TEST
AlphaTestEnabled GL_ALPHA_TEST
CullFaceEnabled GL_CULL_FACE
ColorMaterialEnabled GL_COLOR_MATERIAL
LineStippleEnabled GL_LINE_STIPPLE
PolygonOffsetFillEnabled GL_POLYGON_OFFSET_FILL
PolygonOffsetLineEnabled GL_POLYGON_OFFSET_LINE
PolygonOffsetPointEnabled GL_POLYGON_OFFSET_POINT
NormalizeEnabled GL_NORMALIZE
AutoNormalEnabled GL_AUTO_NORMAL

状態の取得と設定
MGL Property glGet constant OpenGL func.
DepthMask GL_DEPTH_WRITEMASK glDepthMask
StencilMask GL_STENCIL_WRITEMASK glStencilMask
StencilMaskFront GL_STENCIL_WRITEMASK glStencilMaskSeparate
StencilMaskBack GL_STENCIL_BACK_WRITEMASK glStencilMaskSeparate
ColorMask GL_COLOR_WRITEMASK glColorMask
DepthFunc GL_DEPTH_FUNC glDepthFunc
Normal GL_CURRENT_NORMAL glNormal3d
Color GL_CURRENT_COLOR glColor4f
SrcBuffer GL_READ_BUFFER glReadBuffer
DstBuffer GL_DRAW_BUFFER glDrawBuffer
PolygonOffsetFactor GL_POLYGON_OFFSET_FACTOR glPolygonOffset
PolygonOffsetUnits GL_POLYGON_OFFSET_UNITS glPolygonOffset
FrontPolygonMode GL_POLYGONMODE glPolygonMode
BackPolygonMode GL_POLYGONMODE glPolygonMode
CullFace GL_CULL_FACE_MODE glCullFace
LightingTwoSide GL_LIGHT_MODEL_TWO_SIDE glLightModeli
LineStipplePattern GL_LINE_STIPPLE_PATTERN glLineStipple
PointSize GL_POINT_SIZE glPointSize
LineWidth GL_LINE_WIDTH glLineWidth
Matrix GL_MODELVIEW_MATRIX glLoadMatrixf
Viewport GL_VIEWPORT glViewport
ColorClearValue GL_COLOR_CLEAR_VALUE glClearColor
DepthClearValue GL_DEPTH_CLEAR_VALUE glClearDepth
StencilClearValue GL_STENCIL_CLEAR_VALUE glClearStencil
GLRenderMode RenderMode GL_RENDER_MODE glRenderMode

光源

 static class MGL
 {
   public static readonly LightCollection Lights = new LightCollection();


   public class Light
   {
     public bool     IsEnabled { get; set; }
     public Color4f  Ambient { get; set; }
     public Color4f  Diffuse { get; set; }
     public Color4f  Specular { get; set; }
     public HmCod3f  Position { get; set; }
     public Vector3f SpotDirection { get; set; }
     public float    SpotExponent { get; set; }
     public Angle    SpotCutoff { get; set; }
     public float    ConstantAttenuation { get; set; }
     public float    LinearAttenuation { get; set; }
     public float    QuadraticAttenuation { get; set; }
   }

   public class LightCollection : IEnumerable<Light>
   {
     public int Count { get; }
     public Light this[int i] { get; }
   }
}

頂点配列

頂点のデータ型として使えるものを下記に示します。

  • Point3f
  • Point3d
  • PointNormal3f
  • PointNormal3d
  • PointNormalUV3f
  • PointUV3f
  • ColoredPoint3f
  • ColoredPointNormal3f

OpenGL の頂点配列には、大きく分けて次の2種類があります。

  • glDrawArrays() によるもの
  • glDrawElements() によるもの

MGL.DrawArrays( GLPrimType type, 頂点データ型[] points )

glDrawArrays によって頂点配列を描画します。 「頂点データ型」として上記のデータ型が使えるようにオーバーロードされています。

MGL.DrawArrays( GLPrimType type, 頂点データ型[] points, int[] indices )

glDrawElements によって頂点配列を描画します。

MGL.DrawArrays( 頂点データ型[] points, params Array<int>[] elements )

glDrawElements によって頂点配列を描画します。 MGL.Array<T> は次のように定義されています。

static class MGL
{
  public sealed class Array<T>
  {
    public readonly GLPrimType Type;
    public readonly T[] Data;
    public Array( GLPrimType type, params T[] data );
  }
}

Texture Mapping

MGL.ITexture

※ ほとんどの場合、次節の MGL.ITextureObject を利用するほうが適切です。

API
public static class MGL
{
  public interface ITexture : IContextual
  {
    GLTextureTarget Target { get; }
    uint Handle { get; }
    ITextureBinding Bind();
  }

  public static ITexture GenTexture( Bitmap bmp );
  ...
}
Usage
     var tex = MGL.GenTexture( bitmap );
     using ( var binding = tex.Bind() ) {
       binding.SetTexFilterParams( ... );  // 必要ならこういった設定を行う
       ... // ここに描画処理を書く
     }

ITexture はレンダリングコンテキストに依存しています。

GenTexture() を呼ぶと内部では glGenTextures() が呼び出され、そのとき有効なレンダリングコンテキストにテクスチャが生成されます。 そのテクスチャIDは Handle プロパティで取得できます。

ITexture のオブジェクトを使って複数のレンダリングコンテキストでテクスチャマッピングを行うことはできません。 3Dビューが複数あるアプリケーションの開発では注意して下さい。そういった場合は下記の ITextureObject を使用して下さい。

MGL.ITextureObject

API
public static class MGL
{
  public interface ITextureObject : IObject
  {
    new ITexture Current { get; }
    Size2i ImageSize { get; }
    ITextureBinding Bind();
  }
  public static ITextureObject CreateTextureObject( Bitmap bitmap );
  ...
}
Usage
     // 事前にテクスチャオブジェクトを生成しておく
     var tex = MGL.CreateTextureObject( bitmap );

     // 描画処理において(IScene.Draw メソッドの中など)次のように書く
     using ( var binding = tex.Bind() ) {
       binding.SetTexFilterParams( ... );  // 必要ならこういった設定を行う
       ... // ここに描画処理を書く
     }

ITextureObject は複数のレンダリングコンテキストにまたがって利用できます。 常に現在アクティブなレンダリングコンテキストを確認し、適切な ITexture を取得・生成してテクスチャマッピングを行います。 Current プロパティは、現在のレンダリングコンテキストに合わせた ITexture を返します。

Shader

Usage
     using MoNo.OpenGL;
     string shadersrcV = ...;  // source code of vertex shader
     string shadersrcF = ...;  // source code of fragment shader
     var program = MGL.CreateProgramObject( shadersrcV, shadersrcF );
     using ( var use = program.Use() ) {
       use.Uniform( "hoge", .. );  // pass uniform variables to shader program.
       ... // ここに描画処理を書く
     }
  • 代表的な使用例です。
  • なお、このコードでは CreateProgramObject() の直後に描画処理に入っていますが、毎フレームの描画のたびに CreateProgramObject() を呼び出す必要はありません。 事前に CreateProgramObject() してそのオブジェクトを保持しておけば、描画の際には Use() するだけでOKです。
  • シェーダーのソースコードは、DLLのリソースとして定義しておくのが良いでしょう。

各インターフェイス

※ RC = Rendering Context

RC非依存 RC依存 説明
IProgramObject IProgram 複数のシェーダーを束ねたもの。
IShaderObject IShader バーテックスシェーダー、フラグメントシェーダーなど

Example

let vs = """
#version 120
void main(void){ gl_Position=gl_ModelViewProjectionMatrix* gl_Vertex; }
"""

let fs = """
#version 120
void main (void) { gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0); }
"""

let program = MGL.CreateProgramObject(vs,fs)
let scene = GraphicsUT.CreateScene (fun sc ->
        use scope = sc.Push()
        use prog = program.Use()
        sc.DrawQuads (fun gl ->
          gl.Vertex( 0.0, 0.0, 0.0 )
          gl.Vertex( 1.0, 0.0, 0.0 )
          gl.Vertex( 1.0, 1.0, 0.0 )
          gl.Vertex( 0.0, 1.0, 0.0 )))

古いGLSL(version 120)で書いたほうがMVP行列の値が自動で受け継がれるので楽