みけCATのにっき(仮)
つれづれなるまゝに、日くらし、PCにむかひて、心に移りゆくよしなし事を、そこはかとなく書きつくれば、あやしうこそものぐるほしけれ。
(本当か!?)
出典

シェーダーとやらを使ってみた

アバター
みけCAT
記事: 6734
登録日時: 14年前
住所: 千葉県
連絡を取る:

シェーダーとやらを使ってみた

投稿記事 by みけCAT » 12年前

DXライブラリには、よく知られているのかはわかりませんが、シェーダー(プログラマブルシェーダー)という機能があります。
これを使うと、様々な画像加工ができるようです。

俺「描画の速さが売りのDXライブラリの機能なんだし、このシェーダーとやらを使えば、
画像加工、ひいては計算処理が高速にできるワンチャンあるんじゃないか?(情弱並の感想)」

とりあえず使ってみる。

main.cpp

CODE:

#include "DxLib.h"

char Key[256];

int WINAPI WinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance,LPSTR lpCmdLine, int nCmdShow ){
	SetGraphMode(512,512,32);
	if(ChangeWindowMode(TRUE) != DX_CHANGESCREEN_OK || DxLib_Init() == -1 ) return -1; //初期化処理

	int imageHandle,shaderHandle;
	VERTEX2DSHADER vert[6]={};
	vert[0].pos=VGet(0.0f,0.0f,0.0f);
	vert[0].rhw=1.0f;
	vert[0].u=0.0f;
	vert[0].v=0.0f;
	vert[1].pos=VGet(512.0f,0.0f,0.0f);
	vert[1].rhw=1.0f;
	vert[1].u=1.0f;
	vert[1].v=0.0f;
	vert[2].pos=VGet(512.0f,512.0f,0.0f);
	vert[2].rhw=1.0f;
	vert[2].u=1.0f;
	vert[2].v=1.0f;
	vert[3].pos=VGet(0.0f,0.0f,0.0f);
	vert[3].rhw=1.0f;
	vert[3].u=0.0f;
	vert[3].v=0.0f;
	vert[4].pos=VGet(512.0f,512.0f,0.0f);
	vert[4].rhw=1.0f;
	vert[4].u=1.0f;
	vert[4].v=1.0f;
	vert[5].pos=VGet(0.0f,512.0f,0.0f);
	vert[5].rhw=1.0f;
	vert[5].u=0.0f;
	vert[5].v=1.0f;

	imageHandle=LoadGraph("../lenna_orig.bmp");
	shaderHandle=LoadPixelShader("shader.pso");
	SetUseTextureToShader(0,imageHandle);
	SetUsePixelShader(shaderHandle);
	DrawPolygon2DToShader(vert,2);
	WaitKey();

	InitShader();
	DxLib_End();
	return 0;
}
shader.fx

CODE:

struct PS_INPUT {
	float4 DiffuseColor		: COLOR0;
	float4 SpecularColor	: COLOR1;
	float2 TextureCoord0	: TEXCOORD0;
	float2 TextureCoord1	: TEXCOORD1;
};

struct PS_OUTPUT {
	float4 Output			: COLOR0;
};

sampler DiffuseMapTexture : register(s0);

PS_OUTPUT main(PS_INPUT PSInput) {
	PS_OUTPUT PSOutput;
	float4 nowColor;
	nowColor=tex2D(DiffuseMapTexture,PSInput.TextureCoord0);

	PSOutput.Output.r=1.0f-nowColor.r;
	PSOutput.Output.g=1.0f-nowColor.g;
	PSOutput.Output.b=1.0f-nowColor.b;
	PSOutput.Output.a=nowColor.a;

	return PSOutput;
}
コンパイルして実行すると
dx_shader_nega.png
シェーダーでネガポジ反転
やったぜ!自分の環境でも使える!
さらに、重いC++のコンパイルをしなくても、軽いfxのコンパイルをするだけで行う加工を変えられるというのも便利です。

では、もう少し複雑なことをしてみましょう。

main.cpp

CODE:

#include "DxLib.h"

char Key[256];

