Форум Flasher.ru
Ближайшие курсы в Школе RealTime
Список интенсивных курсов: [см.]  
  
Специальные предложения: [см.]  
  
 
Блоги Правила Справка Пользователи Календарь Поиск рулит! Сообщения за день Все разделы прочитаны
 

Вернуться   Форум Flasher.ru > Блоги > ZackMercury

Оценить эту запись

Цветовая модель HSV(HSB)

Запись от ZackMercury размещена 03.07.2017 в 13:37
Обновил(-а) ZackMercury 06.07.2017 в 18:23

Что происходит? Где наш привычный RGB?

Что же такое RGB? Почему мы используем именно его повсюду, и чем он мне не угодил?


Цитата:
Цветовая модель RGB - это аддитивная цветовая модель, в которой красный, зеленый и синий свет объединяются различными способами для воспроизведения широкого спектра цветов. Название модели происходит от инициалов трех основных основных цветов: red-красного, green-зеленого и blue-синего.
Наверняка ни для кого не открытие, что большинство современных дисплеев состоит из сетки пикселей, где каждый пиксель содержит 3 лампы/светодиода, красного, зелёного и синего цветов. Для получения разных цветов цветовой палитры, они подаются в разных пропорциях. Например, для получения жёлтого смешивается красный и зелёный, а для получения пурпурного смешивается синий и красный. Для получения белого они все горят на максимальной мощности, а для получения чёрного - на минимальной. Конечно, смешивания происходят не всегда в равных пропорциях для получения наиболее сочных оттенков цвета.


Удобно ли это - думать о светодиодах, работая с компонентами цвета?

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

Hue-Saturation-Value, HSV.

Что-ж, в связи с неудобствами, связанными с моделью RGB, была
/*довольно давно*/
Цитата:
Модель была создана Элви Реем Смитом, одним из основателей Pixar, в 1978 году.
создана модель HSV, которая является дружелюбной для художников.

Цитата:
HSV (англ. Hue, Saturation, Value — тон, насыщенность, значение) или HSB (англ. Hue, Saturation, Brightness — тон, насыщенность, яркость) — цветовая модель, в которой координатами цвета являются:
  • Шкала оттенков — Hue
    Hue — цветовой тон, (например, красный, зелёный или сине-голубой). Варьируется в пределах 0—360°, однако иногда приводится к диапазону 0—100 или 0—1.
  • Saturation — насыщенность. Варьируется в пределах 0—100 или 0—1. Чем больше этот параметр, тем «чище» цвет, поэтому этот параметр иногда называют чистотой цвета. А чем ближе этот параметр к нулю, тем ближе цвет к нейтральному серому.
  • Value (значение цвета) или Brightness — яркость. Также задаётся в пределах 0—100 или 0—1.
Проще всего модель объясняет данная иллюстрация из Википедии:


Хоть она немного неправильная(S должен быть медианой к серому цвету, ну и до кучи, цвет посередине должен быть намного темнее).

UPD: Она - абсолютно правильная. То, что я представлял думая о HSV раньше на самом деле является HSL, и, скорее всего, в будущем будет статья об нём(мне он кажется наиболее привлекательным).

Градиенты! Теперь цвет будет переливаться всеми цветами радуги, путешествуя по цветовому колесу при интерполяции от одного значения HSV до другого!

Нажмите на изображение для увеличения
Название: Снимок.JPG
Просмотров: 356
Размер:	17.6 Кб
ID:	562
Изображение взято отсюда: https://www.youtube.com/watch?v=E_pfLqNeS7s

Зачем это нужно? Ну, разумеется, это нужно никому другому больше, чем художникам и дизайнерам, работающим с кодом.
Достаточно отнять от 360 компонент Hue, чтобы получить 2 комплиментарных(complementary) цвета.
Достаточно отнять от каждого одинаковое число в обе стороны, чтобы получить разделённые комплиментарные(split-complementary) цвета, и проделать то же с обратной стороной, чтобы получить двойные разделённые комплиментарные(double complementary) цвета.
Для тех, кто не знаком с теорией цвета, это называется цветовой гармонией или цветовой схемой, и определённым образом выстраивая фигуры на цветовом колесе можно получить неплохо сочетающиеся друг с другом цвета.


