• Unity
  • Sprite Shaders for Unity

Related Discussions
...

Image removed due to the lack of support for HTTPS. | Show Anyway


Hello!

I've created some shaders to allow for bump mapped sprite rending in Unity that people might find useful.
I've made them into a single 'uber-shader' for sprites with a custom inspector in unity so you can change blend modes / lighting modes etc easily via the editor without dealing with swapping around a billion different shaders (technically it's 3 shader labs shaders behind the scenes).

It supports a whole load of lighting & blending modes:


Bump Maps This works both with Pixel and Vertex lighting modes.

Vertex Lighting Up to 4 vertex lights per sprite are supported - including bump map approximation (the data for the 4 vertex lights is passed to the fragment shader which is then used to render with the bump map).

Pixel Lighting Pixel lighting's a lot more accurate than vertex lighting but as well as being more expensive, with sprites it normally means you get loads of nasty overdraw of bits that should be hidden but are just rendered additvely on top of the sprite due to there being no depth tests. Because of this with Pixel lighting its recommended writing to depth and clip pixels below an alpha threshold meaning you wont get the overdraw BUT you will get hard alpha edges due to the clipping.

If your mesh has enough verts, Vertex shadings going to look pretty much the same but depending on your games art style pixel lighting may be a better option. Worth trying out both and seeing what works!

Premultiply Alpha toggle You can also choose whether a sprite uses premultiplied alpa or vanilla alpha blending using the blend modes drop down in unity .

Solid, Additive and Multipy blend modes

Diffuse Ramping which can be used to generate cartoony lighting effects

Color Adjustment Adjust the Hue, Saturation and Brightness of sprites and fade to a sold color for effects like taking damage.

Emission an emission channel can be optionally used when lighting the sprite.

Rim Lighting which works in both pixel and vertex lighting modes. If you use these shaders on a sprite you'll need a normal map texture in order to get the range of normals needed in rim lighting.

Render Queue is exposed meaning you can force a particular draw order between sprites.

Shadows All the shaders support casting shadows (with hard alpha clipping). Only Pixel lighting allows for receiving shadows(but vertex lighting and unlit modes still support casting shadows).

To use the shaders just import the attached unity package and then set your skeleton animations material's shader to one of the 3 sprite shaders (eg Game\Sprites Unlit) 🙂

Notes!!

Normals
For lighting sprites it's recommended to use 'Fixed Normals' - ie a constant defined normal rather than the mesh's normals. (Sprite verts always face the same way after all).
This Fixed Normal is defined in camera space and for Unity this should be negative Z axis (ie the inverse of Camera forward).
To do this in the material inspector make sure the 'Use Mesh Normals' box is unticked and the normal is (0, 0, 1).
You should also untick the 'Add Normals' box in the advanced section of the Skeleton Animation component as the mesh normals aren't required.

If in your game you rotate your sprites 180 degrees around Y to make sprites face the opposite way, you should also tick 'Allow Back Rendering' in order for lighting to work correctly when Sprites are facing away from the camera.
If you use negative scale or 'FlipX' instead then leave it unticked and it will save a little bit of processing time 🙂

Bump Mapping
To get bump maps working the model needs to have correctly generated tangents. You'll need the latest Spine Runtimes, then just click the 'Solve Tangents' box on your Skeleton Animation component.

Depth Writing

To get depth writing (ie hard alphas) working without Z fighting you will also need to set the animations Z spacing to something less than zero (the amount will depend on your cameras near and far planes). This is also set in the same place as the 'Solve Tangents' box above.

The shaders are now up on GitHub. To download them follow this link and click 'Clone or download' and then download as a zip. Copy the SpriteShaders folder into anywhere inside your Unity Assets folder.
https://github.com/traggett/UnitySpriteShaders

Thanks for the notes! I think those two changes are pretty easy to make. Should be included in the incoming Spine-Unity 3 update.

Also, thanks for sharing these. They're not only cool for immediate use, they're instructive for writing more specialized shaders too, like ones with custom ramps or something.
Did you write these shaders yourself? Did you write them specifically for use with Spine-Unity or could you use them for regular Unity sprites too?

I'm looking at the shaders in the inspector and I see a couple of warnings/errors:

Output value 'vert' is not completely initialized
Compiling Vertex program with DIRECTIONAL SHADOWS_OFF LIGHTMAP_OFF DIRLIGHTMAP_OFF DYNAMICLIGHTMAP_OFF
Platform defines: UNITY_ENABLE_REFLECTION_BUFFERS UNITY_PBS_USE_BRDF1 UNITY_SPECCUBE_BOX_PROJECTION UNITY_SPECCUBE_BLENDING UNITY_TEXTURE_ALPHASPLIT_ALLOWED

