EasyAR4.0开发 (SpatialMap空间地图三)

这篇文章主要针对SpatialMap_Dense_BallGame场景讲一下DenseSpatialMapBuilderFrameFilter 和 DenseSpatialMapDepthRenderer 这两个脚本。

一、场景分析

EasyAR4.0的所有功能都模块化了,SpatialMap_Dense_BallGame这个场景中

绿色框的物体上面挂载的脚本就是实现稠密空间的脚本了。其他三个物体是属于基础脚本。

Sample物体上的脚本是写了UI的控制,和小球的生成,这个代码很简单,所以就不说了。

二、DenseSpatialMapDepthRenderer脚本

这个脚本比较简单,主要功能就是记录相机的视口坐标系中的深度信息,然后转换成一张texture,把这种图片传递到场景中自动生成的环境Mesh的材质中。

相机是用的是session.Assembly.Camera也就是Main Camera,

 if (!RenderDepthCamera || !MapMeshMaterial)
            {
                return;
            }
            if (depthTexture && (depthTexture.width != Screen.width || depthTexture.height != Screen.height))
            {
                Destroy(depthTexture);
            }
            if (!depthTexture)
            {
                depthTexture = new RenderTexture(Screen.width, Screen.height, 24);
                MapMeshMaterial.SetTexture("_DepthTexture", depthTexture);
            }
            RenderDepthCamera.targetTexture = depthTexture;
            RenderDepthCamera.RenderWithShader(Shader, "Tag");
            RenderDepthCamera.targetTexture = null;

代码主要就是通过一个特殊shader 渲染相机,生成一个可以说是深度图的图片,然后传递给材质。

  v2f vert(appdata_base v)
            {
                v2f o;
                o.depthPos.xyz = UnityObjectToViewPos(v.vertex);
                o.depthPos.w = 1.0;
                o.pos = mul(UNITY_MATRIX_P, o.depthPos);
                return o;
            }

            float4 frag(v2f i) : SV_Target {
                float depth = length(i.depthPos.xyz);
                depth = depth * 0.000001;
                return EncodeFloatRGBA(depth);
            }

这是shader主要部分,这里使用的是length当作深度数据,因为一个画面中xy相同的情况下,length的大小也代表了深度。

然后把这个数据*0.000001,因为EncodeFloatRGBA范围0-1.。


                if (length(i.viewPos.xyz) * 0.000001 - depth >= 0.005 * 0.000001)
                {
                    discard;
                }

这是环境Mesh的Shader,会剔除深度远的mesh。这么做可能是因为本身环境Mesh是透明的,如果不剔除,可能会有很多层渲染出来。

三、DenseSpatialMapBuilderFrameFilter脚本

讲这个脚本,先讲几个其他类。

DenseSpatialMap  这个类用来对环境进行精确的三维稠密重建,就是这个类其实是主要的计算控制类

DenseSpatialMapBlockController  这个类是管理每一个生成的Mesh的,用做对相应Mesh的更新

BlockInfo 这是一个结构体,主要保存了一个Mesh的更新记录 其中x,y,z组成了类似id ,version是记录更新的次数

首先看类中的Awake函数:我这里删除了一些不重要的代码

 protected virtual void Awake()
        {
            if (!EasyARController.Initialized)  //是否初始化
            {
                return;
            }
            if (!DenseSpatialMap.isAvailable())  //是否支持稠密重建
            {
                throw new UIPopupException(typeof(DenseSpatialMap) + " not available");
            }
            Builder = DenseSpatialMap.create();  //创建 DenseSpatialMap 对象
        }

然后稠密空间计算的控制可以有下面几个函数:

Builder.start(); //开始重建或从暂停中恢复,继续重建
Builder.stop();   //暂停重建过程。调用start来继续重建过程。
Builder.Dispose();  //这个api中没解释,应该是释放资源,但是不关闭重建
Builder.close();   //关闭重建过程。close之后不应继续使用。
     public InputFrameSink InputFrameSink()
        {
            if (Builder != null)
            {
                return Builder.inputFrameSink();
            }
            return null;
        }

这个函数主要是实现 FrameFilter.IInputFrameSink这个接口的,这个接口应该是在获取到数据后,会回调。因此这里在数据更新后,需要更新Builder。

 protected virtual void Update()
        {
            if (dirtyBlocks.Count <= 0)   //dirtyBlocks是一个保存需要更新的mesh的集合
            {
                if (Builder.updateSceneMesh(false)) //这里会让重新计算mesh
                {
                    using (var mesh = Builder.getMesh())  //获取计算后的BlockInfo集合
                    {
                        foreach (var blockInfo in mesh.getBlocksInfoIncremental()) //遍历 
                                                                    //所有的blockInfo 
                        {
                            DenseSpatialMapBlockController oldBlock;
                            blocksDict.TryGetValue(new Vector3(blockInfo.x, blockInfo.y, blockInfo.z), out oldBlock);                        //查看所有生成的环境mesh中有没有这个id
                            if (oldBlock == null)       //没有的话就生成Mesh
                            {
                                var go = new GameObject("MeshBlock");
                                go.AddComponent<MeshCollider>();
                                go.AddComponent<MeshFilter>();
                                var renderer = go.AddComponent<MeshRenderer>();
                                renderer.material = mapMaterial;
                                renderer.enabled = RenderMesh;
                                var block = go.AddComponent<DenseSpatialMapBlockController>();
                                block.UpdateData(blockInfo, mesh);
                                go.transform.parent = mapRoot.transform;
                                blocksDict.Add(new Vector3(blockInfo.x, blockInfo.y, blockInfo.z), block);
                                dirtyBlocks.Add(block);
                                if (MapCreate != null)
                                {  //这里是Action的回调,用户可以监听地图生成
                                    MapCreate(block);
                                }
                            }
                            else if (oldBlock.Info.version != blockInfo.version) 
                            {
                                oldBlock.UpdateData(blockInfo, mesh); //更新mesh的数据
                                if (!dirtyBlocks.Contains(oldBlock))
                                {
                                    dirtyBlocks.Add(oldBlock);  //将这个mesh放到需要更新的 
                                                                 //集合
                                }
                            }
                        }
                    }
                }
            }
            else  //这里每次刷新数据的数量是可以设置的,默认是5个
            {
                var count = Math.Min(dirtyBlocks.Count, BlockUpdateLimitation);
                var blocks = dirtyBlocks.GetRange(0, count);
                foreach (var block in blocks)  //每次最多刷新5个mesh
                {
                    block.UpdateMesh();       //更新mesh ,更新数据不代表mesh更新了
                }  
                dirtyBlocks.RemoveRange(0, count);   //移出需要更新的列表
                if (MapUpdate != null)
                {
                    MapUpdate(blocks);   //这里是Action的回调,用户可以监听地图更新
                }
            }
        }

稠密空间构建的基本脚本就是这样了。

有问题可以加入qq群:

发布了34 篇原创文章 · 获赞 2 · 访问量 3310

猜你喜欢

转载自blog.csdn.net/BDDNH/article/details/104677451