Изображение взято отсюда: https://coloursandmaterials.wordpres...colour-system/

Конечно, в использовании этой модели есть свои недостатки. Например,

Цитата:
При целочисленном кодировании для каждого цвета в HSV есть соответствующий цвет в RGB. Однако обратное утверждение не является верным: некоторые цвета в RGB нельзя выразить в HSV так, чтобы значение каждого компонента было целым. Фактически, при таком кодировании доступна только 1/256 часть цветового пространства RGB.
Это значит, что для цветов придётся использовать числа с плавающей точкой, хотя раньше они обходились гораздо дешевле(всего одним четырёхбайтовым целым числом).

Мы будем сегодня кодить?

Да, да, конечно, я хочу понять, как преобразовать RGB->HSV и обратно, поэтому давайте возьмём вот эту формулу из Википедии:

Нажмите на изображение для увеличения
Название: Снимок1.JPG
Просмотров: 306
Размер:	51.4 Кб
ID:	565

Здесь всё просто, сначала находим минимум и максимум.

Код AS3:
var rgb:Object = {r:0xFF, g:0xAA, b:0x22}; //Сочный оранжевый цвет
rgb.r /= 0xFF;
rgb.g /= 0xFF;
rgb.b /= 0xFF; //Нам нужны значения от нуля до единицы
 
var min:Number = Math.min(rgb.r, rgb.g, rgb.b); //Находим минимум
var max:Number = Math.max(rgb.r, rgb.g, rgb.b); //Находим максимум
Далее находим тон.

Код AS3:
var h:Number;
if (max == min) h = 0; //если цвет серый, ничего не надо считать
else if ((max == rgb.r) && (rgb.g >= rgb.b)) h = (60 * (rgb.g - rgb.b)) / (max - min); //если красного больше, всего, и зелёного больше или столько же, сколько и синего
else if ((max == rgb.r) && (rgb.g < rgb.b)) h = (60 * (rgb.g - rgb.b)) / (max - min) + 360; //если красного больше всего и зелёного меньше, чем синего
else if (max == rgb.g) h = (60 * (rgb.b - rgb.r)) / (min - max) + 120; //если зелёного больше всего
else h = (60 * (rgb.r - rgb.g)) / (max - min) + 240; //если синего больше всего
Здесь вдаваться в подробности того, как работает эта формула я не стану, работает и достаточно.

Далее - насыщенность.

Код AS3:
var s:Number;
if(max == 0) s = 0;
else s = 1 - min / max;
Здесь всё просто: чем больше разбросаны компоненты цвета, тем больше насыщенность, чем все они ближе друг к другу, тем менее насыщенность. Например, #000000 - чёрный, #ffffff - белый, #dddddd - светло-серый.
Здесь мы делим минимальное на максимальное, чтобы получить соотношение того, насколько цвет обесцвеченный(0 - вообще не обесцвеченный, может быть при красном, зелёном и синем, также жёлтом , магента и циан(F00, 0F0, 00F, FF0, F0F, 0FF), 1 - полностью обесцвеченный, будет у серых тонов, чёрного и белого, и затем инвертируем, чтобы получить насколько цвет насыщенный, отнимая от единицы обесцвеченность).


Ну и конечно же значение цвета.

Код AS3:
var v:Number = max;
HSV->RGB

Оттуда же возьмём формулу для преобразования цвета обратно из HSV в RGB:

Нажмите на изображение для увеличения
Название: Снимок3.JPG
Просмотров: 411
Размер:	42.0 Кб
ID:	566

Сначала считаем значения Hi, Vmin, Vinc, Vdec

Код AS3:
var hsv:Object = {h:h, s:s*100, v:v*100};
var hi:int = int(hsv.h / 60) % 6; //делим тон на 6 промежутков, для каждого отдельно будем считать компоненты RGB
var vMin:int = (100 - hsv.s) * hsv.v / 100; 
 
var a:Number = (hsv.v - vMin) * (hsv.h % 60) / 60;
var vInc:Number = vMin + a;
var vDec:Number = hsv.v - a;
Далее в зависимости от того, в каком цветовом промежутке мы находимся, мы по-разному арранжируем посчитанные наши значения для компонентов RGB.

