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

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

Рейтинг: 5.00. Голосов: 6.

Зажигаем свет в 2d.

Запись от samana размещена 11.12.2013 в 01:14
Обновил(-а) samana 25.02.2014 в 14:11

Здравствуйте. Хочу поделиться с вами, одним из вариантов, как можно создать 2d свет на actionscript 3.0. Сначала хотел сделать подробный урок для начинающих, но объяснять все свои действия оказалось настолько сложно, что я решил просто выложить свою наработку и кратко рассказать о том, как она работает и как ей пользоваться.
Свет можно делать и мягким и чётким. Пожалуй лучше всего, увидеть это на деле. В обоих флешках, подвигайте мышкой. К сожалению, лично у меня в браузере, обе подтормаживают.
Мягкий свет с прозрачными тенями :
NewProjectLightEdges.swf   (89.5 Кб)


и чёткий свет с непрозрачными тенями:
FiguresLight.swf   (37.4 Кб)


Немного теории. Свет будет идти из одной точки во все стороны. Для того чтобы свет не проникал сквозь объекты, надо сначала вычислить тень от объекта и просто не пускать в её область свет. Ведь тень – это отсутствие света.
Вид тени прямиком зависит от внешнего вида объекта, который её отбрасывает. Точнее от силуэта объекта или контура, как вам удобнее. По этому этот контур придётся как-то обозначить, но об этом чуть позже.
Возьмём самый простой вариант – найдём тень от простой линии. У линии есть точки A и B. Находим для каждой точки угол к свету. Затем проецируем дополнительную точку на нужное нам расстояние в направлении найденного угла и получаем – границы тени. Осталось только нарисовать из полученных четырёх точек – прямоугольную фигуру и тень готова!
Название: find points.gif
Просмотров: 1575

Размер: 4.9 Кб
Теперь осталось лишь обложить нужный объект по его контуру такими линиями (далее рёбрами) и всё.

Для создания таких рёбер и служит класс ShadowObject. К сожалению, он не создаст автоматически рёбра по контуру объекта, он может лишь составить рёбра из переданных ему точек. По этому нахождения этих точек, лежит полностью на ваших плечах, но это не так и сложно. ShadowObject – не визуальный класс. Это просто набор данных.
Ещё у этого класса, есть ссылка на любой визуальный объект, которому должен принадлежать этот контур из рёбер. Таких контуров в ShadowObject–е можно создавать сколько угодно. И визуальных объектов тоже можно передавать без ограничений. Ссылка на визуальный объект нужна для того, чтобы тень корректно просчитывалась, не завися от вложенности объектов и их трансформации. Например, если вы хотите сделать тени от ста одинаковых квадратов, то вам достаточно создать только один ShadowObject для этого, передать ему четыре точки с координатами углов квадрата и ссылки на все визуальные квадраты. Сами же визуальные квадраты можете перемещать, изменять их масштаб, поворачивать, добавлять в другие объекты, но это не повлияет на формирование тени от них. Подробнее о классе ShadowObject, я напишу ниже.

Второй класс, это класс света – Light. Это визуальный класс, который и нарисует сам свет. Его мы добавляем на сцену и перемещаем, как лампочку. Сам свет – это будет растровое изображение, по этому, чем больше радиус для света вы задаёте, тем дольше идут вычисления. Так же этому классу вы передаёте ссылки на все ShadowObject-ы, которые он должен обработать. Главный метод класса Light – это renderLight() и я очень вкратце опишу, что он делает.

1) Сначала просчитывает тени от всех рёбер, всех ShadowObject-ов, которые ему передали. С условием, что это ребро попадает в занимаемую область света (его ограничивающий прямоугольник). Точнее, если хоть одна из точек ребра попадает в эту область, то тень от этого ребра просчитывается. И рисует тени в спрятанном шейпе, с помощью обычных методов graphics.

2) Затем делается растровый снимок, этого шейпа с тенями. Получаем прозрачную картинку с чёрными тенями.

3) Инвертируем альфу этой картинки и получается всё наоборот. Всё что было прозрачное, стало видимое, а на месте теней – образовались дырки. Своего рода маска для рисунка светового пятна.

4) И на полученное изображение, налаживаем картинку света. По умолчанию – это радиальный градиент с прозрачными краями. Но можно и указать своё, если нужно. Вот собственно и всё.
Название: renderLight.gif
Просмотров: 1512

Размер: 50.0 Кб

А теперь опишу все публичные свойства и методы этих двух классов, чтобы было понятно, как этим всем пользоваться. Начну с
ShadowObject.


Манипуляции с передачей ссылок на дисплейОбъекты:

