EsotericSoftware / spine-runtimes

2D skeletal animation runtimes for Spine.
http://esotericsoftware.com/
Other
4.41k stars 2.91k forks source link

[unity] Bug in pixel-lit shader - parts shining through #1335

Closed HaraldCsaszar closed 5 years ago

HaraldCsaszar commented 5 years ago

As reported on the forum: http://esotericsoftware.com/forum/Lit-shader-problem-with-layers-11810

Repro package received via email: FMfcgxwCgLmmGMJhLtFczndPRWbfnZDx

Occurs in pixel-lit Spine shader variants with at least one point light. It seems as if the add-pass does not properly do a depth-test but add the light in occluded parts of a character as well. This leads to an undiscovered shine-through effect.

HaraldCsaszar commented 5 years ago

Potentially similar problem reported here on the forum: http://esotericsoftware.com/forum/Simple-surface-shader-lighting-in-Unity-12307

HaraldCsaszar commented 5 years ago

The problem has the following cause:

Background: As can also be found on the ForwardRendering reference page: Unity's forward renderer of the standard pipeline uses two separate passes:

  1. ForwardBase with Tags { "LightMode" = "ForwardBase " }
  2. ForwardAdd with Tags { "LightMode" = "ForwardAdd" }

The ForwardBase pass applies:

  1. a single directional light per pixel (even a single point light ends up in the add pass unfortunately),
  2. and all vertex lights.

The ForwardAdd passes (after the base pass) apply:

  1. a single per-pixel light via additive blending, on top of the existing base.

The problem: Now if depth-write (zwrite) is disabled on both passes, the add pass will add light to hidden occluded regions.

Why? Imagine the following scenario: Two parts overlap in view-order, zwrite disabled:

  1. The ForwardBase pass writes lit colored pixels of the base-lights for both the further-away and closer part to the screen correctly, but does not write depth to the zbuffer. Until now the color buffer of the screen is correct.
  2. Now the ForwardAdd pass will perform the depth-test (ztest) on the further-away part - it will pass since no zwrite happened in the ForwardBase pass before - thus it applies light to the hidden area and adds the resulting lit color to the screen buffer via additive blending. This part is incorrect and leads to the hidden part "shining through".
  3. The ForwardAdd pass then correctly computes the lit color of the closer part and additively blends it onto the screen. This addition is correct as such.

Solution:

  1. ZWrite has to be enabled and additionally:
  2. There has to be at least some minimal Z-Spacing between parts so that the depth-write really has any blocking effect of overlapping geometry. Z Spacing can be set in every SkeletonRenderer like e.g. SkeletonAnimation inspector, under Advanced - Z Spacing.

Also note that if Z Spacing does not move overlapping parts in Z direction, you will need to visit the Spine Editor to adapt the skeleton and re-export so that the overlapping parts can be on different Z-depth.

Alternative shaders: Spine/Sprite/Vertex Lit shader Note that you can also switch to the Spine/Sprite/Vertex Lit shader which uses the single-pass legacy "Vertex" tag. It works correctly due to the single pass rendering and can apply per-pixel lights. Note however, that the legacy renderer will not be available in more recent Unity versions.

Lightweight Render Pipeline/Spine/Sprite shader You can also use the new LWRP shader, see the Spine Unity download page and this issue ticket: https://github.com/EsotericSoftware/spine-runtimes/issues/1255.

Proposed changes to the Spine-Unity 3.8 runtime

  1. The Spine/Sprite/Pixel Lit shader shall have ZWrite always enabled on the ForwardBase pass.
  2. Additionally, when this shader is detected, minimal Z Spacing shall be either suggested via a Warning-Box or automatically setup if set to 0.0.
HaraldCsaszar commented 5 years ago

Fixed on 3.8-beta branch via above commit.