「回転させると正方形が長方形になっちゃう」という話について回答
OpenGLのルールとしては,(ここではビューポートがクライアント領域全体だから)
「クリッピング座標系の -1~1 の範囲を,ウィンドウのクライアント領域全体に表示するよ」
という話だよね.
だから,例えば,クライアント領域のサイズが 横200縦100[pixel] だとしたら,
クリッピング座標系でX,Yが共に -1~1 の正方形な範囲を,200100[pixel]という横長な長方形の形に伸縮するという話になるわけ.
…ということは,
最終的に表示される形状がこの伸縮によって歪んでほしくない(正方形を描画したいのに長方形とかになってほしくない)ならば,
この伸縮の結果がまともな形になるようにクリッピング座標系での座標を指定すればいいってことになるよね.
この200*100の例であれば,クリッピング座標系では横方向を1/2に潰した形状にしておけば,最終的にうまい形に伸縮されるわけだ.
という感じで,最終的にうまい形になるようなクリッピング座標系にpixel座標系から変換する処理を書けばいいよ.
さて,回答としては上記だけでも十分だと思うけど,
「最終的な伸縮でうまくいくような座標に変換する」ってところがちょっと難しいかもしれないから,
ここでは考えやすいように,
pixel座標系 → クリッピング座標系(形状はいい感じに潰しておく必要がある)
という変換を,
pixel座標系 → 何か考えやすい中間の座標系(形状はそのまま.潰れてない) →(潰す)→ クリッピング座標系(形状はいい感じに潰しておく必要がある)
として,間に1つ座標系を挟んでみる話をするよ.
この座標系の名前を,ここでは便宜上「おCAT座標系」とか呼ぼうか.
このようにすると,
必要な回転とか並進とかは全てこの「おCAT座標系」の上で行ってやり,その結果を最後に一発潰してクリッピング座標系に変換すればいいって話になる.
「最後に一発潰す」のところは,こう書けるよね.
glScalef( 0.5f, 1, 1 ); //X座標を半分に潰す
{//このブロックの中は「おCAT座標系」
glTranslatef( ... ); //おCAT座標系での並進
glRotatedf( ... ); //おCAT座標系での回転
glRectf( ... ); //おCAT座標系での座標
}
だからあとは,pixel座標系からおCAT座標系への変換があればいい.
最終的にクライアント領域に表示されることになるおCAT座標系の範囲というのはどこからどこまでかな?
というのを考えれば,
この例ではX方向を半分に潰した結果が -1~1 になる範囲なんだから,「X方向が -2~2,Y方向が -1~1」という長方形な範囲だ.
この範囲に 200*100[pixel] な範囲がすっぽりはまるような変換をしてやることは問題ないよね.
(元々あなたがやっていた,pixel座標をクリッピング座標に変換する計算と話は全く一緒.倍率とオフセット量を考えればいいよ)
…と言う話のサンプルコードを以下に示すよ.
おCAT座標系での値は,そうだとわかるように変数名の末尾を _oc としている.
ウィンドウのサイズが変更された際に,上記の計算をして,glRectf()とかに渡す値を求めてるようにしてある.
あと,このコードでは,上下方向の反転も glScalef で行う形にしてある.
つまり,pixelの世界とおCAT座標系では上下の向き(Y軸の正の方向)は揃っていて,クリッピング座標系だけがそれらとは逆という形で考えてる.
C++
1namespace
2{
3 //値がクライアント領域のサイズ次第である変数群の再計算が必要なことを示すフラグ
4 bool NeedToRecalc_Flag = true;
5
6 //ウィンドウサイズ変化時のコールバック
7 void OnWndSizeChanged( GLFWwindow *const pWnd, int w, int h )
8 {
9 NeedToRecalc_Flag = true; //フラグを立てておく
10 glViewport( 0,0, w,h );
11 }
12}
13
14int main()
15{
16 GLFWwindow *pWindow = nullptr;
17 {//GLFW ウィンドウ準備
18 if( !(pWindow = glfwCreateWindow( 640, 480, "GLFW", NULL, NULL )) )return 1; //ウィンドウ生成
19 glfwMakeContextCurrent( pWindow ); //ウィンドウをGLの処理対象にする
20 glfwSetWindowSizeCallback( pWindow, OnWndSizeChanged ); //callback設定
21 }
22
23 //設定.てきとーに.
24 glfwSwapInterval(1); //垂直同期信号待ちの設定っぽい
25 glClearColor( 0.1f, 0.2f, 0.4f, 0.0f );
26 glMatrixMode( GL_MODELVIEW );
27 glColor3d( 1,0,0 ); //Rectの描画色
28
29 //描画したいRect の2頂点 [pixel]
30 const glm::vec2 V1{ 100, 70 };
31 const glm::vec2 V2 = V1 + glm::vec2{ 100,100 };
32
33 //ウィンドウクライアント領域のサイズに合わせて計算する必要がある変数群
34 //※初期値には特に意味はない
35 float AspectRatio = 1.0; //ウィンドウクライアント領域の縦横比(W/H)
36 glm::vec2 C_oc{0,0}; //Rectの中心
37 glm::vec2 RectHalfSize_oc{1,1}; //Rectの(縦横半分の)サイズ
38
39 //メインループ
40 double rotate_angle = 0; //Rectの回転角度[deg]
41 while( glfwWindowShouldClose( pWindow ) == 0 )
42 {
43 //(ESCキーで終了)
44 if( glfwGetKey( pWindow, GLFW_KEY_ESCAPE ) == GLFW_PRESS ){ glfwDestroyWindow( pWindow ); break; }
45
46 //回転角度の変更用キー処理
47 if( glfwGetKey( pWindow, GLFW_KEY_A ) == GLFW_PRESS ){ rotate_angle -= 1.0; }
48 if( glfwGetKey( pWindow, GLFW_KEY_S ) == GLFW_PRESS ){ rotate_angle += 1.0; }
49 if( glfwGetKey( pWindow, GLFW_KEY_D ) == GLFW_PRESS ){ rotate_angle = 0; }
50
51 //ウィンドウサイズが変わったら必要な計算をする
52 if( NeedToRecalc_Flag )
53 {
54 NeedToRecalc_Flag = false; //フラグを下す
55
56 int WndW, WndH;
57 glfwGetWindowSize( pWindow, &WndW, &WndH );
58 AspectRatio = WndW / (float)WndH;
59
60 const float CvtRate = 2.0f / WndH;
61 const glm::vec2 CvtOffset{ -AspectRatio, -1.0f };
62
63 const glm::vec2 V1_oc = V1*CvtRate + CvtOffset;
64 const glm::vec2 V2_oc = V2*CvtRate + CvtOffset;
65
66 C_oc = ( V1_oc + V2_oc ) * 0.5f;
67 RectHalfSize_oc = glm::abs( V1_oc - V2_oc ) * 0.5f;
68 }
69
70 //
71 //draw
72 //
73 glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );
74 glLoadIdentity();
75
76 glScalef( (1.0f / AspectRatio), -1, 1 ); //おCAT座標系 から クリッピング座標系 への変換
77 {//この中は おCAT座標系
78 glTranslatef( C_oc.x, C_oc.y, 0 );
79 glRotated( rotate_angle, 0,0,1 );
80 glRectf( -RectHalfSize_oc.x, -RectHalfSize_oc.y, RectHalfSize_oc.x, RectHalfSize_oc.y );
81 }
82
83 glfwSwapBuffers( pWindow );
84 glfwWaitEvents();
85 }
86
87 return 0;
88}
[追記]
動作結果のスクリーンショット画像を示す.
これは実行後にウィンドウをかなり小さくした状態のスクリーンショットである.
矩形を回転させても,ウィンドウ座標を変更しても
- 矩形の中心は常に(150,120)あたりにある
- 矩形の1辺は100[pixel]くらいになってる