如何在 OpenTK 中为 3D 立方体获取正确的名称堆栈?

How to get the correct name stack in OpenTK for a 3D cube?

我正在尝试在 OpenTK 中绘制一个 3D 立方体(6 个面),然后 select 使用右键单击所需的面。我正在使用 select 函数,并为每张脸分配名称(即 1-6)。但是,当我 运行 我的代码时,即使我右键单击其他面孔,我也无法获得正确的名称堆栈。我刚刚得到同名堆栈(5)。

我不确定 select 函数/ GluPickMatrix 有什么问题。

你知道为什么在这种情况下我无法获得正确的名称堆栈吗?如何获取正确的名称栈?

这是我的代码:

using System;
using OpenTK;
using OpenTK.Graphics.OpenGL;
using OpenTK.Input;

namespace OpenTK3D
{
    public class Game3D
    {
        private GameWindow window;
        private float zoom;
        private bool hasRotationStarted;
        private int startX;
        private int startY;
        private float xRotAngle;
        private float yRotAngle;

        private bool hasPanningStarted;
        private float xTrans;
        private float yTrans;
        private int BUFSIZE = 512;

        public Game3D(GameWindow wd)
        {
            this.window = wd;
            start();
        }

        public void start()
        {
            window.Load += loaded;
            window.Resize += resize;           
            window.RenderFrame += renderFrame;
            window.MouseDown += mouseLeftPress;
            window.MouseUp += mouseRelease;
            window.MouseMove += mouseDragEvent;
            window.MouseWheel += MouseWheelHandler;
            window.MouseDown += wheelPressEvent;
            window.MouseUp += wheelReleaseEvent;
            window.MouseMove += wheelDragEvent;
            window.MouseDown += select;
            window.Run(1.0 / 60.0);
        }

        public void loaded(object o, EventArgs e)
        {
            GL.ClearColor(0.0f, 0.0f, 0.0f, 0.0f);
            GL.Enable(EnableCap.DepthTest);
        }

        public void renderFrame(object o, EventArgs e)
        {
            GL.LoadIdentity();
            GL.Clear(ClearBufferMask.ColorBufferBit | ClearBufferMask.DepthBufferBit);
            GL.Translate(xTrans, yTrans, zoom*3);
            GL.Rotate(xRotAngle, 1.0, 0, 0);
            GL.Rotate(yRotAngle, 0, 1, 0);

            GL.Begin(BeginMode.Quads);
            GL.Color3(1.0, 1.0, 0.0);
            GL.Vertex3(-10.0, 10.0, 10.0);
            GL.Vertex3(-10.0, 10.0, -10.0);
            GL.Vertex3(-10.0, -10.0, -10.0);
            GL.Vertex3(-10.0, -10.0, 10.0);
            GL.End();

            GL.Begin(BeginMode.Quads);
            GL.Color3(1.0, 0.0, 1.0);
            GL.Vertex3(10.0, 10.0, 10.0);
            GL.Vertex3(10.0, 10.0, -10.0);
            GL.Vertex3(10.0, -10.0, -10.0);
            GL.Vertex3(10.0, -10.0, 10.0);
            GL.End();

            GL.Begin(BeginMode.Quads);
            GL.Color3(0.0, 1.0, 1.0);
            GL.Vertex3(10.0, -10.0, 10.0);
            GL.Vertex3(10.0, -10.0, -10.0);
            GL.Vertex3(-10.0, -10.0, -10.0);
            GL.Vertex3(-10.0, -10.0, 10.0);
            GL.End();

            GL.Begin(BeginMode.Quads);
            GL.Color3(1.0, 0.0, 0.0);
            GL.Vertex3(10.0, 10.0, 10.0);
            GL.Vertex3(10.0, 10.0, -10.0);
            GL.Vertex3(-10.0, 10.0, -10.0);
            GL.Vertex3(-10.0, 10.0, 10.0);
            GL.End();

            GL.Begin(BeginMode.Quads);
            GL.Color3(0.0, 1.0, 0.0);
            GL.Vertex3(10.0, 10.0, -10.0);
            GL.Vertex3(10.0, -10.0, -10.0);
            GL.Vertex3(-10.0, -10.0, -10.0);
            GL.Vertex3(-10.0, 10.0, -10.0);
            GL.End();

            GL.Begin(BeginMode.Quads);
            GL.Color3(0.0, 0.0, 1.0);
            GL.Vertex3(10.0, 10.0, 10.0);
            GL.Vertex3(10.0, -10.0, 10.0);
            GL.Vertex3(-10.0, -10.0, 10.0);
            GL.Vertex3(-10.0, 10.0, 10.0);
            GL.End();

            window.SwapBuffers();
        }

