HSPポータル
サイトマップ お問い合わせ


HSPTV!掲示板


未解決 解決 停止 削除要請

2009
0525
ななしさんHGIMG3での、3D→2D座標変換7解決


ななしさん

リンク

2009/5/25(Mon) 23:38:32|NO.25379

3Dオブジェクトの座標をスクリーン座標に変換して、3Dシューティングゲームのロックオンマーカーのようなものを表示したいのですが、どうしても上手くいきません。

手順としては、

1.カメラの位置と、角度を求める。
2.対象オブジェクトの座標を求める
3.オブジェクトの座標からカメラの座標を引いて、カメラを原点としたローカル座標を求める。
4.fvdirを使って、ローカル座標を、カメラの角度だけ回転。
5.X座標、Y座標を、Z座標で割る。
6.x座標、Y座標をスクリーンのXサイズを掛る。
7.スクリーンの中心座から、先ほど求めたX座標、Y座標を引く

で、やってみましたけど、ダメでした。
カメラのx軸の角度が少しでもあると、y座標がおかしくなりますし、たとえx,y,zとも角度が0でも、誤差が出ます。

カメラ角度で座標を変換し、x・y座標をz座標で割れば良いと思ったのですが、どこがおかしいのか、教えていただけないでしょうか?



この記事に返信する


KIMU

リンク

2009/5/26(Tue) 04:30:12|NO.25384

3年ぐらい前に作ったモジュールなので自分でも説明が・・・
間違ってる可能性も・・・


