Программирование

Шейдер Godot не обнаруживает синий пиксель выше: как исправить

Почему фрагментный шейдер canvas_item в Godot не меняет прозрачный пиксель на красный при синем сверху? Анализ ошибки в логике, правильный сэмплинг с TEXTURE_PIXEL_SIZE и godot uv, полный исправленный код godot шейдеры для 2D эффектов.

4 ответа 1 просмотр

Шейдер Godot не обнаруживает синий цвет пикселя выше и не меняет текущий цвет на красный

В шейдере Godot код должен проверять значение канала B пикселя выше (above.b > 0.9) и, если текущий пиксель имеет низкую альфа-канал (current.a < 0.1), изменять его цвет на красный. Однако этого не происходит.

Проблемный фрагмент:

glsl
else if (current.a < 0.1){
 if (above.b > 0.9 && current.a > 0.9) {
 COLOR.rgba = RED;
 }
}

При изменении цвета фона шейдер успешно обнаруживает синие пиксели в других случаях, но не реагирует, когда синий пиксель находится выше.

Полный код шейдера (canvas_item):

glsl
shader_type canvas_item;
void fragment() {
 vec4 below = texture(TEXTURE, UV + vec2(0.0, TEXTURE_PIXEL_SIZE.y));
 vec4 above = texture(TEXTURE, UV - vec2(0.0, TEXTURE_PIXEL_SIZE.y));
 vec4 current = texture(TEXTURE, UV);

 vec4 RED = vec4(1.0, 0.0, 0.0, 1.0);
 vec4 BLUE = vec4(0.0, 0.0, 1.0, 1.0);
 vec4 EMPTY = vec4(0.0, 0.0, 0.0, 0.0);

 COLOR = current;

 if (current.r > 0.9 && current.a > 0.9){
 if (below.a < 0.1) {
 COLOR.rgba = BLUE;
 }
 }
 else if (current.b > 0.9 && current.a > 0.9) {
 if (below.r > 0.9 && current.a > 0.9) {
 COLOR.rgba = EMPTY;
 }
 }
 else if (current.a < 0.1){
 if (above.b > 0.9 && current.a > 0.9) {
 COLOR.rgba = RED;
 }
 }
}

Почему шейдер не работает как ожидается? Как исправить обнаружение цвета соседних пикселей в фрагментном шейдере Godot?

Ваш шейдер Godot не обнаруживает синий пиксель выше из‑за логической ошибки: в блоке else if (current.a < 0.1) условие above.b > 0.9 && current.a > 0.9 никогда не сработает, потому что альфа не может быть одновременно меньше 0.1 и больше 0.9. Просто уберите && current.a > 0.9 — и шейдер в godot шейдеры заработает как надо, меняя прозрачные пиксели на красный при синем соседе сверху. Сэмплинг через TEXTURE_PIXEL_SIZE и godot uv уже правильный, это стандарт для canvas_item в Godot 4 shaders.


Содержание


Проблема с шейдером Godot: почему не обнаруживается синий пиксель выше

Представьте: вы пишете godot шейдеры для крутого 2D‑эффекта, где прозрачные пиксели «реагируют» на синие сверху, становясь красными. Звучит просто, но бац — ничего не происходит. Почему? Всё дело в том фрагменте кода, который вы выделили.

В блоке else if (current.a < 0.1) идёт проверка if (above.b > 0.9 && current.a > 0.9). Здесь чистое противоречие. Альфа‑канал current.a не может быть меньше 0.1 (условие блока) и больше 0.9 одновременно. Логика коротит, if никогда не выполняется, COLOR не меняется на RED.

А когда меняете фон? Шейдер ловит синие пиксели в других ветках — там условия не противоречат. Это классическая засада в godot shaders: копируете логику из одного места в другое, забывая подкорректировать. Но сэмплинг above через UV - vec2(0.0, TEXTURE_PIXEL_SIZE.y) верный — пиксель выше берётся точно.


Анализ полного кода шейдера godot шейдеры

Давайте разберём весь ваш код по косточкам. Он в shader_type canvas_item;, что идеально для 2D‑спрайтов и текстур. Вот что происходит шаг за шагом:

Сначала сэмплим три пикселя:

  • below: снизу (UV + vec2(0.0, TEXTURE_PIXEL_SIZE.y)).
  • above: сверху (UV - vec2(0.0, TEXTURE_PIXEL_SIZE.y)).
  • current: сам пиксель.

Определяем константы: RED, BLUE, EMPTY. COLOR = current; — базовый цвет.

Первая ветка: если текущий красный (current.r > 0.9 && current.a > 0.9) и снизу прозрачно (below.a < 0.1), то красим в BLUE. Работает?

Вторая: текущий синий (current.b > 0.9 && current.a > 0.9), снизу красный (below.r > 0.9) — делаем EMPTY. Тоже ок, но дублирует current.a > 0.9.

Третья — ваша проблема: прозрачный пиксель (current.a < 0.1), синий сверху (above.b > 0.9), но с лишним && current.a > 0.9. Удалите это, и godot шейдеры оживут.

Код логичен для цепной реакции цветов — красный «капает» синим вниз, синий стирает себя при красном снизу, прозрачка краснеет от синего сверху. Но та опечатка всё ломает. Согласно документации Godot, в fragment() COLOR перезаписывается только явно, иначе остаётся оригинал.


TEXTURE_PIXEL_SIZE и godot uv: правильный сэмплинг соседних пикселей

Один из самых хитрых моментов в godot uv — как точно взять соседний пиксель? UV — нормализованные координаты от 0.0 до 1.0, текстура размером 64x32 пикселя имеет TEXTURE_PIXEL_SIZE = vec2(1/64, 1/32).

Ваш подход супер:

  • Сверху: UV - vec2(0.0, TEXTURE_PIXEL_SIZE.y) — смещение вверх на высоту одного пикселя.
  • Снизу: + vec2(0.0, TEXTURE_PIXEL_SIZE.y).

Это даёт точный доступ без размытия. Если текстура большая, ошибка минимальна. Но! Если фильтрация «Linear», texture() усреднит с соседями — поставьте «Nearest» в импортере текстуры для текстуры для godot.

На Reddit r/godot народ подтверждает: TEXTURE_PIXEL_SIZE — золотой стандарт для пиксельных эффектов в godot 2d shader. Альтернатива? SCREEN_PIXEL_SIZE, но оно для экрана, не текстуры. Не путайте!

А если спрайт масштабирован? UV подстраивается автоматически в canvas_item.


Исправленный код фрагментного шейдера canvas_item в Godot

Вот полный рабочий код для Godot 4 shaders. Я убрал противоречия, почистил дубли (лишние current.a > 0.9 в if), добавил комментарии. Тестировал мысленно — должно летать.

glsl
shader_type canvas_item;

void fragment() {
 vec4 below = texture(TEXTURE, UV + vec2(0.0, TEXTURE_PIXEL_SIZE.y));
 vec4 above = texture(TEXTURE, UV - vec2(0.0, TEXTURE_PIXEL_SIZE.y));
 vec4 current = texture(TEXTURE, UV);

 vec4 RED = vec4(1.0, 0.0, 0.0, 1.0);
 vec4 BLUE = vec4(0.0, 0.0, 1.0, 1.0);
 vec4 EMPTY = vec4(0.0, 0.0, 0.0, 0.0);

 COLOR = current;

 // Красный сверху -> синий вниз, если прозрачно
 if (current.r > 0.9 && current.a > 0.9) {
 if (below.a < 0.1) {
 COLOR.rgba = BLUE;
 }
 }
 // Синий -> стираем себя, если красный снизу
 else if (current.b > 0.9 && current.a > 0.9) {
 if (below.r > 0.9) {
 COLOR.rgba = EMPTY;
 }
 }
 // Прозрачный -> красный, если синий сверху
 else if (current.a < 0.1) {
 if (above.b > 0.9) {
 COLOR.rgba = RED;
 }
 }
}

Скопируйте в ShaderMaterial на Sprite2D. Работает? Проверьте альфы — 0.9/0.1 строгие, можно смягчить до 0.8/0.2 для градиентов.


Дополнительные советы по текстурам для godot и фильтрации

Текстуры для godot — ключ к шейдерам. Импортируйте PNG с альфой, фильтр «Nearest» для пикселей. Масштаб спрайта? Не ломает UV.

Хотите круче? Добавьте uniform:

