• 中文
  • UGUI下的SkeletonGraphic效果不正确且有闪烁(对比Unity2D)

测试环境
UnityVersion:2021.3.0f1
UrpVersion:12.1.6
ColorSpace:Linear
SpineRuntimeVersion: 4.0.0+

问题:

动画资源文件有一个关于color alpha的关键帧,我们在导入Unity之后,发现在UGUI下SkeletonGraphic显示的效果有问题。(与之相比,它在Unity2D中的SkeletonAnimation更接近我们想要的真实效果)

特效背后的紫色背光(slot名为E3D-Point134),在slot 透明度接近0.1的时候会出现闪烁的现象,即输入的alpha值更低,反而紫色背光更亮。对比图如下:

尝试解决:

经过查阅代码,发现可能问题是在Spine-SkeletonGraphic-NormalPass.cginc的顶点着色器中

VertexOutput vert (VertexInput IN) {
   VertexOutput OUT;

   UNITY_SETUP_INSTANCE_ID(IN);
   UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(OUT);

   OUT.worldPosition = IN.vertex;
   OUT.vertex = UnityObjectToClipPos(OUT.worldPosition);
   OUT.texcoord = IN.texcoord;

   #ifdef UNITY_HALF_TEXEL_OFFSET
   OUT.vertex.xy += (_ScreenParams.zw-1.0) * float2(-1,1);
   #endif

#ifdef _CANVAS_GROUP_COMPATIBLE
   half4 vertexColor = IN.color;
   // CanvasGroup alpha sets vertex color alpha, but does not premultiply it to rgb components.
   vertexColor.rgb *= vertexColor.a;
   // Unfortunately we cannot perform the TargetToGamma and PMAGammaToTarget transformations,
   // as these would be wrong with modified alpha.
#else
   // Note: CanvasRenderer performs a GammaToTargetSpace conversion on vertex color already,
   // however incorrectly assuming straight alpha color.
   float4 vertexColor = PMAGammaToTargetSpace(half4(TargetToGammaSpace(IN.color.rgb), IN.color.a));
#endif
   OUT.color = vertexColor * float4(_Color.rgb * _Color.a, _Color.a); // Combine a PMA version of _Color with vertexColor.
   return OUT;
}
float4 vertexColor = PMAGammaToTargetSpace(half4(TargetToGammaSpace(IN.color.rgb), IN.color.a));

可能的问题:

我们猜想是这两次颜色空间的转换可能在IN.color.a小于0.12(大致范围)的时候,发生了一些问题,导致alpha越小,最后输出的颜色反而越亮。但是通过代码并没有发现具体的问题所在。因此我们尝试在这里做一些强制的限制,发现闪烁的情况消失了。

float4 vertexColor = PMAGammaToTargetSpace(half4(TargetToGammaSpace(IN.color.rgb), IN.color.a));
if(IN.color.a<=0.12){
  vertexColor = IN.color;
}

当然代价是,alpha小于0.12的时候,颜色的转换是有问题的。这也是不正确的。

求助:

请问这个问题是否是因为本身这个转换算法会带来精度问题?还是因为我们忽略了某些设置?以下是SkeletonGraphic以及资产中SkeletonData的设置

希望您可以下载这里的动画资产文件,您会发现动画末尾的紫色背光会有闪烁现象。如果不太明显,可以将动画速率调整至原来的0.3倍左右,这样能够比较清晰地看到这个问题。

动画资产文件:
pt_biaoji.zip

不胜感激!

Related Discussions
...

不幸的是,我很难分析您的资产,因为它看起来一开始就显示错误 - 我看到看起来像错误应用的混合模式或 alpha 的矩形形状:

然而,当您遇到闪烁问题时,您的所有子 Renderer0-RendererN 对象是否在CanvasRenderer 组件中禁用了Cull Transparent Mesh? 否则,由于 color.alpha 为 0,可能会完全删除附加附件。

