質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.37%
OpenGL

OpenGLは、プラットフォームから独立した、デスクトップやワークステーション、モバイルサービスで使用可能な映像処理用のAPIです。

Q&A

2回答

1999閲覧

OpenGLでBlenderみたいにカメラを動かしたい

siwameron

総合スコア0

OpenGL

OpenGLは、プラットフォームから独立した、デスクトップやワークステーション、モバイルサービスで使用可能な映像処理用のAPIです。

0グッド

2クリップ

投稿2023/06/16 09:13

編集2023/06/20 13:35

実現したいこと

gluLookAt(eyeX,eyeY,eyeZ,centerX,centerY,centerZ,upX,upY,upZ)
で求めたカメラの位置と姿勢があります。
eyeはワールド座標系のカメラの位置、
centerはワールド座標系の目視点、
upはワールド座標系のカメラの上方向を表しています。

この状態で、カメラを球体座標上で回転させたり、回転させず平行移動のみでしたらできますが、回転した後に平行移動をさせると画面のx,y軸ではなく、おそらくカメラ座標系の軸をもとにした動きをしてしまいます。
理想的なカメラの動きは、Blenderなどで、視点の中心に立方体をおいて、その立方体を360度眺めたり、平行移動したりした時の動きです。
このような動きを実装するにはどうしたらよいでしょうか。

追記

遅れてすみません。
質問には、glLookAtを使ってと書きましたが、私はこちらの資料をGLFWによるOpenGL入門https://tokoik.github.io/GLFWdraft.pdf
を参考にプログラムを書きました。こちらの資料では、glLookAt関数と同様の処理をlookAt関数を作成しました。

イメージ説明

posにマウスの増加量を示すlocationを使って、球体座標のような座標を作成しています。
この処理で、マウスを動かすことで、立方体を中心とした球の表面をカメラが沿うような動きをさせています。

上のlookAt関数の引数や機能はgluLookAt関数と同じだと思われます。第1から第3引数にカメラの座標、第4から第6引数にカメラの視線を向かせる方向を示す座標、第7から第9引数にカメラの上方向を示す座標です。
今回は、球に沿った座標を入れたposがカメラの座標となり、視線が原点を向き続けることで、原点周辺にある、立方体を360度眺めることができています。

イメージ説明イメージ説明
画像では位置や大きさが変わっているように見えますが、実際にはその場で大きさも変わらずに、カメラが立方体の周囲を回りこんだような出力が得られています。

現在のプログラムでは行っていませんが、カメラを平行移動のみさせることはできます。
ただ、カメラを球面座標を用いた移動をさせた後に、通常のx,y座標軸ではなくカメラの姿勢に沿った平行移動をさせようとすると、カメラの姿勢に沿った座標軸ではないのか、もしかしたら通常のx,y座標軸に沿った移動をしてるのか、画像の立方体が大きさや回転によって見えている面が変わらず、画像の立方体をトリミングして左に動かしたかのような平行移動をさせたいのですが、うまくいきません。

実際の動き
イメージ説明イメージ説明
上の画像はマウスを右に動かす前、下の画像はマウスを右に動かした後。

理想の動き
実際にBlenderというソフトを使用して、私が作成しているソフトにしてほしい動作の様子を再現しました。
私は以下の動画のようなカメラの動きを実装したいです。
動画のリンクです。
https://youtu.be/fB5qPW6og8U

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

hoshi-takanori

2023/06/16 22:47

> 回転した後に平行移動 のコードと実行結果のスクショがないので何とも言えませんが、とりあえずカメラ位置 (eyeX, eyeY, eyeZ) を適切に計算して gluLookAt をやり直せばいいだけでは…。
umimi

2023/06/17 01:36

[香車]東上☆あらし☆海美「 Blender は Windows や Linux では、OpenGL で動いてますが、それを踏まえた質問ですか ? Blender で表示している時の、 カメラ座標、目視点の座標、カメラの上方向ベクトル、立方体の座標/大きさ等を そのままプログラムに書けばいいんじゃないですか ? 」
fana

2023/06/17 05:17

OpenGLの関数の「現在の行列に対して行列が乗じられる」っていう働きの意味を把握してないと,どんな順序で何を使えばよいのかが判断できない. そこを「なんとなく」でやってるとそういうことになりがち. もしもそういうあたりが原因で所望の動きをする処理を組み立てられないのであれば, まず ModelView 行列(回転,並進,スケーリング)くらいは自前で実装してみてはどうか. それで所望の動きが達成できたら→(必要なら)gluLookAtだのを使う形の実装に換えればよいのでは.
fana

2023/06/17 05:19

OpenGLの関数がやるのとは逆方向から行列を乗じたいような場合は,半ば自前でやることになるのだし.
cure_fontaine

2023/06/18 22:53

カメラ位置を回転させたら、upX,upY,upZも変化させないといけませんよ。
fana

2023/06/19 01:46 編集

完全に無反応だが……必要最低限のコードを示さないとまともなやりとりにならないと思う. 視点の位置と姿勢を一括で指定する意味合いの gluLookAt() を使っているのであれば > カメラを球体座標上で回転させたり、回転させず平行移動のみでしたらできますが、回転した後に平行移動をさせると …っていう話がそもそも成り立たないし.(カメラを 回転した後に平行移動させる とは実際どういうコードを書いているのか?) > Blenderなどで、視点の中心に立方体をおいて、その立方体を360度眺めたり、平行移動したりした時の動き Blender は知らんけど,カメラではなくて「立方体の側を 回す/動かす」 と考えた方が分かりやすい可能性もあるか? (結局相対関係でしかない以上は「同じ」なんだけど,コードの見た目は変わる…かな)
fana

2023/06/21 01:24

何か追記されましたが,結局のところ「マトリクスを乗じる 順番/方向」に起因しているのか,そうではないのか,どっちなんですかね? (ということくらいは数分で確認できると思うのですが) あえて絵で(スクショで)示されたコードというのは, 「カメラを球面座標を用いた移動」とかいうのを目的とした 変換行列(の中の要素群の値)を求める処理 なのであって, 実際に その行列を用いた演算を行う部分 というのが別にあるんですよね? 問題はそっち側の実装なんじゃないんですか? と言っているわけですが.
fana

2023/06/21 01:34 編集

その立方体の頂点座標を定義している座標系の軸も描画するなりすれば,何が起こっているのかを把握しやすくなったりしませんか? 現在の「平行移動」というのがその軸に沿っているとか沿ってないとか. 仮に,その軸に沿う形で並進しているのであれば,演算が 変換結果の立方体の頂点座標 = (lookAtで求めた行列) * (平行移動用の行列) * (元々の立方体の頂点座標) という形になっているのではないのか? とか何とか推測できそうですよね. そしたら,じゃあ (平行移動用の行列) * (lookAtで求めた行列) * (元々の立方体の頂点座標) っていう順序に変えたらどうなるんですかね? とか思いますよね. こっちにすれば,平行移動が「カメラ座標系の」値になるだろうから,それが所望の動きだったりしないのですか? っていう. (もちろん,あなたの実装している演算はこんな簡単な形ではなくて全然別のことをしているのだ! みたいなことであれば,こんな話してても意味ない. ← もしもそうなのであれば,そうだとさっさと言ってくれれば無駄なやりとりを避けられる)
cure_fontaine

2023/06/21 05:11

Blenderでの動きを見ると、平行移動時に立方体が変形していますから透視投影(Perspective)で描画しているのだと思います。Blenderでは視方向は立方体の中心よりももっと遠い位置にあり、カメラ位置と視方向が一定の距離を取りながら移動しているように見えます。 仮に、作成中のプログラムでカメラ位置を(50,10,0)、視方向を(0,0,0)にして、カメラ位置のZ座標と視方向のZ座標を同じになるように連続的に移動させたらどうなりますか。 つまり、カメラ位置は(50,10,0)→(50,10,-50)、視方向は(0,0,0)→(0,0,-50)のように移動するということです。 立方体は縮小しながら左に移動すると思います。Blenderと同じ動作を期待するならば、視方向をもっと遠い点に変えて実験してみてください。
fana

2023/06/21 06:21

「視方向」というのが(lookAt の引数群の真ん中へんに指定している)注視点のことであって, 話の内容が 「lookAt でカメラの平行移動相当のことをやるのであれば 視点 と 注視点 とを同じだけシフトさせる必要があるよね」 ということであればその点はわかりますが,そうだとしたら > 視方向をもっと遠い点に変えて実験 の意味がわからないです.(使われるのは 視点→注視点 の方向であって,距離は非0ならどうでもよいハズでは)
cure_fontaine

