• Unity
  • Colour different Spine -> Unity (colouring in Unity)

Hi there,

I found this topic on the issue: Color changing from Spine to Unity

However their eventual solution was to change the colour space from Linear to Gamma. However this isn't possible for our project, as it will completely change how the rest of the game looks.

I'm wondering if there is any other way to fix this? I have a greyscale object in Spine that I then colour in Unity, but the colour is far paler than other non-Spine sprites I set to the same colour in Unity.

Any help is greatly appreciate!

Related Discussions
...

This isn't quite a Spine thing as much as it is a Unity thing, as pointed out in that thread.

My hunch is that you may need to switch to shaders that do everything with assumptions relative to linear space colors, and however your images are encoded. If your graphics programmer requires you to do things in linear space, they probably know how to handle this.

Note that it's up to you whether you want to do things PMA or not. Just note that there's a checkbox on Spine rendering components (such as SkeletonAnimation) under "Advanced" where you can choose whether the colors from Spine are applied with premultipled alpha or not. Just choose whatever makes sense in your rendering setup.

Thanks for the reply!

I tried the PMA checkbox, but it isn't set. (I use a separator, could that cause any issues?) I also use the same shader for both Spine and all other sprites in my game, however when set to the same colour, the Spine version is noticeably paler. (They are all grey scale sprites, that are then coloured in Unity.) So it is perhaps not the exact same issue as that other thread, as at grey scale the sprite actually looks correct, it's only when adding colour that it looks paler.

I don't suppose you have any other ideas of what it could be? Adding the Spine atlas as a sprite in the scene with the same shader yields the correct colouring, so I am fairly confident that something is happening inside Spine? I set the colour using:

_baseSlot.SetColor(color); // (_baseSlot is a Slot)

Is there a different way to set it perhaps?

Edit: Though I should note that by default, the atlas doesn't have a "Sprite Mode" set. Though changing this doesn't make a difference.


Hi again,

I tried removing the separator, unfortunately the paleness is still present. I can't figure out why if the sprite sheet itself looks fine, the Spine Animator is so pale?

Can you provide a screen grab or two perhaps to show what you are talking about? I have my suspicions but would like to see exactly what you encountering.

Have you actually confirmed that your project color space is currently linear? Wouldn't you know if that you changed that?
You might be looking for the wrong solutions.

The project space is definitely linear. I'm fairly sure that's not the issue, as like I said: the sprites colour correctly if I use the Spine atlas with the same shader. If it were a colour space issue, wouldn't the atlas and Spine have the same results?

Here's an image of the issue: https://www.dropbox.com/s/k6w82rzk4vg8hta/spine-colour-issue.png
And for completeness, this is what the sprite looks like uncoloured: https://www.dropbox.com/s/le2wltfy4kggi6r/spine-colour-issue-2.png

The top circle is the Spine Skeleton Animation. The bottom is the Spine atlas (set to multiple sprites). Both use the same shader and are coloured using the exact same colour: Color32(66, 104, 214, 255).

But if the problem doesn't exist if your color space is gamma, then it probably is a color space issue.
When you say "same shader", which one do you mean?
I suppose this could be tackled in more than one way.
If it's your own shader, the adjustment in color calculation needs to be made in the right place.

Spine-Unity puts whatever color is in the skeleton and slots into the mesh data as is (with pma applied if the checkbox is checked). It's up to the shader to interpret and blend it with the texture color.

I'm unsure if the issue exists in the gamma colour space, as our project is massive and our textures take about an hour and a half to reimport, so I haven't had the time yet to attempt it.

When I say "same shader" I mean a material using our own custom shader. When applied to the atlas colouring works as expected, when applied to the Skeleton Animation it doesn't.

OK, so it might have something to do with the colour being applied to the mesh, as opposed to the sprites?

Color is stored as vertex colors in the mesh. The shader decides how to use that color to blend it with the texture.
Apart from PMA (which really only makes a difference when the alpha is not 1), nothing's special about how that color is stored.

I suppose an easy way to see if it is an issue with Spine or with your shader is to try one of the Spine shaders, see if the difference is evident, and then make a determination. I have to imagine that if it was a Spine issue there would have been others with this same issue by now.

Are you building your shader by hand or using something like Unity's Shader Graph tool?

