我对OpenGL很陌生,并开始使用OpenGL 4.5在摄影机系统上工作。我有一个正交摄影机,该摄影机应通过由其他实体组成的2D级别跟随玩家实体(鸟瞰)。 我知道在OpenGL中没有真正的摄像头,相反,您以相反的方向移动其他所有内容,以为我在代码中实现正交摄像头很费劲。 下面的代码将播放器绘制为2D三角形,而关卡本身则由具有或不具有纹理的其他2D实体组成。
用于创建以下代码的库和语言:
我的相机不跟随播放器,并且每当我按下一个按钮(例如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);
}
}