Код AS3:
var rgb:Object = {r:0, g:0, b:0};
switch(hi)
{
    case 0:
        rgb.r = hsv.v;
        rgb.g = vInc;
        rgb.b = vMin; 
        break;
    case 1:
        rgb.r = vDec;
        rgb.g = hsv.v;
        rgb.b = vMin; 
        break;
    case 2:
        rgb.r = vMin;
        rgb.g = hsv.v;
        rgb.b = vInc; 
        break;
    case 3:
        rgb.r = vMin;
        rgb.g = vDec;
        rgb.b = hsv.v; 
        break;
    case 4:
        rgb.r = vInc;
        rgb.g = vMin;
        rgb.b = hsv.v; 
        break;
    case 5:
        rgb.r = hsv.v;
        rgb.g = vMin;
        rgb.b = vDec; 
        break;
}
 
rgb.r = int(rgb.r/100*255);
rgb.g = int(rgb.g/100*255);
rgb.b = int(rgb.b/100*255);
Теперь необходимо запустить весь код, и проверить, чтобы на выходе получился тот же цвет.

Код AS3:
var rgb:Object = {r:0xFF, g:0xAA, b:0x22}; //Сочный оранжевый цвет
rgb.r /= 0xFF;
rgb.g /= 0xFF;
rgb.b /= 0xFF; //Нам нужны значения от нуля до единицы
 
var min:Number = Math.min(rgb.r, rgb.g, rgb.b); //Находим минимум
var max:Number = Math.max(rgb.r, rgb.g, rgb.b); //Находим максимум
var h:Number;
if (max == min) h = 0; //если цвет серый, ничего не надо считать
else if ((max == rgb.r) && (rgb.g >= rgb.b)) h = (60 * (rgb.g - rgb.b)) / (max - min); //если красного больше, всего, и зелёного больше или столько же, сколько и синего
else if ((max == rgb.r) && (rgb.g < rgb.b)) h = (60 * (rgb.g - rgb.b)) / (max - min) + 360; //если красного больше всего и зелёного меньше, чем синего
else if (max == rgb.g) h = (60 * (rgb.b - rgb.r)) / (min - max) + 120; //если зелёного больше всего
else h = (60 * (rgb.r - rgb.g)) / (max - min) + 240; //если синего больше всего
var s:Number;
if(max == 0) s = 0;
else s = 1 - min / max;
var v:Number = max;
 
var hsv:Object = {h:h, s:s*100, v:v*100};
var hi:int = int(hsv.h / 60) % 6; //делим тон на 6 промежутков, для каждого отдельно будем считать компоненты RGB
var vMin:int = (100 - hsv.s) * hsv.v / 100; 
 
var a:Number = (hsv.v - vMin) * (hsv.h % 60) / 60;
var vInc:Number = vMin + a;
var vDec:Number = hsv.v - a;
 
rgb = {r:0, g:0, b:0};
switch(hi)
{
	case 0:
		rgb.r = hsv.v;
		rgb.g = vInc;
		rgb.b = vMin; 
		break;
	case 1:
		rgb.r = vDec;
		rgb.g = hsv.v;
		rgb.b = vMin; 
		break;
	case 2:
		rgb.r = vMin;
		rgb.g = hsv.v;
		rgb.b = vInc; 
		break;
	case 3:
		rgb.r = vMin;
		rgb.g = vDec;
		rgb.b = hsv.v; 
		break;
	case 4:
		rgb.r = vInc;
		rgb.g = vMin;
		rgb.b = hsv.v; 
		break;
	case 5:
		rgb.r = hsv.v;
		rgb.g = vMin;
		rgb.b = vDec; 
		break;
}
rgb.r = Math.round(rgb.r / 100 * 255);
rgb.g = Math.round(rgb.g / 100 * 255);
rgb.b = Math.round(rgb.b / 100 * 255);
trace(rgb.r, rgb.g, rgb.b); //FF AA 21 - синий компонент ошибся на одну единицу, ну что-ж, не так уж и страшно.
Теперь нужно организовать это всё в 2 метода конвертации, и завернуть в утилиту.

Закончил с вот таким классом. Давайте теперь склепаем пару демок!

