【问题标题】:LWJGL 3 Mouse MovementLWJGL 3 鼠标移动
【发布时间】:2015-02-01 14:22:43
【问题描述】:

我想在我的 lwjgl3 Java 应用程序中实现 FPS 风格的鼠标外观,但由于没有任何 Mouse.getDX()Mouse.getDY(),我正在寻找使用 glfw 绑定的不同方法。 我编写了这个在我的update() 方法中调用的方法:

public double[] pollMouseDelta() {
    DoubleBuffer x = BufferUtils.createDoubleBuffer(1);
    DoubleBuffer y = BufferUtils.createDoubleBuffer(1);

    glfwGetCursorPos(WINDOW, x, y);
    x.rewind();
    y.rewind();

    double tempX = mouseX;
    double tempY = mouseY;

    mouseX = x.get();
    mouseY = y.get();

    return new double[] {
           mouseX - tempX,
           mouseY - tempY
    };
}

其中mouseXmouseY 是全局变量。

在我的更新方法中,我执行以下操作:

double[] mouseDelta = pollMouseDelta();

camera.rotate(Camera.Direction.LEFT_RIGHT, (float) (0.2f * -mouseDelta[0]));
camera.rotate(Camera.Direction.UP_DOWN, (float) (0.2f * mouseDelta[1]));

camera.update();

我还将GLFW_CURSOR 模式设置为GLFW_CURSOR_DISABLED
但是相机会卡顿并且跳跃很大。此外,有时鼠标似乎是倒置的。

有什么建议吗?

【问题讨论】:

  • 你把我弄糊涂了。 Mouse 是一个 LWJGL 类,它肯定有 Mouse.getDX/DY()
  • 他们删除了所有带有静态方法的类,这些类必须对窗口系统做一些事情,因为他们现在使用 glfw 来支持多个窗口

标签: java opengl lwjgl glfw


【解决方案1】:

我现在正在做类似的事情。

这个想法是在每次循环时将相机位置重置为中心,如下所示:

glfwSetCursorPos(YOUR WINDOW HANDLE, YOUR_WINDOW_WIDTH / 2, YOU_WINDOW_HEIGHT / 2);

这样,您的增量始终与与屏幕中心的距离相比较。所以这应该可以解决你的跳跃问题。

编辑(将我的答案留给后代):

好的,我也遇到了和你一样的问题。楼上说的不太对。

我最终实现了与您正在做的类似的事情,但在增量太大导致相机移动太快的情况下收到了相同的结果。

我为解决这个问题所做的实际上是让鼠标轮询每一帧并使用以下操作计算“增量”:

deltaX = currentMousePos.x - lastFrameMousePos.x
deltaY = currentMousePos.y - lastFrameMousePos.y

现在增量应该足够小,并且还按帧计算。

然后我用这个来计算我的旋转角度:

deltaPos.getY() * sensitivity * Time.getDelta()

其中 Time.getDelta() 是每个帧循环之间的时间差异。我最终得到的是一个相当流畅的相机,虽然还不完美。

如果您需要更多信息,请告诉我。

编辑 2:

查看此链接,该链接在 LWJGL 论坛上有我的类似问题 Link。我现在确实有这个工作得很好。我会用代码发布一个单独的答案。

【讨论】:

  • 感谢您的回复。我尝试了这个并得到一个奇怪的控制台输出:鼠标在游戏开始时跳跃很大,每个 X 和 Y 坐标的鼠标增量超过 800。如果我重新启动游戏,有时鼠标似乎会倒转。
  • 查看我的答案的编辑,如果这有帮助,请告诉我
  • 感谢您的快速回复。你是用回调还是直接拉光标位置?
  • 我有两种风格。查看我的第二次更新。另外,您是否尝试过仅乘以较低的灵敏度。我使用的灵敏度是浮点灵敏度 = 0.0005f;
【解决方案2】:

这是我的 FPS 风格相机的基础代码 sn-p。如您所见,我正在计算鼠标位置与屏幕中心沿 X 和 Y 轴的增量。

您可以在我的 System.out.println 调用中看到我正在为增量计算的值以及它应该按这个量旋转的事实。然后,我将这些值乘以我的灵敏度,并将相机旋转这个量。

import org.lwjgl.BufferUtils;
import org.lwjgl.Sys;
import org.lwjgl.glfw.*;
import org.lwjgl.opengl.*;

import java.nio.ByteBuffer;
import java.nio.DoubleBuffer;
import java.nio.FloatBuffer;

