東方の背景のぐにゃぐにゃを作ってみた

アバター
てんむすキツネ
記事: 88
登録日時: 14年前
住所: 岡山

東方の背景のぐにゃぐにゃを作ってみた

投稿記事 by てんむすキツネ » 13年前

書き方が初心者まるだしかと。
あくまでも方法ですので
皆様からのアドバイスをお待ちしています

こちらの動画を参考にして、
[nico]http://www.nicovideo.jp/watch/sm12147682[/nico]
作ってみました!

↑の動画との変更点は、
・サイズを自由に変えられるようにした(2のn乗以外でも扱えるように)
・計算式を全て単純でわかりやすくした(厨房の自分でも考えつくレベルの計算です)
・色等の変更をしやすくした(まだ引数化できてませんが、色情報はできるだけ変更が簡単に)
・エフェクトの形を動画のものと違う方法でつくった(シンプルに。でもできるだけ東方に近くなるように)

これらを含めて作った結果がこちら↓


では、ソースを載せたいと思います


*実装する前の準備!*

・龍神録プログラミングの館で背景描画ができているものを、どれでも
(main関数の中のenemyやboss関連消しておくと背景に集中できていいかも)

・参考にしてある動画にある処理をコピペしてHLSLプログラミングの準備
 (動画なので見えにくいかも・・・このUP主の許可がおりればコピペ貼り付けるかも)


①まずは参考動画の内容を実行してみてください。

②DxLibにある、Rippleeffect関数をvoid gunya_effect(int red,int green,int blue,double r,double x,double y);
 という名前と引数に変えてください(関数名はかえなくてもいいです)

③この関数をグラフィックメイン関数の背景描画のすぐ後においてください

④引数を適当に。まだ、色情報(red,green,blue)は意味無いです。使いません
 動画ではgunya_effect(255,255,255,200,ch.x,ch.y);です

⑤関数の処理を変えます コピペでもしてくださいな
 

CODE:

void gunya_effect(int red,int green,int blue,double r,double x,double y){
//色情報、大きさ、中心点
	int grhandle;//グラフィックハンドル
	
	grhandle=MakeGraph(FMX,FMY);//スクショ分メモリ確保
	GetDrawScreenGraph(FX,FY,FX+FMX,FY+FMY,grhandle);//グニャグニャさせる範囲全体を保存	
	
	RippleVertex.Vert[0].pos=VGet(float(FX),float(FY),0.0f);//2のn乗でおそらくこれよりでかいサイズは使わない
	RippleVertex.Vert[1].pos=VGet(float(510+FX),float(FY),0.0f);//から左上をFX,FYにして
	RippleVertex.Vert[2].pos=VGet(float(FX),float(510+FY),0.0f);//510分のテクスチャを作る
	RippleVertex.Vert[3].pos=VGet(float(510+FX),float(510+FY),0.0f);
	RippleVertex.Vert[4]=RippleVertex.Vert[2];
	RippleVertex.Vert[5]=RippleVertex.Vert[1];

	FLOAT4 vec;//引数
	
	vec.x=x/510;//中心点Xをテクスチャ用座標に交換して渡す
	SetPSConstF(0,vec);
	vec.y=y/510;//中心点Yを~
	SetPSConstF(0,vec);
	vec.w=r/510;//大きさを~
	SetPSConstF(0,vec);
	vec.z=count;//カウンタを渡す
	SetPSConstF(0,vec);

	SetUseTextureToShader(0,grhandle);//使用するグラフィックハンドルをセット
	SetUsePixelShader(RipplePShandle);//シェーダをセット
	DrawPrimitive2DToShader(RippleVertex.Vert,6,DX_PRIMTYPE_TRIANGLELIST);//情報を渡す

	DeleteGraph(grhandle);//イメージハンドルの削除
}
 やってることは簡単です

⑥エフェクトファイルを変更
 

CODE:

float4 temp;

struct PS_INPUT{
	float4 Diffuse:COLOR0;
	float4 Specular:COLOR1;
	float2 TexCoords0:TEXCOORD0;
};

struct PS_OUTPUT{
	float4 Output:COLOR0;
};

sampler sampler0:register(s0);

