233 горішка для Попелюшки: відбираємо кольору для «ідеальної» палітри



У процесі розробки гри в текстовому режимі, мені довелося намалювати більше сотні анімаційних ASCII спрайтів. Після релізу гра отримала несподівано хороші відгуки і було прийнято рішення робити продовження. Малюючи спрайт для першої частини і перепробувавши з десяток варіантів вибору кольору і кілька десятків різних палітр, я зрозумів, що потрібен свій, «ідеальний» набір кольорів на всі часи. За сотні і сотні годин малювання, склалися наступні критерії ідеальності палітри:

  • Стислість: невелику кількість кольорів в палітрі. Весь набір квітів можна охопити одним поглядом.
  • Повнота: кольори палітри повинні рівномірно і досить щільно заповнювати простір.
  • Дискретність: кольори палітри повинні відрізнятися один від одного на око.
  • Угруповання: кольори повинні бути зручно згруповані для швидкого знаходження потрібного.
Виявилося, що можна підібрати набір з рівного 233 квітів, який задовольнить всім цим критеріям.

Починаючи відбирати кольору, поглянемо на все колірне простір. Його можна представити у вигляді RGB куба, HSB циліндра/конуса — і це тільки найпопулярніші адитивні моделі.



Вибирати колір з «нутрощів» цих тривимірних фігур не зручно. Тому графічні редактори придумують різні зручні і красиві способи вибрати довільний колір. Але зверніть увагу, що в кожної колірної моделі все одно як мінімум три виміри.



Для ASCII графіки весь цей «континуум» квітів не потрібен. Вибір довільного кольору тільки ускладнює редагування. Справа в тому, що кожен символ займає досить велику площу на екрані і символи як правило не стикаються. Щоб помітити різницю, кольору символів повинні відрізнятися на око.



Отже, потрібна дискретна палітра. Різних палітр — безліч. Це як сучасні панелі, так і ретро палітри, що прийшли з минулого комп'ютерної техніки.



Не дивлячись на все різноманіття, я не знайшов палітру, яка б відповідала моїм вимогам. Це і не дивно. Ретро панелі визначалися можливостями заліза. Сучасні ж палітри складаються, як правило, для цілей реалізації певного стилю в дизайні.

Більшість графічних редакторів дозволяють формувати користувальницькі панелі. Але вбудованих можливостей групування кольорів палітри виявилося не достатньо для моїх цілей. Отже, я прийняв рішення зібрати палітру сам.

Процедурна палітра?

Що, якщо рівномірно розподілити точки-кольори палітри всередині колірного простору? Наприклад, кожну компоненту кольору в моделі RGB розділити на 6 частин. Вийти 216 кольорів.

Проблема в тому, що людина сприймає кольори не лінійно. Зелені відтінки панелі «зіллються», а сині будуть занадто дискретні. Ось цікавий матеріал, присвячений кольорам:
Poynton's Color FAQ

Тільки ручний відбір!

Розроблена в 70-их роках модель HSB, є нелінійним перетворенням моделі RGB, дозволяє набагато більш інтуїтивно вибирати кольору. Першим компонентом моделі є Hue – Колірний тон. Які бувають колірні тони? Наприклад, як у мнемонічної фразі: «Кожен мисливець бажає знати, де сидить фазан».

7 колірних тонів: Червоний, Оранжевий, Жовтий, Зелений, Блакитний, Синій, Фіолетовий

Цього явно мало! А де, наприклад, рожевий або бірюзовий? Більш повний набір з 12-ти тонів дає палітра «циферблата», де кожній годині на циферблаті відповідає свій колір.

12 колірних тонів: Червоний, Оранжевий, Жовтий, Лайм, Зелений, Аквамарин, Бірюзовий, Блакитний, Синій, Фіолетовий, Рожевий, Малиновий

Отримані значення Hue все ще занадто дискретні. Я додав кілька тонів, щоб різниця-на-віч між сусідніми тонами була однакова. Тепер результат мене влаштував.

15 колірних тонів: Червоний, Оранжевий, Золотий, Жовтий, Лайм, Зелений, Аквамарин, Бірюзовий, Блакитний, Синій, Ультрамарин, Фіолетовий, Рожевий, Малиновий, Червоний



Для кожного з 15-ти колірних тонів можна задавати компоненти Saturation – Насиченість і Brightness – Яскравість. Ось, наприклад, множини значень насиченості і яскравості для помаранчевого, фіолетового і лайма.



Існує відома палітра, в якій компоненти насиченості і яскравості витягнуті в одну лінію.



Може здатися, що в цій палітрі представлено взагалі все колірне простір. Ось вона — ідеальна палітра з добре згрупованими квітами. Але ні, тут повністю відсутні кольору з не одиничної яскравістю і насиченістю. Наприклад, колір бузку, виділений на картинці вище — не можливо зменшити його яскравість в рамках даної палітри.

Цікаво, що кольори панелі утворюють поверхню циліндра/конуса моделі HSB. Кольору з внутрішніх частин циліндра/конуса — відсутні.

Повернемося до наших 15-ти колірних тонів. Побудуємо для кожного дискретну розкладку по компонентам яскравості і насиченості. Значення компонент виберемо рівномірно наступним чином: 100%, 80%, 60%, 40%, 20%.



Часто, простір значень яскравості і насиченості представляють не у вигляді квадрата, а у вигляді трикутника. Справа в тому, що око людини гірше розрізняє зміна насиченості при більш низькій яскравості.

В нашому випадку, трикутник виходить так: для ряду з більш низькою яскравістю надається менше варіантів насиченості.



Непогано! Тепер приступимо до більш тонкої настройки. Для кожного тону, компоненти яскравості і насиченості були розподілені рівномірно. Але око людини сприймає яскравість нелінійно. Більш того, для різних колірних тонів сприйняття яскравості — різне.

Однієї, найбільш часто застосовується формулою для оцінки яскравості кольору, є наступна:



Треба розуміти, що яскравість кольору залежить як від типу монітора, так і від індивідуальних особливостей зору людини, а ця формула була виведена ще для CRT моніторів. Тим не менш, вона відображає той факт, що внесок у загальну яскравість синього компоненту менше ніж внесок червоного, який, у свою чергу, менше вкладу зеленого. Це видно неозброєним поглядом, достатньо, наприклад, порівняти синій колір з яскравістю 20% і зелений з яскравістю 20%. Синій набагато темніше!

Для того, щоб вирівняти темні кольори і зробити візуальне зменшення яскравості більш лінійним, я застосував наступне перетворення:





Формула виглядає дещо громіздко, але насправді вона проста. На вхід подається рівень яскравості Y, колірний тон (r, g, b), а так само кількість градацій яскравості n. Усередині великих дужок відбувається вирівнювання темних кольорів в залежності від тону. Зведення в ступінь 0.7 робить зміна яскравості більш рівномірним візуально. Ступеня 0.15 і 0.7 я підбирав вручну, намагаючись домогтися кращого результату.

Для вирівнювання насиченості мені було досить наступного простого перетворення:



Ступінь 0.65 так само підбиралася вручну. На представленій нижче GIF анімації наводиться варіант до перетворення і після. Фон я зробив чорним, щоб краще було видно темні кольори. Судіть самі, чи стали кольору, яскравості і насиченості більш рівномірно розподілені. На мій погляд — так, стало краще.



Залишилося додати відтінки сірого і згрупувати отримані кольору. В результаті вийшла така палітра.



Я отримав 233 горішка для попелюшки — 233 кольору для редактора ASCII графіки, яка прекрасна!