int WINAPI WinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance,LPSTR lpCmdLine, int nCmdShow ){
	SetGraphMode(512,512,32);
	if(ChangeWindowMode(TRUE) != DX_CHANGESCREEN_OK || DxLib_Init() == -1 ) return -1; //初期化処理

	int imageHandle,shaderHandle;
	VERTEX2DSHADER vert[6]={};
	vert[0].pos=VGet(0.0f,0.0f,0.0f);
	vert[0].rhw=1.0f;
	vert[0].u=0.0f;
	vert[0].v=0.0f;
	vert[1].pos=VGet(512.0f,0.0f,0.0f);
	vert[1].rhw=1.0f;
	vert[1].u=1.0f;
	vert[1].v=0.0f;
	vert[2].pos=VGet(512.0f,512.0f,0.0f);
	vert[2].rhw=1.0f;
	vert[2].u=1.0f;
	vert[2].v=1.0f;
	vert[3].pos=VGet(0.0f,0.0f,0.0f);
	vert[3].rhw=1.0f;
	vert[3].u=0.0f;
	vert[3].v=0.0f;
	vert[4].pos=VGet(512.0f,512.0f,0.0f);
	vert[4].rhw=1.0f;
	vert[4].u=1.0f;
	vert[4].v=1.0f;
	vert[5].pos=VGet(0.0f,512.0f,0.0f);
	vert[5].rhw=1.0f;
	vert[5].u=0.0f;
	vert[5].v=1.0f;

	FLOAT4 embosParam;
	embosParam.x=3.0f/512.0f;
	embosParam.y=3.0f/512.0f;

	imageHandle=LoadGraph("../lenna_orig.bmp");
	shaderHandle=LoadPixelShader("shader.pso");
	SetUseTextureToShader(0,imageHandle);
	SetUsePixelShader(shaderHandle);
	SetPSConstF(10,embosParam);
	DrawPolygon2DToShader(vert,2);
	WaitKey();

	InitShader();
	DxLib_End();
	return 0;
}
shader.fx

CODE:

struct PS_INPUT {
	float4 DiffuseColor		: COLOR0;
	float4 SpecularColor	: COLOR1;
	float2 TextureCoord0	: TEXCOORD0;
	float2 TextureCoord1	: TEXCOORD1;
};

struct PS_OUTPUT {
	float4 Output			: COLOR0;
};

float4 embosParam : register(c10);
sampler DiffuseMapTexture : register(s0);

float4 color2gray(float4 color) {
	float4 result;
	result.r=0.3f*color.r+0.59f*color.g+0.11f*color.b;
	result.g=result.r;
	result.b=result.r;
	result.a=color.a;
	return result;
}

PS_OUTPUT main(PS_INPUT PSInput) {
	PS_OUTPUT PSOutput;
	float4 nowColor;
	nowColor=color2gray(tex2D(DiffuseMapTexture,PSInput.TextureCoord0));
	PSOutput.Output.r=1.0-nowColor.r;
	PSOutput.Output.g=1.0-nowColor.g;
	PSOutput.Output.b=1.0-nowColor.b;
	PSOutput.Output.a=nowColor.a;
	if(PSInput.TextureCoord0.x>=embosParam.r && PSInput.TextureCoord0.y>=embosParam.g) {
		float2 secondColorPos;
		float4 secondColor;
		secondColorPos.x=PSInput.TextureCoord0.x-embosParam.r;
		secondColorPos.y=PSInput.TextureCoord0.y-embosParam.g;
		secondColor=color2gray(tex2D(DiffuseMapTexture,secondColorPos));
		PSOutput.Output.r=PSOutput.Output.r+secondColor.r-0.5f;
		PSOutput.Output.g=PSOutput.Output.g+secondColor.g-0.5f;
		PSOutput.Output.b=PSOutput.Output.b+secondColor.b-0.5f;
		if(PSOutput.Output.r1.0f)PSOutput.Output.r=1.0f;
		if(PSOutput.Output.g1.0f)PSOutput.Output.g=1.0f;
		if(PSOutput.Output.b1.0f)PSOutput.Output.b=1.0f;
	}
	
	return PSOutput;
}
これをコンパイルし、実行すると
dx_shader_embos.png
シェーダーでエンボス加工
グレースケール化からのエンボス加工、きれいに決まった!