#include "hgimg3.as" #module hgpcv // _rangescalxy [スケール変更時に変換係数をスケールに合わせる(初期化時、efx変更時に使われる)] #define global _rangescalxy rangescalx@hgpcv=scalx@hgpcv/rangecoef@hgpcv:rangescaly@hgpcv=scaly@hgpcv/rangecoef@hgpcv:rangescalz@hgpcv=scalz@hgpcv/rangecoef@hgpcv // _pcvsetcpos _pcvsetcang _pcvsetcscale _pcvsetcefx [hgpcv側にカメラ情報をセット(初期化時、カメラ操作時に使われる)] #define global _pcvsetcpos getpos HGOBJ_CAMERA,cx@hgpcv,cy@hgpcv,cz@hgpcv #define global _pcvsetcang getang HGOBJ_CAMERA,crx@hgpcv,cry@hgpcv,crz@hgpcv #define global _pcvsetcscale getscale HGOBJ_CAMERA,scalx@hgpcv,scaly@hgpcv,scalz@hgpcv:_rangescalxy@ #define global _pcvsetcefx getefx HGOBJ_CAMERA,FOV@hgpcv,NearZ@hgpcv,FarZ@hgpcv //hgfvwork [%1を省略した時に使われる配列変数] #define global hgfvwork fvwork@hgpcv #define global hgfvwork2 fvwork2@hgpcv // _getfvector %1,%2,%3,%4 [カメラレイのベクトルを収得] // %1 ベクトルが代入される変数名(配列要素4) // %2,%3 x,yスクリーン座標(実数値) #define global _getfvector(%1=fvwork@hgpcv,%2=0.0,%3=0.0) \ fvset %1,crx@hgpcv,-cry@hgpcv,crz@hgpcv: \ fvdir %1,double(%2)/rangescalx@hgpcv,double(%3)/rangescaly@hgpcv,-1.0: \ vx@hgpcv=%1:vy@hgpcv=%1(1):vz@hgpcv=%1(2) // _getfvwpos %1,%2,%3,%4,%5 [スクリーン座標をカメラからの距離とベクトルでワールド座標に変換] // %1 ワールド座標が代入される変数名(配列要素4) // %2 カメラからの距離(実数値) // %3,%4,%5 x,y,zベクトル(実数値)省略時は_getfvwpos実行前に_getfvectorで収得したものが使われる #define global _getfvwpos(%1=fvwork@hgpcv,%2,%3=vx@hgpcv,%4=vy@hgpcv,%5=vz@hgpcv) \ fvset %1,(%3),(%4),(%5): \ fvmul %1,(%2),(%2),(%2): \ fvadd %1,cx@hgpcv,cy@hgpcv,cz@hgpcv //hgposcvfovini [hgposcviniで初期化した数値から3D2D座標変換係数をセット] #deffunc hgpcvfovini hfvo=FOV/2 halfscr=sin(hfvo)/cos(hfvo) hgwiny=ginfo_winy/2 rangecoef=halfscr/hgwiny _rangescalxy@ return //hgposcvini [初期化] //システム変数 refdval に変換係数が返ります(この) #deffunc hgpcvini ddim fvz,4:ddim fv,4:ddim fvwork,4:ddim fvwork2,4 vx=0.0:vy=0.0:vz=0.0:tmpz=0.0 crx=0.0:cry=0.0:crz=0.0:cry2=0.0 cx=0.0:cy=0.0:cz=50.0 scalx=1.0:scaly=1.0:scalz=1.0 rangescalx=0.0:rangescaly=0.0:rangescalz=0.0 cx_=0.0:cy_=0.0:cz_=0.0 FOV=atan(1,1) //画角(getefx HGOBJ_CAMERAを) NearZ=0.5 FarZ=768.0 hgpcvfovini return rangecoef //hgpcvini2 [再初期化 640*480以外で hgini を実行した時に必要] //システム変数 refdval に変換係数が返ります #deffunc hgpcvini2 _pcvsetcang@ _pcvsetcpos@ _pcvsetcefx@ _pcvsetcscale@ _rangescalxy@ hgpcvfovini return rangecoef // hg_getspos p1,p2,p3,p4 [ワールド座標からスクリーン座標に変換] // p1 ワールド座標が代入されてる配列変数(配列要素4、実行後の内容はカメラからの相対座標) // p2,p3 x,yスクリーン座標が代入される(実数値) // p4 スクリーンからの距離 -ならスクリーンの前 +ならスクリーンの後ろにある #deffunc hg_getspos array fvxyz,var sx,var sy,var sz fvsub fvxyz,cx,cy,cz fvset fvz,0.0,0.0,crz fvdir fvz,fvxyz(1),fvxyz,fvxyz(2) fvset fv,-cry,crx,0.0 fvdir fv,fvz,fvz(1),fvz(2) ;fvmul fv,scaly,scalx,scalz //カメラスケールを変更している時用 sz=fv(2) fvz=-sz*rangecoef fvdiv fv,fvz,fvz,0.0 sx=fv(1) sy=fv return // hg_getwpos p1,p2,p3,p4 [スクリーン座標からワールド座標に変換] // p1 ワールド座標が代入される配列変数(配列要素4) // p2,p3 x,yスクリーン座標指定(実数値) // p4 スクリーンからの距離指定(実数値、-50でスクリーン前距離50の位置) #deffunc hg_getwpos array fvxyz,double ssx,double ssy,double ssz fvset fvxyz,crx,-cry,crz fvdir fvxyz,-rangecoef*ssx*ssz,-rangecoef*ssy*ssz,ssz fvadd fvxyz,cx,cy,cz return #global hgpcvini //モジュール初期化 randomize screen 0,640,480,0 hgsetreq SYSREQ_DEFTIMER,1 hgini addxfile m_xmodel.0,dir_exe+"\\sample\\hgimg3\\font_a.x" regobj obja, m_xmodel setuv 0,0,128,128 addmesh m_mesh,8,8,16,100,100 texload dir_exe+"\\sample\\hgimg3\\btex.bmp" regobj obj, m_mesh setpos obj,0,0,-25 setefx obj,700 newevent ev1 event_prmon ev1,PRMSET_MYGROUP,2 event_setpos ev1, -20,-20,-30 ,20,10,30 event_setdir ev1, -0.3,-0.2,-0.5,0.3,0.2,0.5 event_setefx ev1,150 event_addang ev1, 0, 0.1, 0 event_wait ev1, 80 event_prmset ev1,PRMSET_FLAG,0 newevent ev2 event_addang ev2, 0, 0.1, 0.1 event_wait ev2, 1 event_jump ev2, 0 ddim x,30: ddim y,30: ddim z,30 setcolor 0,255,0 addbox boxmid,0.2,0.2 regobj objbox, boxmid,,ev2 clscolor $5050 setpos HGOBJ_CAMERA, 0, -10,50 repeat stick key,$3ff if key&128 :break if (cnt&7)=0 { ; 8フレームごとに実行 regobj obj, m_xmodel,OBJ_MOVE setevent obj,ev1 } if key&64{//Ctrl+カーソルキーでカメラ回転 if key&1 : addang HGOBJ_CAMERA, 0.0, 0.02 if key&4 : addang HGOBJ_CAMERA, 0.0, -0.02 if key&8 : addang HGOBJ_CAMERA, -0.02, 0.0 if key&2 : addang HGOBJ_CAMERA, 0.02, 0.0 }else{; カメラをカーソルキーで動かす if key&1 : addpos HGOBJ_CAMERA, -0.2, 0.0 if key&4 : addpos HGOBJ_CAMERA, 0.2, 0.0 if key&8 : addpos HGOBJ_CAMERA, 0.0, 0.2 if key&2 : addpos HGOBJ_CAMERA, 0.0, -0.2 } _pcvsetcang //カメラ操作した後にhgpcv側にも設定 _pcvsetcpos hg_getwpos objafv,mox,moy,-10 //マウス座標を3D座標に selpos objbox objsetfv objafv _getfvector mvxyz,mox,moy //レイの方向ベクトル収得 if mvxyz(1)!0.0{ getpos HGOBJ_CAMERA,cx,cy,cz title ""+cy+" "+mvxyz(1) _getfvwpos mvxyz,-(cy/mvxyz(1)) //ベクトルからカメラのy座標を割ってカメラからの距離にしてワールド座標に mvxyz(1)-3 selpos obja objsetfv mvxyz } oidcnt=0 findobj 0,2 repeat nextobj oid if oid<0{break} selpos oid objgetfv fv hg_getspos fv,x(oidcnt),y(oidcnt),z(oidcnt) //3D座標から2D座標に(3D空間でスクリーンの中心だったら0,0) oidcnt++ loop hgdraw color 255,0,0:gmode 3,32,32,128 repeat oidcnt hgrect x(cnt)+320,y(cnt)+240,,limit(120+z(cnt),10,120),limit(120+z(cnt),10,120) loop hgsync 15 await 0 mox=mousex-320 moy=mousey-240 loop end