Код AS3:
package com.zackmercury.test 
{
	/**
	 * ...
	 * @author ZackMercury
	 */
	public class ColorHSV 
	{
		private var _hue:Number;
		private var _saturation:Number;
		private var _value:Number;
 
		public function ColorHSV(hue:Number, saturation:Number, value:Number) 
		{
			this.hue = hue;
			this.saturation = saturation;
			this.value = value;
		}
 
		public function getRGB():Object
		{
			var hi:int = int(_hue / 60) % 6; //делим тон на 6 промежутков, для каждого отдельно будем считать компоненты RGB
			var vMin:int = (100 - _saturation * 100) * _value; 
 
			var a:Number = (_value * 100 - vMin) * (_hue % 60) / 60;
			var vInc:Number = vMin + a;
			var vDec:Number = _value * 100 - a;
 
			var rgb:Object = {r:0, g:0, b:0};
			switch(hi)
			{
				case 0:
					rgb.r = _value;
					rgb.g = vInc / 100;
					rgb.b = vMin / 100; 
					break;
				case 1:
					rgb.r = vDec / 100;
					rgb.g = _value;
					rgb.b = vMin / 100; 
					break;
				case 2:
					rgb.r = vMin / 100;
					rgb.g = _value;
					rgb.b = vInc / 100; 
					break;
				case 3:
					rgb.r = vMin / 100;
					rgb.g = vDec / 100;
					rgb.b = _value; 
					break;
				case 4:
					rgb.r = vInc / 100;
					rgb.g = vMin / 100;
					rgb.b = _value; 
					break;
				case 5:
					rgb.r = _value;
					rgb.g = vMin / 100;
					rgb.b = vDec / 100; 
					break;
			}
 
			return rgb;
		}
 
		public function setRGB(rgb:Object):void
		{
			if (rgb.r > 1) rgb.r = 1;
			else if (rgb.r < 0) rgb.r = 0;
			if (rgb.g > 1) rgb.g = 1;
			else if (rgb.g < 0) rgb.g = 0;
			if (rgb.b > 1) rgb.b = 1;
			else if (rgb.b < 0) rgb.b = 0; //корректировка значений
 
			var min:Number = Math.min(rgb.r, rgb.g, rgb.b); //Находим минимум
			var max:Number = Math.max(rgb.r, rgb.g, rgb.b); //Находим максимум
 
			if (max == min) _hue = 0; //если цвет серый, ничего не надо считать
			else if ((max == rgb.r) && (rgb.g >= rgb.b)) 
				_hue = (60 * (rgb.g - rgb.b)) / (max - min); //если красного больше, всего, и зелёного больше или столько же, сколько и синего
			else if ((max == rgb.r) && (rgb.g < rgb.b)) 
				_hue = (60 * (rgb.g - rgb.b)) / (max - min) + 360; //если красного больше всего и зелёного меньше, чем синего
			else if (max == rgb.g) 
				_hue = (60 * (rgb.b - rgb.r)) / (min - max) + 120; //если зелёного больше всего
			else 
				_hue = (60 * (rgb.r - rgb.g)) / (max - min) + 240; //если синего больше всего
			if(max == 0) _saturation = 0;
			else _saturation = 1 - min / max;
			_value = max;
		}
 
		public function setHex(hex:uint):void
		{
			var rgb:Object = { r: (hex >> 16) & 0xFF, g: (hex >> 8) & 0xFF, b: hex & 0xFF };
			rgb.r /= 0xFF;
			rgb.g /= 0xFF;
			rgb.b /= 0xFF; //Нам нужны значения от нуля до единицы
 
			setRGB(rgb);
		}
 
		public function getHex():uint
		{
			var rgb:Object = getRGB();
			return (Math.round(rgb.r * 0xFF) << 16) | (Math.round(rgb.g * 0xFF) << 8) | (Math.round(rgb.b * 0xFF));
		}
 
		//GET/SET
		public function get hue():Number 
		{
			return _hue;
		}
 
		public function set hue(val:Number):void 
		{
			if (val < 0) //Немного математики для зацикливания от 0 до 360 для всех значений.
				val += Math.ceil(-val / 360) * 360;
			if (val >= 360)
				val -= int(val / 360) * 360;
			_hue = val;
		}
 
		public function get saturation():Number 
		{
			return _saturation;
		}
 
		public function set saturation(val:Number):void 
		{
			if (val < 0)
				val = 0;
			if (val > 1)
				val = 1; //корректировка значений
			_saturation = val;
		}
 
		public function get value():Number 
		{
			return _value;
		}
 
		public function set value(val:Number):void 
		{
			if (val < 0)
				val = 0;
			if (val > 1)
				val = 1; //корректировка значений
			_value = val;
		}
 
	}
 
}
WindowsStyle.swf   (425.1 Кб)

