目前基于Unity2021.3版本发现的问题是3D的UI虽然空间上位于三维的位置,但是UI之间的渲染的遮挡关系不是按照距离摄像机的远近进行的,导致距离摄像机远的UI有可能遮挡距离摄像机近的UI。由于渲染按照子物体排序进行的,即子物体编号靠后的后渲染,而渲染时又不考虑深度,所以就出现了上述问题,我尝试给UI使用自定义ShaderGraph的Material,其中的ShaderGraph里面使用了UnlitSprite类型,或者使用Unlit类型的同时强制写入深度,并在测试深度中选择了Less Equal。然而一通折腾下来似乎并没有什么作用。不过我还是觉得在Shader方面下手应该是比较好的解决方法,亦或许应该配合渲染管线做一些什么工作,但是目前对渲染管线的理解和开发依旧处于幼稚阶段,所以还搞不定,有高人愿意不吝赐教可以留言,这里先行谢过。
目前的解决办法是给UI按照距离摄像机的远近进行排序。这里面做了两个优化,一个是用距离的平方代替距离进行比较,另一个是通过点乘运算测试UI是否位于摄像机之前,只有在摄像机前面的UI才排序,位于摄像机后面的不考虑。
基于上面的想法,给需要培训的UI的组件添加了sqrtDistToCam浮点属性和inFrontOfCam布尔属性,代码参考如下:
//这个ZJNumberPlate列表里面保存需要排序的UI的组件,就是在摄像机前面的UI组件。//虽然在摄像机的前面也未必在摄像机的视锥体里面,但是检查是否在摄像机视锥体里面这个以后考虑。
List<ZJNumberPlate> listPlateForOrder = new List<ZJNumberPlate>();
voidUpdatePlanteInsSilder()
{
//需要排序的组件位于numberPlateIns数组里面,其类型是ZJNumberPlate。//这个ZJNumberPlate组件里面包含了inFrontOfCam和sqrtDistToCam属性。
if (numberPlateIns == null || numberPlateIns.Length == 0) return;
//
listPlateForOrder.Clear();
Transform tranCam = Camera.main.transform;
for (int i = 0; i < numberPlateIns.Length; i++)
{
//首先通过UpdateParamToCam方法更新了inFrontOfCam和sqrtDistToCam属性的值。
numberPlateIns[i].UpdateParamToCam(tranCam);
//不在摄像机前面的直接忽略。
if (!numberPlateIns[i].inFrontOfCam) continue;
//通过while循环找到UI插入到ZJNumberPlate列表里面的位置。
int j = 0;
while (j < listPlateForOrder.Count)
{
if (numberPlateIns[i].sqrtDistToCam < listPlateForOrder[j].sqrtDistToCam)
{
j++;
continue;
}
break;
}
//在找到的位置将UI插入到ZJNumberPlate列表里面。
listPlateForOrder.Insert(j, numberPlateIns[i]);
}
//凡是在ZJNumberPlate列表里面的UI组件都按照由远及近的顺序排在兄弟关系前面。
for(int i = 0; i < listPlateForOrder.Count; i++)
{
listPlateForOrder[i].rectTran.SetSiblingIndex(i);
}
}