いなえ

リンク

2009/5/26(Tue) 14:26:53|NO.25388

>ななしさん
手順はほぼ正しいです。
おかしい部分を指摘します。

まずカメラの画角を考慮できていません。
手順6.でスクリーンのXサイズを掛けていますが,
画角を考慮するとこの部分がおかしいことになります.
デフォルトの画角は45度(π/4)です(setefxで変更できます)。
また画角の基準はスクリーンのYサイズです。
そこから考え直してみてください。

またfvdirでの回転は単純に指定してはうまくいきません。
詳しく考えていませんが回転方向の問題でしょう。
具体的にはX,Z回転角を逆にしてみてください。

以上です。



えく

リンク

2009/5/26(Tue) 17:49:14|NO.25389

言っていることがかぶってしまいますが……

3D→2Dの4つの変換のうち射影変換をうまく処理していません

ので、そのあたりを検索してでてきたところ
http://homepage2.nifty.com/skimp-studio/htm/crawl/1_9_transform4.htm
などなど

少々ながらも数学の知識を必要としますが、
要するに人間でいうと横やら縦やらの視野角を考えてない、ってことです



ヒカリ

リンク

2009/5/26(Tue) 20:01:56|NO.25394

getang でカメラの角度を求める場合は
cammode で設定したモードによって値が変わるようなので、
その時は注意してみてください。



ななしさん

リンク

2009/5/27(Wed) 07:47:17|NO.25409

コメントありがとうございます。

ただ今、画角とか、投影変換についていろいろ調べています。
分かりかけていますので、結果が分かり次第、書き込みますので、少しお待ちください。



いなえ

リンク

2009/5/27(Wed) 11:57:06|NO.25410

訂正です。
fvdirで「X,Z回転角を逆にしてみてください」と書きましたが、
回転順序も考慮して、X,Y,Zを別々に回転させないとうまくいかないようです。
順序は次の通り(回転方向を正負で表現)
[-Z]→[+Y]→[-X]

ついでに補足。
画角は中心からの角度ではなく視野全体の角度です。
画角を90度(π/2)に設定すると処理が簡単になります。
端の方での歪みが激しいですが。
デフォルトの45度(π/4)だとtanを使うことになりそうです。



ななしさん

リンク

2009/5/29(Fri) 20:54:19|NO.25442

みなさん、ありがとうございます。

いろいろいじって、やっとできるようになりました。

z座標で割ったx,y座標に、基準になるスクリーンサイズをかけた後、画角による修正値を掛けるのですね。
ちなみに画角の修正値は
1/tan(ラジアン角度/2)

また、縦横比もあるので、x座標はアスベクト比も修正しなければいけません。

あと、いなえさん、回転順について、ありがとうございます。これで、カメラのX軸角度の変化にも対応出来るようになりました。
ただ原理がぜんぜん分かりませんが・・・。

でも、これで前に進めます。

本当にいろいろありがとうございます。



ONION software Copyright 1997-2023(c) All rights reserved.