Код AS3:
addToDisplayObject(displayObject:DisplayObject)
Добавляете дисплейОбъекты, относительно которых будут рассчитываться тени от всех рёбер этого ShadowObject-a.


Код AS3:
removeFromDisplayObject(displayObject:DisplayObject)
Тоже самое что выше, но только наоборот.


Код AS3:
removeFromAllDisplayObjects()
Удалить все ссылки на все объекты.

Способы создания рёбер, названия говорят сами за себя:

Код AS3:
addEdge(startPoint:Point,endPoint:Point)
Добавляет одно ребро, от одной точки ко второй.


Код AS3:
addEdgePath(pointsPath:Vector.<Point>, closedPath:Boolean=false)
Добавить цепочку из рёбер. Вы передаёте массив точек, и по ним строятся рёбра. От первой точки ко второй, от второй к третей и т.д. При надобности, вы можете создать дополнительно ребро, от последней точке к первой.
Название: addPath.gif
Просмотров: 1500

Размер: 1.6 КбНазвание: addPathClose.gif
Просмотров: 1492

Размер: 1.9 Кб

Код AS3:
addEdgeRect(x:Number,y:Number,width:Number,height:Number)
Добавить прямоугольник из рёбер, заданной ширины и высоты.


Код AS3:
addEdgeCircle(x:Number,y:Number,radius:Number,segments:uint=8)
Добавить круг из рёбер. Чем больше сегментов, тем круглее.

Код AS3:
removeAllEdges()
Удалить все рёбра, которые были добавлены в этот ShadowObject.


Light

Конструктор:

Код AS3:
Light(radius:uint=200, colorA:uint = 0xFFFFFF, colorB:uint = 0xFFFFFF, shadowAlpha:Number=1, quality:Number=1, shadowDistance:int = 3000)
radius – Радиус света, задаётся только при создании. Чем он больше, тем больше нагрузка.

colorA и colorB – Так как по умолчанию, картинка для света – это радиальный градиент, с прозрачными краями, то указываем два цвета для него. Цвет от середины, и цвет к краю. По умолчанию оба цвета – белые. Так же задаются только при создании.

shadowAlpha – Альфа каждой отдельной тени от ребра. По умолчанию, все тени непрозрачны. Если добавить прозрачность, то получается эффект наложения теней, что более реалистично в некоторых моментах, но от этого и дольше просчёт. Это значение можно потом поменять.

quality – Качество изображения света (его растрового изображения). Обычно, качество со значением 0.5, намного повышает производительность и мало заметна на качестве картинки. При очень низких значениях, изображение света будет пикселизироваться. Очень важный параметр, влияющий на скорость просчёта. Задаётся только при создании.

shadowDistance – Длина тени от рёбер. Обычно остаётся не изменой. Для чего я вывел этот параметр в конструктор, пока точно не знаю, но обещаю подумать.

Методы:

Код AS3:
renderLight()
– Производит все расчёты с тенями и рисует растровый свет.

Манипуляции с ShadowObject-ами

Код AS3:
addShadowObject(shadowObj:ShadowObject)
– добавляет ShadowObject в список. И теперь, свет будет брать его рёбра в расчёт теней тоже.


Код AS3:
removeShadowObject(shadowObj:ShadowObject)
– удалить ShadowObject из списка. Теперь свет не будет просчитывать тени от его рёбер.

Код AS3:
removeAllShadowObjects()
– удаляет все ShadowObject-ы из своего списка и просчитывать тени больше не от чего.

Другие методы:

Код AS3:
setLightImage(imageBMD:BitmapData=null)
– вместо цветного света (радиального градиента, который рисуется по умолчанию), можно добавить своё изображение. Так как прозрачность тоже учитывается, то желательно чтобы это изображение к краям, плавно становилось прозрачным. Если при создании света, quality было ниже единицы, то данное изображение так же будет ухудшено в качестве.


Код AS3:
lightBlur(blur:int=2,quality:int=1)
– размытие для света. Не влияет на цветное изображение света. Чем больше размытие, тем дольше просчёт. По умолчанию - 0, размытия нет. При низких значения качества (quality) света в конструкторе при создании, можно добавить размытия, для более сглаженного варианта.

Код AS3:
lightFog(fog:Number=0)
– определяет сколько света, попадёт на тени. Если 0 – то в тенях света не будет, если 1 – то тени полностью освещены.


На этом описание публичных методов завершено. Осталось лишь одно свойство:

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

Описание закончено. Теперь попробуем что нибудь сделать. Давайте создадим 100 квадратов, разбросаем их по сцене и включим свет.
Код AS3:
package 
{
	import flash.display.Shape;
	import flash.display.Sprite;
	import flash.events.Event;
	import LightEffect.Light;
	import LightEffect.ShadowObject;
 
	/**
	 * ...
	 * @author Samana
	 */
	public class Main extends Sprite 
	{
		//создаём свет с низким качеством
		private var light:Light = new Light(500, 0xFFFFFF, 0xFF0080, 1, 0.3);
		//создаём теневой объект для квадрата
		private var shadowBox:ShadowObject = new ShadowObject();
 
		public function Main():void 
		{
			stage.color = 0x2C1B30;
 
			//создаём 100 квадратов и раскидываем их по сцене,
			//и меняем у них повороты и размеры
			for (var i:int = 0; i < 100; i++) 
			{
				var box:Shape = new Shape();
				box.graphics.beginFill(Math.random() * 0xFFFFFF);
				box.graphics.drawRect( -20, -20, 40, 40);
				box.graphics.endFill();
 
				box.x = Math.random() * 800;
				box.y = Math.random() * 600;
				box.scaleX = Math.random();
				box.scaleY = Math.random();
				box.rotation = Math.random() * 360;
 
				addChild(box);
 
				//и передаём теневомуОбъекту, ссылку на шейп
				shadowBox.addToDisplayObject(box);
			}
 
			//добавляем в теневойОбъект прямоугольную область,
			//с такими же размерами, как у созданных выше шейпов.
			shadowBox.addEdgeRect( -20, -20, 40, 40);
 
			//добавляем в свет - теневойОбъект
			light.addShadowObject(shadowBox);
			//немного размываем свет
			light.lightBlur(2);
			//меняем режим наложения для красовы
			light.blendMode = "add";
			//добавляем свет на сцену
			addChild(light);
 
			addEventListener(Event.ENTER_FRAME, enterFrame);
		}
 
		private function enterFrame(e:Event):void 
		{
			light.x = mouseX;
			light.y = mouseY;
			//рендерим свет
			light.renderLight();
		}
	}
}
На сегодня, пожалуй, всё. Конечно ещё остались идеи по улучшению, и развитию этой идеи. Все исходники доступны (FD 4.5, flashPlayer 11.9) и код прокомментирован. Если вам пригодятся, буду рад. Не претендую на оригинальность или идеальный код. Написал так, как написалось. Всегда есть чему учится. Спасибо.
source.rar
Вложения
Тип файла: swf NewProjectLightEdges.swf (89.5 Кб, 959 просмотров)
Тип файла: swf FiguresLight.swf (37.4 Кб, 937 просмотров)
Всего комментариев 31

Комментарии

Старый 11.12.2013 11:06 Psycho Tiger вне форума
Psycho Tiger
 
Аватар для Psycho Tiger
Результат очень нравится
Старый 11.12.2013 13:53 samana вне форума
samana
 
Аватар для samana
Psycho Tiger, спасибо!
Старый 11.12.2013 13:56 Hauts вне форума
Hauts
 
Аватар для Hauts
Samana, очень круто.

Единственное, — для света блендмод бы + нелинейный градиент (я про вторую флэшку).
Старый 11.12.2013 14:32 samana вне форума
samana
 
Аватар для samana
Hauts, спасибо. Про blendMode честно говоря совсем забыл. А градиент там и так нелинейный выставлен, точнее значение по умолчанию, которое = "rgb".
Старый 11.12.2013 14:46 Hauts вне форума
Hauts
 
Аватар для Hauts
Не не, я, скорее, про опорные точки. Чтобы их не две было (0 - 255), а штук пять, распределенные не линейно, а квадратично (не знаю, как точнее сказать). Будет возможность, проиллюстрирую.
Старый 11.12.2013 15:28 samana вне форума
samana
 
Аватар для samana
Hauts, а, вот оно что. Я даже не знал, что этих точек можно делать больше, чем кол-во цветов. Спасибо за подсказку, узнал новое для себя!
Старый 11.12.2013 15:42 Hauts вне форума
Hauts
 
Аватар для Hauts
Да не за что.

Вот картинка (тут, почему-то, нельзя вложения добавлять, надеюсь ссылка не сломается)

http://cs409829.vk.me/v409829713/50d2/StfAWlk7p9Y.jpg

— справа пример, как было бы лучше. Обрати внимание, что у второго ползунка значение прозрачности 51, хотя положение в ~20.
Старый 11.12.2013 15:55 samana вне форума
samana
 
Аватар для samana
Hauts, отлично! То, что надо! Сам же думал, "а как бы сделать градиент помягче, где настройки?", но почему-то не находил ответа. А оказывается он был под носом.
Старый 11.12.2013 21:26 dimarik вне форума
dimarik
 
