补间 TextureButton / TextureRect 上的纹理。淡出 Image1 同时淡入 Image2

Tween the texture on a TextureButton / TextureRect. Fade out Image1 while simultaneously fade in Image2

人物肖像选择。单击下一个加载数组中的下一个图像,单击后退加载上一个图像。我想要一个变速淡出当前图像并淡入新图像,而不是从一个图像到另一个图像的急剧变化。 Dissolve/Render 效果会很好,但即使是 x 秒内 100->0 / 0-> 100 之间的不透明度。

我真的不喜欢在“当前纹理”中使用多个对象并在它们之间交替使用。

这可能吗?

我们可以通过动画实现淡入淡出modulate。哪个是简单的解决方案。

对于溶解,我们可以使用着色器。 我们可以使用着色器做很多事情。您可以在网上找到大量溶解着色器...我将解释一些有用的变体。我喜欢易于修改的变体。


淡入淡出

我们可以使用 Tween 对象和 modulateself-modulate 属性来做到这一点。

我会继续在代码中创建一个 Tween

var tween:Tween

func _ready():
    tween = Tween.new()
    add_child(tween)

然后我们可以用interpolate_property来操作modulate:

var duration_seconds = 2
tween.interpolate_property(self, "modulate",
    Color.white, Color.transparent, duration_seconds)

别忘了调用开始:

tween.start()

我们可以利用 yield 添加将在补间完成时执行的代码:

yield(tween, "tween_completed")

然后我们改变纹理:

self.texture = target_texture

然后反方向插值modulate

tween.interpolate_property(self, "modulate",
    Color.transparent, Color.white, duration_seconds)
tween.start()

请注意,我使用的是 self,但您可能正在操纵另一个节点。另外 target_texture 是你想要过渡到的任何纹理,预先加载。


溶解纹理

对于需要两个纹理部分可见的任何效果,请使用自定义着色器。继续添加一个 ShaderMaterial 到你的 TextureRect (或类似的),并给它一个新的着色器文件。

这将是我们的起点:

shader_type canvas_item;

void fragment()
{
    COLOR = texture(TEXTURE, UV);
}

这是一个简单显示纹理的着色器。您的 TextureRect 应该看起来与没有此着色器 material 时一样。让我们添加具有统一的第二个纹理:

shader_type canvas_item;
uniform sampler2D target_texture;

void fragment()
{
    COLOR = texture(TEXTURE, UV);
}

您应该会在检查器面板的新纹理的着色器参数上看到一个新条目。

我们还需要另一个参数进行插值。显示原始纹理为 0,替代纹理为 1。在 Godot 中,我们可以为范围添加提示:

shader_type canvas_item;
uniform sampler2D target_texture;
uniform float weight: hint_range(0, 1);

void fragment()
{
    COLOR = texture(TEXTURE, UV);
}

在 Inspector Panel 的 Shader Param 中,您现在应该可以看到新的浮动,其中有一个从 0 到 1 的滑块。

当然,它什么都不做。我们仍然需要混合纹理的代码:

shader_type canvas_item;
uniform sampler2D target_texture;
uniform float weight: hint_range(0, 1);

void fragment()
{
    vec4 color_a = texture(TEXTURE, UV);
    vec4 color_b = texture(target_texture, UV);
    COLOR = mix(color_a, color_b, weight);
}

这样就可以了。但是,为了便于修改,我会做一点重构,稍后在这个答案上:

shader_type canvas_item;
uniform sampler2D target_texture;
uniform float weight: hint_range(0, 1);

float adjust_weight(float input, vec2 uv)
{
    return input;
}

void fragment()
{
    vec4 color_a = texture(TEXTURE, UV);
    vec4 color_b = texture(target_texture, UV);
    float adjusted_weight = adjust_weight(weight, UV);
    COLOR = mix(color_a, color_b, adjusted_weight);
}

现在我们再次使用 Tween 对其进行操作。我假设您有一个 Tween 以与以前相同的方式创建。另外,您已经加载了 target_texture

我们首先将 weight 设置为 0,然后 target_texture:

self.material.set("shader_param/weight", 0)
self.material.set("shader_param/target_texture", target_texture)

