トップページ > 記事閲覧
w×h×3ch(RGB)の配列を描画したい
名前:ガムテープ人形 日時: 2019/05/10 17:51

タイトルの通りで array<array<array<int,3>,w>,h> のような配列に格納されている情報から描画を行いたいです 現在考えているのはw×hのscreenを作ってそこに1ピクセルずつ描画する方法なのですが、 これでは描画に時間が掛かり非効率に思えます 何か良い方法はないでしょうか
メンテ

Page: 1 | 2 |

Re: w×h×3ch(RGB)の配列を描画したい ( No.1 )
名前:管理人 日時:2019/05/11 02:42

お察しの通り DrawPixel で1ピクセルづつ描画するのは非効率なので、 MakeRGB8ColorSoftImage で 24bitカラー( RGB )の『CPUで扱うイメージ』を作成して GetImageAddressSoftImage で画像が格納されているメモリアドレスを取得して、 ポインタで直接色情報を書き込んだ後、DrawSoftImage で画面に描画するのが一番高速です MakeRGB8ColorSoftImage で 640x480 サイズの『CPUで扱うイメージ』を作成して、 毎フレーム全ピクセルに対して『円の画像』の色をポインタを使って直接書き込み、 DrawSoftImage で画面に描画する、というサンプルを作成してみましたので、 よろしければご覧ください m(_ _)m #include "Dxlib.h" int WINAPI WinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow ) { int ScreenSoftImage ; int CircleR ; int CircleRAdd ; ChangeWindowMode( TRUE ) ; // DXライブラリ初期化処理 if( DxLib_Init() == -1 ) { // エラーが起きたら直ちに終了 return -1 ; } // 640x480サイズのRGBのソフトウェア画像を作成 ScreenSoftImage = MakeRGB8ColorSoftImage( 640, 480 ) ; // 描画先を裏画面にする SetDrawScreen( DX_SCREEN_BACK ) ; // 大きさが変化する円の情報を初期化 CircleR = 0 ; CircleRAdd = 4 ; // メインループ while( ProcessMessage() == 0 ) { // 円の大きさを変更 CircleR += CircleRAdd ; if( CircleR >= 400 || CircleR <= 0 ) { CircleRAdd = -CircleRAdd ; } // ソフトウェア画像の中心に黄色い円を描画する( その周りは青色 ) { // ソフトウェア画像のイメージが格納されているメモリアドレスを取得 BYTE *p = ( BYTE * )GetImageAddressSoftImage( ScreenSoftImage ) ; // 640x480 の全ピクセルの数だけループ for( int i = 0 ; i < 480 ; i ++ ) { for( int j = 0 ; j < 640 ; j ++ ) { // 円の大きさより外側かどうかを判定 int xp = 320 - j ; int yp = 240 - i ; if( xp * xp + yp * yp < CircleR * CircleR ) { // 円の外側だったらピクセルの色を青色にする // ( 色のメモリ上の順番は B・G・R ) p[ 0 ] = 255 ; p[ 1 ] = 0 ; p[ 2 ] = 0 ; } else { // 円の内側だったらピクセルの色を黄色にする p[ 0 ] = 0 ; p[ 1 ] = 255 ; p[ 2 ] = 255 ; } // メモリアドレスを 1ピクセル分( 3バイト )進める p += 3 ; } } } // ソフトウェア画像を画面に描画する DrawSoftImage( 0, 0, ScreenSoftImage ) ; // 裏画面の内容を表画面に反映 ScreenFlip() ; } // DXライブラリ使用の終了処理 DxLib_End() ; // ソフトの終了 return 0 ; }
メンテ
Re: w×h×3ch(RGB)の配列を描画したい ( No.2 )
名前:ガムテープ人形 日時:2019/05/12 00:38