        public void resize(object o, EventArgs e)
        {
            GL.Viewport(0, 0, window.Width, window.Height);
            GL.MatrixMode(MatrixMode.Projection);
            GL.LoadIdentity();
            // the fov must be radian
            var matrix = Matrix4.CreatePerspectiveFieldOfView(45.0f*(MathHelper.Pi)/180, window.Width / window.Height, 1.0f, 100.0f);
            GL.LoadMatrix(ref matrix);
            GL.MatrixMode(MatrixMode.Modelview);
        }

        private void processHits(int hits, int[] buffer)
        {
            Console.WriteLine("hit: {0}", hits);

            if (hits > 0)
            {
                int choose = buffer[3];
                int depth = buffer[1];
                for (int i = 0; i < hits; i++)
                {
                    if (buffer[i * 4 + 1] < depth)
                    {
                        choose = buffer[i * 4 + 3];
                        depth = buffer[i * 4 + 1];
                    }
                }
                Console.WriteLine("choosen: {0}", choose);
            }
        }

        private void GluPickMatrix(double x, double y, double deltax, double deltay, int[] viewport)
        {
            if (deltax <= 0 || deltay <= 0)
            {
                return;
            }

            GL.Translate((viewport[2] - 2 * (x - viewport[0])) / deltax, (viewport[3] - 2 * (y - viewport[1])) / deltay, 0);
            GL.Scale(viewport[2] / deltax, viewport[3] / deltay, 1.0);
        }

        public void select(object o, MouseEventArgs e)
        {
            var mouse = Mouse.GetState();
            if (mouse[MouseButton.Right])
            {
                var buffer = new int[BUFSIZE];
                var viewPort = new int[4];
                int hits;
                GL.GetInteger(GetPName.Viewport, viewPort);
                GL.SelectBuffer(BUFSIZE, buffer);
                GL.RenderMode(RenderingMode.Select);

                GL.InitNames();
                GL.PushName(0);

                GL.MatrixMode(MatrixMode.Projection);
                GL.PushMatrix();
                GL.LoadIdentity();
   
                GluPickMatrix(e.Mouse.X, viewPort[3] - e.Mouse.Y, 5.0, 5.0, viewPort);

                var matrix = Matrix4.CreatePerspectiveFieldOfView(45.0f * (MathHelper.Pi) / 180, window.Width / window.Height, 1.0f, 100.0f);
                GL.LoadMatrix(ref matrix);

                GL.MatrixMode(MatrixMode.Modelview);
                GL.LoadIdentity();

                GL.LoadName(1);
                GL.Begin(BeginMode.Quads);
                GL.Color3(1.0, 1.0, 0.0);
                GL.Vertex3(-10.0, 10.0, 10.0);
                GL.Vertex3(-10.0, 10.0, -10.0);
                GL.Vertex3(-10.0, -10.0, -10.0);
                GL.Vertex3(-10.0, -10.0, 10.0);
                GL.End();

                GL.LoadName(2);
                GL.Begin(BeginMode.Quads);
                GL.Color3(1.0, 0.0, 1.0);
                GL.Vertex3(10.0, 10.0, 10.0);
                GL.Vertex3(10.0, 10.0, -10.0);
                GL.Vertex3(10.0, -10.0, -10.0);
                GL.Vertex3(10.0, -10.0, 10.0);
                GL.End();

                GL.LoadName(3);
                GL.Begin(BeginMode.Quads);
                GL.Color3(0.0, 1.0, 1.0);
                GL.Vertex3(10.0, -10.0, 10.0);
                GL.Vertex3(10.0, -10.0, -10.0);
                GL.Vertex3(-10.0, -10.0, -10.0);
                GL.Vertex3(-10.0, -10.0, 10.0);
                GL.End();

                GL.LoadName(4);
                GL.Begin(BeginMode.Quads);
                GL.Color3(1.0, 0.0, 0.0);
                GL.Vertex3(10.0, 10.0, 10.0);
                GL.Vertex3(10.0, 10.0, -10.0);
                GL.Vertex3(-10.0, 10.0, -10.0);
                GL.Vertex3(-10.0, 10.0, 10.0);
                GL.End();

                GL.LoadName(5);
                GL.Begin(BeginMode.Quads);
                GL.Color3(0.0, 1.0, 0.0);
                GL.Vertex3(10.0, 10.0, -10.0);
                GL.Vertex3(10.0, -10.0, -10.0);
                GL.Vertex3(-10.0, -10.0, -10.0);
                GL.Vertex3(-10.0, 10.0, -10.0);
                GL.End();

                GL.LoadName(6);
                GL.Begin(BeginMode.Quads);
                GL.Color3(0.0, 0.0, 1.0);
                GL.Vertex3(10.0, 10.0, 10.0);
                GL.Vertex3(10.0, -10.0, 10.0);
                GL.Vertex3(-10.0, -10.0, 10.0);
                GL.Vertex3(-10.0, 10.0, 10.0);
                GL.End();

                GL.MatrixMode(MatrixMode.Projection);
                GL.PopMatrix();
                GL.MatrixMode(MatrixMode.Modelview);

                GL.Flush();
                hits = GL.RenderMode(RenderingMode.Render);
                processHits(hits, buffer);
            }
        }

