• Unity
  • How to make TintBlack work with multiply shadow?

I want to perform whole character tint to make it match the stage color.
e.g. brighter character on desert area.

Some of my characters require multiply blend mode for shadows.
The shaders used is auto picked when I dynamically compose the SkeletonAnimation GameObject from code.

When I do

MaterialPropertyBlock mpb = new MaterialPropertyBlock();
mpb.SetColor("_Black", mEditor_TintColor);
                   
skeletonAnimation.GetComponent<MeshRenderer>()?.SetPropertyBlock(mpb);

Most part of the character tint correctly except some shadow parts that use multiply.
It seems to be the shader (Spine/BlendMode/SkeletonPMAMultiply) that don't support tint black.

Any hint how I could to get this to work?
Thanks

Related Discussions
...
  • Изменено
Nick написал

It seems to be the shader (Spine/BlendMode/SkeletonPMAMultiply) that don't support tint black.

Your observations are correct, only the two shaders Skeleton Tint and Skeleton Tint Black support tinting the black value.

Nick написал

Any hint how I could to get this to work?

You can quite easily create your own modified copy (or replacement) of the Skeleton PMA Multiply shader. You just have to add the relevant parts from the Skeleton Tint shader:
https://github.com/EsotericSoftware/spine-runtimes/blob/3.8/spine-unity/Assets/Spine/Runtime/spine-unity/Shaders/Spine-Skeleton-Tint.shader#L10
https://github.com/EsotericSoftware/spine-runtimes/blob/3.8/spine-unity/Assets/Spine/Runtime/spine-unity/Shaders/Spine-Skeleton-Tint.shader#L54
And make sure the black color is applied:
https://github.com/EsotericSoftware/spine-runtimes/blob/3.8/spine-unity/Assets/Spine/Runtime/spine-unity/Shaders/Spine-Skeleton-Tint.shader#L80

Another question though is why you want to tint the black value of a multiply shader used for shadows. You could quite easily use e.g. a white base (texture) color and use the normal color material parameter to tint it in any color you want.

Another question though is why you want to tint the black value of a multiply shader used for shadows. You could quite easily use e.g. a white base (texture) color and use the normal color material parameter to tint it in any color you want.

Can you elaborate more on this? I am not familiar with visual stuff in Unity. I only know how to adjust the "_Color" and the R,G,B,A of the Skeleton class which seems can't make character brighter because the color is already white.

The above text was describing a setup where you use separate Attachment images for the shadow and don't share them with the main part of the character. If you are re-using colored body parts of the character, then this will not be applicable.

If you have a separate shadow Attachment image which is black before tinting, then the above solution can be used to replace yours.
You could then usa a white texture in the same shape as before for the shadow, you can then set the color tint to either RGB (0, 0, 0) black, or a slightly blue dark gray RGB (0.1, 0.1, 0.2f) or any custom color that you want.

So in other words:
A white base texture allows for more tinting flexibility than a gray or black one.

Oh I see. I understand what you mean white base texture now. Unfortunately the shadow I am talking about is not really the shadow of the whole character but an attachment image used within the character. e.g. shadow of cloth on the skin exported from multiply layers of PSD.

In this case I think I may need to try something else.
Do you suggest me to to take the Tint Black route? or is there other better options ?

Nick написал

Oh I see. I understand what you mean white base texture now. Unfortunately the shadow I am talking about is not really the shadow of the whole character but an attachment image used within the character. e.g. shadow of cloth on the skin exported from multiply layers of PSD.

In general I see no technical problem here, you could still make the Attachment image white. I assume that you mean that you don't want to mess up the displayed version in Photoshop, which would then have a white shadow Attachment image (having no effect), right? An admittedly not very clean workaround would be to use a preview (black) and an export version (white) at these shadow layers, e.g. with layer style Color Overlay with white or black color.

Nick написал

Do you suggest me to to take the Tint Black route? or is there other better options ?

