OpenGL translation based on “scaled” pixels? (Not physical pixels)

My (2D!) game runs in a “scaling mode” so to speak, and I’ve yet to find anyone asking this question anywhere. Basically, I want to achieve a sort of “scaling algorithm” whether graphics are translated based on some “virtual pixel” multiplier.

Essentially, if the game is running natively at 624x240, then everything is translated exactly 1 physical pixel. But, if the game has been scaled 2 or 3 times, then I need the graphics to be translated by 2 or 3 pixels, respectively, because the graphics are scaled. Basically, each pixel should move 1 pixel multiplied by the scaling factor. In other words, a 64x64 pixel Sprite would move 3 pixels to the right when scaled 3 times. If this sprite starts at 100x50, then it’s new position would be 103x50, not 101x50. This is the issue I cannot overcome. I have tried something like this: model = glam::mat4(<identity>, glm::vec3(pos.x + scale, pos.y + scale, 0.f)); -> everything still moves 1 physical pixel.

I asked on the gamedev subreddit, but no one was able to help me out, primarily because either I probably misunderstood what they were saying, or they were quite vague in what they were saying. One person was talking about scaling the vertex positions in the vertex shader, then mentioned using GL_SHORT when calling glVertexAttribPointer but I was trying for over an hour and couldn’t make GL_SHORT work to save my life.

I tried using glViewport, but everything is still translated by physical pixels. Anyone have any ideas? Any insights? I also tried framebuffers but they ended up being a huge pain for what I want to do, that they didn’t seem worth it to me.

Ways to do this, in roughly descending order of complexity.

Modify all vertex positions by hand in code

This is going to be nasty, messy, bug-prone and you:re all but guaranteed to find edge cases where it doesn't do exactly what you want. I would avoid this if at all possible.

Use a framebuffer object

Draw to the sized-down framebuffer, then draw that to the screen. You mention you tried it and rejected it for reasons of complexity. I wouldn't consider it hugely complex but much simpler solutions do exist.

Use glCopyTexImage

With this method you draw to a sized-down viewport, copy it to a texture, then draw that to screen. It's like a poor man's version of framebuffer objects, but can work well enough.