・・・さて、自分の環境でもシェーダーが使えることがわかったところで、本当に高速なのか実験してみましょう。
上のエンボス加工処理の速さを測定します。
エントリーしたのは、以下の3個のプログラム。
・シェーダーを使用
► スポイラーを表示
・BMPファイルのデータに直接アクセス
► スポイラーを表示
・SoftImage系の関数を使用
► スポイラーを表示
他の2本のプログラムは整数演算なのに対し、シェーダーは浮動小数点演算なので、シェーダーが不利そうです。
果たして、DXライブラリのシェーダーはこのハンデを乗り越えるほど高速に動作するのでしょうか?

検証環境
Windows Vista Home Premium SP2 32ビット
Intel(R) Core(TM)2Duo T8100 @2.10GHz 2.10GHz
RAM 4.00GB
gcc 4.7.2
最適化 -O2
DXライブラリ Ver 3.11

実験結果(表の単位:マイクロ秒)
ここでは
初期化:画像データのロード・パラメータの設定(DXライブラリの初期化は含まない)
画像生成:画像データをグレースケール化・エンボス加工したデータの作成
画像描画:作成したデータをDXライブラリの画面に描画
としています。ただし、シェーダーはその性質上画像生成と画像描画がセットになっています。

シェーダーを使用するプログラム
[table=border:1px solid #FFFFFF;border-collapse:collapse;][tr=]
[td=border:1px solid #FFFFFF;padding:0.2em;] [/td]
[td=border:1px solid #FFFFFF;padding:0.2em;]初期化[/td]
[td=border:1px solid #FFFFFF;padding:0.2em;]画像生成・描画[/td]
[td=border:1px solid #FFFFFF;padding:0.2em;]合計[/td]
[/tr][tr=]
[td=border:1px solid #FFFFFF;padding:0.2em;]1[/td]
[td=border:1px solid #FFFFFF;padding:0.2em;]5034[/td]
[td=border:1px solid #FFFFFF;padding:0.2em;]8141[/td]
[td=border:1px solid #FFFFFF;padding:0.2em;]13175[/td]
[/tr][tr=]
[td=border:1px solid #FFFFFF;padding:0.2em;]2[/td]
[td=border:1px solid #FFFFFF;padding:0.2em;]5159[/td]
[td=border:1px solid #FFFFFF;padding:0.2em;]6054[/td]
[td=border:1px solid #FFFFFF;padding:0.2em;]11213[/td]
[/tr][tr=]
[td=border:1px solid #FFFFFF;padding:0.2em;]3[/td]
[td=border:1px solid #FFFFFF;padding:0.2em;]4816[/td]
[td=border:1px solid #FFFFFF;padding:0.2em;]6378[/td]
[td=border:1px solid #FFFFFF;padding:0.2em;]11194[/td]
[/tr][tr=]
[td=border:1px solid #FFFFFF;padding:0.2em;]4[/td]
[td=border:1px solid #FFFFFF;padding:0.2em;]5458[/td]
[td=border:1px solid #FFFFFF;padding:0.2em;]10365[/td]
[td=border:1px solid #FFFFFF;padding:0.2em;]15823[/td]
[/tr][tr=]
[td=border:1px solid #FFFFFF;padding:0.2em;]5[/td]
[td=border:1px solid #FFFFFF;padding:0.2em;]4798[/td]
[td=border:1px solid #FFFFFF;padding:0.2em;]6021[/td]
[td=border:1px solid #FFFFFF;padding:0.2em;]10819[/td]
[/tr][tr=]
[td=border:1px solid #FFFFFF;padding:0.2em;]平均[/td]
[td=border:1px solid #FFFFFF;padding:0.2em;]5053[/td]
[td=border:1px solid #FFFFFF;padding:0.2em;]7391.8[/td]
[td=border:1px solid #FFFFFF;padding:0.2em;]12444.8[/td]
[/tr][/table]
BMPファイルのデータに直接アクセスするプログラム
[table=border:1px solid #FFFFFF;border-collapse:collapse;][tr=]
[td=border:1px solid #FFFFFF;padding:0.2em;] [/td]
[td=border:1px solid #FFFFFF;padding:0.2em;]初期化[/td]
[td=border:1px solid #FFFFFF;padding:0.2em;]画像生成[/td]
[td=border:1px solid #FFFFFF;padding:0.2em;]画像描画[/td]
[td=border:1px solid #FFFFFF;padding:0.2em;]画像生成・描画[/td]
[td=border:1px solid #FFFFFF;padding:0.2em;]合計[/td]
[/tr][tr=]
[td=border:1px solid #FFFFFF;padding:0.2em;]1[/td]
[td=border:1px solid #FFFFFF;padding:0.2em;]2197[/td]
[td=border:1px solid #FFFFFF;padding:0.2em;]3910[/td]
[td=border:1px solid #FFFFFF;padding:0.2em;]6692[/td]
[td=border:1px solid #FFFFFF;padding:0.2em;]10602[/td]
[td=border:1px solid #FFFFFF;padding:0.2em;]12799[/td]
[/tr][tr=]
[td=border:1px solid #FFFFFF;padding:0.2em;]2[/td]
[td=border:1px solid #FFFFFF;padding:0.2em;]2021[/td]
[td=border:1px solid #FFFFFF;padding:0.2em;]3615[/td]
[td=border:1px solid #FFFFFF;padding:0.2em;]5573[/td]
[td=border:1px solid #FFFFFF;padding:0.2em;]9188[/td]
[td=border:1px solid #FFFFFF;padding:0.2em;]11209[/td]
[/tr][tr=]
[td=border:1px solid #FFFFFF;padding:0.2em;]3[/td]
[td=border:1px solid #FFFFFF;padding:0.2em;]2168[/td]
[td=border:1px solid #FFFFFF;padding:0.2em;]3773[/td]
[td=border:1px solid #FFFFFF;padding:0.2em;]7430[/td]
[td=border:1px solid #FFFFFF;padding:0.2em;]11203[/td]
[td=border:1px solid #FFFFFF;padding:0.2em;]13371[/td]
[/tr][tr=]
[td=border:1px solid #FFFFFF;padding:0.2em;]4[/td]
[td=border:1px solid #FFFFFF;padding:0.2em;]2373[/td]
[td=border:1px solid #FFFFFF;padding:0.2em;]3640[/td]
[td=border:1px solid #FFFFFF;padding:0.2em;]6956[/td]
[td=border:1px solid #FFFFFF;padding:0.2em;]10596[/td]
[td=border:1px solid #FFFFFF;padding:0.2em;]12969[/td]
[/tr][tr=]
[td=border:1px solid #FFFFFF;padding:0.2em;]5[/td]
[td=border:1px solid #FFFFFF;padding:0.2em;]2624[/td]
[td=border:1px solid #FFFFFF;padding:0.2em;]3890[/td]
[td=border:1px solid #FFFFFF;padding:0.2em;]9205[/td]
[td=border:1px solid #FFFFFF;padding:0.2em;]13095[/td]
[td=border:1px solid #FFFFFF;padding:0.2em;]15719[/td]
[/tr][tr=]
[td=border:1px solid #FFFFFF;padding:0.2em;]平均[/td]
[td=border:1px solid #FFFFFF;padding:0.2em;]2276.6[/td]
[td=border:1px solid #FFFFFF;padding:0.2em;]3765.6[/td]
[td=border:1px solid #FFFFFF;padding:0.2em;]7171.2[/td]
[td=border:1px solid #FFFFFF;padding:0.2em;]10936.8[/td]
[td=border:1px solid #FFFFFF;padding:0.2em;]13213.4[/td]
[/tr][/table]
SoftImage系の関数を使用するプログラム
[table=border:1px solid #FFFFFF;border-collapse:collapse;][tr=]
[td=border:1px solid #FFFFFF;padding:0.2em;] [/td]
[td=border:1px solid #FFFFFF;padding:0.2em;]初期化[/td]
[td=border:1px solid #FFFFFF;padding:0.2em;]画像生成[/td]
[td=border:1px solid #FFFFFF;padding:0.2em;]画像描画[/td]
[td=border:1px solid #FFFFFF;padding:0.2em;]画像生成・描画[/td]
[td=border:1px solid #FFFFFF;padding:0.2em;]合計[/td]
[/tr][tr=]
[td=border:1px solid #FFFFFF;padding:0.2em;]1[/td]
[td=border:1px solid #FFFFFF;padding:0.2em;]2291[/td]
[td=border:1px solid #FFFFFF;padding:0.2em;]49470[/td]
[td=border:1px solid #FFFFFF;padding:0.2em;]4108[/td]
[td=border:1px solid #FFFFFF;padding:0.2em;]53578[/td]
[td=border:1px solid #FFFFFF;padding:0.2em;]55869[/td]
[/tr][tr=]
[td=border:1px solid #FFFFFF;padding:0.2em;]2[/td]
[td=border:1px solid #FFFFFF;padding:0.2em;]2123[/td]
[td=border:1px solid #FFFFFF;padding:0.2em;]43229[/td]
[td=border:1px solid #FFFFFF;padding:0.2em;]3894[/td]
[td=border:1px solid #FFFFFF;padding:0.2em;]47123[/td]
[td=border:1px solid #FFFFFF;padding:0.2em;]49246[/td]
[/tr][tr=]
[td=border:1px solid #FFFFFF;padding:0.2em;]3[/td]
[td=border:1px solid #FFFFFF;padding:0.2em;]2183[/td]
[td=border:1px solid #FFFFFF;padding:0.2em;]44743[/td]
[td=border:1px solid #FFFFFF;padding:0.2em;]3977[/td]
[td=border:1px solid #FFFFFF;padding:0.2em;]48720[/td]
[td=border:1px solid #FFFFFF;padding:0.2em;]50903[/td]
[/tr][tr=]
[td=border:1px solid #FFFFFF;padding:0.2em;]4[/td]
[td=border:1px solid #FFFFFF;padding:0.2em;]2531[/td]
[td=border:1px solid #FFFFFF;padding:0.2em;]45833[/td]
[td=border:1px solid #FFFFFF;padding:0.2em;]8715[/td]
[td=border:1px solid #FFFFFF;padding:0.2em;]54548[/td]
[td=border:1px solid #FFFFFF;padding:0.2em;]57079[/td]
[/tr][tr=]
[td=border:1px solid #FFFFFF;padding:0.2em;]5[/td]
[td=border:1px solid #FFFFFF;padding:0.2em;]2082[/td]
[td=border:1px solid #FFFFFF;padding:0.2em;]65611[/td]
[td=border:1px solid #FFFFFF;padding:0.2em;]3746[/td]
[td=border:1px solid #FFFFFF;padding:0.2em;]69357[/td]
[td=border:1px solid #FFFFFF;padding:0.2em;]71439[/td]
[/tr][tr=]
[td=border:1px solid #FFFFFF;padding:0.2em;]平均[/td]
[td=border:1px solid #FFFFFF;padding:0.2em;]2242[/td]
[td=border:1px solid #FFFFFF;padding:0.2em;]49777.2[/td]
[td=border:1px solid #FFFFFF;padding:0.2em;]4888[/td]
[td=border:1px solid #FFFFFF;padding:0.2em;]54665.2[/td]
[td=border:1px solid #FFFFFF;padding:0.2em;]56907.2[/td]
[/tr][/table]
画像

詳しく見ていくと、以下のことがわかります。
・頂点を設定したりシェーダーをロードしたりするシェーダーの初期化に比べ、ほぼ画像のロードとメモリ確保だけのBMPやSoftImageの初期化は速い
・BMPファイルのデータの描画は、シェーダーを用いた描画と同じくらいの時間がかかる
・DXライブラリのSoftImageの操作はかなり遅いが、逆に描画は速い

このことから、実用上は、DXライブラリのファイル容量と起動時間が大きいということもあり、
一般の画像加工にはシェーダーを使う必要は無さそう、と言えそうです。

さて、試しにシェーダーを使わず、画像処理をせずに描画してみたら、どのくらいの時間がかかるのでしょうか?
► スポイラーを表示
1回の測定結果(マイクロ秒)
初期化:4692 画像描画:46 合計:4738

やはり、何も加工せずただ描画するのは、かなり速いです。さすがDXライブラリですね。
しかし、LoadGraphはこんなに重いのでしょうか?そういえば、シェーダーの初期化にもLoadGraphを使っていました。

次回予告
あまり画像加工の性能が良くないことがわかったシェーダー。それでも計算処理に使用してみたらどうなるか?
遅いかもしれない。しかし、それでも一度やってみよう。

ISLe
記事: 2650
登録日時: 15年前

RE: シェーダーとやらを使ってみた

投稿記事 by ISLe » 11年前

みけCAT さんが書きました:このことから、実用上は、DXライブラリのファイル容量と起動時間が大きいということもあり、
一般の画像加工にはシェーダーを使う必要は無さそう、と言えそうです。
DXライブラリは仕様的に並行処理に向いてない気がするので、シェーダーを使うとメインCPUで別の処理を同時に行えるというメリットは少ないかもしれませんね。