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

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

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

Stage3D заметки

Запись от Волгоградец размещена 06.04.2012 в 13:07
Обновил(-а) Волгоградец 11.04.2012 в 11:37

Привет. Сегодняшняя заметка будет особенно интересна математикам. И я надеюсь получить от тех, кто в теме, т.к. некоторые вещи я так и не смог понять.
После выхода 11-го плеера в сети появились несколько однотипных примеров использования stage3D, которые показывают базовые возможности использования нового API. К сожалению, комьюнити мало пишет о секретных фишках, поэтому интересную инфу приходится собирать по крупицам. Так, например, примеры отрисовки треугольника или куба сводятся к следующему:
Код AS3:
//задаем координаты и цвет вершин
var vertices:Vector.<Number> = Vector.<Number>([
				//x    y	z	r	g	b
				0, 	0, 	1, 	1,	0, 	0,
				1, 	0, 	1, 	0,	1, 	0,
				0, 	1, 	1, 	0,	0, 	1
			]);
//пишем шейдеры
var vertexShader:String = 
			'mov op va0\n' +
			'mov v0 vt0';
var fragmentShader:String = 
			'mov oc, v0';
Меня сразу смутили вот эти вот нолики и единички в координатах. Мой мозг привык думать в пикселах и привычных размерах дисплей обжектов, например, 200 на 200 пикселов в ширину и высоту и 153 пиксела в глубину. Но для правильного отображения нам необходимо привести реальные размеры к нормализованным координатам (NDC - Normalized Device Coordinates), т.е. к интервалу [-1, +1] для x и y и [0, +1] для z. (можно конечно выйти за этот интервал - но отрисовываться ничего не будет - лишнее просто обрежется). Для конвертации в эти самые NDC служат матрицы. Адоб предоставляет класс PerspectiveMatrix3D, с помощью которого можно соорудить различные матрицы - ортогональные и перспективные с различными параметрами и различным расположением осей (я про леворучные и праворучные координатные системы). Такую матрицу передаем в шейдер, где инструкция
Код AS3:
m44 op va0 vc0
преобразовывает координаты вершины.
Рассмотрим матрицу PerspectiveMatrix3D.perspectiveOffCenterLH. Начнем с того, что в приведенной выше инструкции вектор (координаты вершины), расположен справа, т.е. это так называемый строчный вектор (row vector), а значит матрица тоже должна быть строчной (тот кто в теме - тот поймет). Однако PerspectiveMatrix3D.perspectiveOffCenterLH создает перевернутую (transposed) матрицу и для правильного отображения нужно передавать true 4-м параметром в context3D.setProgramConstantsFromMatrix(). Теперь проведем такой тест:
Код AS3:
//координаты
var vertices:Vector.<Number> = Vector.<Number>([
				//x	y	z	w
				0, 	0, 	600, 	1,
				200, 	0, 	600, 	1,
				200, 	200, 	600, 	1,
				0, 	200,	600, 	1
			]);
 
//вершинный шейдер
var vertexShader:String = 
			'm44 op va0 vc0';
 
//проекционная матрица
perspectiveMatrix.perspectiveOffCenterLH(-400, 400, 300, -300, 400, 1000);
Ура, все работает.
Однако, сделаем вот так:
Код AS3:
var v:Vector3D = new Vector3D(200, 200, 600, 1);
v = _perspectiveMatrix.transformVector(v); //вот тут кстати мы матрицу умножаем на вектор, а не наоборот - как в шейдере, поэтому трансопзить матрицу не нужно
trace(v); //Vector3D(200, -266.66668701171875, 333.3332824707031)
trace(v.w); //600
OMG! Но ведь эти координаты НЕ нормализованы, как тогда, черт возьми это работает?????????
По правде говоря, получившийся вектор - ммм, как бы сказать, - не обычный - он в другой кординатной системе... Его четвертая координата w не равна 1, а равна 600. И чтобы перевести вектор из гомогенных координат (в которых он сейчас находится) в обычные, надо все величины разделить на 600, чтобы w была 1. Разделим:
Код AS3:
v.scaleBy(1 / v.w);
v.w /= v.w
trace(v); //Vector3D(0.33333333333333337, -0.4444444783528646, 0.5555554707845053)
trace(v.w); //1
Гуд, это уже похоже на правду.
И вот тут у меня главная непонятка - в шейдере мы НЕ делим кординаты вектора на гомогенную координату, однако все работает как надо. Почему??? Основная догадка - где-то там в недрах карты это деление все таки происходит. Однако происходит это поздно - позже, чем данные передаются во фрагментный шейдер. Т.е. если мы будем делать так:
Код AS3:
var _vertexShader:String = 
			'm44 vt0 va0 vc0\n' +
			'mov op vt0\n' +
			'mov v0 vt0';
