Skip to content

Render sprites visually correctly with Unity3D perspective camera by setting sortOrder based on z position

Notifications You must be signed in to change notification settings

angusmf/SortByZCam

Repository files navigation

SortByZCam

This code is inteded to be pulled into a Unity3d project's Asset folder as a submodule, thus it has no Assets folder or project settings, which you usually see in things intended for the Asset Store. It would be interesting to use Unity's package manaager if/when it's opened to the public.

SortByZCam is a hack to allow somewhat free camera movement when not using an orthographic camera. The default determiner of render order (which texture appears on top of another) is the distance* between the camera and an object. The result is that textures displayed using sprite shader can pop in front/behind each other as the camera moves and the distance changes. Presumably this is because sprite shaders are intended to be used with the SpriteRenderer, which has no 3D geometry. Unfortunately, even if there is Geometry there, such as a quad or plane, the shader seems to have no idea and sorts by the pivot or center of the texture anyway. One solution would be to create a new shader based on a "normal" non-sprite shader. It would need add support for sprite-like things, such as transparency and perhaps double-sidedness. For convenience, one would want a Monobehavior component that replicates the relevent functionality from the SpriteRenderer, and it's unclear how much extra work would be involved in this. But everything would look correct!

This library takes a different approach. In the Unity render pipeline, and the sprite shader in particular, the render order of objects can be overridden based on a sort layer and index within that layer. For any given scene filled with sprites and any given camera position, there is a definition of a correct sorting order, with each sprite assigned a higher integer than any it should appear before, and a lower integer than any it should appear behind. In the even that two sprites had the same number, however, the render order falls back to camera distance. This allows us to assign a raw sort order to each sprite based on their position on the Z axis. Let's say there are a bunch of sprites in a scene. At rotation 0,0,0 they will be "facing" the z axis, and their z-depth will be the same across the entire sprite. This allows us to set a raw sort order based on the z position, and simply reverse it if the camera rotates to face the other direction. SortByZCam performs that update when it moves, and also checks each of the sprites it has info about and updates the sort order if the transform changes.

This works well for sprites that have no rotation, but once you get started, you may want to experiment with building 3D scenes out of sprites, and you may start rotating them. The problem with this is that the z-depth and z position are no longer the same. This appears as incorrect sorting, with objects appearing to be above something that they are below, or vice versa. The solution taken here is to find the distance between the z position of the center of the sprite and the z position of one corner and use this as an offset to the original z-depth based ordering. This works ok for simple scenes where you DON'T have something like a sprite sandwiched between two rotated sprites, and then start moving the camera around. Help wanted with a more robust solution for this!

Finally, you may find you are simply layering sprites over one-another at the same z-depth because they are part of the same logical object, at the same position. This is actually with the sort ordering values were intended for! The solution here is to apply a sort order to these sprites just as you normally would. That is, the closest sprite should have the highest number, and so on. When SortByZCam starts up, it reads and saves whatever sort order is set for the SpriteRenderer in the inspector. When calculating the sort order for a sprite, this value is used as an offset with no scaling. By default, the z order and rotation offset are multiplied by 1000. For an initial offset of 10, this means that as long as the z-distance including rotation offset between two sprites is greater than .01 units, which is very, very close, the sort order should still give the correct appearance. If not, the options are to:

a) Increase z-distance between sprites, or b) Increase SortByZCam.worldscale

Please submit an issue if you have questions or comments!

Written with StackEdit.

About

Render sprites visually correctly with Unity3D perspective camera by setting sortOrder based on z position

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages