Как стать автором
Обновить

Комментарии 17

Самое лучше описание было в «Математические основы машинной графики», написано довольно простым языком, всё при этом качественно иллюстрировано.
Хорошая статья, но есть несколько ошибок.
Во-первых «левосторонняя» и «правосторонняя» — режет слух. Их называют просто «левая» и «правая». И определение левой и правой систем координат у вас странноватое. Их определяют через направление кратчайшего поворота оси «X» до оси «Y», наблюдаемого с вершины оси «Z» (против часовой — правая, по часовой — левая), а графическое представление лишь следствие.

Формула «3» не обосновывается, не понятно, каким образом формируется матрица 1 и из каких положений она переходит к матрице 8. Вы рассписали тригонометрию, что может (должен, по крайней мере) уметь каждый школьник, а самое сложное — опустили.
Спасибо что подняли тему. Достаточно интересный момент, который большинство не знает и просто использует «готовое».
Однако, ИМХО, написано немного замудрено. Хотя может быть это только для меня так. В любом случае — спасибо за работу.
Спасибо. В своё время сам долго голову ломал, разбираясь в «матричном вопросе».
Хех. Я помню на протяжение года (учебного) разбирался на первом курсе с учебником по выш.мату, и выше названной книгой «Математические основы машинной графики». И сейчас прочитав статью (без углубленного анализа конечно), вижу очень похожие формулы и выводы. Тогда правда писалось для 386х, никакого OpenGL. Делал матрицу синусов и косинусов, и брал значения от туда — так как это было быстрее, чем их считать на каждой точке.
Сейчас же я думаю нужно будет не меньше времени, чтобы досконально понимать все написанное. Так что замудренность тут необходима, я думаю.
только меня смущает название статьи?
На мой взгляд, не хватает описания одной очень важной проблемы: какие же значения near_z и far_z все-таки использовать, и какое между ними должно быть соотношение. Ответ можно получить, взглянув на нижеследующий график. Это зависимость значения Z, которое записывается в буфер глубины для дальнейшего теста глубины, от значения координаты Z точки в пространстве вида.

Очевидно, что зависимость нелинейная (в статье это формула (3)), поэтому точки, находящиеся далеко от наблюдателя, но все еще попадающие в объем отсечения, будут иметь коллизии значений Z из-за конечной разрядности буфера глубины (16, 24, 32 бит). Крутизна графика зависит от отношения z_near*z_far/(z_far-z_near): если значение близко к единице, то для дальних объектов точность будет плохая.
Таким образом, если к примеру требуется отрендерить большую сцену на открытом пространстве (ландшафт с деревьями) без ошибок глубины, придется использовать буфер глубины большой разрядности, что все равно в какой-то момент перестанет спасать, поэтому лучшее писать свой умный взять готовый рендерер, который, во-первых, будет сортировать объекты по удаленности (алгоритм художника) и, во-вторых, для каждой группы объектов, в зависимости от расстояния, задавать подходящую матрицу проекции со своими z_near и z_far. Такой подход упрется в быстродействие, поэтому придется использовать прогрессивные сетки с возможностью изменения LOD (уровня детализации) в зависимости от расстояния, но это уже оффтоп.
А идея с алгоритмом художника и некой сеткой мне нравится. Никогда не думал о таком решении проблемы z-файтинга. На настоящий момент использую 32 разрядный буфер глубины в своём GBuffer'e. Параметры матрицы проекции n=0.1f, f=5000.0f. Условная единица сцены — 1 реальный метр. Полностью согласен, что от проблемы z-файтинга это не спасёт, однако на карте площадью в 2 квадратных километра (квадрат со стороной 2 км) с любого ракурса z-файтинга выявлено не было.
Компьютерная графика — это наука об артефактах. Скрыть артефакты — великое искусство программиста и дизайнера.
Идея с прогрессивными сетками принадлежит Хьюджесу Хоппу, она уже давно реализована в библиотеке d3dx из DirectX SDK в качестве интерфейса ID3DXPMESH. Суть его работы заключается в том, что всем вершинам в сетке нужно поставить в соответствие значения весов, а в программе достаточно в нужный момент вызвать метод SetNumFaces либо SetNumVertices, и LOD сетки будет изменен путем удаления наименее значимых вершин. Если интересно, как это работает, вот оригинал статьи.
Честно говоря, с OpenGL у меня совсем поверхностный опыт работы, и для него подобного инструмента я не встречал.
Кстати, насколько я помню, очень давно в World of Warcraft (возможно, что сейчас уже все иначе) во избежание z-файтинга находящиеся далеко от камеры сооружения (например, всякие башни и т.д.) просто не отрисовывались и отображался пустой ландшафт, а по мере приближения внезапно появлялись из ниоткуда.
OpenGL в корне отличается от того, во что превратился DirectX. OpenGL — это интерфейс для работы с графикой и всё. DirectX в свою очередь из подобного интерфейса превратился в разросшееся программное обеспечение. Теперь DirectX занимается всем подряд, начиная от растеризации и заканчивая сетью. Несомненно, это может ускорить разработку среднего игрового движка для двух платформ (PC и XBOX). Однако, на мой взляд, DirectX убивает желание разрабатывать что-то своё. Последний гвоздь в гроб DirectX — его не поддерживают мобильные платформы и тот же самый Apple, потому что DirectX стал непомерно большим. Другое дело OpenGL — он полностью описан в спецификации. Реализовывай хоть для микроволновки.
Факт, что больше придётся заниматься программированием, зато у вас больше контроля над происходящим. В DirectX же если хотите больше контроля, то начинаете отказываться от штук, типа ID3DXPMESH. Вспомните хотябы первый Crysis. Чуваки из Crytek просто отказались от всех наворотов DirectX, и практически с нуля написали свой рендерер. А потом, в DirectX появисиль интерфейсы которые были реализованы в Crytek. Не знаю, что будет с 12 версией DirectX, но я крайне скептичен. Время рассудит.
Есть, кстати, такой подход, называется — reversed depth buffer, он довольно хорошо справляется с проблемой больших пространств.
У меня такой вот вопрос.
В браузере используется перспектива с d=500 и поворот div на 50 градусов.
матрица которую делает хром
matrix3d(
	1, 0, 0, 0,
	0, 0.642787609686539, 0.766044443118978, -0.00153208888623796,
	0, -0.766044443118978, 0.642787609686539, -0.00128557521937308,
	0, 0, 0, 1
)

