Laden...

OpenGL(OpenTK) 3d Koordinaten zu Client Koordinaten vise versa

Erstellt von Ayke vor 12 Jahren Letzter Beitrag vor 11 Jahren 3.364 Views
Ayke Themenstarter:in
643 Beiträge seit 2006
vor 12 Jahren
OpenGL(OpenTK) 3d Koordinaten zu Client Koordinaten vise versa

Hallo,

ich möchte Schaltflächen in OpenGL(OpenTK) erstellen.
Jetzt muss ich herausfinden, wann sich die Maus über meiner Schalfläche befindet.
Dazu bräuchte ich die 2d Client Koordinaten meines 3d Vectors. Wie ist es möglich ?
Auch anderes rum wäre es sehr Praktisch also 2d Client zu 3d. Würde mich über eure Hilfe sehr freuen.

5.658 Beiträge seit 2006
vor 12 Jahren

Hi Ayke,

dir ist sicherlich bewußt, daß es keine eindeutige Umrechnung von 2D- in 3D-Koordinaten geben kann. Ein (Maus-)Punkt in 2D stellt immer einen Strahl in 3D dar (bestehend aus 2 Vektoren). Du kannst dir also eine Mausposition als Strahl darstellen lassen, ausgehend von der Kameraposition. Damit kannst du dann gegen Bounding Volumes prüfen, ob der Strahl dein Objekt trifft oder nicht.

Andersherum ist die Konvertierung einfacher. Mit Hilfe der ViewProjectionMatrix kannst du einfach einen 3D-Punkt in 2D umrechnen.

Christian

Weeks of programming can save you hours of planning

Ayke Themenstarter:in
643 Beiträge seit 2006
vor 12 Jahren

Hallo MrSparkle,
danke für den Leitfaden. Hilft mir bestimmt weiter.

Ayke Themenstarter:in
643 Beiträge seit 2006
vor 12 Jahren
        public static Vector3 ClientToWorld(int x, int y)
        {
            var viewport = new int[4];
            Matrix4 modelviewMatrix, projectionMatrix;
            GL.GetFloat(GetPName.ModelviewMatrix, out modelviewMatrix);
            GL.GetFloat(GetPName.ProjectionMatrix, out projectionMatrix);
            GL.GetInteger(GetPName.Viewport, viewport);

            var t = new float[1];
            GL.ReadPixels(x, Properties.ViewProperties.ViewPortHeight - y, 1, 1, PixelFormat.DepthComponent, PixelType.Float, t);

            return UnProject(new Vector3(x, viewport[3] - y, t[0]), modelviewMatrix, projectionMatrix, viewport);
        }

        private static Vector3 UnProject(Vector3 screen, Matrix4 view, Matrix4 projection, IList<int> viewPort)
        {
            var pos = new Vector4();

            pos.X = (screen.X - viewPort[0]) / viewPort[2] * 2.0f - 1.0f;
            pos.Y = (screen.Y - viewPort[1]) / viewPort[3] * 2.0f - 1.0f;
            pos.Z = screen.Z * 2.0f - 1.0f;
            pos.W = 1.0f;

            var pos2 = Vector4.Transform(pos, Matrix4.Invert(Matrix4.Mult(view, projection)));
            var posOut = new Vector3(pos2.X, pos2.Y, pos2.Z);

            return posOut / pos2.W;
        }
Ayke Themenstarter:in
643 Beiträge seit 2006
vor 11 Jahren

Performance fresser... kanns doch nicht sein. Wie machen das den die Professionellen Spiele- Entwickler.

5.658 Beiträge seit 2006
vor 11 Jahren

Hi Ayke,

"professionelle Spieleentwickler" kochen auch nur mit Wasser. Ich weiß zwar nicht, was die OpenGL-Methoden (GetInt, GetFloat) genau machen, aber ich habe auf die Schnelle folgenden Code gefunden, der ohne diese Methoden auskommt: GluProject and gluUnProject code.

Am aufwändigsten ist wahrscheinlich die Berechnung der Invertierten Matrix, die könnte man aber zwischenspeichern, und man braucht sie nur zu aktualisieren, wenn sich die Kamera geändert hat.

Christian

Weeks of programming can save you hours of planning

Gelöschter Account
vor 11 Jahren

Hallo Ayke

das ist zwar nicht OTK, aber in der Mathematik sehr ähnlich aufgebaut. Ich verwende die Methode um Lichtquellen in Echtzeit verschieben. Du solltest auf alle fälle mal darüber nachdenken, ob du nicht deine Matritzen selber berechnen willst. Das ist nicht so schwer wies sich anhört und dürfte mit OTK auch nicht komplizierter sein.


//From Device.cs
public static void PointToScreen(ref Vector3 point, ref Matrix4 modelview, ref Matrix4 projection)
        {
            int[] viewport = new int[4];

            unsafe
            {
                fixed (int* ptr = viewport)
                {
                    gCore.GetIntegerv(GetPName.Viewport, ptr);
                }
            }

            Matrix4.Project(ref point, ref modelview, ref projection, viewport, out point);
            point.Y = (float)viewport[3] - point.Y;
        }

//From Matrix3.cs
public static bool Project(ref Vector3 world, ref Matrix4 modelviewMatrix, ref Matrix4 projectionMatrix, int[] viewport, out Vector3 screen)
        {
            Vector4 _in = new Vector4(world, 1.0f);
            Vector4 _out = new Vector4();

            //__gluMultMatrixVecd(modelMatrix, in, out);
            //__gluMultMatrixVecd(projMatrix, out, in);
            Vector4.Transform(ref _in, ref modelviewMatrix, out _out);
            Vector4.Transform(ref _out, ref projectionMatrix, out _in);

            if (_in.W == 0.0f)
            {
                screen = Vector3.Zero;
                return false;
            }

            _in.X /= _in.W;
            _in.Y /= _in.W;
            _in.Z /= _in.W;

            /* Map x, y and z to range 0-1 */
            _in.X = _in.X * 0.5f + 0.5f;
            _in.Y = _in.Y * 0.5f + 0.5f;
            _in.Z = _in.Z * 0.5f + 0.5f;

            /* Map x,y to viewport */
            _in.X = _in.X * viewport[2] + viewport[0];
            _in.Y = _in.Y * viewport[3] + viewport[1];

            screen = new Vector3(_in);
            return true;
        }

Die Performance liegt so zwischen 40 und 50 Microsekunden auf meinem 6 Jahre alten Rechner mit 2 GHz DualCore, sollte also ausreichend sein.

Ayke Themenstarter:in
643 Beiträge seit 2006
vor 11 Jahren

danke MrSparkle, Shaarigan vorallem für die Zeit damit habe ich mal eine Referenz.