#godot #gdscript
Вопрос:
Выбор портрета персонажа. При нажатии кнопки далее загружается следующее изображение в массиве, при нажатии кнопки назад загружается предыдущее изображение. Вместо резкого перехода от одного изображения к другому я хочу, чтобы текущее изображение исчезало с переменной скоростью, а новое изображение исчезало. Эффекты растворения/рендеринга были бы хороши, но даже непрозрачность между 100->0 / 0->> 100 через x Секунд.
Я действительно предпочитаю не использовать несколько объектов друг на друге и чередовать их для «текущей текстуры».
Возможно ли это?
Ответ №1:
Мы можем исчезать и исчезать с помощью анимации modulate
. Что является простым решением.
Для растворения мы можем использовать шейдеры. И мы многое можем сделать с шейдерами. существует множество шейдеров для растворения, которые вы можете найти в Интернете… Я объясню некоторые полезные вариации. Я предпочитаю варианты, с которыми легко возиться.
Затухание и затухание
Мы можем сделать это с Tween
объектом и modulate
self-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)
Не забудьте позвонить в start:
tween.start()
Мы можем воспользоваться преимуществами yield, чтобы добавить код, который будет выполняться по завершении анимации:
yield(tween, "tween_completed")
Затем мы меняем текстуру:
self.texture = target_texture
And then interpolate modulate
in the opposite direction:
tween.interpolate_property(self, "modulate",
Color.transparent, Color.white, duration_seconds)
tween.start()
Note that I’m using self
but you could be manipulating another node. Also target_texture
is whatever texture you want to transition into, loaded beforehand.
Dissolve Texture
For any effect that require both textures partially visible, use a custom shader. Go ahead and add a ShaderMaterial
to your TextureRect
(or similar), and give it a new Shader file.
This will be our starting point:
shader_type canvas_item;
void fragment()
{
COLOR = texture(TEXTURE, UV);
}
That is a shader that simply shows the texture. Your TextureRect
should look the same it does without this shader material. Let us add the second texture with an uniform:
shader_type canvas_item;
uniform sampler2D target_texture;
void fragment()
{
COLOR = texture(TEXTURE, UV);
}
You should see a new entry on Shader Param on the Inspector panel for the new texture.
We also need another parameter to interpolate. It will be 0 to display the original Texture, and 1 for the alternative texture. In Godot we can add a hint for the range:
shader_type canvas_item;
uniform sampler2D target_texture;
uniform float weight: hint_range(0, 1);
void fragment()
{
COLOR = texture(TEXTURE, UV);
}
В параметре шейдера на панели инспектора теперь вы должны увидеть новый поплавок с ползунком, который переходит от 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 возвращает 0, а input
значение 1 возвращает 1. Но переход управляет промежуточной кривой.
Если вы построите график x * x (x - x * x) * y
в диапазоне от 0 до 1 по обеим осям, вы увидите, что когда y
(переход) равен 1, у вас есть линия, но когда y
0, у вас есть парабола.
Alternatively, we can change adjusted_weight
to an step function:
float adjust_weight(float input, vec2 uv)
{
float transition = texture(transition_texture, uv).r;
return smoothstep(transition, transition, input);
}
Using smoothstep
instead of step
to avoid artifacts near 0.
Which will not interpolate between the textures, but each pixel will change from one to the other texture at a different instant. If your noise texture is continuous, then you will see the dissolve advance through the gradient.
Ah, but it does not have to be a noise texture! Any gradient will do. *You can create a texture defining how you want the dissolve to happen (example, under MIT license).
You probably can come up with other versions for that function.
Making Dissolve Edgy
We also could add an edge color. We need, of course, to add a color parameter:
uniform vec4 edge_color: hint_color;
And we will add that color at an offset of where we transition. We need to define that offset:
uniform float edge_weight_offset: hint_range(0, 1);
Now you can add this code:
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
версией. С другой стороны, он смешивает цвет с переходом).
Растворить Альфа
Нетрудно изменить вышеприведенный шейдер, чтобы он растворился в альфа-режиме вместо того, чтобы растворяться в другой текстуре. Прежде всего, удалите 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()