Procedural Hexagonal Pattern

Введение

Визуальные эффекты, основанные на гексагональной сетке, придают уникальный стиль играм и приложениям.

В этом материале мы рассмотрим реализацию гексагонального фрагментного шейдера для движка Godot Engine.

Он позволяет разбивать экран на гексагональные ячейки и окрашивать их в зависимости от положения UV-координат, создавая эффект «расплывающегося выбора» или «плавного выделения» внутри гекса.

Принцип работы

Шейдер делит экранное пространство на гексагональную сетку с помощью преобразования координат. Каждый пиксель определяет свою принадлежность к определённой ячейке, а затем вычисляет вклад (вес) в цвета соседних трёх гексагона. Это делает границы между ячейками мягкими и визуально привлекательными.

Основные шаги, выполняемые шейдером:

  1. Подготовка координат — UV-информация инвертируется по вертикали, масштабируется под заданную плотность сетки и трансформируется в осевые координаты гексагональной решётки.
  2. Округление и индексация — вычисляются ближайшие три гексагона, к которым пиксель может принадлежать.
  3. Локальные координаты — определяются координаты внутри текущей гекс-клетки и проверяется, нужно ли перевернуть направление.
  4. Вычисление влияния (весов) — пиксель «голосует» за три ближайших гексагона, и каждый получает свою долю в итоговом цвете.
  5. Фокусировка — веса поднимаются в степень, регулируемую параметром HexFocus, чтобы усилить влияние центра клетки и уменьшить вклад краёв.
  6. Нормализация и окрашивание — итоговые значения нормализуются и устанавливаются в качестве ALBEDO.

Пример кода

shader_type spatial;
render_mode blend_mix, depth_draw_opaque, cull_back, diffuse_lambert, specular_schlick_ggx;

uniform vec2 hex_grid_density = vec2(6.0, 6.0); // количество ячеек
uniform float hex_cell_size = 1.0;              // размер гекса
uniform float hex_focus = 6.0;                  // фокусировка цвета внутри гекса

void fragment() {
	vec2 uv = UV;

	// Переворот Y-координаты UV
	vec2 flipped_uv = vec2(uv.x, 1.0 - uv.y);

	// Масштабирование под hex-сетку
	vec2 grid_uv = flipped_uv * hex_grid_density;

	// Коэффициенты преобразования
	float sqrt3_div2 = 1.732 / 2.0;
	float inv_sqrt3 = 1.0 / (1.732);

	float axial_x = grid_uv.x - grid_uv.y * (sqrt3_div2 / 1.5);
	float axial_y = grid_uv.y * inv_sqrt3;
	vec2 axial_coords = vec2(axial_x, axial_y);

	// Нормализация относительно размера клетки
	vec2 cell_coords = axial_coords / vec2(hex_cell_size);
	vec2 floored_coords = floor(cell_coords);

	float cell_diff = floored_coords.x - floored_coords.y;

	// Вспомогательный вектор
	vec3 offsets = vec3(0.0, 1.0, 2.0);
	vec3 temp = vec3(cell_diff) + offsets;

	// Нормализация
	vec3 normalized = temp * vec3(1.0 / 3.0);
	normalized += vec3(2.0 / 3.0);

	// Получаем индексы вершин гексагона
	vec3 hex_index = round(fract(normalized));
	float i0 = hex_index.x;
	float i1 = hex_index.y;
	float i2 = hex_index.z;

	// Подготовка векторов для dot product'ов
	vec3 axis0 = vec3(i2, i0, i1);
	vec3 axis1 = vec3(i1, i2, i0);
	vec3 axis2 = vec3(i0, i1, i2);

	// Получаем локальные координаты внутри клетки
	vec2 local_coords = fract(cell_coords);
	float sum_coords = local_coords.x + local_coords.y;
	float abs_dist = abs(sum_coords - 1.0);

	bool flip_needed = (sum_coords - 1.0) > 0.0;

	vec2 flipped_coords = vec2(1.0) - vec2(local_coords.y, local_coords.x);
	vec2 chosen_coords = mix(local_coords, flipped_coords, float(flip_needed));

	vec3 influence = vec3(abs_dist, chosen_coords.x, chosen_coords.y);

	// Вычисляем веса влияния
	float w0 = dot(axis0, influence);
	float w1 = dot(axis1, influence);
	float w2 = dot(axis2, influence);
	vec3 weights = vec3(w0, w1, w2);

	// Усиление акцента с помощью степени
	vec3 focused_weights = pow(weights, vec3(hex_focus));

	// Нормализация
	float total = dot(focused_weights, vec3(1.0));
	vec3 final_color = focused_weights / vec3(total);

	ALBEDO = final_color;
}

Параметры

ПараметрОписание
hex_grid_densityКоличество гексагонов по X и Y. Изменяет плотность сетки.
hex_cell_sizeРазмер одной гекса-клетки. Можно использовать для масштабирования.
hex_focusСтепень фокусировки внутри ячейки. Чем выше, тем ярче центр ячейки.

Применение

Этот шейдер идеально подходит для:

  • процедурной генерации мозаичных материалов.
  • визуализации сеток и структур;
  • эффектов выбора или фокусировки в UI;
  • стилизованных экранных фильтров;

Структура Граффа - Алгоритма HEX GRID
Структура субграфов Алгоритма

Структура Нодов Визуального шейдера
Весь Алгоритм состоит из этих частей

Вы можете скачать этот алгоритм, перейдя по ссылке, или воспользовавшись сайтом: