• Editor
  • Pixel Art Feature Request

The way Spine currently works, it renders the viewport to a very high resolution (whatever your window is sized to), so even with linear filtering off, all pixel animation lerps things at fractions of a pixel, and while the animation might look smooth in Spine, it could be janky in a pixel art game where the res is very low (for example 480 x 272). For example a subtle breathing motion where a character's chest sprite moves up and down from y = 10 to y=9 and back would look smooth in Spine but in game it would be harshly popping between two positions. As far as I can tell, there is no way get get spine to render things this way itself.

One of the things I do in AV is render the whole thing to a 480x272 target, clamp images to the nearest pixel when they're rendered, then render that to a stretched out HD target . So internally sprites might have a position of 5.5, -0.5, but it outputs them to the screen at 6,-1, then the final image gets a 4x treatment so they appear at 24,-4.

I think it would be helpful to have a "Pixel Resolution" feature for Spine to do all of its rendering at a low resolution, clamping output translation of images to pixels, only scaling them up to screen size as a last step.

Related Discussions
...

Welcome, AxiomVerge!

It is a bit tricky to get the exact effect you would like. Rendering to a buffer and drawing that at 4x isn't enough


that would be equivalent to just zooming in with the mouse wheel


we also need to round attachment positions to the nearest pixel (as you mentioned). Rounding attachment vertices doesn't work well if we allow non-integer positions in the setup pose. Rather than trying to keep all bones and attachments on integer positions, maybe it's sufficient to only round how much an animation affects positions. Eg, modify TranslateTimeline apply so bone position modifications are always full pixel amounts:

bone.x = bone.data.x + x * alpha;

Becomes:

bone.x = bone.data.x + Math.round(x * alpha);

The effect is really only useful for translation, since the skeleton's world axes can match the screen axes, we can map texels to pixels 1:1. That's no longer the case for most rotation and scale, so filtering has to be applied. Interpolation between rotation and scale values is smooth and may ruin the pixel art effect. Use of rotation is very common with skeletal animation, it would be difficult to avoid.

Putting a branch in the already CPU intense TranslateTimeline apply to make rounding optional isn't great. We could support the visualization only in the editor, but one of our goals is to render at runtime exactly as it does in the editor. For now I would suggest making the change in your copy of the runtimes. If you are using the official runtimes I expect you'll need a change like this to get the pixel effect with your game toolkit.

I'm sure you are still interested in what can be done in the editor so you can see there what you'll see at runtime. I know you used the 1px movement as an example, but FWIW, for that particular use case you could use stepped interpolation for a 1px translate key so the editor behavior matches your runtime. At low resolution it might be feasible to use a number of stepped translation keys. If you never pose your animations between frames at runtime, then you can set a key every frame and won't have the unwanted interpolation. I know this is not ideal, but may be an acceptable workaround. The main drawback is that editing an animation later can be difficult when there are many keys. You can lower the dopesheet framerate to reduce the number of keys, if you find that acceptable for your animations. Key adjust may help a little too.

Is it better to achieve this with a vertex shader that Math.rounds the vertex positions?
I guess it would end up wonky most of the time.

If you have a vertex at non-integer positions in the setup pose, just displaying it with the vertices rounded (via a shader or code) would distort the images by moving each vertex 0-1 pixels in a random direction. If that is OK (eg, setup pose vertices are all integer positions), there is still the issue of rotation and scaling. As soon as you do that, perfect texel to pixel mapping is lost. I suppose you could use Spine with only translation and do attachment swaps instead of rotation, but I expect that would be pretty limiting.

Ah sorry, I think I understand the request now. My bad.

User has NO PROBLEM IN-ENGINE. They render at a low res, possibly an intermediary FBO, then stretch it.

This doesn't solve the problem of not knowing what it will look like until it's put in-engine.

So it's helpful if we could see a low-res render in Spine itself.
But obviously, a low-res render that's zoomed in so you can actually see what you're animating.

Is that correct?

Nate wrote

Welcome, AxiomVerge!

It is a bit tricky to get the exact effect you would like. Rendering to a buffer and drawing that at 4x isn't enough


that would be equivalent to just zooming in with the mouse wheel


we also need to round attachment positions to the nearest pixel (as you mentioned). Rounding attachment vertices doesn't work well if we allow non-integer positions in the setup pose. Rather than trying to keep all bones and attachments on integer positions, maybe it's sufficient to only round how much an animation affects positions. Eg, modify TranslateTimeline apply so bone position modifications are always full pixel amounts:

Hi Nate, actually, you are right... I was misremembering since in my engine I clamp "normal" sprites (made in GraphicsGale) but then sprites attached to bone transforms don't get the position clamped. I think this is because, as you said, they are usually rotating and perhaps scaling and clamping the position made it look wrong somehow. Maybe if you detected whether that particular piece was being rotated or scaled and only applied it then, it would work, but it seems I did not do this.

I still think the 2nd part - rendering the art at a 1:1 ratio with the images and then stretching this to screen size would help. I attached an image that shows several parts of a single creature that are misaligned from what you see in game. A 1x1 pixel is turned into a 12x12 pixel and parts are off in increments of 1/12. You can see the rotated pixels here, as well; they appear like much larger rotated polygons:

Whereas in the game, the same image would look like this:

In the 2nd case you generally have aliasing artifacts but it is more accurate to what is possible in an 8- or 16-bit game.


Pharan wrote

Ah sorry, I think I understand the request now. My bad.

User has NO PROBLEM IN-ENGINE. They render at a low res, possibly an intermediary FBO, then stretch it.

This doesn't solve the problem of not knowing what it will look like until it's put in-engine.

So it's helpful if we could see a low-res render in Spine itself.
But obviously, a low-res render that's zoomed in so you can actually see what you're animating.

Is that correct?

Yeah, that is correct. I think it would help be able to animate in a way that will be most visible in game.

We've added a Viewport pixel grid scaling setting for pixel art skeletons in 3.7.14-beta, available now. It would be nice to hear what you think! :happy: :beer:


Please keep in mind the ramifications of using beta versions, described here:
Settings - Spine User Guide: Version

Nate wrote

We've added a Viewport pixel grid scaling setting for pixel art skeletons in 3.7.14-beta, available now. It would be nice to hear what you think! :happy: :beer:


Please keep in mind the ramifications of using beta versions, described here:
Settings - Spine User Guide: Version

Oh, yeah!!! I just turned it on and it seems to be exactly what I was expecting! It should help tremendously.

Great! It's not every day we can let a feature jump the backlog, but I'm glad it's working like you wanted. :clap:

Nate wrote

Great! It's not every day we can let a feature jump the backlog, but I'm glad it's working like you wanted. :clap:

It was the exact most perfect result I could have expected from a request like this :clap: :rock: :party: :sun:

일 년 후

Can I change pixel scale from 1:1 to 5:1 for example?