RGB подання кольорів палітри у вигляді JSON
{
"Червоний": [
[
{"r": 255, "g": 0, "b": 85},
{"r": 179, "g": 0, "b": 59},
{"r": 131, "g": 0, "b": 43},
{"r": 91, "g": 0, "b": 30},
{"r": 54, "g": 0, "b": 18}
],
[
{"r": 255, "g": 79, "b": 138},
{"r": 179, "g": 55, "b": 96},
{"r": 131, "g": 41, "b": 71},
{"r": 91, "g": 28, "b": 49}
],
[
{"r": 255, "g": 124, "b": 168},
{"r": 179, "g": 87, "b": 118},
{"r": 131, "g": 64, "b": 86}
],
[
{"r": 255, "g": 162, "b": 193},
{"r": 179, "g": 114, "b": 135}
],
[
{"r": 255, "g": 195, "b": 215}
]
],
"Малиновий": [
[
{"r": 255, "g": 0, "b": 162},
{"r": 178, "g": 0, "b": 113},
{"r": 130, "g": 0, "b": 82},
{"r": 89, "g": 0, "b": 56},
{"r": 52, "g": 0, "b": 33}
],
[
{"r": 255, "g": 79, "b": 191},
{"r": 178, "g": 55, "b": 133},
{"r": 130, "g": 40, "b": 97},
{"r": 89, "g": 27, "b": 67}
],
[
{"r": 255, "g": 124, "b": 207},
{"r": 178, "g": 87, "b": 145},
{"r": 130, "g": 63, "b": 106}
],
[
{"r": 255, "g": 162, "b": 221},
{"r": 178, "g": 113, "b": 154}
],
[
{"r": 255, "g": 195, "b": 233}
]
],
"Рожевий": [
[
{"r": 255, "g": 0, "b": 255},
{"r": 177, "g": 0, "b": 177},
{"r": 129, "g": 0, "b": 129},
{"r": 87, "g": 0, "b": 87},
{"r": 50, "g": 0, "b": 50}
],
[
{"r": 255, "g": 79, "b": 255},
{"r": 177, "g": 55, "b": 177},
{"r": 129, "g": 40, "b": 129},
{"r": 87, "g": 27, "b": 87}
],
[
{"r": 255, "g": 124, "b": 255},
{"r": 177, "g": 86, "b": 177},
{"r": 129, "g": 63, "b": 129}
],
[
{"r": 255, "g": 162, "b": 255},
{"r": 177, "g": 113, "b": 177}
],
[
{"r": 255, "g": 195, "b": 255}
]
],
"Фіолетовий": [
[
{"r": 170, "g": 0, "b": 255},
{"r": 119, "g": 0, "b": 179},
{"r": 88, "g": 0, "b": 132},
{"r": 61, "g": 0, "b": 92},
{"r": 37, "g": 0, "b": 56}
],
[
{"r": 196, "g": 79, "b": 255},
{"r": 138, "g": 56, "b": 179},
{"r": 102, "g": 41, "b": 132},
{"r": 71, "g": 28, "b": 92}
],
[
{"r": 211, "g": 124, "b": 255},
{"r": 149, "g": 88, "b": 179},
{"r": 110, "g": 65, "b": 132}
],
[
{"r": 224, "g": 162, "b": 255},
{"r": 158, "g": 114, "b": 179}
],
[
{"r": 235, "g": 195, "b": 255}
]
],
"Ультрамарин": [
[
{"r": 98, "g": 0, "b": 255},
{"r": 70, "g": 0, "b": 182},
{"r": 52, "g": 0, "b": 136},
{"r": 37, "g": 0, "b": 98},
{"r": 24, "g": 0, "b": 63}
],
[
{"r": 146, "g": 79, "b": 255},
{"r": 105, "g": 56, "b": 182},
{"r": 78, "g": 42, "b": 136},
{"r": 56, "g": 30, "b": 98}
],
[
{"r": 174, "g": 124, "b": 255},
{"r": 124, "g": 89, "b": 182},
{"r": 93, "g": 67, "b": 136}
],
[
{"r": 198, "g": 162, "b": 255},
{"r": 141, "g": 116, "b": 182}
],
[
{"r": 218, "g": 195, "b": 255}
]
],
"Синій": [
[
{"r": 0, "g": 0, "b": 255},
{"r": 0, "g": 0, "b": 187},
{"r": 0, "g": 0, "b": 145},
{"r": 0, "g": 0, "b": 109},
{"r": 0, "g": 0, "b": 76}
],
[
{"r": 79, "g": 79, "b": 255},
{"r": 58, "g": 58, "b": 187},
{"r": 45, "g": 45, "b": 145},
{"r": 34, "g": 34, "b": 109}
],
[
{"r": 124, "g": 124, "b": 255},
{"r": 91, "g": 91, "b": 187},
{"r": 71, "g": 71, "b": 145}
],
[
{"r": 162, "g": 162, "b": 255},
{"r": 119, "g": 119, "b": 187}
],
[
{"r": 195, "g": 195, "b": 255}
]
],
"Блакитний": [
[
{"r": 0, "g": 145, "b": 255},
{"r": 0, "g": 100, "b": 176},
{"r": 0, "g": 72, "b": 128},
{"r": 0, "g": 49, "b": 86},
{"r": 0, "g": 27, "b": 48}
],
[
{"r": 79, "g": 179, "b": 255},
{"r": 55, "g": 124, "b": 176},
{"r": 39, "g": 90, "b": 128},
{"r": 26, "g": 60, "b": 86}
],
[
{"r": 124, "g": 198, "b": 255},
{"r": 86, "g": 137, "b": 176},
{"r": 62, "g": 99, "b":128}
],
[
{"r": 162, "g": 215, "b": 255},
{"r": 112, "g": 149, "b": 176}
],
[
{"r": 195, "g": 229, "b": 255}
]
],
"Бірюзовий": [
[
{"r": 0, "g": 255, "b": 255},
{"r": 79, "g": 255, "b": 255},
{"r": 124, "g": 255, "b": 255},
{"r": 162, "g": 255, "b": 255},
{"r": 195, "g": 255, "b": 255}
],
[
{"r": 0, "g": 173, "b": 173},
{"r": 62, "g": 173, "b": 173},
{"r": 97, "g": 173, "b": 173},
{"r": 127, "g": 173, "b": 173}
],
[
{"r": 0, "g": 121, "b": 121},
{"r": 53, "g": 121, "b": 121},
{"r": 83, "g": 121, "b": 121}
],
[
{"r": 0, "g": 78, "b": 78},
{"r": 44, "g": 78, "b": 78}
],
[
{"r": 0, "g": 38, "b": 38}
]
],
"Аквамарин": [
[
{"r": 0, "g": 255, "b": 170},
{"r": 79, "g": 255, "b": 196},
{"r": 124, "g": 255, "b": 211},
{"r": 162, "g": 255, "b": 224},
{"r": 195, "g": 255, "b": 235}
],
[
{"r": 0, "g": 173, "b": 115},
{"r": 62, "g": 173, "b": 136},
{"r": 98, "g": 173, "b": 148},
{"r": 127, "g": 173, "b": 158}
],
[
{"r": 0, "g": 122, "b": 81},
{"r": 53, "g": 122, "b": 99},
{"r": 83, "g": 122, "b": 109}
],
[
{"r": 0, "g": 79, "b": 52},
{"r": 44, "g": 79, "b": 67}
],
[
{"r": 0, "g": 40, "b": 26}
]
],
"Зелений": [
[
{"r": 0, "g": 255, "b": 0},
{"r": 79, "g": 255, "b": 79},
{"r": 124, "g": 255, "b": 124},
{"r": 162, "g": 255, "b": 162},
{"r": 195, "g": 255, "b": 195}
],
[
{"r": 0, "g": 174, "b": 0},
{"r": 62, "g": 174, "b": 62},
{"r": 98, "g": 174, "b": 98},
{"r": 128, "g": 174, "b": 128}
],
[
{"r": 0, "g": 124, "b": 0},
{"r": 54, "g": 124, "b": 54},
{"r": 84, "g": 124, "b": 84}
],
[
{"r": 0, "g": 81, "b": 0},
{"r": 46, "g": 81, "b": 46}
],
[
{"r": 0, "g": 42, "b": 0}
]
],
"Лайм": [
[
{"r": 196, "g": 255, "b": 0},
{"r": 214, "g": 255, "b": 79},
{"r": 224, "g": 255, "b": 124},
{"r": 233, "g": 255, "b": 162},
{"r": 241, "g": 255, "b": 195}
],
[
{"r": 131, "g": 171, "b": 0},
{"r": 146, "g": 171, "b": 61},
{"r": 154, "g": 171, "b": 97},
{"r": 161, "g": 171, "b": 126}
],
[
{"r": 91, "g": 119, "b": 0},
{"r": 104, "g": 119, "b": 52},
{"r": 110, "g": 119, "b": 81}
],
[
{"r": 57, "g": 75, "b": 0},
{"r": 67, "g": 75, "b": 42}
],
[
{"r": 27, "g": 35, "b": 0}
]
],
"Жовтий": [
[
{"r": 255, "g": 255, "b": 0},
{"r": 255, "g": 255, "b": 79},
{"r": 255, "g": 255, "b": 124},
{"r": 255, "g": 255, "b": 162},
{"r": 255, "g": 255, "b": 195}
],
[
{"r": 170, "g": 170, "b": 0},
{"r": 170, "g": 170, "b": 61},
{"r": 170, "g": 170, "b": 96},
{"r": 170, "g": 170, "b": 125}
],
[
{"r": 118, "g": 118, "b": 0},
{"r": 118, "g": 118, "b": 51},
{"r": 118, "g": 118, "b": 80}
],
[
{"r": 73, "g": 73, "b": 0},
{"r": 73, "g": 73, "b": 41}
],
[
{"r": 33, "g": 33, "b": 0}
]
],
"Золотий": [
[
{"r": 255, "g": 170, "b": 0},
{"r": 255, "g": 196, "b": 79},
{"r": 255, "g": 211, "b": 124},
{"r": 255, "g": 224, "b": 162},
{"r": 255, "g": 235, "b": 195}
],
[
{"r": 173, "g": 115, "b": 0},
{"r": 173, "g": 136, "b": 62},
{"r": 173, "g": 148, "b": 98},
{"r": 173, "g": 157, "b": 127}
],
[
{"r": 122, "g": 81, "b": 0},
{"r": 122, "g": 99, "b": 53},
{"r": 122, "g": 109, "b": 83}
],
[
{"r": 78, "g": 52, "b": 0},
{"r": 78, "g": 67, "b": 44}
],
[
{"r": 39, "g": 26, "b": 0}
]
],
"Помаранчевий": [
[
{"r": 255, "g": 94, "b": 0},
{"r": 255, "g": 144, "b": 79},
{"r": 255, "g": 172, "b": 124},
{"r": 255, "g": 196, "b": 162},
{"r": 255, "g": 217, "b": 195}
],
[
{"r": 175, "g": 64, "b": 0},
{"r": 175, "g": 104, "b": 63},
{"r": 175, "g": 127, "b": 99},
{"r": 175, "g": 146, "b": 129}
],
[
{"r": 126, "g": 46, "b": 0},
{"r": 126, "g": 81, "b": 54},
{"r": 126, "g": 100, "b": 86}
],
[
{"r": 83, "g": 30, "b": 0},
{"r": 83, "g": 60, "b": 47}
],
[
{"r": 45, "g": 16, "b": 0}
]
],
"Червоний": [
[
{"r": 255, "g": 0, "b": 0},
{"r": 255, "g": 79, "b": 79},
{"r": 255, "g": 124, "b": 124},
{"r": 255, "g": 162, "b": 162},
{"r": 255, "g": 195, "b": 195}
],
[
{"r": 180, "g": 0, "b": 0},
{"r": 180, "g": 64, "b": 64},
{"r": 180, "g": 101, "b": 101},
{"r": 180, "g": 132, "b": 132}
],
[
{"r": 133, "g": 0, "b": 0},
{"r": 133, "g": 57, "b": 57},
{"r": 133, "g": 90, "b": 90}
],
[
{"r": 93, "g": 0, "b": 0},
{"r": 93, "g": 52, "b": 52}
],
[
{"r": 57, "g": 0, "b": 0}
]
]
}

Буду радий бачити вас у своїй групі textpuzzlelab на Фейсбуці, де можна подивитися як я застосовую кольору панелі в зображеннях у стилі ASCII.



Робота над палітрою ще не закінчена. Поточна версія 0.8. Однією з проблем є те, що при зміні насиченості, критерій дискретності не зовсім виконується для жовтого, лайма, зеленого, аквамарину і бірюзового. Поки не придумав як бути.

З радістю прийму зауваження і пропозиції. Одна пара очей добре, а дві і більше — краще!
Джерело: Хабрахабр

0 коментарів

Тільки зареєстровані та авторизовані користувачі можуть залишати коментарі.