FFDスプライトってなに?

自由に変形できるスプライトのこと。自由に指定した4頂点を用いて違和感なくスプライトを表現する手法。Free Form Deformation(自由変形)の略。Spineなんかのアニメツールで生成されるスプライトにはこの機能が必要。4頂点を2枚の三角形で表現する分にはどんな絵もだせそうであるが、実際には1枚目の写真のようにテクスチャの接合部分で絵が歪む。そもそも歪んだポリゴンとはどういうものなのか?2枚の三角形で構成できるものなのか?

ffd01.PNG

4頂点に対して割り当てた2枚の三角形の接合部分で折れている。2枚の三角形だとコレが限界。テクスチャの歪みがひどい。

ffd02.PNG

4分割。分割数を増やすと滑らかになってテクスチャの歪みが減った。

ffd03.PNG

9分割 だいぶなめらかになってる。テクスチャの歪みも、かなりわかりにくくなった。

ffd04.PNG

16分割 9分割よりは綺麗に見えるけどあまり違いがなくなってきた。

ffd05.PNG
256分割 ここまで細かくしても16分割とは大差がない感じ。ポリゴンの無駄かな。

結局どういうものなのか?

1枚の四角形を複数の三角形に分けて構成したメッシュにすることでテクスチャの歪みを抑制するスプライト表示。自由な4頂点にも対応しようと思うと2枚の三角形ではねじれに限界がでてくるので分割数を増やさないと上記の通りテクスチャが崩れてしまう。3Dで表現される揺らめくハタなんかの作られ方に近い。

なんとか2枚の三角形で表現できないか?

無理だった。四角形の板の法線方向から4頂点がぴったり一致する角度を割り出すことができそうで色々挑戦してみたけれども、ぴったり一致させる為の元の板のカタチ、回転角度を逆算するのが難しい。よしんばそれらが逆算できたとしてもカメラの視野角が変わることで奥ゆきにより長さが変わってしまうことから、視野角の異なるタイトル間ではデータを共有できないとか、このレンダリングの時のみ視野角を固定するとかアニメデータのくせに汎用性がなくなってレンダリングが面倒といった非現実的な要素からこれ以上追いかけるのをやめた。

プログラム

すごくベタに書くとこんな感じ。

{
	gxPoint pos[4];

	Sint32 div = divNum+1;

	if( div < 2  ) div = 2;
	if( div > 16 ) div = 16;

	for( Sint32 ii=0; ii<4; ii++ )
	{
		pos[ii].x = pXY[ii].x;
		pos[ii].y = pXY[ii].y;

		pos[ii].x *= fx;
		pos[ii].y *= fy;

		gxUtil::RotationPoint( &pos[ii] , fRot );
	}

	Sint32 max = div*div;

	gxPoint* pMat = new gxPoint[max];
	gxPoint* pTx  = new gxPoint[max];

	//四辺の分割座標を確定

	for( Sint32 ii=0;ii<div;ii++ )
	{
		pMat[ ii ].x = pos[0].x + 1.0f * ii * ( pos[1].x - pos[0].x ) /( div-1 );
		pMat[ ii ].y = pos[0].y + 1.0f * ii * ( pos[1].y - pos[0].y ) /( div-1 );

		pMat[ (div-1)*div+ii ].x = pos[3].x + 1.0f * ii * ( pos[2].x - pos[3].x ) /( div-1 );
		pMat[ (div-1)*div+ii ].y = pos[3].y + 1.0f * ii * ( pos[2].y - pos[3].y ) /( div-1 );

		pMat[ ii*div ].x = pos[0].x + 1.0f * ii * ( pos[3].x - pos[0].x ) /( div -1 );
		pMat[ ii*div ].y = pos[0].y + 1.0f * ii * ( pos[3].y - pos[0].y ) /( div -1 );

		pMat[ ii*div+(div-1) ].x = pos[1].x + 1.0f * ii * ( pos[2].x - pos[1].x ) /( div -1 );
		pMat[ ii*div+(div-1) ].y = pos[1].y + 1.0f * ii * ( pos[2].y - pos[1].y ) /( div -1 );
	}

	//中の補完座標を策定

	for( Sint32 ii=1;ii<div-1;ii++ )
	{
		for( Sint32 xx=1;xx<div-1;xx++ )
		{
			Float32 x1 = pMat[ ii*div+0 ].x;
			Float32 y1 = pMat[ ii*div+0 ].y;
			Float32 x2 = pMat[ ii*div+(div-1) ].x;
			Float32 y2 = pMat[ ii*div+(div-1) ].y;

			pMat[ ii*div+xx ].x = x1 + xx * (x2-x1)/(div-1);
			pMat[ ii*div+xx ].y = y1 + xx * (y2-y1)/(div-1);
		}
	}

	for( Sint32 yy=0;yy<div;yy++)
	{
		for( Sint32 xx=0;xx<div;xx++)
		{
			pTx[ yy*div + xx ].x = u + 1.0f * xx *  w  / (div-1);
			pTx[ yy*div + xx ].y = v + 1.0f * yy *  h  / (div-1);
		}
	}


	for( Sint32 yy=0;yy<div-1;yy++)
	{
		for( Sint32 xx=0;xx<div-1;xx++)
		{
			Sint32 p1,p2,p3,p4;
			p1 = yy*div + xx;
			p2 = yy*div + xx+1;
			p3 = yy*div + xx+1+div;
			p4 = (yy+1)*div + xx;

			gxLib::PutTriangle(
					pMat[ p1 ].x+x , pMat[ p1 ].y+y ,	pTx[p1].x , pTx[p1].y,
					pMat[ p2 ].x+x , pMat[ p2 ].y+y ,	pTx[p2].x , pTx[p2].y,
					pMat[ p4 ].x+x , pMat[ p4 ].y+y ,	pTx[p4].x , pTx[p4].y,
					0,
					prio ,ATR_DFLT , ARGB_DFLT );

			gxLib::PutTriangle(
					pMat[ p2 ].x+x , pMat[ p2 ].y+y ,	pTx[p2].x , pTx[p2].y,
					pMat[ p4 ].x+x , pMat[ p4 ].y+y ,	pTx[p4].x , pTx[p4].y,
					pMat[ p3 ].x+x , pMat[ p3 ].y+y ,	pTx[p3].x , pTx[p3].y,
					0,
					prio ,ATR_DFLT , ARGB_DFLT );

		}
	}


	SAFE_DELETES( pMat );
	SAFE_DELETES( pTx );

	return 0;
}

添付ファイル: fileffd05.PNG 237件 [詳細] fileffd04.PNG 195件 [詳細] fileffd03.PNG 228件 [詳細] fileffd02.PNG 216件 [詳細] fileffd01.PNG 222件 [詳細]