Самая большая проблема при работе с представлениями позиций с плавающей точкой в крупном масштабе заключается в том, что вы быстро теряете точность по мере того, как все дальше и дальше отдаляетесь от начала координат.
Чтобы исправить это, вам нужно выразить все позиции относительно чего-то другого, кроме источника. Самый простой способ сделать это - разделить мир на сетку и сохранить позиции всех сущностей примерно так:
struct Position {
int kilometers[3]; // x, y and z offset in kilometers
float offset[3]; //x, y and z offset in meters
};
Положение камеры также сохраняется таким образом, и когда приходит время рендеринга, вы делаете что-то вроде этого:
void
getRelativePosition(float& x, float& y, float& z, const Position& origin, const Position& object) {
x = (object.kilometers[0] - origin.kilometers[0]) * 1000.0f +
(object.offset[0] - origin.offset[0]);
//Ditto for y and z
}
//Somewhere later
float x, y, z;
getRelativePosition(x, y, z, camera.position(), object.position());
renderMesh(x, y, z, object.mesh());
(Для простоты я проигнорировал ориентацию камеры и объектов в этом примере, поскольку с этим нет особых проблем).
Если вы работаете с непрерывным миром в галактическом масштабе, вы можете заменить параметр kilometers
на long long
(64 бита), обеспечив эффективный диапазон 1,8 миллиона световых лет.
РЕДАКТИРОВАТЬ: Чтобы использовать это для непрерывной геометрии, такой как ландшафт и т. Д., Вы должны разделить ландшафт на куски размером один квадратный километр, координаты вершин на участке местности должны быть в диапазоне [0, 1000].
Также в приведенной выше функции getRelativePosition
вы можете изменить ее, чтобы она возвращала bool
и возвращала false
, если разница в километрах больше некоторого порога (скажем, расстояния до вашей дальней плоскости отсечения).