如果设置正确,您能否向我们发送一个仍然显示此问题的最小 Unity 项目? 您可以将其作为 zip 包发送至 contact@esotericsoftware.com,并简要提及此论坛主题 URL,以便我们了解上下文。


Unfortunately I'm having a hard time analysing your asset, since it looks like it's displaying wrong to begin with - I see rectangular shapes which look like incorrectly applied blend mode or alpha:

[see screenshot above]

Nevertheless, when you encounter flickering issues, do all your child Renderer0-RendererN objects have Cull Transparent Mesh disabled at the CanvasRenderer componet? Otherwise additive attachments might be removed completely due to color.alpha being 0.

If it's set up correctly, could you please send us a minimal Unity project that still shows this issue? You can send it as a zip package to contact@esotericsoftware.com, briefly mentioning this forum thread URL so that we know the context.

Harald a écrit

感谢您的回复!

对于无法正确显示动画资产的问题,可能是因为并未设置Straight Alpha Preset,如下图:

另外设置了之后需要删除资产重新导入

除此之外我还勾选了贴图的Alpha is Transparency选项,您可以在我的范例工程中看到这些。

范例的最小化工程:

spineError.zip

不胜感激!

非常感谢提供复制包! 正如您在上面已经怀疑的那样,这个问题实际上是一个数字问题。 已在 4.1 分支上发布了一个错误修复,一个新的 4.1 spine-unity unitypackage 可供下载:
spine-unity 下载

不幸的是,我们不能在 4.0 分支上集成这个提交,因为这可能会改变现有 4.0 项目中的行为,因此太危险了。 我们会要求您将以下提交集成到您的 4.0 安装中以应用错误修复:
https://github.com/EsotericSoftware/spine-runtimes/commit/2b28510acd8d0b65b681f9a58ca25922fd44b4bb
请让我们知道这是否也为您解决了问题。

出票网址供以后参考:
https://github.com/EsotericSoftware/spine-runtimes/issues/2128

再次感谢您的报告!


Thanks very much for providing the reproduction package! The issue turned out to really be a numerical issue, as you have already suspected above. A bugfix has been released on the 4.1 branch, a new 4.1 spine-unity unitypackage is available for download:
spine-unity 下载

Unfortunately we can't integrate this commit on the 4.0 branch, since this might change behaviour in existing 4.0 projects and is therefore too dangerous. We would ask you to please integrate the following commit in your 4.0 installation to apply the bugfix:
https://github.com/EsotericSoftware/spine-runtimes/commit/2b28510acd8d0b65b681f9a58ca25922fd44b4bb
Please let us know if this fixes the issue for you as well.

Issue ticket URL for later reference:
https://github.com/EsotericSoftware/spine-runtimes/issues/2128

Thanks again for reporting!

Harald a écrit

非常感谢提供复制包! 正如您在上面已经怀疑的那样,这个问题实际上是一个数字问题。 已在 4.1 分支上发布了一个错误修复,一个新的 4.1 spine-unity unitypackage 可供下载:
spine-unity 下载

不幸的是,我们不能在 4.0 分支上集成这个提交,因为这可能会改变现有 4.0 项目中的行为,因此太危险了。 我们会要求您将以下提交集成到您的 4.0 安装中以应用错误修复:
https://github.com/EsotericSoftware/spine-runtimes/commit/2b28510acd8d0b65b681f9a58ca25922fd44b4bb
请让我们知道这是否也为您解决了问题。

出票网址供以后参考:
https://github.com/EsotericSoftware/spine-runtimes/issues/2128

再次感谢您的报告!


Thanks very much for providing the reproduction package! The issue turned out to really be a numerical issue, as you have already suspected above. A bugfix has been released on the 4.1 branch, a new 4.1 spine-unity unitypackage is available for download:
spine-unity 下载

Unfortunately we can't integrate this commit on the 4.0 branch, since this might change behaviour in existing 4.0 projects and is therefore too dangerous. We would ask you to please integrate the following commit in your 4.0 installation to apply the bugfix:
https://github.com/EsotericSoftware/spine-runtimes/commit/2b28510acd8d0b65b681f9a58ca25922fd44b4bb
Please let us know if this fixes the issue for you as well.

