3
\$\begingroup\$

I'm making a pixel-art game in Unity. The sprites are set to 16 PPU, and currently to guarantee that the pixels scale nicely (integer intervals), I'm setting the Camera's orthographic size.

In 16x9 aspect ratio on a 1920x1080p monitor, this gives me an orthographic size of 8.4375, effectively scaling up my art 4x.

The problem comes with rotations. When you rotate the pixel art using a standard rotation shader, the shader considers screen pixels but not the art pixels. I know there are solutions to this problem on the Asset store, but I'd like to create my own (I have checked other stack posts on this subject, but there doesn't seem to be a solution).

Currently, I have a rotation shader that rotates the vertices in the vertex function. However, like I mentioned above, this rotates in screen pixels. One idea I had was to first scale the sprite down to its art pixel dimensions (by scaling the vertices in the vertex shader), then rotate it, then scale it back up (in another pass?). I couldn't figure out how to make this work, and I'm not sure if it's the most efficient way.

Another idea I had was to create a copy of the texture that's being rotated in a script, save it in a larger texture, then handle all my rotations within the new texture (using SetPixels and Apply). This would ensure that the rotations are being done in art pixels, but is probably slower (and more inconvenient) than using a shader.

Note: Rendering to a back buffer and then scaling up everything is not a solution for me (this isn't a purist pixel art game, I just want pure pixel art rotations).

Any ideas? What approach should I take? I don't need the code written out for me, I just need an approach that'll work. Thank you!

Here is an example of what I'm talking about (credit for images goes to this stack overflow post, although it didn't have the solution I'm looking for).

Rotation on screen pixel level
Rotation on screen pixel level

Rotation on art pixel level
Rotation on art pixel level

This is not a duplicate of this stack post. The solution that was accepted in that post does not perform rotation on the art pixel level, and because of that, it's not a valid solution in my case.

\$\endgroup\$
  • 3
    \$\begingroup\$ Anton Kudin tweeted about a shader/script that does this recently, linked to a gist of the code here \$\endgroup\$ – DMGregory Feb 16 '18 at 1:59
  • \$\begingroup\$ Also, do you use your quarter-art-texel resolution for anything? Or is it acceptable to have all the on-screen content snapped to 4-screen-pixel increments? If so, just rendering the whole game at native art scale then upscaling the result 4x for display on the screen would get you a similar effect, more simply. \$\endgroup\$ – DMGregory Feb 16 '18 at 2:04
  • \$\begingroup\$ Aww interesting! I'll check out the shader when I get a chance. I just finished creating a solution that involves rotating the texture via script, but I like the idea of using a shader better. And yeah, we don't want to confine ourselves to the art pixel grid. We decided rotations we want to be per art pixel, but other things (like lighting) we might want at a higher resolution. \$\endgroup\$ – FaTal Cubez Feb 16 '18 at 2:57
  • \$\begingroup\$ @fatal-cubez Are ok sharing how you've done the pixelart rotation? \$\endgroup\$ – Nikaas Feb 16 '18 at 3:59
  • \$\begingroup\$ Possible duplicate of How can I rotate pixel art sprites without the aesthetics getting ruined? \$\endgroup\$ – Pikalek Feb 16 '18 at 4:34
1
\$\begingroup\$

Ok, so I ended up getting a working solution. My algorithm is very loosely based off of the RotSprite algorithm (same idea without all the bells and whistles). The rotation is done on a Texture2D in a script I called PixelRotate. This is probably more inefficient than using a shader, but I couldn't figure out how to get the shader to work.

Step by Step explanation:

  1. Create a copy of the sprite texture with transparent padding included. This ensures that when you rotate the sprite about its center none of the corners get cut off.
  2. Use a modified Scale2x algorithm (implementation taken directly from link below, same one that's used in RotSprite) to scale up the sprite 8x (Scale2x is called three times).
  3. Perform the rotation using standard Nearest Neighbor scaling while also simultaneously scaling the image back to its original size.

Notes:

  • Since all rotations can only be done about the sprite's center (because the rotations take place inside a Texture2D with a limited size), rotating about a pivot point is more tricky. The way I handled it was allowing the user to specify a pivot point in PIXELS based on the ORIGINAL sprite, not the padded version. The function allows you to pass in by reference a Vector3 called local. This vector represents the translation in WORLD units that the sprite must undergo for the rotation to successfully be completed about the specified pivot. All you have to do is add local to the sprite's transform after the rotation is finished.

Images:

enter image description here

enter image description here

As you can see, the algorithm isn't perfect. The outline of the sprites often times get messed up when you rotate. Any suggestions for improvements are much appreciated!

Code: https://pastebin.com/hSWuZFi3

Enjoy!

Sources:
Discussion on the RotSprite algorithm - https://pixelation.org/index.php?PHPSESSID=okvmle4tigkomqpjuu7uivchk5&topic=10510.10
Scaling code used in RotSprite - https://github.com/alteredgenome/grafx2/issues/385
Detailed description of RotSprite - http://forums.sonicretro.org/index.php?showtopic=8848&st=15&p=159754&#entry159754

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.