Output value 'vertShadow' is not completely initialized
Compiling Vertex program with SHADOWS_DEPTH
Platform defines: UNITY_ENABLE_REFLECTION_BUFFERS UNITY_PBS_USE_BRDF1 UNITY_SPECCUBE_BOX_PROJECTION UNITY_SPECCUBE_BLENDING UNITY_TEXTURE_ALPHASPLIT_ALLOWED

Argh both those warnings should be harmless but I'll fix them up!

I wrote them all myself, I tried to follow how Unity's own shaders handle lighting plus also tried to keep them easy to read - mostly to remind myself of how everything works! (Haven't had too much experience with shaders and went in circles a couple of times with different vector spaces etc).

I was planning on putting them up on the asset store for free, they should be usable with any sprites not just Spines.
Definitely could add to them though - having an optional ramp texture would make sense and be easy to do so you can have toon shading, might try sticking it in 🙂

Hey no one knows a way of disabling passes in a unity shader using defines / tags or something do they? That what's currently stopping it being a single .shader file.

Just a note, negative zSpacing may cause SkeletonRenderer to draw the attachments in backwards order if you have depth stuff on.
Assuming the 100 pixels to 1 Unity unit ratio, I think a zSpacing of less than 0.01 may be ideal.

It's really unfortunate that per-pixel lights depend on depth buffers so lighting passes don't overlap. This has bugged me forever.
Sorry I can't help you with disabling passes. Maybe we can fish out some answers in the Unity forums.

Ok I've updated the shaders in the first post to fix those pesky warnings and also chucked in an optional ramp texture for diffuse lighting (untested!).

Yeah its annoying about per pixel lighting, but to be honest if you use actual meshes for your Spine sprites with a decent amount of verts, vertex lighting doesn't look bad at all, especially with some decent normal maps. My bad with the zSpacing recommendation - my games units are pretty big for some stupid reason.

With regards to disabling shader Passes, I've looked around on the Unity forums and found 3 people asking the same thing.. and none of them got replies. Its weird would've thought it would be easy for Unity to support as it fits in with there 'one uber shader' thing. Behind the scenes multiple cg shaders are being generated anyways so its more just cosmetic ie in the unity editor you'd only seeing one sprite shader instead of the three.

14 days later

Maybe someday, they'll stick that capability in. Certainly would be nice to have fewer files.
They seem to be much more active in making changes in the shaderlab stuff now than they were back in Unity 4.

Had a closer look at your shaders today. Really nice stuff. I didn't even know half the stuff you used in there existed in Unity's system til now. It was really informative, and super readable too.

I haven't tried the light/diffuse ramp yet.

Yeah theres not much documentaion for the in build shader stuff unity uses behind the scenes - the only way I found stuff was by digging through the source files for there own shaders which are hidden on the Unity site somewhere, it's pretty messy though!

I've since updated them locally to allow you to use sorting layers which is super usefull with sprites, I will share it once I've tidied it a bit.

I'm still not happy there's a few 'if' statements in the shaders which I remember a graphics programmer dude I worked with saying is bad for performance (although unity seem to use them themselves) but glad they're helpfull - can definetly nab bits if you wanted to make bump map support build in to the Spine Unity runtime or whatevs.

Cheers for fixing those tangents bugs btw!

There's this one weird bug with lighting though. Point lights seem to require being on +Z instead of -Z positions which is (1) a bit weird and unintuitive and (2) inconsistent with how Unity's Sprite Lit works? Sorta just fixed that on my end by flipping the calculated light direction Z.

I was just thinking about how ifs affect performance when I wrote that other with with custom lerpable fill. Though between lerp and if, I wonder which is worse.
I figure it'd be terrible on fragment code (checks per pixel) and not as much for vertex code.

Hmm thats really weird! Is that with vertex or pixel lighting?
Its not the tangents being the wrong way round is it? Actaully nah that would effect all lights. Huh. I did try and copy most of that stuff from Unity's standard shader, I'll have another look to see what could be wrong!

Yeah vertex lighting does no if statements in the fragment so should be ok. The pixel lighting does but I think there's a way of multiplying out each branch instead if you know what I mean? Def more stuff that could be looked into!

It's with vertex lighting. The weird thing is that the Spine/Skeleton Lit actually suffers the same problem even if it's just plain fixed-function shaderlab stuff.

This is a dumb question, but how do I import that zip into Unity? Thanks for these, I'd like to try them out

For Unity, you often just have to drag a file from explorer into the Project view.
But unzip it first, of course. Or you'll just have a zip file in your project, which is hilarious. 😃

Ah thanks, I figured it out. Mac Archive Utility was not unzipping it properly. Unzipping via cli works though and I get the .unitypackage properly

Thanks


02 Mar 2016 11:08 am


I tried the zSpacing change, but then my skeleton's attachments were all rendered spaced apart... I guess that was expected but not desired on my part.

Yeah for some reason the forum doesn't allow you to post .unitypackages as attachments so I zipped it up 🙂

With zSpacing, the mesh parts needs to be spaced apart enough to to stop z fighting but not too much to be visually noticeable, its worth playing round with values as it depends on your game scale, the value I said originally is prob way to big.

