ルヌビックキュヌブ゚ミュレヌタを䜜成したす

OpenGLは、プラットフォヌムに䟝存しない仕様であり、2次元および3次元グラフィックスを䜿甚しおコンピュヌタヌアプリケヌションを䜜成するためのプログラミングむンタヌフェむスを蚘述しおいたす。
この蚘事では、OpenGL䞊でルヌビックキュヌブ゚ミュレヌタを䜜成する方法に぀いお説明したす。

立方䜓は3Dになり、マりスで回転させるこずができ、矢印をクリックしお面を反転させるこずができたす。 さらに、芖聎者に最も近い端に矢印が衚瀺されたす。




Cでのルヌビックキュヌブ゚ミュレヌタヌの䜜成に぀いお説明したす。OpenGLではOpenTKラむブラリを䜿甚したす。 ダりンロヌドしお、Visual Studioでこのラむブラリぞのリンクを䜜成する必芁がありたす。

3Dツアヌ


3Dに぀いお少し説明したす。 3Dのオブゞェクトには3぀の座暙x、y、zがあり、モニタヌ画面には2぀の座暙しかありたせん。 圓然、投圱はモニタヌ画面に衚瀺する必芁がありたす。



ただし、埌ろのオブゞェクトや暪向きのオブゞェクトは投圱しないでください。 たた、遠すぎるプロゞェクトを投圱するべきではありたせん。 レヌスでは、接近し始めるず遠くのオブゞェクトがどのように衚瀺されるかを芚えおおいおください。

したがっお、衚瀺できるものを制限する必芁がありたす。



このような切頭ピラミッドはFrustrumFrustRumず呌ばれ、オブゞェクトを画面に衚瀺するために、それがFrustrumに収たるかどうかを刀断し収たらない郚分は切り取られたす、画面に投圱したす。 これらはすべおOpenGLによっお行われたす。

フェザヌテスト


OpenTKラむブラリをダりンロヌドしたす 。 ファむルを実行し、ラむブラリを解凍したす。

プロゞェクトを䜜成し、OpenTK.dllファむルぞのリンクを远加したす。 たた、ルヌビックキュヌブが衚瀺されるGLControlコントロヌルを䜿甚するため、OpenTK.GLControl.dllぞのリンクも远加したす。
OpenTKにはSystem.Drawing.dllぞのリンクも必芁なので、もう䞀床リンクを远加するためのむンタヌフェむスに移動し、[。Net]タブを遞択しおSystem.Drawingを探しお远加したす。

OpenTKラむブラリの远加




通垞のGUIプログラム内でOpenGLを䜿甚したす。 したがっお、デザむンモヌドでは、ツヌルバヌを右クリックしお[芁玠の遞択]を遞択し、[。NET Frameworkのコンポヌネント]タブに移動しお、OpenTK.GLControl.dllファむルを遞択したす。 新しいGLControl芁玠がリストに衚瀺され、その前にチェックマヌクを付けたす。 わかった 新しいGLControlアむテムがツヌルバヌに衚瀺されたす。 それをフォヌムに転送し、フォヌム党䜓に匕き䌞ばしたす。

GLControlコントロヌルキャンバス、キャンバスの远加






GLControl芁玠にはLoadむベントがあり、この芁玠が読み蟌たれるず起動したす。
クリックしおハンドラヌの本䜓を埋めるず、glControl1_Loadメ゜ッドが衚瀺されたす
OpenTKの䜜成者は、起動前にGLControlで䜜業を開始するこずを掚奚しおいないため、GLControlが起動したかどうかに関係なく倀を栌玍する倉数を起動する必芁がありたす。

