Описание
Я новичок в OpenGL и начал работать над системой камер, использующей OpenGL 4.5.У меня есть ортогональная камера, которая должна следовать за объектом игрока (вид с высоты птичьего полета) через 2D-уровень, сделанный из других объектов.Я знаю, что в OpenGL нет реальной камеры, и вместо этого вы перемещаете все остальное в противоположном направлении, думал, что я изо всех сил пытаюсь реализовать ортогональную камеру в коде.Приведенный ниже код рисует игрока как 2D-треугольник, а сам уровень состоит из других 2D-объектов с текстурами или без них.
Библиотеки и языки, использованные для создания приведенного ниже кода:
- Java 9
- GLSL 4.5
- OpenGL 4.5 основной профиль только
- LWJGL 3
- GLFW (часть LWJGL 3)
- математическая библиотека JOML
Проблема
Моя камера не следует за игроком, и когда я нажимаю кнопку, например D, OpenGL отображает два мерцающих игрокаобъекты на экране вместо перемещения плеера с камерой вправо.
Мой текущий код
Ниже приведена минимальная реализация тестового плеера и камеры.Все остальные объекты могут быть описаны как игрок без экземпляра камеры.Поскольку это минимальный рабочий пример моего плеера и камеры (без основного класса для инициализации LWJGL 3), я заменил свои пользовательские методы на прямые вызовы OpenGL.
Плеер:
public class TestPlayer {
private int x = 0, y = 0;
private float size = 1.f;
private OrthoCamera camera;
private int matLocation = 0;
private FloatBuffer matBuffer = BufferUtils.createFloatBuffer(16);
private int shaderProgram = 0;
private IntBuffer vertexArray = BufferUtils.createIntBuffer(1);
private IntBuffer vertexBuffer = BufferUtils.createIntBuffer(1);
private IntBuffer indexBuffer = BufferUtils.createIntBuffer(1);
Matrix4f projection = new Matrix4f().ortho(-16.f, 16.f, -9f, 9f, -1.f, 1.f);
Matrix4f model = new Matrix4f().identity().translate(new Vector3f(0, 0, 0));
private String[] vertexShaderSource = {
"#version 450 core\n",
"\n",
"layout (location = 0) in vec4 position;\n",
"\n",
"uniform mat4 u_MVP;\n",
"\n",
"void main() {\n",
" gl_Position = u_MVP * position;\n",
"}\n"
};
private String[] fragmentShaderSource = {
"#version 450 core\n",
"\n",
"layout (location = 0) out vec4 colour;\n",
"layout (location = 1) uniform vec4 u_Colour;\n",
"\n",
"void main() {\n",
" colour = u_Colour;\n",
"}"
};
public TestPlayer(Vector3f position, Vector3f lookat) {
this.camera = new OrthoCamera(position, lookat);
this.updatePositions();
this.shaderProgram = this.createShader();
glUseProgram(this.shaderProgram);
matBuffer.clear();
matLocation = glGetUniformLocation(this.shaderProgram, "u_MVP");
glUniformMatrix4fv(matLocation, false, projection.get(matBuffer));
glUseProgram(0);
glBindVertexArray(0);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
glBindBuffer(GL_ARRAY_BUFFER, 0);
}
private void updatePositions() {
if(vertexArray.hasRemaining()) {
glDeleteVertexArrays(this.vertexArray);
vertexArray.clear();
}
if(indexBuffer.hasRemaining()) {
glDeleteBuffers(this.indexBuffer);
indexBuffer.clear();
}
if(vertexBuffer.hasRemaining()) {
glDeleteBuffers(this.vertexBuffer);
vertexBuffer.clear();
}
float[] positions = {
x-size, y-size,
x , y+size,
x+size, y-size
};
int[] indices = {
0, 1, 2
};
glGenVertexArrays(this.vertexArray);
FloatBuffer vertexData = BufferUtils.createFloatBuffer(3 * 2);
vertexData.put(positions);
vertexData.flip();
glGenBuffers(this.vertexBuffer);
glBindBuffer(GL_ARRAY_BUFFER, this.vertexBuffer.get(0));
glBufferData(GL_ARRAY_BUFFER, vertexData, GL_STATIC_DRAW);
glBindVertexArray(this.vertexArray.get(0));
glBindBuffer(GL_ARRAY_BUFFER, this.vertexBuffer.get(0));
glEnableVertexAttribArray(0);
glVertexAttribPointer(0, 2, GL_FLOAT, false, Float.BYTES * 2, 0);
IntBuffer indexData = BufferUtils.createIntBuffer(3);
indexData.put(indices);
indexData.flip();
glGenBuffers(this.indexBuffer);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, this.indexBuffer.get(0));
glBufferData(GL_ELEMENT_ARRAY_BUFFER, indexData, GL_STATIC_DRAW);
}
public void update() {
this.camera.update(new Vector3f(x, y, 1));
}
public void keyUpdate(int key) {
if(key == GLFW.GLFW_KEY_W)
y++;
if(key == GLFW.GLFW_KEY_S)
y--;
if(key == GLFW.GLFW_KEY_A)
x--;
if(key == GLFW.GLFW_KEY_D)
x++;
}
public void render(double currentTime) {
matBuffer.clear();
this.updatePositions();
glUseProgram(this.shaderProgram);
glBindVertexArray(this.vertexArray.get(0));
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, this.indexBuffer.get(0));
// colour
glUniform4f(1, 0.f, .8f, 1.f, 1.f);
Matrix4f matrix = projection.mul(camera.getView().mul(model));
glUniformMatrix4fv(matLocation, false, matrix.get(matBuffer));
glDrawElements(GL_TRIANGLES, 3, GL_UNSIGNED_INT, 0);
}
public void dispose() {
glBindVertexArray(0);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
glBindBuffer(GL_ARRAY_BUFFER, 0);
glDeleteVertexArrays(this.vertexArray);
glDeleteBuffers(this.indexBuffer);
glDeleteBuffers(this.vertexBuffer);
glUseProgram(0);
glDeleteProgram(this.shaderProgram);
}
private int createShader() {
int program = glCreateProgram();
int vertexShader = glCreateShader(GL_VERTEX_SHADER);
glShaderSource(vertexShader, vertexShaderSource);
glCompileShader(vertexShader);
if(glGetShaderi(vertexShader, GL_COMPILE_STATUS) == GL_FALSE) {
System.err.println("ERROR: Compiling vertex shader");
System.err.println(glGetShaderInfoLog(vertexShader));
glDeleteShader(vertexShader);
return -1;
}
int fragmentShader = glCreateShader(GL_FRAGMENT_SHADER);
glShaderSource(fragmentShader, fragmentShaderSource);
glCompileShader(fragmentShader);
if(glGetShaderi(fragmentShader, GL_COMPILE_STATUS) == GL_FALSE) {
System.err.println("ERROR: Compiling fragment shader");
System.err.println(glGetShaderInfoLog(fragmentShader));
glDeleteShader(fragmentShader);
return -1;
}
glAttachShader(program, vertexShader);
glAttachShader(program, fragmentShader);
glLinkProgram(program);
glValidateProgram(program);
glDeleteShader(vertexShader);
glDeleteShader(fragmentShader);
return program;
}
}
Камера: Обратите внимание, что это базовая камера класса.Цель состоит в том, чтобы заставить эту очень простую версию камеры работать, прежде чем добавлять к ней дополнительные функции.
public class OrthoCamera {
private Vector3f position;
private Matrix4f view = new Matrix4f().identity();
public OrthoCamera(Vector3f position, Vector3f lookAt) {
this.position = position;
this.view.lookAt(position, lookAt, new Vector3f(0, 1, 0));
}
public void update(Vector3f lookAt) {
this.view.identity().lookAt(position, lookAt, new Vector3f(0, 1, 0));
}
public Matrix4f getView() {
return this.view;
}
}
РЕДАКТИРОВАТЬ: Я только что создал gif проблемы.Синий треугольник - это TestPlayer, а другой треугольник - это неподвижная сущность.D
является единственной клавишей, которая была нажата (один раз) в GIF ниже.
РЕДАКТИРОВАТЬ 2: Основной класс минимального примера:
public class HelloWorld {
// The window handle
private long window;
private TestPlayer player;
private TestPlayer2 player2;
private Callback glErrorCallback;
private static final IntBuffer SCREEN_WIDTH = BufferUtils.createIntBuffer(1);
private static final IntBuffer SCREEN_HEIGHT = BufferUtils.createIntBuffer(1);
public void run() {
// Output in my case: Hello LWJGL 3.2.0 build 12!
System.out.println("Hello LWJGL " + Version.getVersion() + "!");
init();
loop();
if(glErrorCallback != null)
this.glErrorCallback.free();
// Free the window callbacks and destroy the window
glfwFreeCallbacks(window);
glfwDestroyWindow(window);
GL.setCapabilities(null);
// Terminate GLFW and free the error callback
glfwTerminate();
glfwSetErrorCallback(null).free();
}
private void init() {
GLFWErrorCallback.createPrint(System.err).set();
if ( !glfwInit() )
throw new IllegalStateException("Unable to initialize GLFW");
// Configure GLFW
glfwDefaultWindowHints();
glfwWindowHint(GLFW_VISIBLE, GLFW_FALSE);
glfwWindowHint(GLFW_RESIZABLE, GLFW_TRUE);
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 4);
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 5);
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
glfwWindowHint(GLFW_OPENGL_DEBUG_CONTEXT, GLFW_TRUE);
// Create the window
window = glfwCreateWindow(1280, 720, "Hello World!", NULL, NULL);
if ( window == NULL )
throw new RuntimeException("Failed to create the GLFW window");
glfwSetKeyCallback(window, (window, key, scancode, action, mods) -> {
if ( key == GLFW_KEY_ESCAPE && action == GLFW_RELEASE )
glfwSetWindowShouldClose(window, true);
if ( action == GLFW.GLFW_PRESS)
if(player != null)
player.keyUpdate(key);
});
// Get the thread stack and push a new frame
try ( MemoryStack stack = stackPush() ) {
IntBuffer pWidth = stack.mallocInt(1); // int*
IntBuffer pHeight = stack.mallocInt(1); // int*
// Get the window size passed to glfwCreateWindow
glfwGetWindowSize(window, pWidth, pHeight);
// Get the resolution of the primary monitor
GLFWVidMode vidmode = glfwGetVideoMode(glfwGetPrimaryMonitor());
// Center the window
glfwSetWindowPos(
window,
(vidmode.width() - pWidth.get(0)) / 2,
(vidmode.height() - pHeight.get(0)) / 2
);
} // the stack frame is popped automatically
glfwMakeContextCurrent(window);
// Enable v-sync
glfwSwapInterval(1);
// Make the window visible
glfwShowWindow(window);
}
private void loop() {
GL.createCapabilities();
glfwGetFramebufferSize(this.window, SCREEN_WIDTH, SCREEN_HEIGHT);
glViewport(0, 0, SCREEN_WIDTH.get(), SCREEN_HEIGHT.get());
this.glErrorCallback = GLUtil.setupDebugMessageCallback();
// Set the clear color
glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
player = new TestPlayer(new Vector3f(0, 0, 0), new Vector3f(0, 0, 0));
player2 = new TestPlayer2();
// Run the rendering loop until the user has attempted to close
// the window or has pressed the ESCAPE key.
while ( !glfwWindowShouldClose(window) ) {
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); // clear the framebuffer
// normally, update would not be called as often as render
player.update();
player2.update();
player2.render(glfwGetTime());
// the player with the camera
player.render(glfwGetTime());
glfwSwapBuffers(window); // swap the color buffers
// Poll for window events. The key callback above will only be
// invoked during this call.
glfwPollEvents();
}
player.dispose();
player2.dispose();
}
public static void main(String[] args) {
new HelloWorld().run();
}
}
Второй статический объект:
public class TestPlayer2 {
private int x = 3, y = 3;
private float size = 1.f;
private int shaderProgram = 0;
private IntBuffer vertexArray = BufferUtils.createIntBuffer(1);
private IntBuffer vertexBuffer = BufferUtils.createIntBuffer(1);
private IntBuffer indexBuffer = BufferUtils.createIntBuffer(1);
private String[] vertexShaderSource = {
"#version 450 core\n",
"\n",
"layout (location = 0) in vec4 position;\n",
"\n",
"uniform mat4 u_MVP;\n",
"\n",
"void main() {\n",
" gl_Position = u_MVP * position;\n",
"}\n"
};
private String[] fragmentShaderSource = {
"#version 450 core\n",
"\n",
"layout (location = 0) out vec4 colour;\n",
"layout (location = 1) uniform vec4 u_Colour;\n",
"\n",
"void main() {\n",
" colour = u_Colour;\n",
"}"
};
public TestPlayer2() {
float[] positions = {
x-size, y-size,
x , y+size,
x+size, y-size
};
int[] indices = {
0, 1, 2
};
glGenVertexArrays(this.vertexArray);
FloatBuffer vertexData = BufferUtils.createFloatBuffer(3 * 2);
vertexData.put(positions);
vertexData.flip();
glGenBuffers(this.vertexBuffer);
glBindBuffer(GL_ARRAY_BUFFER, this.vertexBuffer.get(0));
glBufferData(GL_ARRAY_BUFFER, vertexData, GL_STATIC_DRAW);
glBindVertexArray(this.vertexArray.get(0));
glBindBuffer(GL_ARRAY_BUFFER, this.vertexBuffer.get(0));
glEnableVertexAttribArray(0);
glVertexAttribPointer(0, 2, GL_FLOAT, false, Float.BYTES * 2, 0);
IntBuffer indexData = BufferUtils.createIntBuffer(3);
indexData.put(indices);
indexData.flip();
glGenBuffers(this.indexBuffer);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, this.indexBuffer.get(0));
glBufferData(GL_ELEMENT_ARRAY_BUFFER, indexData, GL_STATIC_DRAW);
Matrix4f projection = new Matrix4f().ortho(-16.f, 16.f, -9f, 9f, -1.f, 1.f);
this.shaderProgram = this.createShader();
glUseProgram(this.shaderProgram);
int location = glGetUniformLocation(this.shaderProgram, "u_MVP");
FloatBuffer buffer = BufferUtils.createFloatBuffer(16);
glUniformMatrix4fv(location, false, projection.get(buffer));
glUseProgram(0);
glBindVertexArray(0);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
glBindBuffer(GL_ARRAY_BUFFER, 0);
}
private int createShader() {
int program = glCreateProgram();
int vertexShader = glCreateShader(GL_VERTEX_SHADER);
glShaderSource(vertexShader, vertexShaderSource);
glCompileShader(vertexShader);
if(glGetShaderi(vertexShader, GL_COMPILE_STATUS) == GL_FALSE) {
System.err.println("ERROR: Compiling vertex shader");
System.err.println(glGetShaderInfoLog(vertexShader));
glDeleteShader(vertexShader);
return -1;
}
int fragmentShader = glCreateShader(GL_FRAGMENT_SHADER);
glShaderSource(fragmentShader, fragmentShaderSource);
glCompileShader(fragmentShader);
if(glGetShaderi(fragmentShader, GL_COMPILE_STATUS) == GL_FALSE) {
System.err.println("ERROR: Compiling fragment shader");
System.err.println(glGetShaderInfoLog(fragmentShader));
glDeleteShader(fragmentShader);
return -1;
}
glAttachShader(program, vertexShader);
glAttachShader(program, fragmentShader);
glLinkProgram(program);
glValidateProgram(program);
glDeleteShader(vertexShader);
glDeleteShader(fragmentShader);
return program;
}
public void update() {
// Nothing in here, static entity
}
public void render(double currentTime) {
glUseProgram(this.shaderProgram);
glBindVertexArray(this.vertexArray.get(0));
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, this.indexBuffer.get(0));
glUniform4f(1, 8.f, .8f, 1.f, 1.f);
glDrawElements(GL_TRIANGLES, 3, GL_UNSIGNED_INT, 0);
}
public void dispose() {
glBindVertexArray(0);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
glBindBuffer(GL_ARRAY_BUFFER, 0);
glDeleteVertexArrays(this.vertexArray);
glDeleteBuffers(this.indexBuffer);
glDeleteBuffers(this.vertexBuffer);
glUseProgram(0);
glDeleteProgram(this.shaderProgram);
}
}