
Ручной рендер - 3
После небольшой паузы продолжаю ковыряться с движком.
Сделано немного, под катом расскажу что именно и чем я вообще занимался.
1. Искал где спереть графику :)
Художник/текстурщик из меня никакой, а нанимать никого не хочу, потому что судьба проекта еще неизвестна. Может сделаю, может нет. В итоге выдрал текстуры из Quake2. Пока так.
2. Ковырялся с пиксельбендером.
Хотел сделать «отложенное» текстурирование.
Идея была в том, чтобы писать в картинку текстурные координаты из атласа, а настоящую картинку формировать шейдером.
Если бы все получилось, на каждом пикселе сэкономилось бы одно чтение из вектора + куча всяких вспомогательных операций и, может быть, получилась бы бесплатная билинейная интерполяция.
Увы, не сложилось. Вылез глюк в 10.1 плеере (начал жутко жрать память. Отписал в адоб) и, вообще, не сильно быстро бегало.
3. Анимация.
Тут надо немного рассказать как рисуются модельки.
Модельки состоят из треугольников. Для каждой грани просчитана нормаль (перпендикуляр). Если нормаль смотрит в камеру — грань видна и ее надо рисовать, иначе пропускаем.
Длина нормали для грани неважна. Важен только знак (+ или -).
Каждый треугольник состоит из 3 вершин. В каждой вершине тоже просчитана нормаль, но (!) она обязательно должна быть единичной длины (нормализована). Используется для расчета освещения.
Расчет самой нормали не сложен, несколько сложений и умножений, но нормализация нормали требует вычисления корня. Это жутко медленно.
Анимация «покорилась» с третьего подхода.
3.1 Понадеялся на скорость хэкса и сделал все по честному. Храним только координаты вершин. На каждом шаге интерполируем координаты, вычисляем нормали треугольников, вычисляем нормали вершин. Жутко медленно.
3.2 Понадеялся на память. Для каждого кадра храним полную копию геометрии со всеми нормалями и вспомогательными данными. Очень быстро и просто. Сожрало 200 метров :(
3.3 Промежуточный вариант. Храним координаты и нормали вершин. Интерполируем только координаты, нормали берем ближайшие, без интерполяции. Для граней нормали пересчитываем. Таким образом корень не считается никогда. Остановился на этом.
Демка тут megaswf.com/serve/73548/ (здоровая, 4 метра). Если нажать Enter, отключится интерполяция.
Красный пацан специально замедлен в 10 раз, чтобы можно было полетать вокруг и посмотреть как освещение отстает от анимации.
Сделано немного, под катом расскажу что именно и чем я вообще занимался.
1. Искал где спереть графику :)
Художник/текстурщик из меня никакой, а нанимать никого не хочу, потому что судьба проекта еще неизвестна. Может сделаю, может нет. В итоге выдрал текстуры из Quake2. Пока так.
2. Ковырялся с пиксельбендером.
Хотел сделать «отложенное» текстурирование.
Идея была в том, чтобы писать в картинку текстурные координаты из атласа, а настоящую картинку формировать шейдером.
Если бы все получилось, на каждом пикселе сэкономилось бы одно чтение из вектора + куча всяких вспомогательных операций и, может быть, получилась бы бесплатная билинейная интерполяция.
Увы, не сложилось. Вылез глюк в 10.1 плеере (начал жутко жрать память. Отписал в адоб) и, вообще, не сильно быстро бегало.
3. Анимация.
Тут надо немного рассказать как рисуются модельки.
Модельки состоят из треугольников. Для каждой грани просчитана нормаль (перпендикуляр). Если нормаль смотрит в камеру — грань видна и ее надо рисовать, иначе пропускаем.
Длина нормали для грани неважна. Важен только знак (+ или -).
Каждый треугольник состоит из 3 вершин. В каждой вершине тоже просчитана нормаль, но (!) она обязательно должна быть единичной длины (нормализована). Используется для расчета освещения.
Расчет самой нормали не сложен, несколько сложений и умножений, но нормализация нормали требует вычисления корня. Это жутко медленно.
Анимация «покорилась» с третьего подхода.
3.1 Понадеялся на скорость хэкса и сделал все по честному. Храним только координаты вершин. На каждом шаге интерполируем координаты, вычисляем нормали треугольников, вычисляем нормали вершин. Жутко медленно.
3.2 Понадеялся на память. Для каждого кадра храним полную копию геометрии со всеми нормалями и вспомогательными данными. Очень быстро и просто. Сожрало 200 метров :(
3.3 Промежуточный вариант. Храним координаты и нормали вершин. Интерполируем только координаты, нормали берем ближайшие, без интерполяции. Для граней нормали пересчитываем. Таким образом корень не считается никогда. Остановился на этом.
Демка тут megaswf.com/serve/73548/ (здоровая, 4 метра). Если нажать Enter, отключится интерполяция.
Красный пацан специально замедлен в 10 раз, чтобы можно было полетать вокруг и посмотреть как освещение отстает от анимации.
- +6
- ryzed
Комментарии (25)
С форматом анимаций еще не определился, все хранится в больших текстовых файлах, потому и размер большой.
Со сжатием данных вообще надо будет еще разбираться отдельно.
а что за зеленые точки везде? проблемы с натяжкой текстур?
Текстуры неточно натянуты (+- полтекселя) и точность рендера хромает.
Потом каждую текстуру «оконтурю», все руки не доходят.
Когда мне надо было много корней считать я использовал функции из фэстмаса, на «алхимической» памяти довольно быстро считает, на всякий случай приведу функцию:
www.flasher.ru/forum/showthread.php?t=146828
Так что набор таких утилит удобно выносить в статику.
Только что проверил статические функции вызываются примерно на 20% медленее.
FP 10.1 Debug Тачка довольно мощная, AMD. Может ещё кто проверит на другом проце или плеере чтоб сравнить?
Попробуй 1 000 000 итераций, погрешность меньше будет…
10 дебажный: оба метода 540-620 мс
10 релизный стэндэлон: статик 120 — 140 мс, обычный 133-137 мс
10.1 релизный плагин, Мозилла: статик 14 — 16 мс, обычный 13-15 мс
Main.as
package
{
import flash.display.Sprite;
import flash.events.Event;
import flash.events.MouseEvent;
import flash.text.TextField;
import flash.utils.getTimer;
/**
* Unit test
* @author NUMA.Games
*/
public class Main extends Sprite
{
private var outputText:TextField;
public function Main():void
{
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
outputText = new TextField();
addChild(outputText);
stage.addEventListener(MouseEvent.CLICK, testFunction);
}
private function testFunction(e:MouseEvent):void
{
var classInstance:NewClass = new NewClass();
var startTime:uint = getTimer();
for (var i:int = 0; i < 5000000; i++)
{
NewClass.staticFunction();
//classInstance.dynamicFunction();
}
var finishTime:uint = getTimer() — startTime;
outputText.text = String(finishTime);
}
}
}
NewClass.as
package
{
/**
*…
* @author NUMA.Games
*/
public class NewClass
{
public function NewClass()
{
}
public function dynamicFunction():void
{
}
static public function staticFunction():void
{
}
}
}
Результат воспроизводится?
Результаты в дебаг плеере и обычном могут отличаться
давно уже все проверено и оттестированно
jacksondunstan.com/articles/734 (ищем FUNCTION PERFORMANCE)
static чуууууть-чуууууть медленнее чем вызов обычного метода
В хаксе «толковые пацаны» юзают inline, так что разницы между статической и обычной функциями не будет.
Собственно, вызова функции вообще не будет :)
Осталась только последняя отсюда: gamedevblogs.ru/blog/369.html
Сейчас (из-за комментов «У кого нет цветного освещения — тот лох!» :) ) пришлось много переписать.
Для затравки текущий скрин (сильно темный, правда):
Цветные лайтмапы, цветные светильники, водичка из q2.
Скоро будет новый пост.
Тут все на уровне первого курса университета, никаких особых знаний не требуется.