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

问题描述

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

我不确定选择函数/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);
            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);
            GL.Rotate(yRotAngle,1,0);

            GL.Begin(BeginMode.Quads);
            GL.Color3(1.0,0.0);
            GL.Vertex3(-10.0,10.0,10.0);
            GL.Vertex3(-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);
            GL.Vertex3(10.0,-10.0);
            GL.Vertex3(10.0,10.0);
            GL.End();

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

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

            window.SwapBuffers();
        }

        public void resize(object o,EventArgs e)
        {
            GL.Viewport(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,viewPort);

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

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

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

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

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

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

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

                GL.LoadName(6);
                GL.Begin(BeginMode.Quads);
                GL.Color3(0.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();

        }
    }
}

这是我运行这段代码后得到的输出

The output I got after running the code

解决方法

有两个问题:

  • GL.LoadMatrix 加载矩阵并覆盖现有矩阵。如果要将矩阵与当前矩阵连接起来,则需要使用 GL.MultMatrix

  • 设置投影矩阵是不够的。您还必须设置模型视图矩阵。由于模型视图矩阵已经设置好,去掉GL.LoadIdentity后的GL.MatrixMode(MatrixMode.Modelview)就足够了。

# [...]

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

GluPickMatrix(e.Mouse.X,viewPort[3] - e.Mouse.Y,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

# [...]