Repository: godotengine/godot (language: cpp)

Issue:
GPUParticles2D emission mask captures random color instead of pixel color
### Tested versions

- Reproducible in: v4.2.stable.mono.official [46dc27791]

### System information

Godot v4.2.stable.mono - Windows 10.0.22621 - Vulkan (Forward+) - dedicated NVIDIA GeForce RTX 3060 (NVIDIA; 31.0.15.4629) - 13th Gen Intel(R) Core(TM) i5-13600KF (20 Threads)

### Issue description

Emission mask for GPUParticles2D captures colors incorrectly, instead of copying colors from place where particles spawned, it uses random color from mask texture

### Steps to reproduce

1. Create `GPUParticles2D` node
2. Set `Amount` to 10000 to make it look more detailed
3. Create `ParticlesProcessMaterial` for it
4. Load emission mask for it, using some color texture and with options `Solid pixels` and `Capture Colors from Pixels` (`centered` doesn't matter)
5. View result

### Minimal reproduction project (MRP)

[Bug.zip](https://github.com/godotengine/godot/files/14071691/Bug.zip)



Discussion / hints:

Fix patch:
diff --git a/scene/resources/particle_process_material.cpp b/scene/resources/particle_process_material.cpp
index dd81b1c4380d..13bc02014b01 100644
--- a/scene/resources/particle_process_material.cpp
+++ b/scene/resources/particle_process_material.cpp
@@ -523,6 +523,7 @@ void ParticleProcessMaterial::_update_shader() {
 	code += "	float animation_offset;\n";
 	code += "	float lifetime;\n";
 	code += "	vec4 color;\n";
+	code += "	float emission_texture_position;\n";
 	code += "};\n\n";
 
 	code += "struct DynamicsParameters {\n";
@@ -579,11 +580,15 @@ void ParticleProcessMaterial::_update_shader() {
 	if (color_initial_ramp.is_valid()) {
 		code += "	params.color *= texture(color_initial_ramp, vec2(rand_from_seed(alt_seed)));\n";
 	}
-	if (emission_color_texture.is_valid() && (emission_shape == EMISSION_SHAPE_POINTS || emission_shape == EMISSION_SHAPE_DIRECTED_POINTS)) {
-		code += "	int point = min(emission_texture_point_count - 1, int(rand_from_seed(alt_seed) * float(emission_texture_point_count)));\n";
-		code += "	ivec2 emission_tex_size = textureSize(emission_texture_points, 0);\n";
-		code += "	ivec2 emission_tex_ofs = ivec2(point % emission_tex_size.x, point / emission_tex_size.x);\n";
-		code += "	params.color *= texelFetch(emission_texture_color, emission_tex_ofs, 0);\n";
+	if (emission_shape == EMISSION_SHAPE_POINTS || emission_shape == EMISSION_SHAPE_DIRECTED_POINTS) {
+		code += "	params.emission_texture_position = rand_from_seed(alt_seed);\n";
+
+		if (emission_color_texture.is_valid()) {
+			code += "	int point = min(emission_texture_point_count - 1, int(params.emission_texture_position * float(emission_texture_point_count)));\n";
+			code += "	ivec2 emission_tex_size = textureSize(emission_texture_points, 0);\n";
+			code += "	ivec2 emission_tex_ofs = ivec2(point % emission_tex_size.x, point / emission_tex_size.x);\n";
+			code += "	params.color *= texelFetch(emission_texture_color, emission_tex_ofs, 0);\n";
+		}
 	}
 	code += "}\n\n";
 
@@ -614,7 +619,7 @@ void ParticleProcessMaterial::_update_shader() {
 	}
 	code += "}\n\n";
 
-	code += "vec3 calculate_initial_position(inout uint alt_seed) {\n";
+	code += "vec3 calculate_initial_position(inout DisplayParameters params, inout uint alt_seed) {\n";
 	code += "	float pi = 3.14159;\n";
 	code += "	vec3 pos = vec3(0.0);\n";
 	code += "	{ // Emission shape.\n";
@@ -639,7 +644,7 @@ void ParticleProcessMaterial::_update_shader() {
 		code += "		pos = vec3(rand_from_seed(alt_seed) * 2.0 - 1.0, rand_from_seed(alt_seed) * 2.0 - 1.0, rand_from_seed(alt_seed) * 2.0 - 1.0) * emission_box_extents;\n";
 	}
 	if (emission_shape == EMISSION_SHAPE_POINTS || emission_shape == EMISSION_SHAPE_DIRECTED_POINTS) {
-		code += "		int point = min(emission_texture_point_count - 1, int(rand_from_seed(alt_seed) * float(emission_texture_point_count)));\n";
+		code += "		int point = min(emission_texture_point_count - 1, int(params.emission_texture_position * float(emission_texture_point_count)));\n";
 		code += "		ivec2 emission_tex_size = textureSize(emission_texture_points, 0);\n";
 		code += "		ivec2 emission_tex_ofs = ivec2(point % emission_tex_size.x, point / emission_tex_size.x);\n";
 		code += "		pos = texelFetch(emission_texture_points, emission_tex_ofs, 0).xyz;\n";
@@ -860,7 +865,7 @@ void ParticleProcessMaterial::_update_shader() {
 	code += "		TRANSFORM[2].xyz = vec3(0.0, 0.0, 1.0);\n";
 	code += "	}\n";
 	code += "	if (RESTART_POSITION) {\n";
-	code += "		TRANSFORM[3].xyz = calculate_initial_position(alt_seed);\n";
+	code += "		TRANSFORM[3].xyz = calculate_initial_position(params, alt_seed);\n";
 	if (turbulence_enabled) {
 		code += "		float initial_turbulence_displacement = mix(turbulence_initial_displacement_min, turbulence_initial_displacement_max, rand_from_seed(alt_seed));\n";
 		code += "		vec3 noise_direction = get_noise_direction(TRANSFORM[3].xyz);\n";
@@ -871,7 +876,7 @@ void ParticleProcessMaterial::_update_shader() {
 	code += "	if (RESTART_VELOCITY) {\n";
 	code += "		VELOCITY = get_random_direction_from_spread(alt_seed, spread) * dynamic_params.initial_velocity_multiplier;\n";
 	if (emission_shape == EMISSION_SHAPE_DIRECTED_POINTS) {
-		code += "		int point = min(emission_texture_point_count - 1, int(rand_from_seed(alt_seed) * float(emission_texture_point_count)));\n";
+		code += "		int point = min(emission_texture_point_count - 1, int(params.emission_texture_position * float(emission_texture_point_count)));\n";
 		code += "		ivec2 emission_tex_size = textureSize(emission_texture_points, 0);\n";
 		code += "		ivec2 emission_tex_ofs = ivec2(point % emission_tex_size.x, point / emission_tex_size.x);\n";
 		if (particle_flags[PARTICLE_FLAG_DISABLE_Z]) {


Test patch: