diff --git a/doc/classes/CPUParticles.xml b/doc/classes/CPUParticles.xml
index 346fdd100fb0..2ee7cca537a6 100644
--- a/doc/classes/CPUParticles.xml
+++ b/doc/classes/CPUParticles.xml
@@ -129,6 +129,9 @@
Each particle's initial color. To have particle display color in a [SpatialMaterial] make sure to set [member SpatialMaterial.vertex_color_use_as_albedo] to [code]true[/code].
+
+ Each particle's initial color will vary along this [GradientTexture] (multiplied with [member color]).
+
Each particle's color will vary along this [GradientTexture] over its lifetime (multiplied with [member color]).
diff --git a/doc/classes/CPUParticles2D.xml b/doc/classes/CPUParticles2D.xml
index d6e22399e1c0..f37f8f7efc51 100644
--- a/doc/classes/CPUParticles2D.xml
+++ b/doc/classes/CPUParticles2D.xml
@@ -130,6 +130,9 @@
Each particle's initial color. If [member texture] is defined, it will be multiplied by this color.
+
+ Each particle's initial color will vary along this [GradientTexture] (multiplied with [member color]).
+
Each particle's color will vary along this [Gradient] (multiplied with [member color]).
diff --git a/doc/classes/ParticlesMaterial.xml b/doc/classes/ParticlesMaterial.xml
index 99b59799b1cf..52fda5813108 100644
--- a/doc/classes/ParticlesMaterial.xml
+++ b/doc/classes/ParticlesMaterial.xml
@@ -114,6 +114,9 @@
Each particle's initial color. If the [Particles2D]'s [code]texture[/code] is defined, it will be multiplied by this color. To have particle display color in a [SpatialMaterial] make sure to set [member SpatialMaterial.vertex_color_use_as_albedo] to [code]true[/code].
+
+ Each particle's initial color will vary along this [GradientTexture] (multiplied with [member color]).
+
Each particle's color will vary along this [GradientTexture] over its lifetime (multiplied with [member color]).
diff --git a/scene/2d/cpu_particles_2d.cpp b/scene/2d/cpu_particles_2d.cpp
index 2897a7486eb0..e1e4461e9ad0 100644
--- a/scene/2d/cpu_particles_2d.cpp
+++ b/scene/2d/cpu_particles_2d.cpp
@@ -393,6 +393,14 @@ Ref CPUParticles2D::get_color_ramp() const {
return color_ramp;
}
+void CPUParticles2D::set_color_initial_ramp(const Ref &p_ramp) {
+ color_initial_ramp = p_ramp;
+}
+
+Ref CPUParticles2D::get_color_initial_ramp() const {
+ return color_initial_ramp;
+}
+
void CPUParticles2D::set_particle_flag(Flags p_flag, bool p_enable) {
ERR_FAIL_INDEX(p_flag, FLAG_MAX);
flags[p_flag] = p_enable;
@@ -690,6 +698,12 @@ void CPUParticles2D::_particles_process(float p_delta) {
p.hue_rot_rand = Math::randf();
p.anim_offset_rand = Math::randf();
+ if (color_initial_ramp.is_valid()) {
+ p.start_color_rand = color_initial_ramp->get_color_at_offset(Math::randf());
+ } else {
+ p.start_color_rand = Color(1, 1, 1, 1);
+ }
+
float angle1_rad = Math::atan2(direction.y, direction.x) + (Math::randf() * 2.0 - 1.0) * Math_PI * spread / 180.0;
Vector2 rot = Vector2(Math::cos(angle1_rad), Math::sin(angle1_rad));
p.velocity = rot * parameters[PARAM_INITIAL_LINEAR_VELOCITY] * Math::lerp(1.0f, float(Math::randf()), randomness[PARAM_INITIAL_LINEAR_VELOCITY]);
@@ -895,7 +909,7 @@ void CPUParticles2D::_particles_process(float p_delta) {
p.color.g = color_rgb.y;
p.color.b = color_rgb.z;
- p.color *= p.base_color;
+ p.color *= p.base_color * p.start_color_rand;
if (flags[FLAG_ALIGN_Y_TO_VELOCITY]) {
if (p.velocity.length() > 0.0) {
@@ -1127,6 +1141,11 @@ void CPUParticles2D::convert_from_particles(Node *p_particles) {
set_color_ramp(gt->get_gradient());
}
+ Ref gti = material->get_color_initial_ramp();
+ if (gti.is_valid()) {
+ set_color_initial_ramp(gti->get_gradient());
+ }
+
set_particle_flag(FLAG_ALIGN_Y_TO_VELOCITY, material->get_flag(ParticlesMaterial::FLAG_ALIGN_Y_TO_VELOCITY));
set_emission_shape(EmissionShape(material->get_emission_shape()));
@@ -1247,6 +1266,9 @@ void CPUParticles2D::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_color_ramp", "ramp"), &CPUParticles2D::set_color_ramp);
ClassDB::bind_method(D_METHOD("get_color_ramp"), &CPUParticles2D::get_color_ramp);
+ ClassDB::bind_method(D_METHOD("set_color_initial_ramp", "ramp"), &CPUParticles2D::set_color_initial_ramp);
+ ClassDB::bind_method(D_METHOD("get_color_initial_ramp"), &CPUParticles2D::get_color_initial_ramp);
+
ClassDB::bind_method(D_METHOD("set_particle_flag", "flag", "enable"), &CPUParticles2D::set_particle_flag);
ClassDB::bind_method(D_METHOD("get_particle_flag", "flag"), &CPUParticles2D::get_particle_flag);
@@ -1328,6 +1350,7 @@ void CPUParticles2D::_bind_methods() {
ADD_GROUP("Color", "");
ADD_PROPERTY(PropertyInfo(Variant::COLOR, "color"), "set_color", "get_color");
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "color_ramp", PROPERTY_HINT_RESOURCE_TYPE, "Gradient"), "set_color_ramp", "get_color_ramp");
+ ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "color_initial_ramp", PROPERTY_HINT_RESOURCE_TYPE, "Gradient"), "set_color_initial_ramp", "get_color_initial_ramp");
ADD_GROUP("Hue Variation", "hue_");
ADD_PROPERTYI(PropertyInfo(Variant::REAL, "hue_variation", PROPERTY_HINT_RANGE, "-1,1,0.01"), "set_param", "get_param", PARAM_HUE_VARIATION);
diff --git a/scene/2d/cpu_particles_2d.h b/scene/2d/cpu_particles_2d.h
index 88c7ab684a00..d7f2744c827f 100644
--- a/scene/2d/cpu_particles_2d.h
+++ b/scene/2d/cpu_particles_2d.h
@@ -94,6 +94,7 @@ class CPUParticles2D : public Node2D {
float scale_rand;
float hue_rot_rand;
float anim_offset_rand;
+ Color start_color_rand;
float time;
float lifetime;
Color base_color;
@@ -162,6 +163,7 @@ class CPUParticles2D : public Node2D {
Ref curve_parameters[PARAM_MAX];
Color color;
Ref color_ramp;
+ Ref color_initial_ramp;
bool flags[FLAG_MAX];
@@ -255,6 +257,9 @@ class CPUParticles2D : public Node2D {
void set_color_ramp(const Ref &p_ramp);
Ref get_color_ramp() const;
+ void set_color_initial_ramp(const Ref &p_ramp);
+ Ref get_color_initial_ramp() const;
+
void set_particle_flag(Flags p_flag, bool p_enable);
bool get_particle_flag(Flags p_flag) const;
diff --git a/scene/3d/cpu_particles.cpp b/scene/3d/cpu_particles.cpp
index eba5e65892a9..37a045ab0cb1 100644
--- a/scene/3d/cpu_particles.cpp
+++ b/scene/3d/cpu_particles.cpp
@@ -351,6 +351,14 @@ Ref CPUParticles::get_color_ramp() const {
return color_ramp;
}
+void CPUParticles::set_color_initial_ramp(const Ref &p_ramp) {
+ color_initial_ramp = p_ramp;
+}
+
+Ref CPUParticles::get_color_initial_ramp() const {
+ return color_initial_ramp;
+}
+
void CPUParticles::set_particle_flag(Flags p_flag, bool p_enable) {
ERR_FAIL_INDEX(p_flag, FLAG_MAX);
flags[p_flag] = p_enable;
@@ -687,6 +695,12 @@ void CPUParticles::_particles_process(float p_delta) {
p.hue_rot_rand = Math::randf();
p.anim_offset_rand = Math::randf();
+ if (color_initial_ramp.is_valid()) {
+ p.start_color_rand = color_initial_ramp->get_color_at_offset(Math::randf());
+ } else {
+ p.start_color_rand = Color(1, 1, 1, 1);
+ }
+
if (flags[FLAG_DISABLE_Z]) {
float angle1_rad = Math::atan2(direction.y, direction.x) + (Math::randf() * 2.0 - 1.0) * Math_PI * spread / 180.0;
Vector3 rot = Vector3(Math::cos(angle1_rad), Math::sin(angle1_rad), 0.0);
@@ -963,7 +977,7 @@ void CPUParticles::_particles_process(float p_delta) {
p.color.g = color_rgb.y;
p.color.b = color_rgb.z;
- p.color *= p.base_color;
+ p.color *= p.base_color * p.start_color_rand;
if (flags[FLAG_DISABLE_Z]) {
if (flags[FLAG_ALIGN_Y_TO_VELOCITY]) {
@@ -1245,6 +1259,11 @@ void CPUParticles::convert_from_particles(Node *p_particles) {
set_color_ramp(gt->get_gradient());
}
+ Ref gti = material->get_color_initial_ramp();
+ if (gti.is_valid()) {
+ set_color_initial_ramp(gti->get_gradient());
+ }
+
set_particle_flag(FLAG_ALIGN_Y_TO_VELOCITY, material->get_flag(ParticlesMaterial::FLAG_ALIGN_Y_TO_VELOCITY));
set_particle_flag(FLAG_ROTATE_Y, material->get_flag(ParticlesMaterial::FLAG_ROTATE_Y));
set_particle_flag(FLAG_DISABLE_Z, material->get_flag(ParticlesMaterial::FLAG_DISABLE_Z));
@@ -1368,6 +1387,9 @@ void CPUParticles::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_color_ramp", "ramp"), &CPUParticles::set_color_ramp);
ClassDB::bind_method(D_METHOD("get_color_ramp"), &CPUParticles::get_color_ramp);
+ ClassDB::bind_method(D_METHOD("set_color_initial_ramp", "ramp"), &CPUParticles::set_color_initial_ramp);
+ ClassDB::bind_method(D_METHOD("get_color_initial_ramp"), &CPUParticles::get_color_initial_ramp);
+
ClassDB::bind_method(D_METHOD("set_particle_flag", "flag", "enable"), &CPUParticles::set_particle_flag);
ClassDB::bind_method(D_METHOD("get_particle_flag", "flag"), &CPUParticles::get_particle_flag);
@@ -1468,6 +1490,7 @@ void CPUParticles::_bind_methods() {
ADD_GROUP("Color", "");
ADD_PROPERTY(PropertyInfo(Variant::COLOR, "color"), "set_color", "get_color");
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "color_ramp", PROPERTY_HINT_RESOURCE_TYPE, "Gradient"), "set_color_ramp", "get_color_ramp");
+ ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "color_initial_ramp", PROPERTY_HINT_RESOURCE_TYPE, "Gradient"), "set_color_initial_ramp", "get_color_initial_ramp");
ADD_GROUP("Hue Variation", "hue_");
ADD_PROPERTYI(PropertyInfo(Variant::REAL, "hue_variation", PROPERTY_HINT_RANGE, "-1,1,0.01"), "set_param", "get_param", PARAM_HUE_VARIATION);
diff --git a/scene/3d/cpu_particles.h b/scene/3d/cpu_particles.h
index e7b58f11f560..93967d1e3fda 100644
--- a/scene/3d/cpu_particles.h
+++ b/scene/3d/cpu_particles.h
@@ -94,6 +94,7 @@ class CPUParticles : public GeometryInstance {
float scale_rand;
float hue_rot_rand;
float anim_offset_rand;
+ Color start_color_rand;
float time;
float lifetime;
Color base_color;
@@ -163,6 +164,7 @@ class CPUParticles : public GeometryInstance {
Ref curve_parameters[PARAM_MAX];
Color color;
Ref color_ramp;
+ Ref color_initial_ramp;
bool flags[FLAG_MAX];
@@ -259,6 +261,9 @@ class CPUParticles : public GeometryInstance {
void set_color_ramp(const Ref &p_ramp);
Ref get_color_ramp() const;
+ void set_color_initial_ramp(const Ref &p_ramp);
+ Ref get_color_initial_ramp() const;
+
void set_particle_flag(Flags p_flag, bool p_enable);
bool get_particle_flag(Flags p_flag) const;
diff --git a/scene/resources/particles_material.cpp b/scene/resources/particles_material.cpp
index a330e56f6ff6..0342443441c0 100644
--- a/scene/resources/particles_material.cpp
+++ b/scene/resources/particles_material.cpp
@@ -85,6 +85,7 @@ void ParticlesMaterial::init_shaders() {
shader_names->color = "color_value";
shader_names->color_ramp = "color_ramp";
+ shader_names->color_initial_ramp = "color_initial_ramp";
shader_names->emission_sphere_radius = "emission_sphere_radius";
shader_names->emission_box_extents = "emission_box_extents";
@@ -217,6 +218,10 @@ void ParticlesMaterial::_update_shader() {
code += "uniform sampler2D color_ramp;\n";
}
+ if (color_initial_ramp.is_valid()) {
+ code += "uniform sampler2D color_initial_ramp;\n";
+ }
+
if (tex_parameters[PARAM_INITIAL_LINEAR_VELOCITY].is_valid()) {
code += "uniform sampler2D linear_velocity_texture;\n";
}
@@ -299,6 +304,9 @@ void ParticlesMaterial::_update_shader() {
code += " float scale_rand = rand_from_seed(alt_seed);\n";
code += " float hue_rot_rand = rand_from_seed(alt_seed);\n";
code += " float anim_offset_rand = rand_from_seed(alt_seed);\n";
+ if (color_initial_ramp.is_valid()) {
+ code += " float color_initial_rand = rand_from_seed(alt_seed);\n";
+ }
code += " float pi = 3.14159;\n";
code += " float degree_to_rad = pi / 180.0;\n";
code += "\n";
@@ -593,6 +601,12 @@ void ParticlesMaterial::_update_shader() {
} else {
code += " COLOR = hue_rot_mat * color_value;\n";
}
+
+ if (color_initial_ramp.is_valid()) {
+ code += " vec4 start_color = textureLod(color_initial_ramp, vec2(color_initial_rand, 0.0), 0.0);\n";
+ code += " COLOR *= start_color;\n";
+ }
+
if (emission_color_texture.is_valid() && (emission_shape == EMISSION_SHAPE_POINTS || emission_shape == EMISSION_SHAPE_DIRECTED_POINTS)) {
code += " COLOR *= texelFetch(emission_texture_color, emission_tex_ofs, 0);\n";
}
@@ -933,6 +947,17 @@ Ref ParticlesMaterial::get_color_ramp() const {
return color_ramp;
}
+void ParticlesMaterial::set_color_initial_ramp(const Ref &p_texture) {
+ color_initial_ramp = p_texture;
+ VisualServer::get_singleton()->material_set_param(_get_material(), shader_names->color_initial_ramp, p_texture);
+ _queue_shader_change();
+ _change_notify();
+}
+
+Ref ParticlesMaterial::get_color_initial_ramp() const {
+ return color_initial_ramp;
+}
+
void ParticlesMaterial::set_flag(Flags p_flag, bool p_enable) {
ERR_FAIL_INDEX(p_flag, FLAG_MAX);
flags[p_flag] = p_enable;
@@ -1167,6 +1192,9 @@ void ParticlesMaterial::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_color_ramp", "ramp"), &ParticlesMaterial::set_color_ramp);
ClassDB::bind_method(D_METHOD("get_color_ramp"), &ParticlesMaterial::get_color_ramp);
+ ClassDB::bind_method(D_METHOD("set_color_initial_ramp", "ramp"), &ParticlesMaterial::set_color_initial_ramp);
+ ClassDB::bind_method(D_METHOD("get_color_initial_ramp"), &ParticlesMaterial::get_color_initial_ramp);
+
ClassDB::bind_method(D_METHOD("set_flag", "flag", "enable"), &ParticlesMaterial::set_flag);
ClassDB::bind_method(D_METHOD("get_flag", "flag"), &ParticlesMaterial::get_flag);
@@ -1285,6 +1313,7 @@ void ParticlesMaterial::_bind_methods() {
ADD_GROUP("Color", "");
ADD_PROPERTY(PropertyInfo(Variant::COLOR, "color"), "set_color", "get_color");
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "color_ramp", PROPERTY_HINT_RESOURCE_TYPE, "GradientTexture"), "set_color_ramp", "get_color_ramp");
+ ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "color_initial_ramp", PROPERTY_HINT_RESOURCE_TYPE, "GradientTexture"), "set_color_initial_ramp", "get_color_initial_ramp");
ADD_GROUP("Hue Variation", "hue_");
ADD_PROPERTYI(PropertyInfo(Variant::REAL, "hue_variation", PROPERTY_HINT_RANGE, "-1,1,0.01"), "set_param", "get_param", PARAM_HUE_VARIATION);
diff --git a/scene/resources/particles_material.h b/scene/resources/particles_material.h
index 3d564c7880aa..e15d36f9151c 100644
--- a/scene/resources/particles_material.h
+++ b/scene/resources/particles_material.h
@@ -80,6 +80,7 @@ class ParticlesMaterial : public Material {
struct {
uint32_t texture_mask : 16;
uint32_t texture_color : 1;
+ uint32_t texture_initial_color : 1;
uint32_t flags : 4;
uint32_t emission_shape : 3;
uint32_t trail_size_texture : 1;
@@ -119,6 +120,7 @@ class ParticlesMaterial : public Material {
}
mk.texture_color = color_ramp.is_valid() ? 1 : 0;
+ mk.texture_initial_color = color_initial_ramp.is_valid() ? 1 : 0;
mk.emission_shape = emission_shape;
mk.trail_color_texture = trail_color_modifier.is_valid() ? 1 : 0;
mk.trail_size_texture = trail_size_modifier.is_valid() ? 1 : 0;
@@ -174,6 +176,7 @@ class ParticlesMaterial : public Material {
StringName color;
StringName color_ramp;
+ StringName color_initial_ramp;
StringName emission_sphere_radius;
StringName emission_box_extents;
@@ -214,6 +217,7 @@ class ParticlesMaterial : public Material {
Ref tex_parameters[PARAM_MAX];
Color color;
Ref color_ramp;
+ Ref color_initial_ramp;
bool flags[FLAG_MAX];
@@ -271,6 +275,9 @@ class ParticlesMaterial : public Material {
void set_color_ramp(const Ref &p_texture);
Ref get_color_ramp() const;
+ void set_color_initial_ramp(const Ref &p_texture);
+ Ref get_color_initial_ramp() const;
+
void set_flag(Flags p_flag, bool p_enable);
bool get_flag(Flags p_flag) const;