diff --git a/osu.Framework.Font.Tests/Resources/Textures/sample-texture.png b/osu.Framework.Font.Tests/Resources/Textures/sample-texture.png new file mode 100644 index 0000000..46daf25 Binary files /dev/null and b/osu.Framework.Font.Tests/Resources/Textures/sample-texture.png differ diff --git a/osu.Framework.Font.Tests/Visual/Shaders/CustomizedShaderTestScene.cs b/osu.Framework.Font.Tests/Visual/Shaders/CustomizedShaderTestScene.cs index 92d18bd..3a378e1 100644 --- a/osu.Framework.Font.Tests/Visual/Shaders/CustomizedShaderTestScene.cs +++ b/osu.Framework.Font.Tests/Visual/Shaders/CustomizedShaderTestScene.cs @@ -2,16 +2,21 @@ // See the LICENCE file in the repository root for full licence text. using NUnit.Framework; +using osu.Framework.Allocation; using osu.Framework.Extensions; using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Font.Tests.Helper; using osu.Framework.Graphics.Shaders; +using osu.Framework.Graphics.Textures; using osuTK; namespace osu.Framework.Font.Tests.Visual.Shaders { public class CustomizedShaderTestScene : ShaderTestScene { + [Resolved] + private TextureStore textures { get; set; } + [TestCase(0, "#FF0000")] [TestCase(10, "#00FF00")] [TestCase(20, "#0000FF")] @@ -90,5 +95,85 @@ public void TestPixelShader(float x, float y) }; }); } + + [TestCase("sample-texture", 10, 10)] + [TestCase("sample-texture", 30, 30)] + [TestCase("sample-texture", 5, 20)] + public void TestRepeatMovingBackgroundShaderDisplaySize(string textureName, float width, float height) + { + AddStep("Apply shader", () => + { + ShaderContainer.Shaders = new[] + { + GetShaderByType().With(s => + { + s.Texture = textures.Get(textureName); + s.TextureDisplaySize = new Vector2(width, height); + }) + }; + }); + } + + [TestCase("sample-texture", 0, 0)] + [TestCase("sample-texture", 10, 10)] + [TestCase("sample-texture", 5, 20)] + [TestCase("sample-texture", 30, 30)] + public void TestRepeatMovingBackgroundShaderBorder(string textureName, float width, float height) + { + AddStep("Apply shader", () => + { + ShaderContainer.Shaders = new[] + { + GetShaderByType().With(s => + { + s.Texture = textures.Get(textureName); + s.TextureDisplaySize = new Vector2(30); + s.TextureDisplayBorder = new Vector2(width, height); + }) + }; + }); + } + + [TestCase(0, 0)] + [TestCase(1, 1)] + [TestCase(0.1f, 0.1f)] + [TestCase(5, 2)] + public void TestRepeatMovingBackgroundShaderSpeed(float xSpeed, float ySpeed) + { + AddStep("Apply shader", () => + { + ShaderContainer.Shaders = new[] + { + GetShaderByType().With(s => + { + s.Texture = textures.Get("sample-texture"); + s.TextureDisplaySize = new Vector2(30); + s.Speed = new Vector2(xSpeed, ySpeed); + }) + }; + }); + } + + [TestCase(0)] + [TestCase(0.5f)] + [TestCase(1f)] + [TestCase(-1)] // invalid + [TestCase(3)] // invalid + public void TestRepeatMovingBackgroundShaderMix(float mix) + { + AddStep("Apply shader", () => + { + ShaderContainer.Shaders = new[] + { + GetShaderByType().With(s => + { + s.Texture = textures.Get("sample-texture"); + s.TextureDisplaySize = new Vector2(30); + s.Speed = new Vector2(1); + s.Mix = mix; + }) + }; + }); + } } } diff --git a/osu.Framework.Font/Graphics/Shaders/RepeatMovingBackgroundShader.cs b/osu.Framework.Font/Graphics/Shaders/RepeatMovingBackgroundShader.cs new file mode 100644 index 0000000..ab0a21a --- /dev/null +++ b/osu.Framework.Font/Graphics/Shaders/RepeatMovingBackgroundShader.cs @@ -0,0 +1,68 @@ +// Copyright (c) andy840119 . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using System; +using osu.Framework.Graphics.OpenGL.Buffers; +using osu.Framework.Graphics.Textures; +using osuTK; +using osuTK.Graphics.ES30; + +namespace osu.Framework.Graphics.Shaders +{ + public class RepeatMovingBackgroundShader : InternalShader, IApplicableToCurrentTime + { + public override string ShaderName => "RepeatMovingBackground"; + + public Texture Texture { get; set; } + + public Vector2 TextureDisplaySize { get; set; } = new Vector2(10); + + public Vector2 TextureDisplayBorder { get; set; } + + public Vector2 Speed { get; set; } + + public float Mix { get; set; } = 1f; + + public RepeatMovingBackgroundShader(IShader originShader) + : base(originShader) + { + } + + public override void ApplyValue(FrameBuffer current) + { + if (Texture == null) + return; + + var size = current.Size; + GetUniform(@"g_TexSize").UpdateValue(ref size); + + Texture.TextureGL.Bind(TextureUnit.Texture1); + + var unitId = TextureUnit.Texture1 - TextureUnit.Texture0; + GetUniform(@"g_RepeatSample").UpdateValue(ref unitId); + + var textureCoord = Texture.GetTextureRect().TopLeft; + GetUniform(@"g_RepeatSampleCoord").UpdateValue(ref textureCoord); + + var textureSize = Texture.GetTextureRect().Size; + GetUniform(@"g_RepeatSampleSize").UpdateValue(ref textureSize); + + var textureDisplaySize = TextureDisplaySize; + GetUniform("g_DisplaySize").UpdateValue(ref textureDisplaySize); + + var textureDisplayBorder = TextureDisplayBorder; + GetUniform("g_DisplayBorder").UpdateValue(ref textureDisplayBorder); + + var speed = Speed; + GetUniform("g_Speed").UpdateValue(ref speed); + + var mix = Math.Clamp(Mix, 0, 1); + GetUniform(@"g_Mix").UpdateValue(ref mix); + } + + public void ApplyCurrentTime(float currentTime) + { + GetUniform("g_Time").UpdateValue(ref currentTime); + } + } +} diff --git a/osu.Framework.Font/Resources/Shaders/sh_RepeatMovingBackground.fs b/osu.Framework.Font/Resources/Shaders/sh_RepeatMovingBackground.fs new file mode 100644 index 0000000..1a044eb --- /dev/null +++ b/osu.Framework.Font/Resources/Shaders/sh_RepeatMovingBackground.fs @@ -0,0 +1,35 @@ +// see : https://github.com/kiwipxl/GLSL-shaders/blob/master/repeat.glsl + +varying vec2 v_TexCoord; +varying vec4 v_Colour; +varying mediump vec4 v_TexRect; + +uniform lowp sampler2D m_Sampler; + +uniform mediump vec2 g_TexSize; +uniform lowp sampler2D g_RepeatSample; +uniform vec2 g_RepeatSampleCoord; +uniform vec2 g_RepeatSampleSize; +uniform vec2 g_DisplaySize; +uniform vec2 g_DisplayBorder; +uniform vec2 g_Speed; +uniform float g_Time; +uniform float g_Mix; + +void main(void) { + // calculate how many times texture should be repeated. + vec2 repeat = g_TexSize / (g_DisplaySize + g_DisplayBorder); + + // get the repeat texture cooldinate. + float repeatTexCoordX = mod(v_TexCoord.x * repeat.x + g_Speed.x * g_Time, 1); + float repeatTexCoordY = mod(v_TexCoord.y * repeat.y + g_Speed.y * g_Time, 1); + vec2 repeatTexCoord = vec2(repeatTexCoordX, repeatTexCoordY) / g_DisplaySize * (g_DisplaySize + g_DisplayBorder); + + // because repeat texture will be the size of 1024*1024, so should make a conversion to get the target area of the texture. + vec2 fixedTexCoord = repeatTexCoord * g_RepeatSampleSize + g_RepeatSampleCoord; + + // get point colour from sample. + vec4 texColor = texture2D(m_Sampler, v_TexCoord); + vec4 repeatSampleColor = v_Colour * vec4(texture2D(g_RepeatSample, fixedTexCoord).xyz, texColor.a); + gl_FragColor = mix(texColor, repeatSampleColor, g_Mix); +}