A Partyhorn/Blower (vertex animation able)

It’s a partyhorn! You know, the pole shaped thing when blowed maks noise

Set up:

1. Add a Meshinstance3D and use a box mesh

2. Set box mesh params like so:

  • Size : (24.0,1.0,1.0)
  • Subdivide width : 100-300-ish looks pretty good, more is better
  • Subdivide height : 3 (Can’t change unless you modifie my script
  • Subdivide depth : 6 (Can’t change unless you modifie my script

3. Add it a Shadermaterial and copy the code

4. Twick the params

5. Read the code and modification ( Spoiler: My code sucks. 

How to use the animation:

In code: In whatever gdscript, access this material and use “set_shader_parameter()” on front_expaned_length

Use animationplayer: Same as anything else, add an animationplayer and animate the variable  front_expaned_length

Shader code
shader_type spatial;
//Shader created by wo_ri_gou_le

//Plz set this same as the boxmesh you apllied to
uniform float box_xlength_meter = 24;
//Texture params
uniform int PATTERN:hint_range(0, 1, 1) = 1; 
uniform vec2 scale = vec2(10.0,10.0);   
uniform vec4 color_a : source_color = vec4(0.9, 0.1, 0.3, 1.0); 
uniform vec4 color_b : source_color = vec4(0.1, 0.7, 0.8, 1.0); 
uniform float dot_radius : hint_range(0.01, 0.5) = 0.3; 
//
uniform bool front_use_texture = false;
uniform sampler2D front_texture;
uniform sampler2D back_texture;

//Vertex used params
uniform float roll_radius : hint_range(0.3, 1.0, 0.01) = 0.8;
uniform float roll_thickness : hint_range(0.01, 0.1, 0.01)= 0.04;
uniform float shrink_strength : hint_range(6.0, 26.0, 0.1)  = 26.0;
uniform float front_expanded_length : hint_range(0.1, 12.0, 0.1) = 0.1;
uniform float front_solid_length : hint_range(0.0, 0.8, 0.01) = 0.7;
varying float vertex_x; 

void vertex() {
	vertex_x = VERTEX.x;
	//The front part that gets rolled
	float front_not_rolled_length = front_expanded_length+front_solid_length;
	if( VERTEX.x > front_not_rolled_length){
		float roll_remained_length = box_xlength_meter/2.0 - front_solid_length - front_expanded_length;
		float varring_roll_radius = roll_radius * ((front_expanded_length+roll_remained_length+16.0)/(roll_remained_length+16.0));
	    float theta = VERTEX.x - front_not_rolled_length;
		theta *= 1.9 / ((front_expanded_length+roll_remained_length+1.0)/(roll_remained_length+1.0));
		float shrink = shrink_strength/(pow(theta,1)+shrink_strength);
		float new_z = VERTEX.z * 1.4;
		if (VERTEX.y == 0.0){
			
		}
	    else if(VERTEX.y > 0.0){
			shrink -= roll_thickness;
			new_z *= 0.7;
		}
		else{
			shrink += roll_thickness;
			new_z *= 0.7;
		}
		float new_x = varring_roll_radius* sin(theta)*shrink + front_not_rolled_length+0.8;
		float new_y = varring_roll_radius* (1.0 - cos(theta)*shrink);
		
		VERTEX = vec3(new_x,new_y,new_z);
	}
	//The front part that is not rolled
	else if (VERTEX.x > 0.0){
		if (VERTEX.x > front_solid_length){
			float new_y = VERTEX.y*0.8;
			float new_z = VERTEX.z;
			new_z *= 1.0 - pow(abs(VERTEX.y)*1.3,1.4);
			if (VERTEX.y > 0.4){
				new_y *= 0.8;
			}
			VERTEX = vec3(VERTEX.x,new_y,new_z);
		}
		else{
			VERTEX.x = 0.0;
		}
	}
	else if (VERTEX.x > -0.4){
		VERTEX.x = -0.4;
	}
   	else if(VERTEX.x > -1.2){
		VERTEX.x = -1.2;
		VERTEX.y *= 0.2;
		VERTEX.z *= 0.6;
	}
   	else if(VERTEX.x < -1.2){
		VERTEX.x = -1.8;
		VERTEX.y *= 0.2;
		VERTEX.z *= 0.6;
	}
}

void fragment() {
	//simple texture stuff, change to whatever you prefer
	if (vertex_x > 0.0){
		if (front_use_texture){
			ALBEDO = texture(front_texture,UV).rgb;
		}
		else{
		    vec2 coord = UV * scale; 

		    if (PATTERN == 0) {
		        // Checkerboard
		        float checker = mod(floor(coord.x) + floor(coord.y), 2.0);
		        ALBEDO = mix(color_b.rgb, color_a.rgb, checker);
		    } else {
				// Dot
		        vec2 cell = fract(coord);
		        vec2 center = vec2(0.5, 0.5);
		        float dist = distance(cell, center);
		        float circle = smoothstep(dot_radius, dot_radius - 0.02, dist); 
		        ALBEDO = mix(color_a.rgb, color_b.rgb, circle);
		    }
		}
	}
	else{
		ALBEDO = texture(back_texture,UV).rgb;
	}
}
Tags
vertex
The shader code and all code snippets in this post are under MIT license and can be used freely. Images and videos, and assets depicted in those, do not fall under this license. For more info, see our License terms.

Related shaders

Vertex animation with instancing

vertex paint controlled simple 3D wind sway shader

ORM material blending using vertex colors and noise with hard/soft transitions

guest

5 Comments
Oldest
Newest Most Voted
Inline Feedbacks
View all comments
tentabrobpy
14 days ago
Reply to  wo_ri_gou_le

Here’s a quick attempt at fixing the normals, add this right before line 50:

float normal_z = VERTEX.y == 0.0 ? NORMAL.z * 0.16  : 0.0;
NORMAL = vec3(sin(theta), -cos(theta) * -sign(VERTEX.y), normal_z);

The Z is pretty hard to get right considering the distortion.

tentabrobpy
14 days ago

It’s jank, but I love it

tentabrobpy
12 days ago
Reply to  wo_ri_gou_le

Glad to help, I think it’s a very fun and ambitious shader. 😄 Re:normals, the idea is whatever function you use to displace the vertices you can use its derivative to recalculate the normals. I’m no calculus expert so I only came up with a very basic expression, lol. A more general approach is to use finite differences.