import static org.lwjgl.glfw.Callbacks.*;
import static org.lwjgl.glfw.GLFW.*;
import static org.lwjgl.glfw.GLFW.glfwGetCursorPos;
import static org.lwjgl.opengl.GL11.*;
import static org.lwjgl.opengl.GL15.*;
import static org.lwjgl.opengl.GL20.*;
import static org.lwjgl.opengl.GL30.*;
import static org.lwjgl.system.MemoryUtil.*;

public class Main {

    // We need to strongly reference callback instances.
    private GLFWErrorCallback errorCallback;
    private GLFWKeyCallback   keyCallback;

    // The window handle
    private long window;

    public void run() {
        System.out.println("Hello LWJGL " + Sys.getVersion() + "!");

        try {
            init();
            loop();

            // Release window and window callbacks
            glfwDestroyWindow(window);
            keyCallback.release();
        } finally {
            // Terminate GLFW and release the GLFWerrorfun
            glfwTerminate();
            errorCallback.release();
        }
    }

    private void init() {
        // Setup an error callback. The default implementation
        // will print the error message in System.err.
        glfwSetErrorCallback(errorCallback = errorCallbackPrint(System.err));

        // Initialize GLFW. Most GLFW functions will not work before doing this.
        if ( glfwInit() != GL11.GL_TRUE )
            throw new IllegalStateException("Unable to initialize GLFW");

        // Configure our window
        glfwDefaultWindowHints(); // optional, the current window hints are already the default
        glfwWindowHint(GLFW_VISIBLE, GL_FALSE); // the window will stay hidden after creation
        glfwWindowHint(GLFW_RESIZABLE, GL_TRUE); // the window will be resizable
        glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
        glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
        glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
        glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE);


        int WIDTH = 800;
        int HEIGHT = 600;

        // Create the window
        window = glfwCreateWindow(WIDTH, HEIGHT, "Hello World!", NULL, NULL);
        if ( window == NULL )
            throw new RuntimeException("Failed to create the GLFW window");

        // Setup a key callback. It will be called every time a key is pressed, repeated or released.
        glfwSetKeyCallback(window, keyCallback = new GLFWKeyCallback() {
            @Override
            public void invoke(long window, int key, int scancode, int action, int mods) {
                if ( key == GLFW_KEY_ESCAPE && action == GLFW_RELEASE )
                    glfwSetWindowShouldClose(window, GL_TRUE); // We will detect this in our rendering loop
            }
        });

        // Get the resolution of the primary monitor
        ByteBuffer vidmode = glfwGetVideoMode(glfwGetPrimaryMonitor());
        // Center our window
        glfwSetWindowPos(
                window,
                (GLFWvidmode.width(vidmode) - WIDTH) / 2,
                (GLFWvidmode.height(vidmode) - HEIGHT) / 2
        );

        // Make the OpenGL context current
        glfwMakeContextCurrent(window);
        // Enable v-sync
        glfwSwapInterval(1);

        // Make the window visible
        glfwShowWindow(window);
    }

    private void loop() {
        // This line is critical for LWJGL's interoperation with GLFW's
        // This line is critical for LWJGL's interoperation with GLFW's
        // OpenGL context, or any context that is managed externally.
        // LWJGL detects the context that is current in the current thread,
        // creates the ContextCapabilities instance and makes the OpenGL
        // bindings available for use.
        GLContext.createFromCurrent();


        // Create a FloatBuffer to hold our vertex data
        FloatBuffer vertices = BufferUtils.createFloatBuffer(9);
        // Add vertices of the triangle
        vertices.put(new float[]
                {
                        0.0f,  0.5f,  0.0f,
                        0.5f, -0.5f,  0.0f,
                        -0.5f, -0.5f,  0.0f
                });
        // Rewind the vertices
        vertices.rewind();


        int vbo = glGenBuffers();
        int vao = glGenVertexArrays();

        glBindBuffer (GL_ARRAY_BUFFER, vbo);
        glBufferData (GL_ARRAY_BUFFER, vertices, GL_STATIC_DRAW);
        glBindVertexArray(vao);

        glEnableVertexAttribArray (0);
        glBindBuffer (GL_ARRAY_BUFFER, vbo);
        glVertexAttribPointer (0, 3, GL_FLOAT, false, 0, 0);

        final String vertex_shader =
                "#version 410\n" +
                        "in vec3 vp;\n" +
                        "void main () {\n" +
                        "  gl_Position = vec4 (vp, 1.0);\n" +
                        "}";

        final String frag_shader =
                "#version 400\n"    +
                        "out vec4 frag_colour;" +
                        "void main () {"         +
                        "  frag_colour = vec4 (0.5, 0.0, 0.5, 1.0);" +
                        "}";

        int shader_programme = glCreateProgram();


        int vertexShaderID = glCreateShader(GL_VERTEX_SHADER);
        glShaderSource(vertexShaderID, vertex_shader);
        glCompileShader (vertexShaderID);

        if(glGetShaderi(vertexShaderID, GL_COMPILE_STATUS) == 0){
            System.err.println(glGetShaderInfoLog(vertexShaderID, 1024));
            System.exit(1);
        }

        glAttachShader (shader_programme, vertexShaderID);

        int fragmentShaderID = glCreateShader(GL_FRAGMENT_SHADER);
        glShaderSource(fragmentShaderID, frag_shader);
        glCompileShader (fragmentShaderID);

        if(glGetShaderi(fragmentShaderID, GL_COMPILE_STATUS) == 0){
            System.err.println(glGetShaderInfoLog(fragmentShaderID, 1024));
            System.exit(1);
        }

        glAttachShader (shader_programme, fragmentShaderID);

        glLinkProgram (shader_programme);

        if(glGetProgrami(shader_programme, GL_LINK_STATUS) == 0){
            System.err.println(glGetProgramInfoLog(shader_programme, 1024));
            System.exit(1);
        }

        glValidateProgram(shader_programme);

        if(glGetProgrami(shader_programme, GL_VALIDATE_STATUS) == 0){
            System.err.println(glGetProgramInfoLog(shader_programme, 1024));
            System.exit(1);
        }

        boolean mouseLocked = false;
        double newX = 400;
        double newY = 300;

        double prevX = 0;
        double prevY = 0;

        boolean rotX = false;
        boolean rotY = false;

        glfwSetInputMode(window, GLFW_CURSOR, GLFW_CURSOR_DISABLED);


        while ( glfwWindowShouldClose(window) == GL_FALSE ) {

            if (glfwGetMouseButton(window, GLFW_MOUSE_BUTTON_1) == GLFW_PRESS) {
                glfwSetCursorPos(window, 800/2, 600/2);

                mouseLocked = true;
            }

            if (mouseLocked){
                DoubleBuffer x = BufferUtils.createDoubleBuffer(1);
                DoubleBuffer y = BufferUtils.createDoubleBuffer(1);

                glfwGetCursorPos(window, x, y);
                x.rewind();
                y.rewind();

                newX = x.get();
                newY = y.get();

                double deltaX = newX - 400;
                double deltaY = newY - 300;

                rotX = newX != prevX;
                rotY = newY != prevY;

                if(rotY) {
                    System.out.println("ROTATE Y AXIS: " + deltaY);

                }
                if(rotX) {
                    System.out.println("ROTATE X AXIS: " + deltaX);
                }

                prevX = newX;
                prevY = newY;


                System.out.println("Delta X = " + deltaX + " Delta Y = " + deltaY);

                glfwSetCursorPos(window, 800/2, 600/2);
            }

            // wipe the drawing surface clear
            glClear (GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
            glUseProgram (shader_programme);
            glBindVertexArray (vao);
            // draw points 0-3 from the currently bound VAO with current in-use shader
            glDrawArrays (GL_TRIANGLES, 0, 3);
            // update other events like input handling
            glfwPollEvents ();
            // put the stuff we've been drawing onto the display
            glfwSwapBuffers (window);

        }
    }

    public static void main(String[] args) {
        new Main().run();
    }

}

【讨论】:

  • 抱歉我没有回复,这周我很忙。无论如何,感谢您的快速回复。现在可以正常使用了!!
  • 我还有一个问题:你是如何用回调实现这个方法的?您只是设置 newX / newY 变量还是使用 Zeroni 在 LWJGL 论坛中建议的方法?我对此感兴趣,因为我也尝试过他的方法,口吃消失了,但我仍然看到一开始有很大的跳跃,鼠标有时会倒转。
【解决方案3】:

获取鼠标位置的另一种方法是扩展 GLFWCursorPosCallback 类,然后只使用它在其调用方法中提供的 x 和 y 值。

可以在这里找到一个很好的教程:

LWJGL 3 Mouse Position

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2023-03-31
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多