見出し画像

【MMEの作り方】ポストエフェクト編01/ポストエフェクトの基礎知識

▼今回の記事ページで使うサンプルファイル

 今回の記事ぺージの解説で使うサンプルとなるfxファイルは以下のリンクからダウンロードできます。
 ただし、ファイルをDL・使用した際にトラブル・不利益が生じたとしても筆者は責任を負わないものとします。自己責任で御利用ください。

▼ポストエフェクトの基本原理

 ポストエフェクトとは、画面全体に対してエフェクトをかけるタイプのMMEエフェクトのことをいいます。
 ポストエフェクトはオブジェクトエフェクトのときと同様に、fxファイルと同名のXファイルをインポートすることでMMD内に割り当てます。

 ポストエフェクトがどのように動いているかと言うと『画面全体に対してエフェクトをかける』という動作から分かる通り、まず画面に映っているものを一旦仮想のテクスチャとして扱います。
 その上で、その仮想のテクスチャを加工してスクリーン上に出力します。

▼ポストエフェクト用のfxファイルの中身

 今回ポストエフェクトに関して、まずは画面全体をグレーにするエフェクトを作ってみました。
 サンプルファイル『3-1-1_PE_Gray.x』をMMDに読み込んでみてください。
 なお画面内で使われている神社の背景モデルは当方のBOOTHショップ【プリメロ工房】で絶賛配布中です(ダイレクトマーケティング)
【リンク】神社ステージ

画面をグレー化するポストエフェクト

 色々書いてありますが、『画面をグレーにする』という処理の直接かかわっているのはピクセルシェーダーの以下の部分になります。

float4 PS_DrawBuffer(float2 Tex : TEXCOORD0) : COLOR
{   
	// テクスチャ化したスクリーン画像を取り出す
	float4 Color = tex2D(ScnSamp, Tex);

    // 色に関する処理(このファイルではモノトーンにする)
	float Gray = (Color.r + Color.g + Color.b) / 3;
	float4 ResultColor = float4(Gray, Gray, Gray, 1);
    
    return ResultColor;
}

 今回のグレー化に関してはシェーダーエフェクトやオブジェクトエフェクトのときと同じく、RGBAの値を数式を使って操作しているだけで極シンプルなものです。

 では、ポストエフェクトに関する他の処理を見ていきましょう。

// 背景のクリア値
float4 ClearColor = {1,1,1,0};
float  ClearDepth  = 1.0;

// ポストエフェクトでの必須設定
float Script : STANDARDSGLOBAL <
    string ScriptOutput = "color";
    string ScriptClass = "scene";
    string ScriptOrder = "postprocess";
> = 0.8;

// オリジナルの描画結果を記録するためのレンダーターゲット
texture ScnMap : RENDERCOLORTARGET <
    float2 ViewPortRatio = {1.0,1.0};
>;
sampler ScnSamp = sampler_state {
    texture = <ScnMap>;
    MinFilter = LINEAR;
    MagFilter = LINEAR;
    MipFilter = NONE;
};

// 深度バッファ
texture DepthBuffer : RENDERDEPTHSTENCILTARGET <
    float2 ViewPortRatio = {1.0,1.0};
>;

// スクリーンサイズ
float2 ViewportSize : VIEWPORTPIXELSIZE;

// 半ピクセル
static float2 ViewportOffset = (float2(0.5,0.5)/ViewportSize);

 色々とゴチャゴチャと書いてはありますが、ここに書かれていることはポストエフェクトを使うときの定型文みたいなものです。
 特に内容は理解しないでも、もし、ポストエフェクトを新規に作成したいなら上記のコードをコピペで動かすことは可能です。
 とはいえ、それだけだと解説として味気ないので詳細について説明します。

float4 ClearColor = {1,1,1,0};
float  ClearDepth  = 1.0;

これは レンダーターゲットを初期化(クリア)するときの色と深度を指定しています。

◎ClearColor
背景を塗りつぶす色。
{R,G,B,A} で指定。ここでは白 (1,1,1)と透明度0」。