PS_OUTPUT main(PS_INPUT psin){
	PS_OUTPUT psout;
	float2 tempcoord;
	float4 texc;

	int y=psin.TexCoords0.y*510+temp.z*2;//変形させる基準をスクリーン座標になおして、カウンタ分(変化おおきくするため*2)座標をプラス
	float rpt=60;//変更を行うタイミング(波型の一つの波の大きさ)
	float pura;//結果プラスする値

	if(y%(rpt*2)<rpt){//波型が上がりなら
		pura=(y%(rpt*2)-rpt/2)/510;//テクスチャ座標に直して、yが大きいほど、puraが大きくなるように
	}
	else{//下がりなら
		pura=(rpt-y%rpt-rpt/2+1)/510;//テクスチャ ~ 小さくなるように
	}

	float xo=temp.x+pura/4;//新たにできる中心点X

	float dx=psin.TexCoords0.x-xo;//ずらした中心点をもとに
	float dy=psin.TexCoords0.y-temp.y;
	float dist=sqrt(dx*dx+dy*dy);//中心との距離を

	if(dist<temp.w){//半径temp.w以内なら

		float epspura;//グニャグニャ濃度

		if(temp.z%720<360){//グニャグニャ濃度が上がる時なら
			epspura=(temp.z%720)/360;//temp.zが大きいほどepsは大きく
		}
		else{
			epspura=(360-temp.z%360+1)/360;//~は小さく
		}
		
		float eps=epspura*(1-dist/temp.w);//濃度*(1-中心からの距離(最大は1))

		tempcoord.x=((psin.TexCoords0.x-temp.x/2)-temp.w/2)*(1-eps)+temp.w/2+temp.x/2;//重みを求める
		tempcoord.y=((psin.TexCoords0.y-temp.y/2)-temp.w/2)*(1-eps)+temp.w/2+temp.y/2;
       //テクスチャ座標が中心点によってかわるため、-temp.y/2とかをしている(結果のぐにゃぐにゃの大きさや角度がかわるのを防ぐため)

		texc=tex2D(sampler0,tempcoord);//値を保存
		
		psout.Output.r=texc.r-(temp.w-dist)*2;//離れているほど色を+または-する
		psout.Output.g=texc.g+(temp.w-dist);
		psout.Output.b=texc.b-(temp.w-dist)*2;
		psout.Output.a=1.0;//アルファは常に1
	}
	else psout.Output=tex2D(sampler0,psin.TexCoords0);//円外なら処理をしない

	return psout;//返す

}
temp.x(y)=中心
temp.w=大きさ
temp.z=カウンタ
と勝手に使っています。

これで、できるはず・・・
たりてないところがあれば追記します

アバター
nullptr
記事: 239
登録日時: 13年前

Re: 東方の背景のぐにゃぐにゃを作ってみた

投稿記事 by nullptr » 13年前

試してませんが2のn乗から変えてはマズイのでは?DxLibがあえて2のn乗をサポートしてないと思うのですが。

アバター
てんむすキツネ
記事: 88
登録日時: 14年前
住所: 岡山

RE: 東方の背景のぐにゃぐにゃを作ってみた

投稿記事 by てんむすキツネ » 13年前

大丈夫ですよ。多分・・・

最終的に描画するテクスチャのサイズが2のn乗だといいわけですから。

なので、画面全体のスクショをとって、
必要な範囲だけ、処理をしているんです。

なので、実際描画しているものは2のn乗なんです。

加工する範囲を変えることで、ぐにゃぐにゃの範囲を2のn乗以外にする、という意味です。
説明不足ですみません。

あと、元の画像とテクスチャのサイズは違っても、足りてない部分は黒で塗られているみたいです。
どうせ、背景描画の範囲外なら、目で見えることはないので問題ないかと

アバター
SAI
記事: 115
登録日時: 14年前

Re: 東方の背景のぐにゃぐにゃを作ってみた

投稿記事 by SAI » 13年前

おお!これは!

