#region OpenTK

OpenTKがGitHubで更新されるようになりました。NuGetから手に入れることができるようになりました。
現在は.NET Coreに対応する機運があるようです。

アルファブレンディング

後ろが透けて見える

アルファブレンディングという言葉だけでは、もしかしたら難しく聞こえるかもしれませんが、ただ単純に半透明のポリゴンを描画してやるということです。

普段はちょっとOpenGLの設定をしたらすぐに意識しなくなりますが、あえていろいろなバリエーションを動作させてみたいと思います。

「アルファ(α)」とは何か

グラフィックでαというのは、通常は色の要素の一つを指します。色は赤(R)、緑(G)、青(B)で表されますが、ここに透明度としてアルファ(α)を加えます。

αとは言いますが、プログラミングで用いられる場合はAという要素名になることが多いようです。

OpenTKにはColor4という構造体が用意されており、アルファ値も色として含まれています。

GL.Beginを使った描画にはColor4とColor3がありますが、これには前者がα有り、後者がα無しという違いがあります。

Color4構造体の静的プロパティを使う場合、Transparent以外はすべてα値が255(つまり不透明)のため注意してください。

アルファブレンディングの種類

一口にアルファブレンディングと言っても、いくつか種類があります。

ブレンディング無し
アルファブレンディングをオフにしている場合
計算式:Color=Src=Src×[1]+Dest×[0]
半透明合成
普段良く使うブレンディング
計算式:Color=Src×[Src.A]+Dest×[1-Src.A]
加算合成
炎の表示などに光が集まるような場合に使うブレンディング
計算式:Color=Src×Src.A+Dest=Src×[Src.A]+Dest×[1]
減算合成
ダークな表示をさせるときに使うブレンディング(らしい)
計算式:Color=-Src×Src.A+Dest=-Src×[Src.A]+Dest×[1]
乗算合成
カラーセロハンを使ったようなブレンディング 画像加工にも
計算式:Color=Src×Dest=Src×[Dest]+Dest×[0]=Src[0]+Dest×[Src]
反転合成
フィルムカメラのネガのようになるブレンディング
計算式:Color=Src×(1.0-Dest)=Src×[1-Dest]+Dest×[0]

※Srcは上から重ねる色、Destは元の色、Src.AとDest.Aはそれぞれのアルファ値です。

プログラム

AlphaBlending
通常の合成

ソースコードは、合成方法の指定に注目してください。

		//ウィンドウの起動時に実行される。
		protected override void OnLoad(EventArgs e)
		{
			base.OnLoad(e);

			GL.ClearColor(Color4.Gray);
			GL.Enable(EnableCap.DepthTest);

			//裏面削除、反時計回りが表でカリング
			GL.Enable(EnableCap.CullFace);
			GL.CullFace(CullFaceMode.Back);
			GL.FrontFace(FrontFaceDirection.Ccw);

			//ライティングON Light0を有効化
			GL.Enable(EnableCap.Lighting);
			GL.Enable(EnableCap.Light0);

			//法線の正規化
			GL.Enable(EnableCap.Normalize);

			//色を材質に変換
			GL.Enable(EnableCap.ColorMaterial);
			GL.ColorMaterial(MaterialFace.Front, ColorMaterialParameter.Diffuse);

			//アルファブレンディングの設定
			GL.Enable(EnableCap.Blend);
			GL.BlendFunc(BlendingFactorSrc.SrcAlpha, BlendingFactorDest.OneMinusSrcAlpha);

			torus.CreateBuffers();
			sphere.CreateBuffers();
			plate.CreateBuffers();
		}

