Unity开放大世界坐标精度导致抖动的解决方案

做开放大世界的小伙伴肯定都被过大的世界坐标导致的抖动问题头疼。过大的世界坐标导致浮点精度无法准确的表示位置,在表现上看就是物体的抖动和物体之间的穿插问题。要解决这个问题,首先就要分析问题是怎么来的。

我们的位置信息是在CPU以Vector3的形式存储的,里面全部是float类型的值。float类型在CPU有23位尾数,而过大的坐标在CPU已经导致精度没有小坐标高,因为大坐标占用了更多的尾数在小数点前的数字上。而在GPU,对于OpenGL ES的API会被编译成highp类型,highp类型的位数具体多少位是不确定的,根据各个厂商的实现各有不同。很多都是24位浮点,这就导致了再一次浮点精度丢失。再者当过大的坐标和过小的坐标计算时,比如localToWorldMatrix矩阵变换到世界坐标时,会有一次本地坐标和矩阵第四列的加法,float类型大值+小值是常见的导致浮点误差变大的原因。综合以上几个因素,发现比较适合使用相机相对渲染的方案。

什么是相机相对渲染?

就是把相机当做世界坐标原点,所有的计算相对于相机的位置,这样就能把坐标大小控制在相机相对坐标系下,而相机周围的物体正好是我们容易看到的,远处的在相机相对坐标系的坐标很大的位置也不会被相机看到。这样,我们就从大值的float计算变成了小值的计算。

怎么实现呢?

主要思路是现在CPU算出localToWorld矩阵,但是这个world指的是相机相对坐标系的world,即矩阵的第四列是在CPU用高精度的浮点减法减去相机的坐标。将这个矩阵传递到GPU进行变换,变换出来的结果不是世界坐标,而是相机相对坐标(positionRS),然后将这个相对坐标变换到相机空间,最后按原来的方式变换到投影空间就完成了。

相机相对渲染是怎么做到减小浮点误差的?

首先采用相机相对坐标缩小了浮点的值的大小,使得小数点后的有效位数更多,精度更高。其次是避免了在GPU直接做小坐标(localpos)加大坐标的操作,而是变成了小坐标(localpos)+相机相对坐标(值比较小)。然后在CPU计算相机相对坐标的时候采用了double类型,更高精度的减法,最后再转成float,最大程度降低了浮点的误差。

实现细节:

首先需要在想要支持的相机相对渲染的Shader中定义自己的localToWorld矩阵,然后在每一个Renderer上挂脚本每帧传给GPU计算完成的矩阵。计算方法很简单,就是取到transform的localToWorldMatrix矩阵,然后将第四列的前三个元素减去相机坐标就OK。

     _localToWorldMatrix = GetTransform().localToWorldMatrix;
        _localToWorldMatrix.m03 =(float)(_localToWorldMatrix.m03 - (double)camera.transform.position.x);
        _localToWorldMatrix.m13 = (float)(_localToWorldMatrix.m13 - (double)camera.transform.position.y);
        _localToWorldMatrix.m23 =(float)(_localToWorldMatrix.m23 - (double)camera.transform.position.z);

然后就是传到GPU,先变换到相机相对坐标系,然后变换到相机坐标。如何变换到相机坐标系也很简单:

inline float3 CRR_GetViewPosition(float4 positionRS)
{
    return float3(dot(positionRS,UNITY_MATRIX_V[0].xyz),dot(positionRS,UNITY_MATRIX_V[1].xyz),dot(positionRS,UNITY_MATRIX_V[2].xyz));
}

最后变换到投影空间:

//相机相对空间
    float4 positionRS = CRR_GetWorldPositionRelative(positionOS);
    //正确的相机空间
    float3 positionVS = CRR_GetViewPosition(positionRS);
    //裁减空间
    return mul(UNITY_MATRIX_P,float4(positionVS,1));

如果在Shader中要支持世界坐标的访问,只需要将相机相对坐标+相机坐标的位置即可。

本文思路主要参考Unity HDRP Camera-Relative Render的文档。有兴趣的小伙伴可以去看看。HDRP默认支持相机相对渲染,所以如果用HDRP的小伙伴就不用为这个困扰了。

posted @ 2020-10-19 14:38  syb7384  阅读(5239)  评论(6编辑  收藏  举报