◎ClearDepth
深度バッファをクリアする値(1.0=最も遠い距離)。
つまり白背景で深度をリセットするという意味です。

float Script : STANDARDSGLOBAL <
    string ScriptOutput = "color";
    string ScriptClass = "scene";
    string ScriptOrder = "postprocess";
> = 0.8;

 これは MMEがこのシェーダーをどのタイミングで実行するかを指定するメタ情報 です。
 Script というダミー変数に付けられたメタタグ(< ... >)が重要です。

◎ScriptOutput = "color"
 出力するのはカラー画像(画面の色)。深度や法線ではない。

◎ScriptClass = "scene"
 シーン全体に対して適用(モデル個別ではなく、画面全体)。

◎ScriptOrder = "postprocess"
 描画後(Post Process)に適用される。

◎= 0.8
 ダミーの値で数値に特に意味はありません。
 変数宣言上必要なので書いてあるだけです。

texture ScnMap : RENDERCOLORTARGET <
    float2 ViewPortRatio = {1.0,1.0};
>;
sampler ScnSamp = sampler_state {
    texture = <ScnMap>;
    MinFilter = LINEAR;
    MagFilter = LINEAR;
    MipFilter = NONE;
};

これは、ポストエフェクトの元画像(MMDの通常描画結果)をシェーダーで使えるようにしています。

texture DepthBuffer : RENDERDEPTHSTENCILTARGET <
    float2 ViewPortRatio = {1.0,1.0};

 深度バッファ(DepthBuffer)=各ピクセルの「カメラからの距離」情報を持つテクスチャ。
『RENDERDEPTHSTENCILTARGET』によって、MMDのレンダリング時に深度情報をキャプチャします。

float2 ViewportSize : VIEWPORTPIXELSIZE;
static float2 ViewportOffset = (float2(0.5,0.5)/ViewportSize);

 これは、UV座標(0~1)と実際のピクセル位置のずれを補正するための値です。

◎ViewportSize
 MMDの画面解像度(ピクセル単位)。
 例:(1920, 1080) など。

◎ViewportOffset
 テクスチャ座標はピクセルの中心ではなく「端」に基準があるため、
 サンプリング位置を半ピクセルずらして正確な色を取得します。
 これは DirectX9特有の座標補正です。
 これを、

float2 uv = (IN.Tex + ViewportOffset);

 のように使うことで、にじみやずれを防ぎます。

▼画面をモザイクにするポストエフェクト

 ポストエフェクトはスクリーンの色を変えるだけでなく、スクリーン座標を仮想的なUV座標とすることでの処理を加えることができます。
 サンプルファイル『3-1-2_PE_GrayAndMosaic.x』をMMDに読み込んでみてください。
 上記の画面をグレー化する処理に加えて、画面をモザイク化する処理も実装しています。

【img3-1-2-1】画面をモザイク化するポストエフェクト

 画面のモザイク化に関わる処理はコードの下記の部分です。

// テクスチャ化したスクリーン画像をモザイク化させる処理

float MosaicFineness = 100; // モザイクの細かさ

// モザイクタイルが正方形になる調整をしている
float MosaicFineness_X = MosaicFineness * ( ViewportSize.x / ViewportSize.y ); 
float MosaicFineness_Y = MosaicFineness;

// UV座標を離散化してモザイク化している
float Tex_x = floor(Tex.x * MosaicFineness_X) / MosaicFineness_X;
float Tex_y = floor(Tex.y * MosaicFineness_Y) / MosaicFineness_Y;
float2 Tex_Mosaic = float2(Tex_x, Tex_y);

float4 Color = tex2D( ScnSamp, Tex_Mosaic );

 色々と書いてありますが、やっていることの原理としては『シェーダーエフェクト編04/ノイズの作り方の基礎』で紹介したブロックノイズと同じです。
 floor演算子を使うことで本来グラデーションになっているUV座標を離散化しています。

画面を離散化する

目次ぺージに戻る

いいなと思ったら応援しよう!