Hexagon Highlight Shader for Godot Canvas Items

This shader highlights hexagonal regions on a canvas item in Godot, in the example below it works on a TileMap. It converts each fragment’s position into hexagonal cube coordinates to determine if it falls within a hexagon. If so, the shader blends the original texture color with red at a fixed 50% intensity, but you can do whatever color and intensity you want. Adjust the uniform parameters (hex size, active center, grid offset, and scaling) to fit your project’s grid layout.

Example C# code for testing:

public override void _Process(double delta)
	{
			Vector2 localMousePosition = ToLocal(GetGlobalMousePosition());
			Vector2I tileCoords = LocalToMap(localMousePosition);
			Vector2 centerCoords = MapToLocal(tileCoords);
			Vector2 activeCenter = ToGlobal(centerCoords);

			
			if (GetCellSourceId(0,tileCoords) != -1)
			{
				Vector2[] hexCenters = new Vector2[3];
				hexCenters[0] = activeCenter;
				hexCenters[1] = activeCenter + new Vector2(120, 0);
				hexCenters[2] = activeCenter + new Vector2(60, 105);
				_shaderMaterial?.SetShaderParameter("num_active_hexes", 3);
				_shaderMaterial?.SetShaderParameter("active_hex_centers", hexCenters);
			}
	}

Example GDScript code for testing:

func _process(delta):
	var local_mouse_position = to_local(get_global_mouse_position())
	var tile_coords = local_to_map(local_mouse_position)
	var center_coords = map_to_local(tile_coords)
	var active_center = to_global(center_coords)

	if get_cell_source_id(0, tile_coords) != -1:
		var hex_centers = [
			active_center,
			active_center + Vector2(120, 0),
			active_center + Vector2(60, 105)
		]
		shader_material.set_shader_parameter("num_active_hexes", 3)
		shader_material.set_shader_parameter("active_hex_centers", hex_centers)
Shader code
shader_type canvas_item;

const int MAX_HEXES = 64;
uniform int num_active_hexes;
uniform vec2 active_hex_centers[MAX_HEXES];

uniform vec2 hex_size = vec2(120.0, 140.0);
uniform vec2 grid_offset = vec2(0.0, 0.0);
uniform float q_scaling = 1.49;
varying vec2 frag_pos;

void vertex() {
    frag_pos = (MODEL_MATRIX * vec4(VERTEX, 0.0, 1.0)).xy;
}

void fragment() {
    // Flag to determine if the current fragment is within any hexagon
    bool insideHex = false;
    for (int i = 0; i < num_active_hexes; i++) {
        // Use grid_offset if needed (for overall grid positioning)
        vec2 hex_center = active_hex_centers[i] + grid_offset;
        vec2 delta = frag_pos - hex_center;

        // Convert to hexagonal cube coordinates with scaling adjustment
        float q = delta.x / (hex_size.x * 0.75) * q_scaling;
        float r = (delta.y - (delta.x / sqrt(3.0))) / (hex_size.y / 2.0);
        float s = -q - r;

        // Check if the fragment is within the hexagon
        float max_distance = max(max(abs(q), abs(r)), abs(s));
        if (max_distance <= 1.0) {
            insideHex = true;
            break;
        }
    }

    vec4 texture_color = texture(TEXTURE, UV);

    // Discard fragments with low alpha
    if (texture_color.a < 0.1) {
        discard;
    }

    // Apply a fixed red highlight mix if inside any hex
    if (insideHex) {
        COLOR = mix(texture_color, vec4(1.0, 0.0, 0.0, 1.0), 0.5);
    } else {
        COLOR = texture_color;
    }
}
Tags
canvas_item, godot, hexagon, hexgrid, highlight, shader, Tutorial
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.

Related shaders

Hexagon Shield

Hexagon pattern

Barycentric Hexagon for Spatial Nodes

Subscribe
Notify of
guest

0 Comments
Oldest
Newest Most Voted
Inline Feedbacks
View all comments