回答編集履歴
5
Matrix::operator[] について追記
test
CHANGED
@@ -149,6 +149,7 @@
|
|
149
149
|
* メインループの `while` の手前に定数と変数の定義を追加
|
150
150
|
* 透視投影変換行列は適当に見やすい感じに変えてる
|
151
151
|
* モデルビュー行列の演算部分をまるごと差し替え(GLM はここの実装で使っている.変数 `modelview` 自体は元々の `Matrix` 型のままにしてある.)
|
152
|
+
* `modelview` に要素値を代入するために,`Matrix` 型に const ではない operator[] を追加(コードは省略)
|
152
153
|
|
153
154
|
```C++
|
154
155
|
//定数と変数を追加
|
4
回転の部分の dy の話を修正
test
CHANGED
@@ -67,7 +67,8 @@
|
|
67
67
|
|
68
68
|
### カメラの球面上での移動
|
69
69
|
|
70
|
-
フレーム間のマウス移動量(dx, dy) から,カメラ座標系における回転軸を (dx, dy, 0) と直交するベクトルとして (
|
70
|
+
フレーム間のマウス移動量(dx, dy) から,カメラ座標系における回転軸を (dx, -dy, 0) と直交するベクトルとして (dy, dx, 0) と定めて, **CX**, **CZ** をそれぞれ回転させる.
|
71
|
+
(※平行移動の箇所で前記したのと同様に,dy に -1 を乗じている)
|
71
72
|
(言うまでも無く回転量は (dx,dy) のノルムに比例した感じで適当に決める)
|
72
73
|
|
73
74
|
「任意軸周りの回転」とかでググって回転行列 **R** の算出を実装し,
|
3
コード追加と記述修正
test
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
モデルビュー行列に関して,
|
1
|
+
モデルビュー行列に関して,方針を示す.
|
2
2
|
|
3
3
|
---
|
4
4
|
|
@@ -28,7 +28,8 @@
|
|
28
28
|
|
29
29
|
を変数として 管理/更新 すればどうか.
|
30
30
|
|
31
|
-
ここでは話を分かりやすくするために,カメラ姿勢に関してはカメラ座標系基底ベクトルのうちの2つ(例えば{カメラの正面方向 **CZ**,カメラの右手方向 **CX**})を変数として持つことにする.
|
31
|
+
ここでは話を分かりやすくするために,カメラ姿勢に関してはカメラ座標系基底ベクトルのうちの2つ(例えば{カメラの~~正面~~後ろ方向 **CZ**,カメラの右手方向 **CX**})を変数として持つことにする.
|
32
|
+
3つ目の基底ベクトル:カメラの上方向 **CY** については都度この2つから外積で求めればいい.
|
32
33
|
|
33
34
|
モデルビュー行列の算出に必要な登場人物は以下のようになる.
|
34
35
|
|
@@ -40,8 +41,8 @@
|
|
40
41
|
* **CX**, **CZ** (3次元ベクトル.もちろん単位ベクトル)
|
41
42
|
|
42
43
|
**↑から都度求めればよい物:**
|
43
|
-
* **CY** <= **C
|
44
|
+
* **CY** <= **CZ** と **CX** の外積
|
44
|
-
* カメラ位置 <= **P**
|
45
|
+
* カメラ位置 <= **P** + r * **CZ**
|
45
46
|
|
46
47
|
### 初期化
|
47
48
|
|
@@ -54,7 +55,7 @@
|
|
54
55
|
とかかな.この場合,
|
55
56
|
|
56
57
|
* **CY** <= (0,1,0)
|
57
|
-
* カメラ位置 <= (0,0,
|
58
|
+
* カメラ位置 <= (0,0, r)
|
58
59
|
|
59
60
|
になる感じ.
|
60
61
|
|
@@ -62,7 +63,7 @@
|
|
62
63
|
|
63
64
|
(前述のように,カメラ位置は毎回 **P** を用いて計算されるのだから)フレーム間のマウス移動量 (dx, dy) に合わせて **P** を動かすだけの話.
|
64
65
|
動かす方向はカメラ座標系の上下左右に合わせたいのだろうから,**CX**, **CY** に沿う方向に動かしてやればよい.
|
65
|
-
* **P** += ( dx * **CX**
|
66
|
+
* **P** += ( dx * **CX** - dy * **CY** ) //←pixel座標系と3次元座標系とでY方向が逆なので,dyは-1を乗じて使用
|
66
67
|
|
67
68
|
### カメラの球面上での移動
|
68
69
|
|
@@ -71,8 +72,8 @@
|
|
71
72
|
|
72
73
|
「任意軸周りの回転」とかでググって回転行列 **R** の算出を実装し,
|
73
74
|
|
74
|
-
* **CX** <= **R** *
|
75
|
+
* **CX** <= **R** * (1 0 0) を(回転前のカメラ姿勢を用いて)ワールド座標系の値に変換したもの
|
75
|
-
* **CZ** <= **R** *
|
76
|
+
* **CZ** <= **R** * (0 0 1) を 同上
|
76
77
|
|
77
78
|
として更新すればいい.
|
78
79
|
|
@@ -85,3 +86,156 @@
|
|
85
86
|
で.
|
86
87
|
**R** は **CX**, **CY**, **CZ** をしかるべき位置に並べればいい.
|
87
88
|
**T** も (- カメラ位置)の要素をしかるべき位置にならべればいい.
|
89
|
+
|
90
|
+
---
|
91
|
+
|
92
|
+
## 実装してみた
|
93
|
+
|
94
|
+
(※実装して確かめた結果明らかになった↑の記述の諸々の誤りも修正)
|
95
|
+
変えた部分を以下に示す.
|
96
|
+
|
97
|
+
### Window.h
|
98
|
+
|
99
|
+
マウスカーソルのフレーム間移動量 (dx,dy) が欲しいのだけど,既存コードの実装のままでそういった情報を拾えるのか否か不明であったため,ここは適当に以下のようにやっつけコードを追加して対応した.
|
100
|
+
|
101
|
+
* それ用のメンバ変数を追加し
|
102
|
+
* `operator bool()` 内の `glfwPollEvents();` 以降に変数の値を更新する処理を追加した
|
103
|
+
|
104
|
+
```C++
|
105
|
+
//適当にこれらの変数を追加した
|
106
|
+
bool m_bMouseLDown = false;
|
107
|
+
bool m_bAltKeyDown = false;
|
108
|
+
GLfloat m_PrevMousePos[2] = { 0,0 };
|
109
|
+
GLfloat m_CurrMousePos[2] = { 0,0 };
|
110
|
+
|
111
|
+
//描画ループの継続判定
|
112
|
+
explicit operator bool()
|
113
|
+
{
|
114
|
+
double x, y;
|
115
|
+
|
116
|
+
//イベントを取り出す
|
117
|
+
glfwPollEvents();
|
118
|
+
|
119
|
+
//追加した変数群の値更新処理
|
120
|
+
m_bMouseLDown = glfwGetMouseButton(window, GLFW_MOUSE_BUTTON_LEFT) != GLFW_RELEASE;
|
121
|
+
m_bAltKeyDown = glfwGetKey(window, GLFW_KEY_LEFT_ALT) != GLFW_RELEASE;
|
122
|
+
m_PrevMousePos[0] = m_CurrMousePos[0];
|
123
|
+
m_PrevMousePos[1] = m_CurrMousePos[1];
|
124
|
+
glfwGetCursorPos(window, &x, &y);
|
125
|
+
m_CurrMousePos[0] = GLfloat(x);
|
126
|
+
m_CurrMousePos[1] = GLfloat(y);
|
127
|
+
```
|
128
|
+
|
129
|
+
### main.cpp
|
130
|
+
|
131
|
+
ベクトルや行列の演算のために,GLM をもってきた.
|
132
|
+
|
133
|
+
```C++
|
134
|
+
#include "glm.hpp" //GLMのヘッダをinclude
|
135
|
+
|
136
|
+
//作業関数を追加.3次元ベクトルVに直行するベクトルを返す.(※中身が知りたい場合,ここteratailでの私の過去の質問を参照されたい)
|
137
|
+
inline glm::vec3 GetPerpendicularOf( const glm::vec3 &V )
|
138
|
+
{
|
139
|
+
if( fabs(V[0]) < fabs(V[1]) )
|
140
|
+
{ return glm::vec3( 0, -V[2], V[1] ); }
|
141
|
+
else
|
142
|
+
{ return glm::vec3( V[2], 0, -V[0] ); }
|
143
|
+
}
|
144
|
+
```
|
145
|
+
|
146
|
+
で,モデルビュー行列そのものに関する実装は以下.
|
147
|
+
|
148
|
+
* メインループの `while` の手前に定数と変数の定義を追加
|
149
|
+
* 透視投影変換行列は適当に見やすい感じに変えてる
|
150
|
+
* モデルビュー行列の演算部分をまるごと差し替え(GLM はここの実装で使っている.変数 `modelview` 自体は元々の `Matrix` 型のままにしてある.)
|
151
|
+
|
152
|
+
```C++
|
153
|
+
//定数と変数を追加
|
154
|
+
constexpr GLfloat CamRotRadius = 20; //カメラ旋回半径 r
|
155
|
+
glm::vec3 P(0,0,0); //カメラ旋回中心点 P(ワールド座標系での値)
|
156
|
+
glm::vec3 CX(1,0,0); //カメラ右手方向単位ベクトル(ワールド座標系での値)
|
157
|
+
glm::vec3 CZ(0,0,1); //カメラ後ろ方向単位ベクトル(ワールド座標系での値)
|
158
|
+
|
159
|
+
//ウィンドウが開いている間繰り返す
|
160
|
+
while (window)
|
161
|
+
{
|
162
|
+
//フレームバッファを初期化する
|
163
|
+
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
|
164
|
+
//シェーダプログラム使用開始
|
165
|
+
glUseProgram(program);
|
166
|
+
|
167
|
+
//---------------------------------------
|
168
|
+
//透視投影変換行列
|
169
|
+
// ※見やすいように,適度な具合に変更
|
170
|
+
const Matrix projection = Matrix::frustum( -0.1f, 0.1f, -0.1f, 0.1f, 1.0f, 100.0f);
|
171
|
+
|
172
|
+
//---------------------------------------
|
173
|
+
//※元々存在した行列 view に関するコード
|
174
|
+
// view はモデルビュー行列に一切関わっていないので不要なのだが,
|
175
|
+
// なんか後ろの方で別の目的に使われている(?)ので残してある.
|
176
|
+
const GLfloat* location(window.getLocation(0));//マウスをドラッグしたときのカーソルの増加量を取得する
|
177
|
+
GLfloat pos[3] = { 0 };//カメラの球面座標上の座標
|
178
|
+
pos[0] = -4.0f * cos(location[0]) * cos(location[1]);//* -4.0fは素の増加量だと、見た目の変化が乏しいため
|
179
|
+
pos[1] = -4.0f * sin(location[1]);
|
180
|
+
pos[2] = -4.0f * sin(location[0]) * cos(location[1]);
|
181
|
+
Matrix view(Matrix::lookat(pos[0], pos[1], pos[2], 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f));//カメラを注視点を向いたまま、球体座標上に動かす
|
182
|
+
|
183
|
+
//---------------------------------------
|
184
|
+
//モデルビュー変換行列を求める
|
185
|
+
Matrix modelview = Matrix::identity();
|
186
|
+
{
|
187
|
+
glm::vec3 CY = glm::cross( CZ, CX ); //カメラ上方向単位ベクトル(ワールド座標系での値)
|
188
|
+
|
189
|
+
//マウスカーソル移動量 (dx,dy)[pixel]
|
190
|
+
auto dx = window.m_CurrMousePos[0] - window.m_PrevMousePos[0];
|
191
|
+
auto dy = window.m_CurrMousePos[1] - window.m_PrevMousePos[1];
|
192
|
+
auto dnorm = sqrt(dx*dx + dy*dy);
|
193
|
+
|
194
|
+
if( window.m_bMouseLDown && dnorm>1 )
|
195
|
+
{
|
196
|
+
if( window.m_bAltKeyDown )
|
197
|
+
{//並行移動
|
198
|
+
P += 0.01f * ( dx*CX - dy*CY );
|
199
|
+
}
|
200
|
+
else
|
201
|
+
{//P を中心とした旋回
|
202
|
+
glm::mat3x3 R;
|
203
|
+
{//任意軸周り回転行列の計算(カメラ座標系のものを回す用)
|
204
|
+
glm::vec3 Rot_Axis = glm::normalize( glm::vec3( dy, dx, 0 ) );
|
205
|
+
glm::vec3 OtherAxis1 = glm::normalize( GetPerpendicularOf(Rot_Axis) );
|
206
|
+
glm::vec3 OtherAxis2 = glm::cross( Rot_Axis, OtherAxis1 );
|
207
|
+
glm::mat3x3 M(
|
208
|
+
OtherAxis1[0], OtherAxis2[0], Rot_Axis[0],
|
209
|
+
OtherAxis1[1], OtherAxis2[1], Rot_Axis[1],
|
210
|
+
OtherAxis1[2], OtherAxis2[2], Rot_Axis[2]
|
211
|
+
);
|
212
|
+
|
213
|
+
double RotAngle = dnorm * 0.01;
|
214
|
+
glm::mat3x3 Roll( cos(RotAngle), sin(RotAngle), 0, -sin(RotAngle), cos(RotAngle), 0, 0,0,1 );
|
215
|
+
R = glm::transpose(M) * Roll * M;
|
216
|
+
}
|
217
|
+
|
218
|
+
glm::mat3x3 C2W( CX,CY,CZ ); //カメラ座標→ワールド座標 変換行列
|
219
|
+
CX = C2W * R * glm::vec3(1,0,0);
|
220
|
+
CZ = C2W * R * glm::vec3(0,0,1);
|
221
|
+
CY = glm::cross( CZ, CX );
|
222
|
+
}
|
223
|
+
}
|
224
|
+
|
225
|
+
//カメラ位置
|
226
|
+
glm::vec3 CamPos = P + CamRotRadius * CZ;
|
227
|
+
|
228
|
+
//モデルビュー行列の中身を設定
|
229
|
+
modelview[0]=CX[0]; modelview[4]=CX[1]; modelview[8]=CX[2]; modelview[12]=glm::dot( CX, -CamPos );
|
230
|
+
modelview[1]=CY[0]; modelview[5]=CY[1]; modelview[9]=CY[2]; modelview[13]=glm::dot( CY, -CamPos );
|
231
|
+
modelview[2]=CZ[0]; modelview[6]=CZ[1]; modelview[10]=CZ[2]; modelview[14]=glm::dot( CZ, -CamPos );
|
232
|
+
}
|
233
|
+
|
234
|
+
//※以降は既存コードのままなので省略
|
235
|
+
```
|
236
|
+
|
237
|
+
マウスをどっちに動かしたらどっちに動くのか? というのが逆の方がやりやすいとかはあるかも.
|
238
|
+
(マウスを動かした方に「カメラが」動く感じになっているので,絵的には逆に動いているように感じるかも)
|
239
|
+
|
240
|
+
並行移動後の旋回に関しては,多分点 **P** の位置に何かを描画するとか,何かが無いと動きが掴み難いと思う.
|
241
|
+
(Blender の動画だとグリッド平面みたいなのがあるよね)
|
2
誤記修正
test
CHANGED
@@ -66,7 +66,7 @@
|
|
66
66
|
|
67
67
|
### カメラの球面上での移動
|
68
68
|
|
69
|
-
フレーム間のマウス移動量(dx, dy) から,カメラ座標系における回転軸を (dx, dy, 0) として定め, **CX**, **CZ** をそれぞれ回転させる.
|
69
|
+
フレーム間のマウス移動量(dx, dy) から,カメラ座標系における回転軸を (dx, dy, 0) と直交するベクトルとして (-dy, dx, 0) と定めて, **CX**, **CZ** をそれぞれ回転させる.
|
70
70
|
(言うまでも無く回転量は (dx,dy) のノルムに比例した感じで適当に決める)
|
71
71
|
|
72
72
|
「任意軸周りの回転」とかでググって回転行列 **R** の算出を実装し,
|
1
誤変換修正とちょっとした補足追加
test
CHANGED
@@ -51,11 +51,16 @@
|
|
51
51
|
* **CX** <= (1,0,0)
|
52
52
|
* **CZ** <= (0,0,1)
|
53
53
|
|
54
|
-
とかかな.
|
54
|
+
とかかな.この場合,
|
55
|
+
|
56
|
+
* **CY** <= (0,1,0)
|
57
|
+
* カメラ位置 <= (0,0, -r)
|
58
|
+
|
59
|
+
になる感じ.
|
55
60
|
|
56
61
|
### 平行移動
|
57
62
|
|
58
|
-
(
|
63
|
+
(前述のように,カメラ位置は毎回 **P** を用いて計算されるのだから)フレーム間のマウス移動量 (dx, dy) に合わせて **P** を動かすだけの話.
|
59
64
|
動かす方向はカメラ座標系の上下左右に合わせたいのだろうから,**CX**, **CY** に沿う方向に動かしてやればよい.
|
60
65
|
* **P** += ( dx * **CX** + dy * **CY** )
|
61
66
|
|