Issue ticket URL for later reference:
https://github.com/EsotericSoftware/spine-runtimes/issues/2128

Thanks again for reporting!

非常感谢您的回复!

我们确实发现了这个问题。我们也是在转换颜色中间做了一次saturate运算,但是我们普遍认为这不是一个完美的解决方案 😢 ,因为我们认为这从原理上来说是不对的。但是它确实解决了我们的问题,它停止了闪烁。

问题的根源似乎是在alpha值特别小的时候(通常在0.12以下),因为默认情况下SkeletonGraphic会勾选PMA Vertex Color,然后因为项目使用Linear颜色空间,导致了Canvas Renderer在应用阶段提交顶点颜色的之前做了Gamma To Linear的颜色空间转换。这样在Vertex着色器阶段必须要做一次Linear To Gamma转换来得到原本的预乘之后的颜色,然后再做一次GammaToLinear的转换来得到linear颜色空间的颜色来进行后续的计算。问题就在于应用顶点提交阶段所能得到的最小非0浮点数是1/255,这导致了后续转换的错误。这是目前我们对于这个问题的所有理解和猜想。

我们想到了另一个解决方案,就是暂时不使用PMA Vertex Color,这样顶点颜色是正确转换传进Vertex Shader的,然后我们直接用alpha来在Vertex Shader下做PMA,就像你们在面对有Canvas Group组件需要调整alpha值时所作的那样。如下所示:

    // Note: CanvasRenderer performs a GammaToTargetSpace conversion on vertex color already,
    // however incorrectly assuming straight alpha color.
+   float4 vertexColor;
+   #ifdef _SPNIE_CLOSE_PMA
+      vertexColor = half4(IN.color.rgb*IN.color.a, IN.color.a);
+   #else
+      vertexColor = PMAGammaToTargetSpace(half4(TargetToGammaSpace(IN.color.rgb), IN.color.a));
+   #endif

当然理论上来说,这应该会导致一些颜色空间上的差异,也许会导致一些混合上的问题,但是我们认为可能这样是一个更优的选择。因为如果选择了使用PMA Vertex Color,会导致无法使用颜色叠加模式(Addictive Blend Mode)。这在效果上几乎是可接受的。请问相比之下这是一个更加可行的思路和解决方案吗?

再次感谢您的回复,祝您工作顺利,生活愉快,武运昌隆!

Cheers!

虽然添加的 saturate 调用不会神奇地使数值不精确度完美,但它至少可以改善超出有效 [0,1] 范围的 nonPMAGamma.rgb 值(如 gammaPMAColor.rgb / gammaPMAColor.a <= 1.0非添加剂 PMA 颜色)。不幸的是,我想不出在线性色彩空间中启用PMA Vertex Color的代码路径的完美解决方案。但是,由于只有非常低的不透明度值会触发数值问题(您提到的 1/255 PMA rgb 值,较低的 rgb*a 值到达顶点着色器时为 0),我认为这应该不会太成问题.我假设因为应该在输出上保持输入值的升序,较低的输入值会导致较低的输出值而不会将 1、2、3 更改为 1、3、2。或者您认为在某些情况下它会使情况变得更糟?如果是这样,请告诉我们您的想法(也描述输入 rgba 颜色值)。

一般来说,您是正确的,不幸的是 PMA 将低于 1/255 的 PMA 值丢弃为 0,其中直接 alpha 顶点颜色仍然可以将它们传输到顶点着色器。就像例如r*a = 10/255 * 10/255。这些值在 gamma 空间中无关紧要,因为在输出合并阶段,当 0.39/255 将作为 0 混合到目标缓冲区时,它们会产生 0 影响。虽然这些值可以在浮点缓冲区的线性空间中相加,请考虑低伽马空间值在转换为线性空间值后会导致更低的值。例如。 0.39/255 gamma 等于 6.47e-7 或 0.000165/255 线性,而 1/255 gamma 的下一个可显示(8 位)更高的 gamma 值是线性空间中的 5.08e-6 或 0.0013/255。我认为这实际上永远不会成为一个真正的问题。理论上在 10 位显示设备上可能是这样,但前提是整个场景完全黑暗。