2023/06/21 07:14

Blenderでの平行移動の動作を、リンクされた動画でよく観察して下さい。立方体の見え方がやや変化しているんですよ。もしも透視投影ではなく平行投影(Ortho)ならば立方体の見え方は変わりません。 質問者さんがBlenderでの動作をシミュレートすることにこだわっていらっしゃるのでしたら、透視投影での視方向(注視点)の位置についても考慮しなければならなくなります。もしも視方向(注視点)を立方体の重心程度の近い距離に取った場合は、平行移動の距離によって立方体の見え方の変化が大きく変わってきます。 目的とする動作が平行投影で良いのならば、これはパンと同じですから、シェーダーで、2Dに変換されたクリップ座標を加減算して平行移動させるだけ(超簡単)になります。実験の順序としては、まず平行投影で動作確認を成功させてから透視投影に移る方が望ましいと思います。
fana

2023/06/21 07:56 編集

えっと,「平行移動」と言う場合,普通は(?) 姿勢(向き)は変えずに位置だけ変えることを指すのではないかと思うのです. (要は,カメラの位置を T, 姿勢を R で表すときに T 側だけを変えることを「平行移動」と言うのではないか,と.) で,(上記のような言葉の定義の上では) 視点と注視点(と上向きベクトル) という指定の仕方でカメラの位置と姿勢とを示すのであれば, 「平行移動」を実現するには,視点と同じだけ注視点も動かすことになります. まさに > カメラ位置は(50,10,0)→(50,10,-50)、視方向は(0,0,0)→(0,0,-50)のように移動 と同じ話です. 逆に言えば,視点と注視点の移動量が異なるのであればそれは「平行移動」とは呼ばない(「移動しながら向きを変える」みたいに言う)のではないかな,と. (※もちろん,両者の移動量が異なっていても平行移動になるパターンもありますが,論点ではない) #…っていう話においては,射影方式は関係ないです.(投影変換はもっと後で行う事柄)
fana

2023/06/21 07:52

この話においては,視点と注視点の距離を変えても何も影響しませんよね,っていうだけの話です. (「平行移動」の意味が上記の私の解釈と異なるのであれば,もちろん,影響があり得ます.)
umimi

2023/06/22 02:25

[香車]東上☆あらし☆海美「 参考文献だけじゃなく、ご自分の制作された『動いているプログラムのソースコード』を見せてほしいみみ。 」
siwameron

2023/06/23 02:40

githubのリポジトリをpublicにしました。
cure_fontaine

2023/06/25 22:55

6/23 17:00頃にコメントを投稿したんですが、今閲覧できませんね。 どなたか見てくれた方がいらっしゃると思うのですが。 今日、夕方に再投稿します。
cure_fontaine

2023/06/26 06:18

6月23日にコメントした内容を再掲載します。 まず、私はC言語は使っていませんので基本的に「読めない」ということを前提に回答しますのでご理解下さい。 ダウンロードしたソースリストに次の様な部分がありました。 Matrix c_trans(Matrix::lookat(alt[0] * -4.0f, alt[1] * -4.0f, 0.0f, alt[0] * -4.0f, alt[1] * -4.0f, 0.0f, 0.0f, 1.0f, 0.0f));//↑で求めた数値分だけ、カメラをカメラの姿勢を変えずに平行移動させる //モデル変換行列を求める const Matrix r(Matrix::rotate(static_cast<GLfloat>(glfwGetTime() * 0), 0.0f, 1.0f, 0.0f));//回転を設定する const Matrix model(Matrix::translation(0.0f, 0.0f, 0.0f)); Matrix view(Matrix::lookat(pos[0], pos[1], pos[2], 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f));//カメラを注視点を向いたまま、球体座標上に動かす //モデルビュー変換行列を求める const Matrix modelview(c_trans * view * model * r); 最後のモデルビュー変換行列の作成で、c_transではカメラと注視点を平行移動しているように見られます。 一方、viewでは座標原点を注視点にしている様ですが、このviewを再利用しているのはなぜですか。
fana

2023/06/26 10:32 編集