I'm not completely sure which way you intended to utilize the Tint Black feature. One that would work is to create a modified Multiply Tint Black copy of the Spine/Skeleton Tint Black shader and change the blend mode statements to Blend DstColor OneMinusSrcAlpha.

In general the Tint Black route is a bit of an overkill here, as you only want to override the color on these shadow attachments to black or another fixed color. I would rather suggest using a custom shader here, you could even write one where you ignore the texture color RGB values entirely (you need the Alpha value though) and overwrite it in the shader with the color value, if you need just a single-color RGB result with varying transparency values.

3 месяца спустя

Thanks
I will do some test and see which option is best for my need.


Harald написал

You can quite easily create your own modified copy (or replacement) of the Skeleton PMA Multiply shader. You just have to add the relevant parts from the Skeleton Tint shader

I finally tried the tint black approach recently. I modified the mentioned 3 lines in the shader but the sprites which use multiply blend mode display pure purple color. Is there something else I need to do to make it work?

Here is the modified shader:

// Spine/Skeleton PMA Multiply
// - single color multiply tint
// - unlit
// - Premultiplied alpha Multiply blending
// - No depth, no backface culling, no fog.
// - ShadowCaster pass

Shader "Spine/Blend Modes/Skeleton PMA Multiply" {
   Properties {
      _Color ("Tint Color", Color) = (1,1,1,1)
      _Black("Dark Color", Color) = (0,0,0,0)
      [NoScaleOffset] _MainTex ("MainTex", 2D) = "black" {}
      [Toggle(_STRAIGHT_ALPHA_INPUT)] _StraightAlphaInput("Straight Alpha Texture", Int) = 0
      _Cutoff ("Shadow alpha cutoff", Range(0,1)) = 0.1
      [HideInInspector] _StencilRef("Stencil Reference", Float) = 1.0
      [HideInInspector][Enum(UnityEngine.Rendering.CompareFunction)] _StencilComp("Stencil Comparison", Float) = 8 // Set to Always as default

  // Outline properties are drawn via custom editor.
  [HideInInspector] _OutlineWidth("Outline Width", Range(0,8)) = 3.0
  [HideInInspector] _OutlineColor("Outline Color", Color) = (1,1,0,1)
  [HideInInspector] _OutlineReferenceTexWidth("Reference Texture Width", Int) = 1024
  [HideInInspector] _ThresholdEnd("Outline Threshold", Range(0,1)) = 0.25
  [HideInInspector] _OutlineSmoothness("Outline Smoothness", Range(0,1)) = 1.0
  [HideInInspector][MaterialToggle(_USE8NEIGHBOURHOOD_ON)] _Use8Neighbourhood("Sample 8 Neighbours", Float) = 1
  [HideInInspector] _OutlineMipLevel("Outline Mip Level", Range(0,3)) = 0
   }

   SubShader {
      Tags { "Queue"="Transparent" "IgnoreProjector"="True" "RenderType"="Transparent" }
      LOD 100

  Fog { Mode Off }
  Cull Off
  ZWrite Off
  Blend DstColor OneMinusSrcAlpha
  Lighting Off

  Stencil {
     Ref[_StencilRef]
     Comp[_StencilComp]
     Pass Keep
  }

  Pass {
     Name "Normal"

     CGPROGRAM
     #pragma shader_feature _ _STRAIGHT_ALPHA_INPUT
     #pragma vertex vert
     #pragma fragment frag
     #include "UnityCG.cginc"
     uniform sampler2D _MainTex;
     uniform float4 _Color;
     uniform float4 _Black;

     struct VertexInput {
        float4 vertex : POSITION;
        float2 uv : TEXCOORD0;
        float4 vertexColor : COLOR;
     };

     struct VertexOutput {
        float4 pos : SV_POSITION;
        float2 uv : TEXCOORD0;
        float4 vertexColor : COLOR;
     };

     VertexOutput vert (VertexInput v) {
        VertexOutput o;
        o.pos = UnityObjectToClipPos(v.vertex);
        o.uv = v.uv;
        o.vertexColor = v.vertexColor * float4(_Color.rgb * _Color.a, _Color.a); // Combine a PMA version of _Color with vertexColor.
        return o;
     }

     float4 frag (VertexOutput i) : SV_Target {
        float4 texColor = tex2D(_MainTex, i.uv);

        #if defined(_STRAIGHT_ALPHA_INPUT)
        texColor.rgb *= texColor.a;
        #endif

        return fragTintedColor(texColor, _Black.rgb, i.vertexColor, _Color.a, _Black.a);
        //return (texColor * i.vertexColor);
     }
     ENDCG
  }

  Pass {
     Name "Caster"
     Tags { "LightMode"="ShadowCaster" }
     Offset 1, 1

     ZWrite On
     ZTest LEqual

     CGPROGRAM
     #pragma vertex vert
     #pragma fragment frag
     #pragma multi_compile_shadowcaster
     #pragma fragmentoption ARB_precision_hint_fastest
     #include "UnityCG.cginc"
     struct v2f {
        V2F_SHADOW_CASTER;
        float4 uvAndAlpha : TEXCOORD1;
     };

     uniform float4 _MainTex_ST;

     v2f vert (appdata_base v, float4 vertexColor : COLOR) {
        v2f o;
        TRANSFER_SHADOW_CASTER(o)
        o.uvAndAlpha.xy = TRANSFORM_TEX(v.texcoord, _MainTex);
        o.uvAndAlpha.z = 0;
        o.uvAndAlpha.a = vertexColor.a;
        return o;
     }

     uniform sampler2D _MainTex;
     uniform fixed _Cutoff;

     float4 frag (v2f i) : SV_Target {
        fixed4 texcol = tex2D(_MainTex, i.uvAndAlpha.xy);
        clip(texcol.a * i.uvAndAlpha.a - _Cutoff);
        SHADOW_CASTER_FRAGMENT(i)
     }
     ENDCG
  }
   }
   CustomEditor "SpineShaderWithOutlineGUI"
}
Nick написал

