hit   今日   昨日

画像をぐにゃぐにゃさせるには・・・?

フォーラムルール
フォーラムルールはこちら  ※コードを貼り付ける場合は [code][/code] で囲って下さい。詳しくはこちら
Name: J
[URL]
Date: 2008年9月05日(金) 04:48
No: 1
(OFFLINE)

 画像をぐにゃぐにゃさせるには・・・?

こんにちは。
画像に加工をかけて、ぐにゃぐにゃした画像をさらにアニメーションでぐにゃぐにゃさせてたい(?)のですが・・・

こんな説明ではわからないとは思いますので、サンプルを。
http://home.impress.co.jp/books/urawaza/tipsdic/sample175-200/tips176a.html
このサイトのいちごの画像です。ぐにゃぐにゃ。
IEでないとおそらく見れません。
見れない人は・・・そうですね・・・かなりアレなネタになりますが、ポケット○ンスター 金銀あたりのサイコキネシスのアニメーションです。わかる人は相当少ないと思いますが(汗

このぐにゃぐにゃを表現するにはどうすれば良いでしょうか?アドバイスをお願いします。
環境はBCCでDXライブラリ使用です。

Name: GPGA
[URL]
Date: 2008年9月05日(金) 10:29
No: 2
(OFFLINE)

 Re:画像をぐにゃぐにゃさせるには・・・?

ラスタースクロールですね。
画像を縦一ドット単位で、X座標をずらしながら表示することで実現できます。
X座標のずらしは、sinやcosの使用をおすすめします。

Name: toyo
[URL]
Date: 2008年9月05日(金) 10:50
No: 3
(OFFLINE)

 Re:画像をぐにゃぐにゃさせるには・・・?

ゲーム専用機ならH-Blank毎の割り込み使ってX位置変化させるだけですむんですけどね
DxLibならGIFアニメのように複数の画像を用意して表示するのが簡単かな

Name: 管理人
[URL]
Date: 2008年9月05日(金) 11:10
No: 4
(OFFLINE)

 Re:画像をぐにゃぐにゃさせるには・・・?

サンプルを作ってみました。
ぐにゃぐにゃさせたい画像をapple.pngとしています。
ここの画像は任意にご変更下さい。

実行結果

元の画像 → http://dixq.net/img/apple.png

変換後
画像

#include "DxLib.h"
#include <math.h>
#define PI 3.1415926535898

//周期
#define T 200
//ぐにゃぐにゃする間隔
#define L 0.6
//ぐにゃぐにゃする大きさ
#define R 20

int WINAPI WinMain(HINSTANCE hInstance,
HINSTANCE hPrevInstance,LPSTR lpCmdLine, int nCmdShow ){
int i,image,count=0;
ChangeWindowMode(TRUE);//ウィンドウモード
if(DxLib_Init() == -1 || SetDrawScreen( DX_SCREEN_BACK )!=0) return -1;

image=LoadGraph("apple.png");

while(ProcessMessage()==0&&ClearDrawScreen()==0&&CheckHitKey( KEY_INPUT_ESCAPE )==0){

for(i=0;i<480;i++){
SetDrawArea( 0 , i , 640 , i+1 ) ;
DrawGraph(100+R*sin(PI*2*((int)(count+i/L)%T)/T),0,image,TRUE);
}
count++;
ScreenFlip();
}

DxLib_End();
return 0;
}

 
 
以下プログラムコードの説明です。

表示サイズが640x480なので、480回にわけて、
1行ずつx座標をずらしながら描画を繰り返しています。
その際、描画領域を設定する関数SetDrawAreaで描画可能エリアをセットします。
1回目の描画は0<=x<640,0<=y<1
2回目の描画は0<=x<640,1<=y<2
3回目の描画は0<=x<640,2<=y<3
・・・の範囲でyを480まで増加させます。

x座標の計算式はこれです。

100+R*sin(PI*2*((int)(count+i/L)%T)/T)

見た目わけわかんないですね・・。

まず、sinの性質はOKでしょうか。
2πで1周期になりますね。その間、-1<=sin(θ)<=1の間を綺麗に移動してくれます。
今振幅(動かしたい幅)を例えば20とすると

sin(θ)*20は上記の式から

-20<=sin(θ)*20<=20

となります。これで揺らしたい幅がセットできます。
お次は揺らしたい周期です。どれだけでゆらゆらゆれてもとの位置に戻るかです。
ここで、モニタは60FPSだとします。
countは1周で1増えますから、1秒だと60増えますね。
すると(count%60)/60.0は1秒かけて0から1(に近い値)まで増加します。
1秒かけて2πまで増加させたいのなら

2π*(count%60)/60.0

となります。この60は周期なのでTで定義しておきます。また、πもPIとして定義します。

2*PI*(count%T)/T (この時、PIがdoubleなので、全体はdoubleになります。)

後は、y座標によってゆれる位置を変えたいので、countにy座標であるiを足してみます。
どれ位足すかはLで調整出来るようにしておきます。

count+i/L

こうなりますね。これらを全てあわせると

100+R*sin(PI*2*((int)(count+i/L)%T)/T)

となるわけです。Rは上でいう20、100は単に初期位置です。

 
T,L,Rの値を自分がしたい動作になるように変更して下さい。
 
 

Name: 管理人
[URL]
Date: 2008年9月05日(金) 17:35
No: 5
(OFFLINE)

 Re:画像をぐにゃぐにゃさせるには・・・?

ちょうどいい値を見つけられるようツールモドキにしてみました。
先ほどdefineで定義していた値はそのキーを押しながら左右キーを押す事で変動します。

つまり周期Tを変化させたい時は、キーボードのTキーを押しながら十字キーの左右を押してください。
L,Rキーも押しながら左右キーを押す事でそれぞれ値が変動します。
確認しながら値が変更できるので、ちょうど良い値が見つけやすいと思います。

なお、0割りなどのエラー処理はしていませんので、値が0や負にならないようにして下さい。

#include "DxLib.h"
#include <math.h>
#define PI 3.1415926535898

//周期
int T=200;
//ぐにゃぐにゃする間隔
double L=0.6;
//ぐにゃぐにゃする大きさ
int R=20;

int Key[256];

int GetHitKeyStateAll_2(int GetHitKeyStateAll_InputKey[/url]){
char GetHitKeyStateAll_Key[256];
GetHitKeyStateAll( GetHitKeyStateAll_Key );
for(int i=0;i<256;i++){
if(GetHitKeyStateAll_Key[i]==1) GetHitKeyStateAll_InputKey[i]++;
else GetHitKeyStateAll_InputKey[i]=0;
}
return 0;
}

int WINAPI WinMain(HINSTANCE hInstance,
HINSTANCE hPrevInstance,LPSTR lpCmdLine, int nCmdShow ){
int i,image,count=0;
ChangeWindowMode(TRUE);//ウィンドウモード
if(DxLib_Init() == -1 || SetDrawScreen( DX_SCREEN_BACK )!=0) return -1;

image=LoadGraph("apple.png");

while(ProcessMessage()==0&&ClearDrawScreen()==0&&
GetHitKeyStateAll_2(Key)==0 && Key[KEY_INPUT_ESCAPE]==0){

for(i=0;i<480;i++){
SetDrawArea( 0 , i , 640 , i+1 ) ;
DrawGraph(100+R*sin(PI*2*((int)(count+i/L)%T)/T),0,image,TRUE);
}
if(Key[KEY_INPUT_T]>0){
if(Key[KEY_INPUT_LEFT ]==2)T-=10;
if(Key[KEY_INPUT_RIGHT]==2)T+=10;
}
if(Key[KEY_INPUT_[/url]>0){
if(Key[KEY_INPUT_LEFT ]==2)L-=0.05;
if(Key[KEY_INPUT_RIGHT]==2)L+=0.05;
}
if(Key[KEY_INPUT_[/url]>0){
if(Key[KEY_INPUT_LEFT ]==2)R-=1;
if(Key[KEY_INPUT_RIGHT]==2)R+=1;
}
SetDrawArea( 0 , 0 , 640 , 480 ) ;
DrawFormatString(0,460,GetColor(255,255,255),"T=%d,L=%.2f,R=%d",T,L,R);
count++;
ScreenFlip();
}

DxLib_End();
return 0;
}


画像
 
 

Name: J
[URL]
Date: 2008年9月05日(金) 21:17
No: 6
(OFFLINE)

 Re:画像をぐにゃぐにゃさせるには・・・?

す、凄まじい計算式ですね・・・圧巻です。。
こういう式を思いつく人には頭が上がりません(笑)
三角関数は確かに習いましたが、こういう形になると全く理解していないことに気がつかされます(汗
アニメーション全般に関わると言われているんですよね・・・三角関数は。
是非ともどこかでマスターしたいものです。言うだけ言えますけどね(大汗

皆様、回答ありがとうございます。管理人さんのソースは今実行してみるので、
質問があったらまた書き込みます。

Name: 管理人
[URL]
Date: 2008年9月05日(金) 22:36
No: 7
(OFFLINE)

 Re:画像をぐにゃぐにゃさせるには・・・?

いえいえ、式がややこしくみえるだけで、全く難しいことは書いて無いです。
2πで1周期になること、sin(θ)は-1から1まで変化することだけ知っていれば
誰にでも作れる式だと思います。
上の例では常に増加するカウンタを使って任意の周期に変換するという作業が入っているので少々
難しそうに見えるだけだと思います。
私も数学はべつに得意じゃないですし、特に三角関数を意識したこともないです。
なので、あまり構えずにリラックスしてみてみてくださいb

Name: 木霊
[URL]
Date: 2008年9月06日(土) 01:33
No: 8
(OFFLINE)

 Re:画像をぐにゃぐにゃさせるには・・・?

関数化してみました。
第一引数 画像を表示する領域の中心座標(X)
第二引数 画像を表示する領域の中心座標(Y)
第三引数 表示する画像のグラフィックハンドル
第四引数 ラスタースクロールの周期
第五引数 ラスタースクロールの振動幅

const double PI = 3.14159265358979323846;/* グローバル変数 */
void Raster_Scroll ( const int &X, const int &Y, const int &Gr_Handle, double Cycle, double Shake )
{
int Width = 0, Height = 0;/*画像の横幅と縦幅*/
static int Correction = 0;/*ラスタースクロールの補正*/
GetGraphSize ( Gr_Handle, &Width, &Height );
for( int I = 0; I < Height; ++ I ) {
DrawRectGraph ( X -Width /2 +cos ( ( I +Correction )/ 180.0 *PI *Cycle ) *Shake, Y -Height /2 +I,
0, I, Width, 1, Gr_Handle, TRUE, FALSE );
}
++ Correction;
return;
}

色んなやり方を見た方が勉強になるので管理人さんとは別のDxLib関数を使いました。

Name: レッツ
[URL]
Date: 2008年9月06日(土) 02:44
No: 9
(OFFLINE)

 Re:画像をぐにゃぐにゃさせるには・・・?

横から質問すみません。
constって値を変えられなくするんですよね。
一方ポインタで受け取るのは値が書き換えられるようにするためですよね。
constでポインタで受け取る意味ってあるのでしょうか?

Name: 木霊
[URL]
Date: 2008年9月06日(土) 03:16
No: 10
(OFFLINE)

 Re:画像をぐにゃぐにゃさせるには・・・?

void Function ( int argv )
このような普通の形だと、関数が呼ばれた時にint型の変数argvが新しく宣言、値がコピーされ、関数を抜ける時に破棄されます。
void Function ( const int &argv )
一方、こちらはアドレスを介して値を参照してるわけです。

これがint型やdouble型ならたいした意味はないかもしれませんが、構造体だったらどうでしょう?
上の形だと構造体が新しく宣言されるので、そこに含まれる全ての変数が宣言されます。
構造体のメンバに更に別の構造体・・・となるとかなりの数の変数が宣言され、破棄されるわけです。
下のように参照渡しにすると、無駄な変数宣言が減ることになります。

そして、アドレスを渡すということは値を変更できる、ということなんですが、
今回は「逆に変更されたくない」からconst宣言をしているわけです。

Name: Mist
[URL]
Date: 2008年9月06日(土) 09:58
No: 11
(OFFLINE)

 Re:画像をぐにゃぐにゃさせるには・・・?

> constでポインタで受け取る意味ってあるのでしょうか?

簡単に言うと、ポインタを書き換えるのはダメだけど、ポインタの指すエリアの値は更新してもいいよってこと。
関数内で間違ってポインタを書き換えてしまうようなソースを書いたとき、コンパイルエラーで見つけられるようにするための方法。

Name: フリオ
[URL]
Date: 2008年9月06日(土) 11:16
No: 12
(OFFLINE)

 Re:画像をぐにゃぐにゃさせるには・・・?

 
 "const" がつく場所によって意味が変わります。

"const int *np" の場合は、
"np ++" は可ですが、"(*np) ++" は不可。
つまり、ポインタの値は変更できるが、
そのポインタによって参照される値は変更できない。

"int *const np" の場合は、
"np ++" は不可ですが、"(*np) ++" は可。
つまり、ポインタの値は変更できないが、
そのポインタによって参照される値は変更できる。

"const int *const np" の場合は、
"np ++" も、"(*np) ++" も不可。
つまり、ポインタの値も、
そのポインタによって参照される値も変更できない。
 


Return to C言語何でも質問掲示板

オンラインデータ

このフォーラムを閲覧中のユーザー: a5ua, smr_c & ゲスト[7人]

フィード