That a program uses linear space while Spine editor uses sRGB/gamma color space is enough for the color difference, as linked in the original post. But the Spine components just store the vertex colors as is for use, except for the optional PMA.

It doesn't mean it's a "problem" with Spine, but that there's a mismatch. And it's still up to the shader to handle the blending with whatever storing and rendering scheme, to match how the color space works.

a month later

I finally managed to get someone to take a look at this. It is a result of the different colour spaces, however I'm not convinced that it's not a problem with Spine's Unity runtimes. (Not Spine itself, obviously.)

As I understand it, Unity's SpriteRenderer takes care of any colour space conversion under the hood. So whether you're using gamma or linear, it will represent a Sprite correctly when the colour is set. An important aspect of this is that you can use any shader you'd like with a Unity sprite, and the colour will be represented properly.

As Spine is intended to work in tandem with Unity sprites (I assume?), not doing this is not only disparate from the flow of Unity, but (if solved at the shader level) would require the use of different (or specialised) shaders to account for it. While it's not technically a "bug", I do believe it's an issue.

It can be solved at a code level, using a conversion:

static Color GammaToLinear(Color c){
    return new Color(
        Mathf.Pow(c.r, 2.2f),
        Mathf.Pow(c.g, 2.2f),
        Mathf.Pow(c.b, 2.2f),
        c.a
    );
}

However this comes with its own set of issues. For example, needing to ensure a backwards conversion is done when using GetColor(), as well as maintaining two Color values (one for Sprites, one for Spine). That seems cumbersome, and I don't believe it fits in with the flow of Unity?

But I'm open to other opinions.


I made some extension methods to manage the conversion, it works well and alleviates some of the problems I mentioned at the end:

#region Spine
      public static void SetColor(this Slot slot, Color color, bool colorSpaceConvert) {
         slot.SetColor(colorSpaceConvert && QualitySettings.activeColorSpace == ColorSpace.Linear ? GammaToLinear(color) : color);
      }

  static Color GammaToLinear(Color color){
     return new Color(
        Mathf.Pow(color.r, 2.2f),
        Mathf.Pow(color.g, 2.2f),
        Mathf.Pow(color.b, 2.2f),
        color.a
     );
  }

  public static Color GetColor(this Slot slot, bool colorSpaceConvert) {
     return colorSpaceConvert && QualitySettings.activeColorSpace == ColorSpace.Linear ? LinearToGamma(slot.GetColor()) : slot.GetColor();
  }

  static Color LinearToGamma(Color color){
     return new Color(
        Mathf.Pow(color.r, 0.4545f),
        Mathf.Pow(color.g, 0.4545f),
        Mathf.Pow(color.b, 0.4545f),
        color.a
     );
  }
#endregion

However more generally speaking, I believe Spine should attempt to integrate with Unity as smoothly as it can, which would include replicating Sprite behaviour where possible?

spine-unity's AtlasAsset just references materials, it does not adjust gamma or do any part of loading the images. I'll defer to Pharan, but I believe you tell the texture to load using SRGB or linear (see Disabling sRGB sampling). If your atlas images are linear, uncheck sRGB (Color Texture) so Unity doesn't muck up the colors when it loads the images.

Note that using 2.2 and 0.4545f is an approximation for when the performance difference matters. Here are correct formulas for SRGB <-> linear (IEC 61966-2-1:1999):

static float SrgbLinearToGamma (float srgb) {
   return srgb <= 0.04045f ? srgb / 12.92f : Mathf.Pow((srgb + 0.055f) / 1.055f, 2.4f);
}

static float SrgbGammaToLinear (float linear) {
   return linear <= 0.0031308f ? linear * 12.92f : ((Mathf.Pow(linear, 1f / 2.4f) * 1.055f) - 0.055f);
}

Hi Nate,

Thanks for the reply! Forgive me for not understanding, are you explaining why using the default sprite is akin to a normal Unity sprite?

No, I'm trying to explain that the AtlasAsset simply uses the materials provided to it. 🙂 Unity loads the material images, which have a setting to apply SRGB gamma. Don't apply SRGB gamma to images which use linear colors, else everything after that will be wrong.

In my attempts, toggling the SRGB setting of the sprites didn't seem to have any effect on the colour difference?

6 years later