Код AS3:
package com.zackmercury.test
{
	import com.bit101.components.Label;
	import com.bit101.components.PushButton;
	import flash.display.Bitmap;
	import flash.display.BitmapData;
	import flash.display.Sprite;
	import flash.events.Event;
	import flash.events.MouseEvent;
	import flash.geom.Point;
	import flash.text.Font;
	import flash.text.TextFormat;
	import flash.text.TextFormatAlign;
	import flash.utils.getTimer;
	/**
	 * ...
	 * @author ZackMercury
	 */
	public class Main extends Sprite 
	{
		private var clr:ColorHSV;
		[Embed(source="../../../../fonts/segoeuil.ttf",
        fontName = "SegoeUILight",
		fontWeight = "normal",
		fontStyle = "normal",
        mimeType = "application/x-font",
        advancedAntiAliasing="true",
        embedAsCFF="false")]
		public static const SEGOEUILIGHT_FONT:Class;
 
		public static const W:int = 640, H:int = 480;		
 
		public function Main() 
		{
			if (stage) init();
			else addEventListener(Event.ADDED_TO_STAGE, init);
		}
 
		private function init(e:Event = null):void 
		{
			removeEventListener(Event.ADDED_TO_STAGE, init);
			// entry point
 
			var lbl:Label = new Label(this, 180, H / 2 - 20, "");
			var font:Font = new SEGOEUILIGHT_FONT() as Font;
			var tf:TextFormat = new TextFormat(font.fontName, 20, 0xFFFFFF, null, null, null, null, null, TextFormatAlign.CENTER);
			lbl.textField.width = W;
			lbl.textField.defaultTextFormat = tf;
			lbl.text = "Привет! Мы настраиваем ваш ПК";
 
			clr = new ColorHSV(0, 1, 1);
			addEventListener(Event.ENTER_FRAME, update);
 
		}
 
		private function update(e:Event):void
		{
			graphics.clear();
			clr.hue = getTimer() / 100;
			graphics.beginFill(clr.getHex());
			graphics.drawRect(0, 0, W, H);
			graphics.endFill();
		}
	}
}
Wheel.swf   (410.7 Кб)

Код AS3:
package com.zackmercury.test
{
	import com.bit101.components.Label;
	import com.bit101.components.PushButton;
	import flash.display.Bitmap;
	import flash.display.BitmapData;
	import flash.display.Sprite;
	import flash.events.Event;
	import flash.events.MouseEvent;
	import flash.geom.Point;
	import flash.text.Font;
	import flash.text.TextFormat;
	import flash.text.TextFormatAlign;
	import flash.utils.getTimer;
	/**
	 * ...
	 * @author ZackMercury
	 */
	public class Main extends Sprite 
	{
		private var clr:ColorHSV;
		[Embed(source="../../../../fonts/segoeuil.ttf",
        fontName = "SegoeUILight",
		fontWeight = "normal",
		fontStyle = "normal",
        mimeType = "application/x-font",
        advancedAntiAliasing="true",
        embedAsCFF="false")]
		public static const SEGOEUILIGHT_FONT:Class;
 
		public static const W:int = 640, H:int = 480;
 
 
		public function Main() 
		{
			if (stage) init();
			else addEventListener(Event.ADDED_TO_STAGE, init);
		}
 
		private function distance(x1:Number, y1:Number, x2:Number, y2:Number):Number
		{
			return Math.sqrt(Math.pow(x1 - x2, 2) + Math.pow(y1 - y2, 2));
		}
 
		private function init(e:Event = null):void 
		{
			removeEventListener(Event.ADDED_TO_STAGE, init);
			// entry point
			clr = new ColorHSV(0, 1, 1);
 
			var innerRadius:Number = 100, outerRadius:Number = 199;
			var bmp:Bitmap = new Bitmap(new BitmapData(400, 400, true, 0x00000000), "auto", true);
			for (var ox:int = 0; ox < bmp.width; ox ++)
				for (var oy:int = 0; oy < bmp.height; oy ++)
					if (distance(ox, oy, bmp.width / 2, bmp.height / 2) <= outerRadius)
					{
						var angle:Number = Math.atan2(oy - bmp.height / 2, ox - bmp.width / 2);
						clr.hue = angle / Math.PI * 180;
						clr.value = Math.sqrt(distance(ox, oy, bmp.width / 2, bmp.height / 2) / innerRadius);
 
						bmp.bitmapData.setPixel32(ox, oy, clr.getHex() | 0xFF000000);
					}
 
			addChild(bmp);
		}
	}
}
Спасибо блогу Alan Zucconi, без которого я бы узнал о существовании HSV не скоро, пытаясь сделать свой велосипед вроде цветового колеса.
Вложения
Тип файла: swf WindowsStyle.swf (425.1 Кб, 388 просмотров)
Тип файла: swf Wheel.swf (410.7 Кб, 418 просмотров)
Всего комментариев 3