だいぶ前に日記でこのエフェクトできないかなーとぼやいた記憶があるやつじゃないですか! 
さりげないエフェクトですがやはり気になる人がいっぱいいるんですね。
このエフェクトはぜひ使わせていただきたいです。
でもシェーダーやHLSLは知識0なのでコピペを待たせていただきます・・・(´・ω・`)
しかし仕組みを聞いたときはなるほど~と思ってしまいました。
でも毎フレームスクリーンショットを撮ってたらすごく重そうだ・・・

アバター
てんむすキツネ
記事: 88
登録日時: 14年前
住所: 岡山

RE: 東方の背景のぐにゃぐにゃを作ってみた

投稿記事 by てんむすキツネ » 13年前

<SAI 様
確かに重たいですけど、これ以外に方法が思いつかないんですよね・・・

でも、シューティングゲームだったら、よほど派手なエフェクトを使わない限りは問題なく
実行されそうです。(ただ、2つこのエフェクトを表示したら処理が・・・・)

後、問題はこれだけではないようです。
何故かフルスクリーンにすると、色の境目がくっきり見えてしまうんです。
ウインドウでいくら拡大しても、そんなことにはならないんですけど・・・

縦横の比率とか、拡大方法の違いか・・・
それともHLSLをDxLibで使用したらこうなってしまうのか・・・・

解決したら、また日記でぼやくかもしれません。





色を引数で変化できるようにしました!

CODE:

void gunya_effect(int red,int green,int blue,int a,double r,double x,double y){//引数 a が新たに増えました

	int grhandle;
	
	float red_f=float(red),green_f=float(green),blue_f=float(blue);

	grhandle=MakeGraph(FMX,FMY);
	GetDrawScreenGraph(FX,FY,FX+FMX,FY+FMY,grhandle);	
	
	RippleVertex.Vert[0].pos=VGet(float(FX),float(FY),0.0f);
	RippleVertex.Vert[1].pos=VGet(float(510+FX),float(FY),0.0f);
	RippleVertex.Vert[2].pos=VGet(float(FX),float(510+FY),0.0f);
	RippleVertex.Vert[3].pos=VGet(float(510+FX),float(510+FY),0.0f);
	RippleVertex.Vert[4]=RippleVertex.Vert[2];
	RippleVertex.Vert[5]=RippleVertex.Vert[1];

	FLOAT4 vec;
	
	vec.x=x/510;
	SetPSConstF(0,vec);
	vec.y=y/510;
	SetPSConstF(0,vec);
	vec.w=r/510;
	SetPSConstF(0,vec);
	vec.z=count;
	SetPSConstF(0,vec);

	FLOAT4 col;

	col.x=red_f/127.5f;
	SetPSConstF(1,col);
	col.y=green_f/127.5f;
	SetPSConstF(1,col);
	col.w=blue_f/127.5f;
	SetPSConstF(1,col);
	col.z=a/255;
	SetPSConstF(1,col);

	SetUseTextureToShader(0,grhandle);
	SetUsePixelShader(RipplePShandle);
	DrawPrimitive2DToShader(RippleVertex.Vert,6,DX_PRIMTYPE_TRIANGLELIST);

	DeleteGraph(grhandle);
}

CODE:

float4 temp;
float4 col;

struct PS_INPUT{
	float4 Diffuse:COLOR0;
	float4 Specular:COLOR1;
	float2 TexCoords0:TEXCOORD0;
};

struct PS_OUTPUT{
	float4 Output:COLOR0;
};

sampler sampler0:register(s0);

PS_OUTPUT main(PS_INPUT psin){
	PS_OUTPUT psout;
	float2 tempcoord;
	float4 texc;

	int y=psin.TexCoords0.y*510+temp.z*2;
	float pura;
	float rpt=60;

	if(y%(rpt*2)<rpt){
		pura=temp.x+(y%(rpt*2)-rpt/2)/1530;
	}
	else{
		pura=temp.x+(rpt-y%rpt-rpt/2)/1530;
	}

	float dx=psin.TexCoords0.x-pura;
	float dy=psin.TexCoords0.y-temp.y;
	float dist=sqrt(dx*dx+dy*dy);

	if(dist<temp.w){

		float epspura;

		if(temp.z%600<300){
			epspura=(temp.z%600+60)/360;
		}
		else{
			epspura=(300-temp.z%300+60)/360;
		}
		
		float eps=epspura*(1-dist/temp.w);

		tempcoord.x=((psin.TexCoords0.x-temp.x/2)-temp.w/2)*(1-eps)+temp.w/2+temp.x/2;
		tempcoord.y=((psin.TexCoords0.y-temp.y/2)-temp.w/2)*(1-eps)+temp.w/2+temp.y/2;

		texc=tex2D(sampler0,tempcoord);


		psout.Output.r=texc.r+(temp.w-dist)*col.x;
		psout.Output.g=texc.g+(temp.w-dist)*col.y;
		psout.Output.b=texc.b+(temp.w-dist)*col.w;
		psout.Output.a=col.z;

	}
	else psout.Output=tex2D(sampler0,psin.TexCoords0);

	return psout;

}
HLSLのコンパイラの仕様か、64個以上の計算をするとエラーをはかれるようです。
なので、一部 式を変更しています。
最後に編集したユーザー てんむすキツネ on 2012年6月08日(金) 06:43 [ 編集 1 回目 ]

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

RE: 東方の背景のぐにゃぐにゃを作ってみた

投稿記事 by ISLe » 13年前

天紆 狐 さんが書きました:<SAI 様
確かに重たいですけど、これ以外に方法が思いつかないんですよね・・・
法線マップを使ったら速いんじゃないですかね。
ピクセルシェーダで波紋を描画するサンプルとか検索したらたくさん見付かりそうな気がします。

アバター
てんむすキツネ
記事: 88
登録日時: 14年前
住所: 岡山

RE: 東方の背景のぐにゃぐにゃを作ってみた

投稿記事 by てんむすキツネ » 13年前

<ISLe 様

 http://marupeke296.com/DXPS_S_No5_NormalMap.html
このサイトを参考にして、勉強してみます。
HLSLのプラグラムでは、基礎を知らないばかりに大変な間違いをおかしていたので。
こんどはしっかり基礎を固めようかと思います!