То во фрагментном шейдере v0 будет не нормализован. Какая разница, спросите вы? Ну, в некоторых случаях это критично. Я столкнулся с этой проблемой, когда пытался выводить на экран буфер глубины (depth bufer) - в качестве цвета мне нужна была координата z. Однако, т.к. значение было не нормализовано и намного больше 1, то весь буфер был белый.
Статья немного сумбурна, потому что тема довольно-таки сложная для понимания. Не стесняйтесь - по любым вопросам пишите в комменты. Сейчас я приведу получившийся результат, чтобы было понятно что же я все-таки хотел:
Код AS3:
//вершины
var vertices:Vector.<Number> = Vector.<Number>([
				//x	y	z	w
				0, 	0, 	600, 	1,
				200, 	0, 	600, 	1,
				200, 	200, 	600, 	1,
				0, 	200,	600, 	1
			]);
//индексы
var indices:Vector.<uint> = Vector.<uint>([
				0, 1, 3,
				1, 2, 3
 
//проекционная матрица
perspectiveMatrix.perspectiveOffCenterLH(-400, 400, 300, -300, 400, 1000);
			]);
 
//пускай обратная сторона тоже отрисовывается
context3D.setCulling(Context3DTriangleFace.NONE);
 
//шейдеры
private var _vertexShader:String = 
			'm44 vt0 va0 vc0\n' +
			'div vt0 vt0 vt0.wwww\n' +
			'mov op vt0\n' +
			'mov v0 vt0';
 
private var _fragmentShader:String = 
			'sub oc, fc0 v0.zzzz';
 
//отрисовка
function draw(event:Event):void {
 
			matrix.identity();
			_matrix.appendTranslation(0, 0, -600);
			_matrix.appendRotation(getTimer() * 0.05, Vector3D.Y_AXIS);
			_matrix.appendTranslation(stage.mouseX - 400, stage.mouseY - 300, 600);
			_matrix.append(_perspectiveMatrix);
 
			_context3D.setProgramConstantsFromMatrix(Context3DProgramType.VERTEX, 0, _matrix, true);
			_context3D.setProgramConstantsFromVector(Context3DProgramType.FRAGMENT, 0, Vector.<Number>([1, 1, 1, 1]));
 
			_context3D.clear(0, 0, 0, 1);
			_context3D.drawTriangles(_indexBuffer, 0, 2);
			_context3D.present();
}
Результат:
depthTest.zip
Здесь видно, что чем ближе пикселы к камере, тем они светлее.
Всего комментариев 9

Комментарии

Старый 09.04.2012 14:13 BuKT вне форума
BuKT
 
Аватар для BuKT
Цитата:
И вот тут у меня главная непонятка - в шейдере мы НЕ делим кординаты вектора на гомогенную координату
Когда вы делаете mov v0, vt0 (или что там у вас) - это и происходит, если ранее не было сделано вручную.

И мой совет: не уповайте на точность Number в AS =) Let GPU work with it
Старый 09.04.2012 14:49 Волгоградец вне форума
Волгоградец
 
Аватар для Волгоградец
Спасибо за коммент.
mov? Очень сомневаюсь. Наверное вы не поняли вопроса. Про точность Number в AS тоже не понял - к чему это?
Старый 09.04.2012 21:12 gloomyBrain вне форума
gloomyBrain
 
