Floating origins

Rendering large models can cause view flickering effects if we zoom on some model area while being very far from the origin. This problem is often referred to as a "floating origin" problem, which is a problem of insufficient numerical accuracy during the rendering of the image.

REDsdk is a simple and double precision engine: RED::Vector3, RED::Vector4 and RED::Matrix classes are native double precision. Geometrical data in RED::IMeshShape can be either simple or double precision.

We'll discuss this problem from hardware rendering. Software rendering in REDsdk does not suffer from this as all data can be stored and rendered in native double precision.

The real problem with double precision and hardware rendering is that most GPUs do suffer of a terrible slowdown if any double precision data is natively rendered. Slowdowns are between 50% and 95%, making native double precision rendering practically very limited to a few high end graphic cards.

We may need double precision data because we have to render wide models with a decent numerical accuracy. Unfortunately, floating point values have only 7 digits of numerical accuracy, so a 100 km model can't accurately display objects of less than 10 cm, which can be problematic to many applications.

Floating origin: problem description

The problem with floating origin comes from the accuracy of the multiplication operation between the model view matrix and the vertex coordinates. If we have vertex coordinates above 10.000.000 units, we start loosing decimals in our resulting [matrix] x vertex product if our model view matrix also has big values stored in it because we're looking closely to these far away vertices.

Using regular OpenGL, we try to load matrices using double precision floating point values. Despite the storage of matrices in native double precision in REDsdk, many graphic adapters will consider loading matrices using simple floating point precision values, so they'll clamp REDsdk data. At the shader stage, a simple precision model-view matrix is used, and the multiplication of this matrix by a vertex with big coordinates looses some decimals in the result.

This loss in numerical accuracy will cause flickering effects of our view if we try to zoom on our model, far away from our origin.

Bypassing matrix multiplication issues

A solution to this problem is known as applying a floating origin: instead of storing a vertex coordinate at 10.000.000 units away from the origin, we do store a translation matrix of 10.000.000 and a vertex coordinate around the origin: we introduce an intermediate transformation matrix that store the big values so that geometrical data can be stored using small values.

Ok, but this don't solve the problem of the numerical values in the model view matrix. Right. We do have a model matrix which stores big values (our 10.000.000 units translation). We do have a viewing matrix which is far away too, zooming over a small portion of our model, but around 10.000.000 units away from the origin. So the product of both matrix will be inaccurate if done using simple precision floating point values, and we won't solve our view flickering problem.

REDsdk has 'high definition matrices' that are enabled by default and accessed by various stage shader programs:

These matrices are available in addition to the standard modelview and mvp matrices of the regular OpenGL pipeline.

Using these matrices will allow you to define local floating origins for your data: thanks to the full double precision calculations that take place in REDsdk - CPU side - matrix data uploaded on the GPU will have been calculated as accurately as possible, BEFORE being clamped to simple precision floating point value for the vertex programs. And the whole trick resides in the fact that the produce of the model matrix by the view matrix results in small coordinates that don't suffer from clamping to simple precision floating point values: one matrix negates the other. The product has small values, and it'll be multiplied by vertex coordinates that have small values too, due to their previous shifting toward zero. All put together, this solves our floating origin issue.