ビルドして動かしてみたけど,正直何を問題視しているのかわからないな. 少なくとも「平行移動」についてはできてるんじゃないの? (できてないという言うならば,どういう意味でできていないのか?) 透視投影のかけかたが異様にどぎつすぎて何が起きているか見た目にわからないだけなんじゃないの? 例えば, const Matrix projection = Matrix::frustum( -0.1, 0.1, -0.1, 0.1, 1.0f, 100.0f); とかにして,あとはカメラ座標 pos の係数 -4.0 を -20 くらいに変えてやれば大分見やすいと思う. まぁそれ以前に,Model vs View の関係を問題にしているのだから(そうだよね?),まずは平行投影(Orthogonal)で動きを見た方が良いと思うが. Orthogonal ならばまさに > 画像の立方体をトリミングして左に動かしたかのような平行移動をさせたい になるハズ. (既に述べれらているように,その Blender の動画はそういう動きにはなっていない.面の見えが変わっている)
fana

2023/06/26 10:43 編集

lookAt を複数個使おうが何か問題があるわけじゃないんだけども,「並進したい」なら Matrix c_trans = Matrix::translation( 4*alt[0], 4*alt[1], 0 ); とかの方が言葉に対して素直な気もする.(別に動きは変わらないハズ) > 最後のモデルビュー変換行列の作成で、c_transではカメラと注視点を平行移動しているように見られます。 > 一方、viewでは座標原点を注視点にしている様ですが、このviewを再利用しているのはなぜですか。 要は, view が「カメラを球面座標を用いた移動」に相当していて, c_trans がその後の「通常のx,y座標軸ではなくカメラの姿勢に沿った平行移動」に相当している,という話かと. (modelView 作成箇所で乗じている順序的に,c_trans 生成のところの引数値は「カメラを球面座標を用いた移動」をした後のカメラ座標系での値なので,最終的な表示結果で言えばウィンドウの上下左右方向に沿った平行移動になっている.)
fana

2023/06/27 01:46 編集

あれ,でもこれ > Matrix c_trans(Matrix::lookat(alt[0] * -4.0f, alt[1] * -4.0f, 0.0f, alt[0] * -4.0f, alt[1] * -4.0f, 0.0f, 0.0f, 1.0f, 0.0f)); の引数,おかしくないか? 視点と注視点の座標が完全一致って,そんな指定は無いだろう……(注視点のZ成分は非ゼロたるべきだよね意味的に.こういう「意味的に変な」指定で何故それっぽく動けているのかは lookAt の中身次第になるが,そこまで見たくないのでパス.一行目から始点と注視点とを加算して「平行移動の行列」とか言ってるところが私には意味わからんし.)
fana

2023/06/27 02:49

ベクトルの長さが0になるイレギュラーな引数を許容(と言ってよいか?)する元ネタは参考資料の p.144 なのか. でも,この話の通りにやりたいのであれば「平行移動の行列」の実装が資料(p.143)と違うのは謎.
siwameron

2023/06/27 12:24

カメラを球面座標で移動させて、平行移動させることはできました。ありがとうございました。しかし、動画のようなカメラを球面座標上で移動させて、平行移動させた後に球面座標上で移動させたとき起きる、立方体が画面上を回り込むように見える現象を再現したいです。
cure_fontaine

2023/06/27 22:43

回答者の方はサンプルプログラムで平行移動ができた。質問者さんはできなかったという矛盾があったのですが、今度は質問者さんが「平行移動させることはできました。」とコメントされています。 どなたの言っていることが正しいのか、依然として分からない状態です。当初どこに問題があったのでしょうか。 >しかし、動画のようなカメラを球面座標上で移動させて、平行移動させた後に球面座標上で移動させたとき起きる、立方体が画面上を回り込むように見える現象を再現したいです。 これについては私が以前からコメントしていますが、透視投影で注視点を立方体よりも遠くに設定して注視点とカメラ位置を移動しているからです。Blenderで平行投影モードで表示すれば、この様には見えなくなります(立方体の見え方は不変)。
fana

2023/06/28 01:18

> 立方体が画面上を回り込むように見える現象 動画の最後の部分ですかね. 今は,view 側のlookAt が「球面上でどうの」いう部分を司っており,その注視点が常に(0,0,0)すなわち立方体の中心位置を固定的に指定しているので,常に立方体の姿勢がその場で回転するような動きになっているわけですが,それが所望の動作ではない,ということですかね. おそらく,lookAtを使うなら視点に関する事柄はlookAt一発で解決する方向に持っていくのが最も簡単且つ混乱も少ないであろうと考えます. つまり,{視点,注視点,カメラ上向きベクトル}を変数として持って,操作に応じて更新していくという形. (まぁあえてlookAtなる関数の使用にこだわる必要も無いので,カメラの位置姿勢をRとTで普通に持つ方が自然かと思う)
fana