我们可以补间 weight:

var duration_seconds = 4
tween.interpolate_property(self.material, "shader_param/weight",
    0, 1, duration_seconds)
tween.start()
yield(tween, "tween_completed")

然后更改纹理:

self.texture = target_texture

制作溶解花式

我们可以想象一下我们的溶解效果。例如,我们可以添加另一个纹理来控制不同部分从一个纹理过渡到另一个纹理的速度:

uniform sampler2D transition_texture;

将其设置为新的 NoiseTexture(并且不要忘记设置 NoiseTexture 的噪声 属性)。我将使用纹理的红色通道。

一个简单的解决方案如下所示:

float adjust_weight(float input, vec2 uv)
{
    float transition = texture(transition_texture, uv).r;
    return min(1.0, input * (transition + 1.0));
}

插值始终是线性的,过渡控制斜率。

我们也可以这样做:

float adjust_weight(float input, vec2 uv)
{
    float transition = texture(transition_texture, uv).r;
    float input_2 = input * input;
    return input_2 + (input - input_2) * transition;
}

这确保 input 为 0 returns 0,input 为 1 returns 1。但是转换控制着两者之间的曲线。

如果您在两个轴的 0 到 1 范围内绘制 x * x + (x - x * x) * y,您会看到当 y(过渡)为 1 时,您有一条线,但是当 y 是 0 你有一条抛物线。

或者,我们可以将 adjusted_weight 更改为阶跃函数:

float adjust_weight(float input, vec2 uv)
{
    float transition = texture(transition_texture, uv).r;
    return smoothstep(transition, transition, input);
}

使用 smoothstep 而不是 step 来避免 0 附近的伪影。

这不会在纹理之间进行插值,但每个像素会在不同的瞬间从一个纹理变为另一个纹理。如果您的噪点纹理是连续的,那么您会看到溶解在渐变中前进。

啊,但它不一定是噪声纹理!任何渐变都可以。 *您可以创建一个纹理来定义您希望溶解发生的方式(example,在 MIT 许可下)。

您可能可以为该功能想出其他版本。


使溶解前卫

我们还可以添加边缘颜色。当然,我们需要添加一个颜色参数:

uniform vec4 edge_color: hint_color;

我们将在过渡位置的偏移处添加该颜色。我们需要定义偏移量:

uniform float edge_weight_offset: hint_range(0, 1);

现在您可以添加此代码:

float adjusted_weight = adjust_weight(max(0.0, weight - edge_weight_offset * (1.0 - step(1.0, weight))), UV);
float edge_weight = adjust_weight(weight, UV);
color_a = mix(color_a, edge_color, edge_weight);

这里的因素(1.0 - step(1.0, weight))确保当weight为0时,我们传递0。当weight为1时,我们传递1。遗憾的是我们还需要确保差异不会导致负值。必须有另一种方法来做到这一点......这个怎么样:

float weight_2 = weight * weight;
float adjusted_weight = adjust_weight(weight_2, UV);
float edge_weight = adjust_weight(weight_2 + (weight - weight_2) * edge_weight_offset, UV);
color_a = mix(color_a, edge_color, edge_weight);

好的,随意内联adjust_weight。无论您使用哪个版本(这都会与 smoothstep 版本形成边缘。与另一个版本混合颜色与过渡)。


溶解 Alpha

不难修改上述着色器以溶解到 alpha 而不是溶解到另一个纹理。首先,删除target_texture,同时删除我们不需要也不应该使用的color_b。而不是 mix,我们可以这样做:

COLOR = vec4(color_a.rgb, 1.0 - adjusted_weight);

要使用它,请像以前一样转出:

self.material.set("shader_param/weight", 0)
var duration_seconds = 2
tween.interpolate_property(self.material, "shader_param/weight",
    0, 1, duration_seconds)
tween.start()
yield(tween, "tween_completed")

这将使它变得透明。所以你可以改变纹理:

self.texture = target_texture

并过渡(使用新纹理):

tween.interpolate_property(self.material, "shader_param/weight",
    1, 0, duration_seconds)
tween.start()