Комментарии

Старый 04.07.2017 11:48 ZackMercury вне форума
ZackMercury
 
Аватар для ZackMercury
PHP код:
Хоть она немного неправильная(S должен быть медианой к серому цветуну и до кучицвет посередине должен быть намного темнее). 
Я ошибся, там всё верно, так как я попутал HSV с HSL, где параметр Lightness при максимальном значении всегда даёт белый.



Вот, что у меня вышло с колесом, в попытках сделать его симпатичнее.

Старый 16.10.2017 10:26 Dzzirtuoz вне форума
Dzzirtuoz
Было бы неплохо увидеть такое же колесо реализованное работой исключительно с RGB. Ну так, чтобы видеть очевидную(или не очень) разницу в градиенте. Потому что как-то не хочется верить в скриншот видео из ютуба. Мало ли, как там поработали над картинкой в угоду HSV. Я не придираюсь, просто когда не с чем сравнить, то наглядности нет.
А вообще, если скриншот сравнения не врет, то и мне тоже стоит посмотреть в сторону HSV, потому что его градиент выглядит очень вкусно и элегантно на фоне градиента RGB
Обновил(-а) Dzzirtuoz 16.10.2017 в 10:28 (Дополнил)
Старый 16.10.2017 12:48 ZackMercury вне форума
ZackMercury
 
Аватар для ZackMercury
Dzzirtuoz, но при этом учтите, что HSV градиента между двумя цветами может быть 2 - по двум сторонам между цветами, ну и не всегда такой градиент будет тем, что нам нужно.

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

Реализовать колесо с помощью RGB - это уже задача не программистов. Можно считать, что с помощью RGB этого сделать нельзя без преобразования его в что-то из семейства Hue-Saturation.

Градиенты в RGB делаются с помощью обычной интерполяции компонентов, поэтому они могут переходить в серый по пути
Вот мой градиент с довольно удалёнными цветами друг от друга, около половины круга.

Если взять цвета, которые ближе друг к другу, то выглядит это приятнее.


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

Код AS3:
var clr:ColorHSV = new ColorHSV(0, 1, 1);
_groups.splice(0);
 
for (var i:int = 0; i < _stones; i ++)
{
	clr.hue = (i / (_stones)) * 360 + _options.hueSlider.value;
	clr.value = _options.valueShiftSlider.value + Math.pow(Math.cos(i+_options.valueWaveShiftSlider.value), 2) / 3 * _options.valueAmplitudeSlider.value;
	clr.saturation = _options.saturationShiftSlider.value + Math.pow(Math.cos(i+_options.saturationWaveShiftSlider.value), 2) / 3 * _options.saturationAmplitudeSlider.value;
	_groups.push(clr.getHex());
}
Обновил(-а) ZackMercury 16.10.2017 в 15:03
 

 


Часовой пояс GMT +4, время: 01:56.


Copyright © 1999-2008 Flasher.ru. All rights reserved.
Работает на vBulletin®. Copyright ©2000 - 2024, Jelsoft Enterprises Ltd. Перевод: zCarot
Администрация сайта не несёт ответственности за любую предоставленную посетителями информацию. Подробнее см. Правила.