バイリニア補間した際のつなぎ目について

フォーラム(掲示板)ルール
フォーラム(掲示板)ルールはこちら  ※コードを貼り付ける場合は [code][/code] で囲って下さい。詳しくはこちら
アバター
Ketty
記事: 103
登録日時: 11年前

バイリニア補間した際のつなぎ目について

#1

投稿記事 by Ketty » 10年前

こんにちは(^▽^)Kettyです。

表題の件ですが、
以下のプログラム( DXライブラリ + C++ )を実行して、上下左右キーで移動(スクロール)させると、
DrawDivGraph LoadDivGraphでロードした画像のつなぎ目が目に見える瞬間があります。

コード:

#include <DxLib.h>
#include <vector>

// スクリーンサイズ
static const unsigned int SCREEN_W = 640 ;
static const unsigned int SCREEN_H = 480 ;
// 画像サイズ
static const unsigned int GRAPH_W = 32 ;
static const unsigned int GRAPH_H = 32 ;
// 画像分割数
static const unsigned int DIV_X = 2 ;
static const unsigned int DIV_Y = 1 ;

// 移動速度
static const float SPEED = 4.3f ;

int WINAPI WinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow )
{
	// ウィンドウモードで起動
	DxLib::ChangeWindowMode( TRUE ) ;
	// スクリーンサイズ指定
	DxLib::SetGraphMode( SCREEN_W, SCREEN_H, 32 ) ;
	// 非フォーカスでも処理します
	DxLib::SetAlwaysRunFlag( TRUE ) ;

	// ログ出力なし
	DxLib::SetOutApplicationLogValidFlag( FALSE ) ;
	// DXライブラリ初期化
	if( DxLib::DxLib_Init() != 0 ){ return -1 ; }
	// 背景色を白に
	DxLib::SetBackgroundColor( 255, 255, 255 ) ;
	// 描画先を裏画面に設定
	DxLib::SetDrawScreen( DX_SCREEN_BACK ) ;
	
	// 画像ハンドル配列
	std::vector<int> graphList( DIV_X * DIV_Y, -1 ) ;

	/* 【これは実験です】
	// 乗算済みアルファにしてみる
	DxLib::SetUsePremulAlphaConvertLoad( TRUE ) ;
	*/

	// 画像ロード(64*32の画像を32*32の画像として分割ロード)
	if( DxLib::LoadDivGraph( "graph.png", graphList.size(), DIV_X, DIV_Y, GRAPH_W, GRAPH_H, &*graphList.begin() ) != 0 )
	{
		::MessageBox( NULL, TEXT( "画像ロード失敗だよ" ), TEXT( "エラー" ), MB_ICONEXCLAMATION | MB_OK ) ;
		DxLib::DxLib_End() ;
		return -1 ;
	}

	// 描画開始座標を初期化
	float drawX = 0.0f ;
	float drawY = 0.0f ;

	// ESCキーで終了
	while( DxLib::ProcessMessage() == 0 && DxLib::CheckHitKey( KEY_INPUT_ESCAPE ) == 0 )
	{
		// 上下左右で移動
		if( DxLib::CheckHitKey( KEY_INPUT_UP ) != 0 )
		{
			drawY -= SPEED * 1.0f ;
		}
		else if( DxLib::CheckHitKey( KEY_INPUT_DOWN ) != 0 )
		{
			drawY += SPEED * 1.0f ;
		}
		if( DxLib::CheckHitKey( KEY_INPUT_RIGHT ) != 0 )
		{
			drawX += SPEED * 1.0f ;
		}
		else if( DxLib::CheckHitKey( KEY_INPUT_LEFT ) != 0 )
		{
			drawX -= SPEED * 1.0f ;
		}

		// バイリニアで補間する
		DxLib::SetDrawMode( DX_DRAWMODE_BILINEAR ) ;

		/* 【これは実験です】
		// 乗算済みアルファのアルファブレンドで描画してみる
		DxLib::SetDrawBlendMode( DX_BLENDMODE_PMA_ALPHA, 255 ) ;
		*/

		// 画像を敷き詰める
		for( unsigned int i=0, n=SCREEN_H / GRAPH_H; i<n; ++i )
		{
			const float y = drawY + (float)i * (float)GRAPH_H ;
			for( unsigned int j=0, m=SCREEN_W / GRAPH_W; j<m; ++j )
			{
				const float x = drawX + (float)j * (float)GRAPH_W ;

				// float指定で0番目だけを描画する
				DxLib::DrawGraphF( x, y, *graphList.begin(), TRUE ) ;
			}
		}

		/* 【これは実験です】
		// 乗算済みアルファのアルファブレンドを解除する
		DxLib::SetDrawBlendMode( DX_BLENDMODE_NOBLEND, 255 ) ;
		*/

		// ニアレストに戻す
		DxLib::SetDrawMode( DX_DRAWMODE_NEAREST ) ;

		// フリップ
		DxLib::ScreenFlip() ;
		// 裏画面をクリア
		DxLib::ClsDrawScreen() ;

		// 適当なウェイト
		Sleep( 10 ) ;
	}

	// DXライブラリの終了処理
	DxLib::DxLib_End() ;

	// プログラム終了
	return 0 ;
}

↓こんな風につなぎ目の線が見えます(赤丸でマークしている部分の縦ライン) ※拡大しないと見えないかもしれません
境目が見える.jpg
つなぎ目が見える
↓使用している画像(64 * 32)
graph.png
使用している画像
graph.png (186 バイト) 閲覧数: 7632 回
この問題は、バイリニア補間+DrawGraphF系の描画ならではだという認識です。
また、以下を読んだ限りでは、どうにもならないのかもしれないと思っております。

参考URL
http://hpcgi2.nifty.com/natupaji/bbs/pa ... ast&no=899

そこで、私ならではの工夫を考えてみました。
1.敷き詰める際に、微妙に重なるように座標を小細工すれば・・・
 ⇒座標を変えても画像のつなぎ目部分が消えるわけではないので意味が無かったです。

2.乗算済みアルファを使えばきっと・・・
 ⇒変化なしでした。

3.Draw時のみ、バイリニア補間をやめる
 ⇒つなぎ目は見えなくなりますが、描画におけるfloatの恩恵が得られなくなります。
  補足)Drawをfloat座標指定で行いかつ、バイリニア補間しないと、
     見た目の小数ピクセルを表現できないからカクカクするように感じます。

4.画像ファイル自体を変更してつなぎ目を最も目立たない色に変える
 ⇒いまのところこれかな。

質問です。
この問題はみなさんどうやって解決されていますか?

質問させていただいた背景としては、
例えば、横スクロールアクションで、キャラクターの移動量がfloatである場合、
背景(に敷き詰めたマップチップ)のスクロール量もfloatだと思うので、
割と普通に立ちはだかる問題なのではないかな、と思ったからです。

移動量が整数(int)なら問題ないですが、
ゲームプログラムでは、移動量が(あるいはキャラクターの座標x,yが)floatであるというのは
特殊ではなく一般的だろう、と考えてのことです。

私の認識に誤りや思い込みがある場合もご指摘くださいm(__)m

【環境】
Visual C++2010 Express Edition
DXライブラリ ver.3.13d
Windows 7 Home Edition 64bit
最後に編集したユーザー Ketty on 2015年3月21日(土) 09:59 [ 編集 1 回目 ]

アバター
lbfuvab
記事: 72
登録日時: 14年前

Re: バイリニア補間した際のつなぎ目について

#2

投稿記事 by lbfuvab » 10年前

見た感じ、凄く大きい画像の描画の様なのですが、その時だけニアレストバイアーにするのは駄目なのでしょうか?

アバター
Ketty
記事: 103
登録日時: 11年前

Re: バイリニア補間した際のつなぎ目について

#3

投稿記事 by Ketty » 10年前