Известно что она транспонированная.
Задача: есть координаты точки
[x,y]

мы их приводим к однородным
[x,y,0,1]

z — у нас нет по сути. Верно?
затем умножаем ее на матрицу трансформации,
и получаем вектор
[x', y', 0, w]

как я понял из вашего описания.
То есть теперь если я разделю x'/w и y'/w, то я получу координаты точки [x, y] только спроецированные на плоскость что на экране.
Но у меня срабатывает поворот, то есть если убрать проекцию то точка поворачивается на нужный градус и совпадает с желаемым результатом, а вот проекцию ни как не могу поймать.
то есть что-то происходит но оно не то что нужно.
Еще прочитал в спецификации css что коэффициент проекции будет d/(d — Z), где d — дистанция, а что есть Z я не смог разобраться. Пробовал делать [x, y, 1, 1] но это не верно получается. Пробовал подставить в коэффициент w но тоже лажа какая то, когда начинаю смещать то все едет не пропорционально.

Подскажите пожалуйста что я не так делаю и где ошибаюсь.
image
попробовал только с делением x,y на w
точки попали куда надо.
Но при перемещении все равно происходит неверный сдвиг.
Возможно дело уже за малым понять почему сдвиг косячит.

Но буду благодарен любой помощи.
Посмотрев на матрицу matrix3d, я могу сказать точно, это не чистая матрица проекции. Предположим это композиционная матрица, в которую уже зашили модельное, видовое и проекционное преобразование.
Первый вопрос: Какая смысловая нагрузка содержалась в модельной, видовой и перспективной матрице?
Второй вопрос: ваши двухмерные координаты x и y. В каком они пространстве? Модельном, мировом или видовом?

Важный момент: после умножения видовых координат [x, y, 0, 1] на матрицу проекции вы получаете однородные координаты. Они для рисования в двухмерном пространства монитора не подходят. Однородные координаты необходимо перевести в декартовы координаты. Для этого вы производите деление на w. Обратите внимание, что ваш результат будет не в пикселях, а в условных единицах. Если x и y в диапазоне [-1 1], то точка попадает пространство монитора. Скажем реальное разрешение области в которую необходимо рисовать 1024х512. После умножения координат на проекционную матрицу и преобразования результата в декартовы координаты вы, например, получаете x = -0.3, y = 1.0. Если точко отсчёта это правый верхний угол (как принято в WinAPI ).

То координаты в пикселях будут:
x = ( 1024 / 2 ) + ( -0.3 ) * ( 1024 / 2 ) = 512 — 153.6 = 358.4 или 358
y = ( 512 / 2 ) + ( -1.0 ) * ( 512 / 2 ) = 256 — 256 = 0

Если точка отсчёта это правый нижний угол (как в OpenGL)

То координаты в пикселях будут:
x = ( 1024 / 2 ) + ( -0.3 ) * ( 1024 / 2 ) = 512 — 153.6 = 358.4 или 358
y = ( 512 / 2 ) + 1.0 * ( 512 / 2 ) = 256 + 256 = 512

В OpenGL примерно эту же работу выполняет функция glViewPort ( x, y, width, heigth ).
Да в этой матрице уже комбинация всех операции, то есть и поворота и проекции.

Спасибо сейчас попробую применить ваши замечания.
Спасибо за консультацию.

В общем разобрался.
works.artzub.com/onur/
t = projection.convertPoint([t.x - transPoint.x - w2, t.y - transPoint.y - h2]);
return [
    t[0] / t[3] + w2 + transPoint.x
    t[1] / t[3] + h2 + transPoint.y
]

в projection завернута матрица и умножение на нее при вызове convertPoint.

после умножения видовых координат [x, y, 0, 1] на матрицу проекции вы получаете однородные координаты.

Не знаю так это или нет.
Но видимо по наитию делаю вот так:
t.x - transPoint.x - w2 

где
w2 = {ширина рабочего Div}/2
transPoint.x = это смещение от начала координат

как раз и приводит к тому что считается в видовых координатах
а потом делаю так
t[0] / t[3] + w2 + transPoint.x

получаю координат для отрисовки на экране =)

Спасибо еще раз за ответ.
Рад был помочь, дружище
Зарегистрируйтесь на Хабре, чтобы оставить комментарий

Публикации