• RuntimesUnity
  • Custom shader does not work with Spine animation

Hello, i have a problem.
I use "All In 1 Sprite Shader" for changing hue color for my gameObject (made in Spine with spine Skeleton Animation).

When i disable SkeletonAnimation component changing hue color work, but skeleton animation does not work course.

When i enable SkeletonAnimation component changing hue color does not work.

Why Spine Skeleton Animation component work like that and how can i fix it?

Related Discussions
...

We can't tell from the screenshots alone what your problem is.

Are you assigning a different material at the Skeleton? If so, please see the FAQ under "I tried assigning materials at the MeshRenderer, but it is not possible. Why?"
https://esotericsoftware.com/spine-unity#Visual

If you modified the material next to the skeleton assets, it's a different problem. Then we need more information about what the AddAllIn1Shader component does, as it's a thirdparty asset.

  • VOXELIUM がこの投稿に返信しました。

    Harald
    I only changed the shader for this material. This is shown in the screenshot.

    AddAllIn1Shader it adds effects on top of the object's material - glow, hue shift, pixelate, etc

    • Harald がこの投稿に返信しました。

      VOXELIUM I only changed the shader for this material. This is shown in the screenshot.

      Thanks for the clarification. From the screenshot it was not clear whether it was the original material (which is generated upon import) or a copy of it located somewhere else.

      Then the issue seems to be with your thirdparty shader or component.

      VOXELIUM AddAllIn1Shader it adds effects on top of the object's material - glow, hue shift, pixelate, etc

      This description is far too vague for us to be able to help. Does the script replace the material at Edit-Time, and can basically remove it before starting Play mode? Or do you need to have the AddAllIn1Shader component attached and it will apply materials at runtime?

      I recorded a video. It seems to reveal the essence of the problem.
      I have a weapon (made in Spine) with Weapon_01_Material.

      Before launching the game, I disabled SkeletonAnimation because we found out that the SkeletonAnimation component does not allow color changes.
      At the start of a game level, the AllinOneShaders component change color of my weapon using the HueShift effect (I do it using code).
      But when I enable the SkeletonAnimation component during gameplay, it returns the original values for Weapon_01_Material.
      How can I prevent SkeletonAnimation from changing the material?

      @VOXELIUM Thanks for uploading the video. In your video you can see that when you're entering play mode and before enabling the SkeletonAnimation component, the assigned material is Weapon_01_Material (Instance). So any of your components seems to have modified the material and thus created a material instance. After the material was modified and a material instance (clone) is assigned, when the SkeletonAnimation component is activated, it assigns materials based on active attachments, which resets the material to the shared original material in your assets folder.

      Please see the documentation below, especially the "Note" section reading:
      "Note: Direct modifications to the Materials array have no effect "
      https://esotericsoftware.com/spine-unity#Materials
      https://esotericsoftware.com/spine-unity#Changing-Materials-Per-Instance

      VOXELIUM I tried using SkeletonRendererCustomMaterials
      It didn't help

      Then you likely did something wrong.

      Most likely you are modifying the material via code and thus creating the Material instance. If you don't want to prepare material assets and assign them, but instead want to change properties via code at runtime, you should be using MaterialPropertyBlocks. Again, see the documentation on how to do that:
      https://esotericsoftware.com/spine-unity#Changing-Materials-Per-Instance

      • VOXELIUM がこの投稿に返信しました。

        Harald

        Thank you.
        I looked at the information. There are only comments written there. They are probably understandable to an experienced Spine developer. I didn’t find any code examples there on how I can implement SkeletonAnimation.OnMeshAndMaterialsUpdated into my code and then update the material.
        Can you give me some advice?

        Harald

        `using System.Collections;
        using System.Collections.Generic;
        using UnityEngine;
        using Spine.Unity;
        using System;
        using Spine;

        public class WeaponChangeDesign : MonoBehaviour
        {
        [SerializeField] private Weapon weapon;
        private Material matItem;
        private string aOneSpriteFX1 = "_HsvShift";

        private SkeletonAnimation skeletonAnimation;
        private Skeleton skeleton;
        
        void Awake()
        {
            skeletonAnimation = GetComponent<SkeletonAnimation>();
        }
        
        void OnEnable()
        {
            skeletonAnimation.OnMeshAndMaterialsUpdated += ChangeColor;
        }
        
        void Start()
        {
            matItem = GetComponent<MeshRenderer>().material;
        }
        
        private void ChangeColor(SkeletonRenderer skeletonRenderer)
        {
            float hsv;
            int levelForChange = weapon.weaponLevel % 6;
        
            Debug.Log("Change color process " + levelForChange);
        
            switch (levelForChange)
            {
                case 1:
                    hsv = 0f;
                    break;
                case 2:
                    hsv = 60f;
                    break;
                case 3:
                    hsv = 120f;
                    break;
                case 4:
                    hsv = 180f;
                    break;
                case 5:
                    hsv = 240f;
                    break;
                case 0:
                    hsv = 300f;
                    break;
        
                default:
                    hsv = 0;
                    break;
            }
        
            matItem.SetFloat(aOneSpriteFX1, hsv);
        }

        }
        `

        Ok
        I wrote code to call the color change function after OnMeshAndMaterialsUpdated. I see that the ChangeColor function is called every frame, but the color still does not change if Skeleton Animation is enabled

        @VOXELIUM Did you perhaps stop reading after the first paragraph, thinking that the sections is over already? Please note that the section "CustomMaterialOverride and CustomSlotMaterials" and subsequent sub-sections are part of this larger section.

        In my above message I recommended that you use MaterialPropertyBlocks since you mentioned you are setting properties via code. There is a complete subsection with code examples and with a reference to an example scene demonstrating its use that comes with the spine-unity runtime.

        Regarding SkeletonAnimation.OnMeshAndMaterialsUpdated: You can click the method text, it then takes you to the Life Cycle section listing all callback delegates that you can hook your methods up to. I just noticed that the OnMeshAndMaterialsUpdated delegate is not explicitly demonstrated in the code below, and has a slightly different signature than UpdateComplete:

        // your delegate method
        void AfterMeshAndMaterialsUpdated (SkeletonRenderer renderer) {
            // this is called after mesh and materials have been updated.
        }
        
        // register your delegate method
        void Start() {
           skeletonAnimation.OnMeshAndMaterialsUpdated -= AfterMeshAndMaterialsUpdated;
           skeletonAnimation.OnMeshAndMaterialsUpdated += AfterMeshAndMaterialsUpdated;
        }

        I will add this to the documentation, thanks for bringing this to our attention.

        In general it is highly recommended to get familiar with programming basics in C#, like how to use delegates. Also please be sure to understand how Unity Materials behave in general:
        https://forum.unity.com/threads/confused-about-material-instances.402037/#post-2621170
        If you skip the fundamentals, you will run into a lot of frustration down the road.

        • VOXELIUM がこの投稿に返信しました。
          • 編集済み

          Harald

          I don't understand your complaint when I ask you for help.

          Harald I recommended that you use MaterialPropertyBlocks since you mentioned you are setting properties via code.
          There is no description of the solution to my problem. There it is offered.
          That's what it says in the instructions.
          `MaterialPropertyBlock mpb = new MaterialPropertyBlock();
          mpb.SetColor("FillColor", Color.red); // "FillColor" is a named property on the used shader.
          mpb.SetFloat("FillPhase", 1.0f); // "FillPhase" is another named property on the used shader.
          GetComponent<MeshRenderer>().SetPropertyBlock(mpb);

          // to deactivate the override again:
          MaterialPropertyBlock mpb = this.cachedMaterialPropertyBlock; // assuming you had cached the MaterialPropertyBlock
          mpb.Clear();
          GetComponent<Renderer>().SetPropertyBlock(mpb);`

          I do not need it. I need to trigger the color change using the AllinOneShaders component. I want to apply a material to an object that is changed using the AllinOneShaders component.

          Harald skeletonAnimation.OnMeshAndMaterialsUpdated += AfterMeshAndMaterialsUpdated;
          The code you suggested is not much different from the one I wrote. My code works exactly the same as yours.
          Your code also doesn't solve the problem in this thread, so it's pretty inappropriate of you to talk about the need to learn the basics of Unity.
          I'm very disappointed that you can't offer a solution to your product. At the same time, I don’t have to know it - I’m a buyer, and you could understand how to solve the current problem, because you are a Spine developer.
          Sorry for taking up your time.

          p.s

          Harald Also please be sure to understand how Unity Materials behave in general:
          https://forum.unity.com/threads/confused-about-material-instances.402037/#post-2621170
          If you skip the fundamentals, you will run into a lot of frustration down the road.

          This makes no sense at all in this conversation. I understand the difference between Shared Material and Material. And in this problem, this knowledge does not help to prevent the Spine Skeleton Animation component from constantly appropriating old material

          • Harald がこの投稿に返信しました。

            VOXELIUM

            I don't understand your complaint when I ask you for help.

            Sorry if this was coming across as a complaint, it was not intended as such.

            VOXELIUM I do not need it. I need to trigger the color change using the AllinOneShaders component. I want to apply a material to an object that is changed using the AllinOneShaders component.

            Sorry, then I misunderstood the line "I do it using code" in your earlier posting.

            At the start of a game level, the AllinOneShaders component change color of my weapon using the HueShift effect (I do it using code).

            VOXELIUM This makes no sense at all in this conversation. I understand the difference between Shared Material and Material. And in this problem, this knowledge does not help to prevent the Spine Skeleton Animation component from constantly appropriating old material

            This does make sense in that one of your components (likely the AllinOneShaders component, or one of your scripts interacting with the material) is modifying material properties and creating a material instance. I mentioned this since if your components would not create material instances, the problem would not occur at all, as the documentation explains.

            To summarize, the problem seems to be that your AllinOneShaders component is modifying material parameters, creating a material instance. Now it either needs to be avoided that the instance is created in the first place (which I would recommend), or the material instance needs to be assigned after the SkeletonRenderer component updated the materials, by using the OnMeshAndMaterialsUpdated update callback (or by changing your script's exectution order to run after the SkeletonRenderer component).

            In general, why do you need a AllinOneShaders component to just assign material properties and thus create a Material instance, when you could just directly create a material (asset) with the same desired properties?

            • VOXELIUM がこの投稿に返信しました。

              VOXELIUM There is no description of the solution to my problem. There it is offered.
              That's what it says in the instructions.
              `MaterialPropertyBlock mpb = new MaterialPropertyBlock();
              mpb.SetColor("FillColor", Color.red); // "FillColor" is a named property on the used shader.
              mpb.SetFloat("FillPhase", 1.0f); // "FillPhase" is another named property on the used shader.
              GetComponent<MeshRenderer>().SetPropertyBlock(mpb);

              // to deactivate the override again:
              MaterialPropertyBlock mpb = this.cachedMaterialPropertyBlock; // assuming you had cached the MaterialPropertyBlock
              mpb.Clear();
              GetComponent<Renderer>().SetPropertyBlock(mpb);`

              I do not need it. I need to trigger the color change using the AllinOneShaders component. I want to apply a material to an object that is changed using the AllinOneShaders component.

              MaterialPropertyBlock is used to change Material properties, without creating unnecessary Material instances each time, which I thought you would need. I assume that your AllinOneShaders component also just sets material parameters, just the "bad way" which creates a new Material instance with each change.

              Harald
              All In 1 Sprite Shader is a great way to quickly apply great effects to sprites.
              This is a great solution for working quickly and doing it beautifully.
              In comparison, using MaterialPropertyBlock is like walking around with Nokia210 when people use iPhone 15 and wear virtual glasses.
              I found a solution to the problem - change the AllinOneShaders parameter through the animator.
              It's a shame that Spine doesn't allow a simple solution through code to work with such good tools as AllinOneShader

              • Harald がこの投稿に返信しました。

                VOXELIUM All In 1 Sprite Shader is a great way to quickly apply great effects to sprites.
                This is a great solution for working quickly and doing it beautifully.

                Things that work quickly and easily are unfortunately not always efficient, flexible or robust. Unfortunately sometimes it takes a few extra steps which might be more uncomfortable to make things work well in a real world scenario. It's like driving in the middle of the road instead of on the right side - as long as there is no traffic on the opposing side it's alright and provides you with much more space.

                VOXELIUM I found a solution to the problem

                Very glad to hear!

                VOXELIUM In comparison, using MaterialPropertyBlock is like walking around with Nokia210 when people use iPhone 15 and wear virtual glasses.

                Please note that we neither designed Unity's material- nor batching system, you could request a better solution on their forums if you're unhappy with the interface.

                VOXELIUM It's a shame that Spine doesn't allow a simple solution through code to work with such good tools as AllinOneShader

                Please note that the spine-unity runtime needs to update materials to match the active attachments. It's common for thirdparty tools to not be compatible out of the box, like when AllinOneShader wants to park in the same parking slot, the one who gets there first wins the race. spine-unity provides three or more ways to assign material properties, if AllinOneShader can do only one which is not compatible, that's a bit unfortunate.

                You could as well argue that it's a shame that such good tools like AllinOneShader don't offer a simple checkbox Use MaterialPropertyBlock to do that all for you behind the scenes, but only rigidly offers creating material instances, which leads to e.g. 10 instead of 1 draw calls when you set the same hue on 10 objects.

                Nevertheless thanks for sharing your problems and insights with us, sorry that it has been frustrating for you. We will have a look whether we can provide some kind of an easy solution for less experienced programmers to check for modified materials and re-apply them automatically.

                • VOXELIUM がこの投稿に返信しました。
                • VOXELIUM が「いいね」しました。

                  Harald
                  Thank you. Our communication was fruitful.