DirectXでの曲線描画について

フォーラム(掲示板)ルール
フォーラム(掲示板)ルールはこちら  ※コードを貼り付ける場合は [code][/code] で囲って下さい。詳しくはこちら
kloud

DirectXでの曲線描画について

#1

投稿記事 by kloud » 9年前

DirectXで曲線(ベジエ曲線)を描画しているのですが、いきなり曲線全体を描画するのではなく、時間の経過に従って描画したいのですが、どのようにすればよいのでしょうか?

コード:

#include <Windows.h>

LRESULT CALLBACK WndProc ( HWND hWnd , UINT uMsg , WPARAM wParam , LPARAM lParam )
{
	HDC hDC;
	PAINTSTRUCT ps;

	// 座標の配列
	static POINT pt [ 3 ];

	switch ( uMsg )
	{
	   case WM_CREATE:
		   // 座標値の設定
		   pt [ 0 ].x = 0;
		   pt [ 0 ].y = 0;
	   	   pt [ 1 ].x = 150;
	   	   pt [ 1 ].y = 350;
	   	   pt [ 2 ].x = 400;
	   	   pt [ 2 ].y = 100;
	   	   pt [ 3 ].x = 800;
	   	   pt [ 3 ].y = 600;

	   	   break;
	   	   
	   case WM_PAINT:
		   
		      // 曲線の描画
	   	      hDC = BeginPaint ( hWnd , &ps );
		      
		      MoveToEx ( hDC , pt [ 0 ].x , pt [ 0 ].y , NULL );
	   	      PolyBezier ( hDC , pt , 4 );
		      
		      // 描画終了
	   	      EndPaint ( hWnd , &ps );
	   	      
	   	      break;

	   case WM_KEYDOWN:
	   	   if( wParam == VK_ESCAPE )
	   	   {
	   	      UINT nID = MessageBox ( NULL , "ウィンドウを閉じますか?" , "Message" , MB_YESNO );
	   	      if ( nID == IDYES )
	   	      {
	   	          DestroyWindow ( hWnd );
	   	      }  
	   	      else if ( nID == IDNO )
	   	      {
	   	         MessageBox ( NULL , "OK" , "Message" , MB_OK );
	   	      }
	   	   }
	   	   break;
	   	   
	   case WM_DESTROY:
	   	   PostQuitMessage ( 0 );
	   	   break;
	}

	return DefWindowProc ( hWnd , uMsg , wParam , lParam );
}

int WINAPI WinMain ( HINSTANCE hInstance , HINSTANCE hPrevInstance , PSTR IpCmdLine , int nCmdShow )
{
	UNREFERENCED_PARAMETER ( hPrevInstance );
	UNREFERENCED_PARAMETER ( IpCmdLine );

	HWND hWnd;
	MSG msg;
	WNDCLASS winc;

	winc.style = CS_HREDRAW | CS_VREDRAW;
	winc.lpfnWndProc = WndProc;
	winc.cbClsExtra = winc.cbWndExtra = 0;
	winc.hInstance = hInstance;
	winc.hIcon = LoadIcon ( NULL , IDI_APPLICATION );
	winc.hCursor = LoadCursor ( NULL , IDC_ARROW );
	winc.hbrBackground = ( HBRUSH ) GetStockObject ( WHITE_BRUSH );
	winc.lpszMenuName = NULL;
	winc.lpszClassName = TEXT ( "ベジエ" );

	if ( !RegisterClass ( &winc ) )
	{
		return -1;
	}

	// ウィンドウスタイル定義
	const int wStyle = WS_OVERLAPPEDWINDOW | WS_VISIBLE;

	// ウィンドウ生成
	hWnd = CreateWindow ( 
		TEXT ( "ベジエ" ) ,
		TEXT ( "ベジエ曲線" ) ,
		wStyle ,
		USEDEFAULT ,
		USEDEFAULT ,
		USEDEFAULT ,
	     USEDEFAULT,
		NULL ,
		NULL ,
		hInstance ,
		NULL 
        );

	if ( hWnd == NULL )
	{
		return -1;
	}

	while ( GetMessage ( &msg , NULL , 0 , 0 ) )
	{
		DispatchMessage ( &msg );
	}

	return msg.wParam;
}

//====================   EOF   ====================//


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

Re: DirectXでの曲線描画について

#2

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