I modified the mentioned 3 lines in the shader but the sprites which use multiply blend mode display pure purple color. Is there something else I need to do to make it work?

This means that your shader code does not compile because it has compile errors. Please modify the shader code (e.g. add a space somewhere and save the file) so that it's compiled again, then you should receive the error messages in the console, telling you what the problem is.

Problem solved.

The include statement was missing causing fragTintedColor() not compiling.

#include "../CGIncludes/Spine-Skeleton-Tint-Common.cginc"

It is now working correctly.
Thank you~

Very glad to hear you've figured it out, thanks for letting us know!

7 дней спустя

I am getting a warning saying that 'Advanced - Tint Black' is not checked in the setting but the warning is originated from

SkeletonAnimation.NewSkeletonAnimationGameObject()

My guess is it is caused by adding the TintBlack support to the multiply blend mode shader.

Anyway, if tint black is detected inside NewSkeletonAnimationGameObject(), can it automatically enable 'Advanced - Tint Black' so that I don't get this warning? The problem is, I don't even get a chance to set the bool on this simple function call.

This is a valid point. We have added an optional quiet parameter to NewSkeletonAnimationGameObject and Initialize() methods to skip any material checks and warning output.

The issue was tracked under this ticket:
https://github.com/EsotericSoftware/spine-runtimes/issues/1852
This 3.8 commit will be merged to the 4.0-beta branch soon and released alongside the next unitypackage release.
Thanks for reporting!

15 дней спустя

The quiet is set, it will always show the warning...Not quiet at all.

#if UNITY_EDITOR
     if (!Application.isPlaying) {
        string errorMessage = null;
        if (quiet || MaterialChecks.IsMaterialSetupProblematic(this, ref errorMessage))
           Debug.LogWarningFormat(this, "Problematic material setup at {0}: {1}", this.name, errorMessage);
     }
#endif

Oh dear, this if condition was nonsense, thanks for reporting! A bugfix has been pushed to the 3.8 branch, a new 3.8 spine-unity unitypackage is available for download here as usual:
Spine Unity Download
The updates will be pushed to the 4.0-beta branch soon.