@Pharan - I've noticed the point light bug only happens when you don't have a normal map, with one its fine :/
I'll investigate a bit more!


02 Mar 2016, 16:23


Ok think I know whats the light z problem is! The problem comes from the fact the shaders have culling turned off so you can see both side of the mesh (meaning you can easily just rotate the sprite 180° to have them walk the two directions with a single animation).
However! Even though the sprite is rendered on two sides, there's only one set of normal's which are only valid when the sprite is facing forward. Hence lights only working on the correct side.

I've turned on back culling on the lit shaders which means you shouldn't be able to to have meshes with incorrect normals / lit wrong. This obviously means looking at a sprite from the back wont work but you should be able to flip the x scale instead to mirror animations etc.

I've also added a slider for render queue so you force sprites to render in a particular order! (Both are in the updates zip file in the first post)

4 days later

Hmm.. You're right. Generating normals does seem to fix the light z problem.
I guess enabling backface culling forces users to use FlipX/FlipY instead of rotation because it doesn't work both ways if you use rotation.

I have some questions about this.
Triangle winding determines culling, right? Not the normals?
Does triangle winding affect something else that shaders would use for lighting?
I'm asking 'cause I'm now wondering about the need for SkeletonRenderer to wind flipped attachment triangles the "right" way.
That is, would lighting be totally okay if backface culling was turned off but all the normals were pointing the correct direction?

Yep its the vert winding order is what makes a face front or back facing.
The trouble being a vert can only obv have one normal so when if you're not culling the back face your in essence using the same vert twice for the two faces, even though they're only one normal which is only going to be pointing out from one of the faces, so the back on will be lit incorrectly.

Because of that all Verts in Sprites should always be winded in the correct way so the triangles are front facing (in Unity I think its clockwise) and with the normals pointing towards you when you look at the triangle.

Hey am I right in thinking the verts are rebuilt every frame for a Spin Skeleton in Unity?
Cos I think ideally you wouldnt ever use a negative scale or rotation to flip an animation, but rather generate the vert positions flipped on an axis somehow, maybe with a flag?
So like you could just set a flag on the SkeletonRenderer component to tell it to generate verts flipped horizontally or vertically? Would that make sense?

Are you on a phone?

I guess you haven't been using the actual Spine API much? skeleton.flipX and .flipY are the flags for flipping the vertices. It's actually a real skeleton-logic flip, so actual bone transforms flip too. But the normals pushed are always (0, 0, -1). You've already seen that part of the code.
But yes, vertex positions are changed every frame as long as SkeletonRenderer is enabled. When you disable it, it stops making new verts.

Now that you know that skeleton.flipX and flipY is there. My question was:
Does triangle winding affect something other than backface culling? Something that shaders would use for lighting?
In other words, would lighting work correctly if:
(1) backface culling was turned off,
(2) normals are pointing in the right direction (all 0, 0, -1).
(3) but the triangle verts aren't all winding in the same direction.

Ah that's awesome! Yeah sorry I'm still very new to the API, been mostly learning editor stuff.
I guess I assumed there wasn't a flag already just because there no way of setting it in the editor / on the component. I need to place flipped sprites in the scene view hence flipping them using a negative scale. Can write a wrapper or expose it easily enough though..

Anyways to answer your question!
No I think as long as the normals are correct then it should be ok. From what I saw when looking at the shader code, its only the vertex and normal that matter when lighting, so if they're right should all be good.
Its a bit confusing having normals not matching the winding order of the faces they're for, or having a mixture of different winding orders in the model, but yep should be lit fine (I think!)

It's just that there's currently a not-insignificant amount of code in SkeletonRenderer dedicated to checking flip states and winding the triangles attachments are scaled negatively in Spine. If most people won't need that even for your shaders, I'm considering.... something. Maybe compiler directives. :p

Yeah. There would unfortunately be too many things if we started writing wrappers for everything.
Skeleton posing and skeleton manipulation is under .skeleton (Spine.Skeleton).
Animation control is under .state (Spine.AnimationState)
I personally want to surface a few more things too but it'd be too arbitrary.

You should check the Spine Unity Getting Started sample scenes that comes with the unitypackage.
And also the Spine Unity documentation.


10 Mar 2016 5:00 pm


Just thought you might like to know. Getting an error when I add a diffuse ramp to the vertex lit shader.

Shader error in 'Game/Sprite Vertex Lit': cannot map expression to vs_4_0 instruction set at Assets/Game/Shaders/Sprites/SpriteVertexLighting.cginc(50) (on d3d11)

Compiling Vertex program with _DIFFUSE_RAMP _ALPHAPREMULTIPLY_ON
Platform defines: UNITY_ENABLE_REFLECTION_BUFFERS UNITY_PBS_USE_BRDF1 UNITY_SPECCUBE_BOX_PROJECTION UNITY_SPECCUBE_BLENDING