我想在我的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
};
}
其中mouseX
和mouseY
是全局变量。
在我的更新方法中,我执行以下操作:
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
但是相机正在口吃并且大跳跃。此外,有时鼠标似乎是倒置的。
有什么建议吗?
答案 0 :(得分:3)
以下是我的FPS风格相机的基础代码片段。如您所见,我正在计算鼠标位置的增量,屏幕中心沿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();
}
}
答案 1 :(得分:1)
我现在正在研究这样的事情。
想法是每次循环时将摄像机位置重置到中心,如下所示:
glfwSetCursorPos(YOUR WINDOW HANDLE, YOUR_WINDOW_WIDTH / 2, YOU_WINDOW_HEIGHT / 2);
这样,您的delta总是与屏幕中心的距离进行比较。所以这应该解决你的问题。
编辑(将上面的答案留给后人):
好的,所以我也遇到了和你一样的问题。无视我上面所说的不太正确。
我最终实现了类似于你正在做的事情,但收到了相同的结果,其中delta太大,因此相机移动速度太快。
我所做的是解决这个问题,实际上是对每一帧进行鼠标轮询并计算" delta"通过以下操作:
deltaX = currentMousePos.x - lastFrameMousePos.x
deltaY = currentMousePos.y - lastFrameMousePos.y
现在delta应该足够小并且每帧也要计算。
然后,为了计算我的旋转角度,我使用:
deltaPos.getY() * sensitivity * Time.getDelta()
其中Time.getDelta()是每个帧循环之间的时间差异。我最终得到的是一台相当流畅的相机,虽然还不完美。
如果您想了解更多信息,请告诉我。
编辑2:
在LWJGL论坛上查看我的类似问题Link的链接。我确实现在很好地工作了。我将使用代码发布单独的答案。
答案 2 :(得分:0)
获取鼠标位置的另一种方法是扩展GLFWCursorPosCallback类,然后只使用它在其调用方法中提供的x和y值。
有关这方面的好教程可以在这里找到: