PostProcessTest.cpp は以下のようになっています
#include "DxLib.h"
// 画面サイズ
#define SCREEN_W 640
#define SCREEN_H 480
// 画面テクスチャサイズ
#define SCREEN_TEXTURE_W 1024
#define SCREEN_TEXTURE_H 512
// ポストエフェクトの数
#define POSTEFFECT_NUM 2
// 普通に描画する画像のハンドル
int GraphHandle ;
// ピクセルシェーダーハンドル
int ColorBloomHPS ; // ブルームエフェクト用のガウシアンぼかし( 水平 )+輝度操作のピクセルシェーダー
int ColorBloomVPS ; // ブルームエフェクト用のガウシアンぼかし( 垂直 )+輝度操作のピクセルシェーダー
int ColorBrightPassPS ; // 一定の明るさ以下の部分を消すためのピクセルシェーダー
int ColorDownFilter4PS ; // 画像サイズを綺麗に4分の1にするためのピクセルシェーダー
int ColorUpFilter4PS ; // 画像サイズを4倍にする際に使用するピクセルシェーダー
int ColorCombine4PS ; // 画像サイズを4倍にしながらメイン画面と合成するためのピクセルシェーダー
int ColorGBlurHPS ; // ブラーエフェクト用のガウシアンぼかし( 水平 )のピクセルシェーダー
int ColorGBlurVPS ; // ブラーエフェクト用のガウシアンぼかし( 垂直 )のピクセルシェーダー
// 画面グラフィックハンドル
int MainScreenGraph ; // メインの描画先として使用するハンドル
int WorkScreenGraph[ 2 ] ; // ポストエフェクトの作業用に使用するハンドル
int TargetScreen ; // 描画結果を格納する WorkScreenGraph 配列の要素番号
int SourceScreen ; // 描画元となる WorkScreenGraph 配列の要素番号( -1 の場合は MainScreenGraph を使用する )
// ポリゴン描画用の頂点データ
VERTEX2DSHADER Vert[ 4 ] ;
// 描画元用のテクスチャ座標幅
float TexCoordW, TexCoordH ;
// ピクセルシェーダーを使ったポリゴンの描画とそれに付随する処理を行う関数
void RenderProcess( int PixelShader )
{
// 描画先をセット
SetDrawScreen( WorkScreenGraph[ TargetScreen ] ) ;
// 使用するテクスチャを0番にセット
// ( SourceScreen がマイナスの値だった場合はメインの描画用スクリーンを使用する )
SetUseTextureToShader( 0, SourceScreen < 0 ? MainScreenGraph : WorkScreenGraph[ SourceScreen ] ) ;
// メインの描画用スクリーンを1番にセット
SetUseTextureToShader( 1, MainScreenGraph ) ;
// 画面をクリア
ClearDrawScreen() ;
// 使用するピクセルシェーダーをセット
SetUsePixelShader( PixelShader ) ;
// 描画
DrawPrimitive2DToShader( Vert, 4, DX_PRIMTYPE_TRIANGLESTRIP ) ;
// 描画先を描画元の変更
TargetScreen = TargetScreen != 0 ? 0 : 1 ;
SourceScreen = SourceScreen != 0 ? 0 : 1 ;
}
// 指定の定数配列のテクスチャサイズで割ってからピクセルシェーダー定数にセットする関数
void SetupTexCoordConst( const char *ConstArrayName, int PixelShader )
{
const FLOAT4 *DefaultParam ;
FLOAT4 ConstParam ;
int ConstIndex ;
int ConstCount ;
int i ;
// 定数配列デフォルト値が格納されているアドレスを取得
DefaultParam = GetConstDefaultParamFToShader( ConstArrayName, PixelShader ) ;
// 定数配列が使用するシェーダー定数の番号を取得する
ConstIndex = GetConstIndexToShader( ConstArrayName, PixelShader ) ;
// 定数配列が使用するシェーダー定数の数を取得する
ConstCount = GetConstCountToShader( ConstArrayName, PixelShader ) ;
// 定数をセットする
for( i = 0 ; i < ConstCount ; i ++ )
{
// スライドさせる値をピクセル座標単位からテクスチャ座標単位に変換する
ConstParam.x = DefaultParam[ i ].x / SCREEN_TEXTURE_W ;
ConstParam.y = DefaultParam[ i ].y / SCREEN_TEXTURE_H ;
ConstParam.z = 0.0f ;
ConstParam.w = 0.0f ;
// 定数をセット
SetPSConstF( ConstIndex + i, ConstParam ) ;
}
}
// テクスチャ座標の更新
void UpdateTexCoord( float Scale )
{
// テクスチャ座標のスケーリング
TexCoordW *= Scale ;
TexCoordH *= Scale ;
// テクスチャ座標に反映
Vert[ 1 ].u = Vert[ 3 ].u = TexCoordW ;
Vert[ 2 ].v = Vert[ 3 ].v = TexCoordH ;
}
// 頂点座標の更新
void UpdatePosition( float Scale )
{
// 頂点座標のスケーリング
Vert[ 1 ].pos.x = ( Vert[ 1 ].pos.x + 0.5f ) * Scale - 0.5f ;
Vert[ 3 ].pos.x = ( Vert[ 3 ].pos.x + 0.5f ) * Scale - 0.5f ;
Vert[ 2 ].pos.y = ( Vert[ 2 ].pos.y + 0.5f ) * Scale - 0.5f ;
Vert[ 3 ].pos.y = ( Vert[ 3 ].pos.y + 0.5f ) * Scale - 0.5f ;
}
// ブルームフィルター( パス0、水平方向 )
void BloomHorizontal( float BloomScaleParam )
{
SetupTexCoordConst( "PixelKernel", ColorBloomHPS ) ; // テクスチャ座標に関係する定数配列のセットアップ
SetPSConstSF( GetConstIndexToShader( "BloomScale", ColorBloomHPS ), BloomScaleParam ) ; // BloomScale 定数のセット
RenderProcess( ColorBloomHPS ) ; // ポリゴンを描画
}
// ブルームフィルター( パス1、垂直方向 )
void BloomVertical( float BloomScaleParam )
{
SetupTexCoordConst( "PixelKernel", ColorBloomVPS ) ; // テクスチャ座標に関係する定数配列のセットアップ
SetPSConstSF( GetConstIndexToShader( "BloomScale", ColorBloomVPS ), BloomScaleParam ) ; // BloomScale 定数のセット
RenderProcess( ColorBloomVPS ) ; // ポリゴンを描画
}
// 明るい部分のみ残すフィルター
void BrightPass( void )
{
RenderProcess( ColorBrightPassPS ) ; // ポリゴンを描画
}
// 画像のサイズを綺麗に4分の1にするフィルター
void DownFilter4x( void )
{
float Scale = 0.25f ;
UpdatePosition( Scale ) ; // 頂点データのスケーリング
SetupTexCoordConst( "PixelCoordsDownFilter", ColorDownFilter4PS ) ; // テクスチャ座標に関係する定数配列のセットアップ
RenderProcess( ColorDownFilter4PS ) ; // ポリゴンを描画
UpdateTexCoord( Scale ) ; // テクスチャ座標の更新
}
// 4倍拡大フィルター
void UpFilter4x( void )
{
float Scale = 4.0f ;
UpdatePosition( Scale ) ; // 頂点データのスケーリング
RenderProcess( ColorUpFilter4PS ) ; // ポリゴンを描画
UpdateTexCoord( Scale ) ; // テクスチャ座標の更新
}
// 4倍拡大しながらメインの画面と合成するフィルター
void Combine4x( void )
{
float Scale = 4.0f ;
UpdatePosition( Scale ) ; // 頂点データのスケーリング
RenderProcess( ColorCombine4PS ) ; // ポリゴンを描画
UpdateTexCoord( Scale ) ; // テクスチャ座標の更新
}
// ブラーフィルター( パス0、水平方向 )
void GaussianBlurHorizontal( void )
{
SetupTexCoordConst( "PixelKernel", ColorGBlurHPS ) ; // テクスチャ座標に関係する定数配列のセットアップ
RenderProcess( ColorGBlurHPS ) ; // ポリゴンを描画
}
// ブラーフィルター( パス1、垂直方向 )
void GaussianBlurVertical( void )
{
SetupTexCoordConst( "PixelKernel", ColorGBlurVPS ) ; // テクスチャ座標に関係する定数配列のセットアップ
RenderProcess( ColorGBlurVPS ) ; // ポリゴンを描画
}
// WinMain
int WINAPI WinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow )
{
int PostEffectIndex ;
const char *PostEffectName[] = { "ブルームエフェクト", "ブラーエフェクト" } ;
int DisplayFPS ;
int FPSCount ;
LONGLONG StartTime ;
LONGLONG NowTime ;
float BloomScaleParam ;
// ウインドウモードで起動
ChangeWindowMode( TRUE );
// 画面モードの設定
SetGraphMode( SCREEN_W, SCREEN_H, 32 ) ;
// VSYNC待ちをしない
SetWaitVSyncFlag( FALSE ) ;
// ウインドウテキストを変更
SetWindowText( "PostEffectTest" ) ;
// DXライブラリの初期化
if( DxLib_Init() < 0 ) return -1;
// 画像の読み込み
GraphHandle = LoadGraph( "Test1.bmp" );
// ピクセルシェーダーバイナリコードの読み込み
ColorBloomHPS = LoadPixelShader( "PP_ColorBloomH.pso" ) ;
ColorBloomVPS = LoadPixelShader( "PP_ColorBloomV.pso" ) ;
ColorBrightPassPS = LoadPixelShader( "PP_ColorBrightPass.pso" ) ;
ColorDownFilter4PS = LoadPixelShader( "PP_ColorDownFilter4.pso" ) ;
ColorUpFilter4PS = LoadPixelShader( "PP_ColorUpFilter4.pso" ) ;
ColorCombine4PS = LoadPixelShader( "PP_ColorCombine4.pso" ) ;
ColorGBlurHPS = LoadPixelShader( "PP_ColorGBlurH.pso" ) ;
ColorGBlurVPS = LoadPixelShader( "PP_ColorGBlurV.pso" ) ;
// 描画用のスクリーンを作成
MainScreenGraph = MakeScreen( SCREEN_TEXTURE_W, SCREEN_TEXTURE_H ) ; // メインの描画用のスクリーンの作成
WorkScreenGraph[ 0 ] = MakeScreen( SCREEN_TEXTURE_W, SCREEN_TEXTURE_H ) ; // 作業用画面を二つ作る用意しておく
WorkScreenGraph[ 1 ] = MakeScreen( SCREEN_TEXTURE_W, SCREEN_TEXTURE_H ) ;
// 表示モードはブルームにしておく
PostEffectIndex = 0 ;
// ブルームの輝度補正値の値を初期化
BloomScaleParam = 1.5f ;
// フレームレート表示関係の初期化
StartTime = GetNowHiPerformanceCount() ;
DisplayFPS = 0 ;
FPSCount = 0 ;
// メインループ
while( ProcessMessage() == 0 )
{
// キー入力操作
switch( GetInputChar( TRUE ) )
{
case CTRL_CODE_UP :
PostEffectIndex ++ ;
if( PostEffectIndex == POSTEFFECT_NUM )
{
PostEffectIndex = 0 ;
}
break ;
case CTRL_CODE_DOWN :
PostEffectIndex -- ;
if( PostEffectIndex < 0 )
{
PostEffectIndex = POSTEFFECT_NUM - 1 ;
}
break ;
case CTRL_CODE_LEFT :
BloomScaleParam -= 0.1f ;
break ;
case CTRL_CODE_RIGHT :
BloomScaleParam += 0.1f ;
break ;
}
// メインの描画用スクリーンを描画先に変更
SetDrawScreen( MainScreenGraph ) ;
// 画面の初期化
ClearDrawScreen() ;
// 黄色い丸を描画
DrawCircle( 200, 240, 128, GetColor( 255,255,0 ), TRUE ) ;
// 画像を描画
DrawGraph( 400, 0, GraphHandle, TRUE ) ;
// テクスチャ座標幅を初期化
TexCoordW = ( float )SCREEN_W / SCREEN_TEXTURE_W ;
TexCoordH = ( float )SCREEN_H / SCREEN_TEXTURE_H ;
// 頂点データの初期化
Vert[ 0 ].pos = VGet( - 0.5f, - 0.5f, 0.0f ) ;
Vert[ 1 ].pos = VGet( SCREEN_W - 0.5f, - 0.5f, 0.0f ) ;
Vert[ 2 ].pos = VGet( - 0.5f, SCREEN_H - 0.5f, 0.0f ) ;
Vert[ 3 ].pos = VGet( SCREEN_W - 0.5f, SCREEN_H - 0.5f, 0.0f ) ;
Vert[ 0 ].dif = GetColorU8( 255,255,255,255 ) ;
Vert[ 0 ].spc = GetColorU8( 0,0,0,0 ) ;
Vert[ 0 ].u = 0.0f ; Vert[ 0 ].v = 0.0f ;
Vert[ 1 ].u = TexCoordW ; Vert[ 1 ].v = 0.0f ;
Vert[ 2 ].u = 0.0f ; Vert[ 2 ].v = TexCoordH ;
Vert[ 3 ].u = TexCoordW ; Vert[ 3 ].v = TexCoordH ;
Vert[ 0 ].su = 0.0f ; Vert[ 0 ].sv = 0.0f ;
Vert[ 1 ].su = TexCoordW ; Vert[ 1 ].sv = 0.0f ;
Vert[ 2 ].su = 0.0f ; Vert[ 2 ].sv = TexCoordH ;
Vert[ 3 ].su = TexCoordW ; Vert[ 3 ].sv = TexCoordH ;
Vert[ 0 ].rhw = 1.0f ;
Vert[ 1 ].rhw = 1.0f ;
Vert[ 2 ].rhw = 1.0f ;
Vert[ 3 ].rhw = 1.0f ;
// バイリニア補間描画をする
SetDrawMode( DX_DRAWMODE_BILINEAR ) ;
// 描画先画面の情報と描画元画面の情報を初期化
TargetScreen = 0 ;
SourceScreen = -1 ;
// 描画モードによって処理を分岐
switch( PostEffectIndex )
{
case 0 :
// ブルームポストエフェクト
DownFilter4x() ; // 画面画像を4分の1にする
DownFilter4x() ; // 更に4分の1で合わせて16分の1
BrightPass() ; // 明るい部分だけを残す
BloomHorizontal( BloomScaleParam ) ; // 横方向にぼかしながら若干明るくする
BloomVertical( BloomScaleParam ) ; // 縦方向にぼかしながら若干明るくする
BloomHorizontal( BloomScaleParam ) ; // もう一回やって更にぼかし+明るくする、横方向
BloomVertical( BloomScaleParam ) ; // 縦方向
UpFilter4x() ; // 4倍拡大
Combine4x() ; // 更に4倍拡大すると同時にメインの描画用スクリーンと合成
break ;
case 1 :
// ブラーポストエフェクト
DownFilter4x() ; // 画面画像を4分の1にする
GaussianBlurHorizontal() ; // 横方向にぼかす
GaussianBlurVertical() ; // 縦方向にぼかす
GaussianBlurHorizontal() ; // もう一回やって更にぼかす、横方向
GaussianBlurVertical() ; // 縦方向
UpFilter4x() ; // 4倍拡大
break ;
}
// 描画先を裏画面に変更
SetDrawScreen( DX_SCREEN_BACK ) ;
// 次に描画元になるはずだった作業画面を画面に描画
DrawGraph( 0, 0, WorkScreenGraph[ SourceScreen ], FALSE ) ;
// モードを描画
DrawFormatString( 0, 0, GetColor( 255,255,255 ), "%s", PostEffectName[ PostEffectIndex ] ) ;
// FPSを描画
DrawFormatString( 0, 16, GetColor( 255,255,255 ), "FPS:%d", DisplayFPS ) ;
// ブルームエフェクトの輝度パラメータを描画
DrawFormatString( 0, 40, GetColor( 255,255,255 ), "BloomScaleParam:%.1f", BloomScaleParam ) ;
// 操作説明を描画
DrawString( 0, 64, "上下キーでポストエフェクトを切り替えます", GetColor( 255,255,255 ) ) ;
DrawString( 0, 80, "左右キーでブルームエフェクトの輝度パラメータを変更します", GetColor( 255,255,255 ) ) ;
// 裏画面の内容を表画面に変更
ScreenFlip() ;
// フレームレート処理
FPSCount ++ ;
NowTime = GetNowHiPerformanceCount() ;
if( NowTime - StartTime > 1000000 )
{
DisplayFPS = FPSCount ;
FPSCount = 0 ;
StartTime = NowTime ;
}
}
// DXライブラリの後始末
DxLib_End();
// ソフトの終了
return 0;
}
実行すると最初は Bloom エフェクトが実行されます、上下キーで Blur エフェクトと切り替えることが出来ます
case 0:
// ブルームポストエフェクト
DownFilter4x() ; // 画面画像を4分の1にする
DownFilter4x() ; // 更に4分の1で合わせて16分の1
BrightPass() ; // 明るい部分だけを残す
BloomHorizontal( BloomScaleParam ) ; // 横方向にぼかしながら若干明るくする
BloomVertical( BloomScaleParam ) ; // 縦方向にぼかしながら若干明るくする
BloomHorizontal( BloomScaleParam ) ; // もう一回やって更にぼかし+明るくする、横方向
BloomVertical( BloomScaleParam ) ; // 縦方向
UpFilter4x() ; // 4倍拡大
Combine4x() ; // 更に4倍拡大すると同時にメインの描画用スクリーンと合成
break ;
case 1 :
// ブラーポストエフェクト
DownFilter4x() ; // 画面画像を4分の1にする
GaussianBlurHorizontal() ; // 横方向にぼかす
GaussianBlurVertical() ; // 縦方向にぼかす
GaussianBlurHorizontal() ; // もう一回やって更にぼかす、横方向
GaussianBlurVertical() ; // 縦方向
UpFilter4x() ; // 4倍拡大
break ;
この部分で実際に PostProcess を行っています、各関数内では別々のピクセルシェーダーを使って
画面全体にフィルター処理を行っていますので、Bloom エフェクトを実現するために6種類のピクセルシェーダーを使って
画面全体を9回描画していることになります
ピクセルシェーダーのコードが長ければ長いほど1ピクセルの描画に掛かる時間も長くなるので、ただ単に画面全体に
画面と同サイズの画像を描画するのに比べてかなり負荷の大きい処理となります
( また、Blur エフェクトの方が Bloom エフェクトよりパス数( 工程数 )が少ないので、FPS も Blur エフェクトの方が高くなります )
Bloom エフェクトを選択している際に左右キーを押すと BloomHorizontal と BloomVertical のシェーダー内で使用されている
BloomScaleParam 定数の値を変更することが出来ます、BloomScaleParam の値を弄ることで描画結果がどのように変化するか確認してみてください
その他、解説が必要そうな部分について全部解説を書くと結構な量になってしまいそうですので、
シェーダーコードやプログラムや注釈を読むだけでは不明な点がありましたらお訊ねください m(_ _;m
# 18:45にプログラムの一部を修正しました
# ( テクスチャサイズを SCREEN_TEXTURE_W と SCREEN_TEXTURE_H で明示的に指定するようにしました )