>lbfuvabさん
ご回答くださりありがとうございます(^^)
lbfuvab さんが書きました: 見た感じ、凄く大きい画像の描画の様なのですが、
私の理解が遅くて申し訳ないのですが、
"凄く大きい画像の描画"と表現されておりますが、これは特殊なケースであるとご指摘くださっているのでしょうか(??)
画像1チップは縦横32pxですが、それを例えばゲームの背景マップのように、
640*480のスクリーンいっぱいに敷き詰めることは、私は、一般的だろうという認識でした。
lbfuvab さんが書きました: その時だけニアレストバイアーにするのは駄目なのでしょうか?
挙げてくださった方法は、
おそらく、私の3.のやり方と同じだと思います(^^)
↓つなぎ目は消えますが、スクロールに滑らかさが失われますので、なんとかならないかなと思っている次第です。
Ketty さんが書きました: 3.Draw時のみ、バイリニア補間をやめる
 ⇒つなぎ目は見えなくなりますが、描画におけるfloatの恩恵が得られなくなります。
  補足)Drawをfloat座標指定で行いかつ、バイリニア補間しないと、
     見た目の小数ピクセルを表現できないからカクカクするように感じます。

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

Re: バイリニア補間した際のつなぎ目について

#4

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

Ketty さんが書きました:
lbfuvab さんが書きました: その時だけニアレストバイアーにするのは駄目なのでしょうか?
挙げてくださった方法は、
おそらく、私の3.のやり方と同じだと思います(^^)
↓つなぎ目は消えますが、スクロールに滑らかさが失われますので、なんとかならないかなと思っている次第です。
Ketty さんが書きました: 3.Draw時のみ、バイリニア補間をやめる
 ⇒つなぎ目は見えなくなりますが、描画におけるfloatの恩恵が得られなくなります。
  補足)Drawをfloat座標指定で行いかつ、バイリニア補間しないと、
     見た目の小数ピクセルを表現できないからカクカクするように感じます。
スクロール用のマップ(?)を別のグラフィックにニアレストネイバーで描画し、そのグラフィックを画面にバイリニアで描画する、というのはどうでしょうか?
複雑な問題?マシンの性能を上げてOpenMPで殴ればいい!(死亡フラグ)

アバター
Ketty
記事: 103
登録日時: 11年前

Re: バイリニア補間した際のつなぎ目について

#5

投稿記事 by Ketty » 10年前

> みけCAT さん
お世話になっております。ご回答くださりありがとうございます(^^)
みけCAT さんが書きました: スクロール用のマップ(?)を別のグラフィックにニアレストネイバーで描画し、そのグラフィックを画面にバイリニアで描画する、というのはどうでしょうか?
なるほど!その手がありましたか。
試しにプログラムして実験してみようと思います。

アバター
Ketty
記事: 103
登録日時: 11年前

Re: バイリニア補間した際のつなぎ目について

#6

投稿記事 by Ketty » 10年前

みけCATさんのアイデアを使わせていただきましたところ、希望通りの挙動が実現できました(^^)
このトピックとしては解決とさせていただきます。

その後分かったことですが、今回の事象の原因は、どうやらバイリニア補間 + LoadDivGraphにありそうです。
以下の記事に同じようなことが書かれておりました。
http://crazyiscream.blog136.fc2.com/blog-entry-21.html
またLoadDivGraphで分割ロードした場合、分割内の一番外の枠まで描画してあると線のようなものが見えてしまう現象が発生する。
つまり、バイリニア補完すると、切出したチップの外側に線が見えてしまうようです。
なお、DerivationGraphで自前で切り出しても結果は同じでした。

一度、DXライブラリ公式サイトで質問してみようと思います。
(その際はこちらにもリンクを掲載させていただきます)

また、以下に、みけCATさんのアイデアを使わせてもらったコードを記しておきます。
メインループ内のDrawA⇔DrawBを切替えると、事象の再現ができます。

コード:

#include <DxLib.h>
#include <vector>

// スクリーンサイズ
static const unsigned int SCREEN_W = 640 ;
static const unsigned int SCREEN_H = 480 ;
// 画像サイズ
static const unsigned int GRAPH_W = 32 ;
static const unsigned int GRAPH_H = 32 ;
// 画像分割数
static const unsigned int DIV_X = 2 ;
static const unsigned int DIV_Y = 1 ;

// 移動速度
static const float SPEED = 4.3f ;

// 描画開始座標を初期化
float drawX = 0.0f ;
float drawY = 0.0f ;