2023/06/28 01:46

うーん,この質問内で「注視点」という言葉を使うとどうしても「lookAtの引数」すなわち「カメラ座標系の基底ベクトルの方向を表すことだけが目的のもの」みたいな意味になってしまうからいけないのだな. このアプリケーションの動作から言えば「カメラの旋回中心」とでも言えば良いのかな.(で,本件ではたまたまカメラはいつもそこを真正面に見ている.) で,【この「カメラの旋回中心」の位置というのが今は定数(0,0,0)になっているのだけど,これを,カメラの「平行移動」に応じて変えたい】というのがやりたいことなのだと思う. → そしたら変数にして 管理/更新 すればいいよね. あと,このアプリケーションでは,lookAtの引数の末尾で指定している「上向き」もまた固定にしてはおけないハズなので動的に更新(あるいは算出)すべきだと思う.
fana

2023/06/29 02:52 編集

何で「更新」とか述べているかと言うと, マウス操作量の累積値(現在までの総和)みたいなのから諸々を決定しようという方針自体に無理があると思うから. 例えば,カメラを(球面上で~という動きによって) 立方体の向こう側(:初期位置から丁度反対側) に動かすとき,カメラを動かした経路次第でカメラの姿勢が変わるのが自然. 横から回り込むようにして向こう側に行く場合と,立方体の真上を通って向こう側にまわる場合とでは,結果のカメラ姿勢は変わるよね(上下について言えば,逆さまになる.左右も同様.180度Rollした関係). 累積操作量からカメラの位置を算出している今のやり方だと,この違いを扱えないよね. (……というわけで,モデルビュー行列を求める部分をそれなりに作り替えることを個人的に推奨) …ていう話に関する回答を書いた. (実際に実装して動かしたわけじゃないから既述に間違い等があるかもだが,そこは適切に読み替えるなどしてください)
fana

2023/06/29 07:44

実装も追加した. (「これは所望の動作ではない」みたいな場合には,あとは他の方におまかせする)
guest

回答2

0

回転した後に平行移動をさせると画面のx,y軸ではなく、おそらくカメラ座標系の軸をもとにした動きをしてしまいます

ここだけから,ものすごく強引にエスパー回答するなら… 以下の2つの違いなのでは?

C++

1glMatrixMode( GL_MODELVIEW ); 2glLoadIdentity(); 3 4//記述順序が glTranslated → gluLookAt 5glTranslated( ... ); //←これが「平行移動させると」に相当? 6gluLookAt( ... ); 7 8glBegin( ... ); 9... 10glEnd( ... );

C++

1glMatrixMode( GL_MODELVIEW ); 2glLoadIdentity(); 3 4//記述順序が gluLookAt → glTranslated 5gluLookAt( ... ); 6glTranslated( ... ); //←これが「平行移動させると」に相当? 7 8glBegin( ... ); 9... 10glEnd( ... );

質問文の追記によって,話が「実は gluLookAt じゃないです」とかいうことになりましたが,(この回答の)話の本質は変わりません.

gluLookAt ならば内包している「マトリクスを乗じる」処理の部分をおそらくは自前で行っている形かと推測します.
そこの乗じる順序や方向に問題があるのであろう,というだけの話です.


githubのリポジトリをpublicにしました。

を受けての追記:

公開されたコードを見るに main.cpp でのマトリクスを乗じる順序は合っていて,動作させてみると 所望の平行移動をしている ように見える.
なので, 問題点自体が存在しないのでは…? と見える. 動きの見た目には.

ただ,個人的には Matrix.hlookAt 等の実装がなんか変に思えるので,
「そういう部品の実装が変 + それを使う側も変」の相乗効果で何となく動いているだけという可能性もあるんじゃないかな,と.
Matrix.h みたいなのを自作することに特にこだわりが無いならば,そういう行列計算の部分は信頼性のある何か(GLMとか?)を持ってきた方が良いのでは.)

投稿2023/06/19 02:05

編集2023/06/27 02:36
fana

総合スコア11954

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

0

モデルビュー行列に関して,方針を示す.


実現したいこと

多分,このアプリケーションにおけるカメラ配置の制約というのは,以下のような話.

