マテリアルによる半透明処理
マテリアルを使用した半透明処理を行います。今回は簡単な距離判定を行い、反対側から見ても問題なく透けて見えるようにしています。

下のリンクから今回のプロジェクトをダウンロードできます。
今回のメインコードファイルを載せます。重要なコードを赤色で表示させています。
MainSample.cs
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using Microsoft.DirectX;
using Microsoft.DirectX.Direct3D;
namespace MDXSample
{
/// <summary>
///
/// </summary>
public partial class MainSample : IDisposable
{
/// <summary>
/// ティーポットメッシュ
/// </summary>
private Mesh _mesh = null;
/// <summary>
/// 青ティーポットの位置
/// </summary>
private Vector3 _blueTeapotPosition = new Vector3(1.0f, 0.0f, 2.0f);
/// <summary>
/// 赤ティーポットの位置
/// </summary>
private Vector3 _redTeapotPosition = new Vector3(0.0f, 0.0f, 0.0f);
/// <summary>
/// 青ティーポットのマテリアル
/// </summary>
private Material _blueTeapotMaterial;
/// <summary>
/// 青ティーポットのマテリアル
/// </summary>
private Material _redTeapotMaterial;
/// <summary>
///
/// </summary>
/// <param name="topLevelForm"></param>
/// <returns></returns>
/// <remarks>
///
/// </remarks>
public bool InitializeApplication(MainForm topLevelForm)
{
this._form = topLevelForm;
this.CreateInputEvent(topLevelForm);
try
{
this.CreateDevice(topLevelForm);
this.CreateFont();
}
catch (DirectXException ex)
{
MessageBox.Show(ex.ToString(), "エラー", MessageBoxButtons.OK, MessageBoxIcon.Error);
return false;
}
this.CreateXYZLine();
this._mesh = Mesh.Teapot(this._device);
// 青のマテリアル
this._blueTeapotMaterial.Diffuse = Color.FromArgb(192, 64, 64, 255);
this._blueTeapotMaterial.Ambient = Color.FromArgb(192, 64, 64, 64);
// 赤のマテリアル
this._redTeapotMaterial.Diffuse = Color.FromArgb(192, 255, 64, 64);
this._redTeapotMaterial.Ambient = Color.FromArgb(192, 64, 64, 64);
// ライトを設定
this.SettingLight();
// アルファブレンディング方法を設定
this._device.RenderState.SourceBlend = Blend.SourceAlpha;
this._device.RenderState.DestinationBlend = Blend.InvSourceAlpha;
// アルファブレンディングを有効にする
this._device.RenderState.AlphaBlendEnable = true;
return true;
}
/// <summary>
///
/// </summary>
public void MainLoop()
{
this.SettingCamera();
this._device.Clear(ClearFlags.ZBuffer | ClearFlags.Target, Color.SkyBlue, 1.0f, 0);
this._device.BeginScene();
this._device.RenderState.Lighting = false;
this._device.SetTransform(TransformType.World, Matrix.Identity);
this.RenderXYZLine();
this._device.RenderState.Lighting = true;
// レンズの位置を計算
float radius = this._lensPosRadius;
float theta = Geometry.DegreeToRadian(this._lensPosTheta);
float fai = Geometry.DegreeToRadian(this._lensPosPhi);
Vector3 lensPosition = new Vector3(
(float)(radius * Math.Cos(theta) * Math.Cos(fai)),
(float)(radius * Math.Sin(fai)),
(float)(radius * Math.Sin(theta) * Math.Cos(fai)));
// どちらが近いか比較
if (Vector3.Length(lensPosition - this._redTeapotPosition) <
Vector3.Length(lensPosition - this._blueTeapotPosition))
{
// 赤のティーポットの方が近い場合、青のティーポットから描画
this.RenderTeapot(this._blueTeapotMaterial, this._blueTeapotPosition);
this.RenderTeapot(this._redTeapotMaterial, this._redTeapotPosition);
}
else
{
// 青のティーポットの方が近い、赤のティーポットから描画
this.RenderTeapot(this._redTeapotMaterial, this._redTeapotPosition);
this.RenderTeapot(this._blueTeapotMaterial, this._blueTeapotPosition);
}
this._font.DrawText(null, "[Escape]終了", 0, 0, Color.White);
this._font.DrawText(null, "θ:" + this._lensPosTheta, 0, 12, Color.White);
this._font.DrawText(null, "φ:" + this._lensPosPhi, 0, 24, Color.White);
this._device.EndScene();
this._device.Present();
if (this._keys[(int)Keys.Escape])
{
this._form.Close();
}
}
/// <summary>
/// ティーポットの描画
/// </summary>
/// <param name="material">マテリアル</param>
/// <param name="position">位置</param>
private void RenderTeapot(Material material, Vector3 position)
{
// マテリアル
this._device.Material = material;
// 座標変換
this._device.SetTransform(TransformType.World, Matrix.Translation(position));
// ティーポットを描画
this._mesh.DrawSubset(0);
}
/// <summary>
///
/// </summary>
public void Dispose()
{
if (this._mesh != null)
{
this._mesh.Dispose();
}
this.DisposeResource();
}
}
}
|
では、赤文字の部分を説明していきます。MainSamplePartial.cs ファイルのコードはこちらです。
/// <summary>
///
/// </summary>
private Mesh _mesh = null;
/// <summary>
///
/// </summary>
private Vector3 _blueTeapotPosition = new Vector3(1.0f, 0.0f, 2.0f);
/// <summary>
///
/// </summary>
private Vector3 _redTeapotPosition = new Vector3(0.0f, 0.0f, 0.0f);
/// <summary>
///
/// </summary>
private Material _blueTeapotMaterial;
/// <summary>
////
/// </summary>
private Material _redTeapotMaterial;
|
今回は2つの色違いティーポットを描画して半透明が行われているかをチェックします。ここではメッシュと、各ティーポットの位置とマテリアルの情報を持つようにします。
位置に関しては動かないのでここで設定しておきます。マテリアルはここでは設定できないので、初期化メソッド内で設定するようにしています。
this._blueTeapotMaterial.Diffuse = Color.FromArgb(192, 64, 64, 255);
this._blueTeapotMaterial.Ambient = Color.FromArgb(192, 64, 64, 64);
this._redTeapotMaterial.Diffuse = Color.FromArgb(192, 255, 64, 64);
this._redTeapotMaterial.Ambient = Color.FromArgb(192, 64, 64, 64);
|
ここでマテリアルの設定を行っています。前回頂点データによる半透明処理では頂点データのディフューズ色にアルファ値を設定していましたが、マテリアルの場合も同じようにマテリアルにアルファ値を設定します。基本的に違いはこれだけです。
this.SettingLight();
this._device.RenderState.SourceBlend = Blend.SourceAlpha;
this._device.RenderState.DestinationBlend = Blend.InvSourceAlpha;
this._device.RenderState.AlphaBlendEnable = true;
|
マテリアルを使用するので同時にライトの設定も行います。前回同様アルファブレンディングも有効にしておきます。
float radius = this._lensPosRadius;
float theta = Geometry.DegreeToRadian(this._lensPosTheta);
float fai = Geometry.DegreeToRadian(this._lensPosPhi);
Vector3 lensPosition = new Vector3(
(float)(radius * Math.Cos(theta) * Math.Cos(fai)),
(float)(radius * Math.Sin(fai)),
(float)(radius * Math.Sin(theta) * Math.Cos(fai)));
|
後はメッシュを描画すれば前回同様、半透明に見えるのですが、それだとあまりにも簡単すぎるので今回はカメラの方向を変えても奥のポリゴン(ティーポット)から描画できるように簡単な距離判定を行います。
まずカメラの位置を使わないといけないので、あらかじめ計算しておきます。実際にカメラのレンズ位置を計算する式とまったく同じです。
if (Vector3.Length(lensPosition - this._redTeapotPosition) <
Vector3.Length(lensPosition - this._blueTeapotPosition))
{
this.RenderTeapot(this._blueTeapotMaterial, this._blueTeapotPosition);
this.RenderTeapot(this._redTeapotMaterial, this._redTeapotPosition);
}
else
{
this.RenderTeapot(this._redTeapotMaterial, this._redTeapotPosition);
this.RenderTeapot(this._blueTeapotMaterial, this._blueTeapotPosition);
}
|
レンズの位置を求めたら、各ティーポットとの距離を計算します。
ベクトルから距離を求めるには「Vector3.Length」メソッドで求めることが出来ます。
距離を求めるには、青のティーポットの図のように「原点からカメラの位置へのベクトル」から「原点からティーポットの位置へのベクトル」分を引けば「ティーポットからカメラの位置へのベクトル」が求まるのでその長さを計算します。
赤のティーポットに関しては原点位置にあるので、カメラ位置へのベクトルをそのまま使用すればいいのですが、念のため計算しています。
赤のティーポットの位置は原点なので引いても値は変化しない
2つの距離が求まったら長さの判定を行い、「赤」のティーポットが近いなら遠くにある「青」のティーポットから順番に描画します。「青」のティーポットが近いなら遠くにある「赤」のティーポットから順番に描画します。
そうするとわざわざZバッファを無効にしなくてもうまく半透明処理が出来ていることがわかります。(ちなみに暗いのはライトの関係です)
ただ、今回はソートしているわけではない��で、��際の����グ��ミン��で����描画順リスト���を作成���てソ��トするのが普通だ���思います。それは���自作ってみてください。
「RenderTeapot」メソッドは描画処理をまとめるために作成したものであり、マテリアルと位置を引数として渡せばティーポットを描画するようになっています。(次セクション参照)
/// <summary>
///
/// </summary>
/// <param name="material"></param>
/// <param name="position"></param>
private void RenderTeapot(Material material, Vector3 position)
{
this._device.Material = material;
this._device.SetTransform(TransformType.World, Matrix.Translation(position));
this._mesh.DrawSubset(0);
}
|
渡されたマテリアルと位置によってティーポットを描画するメソッドです。何か特別な処理をしているわけではありません。
|