Аватар для gloomyBrain
Вообще поигравшись со всем этим Stage3D я пришел к таким выводам:
- вместо Vector нужно использовать ByteArray (для 2D быстрее получается объединять в батчи)
- адобовская матрица не учитывает сдвиги (начало координат в центре экрана)
- agal - действительно низкоуровневая и интересная штуковина =)
Старый 10.04.2012 09:28 BuKT вне форума
BuKT
 
Аватар для BuKT
Цитата:
mov? Очень сомневаюсь. Наверное вы не поняли вопроса.
Понял. Просто неправильно сформулировал ответ. Разумеется не сам mov это делает. Этот вектор "делится в недрах карты" в процессе растеризации, при переносе данных между вершинным и пиксельным шейдерами. mov v0, vt0 всего лишь даёт инструкцию, что вектор из vt0 следует перенести в пиксельный шейдер и интерполировать между вершинами.
Цитата:
Про точность Number в AS тоже не понял - к чему это?
К тому, что я бы не советовал осуществлять операции деления для получения дробных чисел в чистом AS3. Number очень сильно глючит с округлением (например так). Поэтому лучше предоставить все операции с дробными числами самой видеокарте.
Старый 10.04.2012 12:15 gloomyBrain вне форума
gloomyBrain
 
Аватар для gloomyBrain
Цитата:
лучше предоставить все операции с дробными числами самой видеокарте
Во-первых: как?
Во-ворых: зачем?

То есть - Вы правда уверены, что реализация floating-point number на GPU будет сильно отличаться от CPU-реализации? Тем более, что провести все расчеты будет как минимум проблематично (сама загрузка-выгрузка констант это довольно долгая операция).
Старый 10.04.2012 12:44 ChuwY вне форума
ChuwY
 
Аватар для ChuwY
Цитата:
То есть - Вы правда уверены, что реализация floating-point number на GPU будет сильно отличаться от CPU-реализации?
Статья вообще довольно неплохо разъясняет, что такое floating-point number, а также в заключении содержит вот такой текст (отрывок):
Цитата:
Интересно было другое — одна и та же программа на эмуляторе GPU (на CPU) выдавала корректный результат, а на самом GPU – нет. Позже оказалось, что проблема была в том, что этот GPU не поддерживал полностью стандарт IEEE754 и прямой подход не сработал.
Таки отличается.
Другое дело, что меньше глюков с округлением не станет, если предоставить все видеокарте
Старый 10.04.2012 13:51 Волгоградец вне форума
Волгоградец
 
Аватар для Волгоградец
Цитата:
Этот вектор "делится в недрах карты" в процессе растеризации, при переносе данных между вершинным и пиксельным шейдерами.
Вот как раз проблема в том, что не в этот период делит. Я же написал - что в пиксельный шейдер передается НЕ нормализованное интерполированное значение.
Цитата:
К тому, что я бы не советовал осуществлять операции деления для получения дробных чисел в чистом AS3.
Ну дык где ж вы в коде это увидели (тот небольшой кусок не в счет - он просто показывает порядок чисел). Я ж привел в пример шейдер с делением. Будьте внимательнее пожалуйста.

Вобщем несколько экспериментов показали, что если 4-я координата w не равна 1, то деление таки происходит автоматически и после того, как выполнится вершинный и фрагментный шейдер.
Старый 10.04.2012 16:46 gloomyBrain вне форума
gloomyBrain
 
Аватар для gloomyBrain
Цитата:
Таки отличается
Цитата:
что этот GPU не поддерживал
Неоднозначно. Вариантов работы теоритетически столько же, сколько всего на свете GPU-устройств
Старый 10.04.2012 17:09 BuKT вне форума
BuKT
 
Аватар для BuKT
GPU-устройства либо поддерживают либо не поддерживают этот стандарт. Соответственно вариантов работы только два.

Чутьё мне подсказывает, что Molehill требует поддержки этого стандарта для direct-рендеринга :-)
 

 


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


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