位置について:
ある点 P とカメラとの距離 r は常に一定に保たれている.すなわち,カメラは点 P を中心とする半径 r の球面上に位置する.

姿勢について:
カメラは常に点 P を真正面に捉える.

点 P の位置について:
定位置ではなく移動できる.

カメラは点 P と長さ r の透明な棒で繋がっているイメージ:P を動かせばカメラは勝手についてくる(=平行移動).


実装の方針

上記のような話なのであれば,例えば

  • P の座標
  • カメラ姿勢

を変数として 管理/更新 すればどうか.

ここでは話を分かりやすくするために,カメラ姿勢に関してはカメラ座標系基底ベクトルのうちの2つ(例えば{カメラの正面後ろ方向 CZ,カメラの右手方向 CX})を変数として持つことにする.
3つ目の基底ベクトル:カメラの上方向 CY については都度この2つから外積で求めればいい.

モデルビュー行列の算出に必要な登場人物は以下のようになる.

定数:

  • r (スカラー量)

維持管理すべき変数:

  • P (3次元ベクトル)
  • CX, CZ (3次元ベクトル.もちろん単位ベクトル)

↑から都度求めればよい物:

  • CY <= CZCX の外積
  • カメラ位置 <= P + r * CZ

初期化

制約を満たすように初期値を与える.

  • P <= (0,0,0)
  • CX <= (1,0,0)
  • CZ <= (0,0,1)

とかかな.この場合,

  • CY <= (0,1,0)
  • カメラ位置 <= (0,0, r)

になる感じ.

平行移動

(前述のように,カメラ位置は毎回 P を用いて計算されるのだから)フレーム間のマウス移動量 (dx, dy) に合わせて P を動かすだけの話.
動かす方向はカメラ座標系の上下左右に合わせたいのだろうから,CX, CY に沿う方向に動かしてやればよい.

  • P += ( dx * CX - dy * CY ) //←pixel座標系と3次元座標系とでY方向が逆なので,dyは-1を乗じて使用

カメラの球面上での移動

フレーム間のマウス移動量(dx, dy) から,カメラ座標系における回転軸を (dx, -dy, 0) と直交するベクトルとして (dy, dx, 0) と定めて, CX, CZ をそれぞれ回転させる.
(※平行移動の箇所で前記したのと同様に,dy に -1 を乗じている)
(言うまでも無く回転量は (dx,dy) のノルムに比例した感じで適当に決める)

「任意軸周りの回転」とかでググって回転行列 R の算出を実装し,

  • CX <= R * (1 0 0) を(回転前のカメラ姿勢を用いて)ワールド座標系の値に変換したもの
  • CZ <= R * (0 0 1) を 同上

として更新すればいい.

モデルビュー行列

カメラの位置と姿勢があるのだから,そこから 回転R と 並進T を作って,

  • モデルビュー行列 <= R * T

で.
RCX, CY, CZ をしかるべき位置に並べればいい.
T も (- カメラ位置)の要素をしかるべき位置にならべればいい.


実装してみた

(※実装して確かめた結果明らかになった↑の記述の諸々の誤りも修正)
変えた部分を以下に示す.

Window.h

マウスカーソルのフレーム間移動量 (dx,dy) が欲しいのだけど,既存コードの実装のままでそういった情報を拾えるのか否か不明であったため,ここは適当に以下のようにやっつけコードを追加して対応した.

  • それ用のメンバ変数を追加し
  • operator bool() 内の glfwPollEvents(); 以降に変数の値を更新する処理を追加した

C++

1 //適当にこれらの変数を追加した 2 bool m_bMouseLDown = false; 3 bool m_bAltKeyDown = false; 4 GLfloat m_PrevMousePos[2] = { 0,0 }; 5 GLfloat m_CurrMousePos[2] = { 0,0 }; 6 7 //描画ループの継続判定 8 explicit operator bool() 9 { 10 double x, y; 11 12 //イベントを取り出す 13 glfwPollEvents(); 14 15 //追加した変数群の値更新処理 16 m_bMouseLDown = glfwGetMouseButton(window, GLFW_MOUSE_BUTTON_LEFT) != GLFW_RELEASE; 17 m_bAltKeyDown = glfwGetKey(window, GLFW_KEY_LEFT_ALT) != GLFW_RELEASE; 18 m_PrevMousePos[0] = m_CurrMousePos[0]; 19 m_PrevMousePos[1] = m_CurrMousePos[1]; 20 glfwGetCursorPos(window, &x, &y); 21 m_CurrMousePos[0] = GLfloat(x); 22 m_CurrMousePos[1] = GLfloat(y);

