Stage3D заметки
Привет. Сегодняшняя заметка будет особенно интересна математикам. И я надеюсь получить от тех, кто в теме, т.к. некоторые вещи я так и не смог понять.
После выхода 11-го плеера в сети появились несколько однотипных примеров использования stage3D, которые показывают базовые возможности использования нового API. К сожалению, комьюнити мало пишет о секретных фишках, поэтому интересную инфу приходится собирать по крупицам. Так, например, примеры отрисовки треугольника или куба сводятся к следующему:
//задаем координаты и цвет вершин 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';
преобразовывает координаты вершины.
Рассмотрим матрицу PerspectiveMatrix3D.perspectiveOffCenterLH. Начнем с того, что в приведенной выше инструкции вектор (координаты вершины), расположен справа, т.е. это так называемый строчный вектор (row vector), а значит матрица тоже должна быть строчной (тот кто в теме - тот поймет). Однако PerspectiveMatrix3D.perspectiveOffCenterLH создает перевернутую (transposed) матрицу и для правильного отображения нужно передавать true 4-м параметром в context3D.setProgramConstantsFromMatrix(). Теперь проведем такой тест:
//координаты 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);
Однако, сделаем вот так:
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
По правде говоря, получившийся вектор - ммм, как бы сказать, - не обычный - он в другой кординатной системе... Его четвертая координата w не равна 1, а равна 600. И чтобы перевести вектор из гомогенных координат (в которых он сейчас находится) в обычные, надо все величины разделить на 600, чтобы w была 1. Разделим:
v.scaleBy(1 / v.w); v.w /= v.w trace(v); //Vector3D(0.33333333333333337, -0.4444444783528646, 0.5555554707845053) trace(v.w); //1
И вот тут у меня главная непонятка - в шейдере мы НЕ делим кординаты вектора на гомогенную координату, однако все работает как надо. Почему??? Основная догадка - где-то там в недрах карты это деление все таки происходит. Однако происходит это поздно - позже, чем данные передаются во фрагментный шейдер. Т.е. если мы будем делать так:
То во фрагментном шейдере v0 будет не нормализован. Какая разница, спросите вы? Ну, в некоторых случаях это критично. Я столкнулся с этой проблемой, когда пытался выводить на экран буфер глубины (depth bufer) - в качестве цвета мне нужна была координата z. Однако, т.к. значение было не нормализовано и намного больше 1, то весь буфер был белый.
Статья немного сумбурна, потому что тема довольно-таки сложная для понимания. Не стесняйтесь - по любым вопросам пишите в комменты. Сейчас я приведу получившийся результат, чтобы было понятно что же я все-таки хотел:
//вершины 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:49 | |
Спасибо за коммент.
mov? Очень сомневаюсь. Наверное вы не поняли вопроса. Про точность Number в AS тоже не понял - к чему это? |
10.04.2012 09:28 | |
Цитата:
mov? Очень сомневаюсь. Наверное вы не поняли вопроса.
Цитата:
Про точность Number в AS тоже не понял - к чему это?
|
10.04.2012 12:44 | |
Цитата:
То есть - Вы правда уверены, что реализация floating-point number на GPU будет сильно отличаться от CPU-реализации?
Цитата:
Интересно было другое — одна и та же программа на эмуляторе GPU (на CPU) выдавала корректный результат, а на самом GPU – нет. Позже оказалось, что проблема была в том, что этот GPU не поддерживал полностью стандарт IEEE754 и прямой подход не сработал.
Другое дело, что меньше глюков с округлением не станет, если предоставить все видеокарте |
10.04.2012 16:46 | |
Цитата:
Таки отличается
Цитата:
что этот GPU не поддерживал
|
Последние записи от Волгоградец
- Изометрическая сортировка. Новый подход. (25.01.2013)
- Stage3D заметки (06.04.2012)
- Embed клипа с одним кадром. (16.01.2012)
- Производительность операций с floating point number (18.03.2011)
- FTE based text controls (10.01.2011)