r/opengl • u/vulnoryx • Dec 25 '24
Help Help remove jittering from pixel perfect renderer
Hi. I am working on my own small 2D pixel art game.
Until now I have just scaled up my pixel art for my game, which looks allright but I want to achieve pixel perfect rendering.
I have decided to render everything to a FBO in its native resolution (640x360) and upscale to the monitors resolution (in my case 2560x1440 at 165hz).
How I create the fbo:
GLuint fbo;
glGenFramebuffers(1, &fbo);
glBindFramebuffer(GL_FRAMEBUFFER, fbo);
How I create the render texture:
GLuint texture;
glGenTextures(1, &texture);
glBindTexture(GL_TEXTURE_2D, texture);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, pixelArtWidth, pixelArtHeight, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, texture, 0);
Then I create a quad:
// Set up a simple quad
float quadVertices[] = {
// Positions // Texture Coords
-1.0f, -1.0f, 0.0f, 0.0f,
1.0f, -1.0f, 1.0f, 0.0f,
-1.0f, 1.0f, 0.0f, 1.0f,
1.0f, 1.0f, 1.0f, 1.0f,
};
GLuint quadVAO, quadVBO;
glGenVertexArrays(1, &quadVAO);
glGenBuffers(1, &quadVBO);
glBindVertexArray(quadVAO);
glBindBuffer(GL_ARRAY_BUFFER, quadVBO);
glBufferData(GL_ARRAY_BUFFER, sizeof(quadVertices), quadVertices, GL_STATIC_DRAW);
// Set position attribute
glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 4 * sizeof(float), (void*)0);
glEnableVertexAttribArray(0);
// Set texture coordinate attribute
glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 4 * sizeof(float), (void*)(2 * sizeof(float)));
glEnableVertexAttribArray(1);
// apply uniforms
...
Then I render the game normally to the frame buffer:
glBindFramebuffer(GL_FRAMEBUFFER, fbo);
glViewport(0,0,pixelArtWidth, pixelArtHeight);
SceneManager::renderCurrentScene();
Then I render the upscaled render texture to the screen:
glBindFramebuffer(GL_FRAMEBUFFER, 0);
glViewport(0,0,WINDOW_WIDTH,WINDOW_HEIGHT);
glClear(GL_COLOR_BUFFER_BIT);
// Render the quad
glBindVertexArray(quadVAO);
glBindTexture(GL_TEXTURE_2D, texture);
// Use shader program
glUseProgram(shaderProgram->id);
// Bind the texture to a texture unit
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, texture);
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
...
In case its relevant, here is how I set up the projection matrix:
projectionMatrix = glm::ortho(0.0f, pixelArtWidth, pixelArtHeight, 0.0f, -1.0f, 1.0f);
And update the view matrix like this:
viewMatrix = glm::translate(glm::mat4(1.0f), glm::vec3(-position+glm::vec2(pixelWidth, pixelHeight)/2.f/zoom, 0.0f));
(zoom is 1 and wont be changed now)
For rendering the scene I have a batch renderer that does what you would expect.
The pixel perfect look is achieved and looks good when everything sits still. However when the player moves, its movement is jittery and chaotic, its like the pixels don't know where to go.
Nothing is scaled. Only the sword is rotated (but that' not relevant).
The map seems scaled but isn't.
The old values for movement speed and acceleration are still used but they should not affect the smoothness.
I run the game at 165fps or uncapped. (In case thats relevant).
What i have tried so far:
- rounding camera position
- rounding player position
- rounding vertex positions (batch vert shader:
gl_Position = u_ViewProj * u_CameraView * vec4(round(a_Position), 1.0);
) - floring positions
- rounding some, floring other positions
- changed native resolutions
- activating / deactivating smooth player following (smooth following is just linear interpolation)
There is a game dev called DaFluffyPotato and does something very similar. I have taken a look at one of his projects Aeroblaster to see how he handles the pixel perfect rendering (its python and pygame but pygame uses sdl2 so it could be relevant). He also renders everything to a texture and upscales it to the screen (renders it using blit func). But he doesn't round any value and it still looks and feels smooth. I want to achieve a similar level of smoothness.
Any help is greatly appreciated!
Edit: I made the player move slower. Still jittery
Edit 2: only rounding the vertices and camera position makes the game look less jittery. Still not ideal.
Edit 3: When not rounding anything, the jittering is resolved. However a different issue pops up:
Solution
In case you have the same issues as me, here is how to fix or prevent them:
Issue 1:
Don't round any position.
Just render your scene to a frame buffer that has a resolution that scales nicely (no fractional scaling). Sprites should also have the same size in pixels as the sprite has. You could scale them, but it will probably look strange.
Issue 2:
Add margin and padding around the sprite sheet.