Аватар для dimarik
Крутячая штука. Как у нее с производительностью?
Старый 12.12.2013 00:17 GBee вне форума
GBee
 
Аватар для GBee
Супер.
Старый 12.12.2013 01:17 MikroAcse вне форума
MikroAcse
 
Аватар для MikroAcse
Офигенно. Вдохновлялся статьями хабра?
Старый 12.12.2013 01:31 samana вне форума
samana
 
Аватар для samana
Ух ты, так приятно видеть положительные отзывы!
dimarik, мне стыдно, но я не знаю, как это проверить. Точнее, как правильно это сделать. Конечно я пробовал создавать максимум объектов, что-бы найти границу начала тормозов. Но у меня достаточно старый компьютер и тормоза начинаются относительно быстренько. Но в целом, производительность не плохая, как мне кажется.
GBee, спасибо!
MikroAcse, спасибо! Я на хабре очень редко бываю, в основном через ссылки из этого форума. Статьи там не видел. Интересно. Надо будет почитать, как придумали другие.
Старый 14.12.2013 17:49 Zebestov вне форума
Zebestov
 
Аватар для Zebestov
Чума!!!
Старый 14.12.2013 18:19 samana вне форума
samana
 
Аватар для samana
Zebestov, спасибо
Старый 14.12.2013 22:33 MikroAcse вне форума
MikroAcse
 
Аватар для MikroAcse
Может, стоит выложить проект на Github?
Старый 14.12.2013 22:47 samana вне форума
samana
 
Аватар для samana
MikroAcse, а что, выглядит настолько серьёзно? Хм... подумаю. Идея хорошая, спасибо.
Старый 14.12.2013 23:01 MikroAcse вне форума
MikroAcse
 
Аватар для MikroAcse
samana, по-моему, очень даже
Старый 15.12.2013 04:19 Psycho Tiger вне форума
Psycho Tiger
 
Аватар для Psycho Tiger
Гитхаб – пристанище для любого опенсурса, хорошего и не очень. В этом случае – хорошего
Я правда буду рад, если проект разовьется в либу. Я не знаю, где его применить – но если мне понадобится, то я буду знать, откуда взять)
Старый 16.12.2013 14:23 PainKiller вне форума
PainKiller
 
Аватар для PainKiller
Спасибо, очень ценный материал. Недавно возникал такой вопрос с тенями, и не знал что делать, буду рад увидеть этот проект на гитхабе
Старый 16.12.2013 14:33 samana вне форума
samana
 
Аватар для samana
PainKiller, спасибо!
Пока с github не получается. Пробовал разобраться, что там к чему, как это работает, но безуспешно. Всё на английском, а я его не знаю. Буду искать русские туториалы, а то сам, точно не разберусь.
Старый 16.12.2013 14:38 ChuwY вне форума
ChuwY
 
Аватар для ChuwY
Можешь попробовать вылить на bitbucket.org
С ним попроще разобраться.
Старый 16.12.2013 14:54 samana вне форума
samana
 
Аватар для samana
ChuwY, спасибо, посмотрю. Сейчас ещё нет уверенности, что всё таки стоит вообще куда-то выставлять эту наработку. Хотелось бы сначала сделать из неё, что-то действительно полноценно-стоящее. А пока, только два класса...
Старый 17.12.2013 19:50 dimarik вне форума
dimarik
 
Аватар для dimarik
Давай, давай, учи англицкий! )
Старый 18.12.2013 02:45 samana вне форума
samana
 
Аватар для samana
Придётся
Старый 19.12.2013 00:40 djyamato вне форума
djyamato
 
Аватар для djyamato
Крутая штучка !
Старый 19.12.2013 01:03 samana вне форума
samana
 
Аватар для samana
djyamato, спасибо!
Старый 15.02.2014 16:49 ashkart вне форума
ashkart
 
Аватар для ashkart
Шикарно получилось. Особенно LightEdges понравилась с этими частицами.
Старый 15.02.2014 16:54 samana вне форума
samana
 
Аватар для samana
Спасибо за отзыв, ashkart!
Старый 16.01.2015 05:51 ChuwY вне форума
ChuwY
 
Аватар для ChuwY
Еще раз захожу, чтобы сказать спасибо.
Старый 21.01.2015 12:12 cleptoman вне форума
cleptoman
 
Аватар для cleptoman
класс..можно , наверно, еще добавить учет светопропускаемости (можно на альфе базировать, как вариант)
Старый 23.01.2015 15:21 samana вне форума
samana
 
Аватар для samana
Ух ты, спасибо, ребята!!!
cleptoman, а похожая возможность там есть! За это отвечает параметр shadowAlpha в конструкторе Light.
 
Последние записи от samana

 


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


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