kloud さんが書きました:DirectXで曲線(ベジエ曲線)を描画しているのですが、いきなり曲線全体を描画するのではなく、時間の経過に従って描画したいのですが、どのようにすればよいのでしょうか?
とりあえず、いきなり曲線全体を描画してもいいのでDirectXで曲線(ベジエ曲線)を描画するコードを書いてみるといいでしょう。
その後、「時間の経過に従って描画」するコードの改良するといいでしょう。
ベジェ曲線の書き方はベジェ曲線 - Wikipediaなどが参考になるでしょう。

また、ここに貼ってあるコードは21行目で確保された領域の範囲外にアクセスし、未定義動作を起こすので、修正または削除するといいでしょう。
複雑な問題?マシンの性能を上げてOpenMPで殴ればいい!(死亡フラグ)

kloud

Re: DirectXでの曲線描画について

#3

投稿記事 by kloud » 9年前

3次のベジエ曲線を描画したい場合、始点P1、制御点P2、制御点P3、終点P4とし、tを0~1まで変化させるとすると、ベジエ曲線C(t)は
C(t) = (1-t^3)P1 + 3t(1-t^2)P2 + 3t^2(1-t)P3 + t^3P4
で表せますよね?
これを元にプログラムしてみたのですが、描画の仕方が分からなくなってしまいました。また、計算もこれでいいのか不安です。

コード:

#include <Windows.h>

#define SCREEN_WIDTH ( 800 )
#define SCREEN_HEIGHT ( 600 )
#define X1 ( 0 )
#define Y1 ( 0 )
#define X4 ( 800 )
#define Y4 ( 600 )
#define X2 ( 200 )
#define Y2 ( 350 )
#define X3 ( 600 )
#define Y3 ( 500 )

int WINAPI WinMain ( HINSTANCE hInstance , HINSTANCE hPrevInstance , LPSTR lpCmdLine , int nCmdShow )
{
	UNREFERENCED_PARAMETER ( hPrevInstance );
	UNREFERENCED_PARAMETER ( lpCmdLine );
	
	WNDCLASSEX wcex =
	{
		sizeof ( WNDCLASSEX ) ,
		CS_CLASSDC ,                          
		WndProc ,                             
		0 ,                                   
		0 ,                                   
		hInstance ,                           
		NULL ,                                
		LoadCursor ( NULL , IDC_ARROW ) ,     
		( HBRUSH ) ( COLOR_WINDOW + 1 ) ,     
		NULL ,                                
		CLASS_NAME ,                          
		NULL
	};

	RegisterClassEx ( &wcex );

        static HWND hWnd;
	static MSG msg;

	// ウィンドウスタイル定義
	const int wstyle = WS_OVERLAPPEDWINDOW &~ WS_MINIMIZEBOX &~ WS_MAXIMIZEBOX &~ WS_THICKFRAME ;

    // ウィンドウを作成
	hWnd = CreateWindowEx ( 0 ,                       
		                    CLASS_NAME ,              
							WINDOW_NAME ,             
							wstyle ,     
							CW_USEDEFAULT,           
							CW_USEDEFAULT,           
		                                        SCREEN_WIDTH,
							SCREEN_HEIGHT,           
							NULL ,                    
							NULL ,                    
							hInstance ,            
							NULL                      
							);

	ShowWindow ( hWnd , nCmdShow );     
	UpdateWindow ( hWnd );              

	while ( GetMessage ( &msg , NULL , 0 , 0 ) )
	{
		TranslateMessage ( &msg );
		DispatchMessage ( &msg );
	}

	return msg.wParam;     
}

LRESULT CALLBACK WndProc ( HWND hWnd , UINT uMsg , WPARAM wParam , LPARAM lParam )
{
	static HDC hDC;
	static float a , b , c , d;
	static float t = 0.0f;
	static float px , py = 0.0f;
        PAINTSTRUCT ps;
        
       	a = ( 1 - t * t * t );
	b = 3 * t * ( 1 - t * t );
	c = 3 * t * t * ( 1 - t );
	d = t * t * t;

	switch ( uMsg )
	{
	   case WM_CREATE:
		   
		   px = X1 * a + X2 * b + X3 * c + X4 * d;
		   py = Y1 * a + Y2 * b + Y3 * c + Y4 * d;

	   	   break;
	   	   
	   case WM_PAINT:
		   {
		      // 曲線描画
	   	      hDC = BeginPaint ( hWnd , &ps );
                      
                      // 描画方法がわからない

		      // 描画終了
	   	      EndPaint ( hWnd , &ps );
                      
		      if ( t < 1.0f )
			  {
			      t++;
			  }

			  if ( t > 1.0f )
			  {
				  t = 1.0f;
			  }  

	   	      break;
		   }

	   case WM_KEYDOWN:
	   	   if( wParam == VK_ESCAPE )
	   	   {
	   	      UINT nID = MessageBox ( NULL , "ウィンドウを閉じますか?" , "Message" , MB_YESNO );
	   	      if ( nID == IDYES )
	   	      {
	   	          DestroyWindow ( hWnd );
	   	      }  
	   	      else if ( nID == IDNO )
	   	      {
	   	         MessageBox ( NULL , "OK" , "Message" , MB_OK );
	   	      }
	   	   }
	   	   break;
	   	   
	   case WM_DESTROY:
	   	   PostQuitMessage ( 0 );
	   	   break;
	}

	return DefWindowProc ( hWnd , uMsg , wParam , lParam );
}
//====================   EOF   ====================//


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

