I’m trying to get pixel art to render correctly, but there’s subpixel distortion. From what I understand about scaling pixel art, I should be doing integer scaling so that pixels on screen don’t try to get elongated or cut in half, and I should be keeping my game object positions at whole numbers.
But despite doing this, I end up with this result:
Hi! Everything you wrote is correct, you should be able to get pixel art to render nicely with CGE. If you use “nearest” filtering (for MyScene.RenderOption.TextureMinification and MyScene.RenderOption.TextureMagnification), and position your objects precisely, then it should just work OK.
Can you submit a testcase (simplest smallest project that shows us the problem)? Then we’ll be able to investigate what is wrong easily
I believe that making pixel-perfect render is more complicated with TCastleViewport because it exists in UI-coordinates. E.g. let’s imagine we have our UI window set to default 1600x900. By positioning TCastleImageTransform or other graphic containers on the screen and specifying the nearest neighbor scaling we get them “perfect” in UI coordinates which “believe” the screen is 1600x900. However, the real screen can be 1366x768 and hence there is 0.85 scaling factor which results in the artifact visible above - some pixels are 3 pixels on the monitor, some are 4.
I know how to fix that with TDrawableImage as Draw accepts actual pixels on the screen - so keeping eye on the scale is enough to make them render pixel-perfect. But I’m not sure how to achieve it “the correct way” through TCastleViewport which to my knowledge is not aware about real monitor pixels.
The poster doesn’t mention pixel-perfect rendering (matching 1:1 screen pixel with data pixel). The image seems to show gaps where they shouldn’t be, let’s see the testcase why:)
Rendering with TCastleViewport, if you specify correct coordinates (such that one shape exactly touches another), should be good for this, regardless of whether TCastleViewport does scaling (because the coordinates it “thinks in” are different than actual window size) or not.
In pixel art every pixel should be exactly the same amount of pixels on the screen (1x1, 2x2, 3x3, 4x4 etc) - otherwise it looks bad. In the current render it varies between 3 and 2 (see the screenshot: there I denoted “width pixels” but the same affects height) - which results in different pixel sizes on screen 2x2, 2x3, 3x2 and 3x3 - which doesn’t look good.
EDIT: maybe grid will illustrate the issue better:
I figured out a solution for windows that don’t change size. To do this, you need to design for “minimum screen dimensions”, which is the resolution you design your pixel art scenes at using 1x scaling. I’ve also seen this get called a “logical resolution”. A common resolution for this is 480x320.
Once the size is decided on, I did the following in CGE:
The reference width and height in CastleSettings.xml becomes this minimum screen size, so 480x320.
I set the orthographic camera height to the height of this size, so 320.
The window width and height can be set to any scale of this size, as long as you scale by whole numbers (eg. 2x, 3x, 4x, and not 1.4x or something similar). In this case I scaled by 2x to get 960x640, which can be set in gameinitialize.pas using Window.Width and Window.Height.
Doing this results in pixel perfect scaling, since the window size is exactly 2x bigger than the reference size and camera height.
On resizable screens like on mobile this will get a bit tricky, since you need to calculate these values in code to make sure the content only scales by whole numbers rather than freely scaling to whatever the window size is. I haven’t tested this but I think the concepts above should still apply here.
Indeed, this means that inside the viewport you “think” in coordinates where viewport height is 320. I.e. moving things like TCastleScene or TCastleTransform by their Translation property, moving something “by 320” means to “move by visible viewport height”.
Yes, this means that UI coordinates are conceptually expressed in 480x320 window (adjusted to follow actual aspect ratio of the real window).
Note that this step is not strictly necessary. If you have TCastleViewport using orthographic camera height = 320, and you set Viewport.FullSize, then effectively “inside the viewport” you can think that window height is 320. It doesn’t matter anymore what were the UI scaling parameters.
But practically it makes perfect sense to set UI scaling like you did, for consistency, so that UI coordinates behave the same way as in viewport translation.
Indeed if you want to perfectly hit the screen pixels, this is the way to go.
OK, looks like this thread is solved then Let us know if you have any more questions!