        public void mouseLeftPress(object sender, MouseEventArgs e)
        {
            if (e.Mouse.LeftButton == ButtonState.Pressed)
            {
                hasRotationStarted = true;
                startX = e.Mouse.X;
                startY = e.Mouse.Y;
            }
        }

        public void mouseRelease(object sender, MouseEventArgs e)
        {
            if (e.Mouse.LeftButton == ButtonState.Released)
            {
                hasRotationStarted = false;
            }
        }

        public void mouseDragEvent(object sender, MouseEventArgs e)
        {
            if (hasRotationStarted == true && e.Mouse.X != e.Mouse.Y)
            {
                xRotAngle = xRotAngle + (e.Mouse.Y - startY);
                yRotAngle = yRotAngle + (e.Mouse.X - startX);
                startX = e.Mouse.X;
                startY = e.Mouse.Y;
            }
        }

        public void MouseWheelHandler(object sender, MouseWheelEventArgs e)
        {
            var xBoundary = 10.0;

            if (e.Delta > 0)
            {
                zoom += 0.1f * (float)xBoundary;
            }

            if (e.Delta < 0)
            {
                zoom -= 0.1f * (float)xBoundary;
            }
        }

        public void wheelPressEvent(object sender, MouseEventArgs e)
        {
            if (e.Mouse.MiddleButton == ButtonState.Pressed)
            {
                hasPanningStarted = true;
                startX = e.Mouse.X;
                startY = e.Mouse.Y;
            }
        }

        public void wheelReleaseEvent(object sender, MouseEventArgs e)
        {
            if (e.Mouse.MiddleButton == ButtonState.Released)
            {
                hasPanningStarted = false;
            }
        }

        public void wheelDragEvent(object sender, MouseEventArgs e)
        {
            if (hasPanningStarted == true)
            {
                xTrans = xTrans + 2 * (e.Mouse.X - startX);
                yTrans = yTrans - 2 * (e.Mouse.Y - startY);
                startX = e.Mouse.X;
                startY = e.Mouse.Y;
            }
        }
    }
}

这是主要功能:

using System;
using OpenTK;

namespace OpenTK3D
{
    class Program
    {
        static void Main(string[] args)
        {
            var window = new GameWindow(500, 500);
            var gm = new Game3D(window);
            Console.WriteLine();
            Console.WriteLine();
            Console.WriteLine("Press enter to finish...");
            Console.ReadLine();

        }
    }
}

这是我在 运行 执行此代码后得到的输出:

有 2 个问题:

  • GL.LoadMatrix loads an matrix and overwrites the existing matrix. If you want to concatenate a matrix with the current matrix, you need to use GL.MultMatrix

  • 投影矩阵设置不够。您还必须设置模型视图矩阵。由于设置了模型视图矩阵,所以在GL.MatrixMode(MatrixMode.Modelview).

    之后去掉GL.LoadIdentity就可以了
# [...]

GL.MatrixMode(MatrixMode.Projection);
GL.PushMatrix();
GL.LoadIdentity();

GluPickMatrix(e.Mouse.X, viewPort[3] - e.Mouse.Y, 5.0, 5.0, viewPort);

var matrix = Matrix4.CreatePerspectiveFieldOfView(45.0f * (MathHelper.Pi) / 180, window.Width / window.Height, 1.0f, 100.0f);
GL.MultMatrix(ref matrix); # <-- GL.MultMatrix insterad of GL.LoadMatrix

GL.MatrixMode(MatrixMode.Modelview);
# GL.LoadIdentity();         <-- delete this

# [...]