Математика в Gamedev по-простому. Матрицы и аффинные преобразования

    Всем привет! Меня зовут Гриша, и я основатель CGDevs. Сегодня хочется продолжить тему математики в геймдеве. В предыдущей статье были показаны базовые примеры использования векторов и интегралов в Unity проектах, а сейчас поговорим о матрицах и аффинных преобразованиях. Если вы хорошо разбираетесь в матричной арифметике; знаете, что такое TRS и как с ним работать; что такое преобразование Хаусхолдера – то вы возможно не найдёте для себя ничего нового. Говорить мы будем в контексте 3D графики. Если же вам интересна эта тема – добро пожаловать под кат.



    Начнём с одного из самых главных понятий в контексте статьи – аффинные преобразования. Аффинные преобразования – это, по сути, преобразование системы координат (или пространства) с помощью умножения вектора на специальную матрицу. К примеру, такие преобразования, как перемещение, поворот, масштабирование, отражение и др. Основным свойствами аффинных преобразований является то, что вы остаётесь в том же пространстве (невозможно сделать из трёх мерного вектора двумерный) и то, что если прямые пересекались/были параллельны/скрещивались до преобразования, то это свойство после преобразования сохранится. Помимо этого, у них очень много математических свойств, которые требуют знания теории групп, множеств и линейной алгебры, что позволяет работать с ними проще.

    TRS матрица


    Вторым важным понятием в компьютерной графике является TRS матрица. С помощью неё можно описать самые частые операции, используемые при работе с компьютерной графикой. TRS матрица – это композиция трёх матриц преобразования. Матрицы перемещения (Translation), поворота по каждой оси (Rotation) и масштабирования (Scale).
    Выглядит она так.



    Где:
    Перемещение – это t = new Vector3(d, h, l).
    Масштабированиеs = new Vector3(new Vector3(a, e, i).magnitude, new Vector3(b, f, j).magnitude, new Vector3(c, g, k).magnitude);

    Поворот – это матрица вида:



    А теперь перейдём чуть глубже к контексту Unity. Начнём с того, что TRS матрица – это очень удобная вещь, но ей не стоит пользоваться везде. Так как простое указание позиции или сложение векторов в юнити будет работать быстрее, но во многих математических алгоритмов матрицы в разы удобнее векторов. Функционал TRS в Unity во многом реализован и в классе Matrix4x4, но он не удобен с точки зрения применения. Так как помимо применения матрицы через умножение она может в целом хранить в себе информацию об ориентации объекта, а также для некоторых преобразований хочется иметь возможность рассчитывать не только позицию, а изменять ориентацию объекта в целом (к примеру отражение, которое в Unity не реализовано)

    Все примеры ниже приведены для локальной системы координат (началом координат считается позиция GameObject’а, внутри которого находится объект. Если объект является корнем иерархии в юнити, то начало координат – это мировые (0,0,0)).

    Так как с помощью TRS матрицы можно в принципе описать положения объекта в пространстве, то нам нужна декомпозиция из TRS в конкретные значения position, rotation и scale для Unity. Для этого можно написать методы-расширения для класса Matrix4x4

    Получение позиции, поворота и скейла
    public static Vector3 ExtractPosition(this Matrix4x4 matrix)
    {
    	Vector3 position;
    	position.x = matrix.m03;
    	position.y = matrix.m13;
    	position.z = matrix.m23;
    	return position;
    }
    
    public static Quaternion ExtractRotation(this Matrix4x4 matrix)
    {
    	Vector3 forward;
    	forward.x = matrix.m02;
    	forward.y = matrix.m12;
    	forward.z = matrix.m22;
    
    	Vector3 upwards;
    	upwards.x = matrix.m01;
    	upwards.y = matrix.m11;
    	upwards.z = matrix.m21;
    
    	return Quaternion.LookRotation(forward, upwards);
    }
    
    public static Vector3 ExtractScale(this Matrix4x4 matrix)
    {
    	Vector3 scale;
    	scale.x = new Vector4(matrix.m00, matrix.m10, matrix.m20, matrix.m30).magnitude;
    	scale.y = new Vector4(matrix.m01, matrix.m11, matrix.m21, matrix.m31).magnitude;
    	scale.z = new Vector4(matrix.m02, matrix.m12, matrix.m22, matrix.m32).magnitude;
    	return scale;
    }


    Кроме того, для удобной работы можно реализовать пару расширений класса Transform, чтобы работать в нём с TRS.

    Расширение трансформа
    public static void ApplyLocalTRS(this Transform tr, Matrix4x4 trs)
    {
    	tr.localPosition = trs.ExtractPosition();
    	tr.localRotation = trs.ExtractRotation();
    	tr.localScale = trs.ExtractScale();
    }
    
    public static Matrix4x4 ExtractLocalTRS(this Transform tr)
    {
    	return Matrix4x4.TRS(tr.localPosition, tr.localRotation, tr.localScale);
    }


    На этом плюсы юнити заканчиваются, так как матрицы в Unity очень бедны на операции. Для многих алгоритмов необходима матричная арифметика, которая в юнити не реализована даже в совершенно базовых операциях, таких как сложение матриц и умножения матриц на скаляр. Кроме того, из-за особенности реализации векторов в Unity3d, так же есть, ряд неудобств, связанных с тем, что вы можете сделать вектор 4х1, но не можете сделать 1х4 из коробки. Так как дальше пойдёт речь про преобразование Хаусхолдера для отражений, то сначала реализуем необходимые для этого операции.

    По сложению/вычитанию и умножению на скаляр – всё просто. Выглядит достаточно громоздко, но ничего сложного тут нет, так как арифметика простая.

    Базовые матричные операции
    public static Matrix4x4 MutiplyByNumber(this Matrix4x4 matrix, float number)
    {
    	return new Matrix4x4(
    		new Vector4(matrix.m00 * number, matrix.m10 * number, matrix.m20 * number, matrix.m30 * number),
    		new Vector4(matrix.m01 * number, matrix.m11 * number, matrix.m21 * number, matrix.m31 * number),
    		new Vector4(matrix.m02 * number, matrix.m12 * number, matrix.m22 * number, matrix.m32 * number),
    		new Vector4(matrix.m03 * number, matrix.m13 * number, matrix.m23 * number, matrix.m33 * number)
    	);
    }
    
    public static Matrix4x4 DivideByNumber(this Matrix4x4 matrix, float number)
    {
    	return new Matrix4x4(
    		new Vector4(matrix.m00 / number, matrix.m10 / number, matrix.m20 / number, matrix.m30 / number),
    		new Vector4(matrix.m01 / number, matrix.m11 / number, matrix.m21 / number, matrix.m31 / number),
    		new Vector4(matrix.m02 / number, matrix.m12 / number, matrix.m22 / number, matrix.m32 / number),
    		new Vector4(matrix.m03 / number, matrix.m13 / number, matrix.m23 / number, matrix.m33 / number)
    	);
    }
    
    public static Matrix4x4 Plus(this Matrix4x4 matrix, Matrix4x4 matrixToAdding)
    {
    	return new Matrix4x4(
    		new Vector4(matrix.m00 + matrixToAdding.m00, matrix.m10 + matrixToAdding.m10,
    			matrix.m20 + matrixToAdding.m20, matrix.m30 + matrixToAdding.m30),
    		new Vector4(matrix.m01 + matrixToAdding.m01, matrix.m11 + matrixToAdding.m11,
    			matrix.m21 + matrixToAdding.m21, matrix.m31 + matrixToAdding.m31),
    		new Vector4(matrix.m02 + matrixToAdding.m02, matrix.m12 + matrixToAdding.m12,
    			matrix.m22 + matrixToAdding.m22, matrix.m32 + matrixToAdding.m32),
    		new Vector4(matrix.m03 + matrixToAdding.m03, matrix.m13 + matrixToAdding.m13,
    			matrix.m23 + matrixToAdding.m23, matrix.m33 + matrixToAdding.m33)
    	);
    }
    
    public static Matrix4x4 Minus(this Matrix4x4 matrix, Matrix4x4 matrixToMinus)
    {
    	return new Matrix4x4(
    		new Vector4(matrix.m00 - matrixToMinus.m00, matrix.m10 - matrixToMinus.m10,
    			matrix.m20 - matrixToMinus.m20, matrix.m30 - matrixToMinus.m30),
    		new Vector4(matrix.m01 - matrixToMinus.m01, matrix.m11 - matrixToMinus.m11,
    			matrix.m21 - matrixToMinus.m21, matrix.m31 - matrixToMinus.m31),
    		new Vector4(matrix.m02 - matrixToMinus.m02, matrix.m12 - matrixToMinus.m12,
    			matrix.m22 - matrixToMinus.m22, matrix.m32 - matrixToMinus.m32),
    		new Vector4(matrix.m03 - matrixToMinus.m03, matrix.m13 - matrixToMinus.m13,
    			matrix.m23 - matrixToMinus.m23, matrix.m33 - matrixToMinus.m33)
    	);
    }


    Но для отражения нам понадобится операция умножения матриц в конкретном частном случае. Умножение вектора размерности 4х1 на 1х4 (транспонированный) Если вы знакомы с матричной математикой, то знаете, что при таком умножении надо смотреть на крайние цифры размерности, и вы получите размерность матрицы на выходе, то есть в данном случае 4х4. Информации по тому, как перемножаются матрицы достаточно, поэтому это расписывать не будем. Вот для примера реализованный конкретный случай, который нам пригодится в будущем

    Перемножение вектора на транспонированный
    public static Matrix4x4 MultiplyVectorsTransposed(Vector4 vector, Vector4 transposeVector)
    {
    
    	float[] vectorPoints = new[] {vector.x, vector.y, vector.z, vector.w},
    		transposedVectorPoints = new[]
    			{transposeVector.x, transposeVector.y, transposeVector.z, transposeVector.w};
    	int matrixDimension = vectorPoints.Length;
    	float[] values = new float[matrixDimension * matrixDimension];
    
    	for (int i = 0; i < matrixDimension; i++)
    	{
    		for (int j = 0; j < matrixDimension; j++)
    		{
    			values[i + j * matrixDimension] = vectorPoints[i] * transposedVectorPoints[j];
    		}
    
    	}
    
    	return new Matrix4x4(
    		new Vector4(values[0], values[1], values[2], values[3]),
    		new Vector4(values[4], values[5], values[6], values[7]),
    		new Vector4(values[8], values[9], values[10], values[11]),
    		new Vector4(values[12], values[13], values[14], values[15])
    	);
    }


    Преобразование Хаусхолдера


    В поисках того, как отразить объект относительно какой-либо оси, я часто встречаю совет поставить отрицательный scale по необходимому направлению. Это очень плохой совет в контексте Unity, так как он ломает очень много систем в движке (батчинг, коллизии и др.) В некоторых алгоритмах это превращается в достаточно нетривиальные вычисления, если вам надо отразить не банально относительно Vector3.up или Vector3.forward, а по произвольному направлению. Сам метод отражения в юнити из коробки не реализован, поэтому я реализовал метод Хаусхолдера.

    Преобразование Хаусхолдера, используется не только в компьютерной графике, но в этом контексте — это линейное преобразование, которое отражает объект относительно плоскости, которая проходит через «начало координат» и определяется нормалью к плоскости. Во многих источниках оно описано достаточно сложно, и непонятно, хотя его формула – элементарна.

    H=I-2*n* (n^T)

    Где H – матрица преобразования, I в нашем случае – это Matrix4x4.identity, а n = new Vector4(planeNormal.x, planeNormal.y, planeNormal.z, 0). Символ T означает транспонирование, то есть после умножения n* (n^T) мы получим матрицу 4х4.

    Тут пригодятся реализованные методы и запись получится очень компактной.

    Преобразование Хаусхолдера
    public static Matrix4x4 HouseholderReflection(this Matrix4x4 matrix4X4, Vector3 planeNormal)
    {
    	planeNormal.Normalize();
    	Vector4 planeNormal4 = new Vector4(planeNormal.x, planeNormal.y, planeNormal.z, 0);
    	Matrix4x4 householderMatrix = Matrix4x4.identity.Minus(
    		MultiplyVectorsTransposed(planeNormal4, planeNormal4).MutiplyByNumber(2));
    	return householderMatrix * matrix4X4;
    }


    Важно: planeNormal должна быть нормализована (что логично), а также последней координатой n стоит 0, чтобы не было эффекта растяжения по направлению, так как оно зависит от длинны вектора n.

    Теперь для удобства работы в Unity реализуем метод расширение для трансформа

    Отражение трансформа в локальной системе координат
    public static void LocalReflect(this Transform tr, Vector3 planeNormal)
    {
    	var trs = tr.ExtractLocalTRS();
    	var reflected = trs.HouseholderReflection(planeNormal);
    	tr.ApplyLocalTRS(reflected);
    }


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



    Спасибо за внимание!
    Поделиться публикацией

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

      0
      Есть ли способ передвинуть объект на 17 и более километров от центра так, чтобы графика не взорвалась?
      Или таки придётся делать floating origin?
        0
        Перейти с float на double, либо да, двигать привязку координат.
          0
          Это я знаю, но.
          Да, я могу отказаться от PhysX и использовать свою кинематику, основанную на double. Этого хватит на 17 миллионов (или миллиардов? не помню) километров, что для «наземной» симуляции весьма достаточно. Хватит и на миллиметровую точность или ещё точнее.
          Но сам-то мир Unity3D, вроде бы, построен на float, и, помимо кинематики в вакууме, есть:
          * коллизии. Сможет ли Unity безглючно рассчитать контакт автомобиля или лучше самолёта с террейном далеко от центра, если расстояние в сантиметрах будет выше ёмкости float? А мешей объектов друг с другом?
          * графическое отображение. Сможет ли Unity безглючно визуализировать террейн и окружающие объекты так же далеко от центра?
            0
            В вопросе не было ни слова про юнити. Нет, штатная физика в юнити не умеет большие дистанции от начала координат как раз из-за ограничения точности float-ов. Нет, визуализация так же использует float-ы.
              0
              Спасибо!
              Хотя вопрос включал графику к физике, что могло подразумевать фреймворк/либу, предположительно Unity ;-)
              А что, есть графика на double?
                0
                А почему бы и нет? Софтовый рендер никто не отменял :)
              0
              Ну с 17 км будет сантиметровая точность. Но вообще в Unity да — floating origin. Можно придумать какой угодно маппинг, но по своей сути — это floating origin. И сохранить значения PhysX физики не так сложно (ну проскочит один кадр или можно сделать ручную компенсацию)

              В целом тут две основные причины почему рендер на double особого смысла не имеет. Если камера движется вместе с симуляцией, то с помощью тех же матриц компенсировать позиции чисто для рендера — не самая страшная задача. Дабл работает медленнее, так как просто процессору нужно больше регистров складывать. И скажем PhysX сам по себе работает с флотом. С точки зрения рендера такая точность не имеет смысла, так как объект на расстоянии 17 км от камеры или 18 км от камеры это в большинстве случаев и так, и так пару пикселей. Если нужна точность для расчёта самой симуляции и без PhysX (какая-нить жидкостная симуляция или просто математическая, а юнити для визуализации) то проще считать всё в дабле в своих методах, где нужна точность, а потом делать отображение модели симуляции во флот со сдвигом.
                0
                В общем дело тут не только в Unity. Сам PhysX на такое не рассчитан, а с точки зрения рендера такой диапазон чисел просто смысла не имеет. Так как если дистанция между объектом и камерой будет 17км, то объект тупо не будет видно в большинстве случаев с перспективной камерой. А дабл жрёт больше памяти, работает медленнее и т.п. Конечно было бы удобное апи, чтобы люди стреляли себе в ногу и не понимали, почему фпс в разы медленнее, чем на флоте, и почему PhysX всё равно глючит, если в него прокидывается дабл. А так, учитывая что всё то, что находится в фруструме камеры не требует такой точности, достаточно построить правильное преобразование всех позиций из одного трёхмерного пространства в другое. Да, применить ко всем объектам. Исходя из «абстрактной позиции мира» и «позиции камеры». Самое простое (но правда для реалтайма вряд ли подходит при сложной симуляции) — считать что камера в (0,0,0) вообще всегда для рендера, и к ней подтягивается мир. В этом случае у нас ломается только OcclusionCulling, но зато самая высокая точность с точки зрения рендера будет по идее)
                  0

                  Тут не просто PhysX на это не рассчитан, а тут сразу целый ворох проблем:
                  1) Обычные видеокарты работают во флотах, погуглите цену видеокарт которые могут и даблы и флоты — это Квадры и Теслы.
                  2) PhysX вообще изначально затачивался под ускорение на GPU (см 1) и векторизацию (см далее), так шо да — там только флоты
                  3) SSE будет работать быстрее — вместо 2-х double будет отрабатываться 4 float (вообще цифры от балды, надо смотреть по конкретному процессору сколько у него FPU и какая у них разрядность, но в любом случае, процессор за инструкцию сможет отрабатывать вдвое больше флотов чем даблов)
                  4) Память и пропускная способность памяти...


                  Но это все верно для векторизованных операций, для скалярных операций нет никакой разницы (хотя за счёт выравнивания можно немного потерять для float).


                  Но в любом случае доя обычной ТрыДэ графики только флоты и ничего более. Даблы — это все же для научных вычислений (вот там я сталкивался с тем что и двойной точности может не хватить).

            0

            В PhysX можно при создании сцены указать примерные размеры с которыми вы будете работать. Т.е. если у вас симулятор самолёта, то можно подсказать движку что у вас в юнете 100м, и он, скорей всего, будет внутри у себя масштабировать на это значение, чтобы не терять в точности.

              0

              Да, согласен.
              Но если самолёт летит над танком? :-)

                0
                И ничего не будет. Вот когда вы начнёте выцеливать лючок на нём, то возможно будет сложно попасть в него. Да и то не факт, это всего лишь тысячная часть единицы, которая во флоте не куда не плавает.
                  0

                  Один юнит равный 100м в любом случае даст такую же ошибку флота, так как коичество значащих символов не изменится, а на вход PhysX получает флот, и там он округлится

            0
            Блин, рейтинга не хватает проголосовать ) нормальные статьи продолжай в том же духе ).
              –2
              Спасибо) Но в целом видимо математику не любят. По сравнению с другими статьями статистика прям печальная)
                0
                а с точки зрения рендера такой диапазон чисел просто смысла не имеет

                Смысл есть — если оба, и камера, и объект на 20км от центра, при этом рядом друг с другом, то неточность float, как я предполагаю, можно будет наблюдать вблизи. Хотя бы потому, что графический mesh точно так же поплывёт.

                О математике: зависит от математики. Иногда авторы дают сущие трюизмы. Иногда авторы дают темы, настолько нерелевантные хабру, что я не знаю, что и говорить.
                  0
                  Позиции в сцене юнити для рендера и позиции в симуляции не обязаны совпадать. Абстрагировать симуляцию так, чтобы у тебя камера всегда находилась в (0,0,0) в юнити сцене, а остальное двигалось с поправкой на это — не супер большая проблема. Смысла нет, так как действительно важно расстояние между камерой и объектами. OpenGL если мне не изменяет память работает с интом и флотом (GLSL), а PhysX с флотом, то есть дабл округлится в этой точке, и мы получим тоже самое. Задача симуляции — не так часто встречается и легко обыгрывается, но на уровне движка её обыгрывать нет смысла, так как это приводит к не очевидному поведению.

                  По проводу тривиального забавно, как быстро профессионалы забывают, что такое быть новичком и вообще не осознавать для чего знать тот же метод Хаусхолдера. При том, что встанет задача распарсить любой формат у которого ось y в отличии от юнити направлена вниз, а сам формат хранит TRS (собственно формат LDraw про который я сейчас потихоньку пишу статью) и без матриц крякнешься думать, как это всё считать.

                  TRS — это вообще отдельная песня. Так как, как и с методом Хаусхолдера найти адекватное структурированное описание что это и из чего складывается — надо постараться. Сама матрица простая, если это написано не языком вышмата, а русским. Так как если мы перейдём к определению, в котором вообще это всё работает в гиперплоскости строго говоря, то думаю большинство тупо не сможет прочесть этот текст со справочником (матрица 4х4, а работаем мы с 3д пространством)

                  В целом меня закладки в плане статистики интересуют больше голосов, так как они больше показывают кому та или иная статья была полезна. Эта серия статей не про рокет саенс и сложные алгоритмы. Это по сути про популяризацию математики, так как чтобы была мотивация её учить, нужно чтобы было понятно — зачем. И это зачем проще объяснять на простых примерах, которые можно потрогать и поиграться с ними. И которые в целом легко прочесть.

                  Потом постепенно может буду увеличивать сложность материала. Но условно если брать кватернионы, про них есть крутая огромная статья объясняющая что это, и вот её цель как раз таки научить. Цель же этого цикла показать — зачем читать эту огромную статью про кватернионы к примеру, и дать готовую реализацию каких-то простых компонент для примера, чтобы было ясно, как это можно использовать в Unity. В конкретно этой статье примера нет, так как про LDraw хочется написать отдельно
                    0
                    По этой причине это сказано в самом начале до ката, что тут нет ничего супер сложного, и если вы разбираетесь в теме, то в статье не найдёте ничего нового. Но я работаю с огромным числом самых разных проектов, так как не являюсь штатным сотрудником, и чаще дорабатываю чужие решения. И вот костыли связанные с незнанием математики встречаются слишком часто. При этом достаточно простых вещей, а не того, что допустим при физическом моделировании, при использовании метода конечных элементов, ошибка зависит от самого острого угла треугольника, и поэтому EarClipping — не лучший алгоритм, чтобы триангулировать что-либо в подобных задачах. И надо использовать хотя бы Делоне.
                    0
                    >> Смысл есть — если оба, и камера, и объект на 20км от центра, при этом рядом друг с другом, то неточность float, как я предполагаю, можно будет наблюдать вблизи. Хотя бы потому, что графический mesh точно так же поплывёт.

                    18 км — это максимальная дальность горизонта… в море, со смотровой вышки в 25 метров. 50 км — это уже с воздушного шара и это максимальная дистанция на которой человеческий глаз будет способен заметить пламя свечи… в полной темноте.
                    5 км — это то расстояние на котором поверхность Земли уже имеет заметное искривление для человеческого глаза, если игровая сцена больше, то это уже надо земной шарик симулить и это уже совершенно иной уровень игр :-)

                    Если не делать симуляции космических миров и не заниматься научными расчетам, то я если честно плохо понимаю откуда могут взяться эти десятки километров. Если уж говорить о симуляторах, типа DCS, Orbiter, Silent Hunter, то Юнити явно не движок выбора для таких игр. Хотя вон KSP сделан на Unity, там флоты и ничего, они как-то с этим справились (хотя глюки связанные с точностью там таки вылазиют).
                      0
                      Яковлев Як-9, обычный, рядовой истребитель, 1943 год.
                      Максимальная скорость 600км/ч на высоте 4000м.
                      16км — это меньше двух минут.
                      Для 1945 нормальные скорости 680-720км/ч.
                      Играть в «NATOвские истребители не могут в Эстонии превысить скорость звука, потому, что территория настолько мала и кончается слишком быстро для разгона» не охота.

                      С другой стороны интересно посмотреть, как это всё будет летать над танками, среди которых ультра-максимальная скорость 72км/ч. Такая же для обычного эсминца.
                      Скорость вне дороги 16-20км/ч, что потребует до 60 минут на 16км.
                      Оно, конечно, если бы я хотел поэкспериментировать с самолётиками и только — это одно.
                        0
                        А это вот о том что я писал — уже полноценный симулятор Вы хотите. На мой взгляд это уже не на Unity надо делать. DCS, Orbiter, Ил-2, Тундра, Элит, Стар Ситтезен, и даже такие RTS, как Steel division (там тоже территории огромные) — все на самописных движках (или очень сильно переколбашенных) и писали их очень-очень пряморукие Си++ погроммисты. И эти игры очень сильно отличаются от 99% типичных.

                        Т.е. либо все должно происходить в масштабах максимум 5км х 5км, а все скорости и дальности стрельбы делать в логарифмическом масштабе. Например, реально танки не 70-80 км/ч раскатывают по полю боя — это вообще что-то запредельно, раскатывают по полю боя они куда медленнее. В добавок они же должны совместно с пехотой работать, а это сразу ограничение на среднею скорость. Так что на все эти 40+ км/ч — это на марше по ровному шоссе можно рассчитывать. А например, у сверхзвукового F-16 крейсерская скорость чойт порядка 0.68..0.84 Маха (скорость звука зависит от высоты, как и крейсерская и максимальная скорость самолета), т.е. 230..280 м/с, что не сильно быстрее того же Яка-9 (и даже почти как у пассажирского Боинга), у которого эти 600 км/ч (~170 м/с, т.е. примерно 0.5 Маха) бывают только по праздникам и на большой высоте (4 км и более), так то реальный диапазон скоростей у Як-9 которым он будет оперировать на практике будет думаю где-то в районе 150-300 км/ч (в общем пролетать на нем карту 5x5 игроки в среднем будут за минуту-две, что уже дофига), а реальный оперируемый диапазон скоростей у F-16 будет начинается где-то эдак с 300 км/ч (примерно с 80 м/с), а дальше уже по ситуации, а все эти 1000+ км/ч и сверхзвуки — это на форсаже. Т.е. даже у реальных объектов рядовые цифры скромнее. Ещё есть ограничения и на скорость восприятия информации человеком. В общем если сильно не упирать на симуляция, то можно немного подрезать максимальные скорости или сделать масштаб нелинейным.

                        Либо принебрегать детализацией, допустим 1 unit не 1 метр, а 100 метров или сколько там можно выбрать.

                        Либо придется много страдать и костылять. А страдать придётся сильно. На процессоре с использованием SIMD инструкций производительность в даблах упадёт вдвое по сравнению с сингл, а на GPU (а главная фишка того же PhysX, то что он умеет юзать видеокарту для ускорение) производительность упадёт совсем круто и это будет куда больше чем в 2 раза, т.к. они (именно геймерские сегмент) заточены исключительно под сингл-прецижен, да ещё и память не резиновая у них. Собственно с графоном тоже могут начать вылазить косяки страдать гигантоманией (не уверен).

                        В общем без страданий и костылей в Unity3D чуда не случится. У разработчиков KSP — получилось, но если судить по тому как долго шла разработка и какие весёлые баги я там наблюдал как игрок, то костылей там немерено было и все это было через боль, страдания и унижения. wiki.kerbalspaceprogram.com/wiki/Deep_Space_Kraken/ru

                        Просто 6-7 значимых деесятичных знаков (сингл) вообще-то вполне себе хватает для большинства прикладных задач. 14-15 значимых знаков (дабл) — это уже уровень научных вычислений. Но если начать симулить солнечную систему в реальном масштабе (есть такая игра как Rodina, там нечто подобное), то тут и даблов может не хватит, но я с таким сталкивался всего пару раз в очень спецефических задачах.
                    0
                    Да. Похоже что так и это пичалька. Из личного опыта — был у меня как-то проект под который внезапно расширялся штат и как раз искал программиста в первую очередь и из gamedev (кстати и сейчас ищут я, но уже не ко мне и там совсем все жёстко, как раз с ТрыДэ графоном). Шерстили геймдеве И
                    Именно из того расчёта, что с математикой они там точно дружат, а преобразование Хаусхолдера и кватернионы (на самом деле я сам это слово пишу часто с ошибкой) для него не будут словами из заклинания по вызову Ктулху.
                    Ну перебрать пришлось 50+ человек, откопал 2-3 (и таки с геймдев опытом), но реально удовлитворительный математический бэкграунд был только у одного. Я сам на самом деле с детства не люблю матрицы-шматрицы и все эти комплексные и гиперкомплексные числа, но ситуация чойт совсем пичалька если честно — математику не знают и самое главное учить не хотят. И судя по рейтингу Вашего сообщения ещё и математикофобией народ страдает.

                    Самое забавное тут пишут про трюизмы, но я уже сбился со счета сколько раз народ спрашивает как реализовать тот же PID регулятор на Unity (даже статью тут запилил ради этого), хотя это уже давно пережеванная и избитая тема. Так что любая практическая около математическая статья полезна — у всех есть пробелы в знаниях, а человек хорошо помнит только то, чем недавно занимался… но вот рейтинги таких статей…
                  –1
                  Про кватернионы хоть что-нить сказали ради приличия.

                  Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.

                  Самое читаемое