main.cpp

ベクトルや行列の演算のために,GLM をもってきた.

C++

1#include "glm.hpp" //GLMのヘッダをinclude 2 3//作業関数を追加.3次元ベクトルVに直行するベクトルを返す.(※中身が知りたい場合,ここteratailでの私の過去の質問を参照されたい) 4inline glm::vec3 GetPerpendicularOf( const glm::vec3 &V ) 5{ 6 if( fabs(V[0]) < fabs(V[1]) ) 7 { return glm::vec3( 0, -V[2], V[1] ); } 8 else 9 { return glm::vec3( V[2], 0, -V[0] ); } 10}

で,モデルビュー行列そのものに関する実装は以下.

  • メインループの while の手前に定数と変数の定義を追加
  • 透視投影変換行列は適当に見やすい感じに変えてる
  • モデルビュー行列の演算部分をまるごと差し替え(GLM はここの実装で使っている.変数 modelview 自体は元々の Matrix 型のままにしてある.)
  • modelview に要素値を代入するために,Matrix 型に const ではない operator[] を追加(コードは省略)

C++

1//定数と変数を追加 2 constexpr GLfloat CamRotRadius = 20; //カメラ旋回半径 r 3 glm::vec3 P(0,0,0); //カメラ旋回中心点 P(ワールド座標系での値) 4 glm::vec3 CX(1,0,0); //カメラ右手方向単位ベクトル(ワールド座標系での値) 5 glm::vec3 CZ(0,0,1); //カメラ後ろ方向単位ベクトル(ワールド座標系での値) 6 7 //ウィンドウが開いている間繰り返す 8 while (window) 9 { 10 //フレームバッファを初期化する 11 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); 12 //シェーダプログラム使用開始 13 glUseProgram(program); 14 15 //--------------------------------------- 16 //透視投影変換行列 17 // ※見やすいように,適度な具合に変更 18 const Matrix projection = Matrix::frustum( -0.1f, 0.1f, -0.1f, 0.1f, 1.0f, 100.0f); 19 20 //--------------------------------------- 21 //※元々存在した行列 view に関するコード 22 // view はモデルビュー行列に一切関わっていないので不要なのだが, 23 // なんか後ろの方で別の目的に使われている(?)ので残してある. 24 const GLfloat* location(window.getLocation(0));//マウスをドラッグしたときのカーソルの増加量を取得する 25 GLfloat pos[3] = { 0 };//カメラの球面座標上の座標 26 pos[0] = -4.0f * cos(location[0]) * cos(location[1]);//* -4.0fは素の増加量だと、見た目の変化が乏しいため 27 pos[1] = -4.0f * sin(location[1]); 28 pos[2] = -4.0f * sin(location[0]) * cos(location[1]); 29 Matrix view(Matrix::lookat(pos[0], pos[1], pos[2], 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f));//カメラを注視点を向いたまま、球体座標上に動かす 30 31 //--------------------------------------- 32 //モデルビュー変換行列を求める 33 Matrix modelview = Matrix::identity(); 34 { 35 glm::vec3 CY = glm::cross( CZ, CX ); //カメラ上方向単位ベクトル(ワールド座標系での値) 36 37 //マウスカーソル移動量 (dx,dy)[pixel] 38 auto dx = window.m_CurrMousePos[0] - window.m_PrevMousePos[0]; 39 auto dy = window.m_CurrMousePos[1] - window.m_PrevMousePos[1]; 40 auto dnorm = sqrt(dx*dx + dy*dy); 41 42 if( window.m_bMouseLDown && dnorm>1 ) 43 { 44 if( window.m_bAltKeyDown ) 45 {//並行移動 46 P += 0.01f * ( dx*CX - dy*CY ); 47 } 48 else 49 {//P を中心とした旋回 50 glm::mat3x3 R; 51 {//任意軸周り回転行列の計算(カメラ座標系のものを回す用) 52 glm::vec3 Rot_Axis = glm::normalize( glm::vec3( dy, dx, 0 ) ); 53 glm::vec3 OtherAxis1 = glm::normalize( GetPerpendicularOf(Rot_Axis) ); 54 glm::vec3 OtherAxis2 = glm::cross( Rot_Axis, OtherAxis1 ); 55 glm::mat3x3 M( 56 OtherAxis1[0], OtherAxis2[0], Rot_Axis[0], 57 OtherAxis1[1], OtherAxis2[1], Rot_Axis[1], 58 OtherAxis1[2], OtherAxis2[2], Rot_Axis[2] 59 ); 60 61 double RotAngle = dnorm * 0.01; 62 glm::mat3x3 Roll( cos(RotAngle), sin(RotAngle), 0, -sin(RotAngle), cos(RotAngle), 0, 0,0,1 ); 63 R = glm::transpose(M) * Roll * M; 64 } 65 66 glm::mat3x3 C2W( CX,CY,CZ ); //カメラ座標→ワールド座標 変換行列 67 CX = C2W * R * glm::vec3(1,0,0); 68 CZ = C2W * R * glm::vec3(0,0,1); 69 CY = glm::cross( CZ, CX ); 70 } 71 } 72 73 //カメラ位置 74 glm::vec3 CamPos = P + CamRotRadius * CZ; 75 76 //モデルビュー行列の中身を設定 77 modelview[0]=CX[0]; modelview[4]=CX[1]; modelview[8]=CX[2]; modelview[12]=glm::dot( CX, -CamPos ); 78 modelview[1]=CY[0]; modelview[5]=CY[1]; modelview[9]=CY[2]; modelview[13]=glm::dot( CY, -CamPos ); 79 modelview[2]=CZ[0]; modelview[6]=CZ[1]; modelview[10]=CZ[2]; modelview[14]=glm::dot( CZ, -CamPos ); 80 } 81 82 //※以降は既存コードのままなので省略

