- 題名: 「ぐにゃぐにゃ」なエフェクトに時間がかかる
- 日時: 2011/07/28 3:20:59
- ID: 28798
- この記事の返信元:
- (なし)
- この記事への返信:
- [28801] Re[1]: 「ぐにゃぐにゃ」なエフェクトに時間がかかる2011/07/28 11:41:23
- ツリーを表示
こんにちは、おのでらです。
> やはり、640*480の画像と800*600の画像では動作速度が異なりました。
まあ、画像サイズが大きくなると遅くなるのは一般的な話ですね(^^;)。単純に計算してもわかるように比例して処理コストが上がるので…
640*480 = 307,200
800*600 = 480,000 (1.6倍のコスト)
1280*720 = 921,600 (640〜の3倍のコスト)
> DoubleBufferedはtrueに設定されていました。
> ためしにfrm->CreateGraphics()にしてみたところ、若干早くなりましたが、
> 画面が点滅するようになってしまいました
ゲームのような描画速度を主体にするものはできる限り BackgroundImage や Refresh は使わない方がいいですね。
> ピクセルシェーダはグラフィックカードの対応が必要なんでしたっけ?
はい。グラフィックカードごとに対応しているピクセルシェーダのバージョンが決まっているので使う場合は気を付けないといけません。
DirectX 10 以降対応と書いてあれば ピクセルシェーダバージョン 4.0 以降が必ず使えます。
まあ最近の PC であればチップセットレベルで最低でもバージョン 2.0 に対応しているので大丈夫だと思いますが・・・。
時間が取れたのでためしてみました。
おそらくですが、遅くなると思われる原因がわかりました。
> Rectangle rect = Rectangle(0,i,bmp->Width,1);
> g->DrawImage(bmp,Convert::ToInt32(Math::Sin((count+i)/10.f)*width),i,rect,GraphicsUnit::Pixel);
で描画先の指定を X, Y しか指定していないと思いますが、
この場合描画先の範囲が指定されていないので X, Y 以降、下の領域に対してすべて描画してしまいます。例を挙げると
【1ループ内の処理】
高さ範囲 0〜639 に対して描画
高さ範囲 1〜639 に対して描画
高さ範囲 2〜639 に対して描画
:
高さ範囲 639〜639 に対して描画
となるので明らかに下の方で無駄な描画処理が発生してしまっています。DramImage メソッドを呼ぶ時は
int offset = Convert::ToInt32(Math::Sin((count + h) / 10f) * width);
Rectangle srcRect = Rectangle(0, h, bmp->Width, 1);
Rectangle destRect = Rectangle(offset, h, bmp->Width, 1);
g->DrawImage(bmp, destRect, srcRect, GraphicsUnit::Pixel);
のように描画先の範囲も指定しましょう。私の環境では 50 倍ぐらい早くなりました。
一応私の方で試したサンプルコードを載せておきます。
C# で書いてしまっていますが、同じ .NET Framework を使用しているので C++/CLI でもそのまま置き換えて読めると思います。
(※ using はスコープを抜けるときにリソースを自動的に破棄する構文です(Disposeメソッドを呼ぶ))
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Drawing;
using System.Drawing.Imaging;
using System.Runtime.InteropServices;
using System.Linq;
using System.Text;
using System.Windows.Forms;
namespace RasterScrollEffect
{
class Program
{
static void Main(string[] args)
{
using (Form1 frm = new Form1()) // ウィンドウを作成して実行します (From1 を新規作成した状態です)
using (Bitmap bmpLoad = new Bitmap("画像_1280x720.bmp")) // 画像読み込み
using (Bitmap bmpRender = new Bitmap(bmpLoad.Width, bmpLoad.Height)) // 一時書き込み先ビットマップ
{
//===================================================
// フォーム関連
//===================================================
// フォームのサイズを画像に合わせる
frm.ClientSize = new Size(bmpLoad.Width, bmpLoad.Height);
// フォーム表示
frm.Show();
//===================================================
// 値の初期化
//===================================================
int count = 0;
float width = 5f;
float width_max = 50;
float width_add = 0.15f;
// 画像の矩形サイズ(事前作成)
Rectangle baseRect = new Rectangle(0, 0, bmpLoad.Width, bmpLoad.Height);
// 画像の幅と高さ
int bmpW = bmpLoad.Width;
int bmpH = bmpLoad.Height;
// 描画計測用
Stopwatch swAll = new Stopwatch();
Stopwatch swRaster = new Stopwatch();
//===================================================
// ループ
//===================================================
while (frm.Created)
{
// フォームのグラフィクスデバイスコンテキスト作成
using (Graphics formG = frm.CreateGraphics())
using (Graphics renderG = Graphics.FromImage(bmpRender))
{
// 1ループの計測
swAll.Restart();
count++;
// 背景黒
renderG.FillRectangle(Brushes.Black, 0, 0, bmpW, bmpH);
//===================================================
// ゆらす計算
//===================================================
// ゆらす処理と一時バッファ内の描画計測
swRaster.Reset();
swRaster.Start();
// ゆれの大きさを時間によって変化させる
if (width_add > 0)
{
if ((width += width_add) > width_max)
{
width_add = -width_add;
}
}
else
{
if ((width += width_add) < 0)
{
width = 0;
width_add = -width_add;
}
}
//===================================================
// ゆらす描画
//===================================================
for (int h = 0; h < bmpH; h++)
{
int offset = Convert.ToInt32(Math.Sin((count + h) / 10f) * width);
Rectangle srcRect = new Rectangle(0, h, bmpW, 1);
Rectangle destRect = new Rectangle(offset, h, bmpW, 1);
renderG.DrawImage(bmpLoad, destRect, srcRect, GraphicsUnit.Pixel);
//renderG.DrawImage(bmpLoad, offset, h, srcRect, GraphicsUnit.Pixel);
}
swRaster.Stop();
//===================================================
// フォームに描画
//===================================================
formG.DrawImage(bmpRender, Point.Empty);
swAll.Stop();
// 「出力」に経過時間 表示
Trace.WriteLine("Raster Ellapse =" + swAll.ElapsedMilliseconds.ToString());
Trace.WriteLine("All Ellapse =" + swRaster.ElapsedMilliseconds.ToString());
// UI に処理を返す
Application.DoEvents();
}
}
}
}
}
}
分類:[.NET]
いつもお世話になっています。
現在、ノベルゲームエンジンもどきを作成していまして、
画像切り替えのエフェクトに「ぐにゃぐにゃ」を導入したいと思いました。
インターネットで検索しましたが、あまり見つからず、唯一見つけたのが下のサイトのものでした。
http://takabosoft.com/20000918163126.html
この情報も古いものでしたが、頑張って自己流に書き直したコードが下のものです。
int main(array<System::String ^> ^args)
{
// コントロールが作成される前に、Windows XP ビジュアル効果を有効にします
Application::EnableVisualStyles();
Application::SetCompatibleTextRenderingDefault(false);
// メイン ウィンドウを作成して、実行します
Form1^ frm = gcnew Form1();
frm->Show();
// ここからがぐにゃぐにゃ
// 初期化
int count = 0;
float width=5.f,width_max=50,width_add=0.15f;
Bitmap^ bmp = gcnew Bitmap("画像.bmp");
Bitmap^ newbmp = gcnew Bitmap(bmp->Width,bmp->Height);
Graphics^ g = Graphics::FromImage(newbmp);
while (frm->Created) {
g->FillRectangle(Brushes::Black,0,0,bmp->Width,bmp->Height);
count++;
//ゆれの大きさを時間によって変化させる
if (width_add > 0) {
if((width += width_add) > width_max) {
width_add =- width_add;
}
}
else {
if((width += width_add) < 0) {
width=0;
width_add =- width_add;
}
}
//ゆらす
for(int i = 0; i < bmp->Height; i++) {
Rectangle rect = Rectangle(0,i,bmp->Width,1);
g->DrawImage(bmp,Convert::ToInt32(Math::Sin((count+i)/10.f)*width),i,rect,GraphicsUnit::Pixel);
}
frm->BackgroundImage = newbmp;
frm->Refresh();
}
return 0;
}
これで一応起動すると、「画像.bmp」がぐにゃぐにゃするようになりましたが、
サンプルコードのものとは挙動が違いますし、描画に時間がかかりすぎます。
挙動が違う件に関しては式をいじれば直るのかもしれませんが、
描画に時間がかかりすぎるのは問題なので、どこを直せばいいでしょうか?
どなたかわかる方、回答をお願いします。