Re: DirectXでの曲線描画について

#4

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

返信が遅くなり、申し訳ありません。
DirectXへの移植は後で考えるとして、とりあえずベジェ曲線を描画しましょう。
kloud さんが書きました:3次のベジエ曲線を描画したい場合、始点P1、制御点P2、制御点P3、終点P4とし、tを0~1まで変化させるとすると、ベジエ曲線C(t)は
C(t) = (1-t^3)P1 + 3t(1-t^2)P2 + 3t^2(1-t)P3 + t^3P4
で表せますよね?
あなたの言う「3次のベジエ曲線」が何のことかよくわからないですが、前述のWikipediaの「作図法」をもとに、
線分A-Bをt:(1-t)に内分する点は A+(B-A)*t/(t+(1-t)) すなわち (1-t)*A + t*B であることを利用して式変形をすると、
C(t) = (1-t)^3P1 + 3t(1-t)^2P2 + 3t^2(1-t)P3 + t^3P4
となりました。
従って、例えば t=0.5 、各点のある成分が P1=1, P2=2, P3=3, P4=4 とすると、kloudさんの式は4.75、求めた式は2.5となり、一致しません。
従って、このkloudさんの式では、
ベジェ曲線 - Wikipedia (2016年6月27日 (月) 14:05; Glayhours による版)
に載っている3次のベジェ曲線(4個の制御点で示される曲線)は表せないと考えられます。
kloud さんが書きました:これを元にプログラムしてみたのですが、描画の仕方が分からなくなってしまいました。
適切な計算をすると曲線中の1点の座標が得られるので、適当な数の点を計算して繋ぐことで描画できます。

コード:

#include <Windows.h>

#define SCREEN_WIDTH ( 800 )
#define SCREEN_HEIGHT ( 600 )
#define MAX ( 256 ) // 曲線の分割数
#define X1 ( 0 )
#define Y1 ( 0 )
#define X4 ( 800 )
#define Y4 ( 600 )
#define X2 ( 200 )
#define Y2 ( 350 )
#define X3 ( 600 )
#define Y3 ( 500 )

#define CLASS_NAME "ベジェ"
#define WINDOW_NAME "ベジェ曲線"

LRESULT CALLBACK WndProc ( HWND hWnd , UINT uMsg , WPARAM wParam , LPARAM lParam );

int WINAPI WinMain ( HINSTANCE hInstance , HINSTANCE hPrevInstance , LPSTR lpCmdLine , int nCmdShow )
{
	UNREFERENCED_PARAMETER ( hPrevInstance );
	UNREFERENCED_PARAMETER ( lpCmdLine );
	
	WNDCLASSEX wcex =
	{
		sizeof ( WNDCLASSEX ) ,
		CS_CLASSDC ,
		WndProc ,
		0 ,
		0 ,
		hInstance ,
		NULL ,
		LoadCursor ( NULL , IDC_ARROW ) ,
		( HBRUSH ) ( COLOR_WINDOW + 1 ) ,
		NULL ,
		CLASS_NAME ,
		NULL
	};

	RegisterClassEx ( &wcex );

	static HWND hWnd;
	static MSG msg;

	// ウィンドウスタイル定義
	const int wstyle = WS_OVERLAPPEDWINDOW &~ WS_MINIMIZEBOX &~ WS_MAXIMIZEBOX &~ WS_THICKFRAME ;

	// ウィンドウを作成
	hWnd = CreateWindowEx ( 0 ,
							CLASS_NAME ,
							WINDOW_NAME ,
							wstyle ,
							CW_USEDEFAULT,
							CW_USEDEFAULT,
							SCREEN_WIDTH,
							SCREEN_HEIGHT,
							NULL ,
							NULL ,
							hInstance ,
							NULL
							);

	ShowWindow ( hWnd , nCmdShow );
	UpdateWindow ( hWnd );

	while ( GetMessage ( &msg , NULL , 0 , 0 ) )
	{
		TranslateMessage ( &msg );
		DispatchMessage ( &msg );
	}

	return msg.wParam;
}

void calcCurve(float* px, float* py, float t)
{
#if 1
	float mt = 1 - t;
	float a , b , c , d;

	a = mt * mt * mt;
	b = 3 * t * mt * mt;
	c = 3 * t * t * mt;
	d = t * t * t;

	*px = X1 * a + X2 * b + X3 * c + X4 * d;
	*py = Y1 * a + Y2 * b + Y3 * c + Y4 * d;
#else
	float p5 , p6 , p7 , p8 , p9;
	p5 = X1 * (1 - t) + X2 * t;
	p6 = X2 * (1 - t) + X3 * t;
	p7 = X3 * (1 - t) + X4 * t;
	p8 = p5 * (1 - t) + p6 * t;
	p9 = p6 * (1 - t) + p7 * t;
	*px = p8 * (1 - t) + p9 * t;
	p5 = Y1 * (1 - t) + Y2 * t;
	p6 = Y2 * (1 - t) + Y3 * t;
	p7 = Y3 * (1 - t) + Y4 * t;
	p8 = p5 * (1 - t) + p6 * t;
	p9 = p6 * (1 - t) + p7 * t;
	*py = p8 * (1 - t) + p9 * t;
#endif
}

LRESULT CALLBACK WndProc ( HWND hWnd , UINT uMsg , WPARAM wParam , LPARAM lParam )
{
	HDC hDC;
	int i;
	float px , py;
	PAINTSTRUCT ps;

	switch ( uMsg )
	{
		case WM_CREATE:
			break;

		case WM_PAINT:
			{
				// 曲線描画
				hDC = BeginPaint ( hWnd , &ps );

				calcCurve ( &px , &py , 0.0f );
				MoveToEx ( hDC , (int)px , (int)py , NULL );
				for (i = 1; i <= MAX; i++)
				{
					calcCurve ( &px , &py , i / (float)MAX );
					LineTo ( hDC , (int)px , (int)py );
				}

				// 描画終了
				EndPaint ( hWnd , &ps );

				break;
			}

		case WM_KEYDOWN:
			if( wParam == VK_ESCAPE )
			{
				UINT nID = MessageBox ( NULL , "ウィンドウを閉じますか?" , "Message" , MB_YESNO );
				if ( nID == IDYES )
				{
					DestroyWindow ( hWnd );
				}
				else if ( nID == IDNO )
				{
				MessageBox ( NULL , "OK" , "Message" , MB_OK );
				}
			}
			break;

		case WM_DESTROY:
			PostQuitMessage ( 0 );
			break;
	}

	return DefWindowProc ( hWnd , uMsg , wParam , lParam );
}
//====================   EOF   ====================//
「時間の経過に従って描画」するには、例えば時間によって描画する範囲を変えるといいでしょう。

コード:

#include <Windows.h>

#define SCREEN_WIDTH ( 800 )
#define SCREEN_HEIGHT ( 600 )
#define MAX ( 256 ) // 曲線の分割数
#define X1 ( 0 )
#define Y1 ( 0 )
#define X4 ( 800 )
#define Y4 ( 600 )
#define X2 ( 200 )
#define Y2 ( 350 )
#define X3 ( 600 )
#define Y3 ( 500 )

#define CLASS_NAME "ベジェ"
#define WINDOW_NAME "ベジェ曲線"

LRESULT CALLBACK WndProc ( HWND hWnd , UINT uMsg , WPARAM wParam , LPARAM lParam );

int WINAPI WinMain ( HINSTANCE hInstance , HINSTANCE hPrevInstance , LPSTR lpCmdLine , int nCmdShow )
{
	UNREFERENCED_PARAMETER ( hPrevInstance );
	UNREFERENCED_PARAMETER ( lpCmdLine );
	
	WNDCLASSEX wcex =
	{
		sizeof ( WNDCLASSEX ) ,
		CS_CLASSDC ,
		WndProc ,
		0 ,
		0 ,
		hInstance ,
		NULL ,
		LoadCursor ( NULL , IDC_ARROW ) ,
		( HBRUSH ) ( COLOR_WINDOW + 1 ) ,
		NULL ,
		CLASS_NAME ,
		NULL
	};

	RegisterClassEx ( &wcex );

	static HWND hWnd;
	static MSG msg;

	// ウィンドウスタイル定義
	const int wstyle = WS_OVERLAPPEDWINDOW &~ WS_MINIMIZEBOX &~ WS_MAXIMIZEBOX &~ WS_THICKFRAME ;

	// ウィンドウを作成
	hWnd = CreateWindowEx ( 0 ,
							CLASS_NAME ,
							WINDOW_NAME ,
							wstyle ,
							CW_USEDEFAULT,
							CW_USEDEFAULT,
							SCREEN_WIDTH,
							SCREEN_HEIGHT,
							NULL ,
							NULL ,
							hInstance ,
							NULL
							);

	ShowWindow ( hWnd , nCmdShow );
	UpdateWindow ( hWnd );

	while ( GetMessage ( &msg , NULL , 0 , 0 ) )
	{
		TranslateMessage ( &msg );
		DispatchMessage ( &msg );
	}

	return msg.wParam;
}

void calcCurve(float* px, float* py, float t)
{
#if 1
	float mt = 1 - t;
	float a , b , c , d;

	a = mt * mt * mt;
	b = 3 * t * mt * mt;
	c = 3 * t * t * mt;
	d = t * t * t;

	*px = X1 * a + X2 * b + X3 * c + X4 * d;
	*py = Y1 * a + Y2 * b + Y3 * c + Y4 * d;
#else
	float p5 , p6 , p7 , p8 , p9;
	p5 = X1 * (1 - t) + X2 * t;
	p6 = X2 * (1 - t) + X3 * t;
	p7 = X3 * (1 - t) + X4 * t;
	p8 = p5 * (1 - t) + p6 * t;
	p9 = p6 * (1 - t) + p7 * t;
	*px = p8 * (1 - t) + p9 * t;
	p5 = Y1 * (1 - t) + Y2 * t;
	p6 = Y2 * (1 - t) + Y3 * t;
	p7 = Y3 * (1 - t) + Y4 * t;
	p8 = p5 * (1 - t) + p6 * t;
	p9 = p6 * (1 - t) + p7 * t;
	*py = p8 * (1 - t) + p9 * t;
#endif
}

LRESULT CALLBACK WndProc ( HWND hWnd , UINT uMsg , WPARAM wParam , LPARAM lParam )
{
	HDC hDC;
	RECT rt;
	int i;
	float px , py;
	static unsigned int startTime;
	unsigned int elapsedTime;
	int drawMin, drawMax;
	PAINTSTRUCT ps;

	switch ( uMsg )
	{
		case WM_CREATE:
			startTime = GetTickCount();
			SetTimer ( hWnd, 0 , 20 , NULL );
			break;

		case WM_TIMER:
			InvalidateRect ( hWnd , NULL , FALSE );
			break;

		case WM_PAINT:
			{
				// 時刻によって描画する曲線の範囲を決める
				elapsedTime = GetTickCount() - startTime;
				drawMin = elapsedTime % 4000 < 2000 ? 0 : MAX * (elapsedTime % 2000) / 2000;
				drawMax = elapsedTime % 4000 < 2000 ? MAX * (elapsedTime % 2000) / 2000 : MAX;
				// 背景を塗りつぶす
				hDC = BeginPaint ( hWnd , &ps );
				GetClientRect ( hWnd , &rt );
				FillRect ( hDC , &rt , GetStockObject ( WHITE_BRUSH ) );
				// 曲線描画

				calcCurve( &px , &py , drawMin / (float)MAX );
				MoveToEx( hDC , (int)px , (int)py , NULL );
				for (i = drawMin + 1; i <= drawMax ; i++)
				{
					calcCurve( &px , &py , i / (float)MAX );
					LineTo ( hDC , (int)px , (int)py );
				}

				// 描画終了
				EndPaint ( hWnd , &ps );

				break;
			}

		case WM_KEYDOWN:
			if( wParam == VK_ESCAPE )
			{
				UINT nID = MessageBox ( NULL , "ウィンドウを閉じますか?" , "Message" , MB_YESNO );
				if ( nID == IDYES )
				{
					DestroyWindow ( hWnd );
				}
				else if ( nID == IDNO )
				{
				MessageBox ( NULL , "OK" , "Message" , MB_OK );
				}
			}
			break;

		case WM_DESTROY:
			PostQuitMessage ( 0 );
			break;
	}

	return DefWindowProc ( hWnd , uMsg , wParam , lParam );
}
//====================   EOF   ====================//
複雑な問題?マシンの性能を上げてOpenMPで殴ればいい!(死亡フラグ)

kloud

Re: DirectXでの曲線描画について

#5

投稿記事 by kloud » 9年前

返信遅くなりすみませんでした。
みけCATさんのコードを元にしてみるとうまくできました。
ありがとうございました。

閉鎖

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