How to write the "Alpha Hash" in shader code?

Godot Version

4.2.1

Question

Hey -
the standard material in godot has the Alpha-hash transparency setting as an option!
This is a very cool form of alpha masking.
image

But how do one write that in code?

(I’ve searched and searched and it seems this is hard to find! :face_exhaling:)

instead of discarding if alpha > set_value use a noise texture or function

if (alpha > texture(noise_texture, UV).r) {
    discard
}
1 Like

I’ll try it thanks!

After selecting alpha hash in a material, you can click on the menu to create a material, and select “convert to shader”. You can then just use the provided code.

2 Likes

:exploding_head::exploding_head::exploding_head::exploding_head:Really? Wow. Will def do!

Ok, after much fiddle. I got this working. For posterity! Thanks all

uniform vec3 albedo : source_color = vec3(0.02, 0.45, 0.8);
uniform sampler2D albedo_tex;
uniform sampler2D noise_tex;

float hash_2d(vec2 p) {
	return fract(1.0e4 * sin(17.0 * p.x + 0.1 * p.y) *
			(0.1 + abs(sin(13.0 * p.y + p.x))));
}

float hash_3d(vec3 p) {
	return hash_2d(vec2(hash_2d(p.xy), p.z));
}

float compute_alpha_hash_threshold(vec3 pos, float hash_scale) {
	vec3 dx = dFdx(pos);
	vec3 dy = dFdy(pos);

	float delta_max_sqr = max(length(dx), length(dy));
	float pix_scale = 1.0 / (hash_scale * delta_max_sqr);

	vec2 pix_scales =
			vec2(exp2(floor(log2(pix_scale))), exp2(ceil(log2(pix_scale))));

	vec2 a_thresh = vec2(hash_3d(floor(pix_scales.x * pos.xyz)),
			hash_3d(floor(pix_scales.y * pos.xyz)));

	float lerp_factor = fract(log2(pix_scale));

	float a_interp = (1.0 - lerp_factor) * a_thresh.x + lerp_factor * a_thresh.y;

	float min_lerp = min(lerp_factor, 1.0 - lerp_factor);

	vec3 cases = vec3(a_interp * a_interp / (2.0 * min_lerp * (1.0 - min_lerp)),
			(a_interp - 0.5 * min_lerp) / (1.0 - min_lerp),
			1.0 - ((1.0 - a_interp) * (1.0 - a_interp) / (2.0 * min_lerp * (1.0 - min_lerp))));

	float alpha_hash_threshold =
			(a_interp < (1.0 - min_lerp)) ? ((a_interp < min_lerp) ? cases.x : cases.y) : cases.z;

	return clamp(alpha_hash_threshold, 0.00001, 1.0);
}

void fragment() {
	vec2 base_uv = UV;
	vec4 albedo_tex_ = texture(albedo_tex,base_uv);
	vec4 noise_tex_ = texture(noise_tex,base_uv);
	ALBEDO = albedo.rgb * albedo_tex_.rgb;
	
	if (albedo_tex_.a < compute_alpha_hash_threshold(VERTEX, 0.5)) {
		discard;
	}
	
	//ALPHA = albedo_tex.a;
}

Also note, your code is equivalent to writing:

uniform vec3 albedo : source_color = vec3(0.02, 0.45, 0.8);
uniform sampler2D albedo_tex;
uniform sampler2D noise_tex;

void fragment() {
	vec2 base_uv = UV;
	vec4 albedo_tex_ = texture(albedo_tex,base_uv);
	vec4 noise_tex_ = texture(noise_tex,base_uv);
	ALBEDO = albedo.rgb * albedo_tex_.rgb;
	
	ALPHA_HASH_SCALE = 0.5;
	
	//ALPHA = albedo_tex.a;
}
1 Like

It is? But there is almost nothing. ALPHA_HASH_SCALE does it all without all the jam?

Yep!

When you use ALPHA_HASH_SCALE in the shader, you enable the following code block:

1 Like

tried this but it just makes the mesh completely opaque.
0.1
0.0001
0.99
doesnt do any diff.

luckily this works splendid

	if (albedo_tex_.a < compute_alpha_hash_threshold(VERTEX, 0.5)) {
		discard;
	}

just doesnt look very optimized.

I suppose you still need to assign alpha whne you use the alpha threshold built-in, otherwise Godot can’t know the alpha to compare against.