Thibaut Andrieu
Posted on September 8, 2023
Like most of 3D/Game engines, Unity works with single float precision coordinates. As it is not an issue for most use cases, this may be a problem when you deal with geographical coordinates or very large world. Let’s see how to handle this problem using the Unity’s High Precision Framework.
The Unity High Precision Framework
The Unity High Precision Framework is a package that add double precision support to Unity. To use this package, open the Unity Package Manager and add a package from git URL:
Then past the git url from Github repos:
https://github.com/Unity-Technologies/com.unity.gis.high-precision-framework.git
This package comes with a very good sample. It contains a huge plane of 100.000 km over 100.000 km and 3 small scenes located at random places.
Using The Unity High Precision Framework
High Precision Framework define two new components:
The HPRoot component
Create an HPRoot GameObject and add an HPRoot component. Every objects should be under this HPRoot. Even the camera:
HPRoot can also contains a Local Coordinate System script to automatically update RootUniversePosition property depending on some logic. See Strategies to Update HPRoot to get some ideas.
The HPTransform component
Add this component for GameObject that require double precision position. If possible, try to make “container” objects with HPTransform, and children GameObject with position relative to parent. If you build a map of a country, put an HPTransform for each city and put building under corresponding city, but avoid using an HPTransform for each objects.
Also add this component to camera, as it is likely to require double precision to navigate in your world:
The most important thing is to update HPRoot.RootUniversePosition to a position near your current work area. In the sample, the position is update when camera go to another position. See LocalCoordinateSystem.cs:
private void LateUpdate()
{
if (m_Origin != null && !m_LastPosition.Equals(m_Origin.UniversePosition))
{
m_LastPosition = m_Origin.UniversePosition;
m_Root.RootUniversePosition = m_LastPosition;
}
}
Understanding the Problem of Float Precision
I wrote a series of articles that details the problem of float precision in 3D. Let summarize this and see how the HP framework handle it:
Floating point values have a limited number of digit. To simplify, it can store only 6 digits in decimal representation. This means that possible position for objects are aligned on a grid of representable values. If your game consists in walking around a garden near a house, your position will never be higher than a few dozen of meters, giving you a sub-millimeter precision. The grid has a spacing of less than a millimeter, you don’t see you character jumping from millimeter to millimeter.
However, imagine you develop an open world game with planet size map and origin in bottom left. Your grid will look like this:
A few meters aways from the origin, you will have a sub millimeter precision. But if you go 100 km away, you barely have 10 cm precision. And if you go even further, let say 1.000 km aways, you don’t even have 1 meter precision.
This means that, if you try to put a car on a road, it will “jump” from place to place, because its position can only be on the grid of representable floating point values.
Using double precision don’t completely fix the problem, it just push it farther away. With double, you would have ~15 digits precision. Meaning you will keep a millimeter precision even at the other end of the solar system. If you want to cross the galaxy… You will have to find another solution 😊. That’s the role of HPTransform: To define object’s position in double precision.
Using double precision to gives object's position in world space is just one part of the problem. GPU works in float precision. You CAN NOT have end to end double precision. You MUST go back to float precision at some point.
The good news is that floating precision issues are visible only for… visible objects, so only for object close to the camera. And the good news is that, in camera space, you can go back to float precision because object close to camera are… close to camera space origin 😊, so float precision is enough:
Now, the bad news 😭… Doing this “back to float precision” in camera space would require doing it at every frame (or at least every time camera moves). Which would kill performances. It can work for simple cases, but framerate will drop as soon as you will have many objects in your scene.
BUT, what we could do is to define a space which is “not far away from camera”, and update it only once from time to time. For example, define a regular grid on your map, and use current tile origin as “not far away space”:
That’s the role of the HPRoot component. Define a position near current position, and express all GameObject position relative to this position, using float precision.
Strategies to Update HPRoot
The root of the problem is to properly update HPRoot.RootUniversePosition. There are several strategies to do so:
- Attach it to camera position. It works, but kill performances.
- Define a regular grid, and update it when player go to another tile.
- Define a maximum distance, and update it to current player position when the distance is higher than a threshold.
- Update it regularly through a timer. By the way, this strategy was actually used in the old VistaNav GPS.
- Update depending on “teleportation” logic, like in sample.
Limitations
Unity’s HP Framework is not magic and currently have limitations (taken from documentation):
Platform Support
Currently, only 64 bit windows is fully tested. However, the framework is reported to work on other platforms.
Physics
Physics can only work in a static rebasing context due to the performance cost of moving colliders.
Navigation Mesh
Authored navigation meshes can only be used in a static rebasing context.
Particle Systems
World-space particle simulations may or may not behave as expected depending on the rebasing strategy that is used by the high precision rendering scheme. If dynamic rebasing updates the HPRoot, particles simulated in world space will appear to jump around as a function of the rebasing.
Scripts
Any script that reads world-space positions will likely behave strangely when using the High Precision Framework. These scripts would need to be written such that only relative values are used or so that they use the HPTransform component rather than the Transform component.
Shaders
Shaders that rely on world space coordinates, such as tri-planar shaders will behave oddly when used with the High Precision Framework as the world space is constantly being adjusted with dynamic rebasing schemes.
For the Shader part, I’ve used this package with complex shader based on ray-casting and signed distance field and didn’t have any trouble.
Conclusion
It may be hard to adapt an existing Unity game to High Precision framework because of limitations described above. Script dealing with world coordinates might also require rework to use the HPTransform UniversePosition instead of GameObject transform position.
However, this package it is very well adapter to render static objects in very large scene like in GIS applications or flying simulation game.
Posted on September 8, 2023
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.