まずは起動時に、アルファブレンディングを許可します(178行目)。そして、どのような式でブレンドするかを指定します(179行目)。指定しない場合はブレンドしない場合と同じになります。

			//ブレンディング無し
			if (Keyboard[Key.Z])
			{
				this.Title = "1-9:Alpha Blending(ブレンディング無し)";

				GL.BlendEquation(BlendEquationMode.FuncAdd);//デフォルトに戻す
				//色 = 前景 * (1.0) + 背景 * (0.0);
				GL.BlendFunc(BlendingFactorSrc.One, BlendingFactorDest.Zero);
			}

			//半透明合成
			if (Keyboard[Key.X])
			{
				this.Title = "1-9:Alpha Blending(半透明合成)";

				GL.BlendEquation(BlendEquationMode.FuncAdd);//デフォルトに戻す
				//色 = 前景 * (前景のアルファ値) + 背景 * (1.0-背景のアルファ値)
				GL.BlendFunc(BlendingFactorSrc.SrcAlpha, BlendingFactorDest.OneMinusSrcAlpha);
			}

			//加算合成
			if (Keyboard[Key.C])
			{
				this.Title = "1-9:Alpha Blending(加算合成)";

				GL.BlendEquation(BlendEquationMode.FuncAdd);//デフォルトに戻す
				//色 = 前景 * (前景のアルファ値) + 背景 * (1.0)
				GL.BlendFunc(BlendingFactorSrc.SrcAlpha, BlendingFactorDest.One);
			}

			//減算合成
			if (Keyboard[Key.V])
			{
				this.Title = "1-9:Alpha Blending(減算合成)";

				//色 = - 前景 * (前景のアルファ値) + 背景 * (1.0)
				GL.BlendEquation(BlendEquationMode.FuncReverseSubtract);
				GL.BlendFunc(BlendingFactorSrc.SrcAlpha, BlendingFactorDest.One);
			}

			//乗算合成
			if (Keyboard[Key.B])
			{
				this.Title = "1-9:Alpha Blending(乗算合成)";

				GL.BlendEquation(BlendEquationMode.FuncAdd);//デフォルトに戻す
				//色 = 前景 * (0.0) + 背景 * 前景
				GL.BlendFunc(BlendingFactorSrc.Zero, BlendingFactorDest.SrcColor);
			}

			//反転合成
			if (Keyboard[Key.N])
			{
				this.Title = "1-9:Alpha Blending(反転合成)";

				GL.BlendEquation(BlendEquationMode.FuncAdd);//デフォルトに戻す
				//色 = 前景 * (1.0 - 背景) + 背景 * (0.0)
				GL.BlendFunc(BlendingFactorSrc.OneMinusDstColor, BlendingFactorDest.Zero);
			}

それぞれの合成を行うためのBlendFunc関数は上記の通りになります。

例外として、減算合成の場合はBlendEquation関数をいじる必要があります。257行目の意味は「背景の処理結果から前景の処理結果を引け」というものですが、通常の描画で使うことは少ないと思うため補足でパラメータを列挙しておきます。

それぞれの合成の結果は下のようになります。

AlphaBlending1AlphaBlending2AlphaBlending3AlphaBlending4AlphaBlending5AlphaBlending6

よく出てくる問題

			GL.MatrixMode(MatrixMode.Modelview);

			plate.Draw();

			//モデル描画
			GL.PushMatrix();
			GL.Translate(-1.5f, 0.0f, 0.0f);
			GL.Rotate(angle, Vector3.UnitZ);

			torus.Draw();

			GL.PopMatrix();
			GL.PushMatrix();
			GL.Translate(1.5f, 0.0f, 0.0f);
			GL.Rotate(angle, Vector3.UnitY);

			sphere.Draw();

			GL.PopMatrix();

BadAlphaBlending
悪い例(わかりやすいように球のアルファ値を1.0にしています。)

この画像はほぼ同じプログラムを動作させたものですが、青い物体の後ろにあるべき球の色がありません。これは設定した描画順と、視点と物体の間の距離が違うということで起きるものです。

この現象を簡単に防ぐためには、透明ポリゴンを最後に描画したり、透明ポリゴンを描画する間は深度テスト(描画するポリゴンを遮るポリゴンがあるかどうかの判定)をやめたりするなどの処理が必要になります。

高度な予防法としては、マルチサンプリングやA-Bufferなどの方法があるようです。

補足:GL.BlendEquation関数に渡すパラメータ一覧

BlendEquationMode列挙体

FuncAdd
通常の指定 前景と背景の処理結果の単純な足し算
FuncSubtract
前景の処理結果から背景の処理結果を引く
FuncReverseSubtract
背景の処理結果から前景の処理結果を引く
Min
前景と背景の処理結果の小さい方を選択
Max
前景と背景の処理結果の大きい方を選択

もし画像処理をする場合は、上の指定を組み合わせることになるかもしれません。

記事

外部リンク

Thanks