Blood Spray Particles

This is a simple custom particle shader I’ve made for my game.

If I’m not wrong, what I was looking for with this shader can be achived now with the new features of Godot 4.7, but my game is still in Godot 4.6 and anyway I’m going to use this as a template for other shaders in the future (if needed).

The effect requires also a cone shaped mesh and a blood splat texture, you can get both from my sample/demo project (see below) on my github.

For more details, watch my youtube video (its in spanish).

Shader code
shader_type particles;

uniform vec4 color : source_color = vec4(1.0, 1.0, 1.0, 1.0);
uniform float scale_initial = 0.05; // initial scale
uniform float scale_add = 0.75; // scale amount added (or substracted)
uniform vec3 grav = vec3(0.0, -9.8, 0.0);
uniform float initial_velocity = 5.0;
uniform float vel_damping = 1.0;
uniform float spread = 0.1; // XY random rotation

// function taken from default particle process shader
uint hash(uint x) {
	x = ((x >> uint(16)) ^ x) * uint(73244475);
	x = ((x >> uint(16)) ^ x) * uint(73244475);
	x = (x >> uint(16)) ^ x;
	return x;
}

// function taken from default particle process shader
float rand_from_seed(inout uint seed) {
	int k;
	int s = int(seed);
	if (s == 0) {
		s = 305420679;
	}
	k = s / 127773;
	s = 16807 * (s - k * 127773) - 2836 * k;
	if (s < 0) {
		s += 2147483647;
	}
	seed = uint(s);
	return float(seed % uint(65536)) / 65535.0;
}

void start() {	
	uint base_number = NUMBER;
	uint alt_seed = hash(base_number + uint(1) + RANDOM_SEED);

	if (RESTART_ROT_SCALE) {
		TRANSFORM[0].xyz = vec3(1.0, 0.0, 0.0);
		TRANSFORM[1].xyz = vec3(0.0, 1.0, 0.0);
		TRANSFORM[2].xyz = vec3(0.0, 0.0, 1.0);

		// random angle in Z axis
		float angle_z = rand_from_seed(alt_seed) * TAU;

		// random angle in XY axis
		float angle_x = rand_from_seed(alt_seed) * spread;
		float angle_y = rand_from_seed(alt_seed) * spread;

		// compute x rotation matrix
		float cx = cos(angle_x);
		float sx = sin(angle_x);

		mat3 rot_x = mat3(
			vec3(1.0, 0.0, 0.0),
			vec3(0.0, cx, -sx),
			vec3(0.0, sx,  cx)
		);

		// compute Y rotation matrix
		float cy = cos(angle_y);
		float sy = sin(angle_y);

		mat3 rot_y = mat3(
			vec3( cy, 0.0, sy),
			vec3(0.0, 1.0, 0.0),
			vec3(-sy, 0.0, cy)
		);

		// compute z rotation matrix
		float cz = cos(angle_z);
		float sz = sin(angle_z);

		mat3 rot_z = mat3(
			vec3(cz, -sz, 0.0),
			vec3(sz,  cz, 0.0),
			vec3(0.0, 0.0, 1.0)
		);

		// combine rotations
		mat3 rot = rot_y * rot_x * rot_z;

		// apply rotations
		TRANSFORM[0].xyz = rot * TRANSFORM[0].xyz;
		TRANSFORM[1].xyz = rot * TRANSFORM[1].xyz;
		TRANSFORM[2].xyz = rot * TRANSFORM[2].xyz;
		
		// save some data
		USERDATA1 = vec4(0.0);
		USERDATA1.x = 0.0; // particle time
		USERDATA1.y = angle_z; // desired z rotation		
	}

	// reset position (and appy emissor transform)
	if (RESTART_POSITION) {
		TRANSFORM[3].xyz = vec3(0.0);
		TRANSFORM = EMISSION_TRANSFORM * TRANSFORM; // apply emitter (node) transform 	
	}
	
	// reset color
	if (RESTART_COLOR) {
		COLOR = color;	
	}	
	
	// reset velocity (forward is -Z in Godot)
	if (RESTART_VELOCITY) {
		VELOCITY = normalize(-TRANSFORM[2].xyz) * initial_velocity;	
	}	
}

void process() {
	// increase particle time
	USERDATA1.x += DELTA;
	
	// compute particle time (value from 0 to 1)
	float particle_time = clamp(USERDATA1.x / LIFETIME, 0.0, 1.0);
		
	// apply gravity (and damping)
	VELOCITY += grav * DELTA;
	VELOCITY *= 1.0 - vel_damping * DELTA;
	
	// build rotation matrix along velocity ("forward")
	vec3 dir = normalize(-VELOCITY);

	vec3 up = vec3(0.0, 1.0, 0.0);

	vec3 right = normalize(cross(up, dir));
	vec3 real_up = cross(dir, right);
	
	mat3 desired_rotation = mat3(
		right,
		real_up,
		dir
	);
	
	// rebuild z rotation matrix
	float cz = cos(USERDATA1.y);
	float sz = sin(USERDATA1.y);

	mat3 rot_z = mat3(
		vec3(cz, -sz, 0.0),
		vec3(sz,  cz, 0.0),
		vec3(0.0, 0.0, 1.0)
	);	
	
	// build scale matrix
	float scl = scale_initial + (particle_time * scale_add);
	mat3 desired_scale = mat3(
		vec3(scl, 0.0, 0.0),
		vec3(0.0, scl, 0.0),
		vec3(0.0, 0.0, scl)		
	);

	// combine matrices
	mat3 final = desired_scale * desired_rotation * rot_z;
	TRANSFORM[0].xyz = final[0];
	TRANSFORM[1].xyz = final[1];
	TRANSFORM[2].xyz = final[2];	

	// adjust alpha
	COLOR.a = 1.0 - particle_time;
}
Tags
blood, particle, particles, spray
The shader code and all code snippets in this post are under CC0 license and can be used freely without the author's permission. Images and videos, and assets depicted in those, do not fall under this license. For more info, see our License terms.

More from ProfesorShader

Related shaders

guest

1 Comment
Oldest
Newest Most Voted
Inline Feedbacks
View all comments
ElSuicio
1 day ago

Nice work, keep crushing it! :3