平面检测是很多AR应用的基础,无论是ARKit还是ARCore,都提供平面检测功能。同时,平面也是可跟踪对象,在前几节中我们知道,ARFoundation使用ARPlaneManager管理器来管理平面。

(一)平面检测管理

  AR中检测平面的原理:ARFoundation对摄像机获取的图像进行处理,分离图像中的特征点(这些特征点往往都是图像中明暗、强弱、颜色变化较大的点),利用VIO和IMU跟踪这些特征点的三维空间信息,在跟踪过程中,对特征点信息进行处理,并尝试用空间中位置相近或者符合一定规律的特征点构建平面,如果成功就是检测出了平面。平面有其位置、方向和边界信息,ARPlaneManager负责如何检测平面以及管理这些检测出来的平面,但它并不负责渲染平面。

  在ARPlaneManager中,我们可以设置平面检测的方式,如水平平面(Horizontal)、垂直平面(Vertical)、水平平面&垂直平面(Everything)或者不检测平面(Nothing),因为检测平面也是一个消耗性能的工作,所以根据应用需要选择合适的检测方式可以优化应用性能,如下图所示。


  ARPlaneManager每帧都会进行平面检测,会添加新检测到的平面、更新现有平面、移除过时的平面。当一个新的平面被检测到时,ARPlaneManager会实例化一个平面Prefab来表示该平面,如果开发中ARPlaneManager的Plane Prefab属性没有对赋值,ARPlaneManager将会实例化一个空对象,并在这个空对象上挂载ARPlane组件,ARPlane组件包含了该平面的相关信息数据。

  ARPlaneManager组件还有一个planesChanged事件,开发人员可以注册这个事件,以便在平面发生改变时进行相应处理。

(二)可视化平面

  在ARFoundation中,ARPlaneManager并不负责平面的可视化渲染,而由其Plane Prefab属性指定的预制体负责。在前文中,我们新建了一个AR Default Plane对象作为预制体,该预制体上挂载了如下图所示组件。


  AR Plane组件负责该平面各类属性事宜,如是否在移除平面时销毁该实例化对象,控制可划分为同一平面的特征点的阀值(上图中红框属性,只有偏差在这个阀值内的特征点才可归属为同一平面,这影响平面检测。);AR Plane Mesh Visualizer该组件主要是从边界特征点和其他特征点三角化生成一个平面网格,有这个平面网格后自然就可以使用Mesh Renderer采用合适材质渲染出来了;默认平面预制体还有一个Line Renderer,它负责渲染平面可视化后的边界连线。所以使用默认平面预制体可视已检测到的平面如下图所示。

(三)个性化可视平面

  对已检测到的平面默认的可视化显得有些生硬和突兀,有时我们需要更加友好的界面,这时我们就需要对已检测到的平面定制我们自己个性的可视方案。

  为达到更好的视觉效果,处理的思路如下:
  1)、不显示黑色边框;
  2)、重新制作一个渲染材质和Shader脚本,纹理我们使用半透明的PNG,Shader脚本渲染这个半透明的纹理,将纹理空白区域都镂空;
  3)、编写一个渐隐的脚本,让边缘的纹理渐隐,达到更好的视觉过渡。

  按照以上思路,我们直接对AR Default Plane预制体进行改造。
  1)、删除AR Default Plane预制体上的Line Renderer组件;
  2)、编写如下所示Shader,制作一张PNG半透明纹理,并新建一个利用这个Shader的材质。

