Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
/*
Basic Sprite Shader for aligning pixel art to the same grid, based on the Unity Sprite Shader.
Create one Material where you assign the same Pixels Per Unit value you use on your imported Sprites,
then reuse this Material on all appropriate Sprite Renderers.
(You can use Shader.SetGlobalFloat to set that Pixels Per Unit value for all your shaders:
https://docs.unity3d.com/ScriptReference/Shader.SetGlobalFloat.html)
This is not for scaled or rotated artwork. If you need those features, look at low res render textures.
Use this however you want.
@talecrafter
*/
Shader "Sprites/PixelArt"
{
Properties
{
[PerRendererData] _MainTex ("Sprite Texture", 2D) = "white" {}
_Color ("Tint", Color) = (1,1,1,1)
_pixelsPerUnit("Pixels Per Unit", Float) = 16
}
SubShader
{
Tags
{
"Queue"="Transparent"
"IgnoreProjector"="True"
"RenderType"="Transparent"
"PreviewType"="Plane"
"CanUseSpriteAtlas"="True"
}
Cull Off
Lighting Off
ZWrite Off
Blend One OneMinusSrcAlpha
Pass
{
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#pragma multi_compile _ UNITY_ETC1_EXTERNAL_ALPHA
#include "UnityCG.cginc"
struct appdata_t
{
float4 vertex : POSITION;
float4 color : COLOR;
float2 texcoord : TEXCOORD0;
};
struct v2f
{
float4 vertex : SV_POSITION;
fixed4 color : COLOR;
float2 texcoord : TEXCOORD0;
};
fixed4 _Color;
float _pixelsPerUnit;
float4 AlignToPixelGrid(float4 vertex)
{
float4 worldPos = mul(unity_ObjectToWorld, vertex);
worldPos.x = floor(worldPos.x * _pixelsPerUnit + 0.5) / _pixelsPerUnit;
worldPos.y = floor(worldPos.y * _pixelsPerUnit + 0.5) / _pixelsPerUnit;
return mul(unity_WorldToObject, worldPos);
}
v2f vert(appdata_t IN)
{
float4 alignedPos = AlignToPixelGrid(IN.vertex);
v2f OUT;
OUT.vertex = mul(UNITY_MATRIX_MVP, alignedPos);
OUT.texcoord = IN.texcoord;
OUT.color = IN.color * _Color;
return OUT;
}
sampler2D _MainTex;
sampler2D _AlphaTex;
fixed4 SampleSpriteTexture(float2 uv)
{
fixed4 color = tex2D(_MainTex, uv);
#if ETC1_EXTERNAL_ALPHA
color.a = tex2D(_AlphaTex, uv).r;
#endif
return color;
}
fixed4 frag(v2f IN) : SV_Target
{
fixed4 c = SampleSpriteTexture (IN.texcoord) * IN.color;
c.rgb *= c.a;
return c;
}
ENDCG
}
}
}
@Skybladev2

This comment has been minimized.

Copy link

@Skybladev2 Skybladev2 commented Oct 4, 2016

Don't you think it's better to add 0.625 instead of 0.5 to avoid rounding issues?
Or maybe there are no issues and this is an overkill? :)

@talecrafter

This comment has been minimized.

Copy link
Owner Author

@talecrafter talecrafter commented Oct 4, 2016

@Skybladev2
The 0.5 is for offsetting the result so the source position is inside a virtual square by all four possible new positions. It's half a pixel in world space which would be 0.03125 and not 0.0625 for a _pixelsPerUnit of 16. (I guess you meant 0.0625 and the missing 0 was a typo.)

Here's it written in another way:
float offset = 0.5 / _pixelsPerUnit;
worldPos.x = floor(worldPos.x * _pixelsPerUnit) / _pixelsPerUnit + offset;

@Skybladev2

This comment has been minimized.

Copy link

@Skybladev2 Skybladev2 commented Oct 4, 2016

@talecrafter
No, I meant 0.625 which is 3/4 - 1/8.
Ok, I checked - there is no difference between 0.5 and 0.625, so forget it.

@talecrafter

This comment has been minimized.

Copy link
Owner Author

@talecrafter talecrafter commented Oct 4, 2016

@Skybladev2
Ok, that's what you meant. Thanks for checking. :)

@edwardrowe

This comment has been minimized.

Copy link

@edwardrowe edwardrowe commented Dec 21, 2016

Note it looks like before Unity 5.4, the functions in the UnityCG.cginc library "unity_WorldToObject" and "unity_ObjectToWorld" were named "_World2Object" and "_Object2World" respectively. https://forum.unity3d.com/threads/_object2world-undeclared.385227/

So if using this on older versions of unity, you'll want to change those lines or it won't compile.

Thanks for sharing the code @talecrafter!

@leegrey

This comment has been minimized.

Copy link

@leegrey leegrey commented Jun 15, 2017

Hi @talecrafter. I've been using this shader and it's really useful! In a recent version of Unity it seems to be broken though. Would you happen to have patched this in the meantime? Any chance of updating the gist? Thanks for sharing! :)

EDIT: Oh, I just realised you have already updated it. It works now. Thank you!

@timsoret

This comment has been minimized.

Copy link

@timsoret timsoret commented Aug 4, 2017

"(As far as I know there's no possiblity to get this value automatically.)"

You can use Unity - Scripting API: Shader.SetGlobalFloat
https://docs.unity3d.com/ScriptReference/Shader.SetGlobalFloat.html

It sets a float shared by all shaders at the same time.

@ezonecom

This comment has been minimized.

Copy link

@ezonecom ezonecom commented Jun 17, 2019

Thanks for sharing, a must have for any pixel-perfect project!

@denartelny

This comment has been minimized.

Copy link

@denartelny denartelny commented Nov 12, 2020

Good

@denartelny

This comment has been minimized.

Copy link

@denartelny denartelny commented Nov 12, 2020

Bruh

Bruh

@TheStigC

This comment has been minimized.

Copy link

@TheStigC TheStigC commented Jan 24, 2021

Is there a URP version of this? I'd love to be able to use this with the 2D lights

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment