Цвет: RGB, HSB и прозрачность

В этом уроке наш цыплёнок CodeChick наконец перестанет быть серым: ты научишься смешивать цвет из красного, зелёного и синего, плавно перебирать оттенки через HSB и делать фигуры полупрозрачными.
Цвет в p5.js — это всего лишь набор чисел: говоришь библиотеке, сколько в краске красного, зелёного и синего, и она смешивает оттенок прямо на холсте.

Зачем тебе это нужно

До этого момента наш CodeChick жил в чёрно-белом мире. В прошлом уроке про первые формы ты собрал его из круга-тела, круга-головы и треугольника-клюва — но всё это было серым, как набросок карандашом. Согласись, настоящий цыплёнок так не выглядит. Он жёлтый и пушистый, с ярким оранжевым клювом, как будто его только что нарисовали неоновым маркером.

Цвет — это то, что превращает скучную геометрию в картинку, на которую хочется смотреть. Подумай про свои любимые игры или обложки треков в плеере: половина их вайба держится именно на цвете. Кислотный фиолетовый фон, мягкое свечение, полупрозрачные блики — всё это можно описать парой чисел и поставить в код.

К концу урока ты сможешь:

  • покрасить любую фигуру в нужный цвет через модель RGB;
  • плавно перебирать оттенки радуги через модель HSB (это пригодится для анимаций позже);
  • делать фигуры полупрозрачными через канал alpha и накладывать их друг на друга, как стёкла светофильтров;
  • и, конечно, покрасить нашего CodeChick: тело — в жёлтый, клюв — в оранжевый.

Вот к чему мы придём в самом конце — держи этот кадр в голове как цель:

function setup() {
  createCanvas(400, 400);
}

function draw() {
  background(135, 206, 235); // нежно-голубое небо
  noStroke();

  fill(255, 221, 51);   // жёлтое тело
  ellipse(200, 230, 160, 160);

  fill(255, 235, 120);  // голова чуть светлее
  ellipse(200, 140, 110, 110);

  fill(255, 140, 0);    // оранжевый клюв
  triangle(200, 140, 250, 155, 200, 175);

  fill(40);             // чёрный глаз
  ellipse(225, 130, 14, 14);
}

Результат: на голубом фоне сидит жёлтый круглый цыплёнок с головой посветлее, ярко-оранжевым клювом-треугольником и маленьким чёрным глазом. Наконец-то он выглядит как живой CodeChick, а не как карандашный набросок. Дальше мы разберём каждое число в этом коде по косточкам.

Главная идея: цвет — это рецепт из трёх банок краски

Представь, что у тебя есть три банки светящейся краски: красная, зелёная и синяя. Ты смешиваешь их в разных пропорциях и получаешь любой цвет. Чем больше плеснёшь красной — тем краснее. Добавишь немного зелёной к красной — получится оранжевый. Нальёшь поровну всех трёх — выйдет белый.

Именно так и устроен экран твоего телефона: каждая точка-пиксель — это крошечная тройка красной, зелёной и синей лампочек. Меняя их яркость, экран рисует тебе и мемы, и игры, и этот текст. p5.js даёт тебе те же три банки в виде чисел.

RGB — модель цвета, в которой оттенок задаётся долями красного (Red), зелёного (Green) и синего (Blue).

В p5.js каждая «банка» — это число от 0 (краска не льётся совсем) до 255 (краска на максимум). Почему именно 255, а не 100? Это техническая традиция компьютеров: в один байт помещается ровно 256 разных значений, от 0 до 255. Можешь воспринимать это просто как «шкала яркости от нуля до 255».

Чтобы задать цвет фигуры, используют функцию fill() — она задаёт заливку, то есть цвет, которым закрашивается внутренняя площадь фигуры.

Заливка (fill) — цвет, которым закрашивается внутренняя площадь фигуры.

А контур фигуры — это обводка, за неё отвечает stroke().

Обводка (stroke) — цвет и линия контура фигуры или самой линии.

Как читать три числа в fill()

Когда ты пишешь fill(255, 221, 51), ты говоришь: «красной — почти на максимум, зелёной — много, синей — совсем чуть-чуть». Много красного и зелёного без синего как раз и дают жёлтый. Вот маленькая шпаргалка, которую полезно запомнить:

КодRGBЧто получится
fill(255, 0, 0)25500Чистый красный
fill(0, 255, 0)02550Ярко-зелёный
fill(0, 0, 255)00255Синий
fill(255, 255, 0)2552550Жёлтый (красный + зелёный)
fill(255, 140, 0)2551400Оранжевый (клюв!)
fill(0, 0, 0)000Чёрный (краски нет)
fill(255, 255, 255)255255255Белый (всё на максимум)

Заметь забавную штуку: на экране «нет краски» (все нули) — это чёрный, а «вся краска» (все 255) — белый. Это противоположно тому, как смешиваются краски в красках на бумаге, где побольше краски — темнее. Дело в том, что экран сам светится, и мы смешиваем не пигменты, а свет. Чем больше света от всех трёх лампочек — тем ближе к белому.

Короткая запись: оттенки серого

Если дать fill() только одно число, p5.js поставит его сразу во все три банки. fill(0) — это то же самое, что fill(0, 0, 0), то есть чёрный. fill(128) — ровно посередине, серый. fill(255) — белый. Это удобный способ быстро задать любой оттенок серого, не повторяя число трижды. Помнишь чёрный глаз цыплёнка из первого примера? Там стояло как раз fill(40) — почти чёрный.

Разбираем на примерах

Пример 1. Красим тело цыплёнка в жёлтый

Начнём с самого главного — сделаем CodeChick жёлтым. Разберём код построчно.

function setup() {
  createCanvas(400, 400);
}

function draw() {
  background(245);          // светло-серый фон
  noStroke();              // убираем контур у фигур
  fill(255, 221, 51);      // жёлтая краска: R=255, G=221, B=51
  ellipse(200, 200, 160, 160); // круглое тело в центре
}

Результат: на почти белом фоне в центре холста — большой ровный жёлтый круг без контура. Это тело будущего цыплёнка.

Что здесь происходит, по шагам:

  1. createCanvas(400, 400) создаёт квадратный холст 400 на 400 пикселей — наш лист скетчбука.
  2. background(245) заливает фон светло-серым (одно число — значит оттенок серого).
  3. noStroke() говорит: рисуй фигуры без контура. Без этой строки у круга была бы тонкая чёрная обводка по умолчанию.
  4. fill(255, 221, 51) — выбираем жёлтую краску. Эта строчка действует на всё, что нарисуем после неё.
  5. ellipse(200, 200, 160, 160) рисует эллипс с центром в точке (200, 200) шириной и высотой по 160 — получается круг.

Важнейший момент про порядок: fill() работает как кисть, которую ты обмакнул в банку. Пока не обмакнёшь в другую банку, все фигуры красятся текущим цветом. Поэтому цвет всегда ставят перед фигурой, которую хотят им покрасить.

Пример 2. Добавляем оранжевый клюв

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

function draw() {
  background(245);
  noStroke();

  fill(255, 221, 51);          // жёлтый — для тела и головы
  ellipse(200, 230, 160, 160); // тело
  ellipse(200, 140, 110, 110); // голова

  fill(255, 140, 0);           // меняем кисть на оранжевую
  triangle(200, 140, 250, 150, 200, 170); // клюв
}

Результат: на сером фоне жёлтый цыплёнок из двух кругов (тело и голова сверху), а сбоку от головы торчит маленький оранжевый треугольник-клюв.

Главная мысль примера: одну краску можно использовать для нескольких фигур подряд. Мы поставили жёлтый один раз и нарисовали и тело, и голову. А потом сменили кисть на оранжевый — и всё, что идёт дальше, красится оранжевым. Если бы ты поставил fill(255, 140, 0) в самом начале, оранжевыми вышли бы и тело, и голова. Попробуй мысленно переставить строки и представь результат — это лучший способ прочувствовать, как работает порядок.

Поэкспериментируй: поменяй fill(255, 140, 0) на fill(255, 80, 0) — клюв станет более красно-морковным. А fill(255, 180, 40) сделает его медово-золотистым. Меняй среднее число (зелёный канал) и смотри, как оранжевый перетекает от красного к жёлтому.

Пример 3. HSB — радуга в одно число

Допустим, ты хочешь сделать цыплёнка-радугу или плавно перекрасить фон через все цвета спектра. В RGB это мучение: чтобы пройти красный → оранжевый → жёлтый → зелёный → голубой, придётся одновременно крутить три числа в разные стороны. Неудобно. Для таких задач есть вторая модель цвета — HSB.

HSB — модель цвета через тон (Hue), насыщенность (Saturation) и яркость (Brightness); удобна для плавного перебора оттенков.

Представь круглую палитру художника — цветовое колесо. Hue (тон) — это угол на этом колесе: крути его, и цвет едет по радуге. Saturation (насыщенность) — насколько цвет сочный: 0 — это серый, 100 — кричаще-яркий. Brightness (яркость) — насколько он светлый: 0 — чёрный, 100 — во всю мощь.

Чтобы переключить p5.js в режим HSB, в начале вызывают colorMode(HSB). Удобно сразу задать диапазоны: тон по кругу 0–360 градусов, а насыщенность и яркость — 0–100, как проценты.

function setup() {
  createCanvas(400, 200);
  colorMode(HSB, 360, 100, 100); // тон 0-360, насыщенность и яркость 0-100
  noStroke();
}

function draw() {
  background(0, 0, 100); // белый фон (яркость 100, насыщенности нет)
  // рисуем 12 кружков-перьев, каждое со своим тоном
  for (let i = 0; i < 12; i++) {
    let tone = i * 30;          // 0, 30, 60 ... 330 градусов
    fill(tone, 90, 90);         // сочный и яркий
    ellipse(30 + i * 30, 100, 26, 26);
  }
}

Результат: на белом фоне выстроились в ряд 12 кружков — настоящая радуга: от красного слева через оранжевый, жёлтый, зелёный, голубой, синий к фиолетовому справа. Каждый следующий кружок — это тон, сдвинутый на 30 градусов по цветовому колесу.

Видишь, в чём магия? Чтобы пройти всю радугу, мы крутим одно число — tone. Насыщенность и яркость держим постоянными. В RGB такое нарисовать было бы куда муторнее. Запомни правило: RGB удобен, когда ты знаешь конкретный цвет; HSB — когда хочешь плавно перебирать оттенки. В уроках про анимацию мы будем медленно увеличивать tone от кадра к кадру и получим переливающийся фон — но это позже.

Пример 4. Прозрачность — стёкла светофильтров

Последний кирпичик — прозрачность. Представь цветные стёкла или плёнки-светофильтры: накладываешь жёлтое стекло на синее — и сквозь них просвечивает зеленоватый цвет. В p5.js за это отвечает четвёртый параметр цвета — alpha.

Прозрачность (alpha) — четвёртый параметр цвета, задающий, насколько фигура просвечивает то, что под ней.

Вернёмся в режим RGB. Теперь у fill() может быть четыре числа: fill(R, G, B, alpha). Alpha тоже идёт от 0 до 255, где 0 — фигура полностью прозрачная (невидимая), а 255 — полностью непрозрачная (как обычно). Значение посередине, например 100, делает фигуру похожей на цветное стекло.

function setup() {
  createCanvas(400, 300);
  noStroke();
}

function draw() {
  background(255); // белый фон

  fill(255, 0, 0, 120);   // красный, полупрозрачный
  ellipse(160, 150, 180, 180);

  fill(0, 0, 255, 120);   // синий, полупрозрачный
  ellipse(240, 150, 180, 180);
}

Результат: два больших круга — красный слева и синий справа — частично накладываются друг на друга. В зоне пересечения сквозь оба стекла просвечивает фиолетовый цвет, как будто два светофильтра наложили один на другой. Будь alpha равной 255, верхний круг просто перекрыл бы нижний без всякого смешивания.

Где это пригодится нашему CodeChick? Например, можно нарисовать у него мягкие розовые щёчки полупрозрачным кругом поверх жёлтой головы — и щёки не закрасят голову наглухо, а лишь подрумянят её. А ещё прозрачный alpha — секрет красивых «хвостов» за движущимися объектами в анимации, но об этом тоже в следующих модулях.

Частые ошибки и подводные камни

На цвете новички спотыкаются предсказуемо. Вот пять граблей — обходи их.

1. fill() стоит после фигуры

Самая частая ошибка. Ты пишешь сначала ellipse(...), а fill() ставишь строкой ниже — и удивляешься, почему круг не пожелтел. Запомни: краску берут до того, как рисуют. Цвет действует только на фигуры, которые идут после него в коде.

2. Числа больше 255 или меньше 0

В RGB каждый канал живёт в диапазоне 0–255. Если написать fill(300, 0, 0), p5.js не сломается, но и ярче «максимально красного» не станет — лишнее просто обрежется до 255. А отрицательные числа превратятся в 0. Так что не жди, что fill(500, 0, 0) будет «суперкрасным», это всё тот же fill(255, 0, 0).

3. Забыл colorMode и удивляешься, почему HSB врёт

Если ты не вызвал colorMode(HSB, ...), то p5.js по умолчанию читает три числа как RGB. Тогда fill(60, 90, 90) ты задумывал как яркий жёлтый по тону, а получишь тускло-серо-зелёный, потому что библиотека приняла это за R=60, G=90, B=90. Правило: хочешь HSB — переключи режим в setup() заранее.

4. Перепутал заливку и обводку

Иногда хочешь покрасить площадь фигуры, а меняешь цвет контура — или наоборот. fill() — это про внутреннюю заливку, stroke() — про линию контура. Если контур тебе вообще не нужен, вызови noStroke(); если не нужна заливка (только контур) — noFill().

5. Ждёшь, что alpha сработает на одиночной фигуре

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

Мини-практика: раскрась своего CodeChick

Теперь твоя очередь. Возьми код финального цыплёнка из начала урока и доведи его до собственного стиля. Сделай по шагам:

  1. Смени небо. Поменяй background(135, 206, 235) на закатное небо. Подсказка: для тёплого заката добавь побольше красного и поменьше синего, например background(255, 150, 100).
  2. Подбери свой жёлтый. Поиграй с fill(255, 221, 51): сделай цыплёнка более лимонным (подними зелёный) или более золотым (опусти зелёный, добавь немного красноты в клюв).
  3. Добавь щёчки. После головы нарисуй два маленьких полупрозрачных розовых круга: fill(255, 150, 150, 100) и два ellipse(...) по бокам клюва. Благодаря alpha они лягут румянцем, а не пятном.
  4. Сделай радужный вариант. Отдельным скетчем переключись в colorMode(HSB, 360, 100, 100) и покрась тело цыплёнка случайным тоном — поставь, например, fill(200, 80, 95) и посмотри, в какой цвет он оденется. Меняй первое число и лови момент, когда он станет мятным, потом сиреневым.

Цель — почувствовать руками, как числа превращаются в настроение картинки. Не бойся ставить «неправильные» значения: в худшем случае цыплёнок выйдет кислотным, и это нормально для экспериментов.

Итоги

Соберём всё в одну картину:

  • RGB — цвет как смесь трёх банок краски: красной, зелёной и синей, каждая от 0 до 255. fill(255, 221, 51) — жёлтый, fill(255, 140, 0) — оранжевый клюв.
  • Одно число в fill() — это оттенок серого: fill(0) чёрный, fill(255) белый.
  • HSB — цвет через тон, насыщенность и яркость; крутишь один тон (Hue) — и едешь по всей радуге. Включается через colorMode(HSB, ...).
  • alpha — четвёртое число цвета, прозрачность от 0 до 255; полупрозрачные фигуры накладываются, как цветные стёкла.
  • fill() красит заливку, stroke() — контур; цвет всегда ставят до фигуры.
  • Главные грабли: fill() после фигуры, забытый colorMode, путаница заливки и обводки.

Наш CodeChick больше не серый набросок — он жёлтый, с оранжевым клювом и собственным небом за спиной. В следующем уроке этого модуля мы займёмся обводкой и стилем линий поплотнее, а затем научим цыплёнка двигаться: добавим draw()-анимацию, и те самые цвета и прозрачность, что ты освоил сегодня, оживут в движении.

Проверьте себя
1. Что означают три числа в вызове fill(255, 221, 51) в режиме по умолчанию?
AШирину, высоту и поворот фигуры
BДоли красного, зелёного и синего цвета
CКоординаты x, y и радиус
DТон, насыщенность и яркость
2. Какой цвет даст fill(0, 0, 0) на экране?
AБелый, потому что краски нет
BЧёрный, потому что все лампочки погашены
CПрозрачный
DСерый
3. Почему модель HSB удобнее RGB, когда нужно плавно перебрать оттенки радуги?
AПотому что в HSB всего одно число
BПотому что в HSB достаточно крутить один параметр — тон (Hue), а остальные держать постоянными
CПотому что RGB не умеет рисовать жёлтый
DПотому что HSB ярче
4. За что отвечает четвёртый параметр в fill(255, 0, 0, 120)?
AЗа толщину контура
BЗа прозрачность (alpha): насколько фигура просвечивает то, что под ней
CЗа яркость экрана
DЗа номер цвета в палитре
5. Ты написал ellipse(200, 200, 100, 100), а строкой ниже fill(255, 0, 0). Почему круг не стал красным?
AПотому что fill принимает только одно число
BПотому что цвет действует только на фигуры, нарисованные ПОСЛЕ него, а круг нарисован раньше
CПотому что нужно вызвать noStroke()
DПотому что 255 — слишком большое число
6. Что произойдёт, если в режиме RGB написать fill(500, 0, 0)?
AСкетч сломается с ошибкой
BЦвет станет в два раза ярче обычного красного
CЛишнее обрежется до 255, получится обычный максимально красный
DФигура станет прозрачной