// バイリニアで敷き詰め描画する
void DrawA( const int& chipHandle )
{
	// バイリニアで補間する
	DxLib::SetDrawMode( DX_DRAWMODE_BILINEAR ) ;

	// 画像を敷き詰める
	for( unsigned int i=0, n=SCREEN_H / GRAPH_H; i<n; ++i )
	{
		const float y = drawY + (float)i * (float)GRAPH_H ;
		for( unsigned int j=0, m=SCREEN_W / GRAPH_W; j<m; ++j )
		{
			const float x = drawX + (float)j * (float)GRAPH_W ;

			// float指定で0番目だけを描画する
			DxLib::DrawGraphF( x, y, chipHandle, TRUE ) ;
		}
	}

	// ニアレストに戻す
	DxLib::SetDrawMode( DX_DRAWMODE_NEAREST ) ;
}
// ニアレストであらかじめ敷き詰め描画したものをバイリニアで移動させる
void DrawB( const int& chipHandle, const int& mapHandle )
{
	// 描画先を変更
	DxLib::SetDrawScreen( mapHandle ) ;

	// あらかじめニアレストで敷き詰める
	for( unsigned int i=0, n=SCREEN_H / GRAPH_H; i<n; ++i )
	{
		const int y = i * GRAPH_H ;
		for( unsigned int j=0, m=SCREEN_W / GRAPH_W; j<m; ++j )
		{
			const int x = j * GRAPH_W ;
			// int指定で0番目だけを描画する
			DxLib::DrawGraph( x, y, chipHandle, TRUE ) ;
		}
	}

	// 描画先を戻す
	DxLib::SetDrawScreen( DX_SCREEN_BACK ) ;

	// バイリニアで補間する
	DxLib::SetDrawMode( DX_DRAWMODE_BILINEAR ) ;

	// 敷き詰めておいた背景を描画する
	DxLib::DrawGraphF( drawX, drawY, mapHandle, TRUE ) ;

	// ニアレストに戻す
	DxLib::SetDrawMode( DX_DRAWMODE_NEAREST ) ;
}

int WINAPI WinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow )
{
	// ウィンドウモードで起動
	DxLib::ChangeWindowMode( TRUE ) ;
	// スクリーンサイズ指定
	DxLib::SetGraphMode( SCREEN_W, SCREEN_H, 32 ) ;
	// 非フォーカスでも処理します
	DxLib::SetAlwaysRunFlag( TRUE ) ;

	// ログ出力なし
	DxLib::SetOutApplicationLogValidFlag( FALSE ) ;
	// DXライブラリ初期化
	if( DxLib::DxLib_Init() != 0 ){ return -1 ; }
	// 背景色を白に
	DxLib::SetBackgroundColor( 255, 255, 255 ) ;
	// 描画先を裏画面に設定
	DxLib::SetDrawScreen( DX_SCREEN_BACK ) ;
	
	// 画像ロード(64*32の画像を32*32の画像として分割ロード)
	std::vector<int> graphList( DIV_X * DIV_Y, -1 ) ;
	if( DxLib::LoadDivGraph( "graph.png", graphList.size(), DIV_X, DIV_Y, GRAPH_W, GRAPH_H, &*graphList.begin() ) != 0 )
	{
		::MessageBox( NULL, TEXT( "画像ロード失敗だよ" ), TEXT( "エラー" ), MB_ICONEXCLAMATION | MB_OK ) ;
		DxLib::DxLib_End() ;
		return -1 ;
	}

	// ニアレストであらかじめ敷き詰め描画するための描画用グラフィックハンドル作成
	int mapHandle = DxLib::MakeScreen( SCREEN_W, SCREEN_H, TRUE ) ;

	// ESCキーで終了
	while( DxLib::ProcessMessage() == 0 && DxLib::CheckHitKey( KEY_INPUT_ESCAPE ) == 0 )
	{
		// 上下左右で移動
		if( DxLib::CheckHitKey( KEY_INPUT_UP ) != 0 )
		{
			drawY -= SPEED * 1.0f ;
		}
		else if( DxLib::CheckHitKey( KEY_INPUT_DOWN ) != 0 )
		{
			drawY += SPEED * 1.0f ;
		}
		if( DxLib::CheckHitKey( KEY_INPUT_RIGHT ) != 0 )
		{
			drawX += SPEED * 1.0f ;
		}
		else if( DxLib::CheckHitKey( KEY_INPUT_LEFT ) != 0 )
		{
			drawX -= SPEED * 1.0f ;
		}

		// バイリニアで敷き詰め描画する
		//DrawA( *graphList.begin() ) ;

		// ニアレストであらかじめ敷き詰め描画したものをバイリニアで移動させる
		DrawB( *graphList.begin(), mapHandle ) ;

		// フリップ
		DxLib::ScreenFlip() ;
		// 裏画面をクリア
		DxLib::ClearDrawScreen() ;

		// 適当なウェイト
		Sleep( 10 ) ;
	}

	// DXライブラリの終了処理
	DxLib::DxLib_End() ;

	// プログラム終了
	return 0 ;
}


アバター
Ketty
記事: 103
登録日時: 11年前

Re: バイリニア補間した際のつなぎ目について

#7

投稿記事 by Ketty » 10年前

DXライブラリ公式サイト様にも質問をいたしました(^^)
http://hpcgi2.nifty.com/natupaji/bbs/pa ... ew&no=3519

ISLe()

Re: バイリニア補間した際のつなぎ目について

#8

投稿記事 by ISLe() » 10年前

テクセルからピクセルへの直接的なマッピング (Direct3D 9)
浮動小数点数版のときはDirect3Dの機能を使って描画している(と思われる)ので、ピクセルとテクセルが0.5ズレます。
その結果、となりのテクセル(テクスチャ上のピクセル)の色が漏れてきます。

残念ながらDXライブラリに切り出し元の座標を浮動小数点数で指定する関数は用意されていません。
3D描画機能をテクスチャ座標を補正して使えば期待通りの描画を得ることは可能です。

ちなみにDirect3D10以降ではこのような現象が発生しない仕様に変更されました。

ISLe()

Re: バイリニア補間した際のつなぎ目について

#9

投稿記事 by ISLe() » 10年前

すみません。
テクスチャのフィルタリングで色が漏れる問題は、テクセルのマッピング問題とは関係なかったですね。
画像が歪まないのだから、DXライブラリ内部でマッピングは適切に補正されているはずなのでした。

アバター
Ketty
記事: 103
登録日時: 11年前

Re: バイリニア補間した際のつなぎ目について

#10

投稿記事 by Ketty » 10年前

>ISLeさん
どうも(^v^)いつも貴重な情報をくださり、ありがとうございます。
ISLe さんが書きました: テクセルからピクセルへの直接的なマッピング (Direct3D 9)
浮動小数点数版のときはDirect3Dの機能を使って描画している(と思われる)ので、ピクセルとテクセルが0.5ズレます。
その結果、となりのテクセル(テクスチャ上のピクセル)の色が漏れてきます。
ISLe さんが書きました: ちなみにDirect3D10以降ではこのような現象が発生しない仕様に変更されました。
いずれも、私はまったく存じませんでした。
テクセル(という言葉)さえも知りませんでした。

Direct3D 9ではテクスチャを歪ませると色が漏れるよ、ということですね?
3Dゲーム製作者にとっては重要な知識のひとつなのだろうと想像しましたので、
Web検索でキーワードヒットしやすいように勝手にSEOさせていただきます(^^)ヾ

テクセル 色漏れ Direct3D 9

↓原因についてはここを参照
テクセルからピクセルへの直接的なマッピング (Direct3D 9)
https://msdn.microsoft.com/ja-jp/librar ... s.85).aspx
Direct3D10以降では仕様変更により、解決されている模様です。

ISLe()

Re: バイリニア補間した際のつなぎ目について

#11

投稿記事 by ISLe() » 10年前

Ketty さんが書きました:Direct3D 9ではテクスチャを歪ませると色が漏れるよ、ということですね?
Direct3D 9ではUV座標を補正しないと描画時に画像が歪んで線が入ったように見えることがあります。

テクスチャのフィルタリングによる色漏れは、周辺ピクセルと色を平均化する際に隣り合った画像が巻き込まれて発生するものです。

両者は別の問題です。

「線が見える」という点に反応して勘違いしました。

閉鎖

“C言語何でも質問掲示板” へ戻る