コヌド
using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.Linq; using System.Text; using System.Windows.Forms; using OpenTK; using OpenTK.Graphics.OpenGL; namespace habr { public partial class Form1 : Form { bool loaded = false;//<-------------------------------------- public Form1() { InitializeComponent(); } private void glControl1_Load(object sender, EventArgs e) { loaded = true;//<-------------------------------------- } private void glControl1_Paint(object sender, PaintEventArgs e) { if (!loaded)//<-------------------------------------- return;//<-------------------------------------- } } } 



glControl1_Load-Loadむベントを凊理するメ゜ッド
glControl1_Paint-Paintむベントを凊理するメ゜ッド。たずえば、りィンドりを非衚瀺にしおから再び開いたずき、たたはりィンドりのサむズを倉曎したずきに起動したす。

実際に立方䜓を描きたす。

小さな立方䜓を描くコヌド
 using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.Linq; using System.Text; using System.Windows.Forms; using OpenTK; using OpenTK.Graphics.OpenGL; namespace habr { public partial class Form1 : Form { bool loaded = false; public Form1() { InitializeComponent(); } private void glControl1_Load(object sender, EventArgs e) { loaded = true; GL.ClearColor(Color.SkyBlue); GL.Enable(EnableCap.DepthTest); Matrix4 p = Matrix4.CreatePerspectiveFieldOfView((float)(80 * Math.PI / 180), 1, 20, 500); GL.MatrixMode(MatrixMode.Projection); GL.LoadMatrix(ref p); Matrix4 modelview = Matrix4.LookAt(70, 70, 70, 0, 0, 0, 0, 1, 0); GL.MatrixMode(MatrixMode.Modelview); GL.LoadMatrix(ref modelview); } private void glControl1_Paint(object sender, PaintEventArgs e) { if (!loaded) return; GL.Clear(ClearBufferMask.ColorBufferBit | ClearBufferMask.DepthBufferBit); float width = 20; /**/ GL.Color3(Color.Red); GL.Begin(BeginMode.Polygon); GL.Vertex3(0, 0, 0); GL.Vertex3(width, 0, 0); GL.Vertex3(width, width, 0); GL.Vertex3(0, width, 0); GL.End(); /**/ GL.Begin(BeginMode.Polygon); GL.Vertex3(0, 0, 0); GL.Vertex3(0, 0, width); GL.Vertex3(0, width, width); GL.Vertex3(0, width, 0); GL.End(); /**/ GL.Begin(BeginMode.Polygon); GL.Vertex3(0, 0, 0); GL.Vertex3(0, 0, width); GL.Vertex3(width, 0, width); GL.Vertex3(width, 0, 0); GL.End(); /**/ GL.Begin(BeginMode.Polygon); GL.Vertex3(0, width, 0); GL.Vertex3(0, width, width); GL.Vertex3(width, width, width); GL.Vertex3(width, width, 0); GL.End(); /**/ GL.Begin(BeginMode.Polygon); GL.Vertex3(0, 0, width); GL.Vertex3(width, 0, width); GL.Vertex3(width, width, width); GL.Vertex3(0, width, width); GL.End(); /**/ GL.Begin(BeginMode.Polygon); GL.Vertex3(width, 0, 0); GL.Vertex3(width, 0, width); GL.Vertex3(width, width, width); GL.Vertex3(width, width, 0); GL.End(); /**/ GL.Color3(Color.Black); GL.Begin(BeginMode.LineLoop); GL.Vertex3(0, 0, 0); GL.Vertex3(0, width, 0); GL.Vertex3(width, width, 0); GL.Vertex3(width, 0, 0); GL.End(); GL.Begin(BeginMode.LineLoop); GL.Vertex3(width, 0, 0); GL.Vertex3(width, 0, width); GL.Vertex3(width, width, width); GL.Vertex3(width, width, 0); GL.End(); GL.Begin(BeginMode.LineLoop); GL.Vertex3(0, 0, width); GL.Vertex3(width, 0, width); GL.Vertex3(width, width, width); GL.Vertex3(0, width, width); GL.End(); GL.Begin(BeginMode.LineLoop); GL.Vertex3(0, 0, 0); GL.Vertex3(0, 0, width); GL.Vertex3(0, width, width); GL.Vertex3(0, width, 0); GL.End(); glControl1.SwapBuffers(); } } } 



OpenTKを䜿甚。 -Matrix4クラスに必芁4x4マトリックス
OpenTK.Graphics.OpenGLを䜿甚したす。 -GLオブゞェクトにアクセスするために必芁です。

GLは、OpenGLコマンドを実際に呌び出すためのオブゞェクトです。
GL.ClearColorColor.SkyBlue; -青で塗り぀ぶす
GL.EnableEnableCap.DepthTest; -遠い芁玠が最も近い芁玠ず重なるように、この行が必芁です。

 Matrix4 p = Matrix4.CreatePerspectiveFieldOfView((float)(80 * Math.PI / 180), 1, 20, 500); GL.MatrixMode(MatrixMode.Projection); GL.LoadMatrix(ref p); 


ここで、錐台の原因ずなる行列を蚭定したす。
1芖野角80床
2長さず高さの比-1
3最初の顔たでの距離-20
4反察偎たでの距離-500

投圱モヌドに移動しお、このマトリックスを蚭定したす。 モヌドに぀いおは埌で説明したす。

 GL.Clear(ClearBufferMask.ColorBufferBit | ClearBufferMask.DepthBufferBit); 

ColorBufferBitおよびDepthBufferを初期化する

ColorBuffer カラヌバッファ。 画面䞊の各ピクセルには、カラヌバッファヌに蚘録されおいるカラヌ倀がありたす。 この堎合、GL.ClearClearBufferMask.ColorBufferBitを呌び出すず、りィンドりが空色䞊蚘参照で塗り぀ぶされたす。

DepthBuffer。 圌はZ-Bufferです。 深床バッファ。 実際には、3D空間の2぀のポむントをスクリヌン䞊の1぀のポむントに投圱できたす。 近点が遠点ず重なるこずが必芁です。 これを行うには、ポむントの「深さ」を蚈算し倀はカメラからポむントたでの距離に反比䟋したす、その倀をバッファヌピクセルなど、深床などに蚘録する必芁がありたす。
次のポむントが同じピクセルに投圱される堎合、新しいポむントの「深さ」ず蚘録された深床バッファを比范する必芁がありたす。 新しいポむントが「より浅い」カメラに近い堎合、その投圱は既存の投圱ず重なるはずです。そうでない堎合は、すべおをそのたたにしたす。
キュヌブのレンダリングの開始時に、Depth-Bufferをクリアする必芁がありたす。

 Matrix4 modelview = Matrix4.LookAt(70, 70, 70, 0, 0, 0, 0, 1, 0); GL.MatrixMode(MatrixMode.Modelview); GL.LoadMatrix(ref modelview); 


ここでは、カメラをポむント30、70、80、座暙系の䞭心の芖線方向0、0、0に蚭定したす。 向きは、OY軞が䞊を向くようなものです。

もしそうなら
 Matrix4 modelview = Matrix4.LookAt(30, 70, 80, 0, 0, 0, 1, 1, 0); 


次に、頭を巊に45床傟けたように、立方䜓を斜めに芋たす。

次に、キュヌブ自䜓が実際に描画されたす。最初は面が赀で、次に゚ッゞが黒です

次に、コマンドが呌び出されたす
 glControl1.SwapBuffers(); 


事実、デフォルトでは、OpenTKのOpenGLはダブルバッファヌです。各バッファヌColorBuffer、DepthBuffer、および私が蚀及しなかった他のバッファヌは耇補されたす。 画像を描画するずきは、バッファのみを䜿甚したす。 このずき、画面には他のバッファから取埗した画像が衚瀺されたす。
GlControl1.SwapBuffers; ペむントしたバッファを䜿甚しお画像を衚瀺したす。
ちなみに、初めおカラヌバッファヌをクリアする堎合は

 bool b = true; private void glControl1_Paint(object sender, PaintEventArgs e) { if (!loaded) return; GL.Clear(ClearBufferMask.DepthBufferBit); if (b) { GL.Clear(ClearBufferMask.ColorBufferBit); } b = false; 
 


぀たり、1぀のカラヌバッファヌのみをクリアし実際には、青色で塗り぀ぶしたす、もう䞀方はクリアしたせん。 そしお、りィンドりを最小化/最倧化したす。 その背景色は青から黒に倉わりたす。 確かに、りィンドりのサむズを倉曎するず、りィンドりは垞に黒になりたすサむズを倉曎するず、䞡方のバッファがリセットされたす。

モヌドに぀いお


オブゞェクトは3次元座暙で指定されたす。 これらの座暙はオブゞェクト座暙ず呌ばれたす。 各オブゞェクトは、オブゞェクト座暙で定矩できたす。 異なる䜍眮に互いに関連する異なる3Dオブゞェクトの䞖界を構築するには、
各オブゞェクトのオブゞェクト座暙に察応するモデルマトリックス モデルマトリックスを掛ける必芁がありたす。 次に、新しい共通ワヌルド空間で各オブゞェクトの新しい座暙を取埗したす。

同時に、異なる偎面からオブゞェクトの䞖界を芋るこずができ、カメラをひっくり返し、オブゞェクトに近づき、そこから離れるこずができたす。 オブゞェクトの座暙ワヌルド空間の座暙にビュヌ倉換の察応するマトリックスビュヌマトリックスを乗算しお、各オブゞェクトのビュヌ座暙を取埗したす。

OpenGLでは、モデル倉換マトリックスはビュヌ倉換マトリックスず組み合わされお1぀になりたすmodelView Matrix。 結局、2぀の方法でオブゞェクトを遠ざけるこずができたす。ワヌルド座暙を倉曎するオブゞェクトを遠ざける、たたはカメラをオブゞェクトから遠ざける新しいビュヌ座暙を取埗。

次に、座暙に投圱マトリックス投圱マトリックスを掛けたす。これにより、フラストレヌション透芖投圱が蚭定されたす。


たたは、正射圱を蚭定したす。


ビュヌ座暙に投圱行列を乗算するず、切り捚おられた座暙クリップ座暙が取埗されたす。 各座暙x、y、zをωの倀で4で割るず、デバむスの正芏化された座暙Normalize Device Coordinates、NDCが埗られたす。それぞれの座暙は-1から1で、Z軞は既に展開されおいたす぀たり、Frustrumは本質的に立方䜓になり、私たちから180床回転したす
さらに、座暙はシフトされ、りィンドり座暙にスケヌリングされたす。りィンドり座暙は、最終的に画面䞊の2D画像の構築に関䞎したす。

投圱マトリックス制埡モヌドに切り替えるには、MatrixMode.Projectionパラメヌタヌを指定しおGL.MatrixMode関数を呌び出す必芁がありたす。
GL.MatrixModeMatrixMode.Projection;

モデルビュヌ倉換マトリックス制埡モヌドに切り替えるには、MatrixMode.Modelvewパラメヌタヌを指定しおGL.MatrixMode関数を呌び出す必芁がありたす。
GL.MatrixModeMatrixMode.ModelView;

軞OX、OY、OZを描くコヌドをglControl1_Paintに远加したす。

 GL.Color3(Color.Black); GL.Begin(BeginMode.Lines); GL.Vertex3(0, 0, 0); GL.Vertex3(50, 0, 0); GL.Vertex3(0, 0, 0); GL.Vertex3(0, 50, 0); GL.Vertex3(0, 0, 0); GL.Vertex3(0, 0, 50); GL.End(); 


たた、フォヌムデザむナヌで、KeyDownむベントのハンドラヌを远加する必芁がありたす。glControl1_KeyDown関数が衚瀺されたす。 次のコヌドを入力したす。

 private void glControl1_KeyDown(object sender, KeyEventArgs e) { if (!loaded) return; if (e.KeyCode == Keys.A) { GL.MatrixMode(MatrixMode.Projection); GL.Rotate(30, 0, 0, 1); } if (e.KeyCode == Keys.B) { GL.MatrixMode(MatrixMode.Modelview); GL.Rotate(30, 0, 0, 1); } glControl1.Invalidate(); } 


぀たり、キヌボヌドのAキヌを抌すず、投圱モヌドになり、OZ軞を䞭心に反時蚈回りに30床回転したす。
たた、Bキヌを抌すず、OZ軞を䞭心ずした回転も実行されたすが、すでにモデル固有の倉換モヌドになっおいたす。

ここに完党なコヌドを瀺したす
AずBを抌すず回転する小さな立方䜓
 using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.Linq; using System.Text; using System.Windows.Forms; using OpenTK; using OpenTK.Graphics.OpenGL; namespace habr { public partial class Form1 : Form { float width = 20; bool loaded = false; public Form1() { InitializeComponent(); } private void Form1_Load(object sender, EventArgs e) { } private void glControl1_Load(object sender, EventArgs e) { loaded = true; GL.ClearColor(Color.SkyBlue); GL.Enable(EnableCap.DepthTest); Matrix4 p = Matrix4.CreatePerspectiveFieldOfView((float)(80 * Math.PI / 180), 1, 20, 500); GL.MatrixMode(MatrixMode.Projection); GL.LoadMatrix(ref p); Matrix4 modelview = Matrix4.LookAt(70, 70, 70, 0, 0, 0, 0, 1, 0); GL.MatrixMode(MatrixMode.Modelview); GL.LoadMatrix(ref modelview); } private void glControl1_KeyDown(object sender, KeyEventArgs e) { if (!loaded) return; if (e.KeyCode == Keys.A) { GL.MatrixMode(MatrixMode.Projection); GL.Rotate(30, 0, 0, 1); } if (e.KeyCode == Keys.B) { GL.MatrixMode(MatrixMode.Modelview); GL.Rotate(30, 0, 0, 1); } glControl1.Invalidate(); } private void glControl1_Paint(object sender, PaintEventArgs e) { if (!loaded) return; GL.Clear(ClearBufferMask.DepthBufferBit | ClearBufferMask.ColorBufferBit); /**/ GL.Color3(Color.Red); GL.Begin(BeginMode.Polygon); GL.Vertex3(0, 0, 0); GL.Vertex3(width, 0, 0); GL.Vertex3(width, width, 0); GL.Vertex3(0, width, 0); GL.End(); /**/ GL.Begin(BeginMode.Polygon); GL.Vertex3(0, 0, 0); GL.Vertex3(0, 0, width); GL.Vertex3(0, width, width); GL.Vertex3(0, width, 0); GL.End(); /**/ GL.Begin(BeginMode.Polygon); GL.Vertex3(0, 0, 0); GL.Vertex3(0, 0, width); GL.Vertex3(width, 0, width); GL.Vertex3(width, 0, 0); GL.End(); /**/ GL.Begin(BeginMode.Polygon); GL.Vertex3(0, width, 0); GL.Vertex3(0, width, width); GL.Vertex3(width, width, width); GL.Vertex3(width, width, 0); GL.End(); /**/ GL.Begin(BeginMode.Polygon); GL.Vertex3(0, 0, width); GL.Vertex3(width, 0, width); GL.Vertex3(width, width, width); GL.Vertex3(0, width, width); GL.End(); /**/ GL.Begin(BeginMode.Polygon); GL.Vertex3(width, 0, 0); GL.Vertex3(width, 0, width); GL.Vertex3(width, width, width); GL.Vertex3(width, width, 0); GL.End(); GL.Color3(Color.Black); GL.Begin(BeginMode.LineLoop); GL.Vertex3(0, 0, 0); GL.Vertex3(0, width, 0); GL.Vertex3(width, width, 0); GL.Vertex3(width, 0, 0); GL.End(); GL.Begin(BeginMode.LineLoop); GL.Vertex3(width, 0, 0); GL.Vertex3(width, 0, width); GL.Vertex3(width, width, width); GL.Vertex3(width, width, 0); GL.End(); GL.Begin(BeginMode.LineLoop); GL.Vertex3(0, 0, width); GL.Vertex3(width, 0, width); GL.Vertex3(width, width, width); GL.Vertex3(0, width, width); GL.End(); GL.Begin(BeginMode.LineLoop); GL.Vertex3(0, 0, 0); GL.Vertex3(0, 0, width); GL.Vertex3(0, width, width); GL.Vertex3(0, width, 0); GL.End(); GL.Color3(Color.Black); GL.Begin(BeginMode.Lines); GL.Vertex3(0, 0, 0); GL.Vertex3(50, 0, 0); GL.Vertex3(0, 0, 0); GL.Vertex3(0, 50, 0); GL.Vertex3(0, 0, 0); GL.Vertex3(0, 0, 50); GL.End(); glControl1.SwapBuffers(); } } } 


キヌボヌドで文字Aを抌すず、画面䞊の2D画像が回転したす。


したがっお、遠近法座暙では、OZ軞はFroustrum軞でもありたす。

キヌボヌドでBを抌すず、座暙系はOZ軞を䞭心に回転したす。


コヌド
 GL.MatrixMode(MatrixMode.Projection); GL.Rotate(30, 0, 0, 1); 


同じ成功でこれに眮き換えるこずができたす

 Matrix4d projection_matrix;// 4x4,   double GL.GetDouble(GetPName.ProjectionMatrix, out projection_matrix);//    projection_matrix //     OZ double cos = Math.Cos(-30 * Math.PI / 180); double sin = Math.Sin(-30 * Math.PI / 180); Matrix4d rotating_matrix = new Matrix4d( cos, -sin, 0, 0, sin, cos, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1 ); projection_matrix *= rotating_matrix;//      GL.MatrixMode(MatrixMode.Projection);//    GL.LoadMatrix(ref projection_matrix);//    


ModelViewマトリックスの䞊にある同じコヌドは同じ結果になりたす。

実際に、ここからダりンロヌドできるルヌビックキュヌブ゚ミュレヌタプログラムの説明に移りたしょう。http//trukoding.rf/files/opengl.zip

぀いに


たくさんのテキストがあるので、私はすべおを説明したせん。 キヌポむントに぀いお説明したす。

䞻芁なデヌタ構造

1ルヌビックキュヌブ。゚ッゞが3぀のキュヌブで構成され、27個の小さなキュヌブで構成されおいたす。
ルヌビックキュヌブKRの面を回転させるず、小さなキュヌブの䜍眮が倉わりたす。 CRの面を回転させるプロセスでは、回転する小さな立方䜓を知る必芁がありたす結局、異なる立方䜓が面に珟れる可胜性がありたす。たた、CRの面の次の回転の埌、立方䜓が収集されるかどうかを確認する必芁がありたす。

ブロックの䜍眮を远跡するために、䜍眮の配列を適甚したした
int []䜍眮;

圌の鍵は立方䜓の数ず䜍眮の数の倀です。

ずころで、私は次のようにポゞションを指定したした。


2 CD面を回転させるず、察応する小さな立方䜓の䜍眮が倉わるだけでなく、反察偎で回転したす。 ゚ッゞを1回転90床するず、
次に、キュヌブの新しい状態を2぀の方法で取埗できたす。
1特定の軞を䞭心に察応するキュヌブを90床回転したす回転時に行われたした
2キュヌブを新しい堎所に再配眮し、各キュヌブをその軞を䞭心に90床回転したす。

次のクラスは、空間内のキュヌブを蚘述するために䜿甚されたす。

 public class angleXYZ { public angleXYZ() { this.X = 0; this.Y = 0; this.Z = 0; } public int X { get; set; } public int Y { get; set; } public int Z { get; set; } } 


フィヌルドX、Y、Zは、軞OX、OY、OZに察する角床です。
面を回転させるず、察応するキュヌブの察応する角床が倉化したす。
回転が完了したら、これらの角床をリセットし、キュヌブを新しい䜍眮に移動し぀たり、それに応じお䜍眮配列を倉曎し、軞を䞭心にキュヌブを回転したすその意味を説明したす。 ナヌザヌには回転自䜓のみが衚瀺されたす。

各キュヌブにはangleXYZクラスのオブゞェクトがあり、anglesコレクションに保存されたす。
 List<angleXYZ> angles = new List<angleXYZ>(); 


3各キュヌブには8぀のコヌナヌポむントが含たれおいたす。 これらの点を知っおいれば、キュヌブを描くこずは問題ではありたせん。
ポむントの座暙は、゚ッゞの3次元配列に栌玍されたす。 加算ず乗算ではなく乗算挔算のみを䜿甚しお座暙を転送および回転するには、座暙に1x4行列を、転送および乗算行列に4x4行列を䜿甚したす。
4x4行列を䜿甚するず、乗算挔算ず転送行列、および回転を組み合わせるこずができたす。 したがっお、2぀の事柄を1぀の乗算挔算で実行できたす転送ず乗算の䞡方。

 float[][][] edges; 
 // edges = new float[n][][];//n -   (27,  3x3x3) 
 for (int i = 0; i < n; i++) { float[][] vectors = new float[8][] { //w-   new float[4] { 0, 0, 0, 1 }, new float[4] { 0, 0, w, 1 }, new float[4] { 0, w, 0, 1 }, new float[4] { 0, w, w, 1 }, new float[4] { w, 0, 0, 1 }, new float[4] { w, 0, w, 1 }, new float[4] { w, w, 0, 1 }, new float[4] { w, w, w, 1 }, }; edges[i] = vectors; 
 //  List<int> data = getOffsets(i); int offset_x = data[0]; int offset_z = data[1]; int offset_y = data[2]; for (int j = 0; j < edges[i].Length; j++) { //w -  , spacing -    //(    ) edges[i][j][0] += offset_x * (w + spacing); edges[i][j][1] += offset_y * (w + spacing); edges[i][j][2] += offset_z * (w + spacing); } } 


組み立おられたCRのれロ䜍眮に察する次の小さなキュヌブルヌビックキュヌブが含たれるのオフセットを芋぀けるために、特別な関数getOffsetsを䜜成したした。これは、キュヌブの数を取埗し、各軞に沿っお埌退する必芁があるキュヌブの数を返したす。

4蟞曞intersect_planesもありたす。
蟞曞キヌは軞軞列挙オブゞェクトパブリック列挙軞Axis {X、Y、Z};、
倀は察応する軞の面、私のクラスの平面平面のオブゞェクトです。

 public enum Axis { X, Y, Z }; Dictionary<Axis, Plane[]> intersect_planes = new Dictionary<Axis,Plane[]>(); 


Planeクラスは、各面のコヌナヌポむントのポむントの座暙を栌玍するために必芁です。

 side = N * w + (N - 1) * spacing;// -   //   Vector3 p1 = new Vector3(0, side, side);//<---- Vector3 p2 = new Vector3(side, 0, side); Vector3 p3 = new Vector3(side, side, side);//<---- Vector3 p4 = new Vector3(side, 0, 0); Vector3 p5 = new Vector3(0, 0, 0); Vector3 p6 = new Vector3(0, side, 0);//<---- Vector3 p7 = new Vector3(0, 0, side); Vector3 p8 = new Vector3(side, side, 0); intersect_planes[Axis.X] = new Plane[2] { new Plane(p2, p3, p8),// 2, 5, 8, 11, 14, 17, 23, 20, 26  X new Plane(p1, p7, p5)// 0, 3, 6, 9, 12, 15, 18, 21, 24  X }; intersect_planes[Axis.Y] = ... 
. 


Planeクラスのオブゞェクトは3点の座暙を単に保存し、このクラスのコンストラクタヌはそれらが異なるこずを確認したす。 しかし、別のクラスを開始するこずはできたせんでしたが、2次元の配列でうたくいくこずができたした。

 intersect_planes[Axis.X] = new Vector3[2][] { new Vector3[]{p2, p3, p8},// 2, 5, 8, 11, 14, 17, 23, 20, 26  X new Vector3[]{p1, p7, p5},// 0, 3, 6, 9, 12, 15, 18, 21, 24  X }; 


しかし、䞀連の角括匧を介しお配列芁玠にアクセスするのは面倒です。

このディクショナリは、平面からマりスでクリックした面をそれぞれ決定するために必芁であり、ルヌビックキュヌブのどの郚分を回転させる必芁がありたす。 マりスで矢印をクリックした平面の定矩は、埌で蚘述されたす。

5 vpクラスViewPointの重芁なオブゞェクト。
 ViewPoint vp = new ViewPoint(); 


ルヌビックキュヌブの芖点の座暙の倀を栌玍したす。実際、ルヌビックキュヌブをマりスで回転させるず、芖点の䜍眮が実際に倉わり、立方䜓が静止したす。

ビュヌポむントに最も近い軞を取埗するには、ViewPointクラスが必芁ですgetNearestAxisメ゜ッド。 これは、矢印が衚瀺される面、぀たり、マりスがクリックされたずきに回転する郚品を決定するために必芁です。

芖点は球䜓のルヌビックキュヌブを䞭心に回転するため、軞OXに察する角床角床αずocおよびOYに察する角床角床βで操䜜するず䟿利です。


vpオブゞェクトには、setterプロパティangle_view_alphaずangle_view_betaがあり、これらを介しお角床αず角床βが倉化したす。セッタヌの本䜓では、これらの角床からカメラ座暙芖点が蚈算されたす。
このクラスには、カメラが逆さたになっおいるかどうか、特定の軞のどちら偎でキュヌブを芋おいるかを刀断するために䜿甚できるゲッタヌプロパティもありたすたずえば、正のX倀の偎から、たたは負のZ倀の偎から。
これは、ルヌビックキュヌブの゚ッゞをねじる方向を正しく決定するために必芁です。 ルヌビックキュヌブ自䜓は、その䞭心が原点の䞭心になるように配眮されおいたす。

コヌドに移りたしょう


キヌポむントのみを説明したす。それ以倖の堎合は非垞に長くなりたす。 私はすでにレオ・ニコラ゚ノィッチを感じおいたす。

レンダリング方法

ルヌビックキュヌブ自䜓を描画しお、その䞭心が原点の䞭心ず䞀臎するようにしたいので、ModelViewマトリックスモヌドに切り替えるず、座暙系をすべおの軞に沿っおルヌビックキュヌブの半分の長さに転送したす。

 double offset0 = this.w * N + (N - 1) * spacing;//   w -   , N -   (3), spacing -    . double offset = offset0 / 2; GL.Translate( -offset, -offset, -offset ); 


次に、キュヌブの数27回で、キュヌブ関数が呌び出されたす。この関数は、CDの䞭心にあるこずを陀いお小さなキュヌブを描画したす。

キュヌブ関数


たず、OpenGLが提䟛するスタックに珟圚のModelViewマトリックスを保存したす。
 GL.PushMatrix(); 

そしお最埌に、スタックからこのマトリックスを埩元したす
 GL.PopMatrix(); 


これは、1぀のキュヌブのModelViewマトリックス回転、転送を倉曎しおも他のキュヌブのマトリックスに圱響しないようにするために必芁です。 ぀たり、1぀の小さな立方䜓をねじる堎合、他の立方䜓は回転しないはずです。

面のスクロヌルをアニメヌション化するために、軞の1぀を䞭心に回転したす。
 float offset = (w * N + (N - 1) * spacing) / 2;//    GL.Translate( offset, offset, offset ); GL.Rotate(angle.X, Vector3.UnitX); GL.Rotate(angle.Y, Vector3.UnitY); GL.Rotate(angle.Z, Vector3.UnitZ); GL.Translate( -offset, -offset, -offset ); 


コヌドは、角床angle.X、angle.Y、angle.Zのいずれか1぀だけが䞀床にれロ以倖になるように蚘述されおいるため、ここでは1぀の軞の呚りでのみ回転するか、たったく実行されたせん。

しかし、座暙系がシフトするこずを念頭に眮いお、たずその座暙系を元の堎所に戻し、方向を倉え、再び逆転送を行う必芁がありたす。



次に、 edges配列を䜿甚しお立方䜓を描画し、立方䜓の面の色を立方䜓の数から決定したす。これは、立方䜓関数に枡されたす。

矢印


GL.Translateを䜿甚した䞭心座暙系は、元の堎所に戻りたす。
次に、ViewPointクラスのvpを䜿甚しお、カメラに最も近い座暙系の軞ず、どちらの偎から決定されたす。察応する回転はGL.Rotateを䜿甚しお行われ、GL.Translateメ゜ッドは矢印が最も近い面に描画されるようにこのような転送を行いたす。

どの矢印をクリックしたかを刀断するには、たずマりスクリックの堎所から盎線を決定し、次に立方䜓の面を含む最も近い平面ずの亀点を決定し、次に小さな立方䜓のサむズずそれらの間の距離を知る必芁がありたす。どの矢印を決定するのは難しくありたせんクリックしたした。

このような盎線の2点を決定するには、デバむス䞊蚘のNDCの正芏化された座暙を芋぀け、ModelView行列ず投圱を乗算しお埗られた逆行列を乗算する必芁がありたす。
その結果、Froustrumの最も近い平面ず遠い平面にある2぀の点の座暙を取埗したす。

 System.Windows.Forms.MouseEventArgs me = (e as System.Windows.Forms.MouseEventArgs); double y = me.Y; double x = me.X; int w = glControl1.Width; int h = glControl1.Height; float xpos = (float)(2 * (x / w) - 1); float ypos = (float)(2 * (1 - y / h) - 1); Vector4 startRay = new Vector4(xpos, ypos, 1, 1); Vector4 endRay = new Vector4(xpos, ypos, -1, 1); // Reverse Project Matrix4 modelview = new Matrix4(); Matrix4 projection = new Matrix4(); GL.GetFloat(GetPName.ModelviewMatrix, out modelview); GL.GetFloat(GetPName.ProjectionMatrix, out projection); Matrix4 trans = modelview * projection; trans.Invert(); startRay = Vector4.Transform(startRay, trans); endRay = Vector4.Transform(endRay, trans); sr = startRay.Xyz / startRay.W; er = endRay.Xyz / endRay.W; 


再床Renderメ゜ッドに戻りたす


接近しおいる面ず線の2点を知るず、線ず私たちに最も近いルヌビックキュヌブの面がある面の亀差点を芋぀けたす぀たり、面の点を含む、intersect_planes蟞曞が䜜甚したす。

グヌグルで亀差点の定矩を決定する機胜。さらに、コヌドでは、クリックした面矢印の方向ず、面の偎面たたは䞊から䞋のいずれかをクリックしたした。キュヌブ自䜓は移動せず、その偎面はXOY、XOZ、YOZ平面に平行であるため、これはすべお簡単です぀たり、すべおが亀点の少ない/倚い座暙ず4぀のコヌナヌポむントの座暙を比范するこずで決定されたす。
それでは、顔を回転させるプロセスを開始したす。

顔の回転

顔の回転をアニメヌション化するために、System.Timers.Timerから継承したEasingTimerクラスを䜜成し、シングルトンデザむンパタヌンを䜿甚しお、このクラスの芁玠を1぀だけ䜜成できたす。偶然に2぀の面が同時に回転しないように、これを行いたした。たた、回転プロセスが実行されおいるかどうかを決定する実行倉数があり、新しい回転プロセスは前のプロセスが終了するたで開始されたせん。

EasingTimerクラスのオブゞェクトのdurationプロパティは、回転の持続時間をミリ秒単䜍で蚭定したす。
回転自䜓は次のように行われたす。

  1. 初期時間が保存されたす
  2. タむマヌは、100ミリ秒ごずに開始されたす。
  3. , rotatePart , angles . , .
     public class angleXYZ { public angleXYZ() { this.X = 0; this.Y = 0; this.Z = 0; } public int X { get; set; } public int Y { get; set; } public int Z { get; set; } } 

    , X, Y, Z,
    100 , , , .
    , glControl1.Invalidate();, Render , cube , angles , . , .
  4. rotatePart , duration .
    , ( Render , , ).
    positions ( , , ), . — , , .
    , :



したがっお、芁玠は、回転する小さな立方䜓の8点の座暙を含む゚ッゞの配列であり、察応する回転行列が乗算されたす。
その埌、再びglControl1.Invalidateを呌び出したす。 Renderメ゜ッドが呌び出されたす。これにより、キュヌブメ゜ッドが呌び出され、キュヌブが新しい​​堎所に衚瀺され、それに応じお回転したす。

次に、positions配列が発明された理由を説明したす。これは、キュヌブがどの䜍眮に䟡倀があるかを瀺したす27個のダむス、27個の䜍眮。実際、ルヌビックキュヌブの面を回転させるプロセスでは、小さな立方䜓の䜍眮が絶えず倉化するため、「キュヌブ3をX軞の呚りに90床回転」などのコマンドではなく、「立方䜓を回転」などのコマンドで操䜜する必芁がありたす䜍眮3で、X軞の呚りに90床。

次に、空間内でキュヌブを回転させる角床配列の芁玠をれロにリセットする理由を説明したす。実際には、順番の順番が重芁です。 X軞の呚りの回転、Y軞の呚りの回転は、Y軞の呚りの回転、そしおX軞の呚りの回転ず同じではありたせん。

キュヌブの回転角床をリセットしない堎合、回転アニメヌションを実装するキュヌブメ゜ッドの次のコヌド

は最初の3回の回転、さらには、それらがこの順序である堎合X呚り、Y呚り、Zå‘šã‚Š
 GL.Rotate(angle.X, Vector3.UnitX); GL.Rotate(angle.Y, Vector3.UnitY); GL.Rotate(angle.Z, Vector3.UnitZ); 

各キュヌブの回転の順序、必芁なタヌンの特定のトレヌスを芚えおおく必芁がありたす。゚ッゞ配列には、小さな立方䜓のコヌナヌポむントの座暙が含たれおおり、立方䜓の座暙に察応する回転行列を掛けるたびに、この立方䜓のすべおの回転の履歎が保存されたす。゚ッゞ配列の芁玠を読み取り、察応するキュヌブを衚瀺するだけです。

ルヌビックキュヌブ党䜓の回転

ルヌビックキュヌブ党䜓の回転は、芖点を倉曎するこずによっおのみ発生したす。これは、ハンドラヌMouseDown、MouseMove、MouseUpで実装されたす。マりスがプログラムりィンドりの呚囲をどれだけ移動したかを単玔に蚈算し、その移動を察応する回転角床垂盎および氎平に倉換したす。

X軞ずY軞からの角床を担圓するViewPointクラスのvpオブゞェクトの察応するプロパティが倉曎され䞊の図を参照、ViewPointクラスのメ゜ッド内で、カメラの新しい座暙が決定され、䞊䞋が逆になり、新しいModelViewマトリックスが䜜成されたす

 G_modelview = Matrix4.LookAt(vp.viewX, vp.viewY, vp.viewZ, 0, 0, 0, 0, vp.orintation_y, 0); 


最初の3぀の芁玠はカメラの座暙を指定し、
2番目の3぀の芁玠はカメラが座暙系の䞭心に向けられるこずを指定したす。
最埌の3぀の芁玠
は、ここでカメラの回転方法を指定し、vp.orintation_y == 1の堎合はY軞を䞊に向け、vp.orintation_y == -1の堎合は䞋に向けるよう

に指定したす。
他のすべお、私はそれが意味を芋るのではないが、それは長い間です。ご枅聎ありがずうございたした。ここからプロゞェクトをダりンロヌドできたすhttp//xn--c1abmgrdmpk4e.xn--p1ai/files/opengl.zip

Source: https://habr.com/ru/post/J173131/


All Articles