-Eugene-
> Кхм... А ты знаешь, что такое мировая матрица? С точки зрения математики?
С точки зрения математики я знаю что это таблица Сколько-нибудьxСколько-нибудь с цифрами. В Direct3D 4x4 первые 3x3 определяют поворот а остальные 7 цифр непонятно что.
kroonk
Лучше бы ты реально изучил матричную математику. Мировая матрица - матрица перевода из локальной системы координат в глобальную. Т.е. матрица вообще описывает систему координат.
kroonk
Почитай как представляется float, мантисса (дробная часть <1) умножить на степень (экспонента), поэтому можно представить огромные значения (10^38), но точность относительная, гдето 4-5 десятичных знаков, поэтому напр.
1e6+1=1e6 - идет потеря точности, мелкие слагаемые "глотает"
Видеокарты все работают с float, даже когда координаты double передаешь, они внутри конвертятся, это не решение
Я вижу ты пишешь симулятор космоса
Выход один - считать самому кординаты в double ОТНОСИТЕЛЬНО камеры (вычитать ее позицию), матрицу поворота оставь для DirectX. Для расстояния до самых дальних объектов (звезд) не хватит и точности double, надо хранить в двух числах, скажем 1 double - координаты звезды в больших единицах (вроде световых годов), 2й double - приращение внутри системы, для самой звезды и планет. При наблюдении из др. системы достаточно первого числа, находясь внутри системы - используешь 2е.
При отрисовке кадра рисуешь сначала звезды либо на "небесной сфере", т.е. все на равном (пофиг чему) расстоянии, без z-buffer, либо если учитываешь перекрытия - используй 1ю звездную координату
Потом обнуляешь z-byffer и рисуешь уже объекты внутри системы где ты находишься
Вот, примерно так
Если будут вопросы - пиши
P.S. Глянь мою тему Случайный Космос
kroonk
Видовая матрица (в OpenGL, в DirectX может отличаться транспонированием)
| 2*near/(right-left) 0 A 0 |
| 0 2*near/(top-bottom) B 0 |
| 0 0 C D |
| 0 0 -1 0 |
A=(right+left)/(right-left)
B=(top+bottom)/(top-bottom)
C=-(far+near)/(far-near)
D=-2*far*near/(far-near)
При far -> бесконечности
C=-1
D=-2*near
Такую матрицу и передавай
Учитывай, что на расстояниях много больше znear Z-byffer неправильно работает в силу недостаточной точности float, вообще дальние объекты надо отрисовывать с отдельной матрицей проекции (большИм z-near) и отдельным z-buffer, либо же сортировать вручную по расстоянию
Для сфер различного радиуса я вывел что надо сортировать по d^2-r^2, где d-расстояние до центра, r-радиус, что дает длину касательной и корректное перекрытие сфер во всех случаях
kroonk
> С точки зрения математики я знаю что это таблица Сколько-нибудьxСколько-нибудь
> с цифрами. В Direct3D 4x4 первые 3x3 определяют поворот а остальные 7 цифр
> непонятно что.
Четвёртая строка матрицы (или столбец, не знаю как в DX) содержит сдвиг (т.е. координаты). Т.е. матрица 3*3 описывает поворот и масштабирование (диагональные элементы умножены на фактор масштаба), а ещё одна строка задаёт сдвиг.
Aslan
> Видеокарты все работают с float, даже когда координаты double передаешь, они
> внутри конвертятся, это не решение
Непарвда, считать в double - это решение. Не нужно пользоваться DX-овскими функциями матричной математики, если они все работают в float (не знаю, если лы там double версии). Все вычисления проводи в double - повороты, сдвиги, масштабирование, умножение матриц, в т.ч. и на матрицу проекции. А потом перед самым использованием матрицы переводи её в float и передавай в DX. В OpenGL например есть функция glLoadMatrixd, она принимает double матрицу и сама уже переводит в float и загружает её.
Реализацю С.К. можешь сделать как описал Aslan. Самое главное - процесс переключения С.К. У меня сделано так. Звёзды и галактики задаются в глобальной С.К., где единица 1 парсек. Функция рендера звёзд помещает в отдельный массив звёзды, находящиеся достаточно близко к камере, чтобы их планетные системы нужно было рендерить с планетами, остальные звёзды рисуются просто точками. Потом в этом массиве ищется ближайшая звезда, и С.К. переключается на неё. Т.е. всё что находится в системе этой звезды, рисуется с центром в звезде (ну или барицентре системы, если звезда двойная-тройная), единица расстояния - километр (хотя можно и парсек оставить, это не принципиально, просто так дебажить удобнее). Самое главное, что и движения камеры обрабатываются тоже в этой С.К. Т.е. класс камеры имеет две координаты - double UniversalPos для описания положения камеры в певрой системе и double LocalStarPos для описания положения относительно центра текущей планетной системы. Координаты обновляются приращением, и оно прибавляется одновременно к обоим величинам:
void UpdatePos(double Shift) { UniversalPos += Shift; LocalStarPos += Shift; }
Просто вторая оказывается точнее, потому что там расстояния не превышают 1 парсека и точности double хватает до метровых расстояний, а в первой (глобальной) системе расстояния могут быть миллиарды парсек, и точности хватает толко до сотых парсека, чего достаточно для звёзд, но недостаточно для планет. При подлёте к новой планетной системе происходит переключение координат:
void SetLocalStar(double starUniversalPos, ObjectID starID)
{
if (localStarID == starID) return;
LocalStarPos = UniversalPos - starUniversalPos;
}
starID - это внутренний id объекта (звезды), проверка делается для того, чтобы переключение происходило только при подлёте к другой звезде (когда та станет ближайшей). Само переключение - это просто вычитание мировых координат камеры ("парсековой" точности) и мировых координат звезды (тоже "парсековой" точности). Дальше все движения вычисляются точно во второй (локальной) С.К., и рендер планет тоже происходит в ней.
Можно ввести ещё несколько аналогичных уровней С.К. - галактический для более точного задания звёзд в галактиках (пока я обхожусь без него) и планетарный - для точного задания мелких объектов на планетах типа деревьев, строений, кораблей. Тебе скорее всего придётся сделать этот ещё один уровень и привязать его к центру корабля. Т.е. расширить немного:
void UpdatePos(double Shift) { UniversalPos += Shift; LocalStarPos += Shift; LocalShipPos += Shift; } void SetLocalStar( double starUniversalPos, ObjectID starID) { if ( localStarID == starID) return; LocalStarPos = UniversalPos - starUniversalPos; } void SetLocalShip( double shipSystemlPos, ShipID shipID) { if ( localShipID == ShipID) return; LocalShipPos = LocalStarPos - shipSystemlPos; }
При этом "локальная корабельная" С.К. цепляется за предыдущую - локльную планетную, для большей точночти.
Ну я понял уже как отдельные системы координат сделать там к примеру надо сделать сначала одну глобальную матрицу и локальную потом глобальную на локальную умножить и всё. И кстати только сейчас попробовал сделать эксперимент посадил камеру на место корабля и камера не трясётся причём не зависимо что я использую float или double а вот если туда корабль посадить то он трясётся. Я передвигаю корабль матрицей то-есть в ней дело.
kroonk
Ну да, надо пересчитывать все координаты, вычитая позицию камеры, для вращения оставь MODELVIEW MATRIX
> потом глобальную на локальную умножить
Как раз нет, смысл чтоб разнести их друг от друга. Т.е. звездное небо ты рисуешь координатами UniversalPos, заданными в парсеках (т.е. соизмеримой с ним величиной), LocalStarPos тут можно смело выкинуть, и Frustum ставишь с соответствующим znear (до ближайшей звезды, кроме той которая в непосредств. близости, ее рисуешь как Солнце (планету) )
Планеты внутри системы, где пребываешь рисуешь по LocalStarPos с др. Frustum, выбираешь соотв-й znear
Можно еще один Frustum для мелких объектов (др. кораблей итд)
Каждый раз обнуляешь z-buffer, т.к. в него пишутся разные величины
Что-то я вообще запутался что такое znear и frustum и как обнулить z-buffer и как тогда правильно реализовать несколько систем координат? Можно рабочий пример приложения с кодом?
kroonk
Frustum - это область обзора, пирамида, определяемая параметрами твоей камеры, размерами экрана и znear, zfar - ближ. и дальнее расстояния отсечения, может они в DX подругому называются, незнаю
Так вот, допустим расстояния внутри звездной системы несоизмеримы с межзвездными, поэтому далекие звезды и ближние планеты рисуешь в разных системах координат, соотв-о меняешь Frustum и znear, я тебе выше написал формулу для ModelView Matrix без zfar, ей пользуйся.
znear необходимо выбирать в зависимости от того, сколь дальние объекты ты рисуешь, см. выше
Кроме znear там параметры left, right и top, bottom, их ставишь так:
float w=znear*tan(fov); // fov-половина угла обзора по горизонтали (в радианах) left=-w; right=w; float h=w*aspect; // aspect - отношение высота/ширина экрана top=h; bottom=-h;
> как обнулить z-buffer
одним вызовом соотв-й функции, читай Help к DX
А как же менять эти системы координат?
Aslan
> left, right и top, bottom
Разве это есть в ДХ?
-Eugene-
я без понятия как в DX, пишу в OpenGL, трудно адаптировать?
P.S. Проще задать через znear, zfar и аспект, если есть такая готовая функция
kroonk
device->setTransform(...)
-Eugene-
> device->setTransform(...)
это устанавливает матрицу а как сделать матрицу системы координат и как её трансформировать к примеру что-бы трансформировать мир
D3DXMATRIX Matrix; D3DXMatrixTranslation(&Matrix, x, y, z); Device->SetTransform( D3DTS_WORLD, &Matrix);
а что писать вместо D3DXMatrixTranslation и D3DTS_WORLD в случае с матрицей системы координат(если такая вообще есть)?
kroonk
> в случае с матрицей системы координат(если такая вообще есть)?
Ты не понимаешь... ЛЮБАЯ матрица служит для перехода между системами координат.
Но я не могу сейчас сказать, какими именно функциями тебе нужно трансформировать мир... Надо думать.
Тема в архиве.