glsl
uniform float threshold : hint_range(0.0, 1.0) = 0.9;

И используйте вместо 0.9. Или blur‑сэмплинг: textureLod(TEXTURE, UV_above, 0.0).

Для анимации — несколько текстур в AtlasTexture. На GitHub Godot docs есть про specular, но для вас не надо.

Тестируйте в редакторе: запустите сцену, меняйте текстуру — шейдер живой.


Частые ошибки в godot 2d shader и как их избежать

Бывает у всех с godot pixel shader:

  • Забыли shader_type canvas_item; — шейдер не компилится.
  • UV без TEXTURE_PIXEL_SIZE — берёт не пиксель, а кусок.
  • Linear фильтр размывает цвета — Nearest спасает.
  • COLOR.a = 0.0 вместо EMPTY — альфа не сбрасывается.
  • Противоречия в if, как у вас.

Ещё: в Godot 4 vec4 константы лучше uniform vec4. И профилируйте — лишние texture() жрут FPS.

Избегайте: не трогайте vertex(), если не надо. Читайте официальные туториалы — там золото.


Источники

  1. Документация Godot Engine (4.4) — Справочник по canvas_item шейдерам и переменным: https://docs.godotengine.org/en/4.4/tutorials/shaders/shader_reference/canvas_item_shader.html
  2. Документация Godot Engine (stable) — Основы фрагментных шейдеров canvas_item: https://docs.godotengine.org/en/stable/tutorials/shaders/shader_reference/canvas_item_shader.html
  3. Godot Docs на GitHub — Описание TEXTURE_PIXEL_SIZE и UV в шейдерах: https://github.com/godotengine/godot-docs/blob/master/tutorials/shaders/shader_reference/canvas_item_shader.rst
  4. Reddit r/godot — Обсуждение TEXTURE_PIXEL_SIZE для пиксельного сэмплинга: https://www.reddit.com/r/godot/comments/gsplbn/help_needed_in_2d_shaders/
  5. Reddit r/godot — Примеры смещения UV в шейдерах Godot: https://www.reddit.com/r/godot/comments/nqq0s5/how_to_set_uv_coordinates_in_shaders_in_godot_to/

Заключение

В godot шейдеры главное — логика без противоречий и точный сэмплинг через TEXTURE_PIXEL_SIZE. Уберите лишнюю проверку альфы, и ваш эффект с красным под синим взлетит. Экспериментируйте с threshold, Nearest‑фильтром — получится огонь для godot 2d shader. Если не сработает, киньте скрин текстуры — разберёмся!

Шейдер не работает из-за логической ошибки в условии: внутри блока else if (current.a < 0.1) проверяется above.b > 0.9 && current.a > 0.9, но current.a > 0.9 противоречит current.a < 0.1 и никогда не выполняется. Исправьте на if (above.b > 0.9), удалив && current.a > 0.9. Сэмплинг пикселя выше через texture(TEXTURE, UV - vec2(0.0, TEXTURE_PIXEL_SIZE.y)) корректен — TEXTURE_PIXEL_SIZE даёт размер одного пикселя в UV-пространстве для точного доступа к соседям.

GitHub / Платформа для хостинга кода и совместной разработки

Ваш код правильно использует TEXTURE_PIXEL_SIZE.y для смещения на пиксель выше (UV - vec2(0.0, TEXTURE_PIXEL_SIZE.y)). Проблема исключительно в противоречивом условии current.a < 0.1 и current.a > 0.9 — удалите второе. Это стандартный способ доступа к соседним пикселям в фрагментных шейдерах canvas_item, как указано в официальных источниках документации.

Сэмплинг соседних пикселей в Godot шейдерах делается через смещение UV на TEXTURE_PIXEL_SIZE (нормализованный размер пикселя: 1/ширина, 1/высота). Ваш offset для above верен, используйте с фильтрацией ‘nearest’ для точности. Основная ошибка — логическое противоречие в условиях альфы текущего пикселя; исправьте условие на проверку только above.b > 0.9.

Авторы
Источники
Документация
GitHub / Платформа для хостинга кода и совместной разработки
Платформа для хостинга кода и совместной разработки
Форум
Проверено модерацией
Модерация
Шейдер Godot не обнаруживает синий пиксель выше: как исправить