わざわざサンプルコードまでありがとうございます。 DrawSoftImageの説明を読んでみたところ負荷が非常に高いとありました。 この方法で描画を行う以上この関数を使用する他ないのでしょうか。 >グラフィックハンドルの描画に比べて負荷が非常に高いので、確認以外の用途で使用することは避けた方が賢明です。 dxlib.xsrv.jp/function/dxfunc_other.html#R20N15
メンテ
Re: w×h×3ch(RGB)の配列を描画したい ( No.3 )
名前:管理人 日時:2019/05/12 02:41

> DrawSoftImageの説明を読んでみたところ負荷が非常に高いとありました。 > この方法で描画を行う以上この関数を使用する他ないのでしょうか。 はい、DrawSoftImage は DrawGraph 等に比べると非常に低速ですが、現状ではビットマップを 描画する最も高速な手段は DrawSoftImage となっています DirectDraw時代は画面の VRAM領域に直接データを転送する方法があり、それが一番高速なビットマップ表示方法 だったのですが、DirectX 8 以降は DirectDraw が無くなってしまったのでその方法は使えなくなりました… ( 現在でも DirectX 7 を使用することはできますが、WindowsXP までの頃と異なりエミュレートされた ものなので無理矢理 DirectX 7 を使用しても非常に低速な動作となってしまいます… ) DirectX を使用せず DIB で表示する方法もありますが、こちらは VSYNC 関係が綺麗に行われず 見た目の動きがガクガクになってしまいます…
メンテ
Re: w×h×3ch(RGB)の配列を描画したい ( No.4 )
名前:ギウ 日時:2019/05/12 08:24

(横から失礼します) DIBは、今のPCなら気にならないレベルですよ。 (DXLibを使う数年前まで、DIBで描画してました。Steamに登録した時もDIB描画方式) Windows専用になっても良いなら&全部自身で描画できるなら、選択肢としてありかなと思います。 (追記) DrawSoftImageの方法、Windowsなら負荷は気にならないレベルではと思います。(Androidだと気になるんですが) DrawSoftImageなら他のDXLibの描画命令と併用できるメリットがありますね。
メンテ
Re: w×h×3ch(RGB)の配列を描画したい ( No.5 )
名前:管理人 日時:2019/05/13 00:37

> ギウさん > DIBは、今のPCなら気にならないレベルですよ。 > (DXLibを使う数年前まで、DIBで描画してました。Steamに登録した時もDIB描画方式) いえ、DIB は処理負荷は低いのですが、VSYNC 待ちが DirectX ほど正確に行われないので 以下のような等速直線運動する物体を表示すると動きがガクガクになるのです… #include "DxLib.h" int WINAPI WinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow ) { int x, add ; // ウインドウモードで起動 ChangeWindowMode( TRUE ) ; // ソフトウェアレンダリングモード( DIB )で起動 SetScreenMemToVramFlag( FALSE ) ; // DXライブラリの初期化 if( DxLib_Init() < 0 ) return -1 ; // 描画先を裏画面にする SetDrawScreen( DX_SCREEN_BACK ) ; // メインループ x = 0 ; add = 8 ; while( ProcessMessage() == 0 ) { // 移動 x += add ; if( x < 0 || x > 640 ) add = -add ; // 画面のクリア ClearDrawScreen() ; // 四角形の描画 DrawBox( x, 240 - 32, x + 64, 240 + 32, GetColor( 255,255,255 ), TRUE ) ; // 裏画面の内容を表画面に反映 ScreenFlip() ; } // DXライブラリの後始末 DxLib_End() ; // ソフトの終了 return 0 ; } と、思って実行してみたのですが、ガクガクすることなく表示されました… すみません、少し前まで Windows7 を使用していて、Windows7 では間違いなくガクガクに なっていたのですが、現在使用している Windows10 では綺麗に VSYNC待ちができているようで ガクガクになることはありませんでした m(_ _;m Windows8 〜 Windows10 の間で改善されたようです… > ガムテーブ人形さん すみません、こちらの問題『DirectX を使用せず DIB で表示する方法もありますが、こちらは VSYNC 関係が綺麗に行われず見た目の動きがガクガクになってしまいます…』は Windows10 では 発生しないようです もし DrawGraph や DrawBox などの描画関数を一切使用せず、すべての表示処理を自前で 行われてDXライブラリでは wxhx3ch(RGB) の配列を描画するだけ、ということでしたら ソフトウェアレンダリングモードを使用して以下のように GraphLock を使用して 裏画面に直接配列を書き込んだほうが高速になります #include "Dxlib.h" int WINAPI WinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow ) { int GrHandle ; int CircleR ; int CircleRAdd ; // ウィンドウモードで起動 ChangeWindowMode( TRUE ) ; // ソフトウェアレンダリングモードを使用する SetScreenMemToVramFlag( FALSE ) ; // VSYNC同期をしない SetWaitVSyncFlag( FALSE ) ; // 画面モードを設定 SetGraphMode( 640, 480, 32 ) ; // DXライブラリ初期化処理 if( DxLib_Init() == -1 ) { // エラーが起きたら直ちに終了 return -1 ; } // 描画先を裏画面にする SetDrawScreen( DX_SCREEN_BACK ) ; // 大きさが変化する円の情報を初期化 CircleR = 0 ; CircleRAdd = 4 ; // メインループ while( ProcessMessage() == 0 ) { // 円の大きさを変更 CircleR += CircleRAdd ; if( CircleR >= 400 || CircleR <= 0 ) { CircleRAdd = -CircleRAdd ; } // ソフトウェア画像の中心に黄色い円を描画する( その周りは青色 ) { int Pitch ; BYTE *Image ; // 裏画面のアドレスとピッチを取得する GraphLock( DX_SCREEN_BACK, &Pitch, ( void ** )&Image ) ; // 640x480 の全ピクセルの数だけループ for( int i = 0 ; i < 480 ; i ++ ) { BYTE *p = Image ; for( int j = 0 ; j < 640 ; j ++ ) { // 円の大きさより外側かどうかを判定 int xp = 320 - j ; int yp = 240 - i ; if( xp * xp + yp * yp < CircleR * CircleR ) { // 円の外側だったらピクセルの色を青色にする // ( 色のメモリ上の順番は B・G・R ) p[ 0 ] = 255 ; p[ 1 ] = 0 ; p[ 2 ] = 0 ; } else { // 円の内側だったらピクセルの色を黄色にする p[ 0 ] = 0 ; p[ 1 ] = 255 ; p[ 2 ] = 255 ; } // メモリアドレスを 1ピクセル分( 3バイト )進める p += 4 ; } // アドレスを1ライン分進める Image += Pitch ; } // 裏画面のロックを解除 GraphUnLock( DX_SCREEN_BACK ) ; } // 裏画面の内容を表画面に反映 ScreenFlip() ; } // DXライブラリ使用の終了処理 DxLib_End() ; // ソフトの終了 return 0 ; } 手元の環境で計測したところ、それぞれの処理時間は以下の通りでした ソフトウェアレンダリングモードでGraphLock/GraphUnlock + ScreenFlip 0.09ミリ秒 DrawSoftImage + ScreenFlip 0.8ミリ秒
メンテ
Re: w×h×3ch(RGB)の配列を描画したい ( No.6 )
名前:ギウ 日時:2019/05/13 08:00

>管理人様 GraphLockという命令もあったんですね。 以前、ReCreateGraphFromSoftImage というのを教えて頂いて使ってるんですが、 一括転送だと ReCreateGraphFromSoftImage の方が速くなるんでしょうか。(内部的にはほとんど同じ?) あ、比べないとわからない場合は、自分で確かめてみます。 P.S >Windows7 では間違いなくガクガクに Win95の頃はチラついてたんですが、Win7はそうでもなかったような…。 もしかしたら20fpsくらいで動かしてたから気にならなかったのかもです。 自分でウエイトかけないと、300fpsとかになるので。
メンテ

Page: 1 | 2 |

題名
名前
コメント
パスワード (記事メンテ時に使用)

   クッキー保存