Shader "Unlit/FeatheredPlaneShader"
{Properties{_MainTex ("Texture", 2D) = "white" {}_TexTintColor("Texture Tint Color", Color) = (1,1,1,1)_PlaneColor("Plane Color", Color) = (1,1,1,1)}SubShader{Tags { "RenderType"="Transparent" "Queue"="Transparent" }LOD 100Blend SrcAlpha OneMinusSrcAlphaZWrite OffPass{CGPROGRAM#pragma vertex vert#pragma fragment frag#include "UnityCG.cginc"struct appdata{float4 vertex : POSITION;float2 uv : TEXCOORD0;float3 uv2 : TEXCOORD1;};struct v2f{float4 vertex : SV_POSITION;float2 uv : TEXCOORD0;float3 uv2 : TEXCOORD1;};sampler2D _MainTex;float4 _MainTex_ST;fixed4 _TexTintColor;fixed4 _PlaneColor;float _ShortestUVMapping;v2f vert (appdata v){v2f o;o.vertex = UnityObjectToClipPos(v.vertex);o.uv = TRANSFORM_TEX(v.uv, _MainTex);o.uv2 = v.uv2;return o;}fixed4 frag (v2f i) : SV_Target{fixed4 col = tex2D(_MainTex, i.uv) * _TexTintColor;col = lerp( _PlaneColor, col, col.a);// Fade out from as we pass the edge.// uv2.x stores a mapped UV that will be "1" at the beginning of the feathering.// We fade until we reach at the edge of the shortest UV mapping.// This is the remmaped UV value at the vertex.// We choose the shorted one so that ll edges will fade out completely.// See ARFeatheredPlaneMeshVisualizer.cs for more details.col.a *=  1-smoothstep(1, _ShortestUVMapping, i.uv2.x);return col;}ENDCG}}
}

  在这个Shader中,有三个参数,Texture即为纹理,将我们制作的纹理赋给它,TextureTintColor为纹理显示,我们要让透明的十字星号显示出来,Alpha设置为220,PlaneColor为平面的背景色,这里我们不要背景色,Alpha设置为0,如下图所示。

  3)、新建一个C#脚本文件,命名为ARFeatheredPlaneMeshVisualizer.cs,编写如下代码:

using System.Collections.Generic;
using UnityEngine;
using UnityEngine.XR.ARFoundation;/// <summary>
/// This plane visualizer demonstrates the use of a feathering effect
/// at the edge of the detected plane, which reduces the visual impression
/// of a hard edge.
/// </summary>
[RequireComponent(typeof(ARPlaneMeshVisualizer), typeof(MeshRenderer), typeof(ARPlane))]
public class ARFeatheredPlaneMeshVisualizer : MonoBehaviour
{[Tooltip("The width of the texture feathering (in world units).")][SerializeField]float m_FeatheringWidth = 0.2f;/// <summary>/// The width of the texture feathering (in world units)./// </summary>public float featheringWidth{ get { return m_FeatheringWidth; }set { m_FeatheringWidth = value; } }void Awake(){m_PlaneMeshVisualizer = GetComponent<ARPlaneMeshVisualizer>();m_FeatheredPlaneMaterial = GetComponent<MeshRenderer>().material;m_Plane = GetComponent<ARPlane>();}void OnEnable(){m_Plane.boundaryChanged += ARPlane_boundaryUpdated;}void OnDisable(){m_Plane.boundaryChanged -= ARPlane_boundaryUpdated;}void ARPlane_boundaryUpdated(ARPlaneBoundaryChangedEventArgs eventArgs){GenerateBoundaryUVs(m_PlaneMeshVisualizer.mesh);}/// <summary>/// Generate UV2s to mark the boundary vertices and feathering UV coords./// </summary>/// <remarks>/// The <c>ARPlaneMeshVisualizer</c> has a <c>meshUpdated</c> event that can be used to modify the generated/// mesh. In this case we'll add UV2s to mark the boundary vertices./// This technique avoids having to generate extra vertices for the boundary. It works best when the plane is /// is fairly uniform./// </remarks>/// <param name="mesh">The <c>Mesh</c> generated by <c>ARPlaneMeshVisualizer</c></param>void GenerateBoundaryUVs(Mesh mesh){int vertexCount = mesh.vertexCount;// Reuse the list of UVss_FeatheringUVs.Clear();if (s_FeatheringUVs.Capacity < vertexCount) { s_FeatheringUVs.Capacity = vertexCount; }mesh.GetVertices(s_Vertices);Vector3 centerInPlaneSpace = s_Vertices[s_Vertices.Count - 1];Vector3 uv = new Vector3(0, 0, 0);float shortestUVMapping = float.MaxValue;// Assume the last vertex is the center vertex.for (int i = 0; i < vertexCount - 1; i++){float vertexDist = Vector3.Distance(s_Vertices[i], centerInPlaneSpace);// Remap the UV so that a UV of "1" marks the feathering boudary.// The ratio of featherBoundaryDistance/edgeDistance is the same as featherUV/edgeUV.// Rearrange to get the edge UV.float uvMapping = vertexDist / Mathf.Max(vertexDist - featheringWidth, 0.001f);uv.x = uvMapping;// All the UV mappings will be different. In the shader we need to know the UV value we need to fade out by.// Choose the shortest UV to guarentee we fade out before the border.// This means the feathering widths will be slightly different, we again rely on a fairly uniform plane.if (shortestUVMapping > uvMapping) { shortestUVMapping = uvMapping; }s_FeatheringUVs.Add(uv);}m_FeatheredPlaneMaterial.SetFloat("_ShortestUVMapping", shortestUVMapping);// Add the center vertex UVuv.Set(0, 0, 0);s_FeatheringUVs.Add(uv);mesh.SetUVs(1, s_FeatheringUVs);mesh.UploadMeshData(false);}static List<Vector3> s_FeatheringUVs = new List<Vector3>();static List<Vector3> s_Vertices = new List<Vector3>();ARPlaneMeshVisualizer m_PlaneMeshVisualizer;ARPlane m_Plane;Material m_FeatheredPlaneMaterial;
}

  将ARFeatheredPlaneMeshVisualizer挂载到AR Default Plane预制体上,完成之后应该如下图所示:

  编译运行,效果如下所示,视觉效果要好很多了。

  注:本文Shader与ARFeatheredPlaneMeshVisualizer.cs脚本均取自参考代码工程,为方便读者,我已将图片、Shader、脚本分离出来,读者也可以在 这里 下载,直接就可以在工程里使用。

参考代码

arfoundation-samples arfoundation-samples

ARFoundation之路-平面管理相关推荐

  1. Linux进阶之路————组管理与权限管理

    引言 这篇博客将会总结一些关于组的概念和相关操作,以及文件的一些权限管理. 一.组的基本介绍 在前面的博文<Linux进阶之路----用户管理>已经有提到用户组的概念,实际上,组的概念并不 ...

  2. ARFoundation之路-人脸检测增强之二

    版权声明:Davidwang原创文章,严禁用于任何商业途径,授权后方可转载.   前节所述使用"标准模型"匹配人脸以检测人脸姿态是众多人脸姿态检测方法中的一种,实际上,人脸姿态估计 ...

  3. IT经理的两条职业路做管理还是管理咨询

    转自http://g.51cto.com/job/26430 经理具有三个不同于传统行业经理的特点.这些特点把两条职业道路呈现在了他们面前.相比较传统行业,IT业的发展,比如技术和产品的更新速度,都数 ...

  4. ARFoundation之路-环境配置(iOS)之二

    版权声明:Davidwang原创文章,严禁用于任何商业途径,授权后方可转载. (一)AppController   在Project窗口Scripts文件夹下,空白处点击鼠标右键,在弹出的级联菜单中依 ...

  5. ARFoundation之路-视频播放

    版权声明:Davidwang原创文章,严禁用于任何商业途径,授权后方可转载.   在AR中播放视频也是一种常见的需求,如在一个展厅中放置的虚拟电视上播放宣传视频,或者在游戏中为营造氛围而设置的虚拟电视 ...

  6. 【转载】我的编程之路——知识管理与知识体系

    [https://segmentfault.com/a/1190000004612590] 本文的资料放到了Github Repo (本文介绍的这种笔记排布方式不一定适合于初学者理解) 六年前笔者开始 ...

  7. ARFoundation之路-AR阴影生成之一

    版权声明:Davidwang原创文章,严禁用于任何商业途径,授权后方可转载.   阴影在现实生活中扮演着非常重要的角色,通过阴影我们能直观的感受到光源位置.光源强弱.物体离地面高度. 物体轮廓等,在大 ...

  8. 大数据之路——计算管理

    十三.数据管理--计算管理 13.1 系统优化 13.1.1 HBO History-Based Optimizer 13.1.2 CBO Cost-Based Optimizer 13.2 任务优化 ...

  9. Linux进阶之路————用户管理

    引言 前面几篇关于Linux的已经大概领略了Linux的风采,本篇用户管理,将着重总结日常工作中,非常重要的用户管理功能. 主要包括:新增用户.删除用户.查询用户信息.指定/修改密码.切换用户.用户组 ...

最新文章

  1. 2.4G高频PCB天线设计
  2. 浏览器上网 (Safari Chrome)
  3. umask详解、cwd简介
  4. java打包内存溢出_maven build 内存溢出怎么解决?
  5. 【TensorFlow-windows】学习笔记一——基础理解
  6. 基于springboot+vue的前后端分离商城系统
  7. 万能的BERT连文本纠错也不放过
  8. hdu Caocao's Bridges(无向图边双连通分量,找出权值最小的桥)
  9. 1024 程序员节:给 DBA 们的福音
  10. with在python中啥意思,“with”语句在Python中做什么?
  11. Mock Serverj
  12. Matlab停在载入界面,试图在Matlab用户界面中实现保存/加载对象功能时遇到了困难...
  13. 先序、中序和后序数组两两结合重构二叉树 -- 图解
  14. mysql类exadata功能_查看Exadata的版本
  15. 前端常见浏览器兼容性问题解决方案
  16. CC00388.CloudKubernetes——|KuberNetesCI/CD.V26|——|Jenkins.v06|自动构建Java应用.v06|报错处理|
  17. 绝地求生服务器维护需要多久,绝地求生8月4日更新到几点?绝地求生维护一次需要多长时间?...
  18. 《磨菇书三四章整理》
  19. 几个实用的app和网站
  20. python常用re正则表达式大全,查找指定内容

热门文章

  1. 40篇最受网友欢迎的web前端HTML精选文章合集
  2. 孤立森林(隔离树)译文
  3. 图形验证码和短信验证码
  4. 延迟满足 —— 达到目标需要忍住重重诱惑
  5. 基于全卷积神经网络的前列腺磁共振图像分割
  6. 常用技巧精选(一)尺取法
  7. KONG网关和KONGA界面的入门使用,快速上手
  8. 计算机网络 第3章 作业1
  9. 09-单片机模块化程序: μCOS-II中内存管理程序使用说明
  10. java上溯造型与下溯造型