• RuntimesUnity
  • FadeOut with Skeletin Lit ZWrite Shader

Hello,

In Unity I was trying to get a fade out effect similar to the example called "RenderTexture FadeOut Transparency".

However, the example doesn't seem to work if I use the "Skeleton Lit ZWrite" - the model isn't ever shown. I was wondering if anyone had any guidance on how to achieve the same effect when using this shader.

Related Discussions
...

@stbova We just noticed that when having the Spine Preferences setting Fix Prefab Overr. MeshFilter enabled, playing the RenderTexture FadeOut Transparency example scene issues an error of a null reference exception accessing MeshFilter. This leads to the skeleton not being displayed at all then.

We have just released a fix for this issue on the 4.2 branch, a new spine-unity unitypackage is available for download:
https://esotericsoftware.com/spine-unity-download

Issue ticket URL for later reference:
EsotericSoftware/spine-runtimes2608

Did you perhaps encounter any such error messages in the console? If so, please update your spine-unity runtime. If this does not resolve your issue, where did you assign the Skeleton Lit ZWrite shader, at the original skeleton's material or at the render-quad material? Did you receive any error messages in the console?

I received no error messages in the console. I set it on the spineboy-pro_Material. Here's a screenshot of the setting I changed:

And here's what it looks like when I play the scene:

Note that if I disable the Skeleton Render Texture Script the model re-appears, but it obviously has no transparency applied:

@stbova Are you perhaps using Universal Render Pipeline (URP) instead of the Built-in Render Pipeline? Then of course you have to use the respective URP shaders (see the documentation here).

@stbova Sorry, I just noticed that this issue indeed occurs due to the multiple shader passes causing problems. At the SkeletonRenderTexture component you need to set the first element in the Shader Passes array to 0 to draw the first ("Normal") pass only. If it's set to the default -1 to draw all passes, the second shadow-caster pass is causing problems it seems.

We have just changed the default value (in this commit on the 4.2 branch) from -1 (all passes) to 0 to only draw the first (normal) pass by default on newly created SkeletonRenderTexture components. We've also updated the example scene accordingly to use 0 as well. Existing components in scenes and prefabs are unaffected by this change.

A new spine-unity 4.2 unitypackage is available for download here as usual:
https://esotericsoftware.com/spine-unity-download

    Harald Thanks!

    However, now when I run the scene it seems like the light isn't being applied to the model so while it is showing up now, it is darker than it should be. I assume that's because it's skipping the pass that applies the lighting? I've included an attached screenshot for reference.

      stbova I assume that's because it's skipping the pass that applies the lighting?

      No, there is only one lit pass used at this shader. The problem is that unfortunately lighting data is not automatically forwarded to CommandBuffer.DrawMesh: "Note that the rendered mesh will not have any lighting related shader data (light colors, directions, shadows, light and reflection probes etc.) set up. If the shader used by the material uses any lighting related variables, the results are undefined."
      In this regard, the CommandBuffer API is unfortunately very limited.

      So unfortunately it seems that some vertex-lit Spine shaders are unlucky in this regard (Spine/Skeleton Lit and Spine/Sprite/Vertex Lit) while the pixel-lit shader Spine/Sprite/Pixel Lit luckily receives lighting data accordingly, which might not be reliable though.

      A safe way should theoretically be to manually set the lighting data as parameters. Unfortunately we could not get this to work using the MaterialPropertyBlock even when setting params shown in the Frame Debugger. The following code did not work as desired, sharing in case anyone knows a solution to this:

      protected virtual void SetupLightingData () {
      	if (lights == null || lights.Length == 0) return;
      
      	commandBuffer.SetGlobalVector("_WorldSpaceCameraPos", Camera.main.transform.position);
      
      	Light mainLight = lights[0];
      	commandBuffer.SetGlobalVector("_WorldSpaceLightPos0", mainLight.transform.forward * -1.0f);
      	commandBuffer.SetGlobalVector("_LightColor0", mainLight.transform.forward * -1.0f);
      
      	for (int i = 0; i < Mathf.Min(4, lights.Length); i++) {
      		Light light = lights[i];
      		bool isDirectional = light.type == LightType.Directional;
      		Vector3 lightPos = isDirectional ? light.transform.forward * -1.0f : light.transform.position;
      		lightPositions[i] = new Vector4(lightPos.x, lightPos.y, lightPos.z, isDirectional ? 0.0f : 1.0f);
      		lightColor[i] = light.color * light.intensity;
      	}
      	commandBuffer.SetGlobalVectorArray("unity_LightPosition0", lightPositions);
      	commandBuffer.SetGlobalVectorArray("unity_LightColor0", lightColor);
      	commandBuffer.SetGlobalVector("unity_VertexLightParams", new Vector4(1f, 0f, 1f, 0f));
      }

      Another way could be to replace Graphics.ExecuteCommandBuffer(commandBuffer); with adding the CommandBuffer into the render-queue at a different location:

      void OnEnable () {
         Camera.main.AddCommandBuffer(CameraEvent.BeforeForwardOpaque, commandBuffer);
         ...
      }
      
      void OnDisable () {
         Camera.main.RemoveCommandBuffer(CameraEvent.BeforeForwardOpaque, commandBuffer);
         ...
      }

      Note that this is still using undefined behaviour, so not really a safe solution. It works to some extent, but if you pause the game in play-mode you will see some incorrect rendering happening.

      The safest solutions for lit skeletons would be:

      • a) Use an unlit shader at the skeleton and then use a pixel-lit shader as RenderQuad material. This way you receive lighting at the later stage, but it would be correct and reliable.
      • b) To create a separate Camera which renders to a RenderTexture instead of using the SkeletonRenderTexture component. This should apply lighting as desired, but you lose any layout and pixel-perfect benefits.

        Harald shaders go a bit over my head, but I was seemingly able to fix it by just modifying the RenderQuadShader to have "Lighting On" instead of "Lighting Off". I also had to change ZWrite to "On", otherwise it wasn't properly being drawn behind/in front of things. If I do this then I can also have shaderPasses be { 0 } rather than { -1 }.

        Thanks for all your help!

          @stbova Glad to hear you've figured it out, thanks for getting back to us.

          stbova If I do this then I can also have shaderPasses be { 0 } rather than { -1 }.

          If the shader used at the skeleton to render to the RenderTexure has e.g. a shadow pass, then you must use only pass 0 instead of drawing all passes with -1, it's not really an option. This does not change with lit vs. unlit shaders at the render quad.