マウスをどっちに動かしたらどっちに動くのか? というのが逆の方がやりやすいとかはあるかも.
(マウスを動かした方に「カメラが」動く感じになっているので,絵的には逆に動いているように感じるかも)

並行移動後の旋回に関しては,多分点 P の位置に何かを描画するとか,何かが無いと動きが掴み難いと思う.
(Blender の動画だとグリッド平面みたいなのがあるよね)

投稿2023/06/29 02:49

編集2023/06/29 07:55
fana

総合スコア11954

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

fana

2023/06/29 03:07 編集

モデルビュー行列の算出に既存の lookat() みたいな関数を使いたいなら使えばいい. その場合は,CY を用いて「上向き」引数を与えることができる.(-CY を用いればよいのかな)
fana

2023/06/29 03:13

回答内には明記してないけども, 更新計算の誤差蓄積によって,CXとCZとの直行関係が歪んだりとか,あるいは P を真正面に捉えるハズがずれてきたりとか…みたいなことに備えて,定期的に正規化(と呼ぶのかな?)みたいなことはすべきだと思う. カメラ位置→P から CZ を作り直し,そのCZを用いてCXを作り直す(当然その後CYを再計算する)みたいな.
fana

2023/06/29 03:15

カメラと点Pとの距離 r も変数にして,何らかの操作で増減できるようにしても良いんじゃないかな.
fana

2023/06/29 08:52

20分くらいでちょろっと実装してみるつもりが 軸の向きを記憶違いしていて,「???」ってなってわりと手間取ってしまった… 旋回に関しては,回転行列をワールド座標系で回す形に作って CX と CZ に乗じる想定だったのだが それをやってみたら何故か立方体が彼方に吹っ飛んでしまって,そこのバグの理由を掴むのも面倒になったので提示コードの形にした.
siwameron

2023/06/29 13:50

ありがとうございます。試してみます。
fana

2023/07/03 02:39

…で,どうだったんだろうか? 「はやく応答しろ」とか言うつもりじゃないし,余計なお世話かもだけど, もしもこれがダメ(所望動作とは異なるとか)である場合,早めにリアクションしないと,せっかくいる(いた)私とは別の話をしている人を逃がしてしまうことになり得るのではあるまいか,とか心配する. (あと,そのようなリアクションをとる場合,この回答に対してコメントを付けるだけだと多分通知は私にしか飛ばないので,質問文自体への追記をするとか,質問へのコメント欄あたりで何か言うのが良かろうと思われ)
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

まだベストアンサーが選ばれていません

会員登録して回答してみよう

アカウントをお持ちの方は

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.37%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問