不幸的是,您提到的其他解决方案都有其缺点(性能成本),因此我们不想简单地强迫用户使用其他代码路径:
1) 您当然可以使用直接 alpha 并禁用PMA Vertex Color,但是这将需要对附加插槽进行额外的绘制调用。
2)您可以使用tint black``canvas group compatible工作流程单独传递alpha值。不幸的是,这为目标画布上所有传入的网格顶点数据添加了一个顶点属性,需要“画布”“附加着色器通道”。在某些情况下这可能可以忽略不计(或者它可能已经在使用中),但对于其他一些情况可能非常糟糕。

总而言之,不幸的是,我知道没有涵盖所有情况且没有缺点的完美解决方案。 PMA Vertex Color 可能不是您情况的最佳解决方案,因此您可能希望使用所讨论的任何其他解决方案。

如果我忘记了什么或误解了你,请告诉我,如果我写的任何东西不清楚并且在机器翻译中丢失了。我们很乐意尽可能解决任何问题。

也感谢您的客气话! 🙂 我也祝愿您、您的项目、您的团队和您的家人一切顺利! 🙂


While the added saturate call not magically makes the numerical imprecisions perfect, it at least improves nonPMAGamma.rgb values that exceed the valid [0,1] range (as gammaPMAColor.rgb / gammaPMAColor.a <= 1.0 for non-additive PMA colors). Unfortunately I cannot think of a perfect solution for the PMA Vertex Color enabled codepath in linear color space. However, as only very low opacity values are triggering the numerical issue (1/255 PMA rgb values as you mentioned, with lower rgb*a values arriving at the vertex shader as 0), I don't think that this should be too problematic. I assume that because ascending order of input values should be maintained on the output, lower input values resulting in lower output values without changing 1,2,3 to 1, 3, 2. Or do you think it makes the situation worse for some cases? If so, please let us know what situation you have in mind (also describing input rgba color values).

In general you are correct that unfortunately PMA is discarding PMA values below 1/255 as 0 where straight alpha vertex color can still transfer them to the vertex shader. Like e.g. r*a = 10/255 * 10/255. Such values didn't matter in gamma space, since they would have 0 effect during the output merger stage when 0.39/255 would be blended to the destination buffer as 0. While these values could add up in linear space in a floating point buffer, please consider that low gamma space values result in even much lower values after being converted to linear space values. E.g. 0.39/255 gamma being equal to 6.47e-7 or 0.000165/255 linear while the next displayable (8bit) higher gamma value of 1/255 gamma is 5.08e-6 or 0.0013/255 in linear space. I assume that this will in reality not ever be a real problem. Theoretically on 10 bit display devices it could be, however only if the whole scene is completely dark.

The other solutions that you mentioned unfortunately all have their drawbacks (performance cost), so we do not want to simply force the user to use another code path:
1) You can use straight alpha of course and disable PMA Vertex Color, this however will then require additional draw calls for additive slots.
2) You can use the tint black canvas group compatible workflow to pass the alpha value separately. Unfortunately this comes with an added vertex attribute for all passed in mesh vertex data at the target Canvas, requiring the Canvas Additional shader channel. This may be negligible in some cases (or it may be in use already), but may be very bad for some other scenarios.

So to summarize, unfortunately I know of no perfect solution that covers all cases and has no drawbacks. PMA Vertex Color may of not be the best solution for your case, so you might want to use any of the other solutions discussed.

If I forgot something or misunderstood you, please let me know, also if anything that I wrote is unclear and lost in machine translation. We are happy to fix any issues wherever possible.

Thanks also for your kind words! 🙂 I also wish you all the best for you, your project, your team and your families! 🙂