修复shader问题

This commit is contained in:
ww-rm
2025-04-04 11:26:23 +08:00
parent cc7beb7670
commit 6994fa6be8
9 changed files with 147 additions and 126 deletions

View File

@@ -7,36 +7,86 @@ using System.Threading.Tasks;
namespace SpineViewer.Spine
{
/// <summary>
/// SFML 混合模式
/// SFML 混合模式, 预乘模式下输入和输出的像素值都是预乘的
/// </summary>
public static class BlendModeSFML
{
/// <summary>
/// Alpha Blend
/// <code>
/// res.c = src.c * src.a + dst.c * (1 - src.a)
/// res.a = src.a * 1 + dst.a * (1 - src.a)
/// </code>
/// </summary>
public static SFML.Graphics.BlendMode Normal = SFML.Graphics.BlendMode.Alpha;
///// <summary>
///// Normal Blend, 无预乘, 仅在 dst.a 是 1 时得到正确结果, 其余情况是有偏结果
///// <para>当 <c>src.c &lt; dst.c</c> 时, 结果偏大, 例如 src 是半透明纯黑, dst 是全透明纯白</para>
///// <para>当 <c>src.c &gt; dst.c</c> 时, 结果偏小, 例如 src 是半透明纯白, dst 是全透明纯黑</para>
///// <code>
///// res.c = src.c * src.a + dst.c * (1 - src.a)
///// res.a = src.a * 1 + dst.a * (1 - src.a)
///// </code>
///// </summary>
//public static SFML.Graphics.BlendMode Normal = new(
// SFML.Graphics.BlendMode.Factor.SrcAlpha,
// SFML.Graphics.BlendMode.Factor.OneMinusSrcAlpha,
// SFML.Graphics.BlendMode.Equation.Add,
// SFML.Graphics.BlendMode.Factor.One,
// SFML.Graphics.BlendMode.Factor.OneMinusSrcAlpha,
// SFML.Graphics.BlendMode.Equation.Add
//);
///// <summary>
///// Additive Blend, 无预乘, 仅在 dst.a 是 1 时得到正确结果, 其余情况是有偏结果
///// <para>当 <c>src.a + dst.a >= 1</c> 时, 结果偏大, 例如 src 是不透明纯黑, dst 是全透明纯白</para>
///// <para>当 <c>src.a + dst.a &lt; 1</c> 时, 结果偏差方式类似 <see cref="Normal"/>, 均可假设 dst 是全透明纯白进行判断</para>
///// <code>
///// res.c = src.c * src.a + dst.c * 1
///// res.a = src.a * 1 + dst.a * 1
///// </code>
///// </summary>
//public static SFML.Graphics.BlendMode Additive = new(
// SFML.Graphics.BlendMode.Factor.SrcAlpha,
// SFML.Graphics.BlendMode.Factor.One,
// SFML.Graphics.BlendMode.Equation.Add,
// SFML.Graphics.BlendMode.Factor.One,
// SFML.Graphics.BlendMode.Factor.One,
// SFML.Graphics.BlendMode.Equation.Add
//);
/// <summary>
/// Additive Blend
/// Normal Blend with PremultipliedAlpha
/// <code>
/// res.c = src.c * src.a + dst.c * 1
/// res.a = src.a * 1 + dst.a * 1
/// [res.c * res.a] = [src.c * src.a] * 1 + [dst.c * dst.a] * (1 - src.a)
/// res.a = src.a * 1 + dst.a * (1 - src.a)
/// </code>
/// </summary>
public static SFML.Graphics.BlendMode Additive = SFML.Graphics.BlendMode.Add;
public static SFML.Graphics.BlendMode NormalPma = new(
SFML.Graphics.BlendMode.Factor.One,
SFML.Graphics.BlendMode.Factor.OneMinusSrcAlpha,
SFML.Graphics.BlendMode.Equation.Add,
SFML.Graphics.BlendMode.Factor.One,
SFML.Graphics.BlendMode.Factor.OneMinusSrcAlpha,
SFML.Graphics.BlendMode.Equation.Add
);
/// <summary>
/// Multiply Blend (PremultipliedAlpha Only)
/// Additive Blend with PremultipliedAlpha
/// <code>
/// [res.c * res.a] = [src.c * src.a] * 1 + [dst.c * dst.a] * 1
/// res.a = src.a * 1 + dst.a * 1
/// </code>
/// </summary>
public static SFML.Graphics.BlendMode AdditivePma = new(
SFML.Graphics.BlendMode.Factor.One,
SFML.Graphics.BlendMode.Factor.One,
SFML.Graphics.BlendMode.Equation.Add,
SFML.Graphics.BlendMode.Factor.One,
SFML.Graphics.BlendMode.Factor.One,
SFML.Graphics.BlendMode.Equation.Add
);
/// <summary>
/// Multiply Blend with PremultipliedAlpha
/// <code>
/// res.c = src.c * dst.c + dst.c * (1 - src.a)
/// res.a = src.a * 1 + dst.a * (1 - src.a)
/// res.a = src.a * 1 + dst.a * (1 - src.a)
/// </code>
/// </summary>
public static SFML.Graphics.BlendMode Multiply = new(
public static SFML.Graphics.BlendMode MultiplyPma = new(
SFML.Graphics.BlendMode.Factor.DstColor,
SFML.Graphics.BlendMode.Factor.OneMinusSrcAlpha,
SFML.Graphics.BlendMode.Equation.Add,
@@ -46,13 +96,13 @@ namespace SpineViewer.Spine
);
/// <summary>
/// Screen Blend (PremultipliedAlpha Only)
/// Screen Blend with PremultipliedAlpha Only
/// <code>
/// res.c = src.c * 1 + dst.c * (1 - src.c) = 1 - [(1 - src.c)(1 - dst.c)]
/// res.a = src.a * 1 + dst.a * (1 - src.a)
/// </code>
/// </summary>
public static SFML.Graphics.BlendMode Screen = new(
public static SFML.Graphics.BlendMode ScreenPma = new(
SFML.Graphics.BlendMode.Factor.One,
SFML.Graphics.BlendMode.Factor.OneMinusSrcColor,
SFML.Graphics.BlendMode.Equation.Add,

View File

@@ -261,6 +261,7 @@ namespace SpineViewer.Spine.Implementations.Spine
{
vertexArray.Clear();
states.Texture = null;
states.Shader = Shader.GetShader(usePremultipliedAlpha);
// 要用 DrawOrder 而不是 Slots
foreach (var slot in skeleton.DrawOrder)
@@ -322,18 +323,13 @@ namespace SpineViewer.Spine.Implementations.Spine
}
// 似乎 2.1.x 也没有 BlendMode
SFML.Graphics.BlendMode blendMode = slot.Data.AdditiveBlending ? BlendModeSFML.Additive : BlendModeSFML.Normal;
SFML.Graphics.BlendMode blendMode = slot.Data.AdditiveBlending ? BlendModeSFML.AdditivePma : BlendModeSFML.NormalPma;
states.Texture ??= texture;
if (states.BlendMode != blendMode || states.Texture != texture)
{
if (vertexArray.VertexCount > 0)
{
if (usePremultipliedAlpha && (states.BlendMode == BlendModeSFML.Normal || states.BlendMode == BlendModeSFML.Additive))
states.Shader = Shader.FragmentShader;
else
states.Shader = null;
// 调试纹理
if (!isDebug || debugTexture)
target.Draw(vertexArray, states);
@@ -377,11 +373,6 @@ namespace SpineViewer.Spine.Implementations.Spine
//clipping.ClipEnd(slot);
}
if (usePremultipliedAlpha && (states.BlendMode == BlendModeSFML.Normal || states.BlendMode == BlendModeSFML.Additive))
states.Shader = Shader.FragmentShader;
else
states.Shader = null;
//clipping.ClipEnd();
// 调试纹理

View File

@@ -208,10 +208,10 @@ namespace SpineViewer.Spine.Implementations.Spine
{
return spineBlendMode switch
{
BlendMode.Normal => BlendModeSFML.Normal,
BlendMode.Additive => BlendModeSFML.Additive,
BlendMode.Multiply => BlendModeSFML.Multiply,
BlendMode.Screen => BlendModeSFML.Screen,
BlendMode.Normal => BlendModeSFML.NormalPma,
BlendMode.Additive => BlendModeSFML.AdditivePma,
BlendMode.Multiply => BlendModeSFML.MultiplyPma,
BlendMode.Screen => BlendModeSFML.ScreenPma,
_ => throw new NotImplementedException($"{spineBlendMode}"),
};
}
@@ -220,6 +220,7 @@ namespace SpineViewer.Spine.Implementations.Spine
{
vertexArray.Clear();
states.Texture = null;
states.Shader = Shader.GetShader(usePremultipliedAlpha);
// 要用 DrawOrder 而不是 Slots
foreach (var slot in skeleton.DrawOrder)
@@ -286,11 +287,6 @@ namespace SpineViewer.Spine.Implementations.Spine
{
if (vertexArray.VertexCount > 0)
{
if (usePremultipliedAlpha && (states.BlendMode == BlendModeSFML.Normal || states.BlendMode == BlendModeSFML.Additive))
states.Shader = Shader.FragmentShader;
else
states.Shader = null;
// 调试纹理
if (!isDebug || debugTexture)
target.Draw(vertexArray, states);
@@ -336,11 +332,6 @@ namespace SpineViewer.Spine.Implementations.Spine
}
clipping.ClipEnd();
if (usePremultipliedAlpha && (states.BlendMode == BlendModeSFML.Normal || states.BlendMode == BlendModeSFML.Additive))
states.Shader = Shader.FragmentShader;
else
states.Shader = null;
// 调试纹理
if (!isDebug || debugTexture)
target.Draw(vertexArray, states);

View File

@@ -178,10 +178,10 @@ namespace SpineViewer.Spine.Implementations.Spine
{
return spineBlendMode switch
{
BlendMode.Normal => BlendModeSFML.Normal,
BlendMode.Additive => BlendModeSFML.Additive,
BlendMode.Multiply => BlendModeSFML.Multiply,
BlendMode.Screen => BlendModeSFML.Screen,
BlendMode.Normal => BlendModeSFML.NormalPma,
BlendMode.Additive => BlendModeSFML.AdditivePma,
BlendMode.Multiply => BlendModeSFML.MultiplyPma,
BlendMode.Screen => BlendModeSFML.ScreenPma,
_ => throw new NotImplementedException($"{spineBlendMode}"),
};
}
@@ -190,6 +190,7 @@ namespace SpineViewer.Spine.Implementations.Spine
{
vertexArray.Clear();
states.Texture = null;
states.Shader = Shader.GetShader(usePremultipliedAlpha);
// 要用 DrawOrder 而不是 Slots
foreach (var slot in skeleton.DrawOrder)
@@ -256,12 +257,6 @@ namespace SpineViewer.Spine.Implementations.Spine
{
if (vertexArray.VertexCount > 0)
{
// XXX: 实测不用设置 sampler2D 的值也正确
if (usePremultipliedAlpha && (states.BlendMode == BlendModeSFML.Normal || states.BlendMode == BlendModeSFML.Additive))
states.Shader = Shader.FragmentShader;
else
states.Shader = null;
// 调试纹理
if (!isDebug || debugTexture)
target.Draw(vertexArray, states);
@@ -307,11 +302,6 @@ namespace SpineViewer.Spine.Implementations.Spine
}
clipping.ClipEnd();
if (usePremultipliedAlpha && (states.BlendMode == BlendModeSFML.Normal || states.BlendMode == BlendModeSFML.Additive))
states.Shader = Shader.FragmentShader;
else
states.Shader = null;
// 调试纹理
if (!isDebug || debugTexture)
target.Draw(vertexArray, states);

View File

@@ -184,10 +184,10 @@ namespace SpineViewer.Spine.Implementations.Spine
{
return spineBlendMode switch
{
BlendMode.Normal => BlendModeSFML.Normal,
BlendMode.Additive => BlendModeSFML.Additive,
BlendMode.Multiply => BlendModeSFML.Multiply,
BlendMode.Screen => BlendModeSFML.Screen,
BlendMode.Normal => BlendModeSFML.NormalPma,
BlendMode.Additive => BlendModeSFML.AdditivePma,
BlendMode.Multiply => BlendModeSFML.MultiplyPma,
BlendMode.Screen => BlendModeSFML.ScreenPma,
_ => throw new NotImplementedException($"{spineBlendMode}"),
};
}
@@ -196,6 +196,7 @@ namespace SpineViewer.Spine.Implementations.Spine
{
vertexArray.Clear();
states.Texture = null;
states.Shader = Shader.GetShader(usePremultipliedAlpha);
// 要用 DrawOrder 而不是 Slots
foreach (var slot in skeleton.DrawOrder)
@@ -262,12 +263,6 @@ namespace SpineViewer.Spine.Implementations.Spine
{
if (vertexArray.VertexCount > 0)
{
// XXX: 实测不用设置 sampler2D 的值也正确
if (usePremultipliedAlpha && (states.BlendMode == BlendModeSFML.Normal || states.BlendMode == BlendModeSFML.Additive))
states.Shader = Shader.FragmentShader;
else
states.Shader = null;
// 调试纹理
if (!isDebug || debugTexture)
target.Draw(vertexArray, states);
@@ -313,11 +308,6 @@ namespace SpineViewer.Spine.Implementations.Spine
}
clipping.ClipEnd();
if (usePremultipliedAlpha && (states.BlendMode == BlendModeSFML.Normal || states.BlendMode == BlendModeSFML.Additive))
states.Shader = Shader.FragmentShader;
else
states.Shader = null;
// 调试纹理
if (!isDebug || debugTexture)
target.Draw(vertexArray, states);

View File

@@ -180,10 +180,10 @@ namespace SpineViewer.Spine.Implementations.Spine
{
return spineBlendMode switch
{
BlendMode.Normal => BlendModeSFML.Normal,
BlendMode.Additive => BlendModeSFML.Additive,
BlendMode.Multiply => BlendModeSFML.Multiply,
BlendMode.Screen => BlendModeSFML.Screen,
BlendMode.Normal => BlendModeSFML.NormalPma,
BlendMode.Additive => BlendModeSFML.AdditivePma,
BlendMode.Multiply => BlendModeSFML.MultiplyPma,
BlendMode.Screen => BlendModeSFML.ScreenPma,
_ => throw new NotImplementedException($"{spineBlendMode}"),
};
}
@@ -192,6 +192,7 @@ namespace SpineViewer.Spine.Implementations.Spine
{
vertexArray.Clear();
states.Texture = null;
states.Shader = Shader.GetShader(usePremultipliedAlpha);
// 要用 DrawOrder 而不是 Slots
foreach (var slot in skeleton.DrawOrder)
@@ -258,12 +259,6 @@ namespace SpineViewer.Spine.Implementations.Spine
{
if (vertexArray.VertexCount > 0)
{
// XXX: 实测不用设置 sampler2D 的值也正确
if (usePremultipliedAlpha && (states.BlendMode == BlendModeSFML.Normal || states.BlendMode == BlendModeSFML.Additive))
states.Shader = Shader.FragmentShader;
else
states.Shader = null;
// 调试纹理
if (!isDebug || debugTexture)
target.Draw(vertexArray, states);
@@ -309,11 +304,6 @@ namespace SpineViewer.Spine.Implementations.Spine
}
clipping.ClipEnd();
if (usePremultipliedAlpha && (states.BlendMode == BlendModeSFML.Normal || states.BlendMode == BlendModeSFML.Additive))
states.Shader = Shader.FragmentShader;
else
states.Shader = null;
// 调试纹理
if (!isDebug || debugTexture)
target.Draw(vertexArray, states);

View File

@@ -180,10 +180,10 @@ namespace SpineViewer.Spine.Implementations.Spine
{
return spineBlendMode switch
{
BlendMode.Normal => BlendModeSFML.Normal,
BlendMode.Additive => BlendModeSFML.Additive,
BlendMode.Multiply => BlendModeSFML.Multiply,
BlendMode.Screen => BlendModeSFML.Screen,
BlendMode.Normal => BlendModeSFML.NormalPma,
BlendMode.Additive => BlendModeSFML.AdditivePma,
BlendMode.Multiply => BlendModeSFML.MultiplyPma,
BlendMode.Screen => BlendModeSFML.ScreenPma,
_ => throw new NotImplementedException($"{spineBlendMode}"),
};
}
@@ -192,6 +192,7 @@ namespace SpineViewer.Spine.Implementations.Spine
{
vertexArray.Clear();
states.Texture = null;
states.Shader = Shader.GetShader(usePremultipliedAlpha);
// 要用 DrawOrder 而不是 Slots
foreach (var slot in skeleton.DrawOrder)
@@ -258,12 +259,6 @@ namespace SpineViewer.Spine.Implementations.Spine
{
if (vertexArray.VertexCount > 0)
{
// XXX: 实测不用设置 sampler2D 的值也正确
if (usePremultipliedAlpha && (states.BlendMode == BlendModeSFML.Normal || states.BlendMode == BlendModeSFML.Additive))
states.Shader = Shader.FragmentShader;
else
states.Shader = null;
// 调试纹理
if (!isDebug || debugTexture)
target.Draw(vertexArray, states);
@@ -309,11 +304,6 @@ namespace SpineViewer.Spine.Implementations.Spine
}
clipping.ClipEnd();
if (usePremultipliedAlpha && (states.BlendMode == BlendModeSFML.Normal || states.BlendMode == BlendModeSFML.Additive))
states.Shader = Shader.FragmentShader;
else
states.Shader = null;
// 调试纹理
if (!isDebug || debugTexture)
target.Draw(vertexArray, states);

View File

@@ -180,10 +180,10 @@ namespace SpineViewer.Spine.Implementations.Spine
{
return spineBlendMode switch
{
BlendMode.Normal => BlendModeSFML.Normal,
BlendMode.Additive => BlendModeSFML.Additive,
BlendMode.Multiply => BlendModeSFML.Multiply,
BlendMode.Screen => BlendModeSFML.Screen,
BlendMode.Normal => BlendModeSFML.NormalPma,
BlendMode.Additive => BlendModeSFML.AdditivePma,
BlendMode.Multiply => BlendModeSFML.MultiplyPma,
BlendMode.Screen => BlendModeSFML.ScreenPma,
_ => throw new NotImplementedException($"{spineBlendMode}"),
};
}
@@ -192,6 +192,7 @@ namespace SpineViewer.Spine.Implementations.Spine
{
vertexArray.Clear();
states.Texture = null;
states.Shader = Shader.GetShader(usePremultipliedAlpha);
// 要用 DrawOrder 而不是 Slots
foreach (var slot in skeleton.DrawOrder)
@@ -258,12 +259,6 @@ namespace SpineViewer.Spine.Implementations.Spine
{
if (vertexArray.VertexCount > 0)
{
// XXX: 实测不用设置 sampler2D 的值也正确
if (usePremultipliedAlpha && (states.BlendMode == BlendModeSFML.Normal || states.BlendMode == BlendModeSFML.Additive))
states.Shader = Shader.FragmentShader;
else
states.Shader = null;
// 调试纹理
if (!isDebug || debugTexture)
target.Draw(vertexArray, states);
@@ -309,11 +304,6 @@ namespace SpineViewer.Spine.Implementations.Spine
}
clipping.ClipEnd();
if (usePremultipliedAlpha && (states.BlendMode == BlendModeSFML.Normal || states.BlendMode == BlendModeSFML.Additive))
states.Shader = Shader.FragmentShader;
else
states.Shader = null;
// 调试纹理
if (!isDebug || debugTexture)
target.Draw(vertexArray, states);

View File

@@ -9,19 +9,44 @@ namespace SpineViewer.Spine
public static class Shader
{
/// <summary>
/// 用于解决 PMA 和渐变动画问题的片段着色器
/// 用于非预乘纹理的 fragment shader, 乘上了插值后的透明度用于实现透明度变化(插值预乘), 并且输出预乘后的像素值
/// </summary>
private const string FRAGMENT_SHADER = (
private const string FRAGMENT_VertexAlpha = (
"uniform sampler2D t;" +
"void main() { vec4 p = texture2D(t, gl_TexCoord[0].xy);" +
"if (p.a > 0) p.rgb /= max(max(max(p.r, p.g), p.b), p.a);" +
"void main() { vec4 p = texture(t, gl_TexCoord[0].xy);" +
"p.rgb *= p.a * gl_Color.a;" +
"gl_FragColor = gl_Color * p; }"
);
/// <summary>
/// 针对预乘 Alpha 通道的片段着色器
/// 用于预乘纹理的 fragment shader, 乘上了插值后的透明度用于实现透明度变化(插值预乘)
/// </summary>
public static SFML.Graphics.Shader? FragmentShader { get; private set; }
private const string FRAGMENT_VertexAlphaPma = (
"uniform sampler2D t;" +
"void main() { vec4 p = texture(t, gl_TexCoord[0].xy);" +
"p.rgb *= gl_Color.a;" +
"gl_FragColor = gl_Color * p; }"
);
/// <summary>
/// 预乘转非预乘 fragment shader
/// </summary>
private const string FRAGMENT_PmaInv = (
"uniform sampler2D t;" +
"void main() { vec4 p = texture(t, gl_TexCoord[0].xy);" +
"p.rgb *= gl_Color.a;" +
"gl_FragColor = gl_Color * p; }"
);
/// <summary>
/// 考虑了顶点透明度变化的着色器, 输入是非预乘纹理像素, 输出是预乘像素
/// </summary>
private static SFML.Graphics.Shader? VertexAlpha = null;
/// <summary>
/// 考虑了顶点透明度变化的着色器, 输入和输出均是预乘像素值
/// </summary>
private static SFML.Graphics.Shader? VertexAlphaPma = null;
/// <summary>
/// 加载 Shader, 可能会存在异常导致着色器加载失败
@@ -29,7 +54,21 @@ namespace SpineViewer.Spine
/// <exception cref="SFML.LoadingFailedException"></exception>
public static void Init()
{
FragmentShader = SFML.Graphics.Shader.FromString(null, null, FRAGMENT_SHADER);
VertexAlpha = SFML.Graphics.Shader.FromString(null, null, FRAGMENT_VertexAlpha);
VertexAlphaPma = SFML.Graphics.Shader.FromString(null, null, FRAGMENT_VertexAlphaPma);
}
/// <summary>
/// 获取合适的着色器
/// </summary>
/// <param name="pma">纹理是否是预乘的</param>
/// <param name="twoColor">是否是双色着色的(TODO)</param>
public static SFML.Graphics.Shader? GetShader(bool pma, bool twoColor = false)
{
if (pma)
return VertexAlphaPma;
else
return VertexAlpha;
}
}
}