1_平面检测

一、平面检测设置

1. 新建场景,场景中加入【AR Session Origin】和【AR Session】;

2. 【AR Session Origin】挂在【ARPlaneManager】组件;

3. 创建平面检测预制体;

4. 将创建的平面检测预制体置于【ARPlaneManager】组件中;

5. 打包,发布即可;

二、个性化渲染

1. 将【AR Default Plane】中的【LineRender】组件移除;

2. 在【AR Default Plane】上增加【ARFeatheredPlaneMeshVisualizer】组件和【FeatheredPlaneShader】材质球;

using System.Collections.Generic;
using UnityEngine;
using UnityEngine.XR.ARFoundation;

/// <summary>
/// 创建人:
/// 创建时间:2022-2-11
/// 功能描述:平面可视化工具演示了在被检测平面的边缘使用羽化效果,它减少了硬边的视觉印象
/// </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>
    /// 纹理羽化的宽度(世界单位)
    /// </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>
    /// 生成uv2来标记边界顶点和羽状UV坐标  
    /// </summary>
    void GenerateBoundaryUVs(Mesh mesh)
    {
        int vertexCount = mesh.vertexCount;

        // 重用uv列表
        s_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;

        // 假设最后一个顶点是中心顶点
        for (int i = 0; i < vertexCount - 1; i++)
        {
            float vertexDist = Vector3.Distance(s_Vertices[i], centerInPlaneSpace);

            // 重新映射UV,使“1”的UV标记羽化边界  
            // featherBoundaryDistance/edgeDistance的比值与featherUV/edgeUV相同
            // 重新排列得到边缘UV
            float uvMapping = vertexDist / Mathf.Max(vertexDist - featheringWidth, 0.001f);
            uv.x = uvMapping;

            // 所有的UV映射都是不同的。 在着色器中,我们需要知道我们需要淡出的UV值  
            // 选择最短的UV,以确保我们在边界前淡出  
            // 这意味着羽化宽度将略有不同,我们再次依赖于一个相当均匀的平面
            if (shortestUVMapping > uvMapping) { shortestUVMapping = uvMapping; }

            s_FeatheringUVs.Add(uv);
        }

        m_FeatheredPlaneMaterial.SetFloat("_ShortestUVMapping", shortestUVMapping);

        // 添加中心顶点UV
        uv.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;
}
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 100
		Blend SrcAlpha OneMinusSrcAlpha
		ZWrite Off

		Pass
		{
			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
		}
	}
}

 3. 在【AR Default Plane】上的材质球中替换自己想要显示的图片即可;

 三、开启/禁用平面检测、隐藏/显示平面

using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.SceneManagement;
using UnityEngine.UI;
using UnityEngine.XR.ARFoundation;

/// <summary>
/// 创建人:
/// 创建时间:2022-2-11
/// 功能描述:平面检测以及个性化渲染
/// </summary>
[RequireComponent(typeof(ARPlaneManager))]
public class CheckPlaneDetection : MonoBehaviour
{
    #region UI

    [Tooltip("显示/隐藏平面")]
    public Button showandhideBtn;

    [Tooltip("开启/禁用平面检测")]
    public Button openandcloseBtn;

    #endregion

    #region 管理器

    public ARPlaneManager mARPlaneManager;

    #endregion

    void Start()
    {
        mARPlaneManager.planesChanged += OnPlaneChanged;
        InitUI();
    }

    // 当行为被禁用或处于非活动状态时调用此函数
    private void OnDisable()
    {
        mARPlaneManager.planesChanged -= OnPlaneChanged;
    }

    /// <summary>
    /// 初始化UI
    /// </summary>
    private void InitUI()
    {
        openandcloseBtn.onClick.AddListener(TogglePlaneDetection);
        showandhideBtn.onClick.AddListener(TogglePlaneDisplay);
    }

    #region 开启/禁用平面检测
    /// <summary>
    /// 开启禁用平面检测
    /// </summary>
    private void TogglePlaneDetection()
    {
        mARPlaneManager.enabled = !mARPlaneManager.enabled;
        if (mARPlaneManager.enabled)
        {
            openandcloseBtn.transform.Find("Text").GetComponent<Text>().text = "禁用平面检测";
            SetAllPlaneAction(true);
        }
        else
        {
            openandcloseBtn.transform.Find("Text").GetComponent<Text>().text = "启用平面检测";
            SetAllPlaneAction(false);
        }
    }

    /// <summary>
    /// 设置已检测到的平面
    /// </summary>
    private void SetAllPlaneAction(bool value)
    {
        foreach (var item in mARPlaneManager.trackables)
        {
            item.gameObject.SetActive(value);
        }
    }
    #endregion

    #region 显示/隐藏平面
    [Tooltip("存储已检测的平面")]
    private List<ARPlane> planeList = new List<ARPlane>();

    [Tooltip("已检测平面的显隐")]
    private bool isShow = true;

    /// <summary>
    /// 显示/隐藏检测的平面
    /// </summary>
    private void TogglePlaneDisplay()
    {
        if (isShow)
        {
            showandhideBtn.transform.Find("Text").GetComponent<Text>().text = "隐藏平面";
        }
        else
        {
            showandhideBtn.transform.Find("Text").GetComponent<Text>().text = "显示平面";
        }

        for (int i = planeList.Count - 1; i >= 0; i--)
        {
            if (planeList[i] == null || planeList[i].gameObject == null)
            {
                planeList.Remove(planeList[i]);
            }
            else
            {
                planeList[i].gameObject.SetActive(false);
            }
        }
        isShow = !isShow;
    }
    /// <summary>
    /// 平面检测事件回调(增加、删除、更新)
    /// </summary>
    /// <param name="obj"></param>
    private void OnPlaneChanged(ARPlanesChangedEventArgs obj)
    {
        for (int i = 0; i < obj.added.Count; i++)
        {
            planeList.Add(obj.added[i]);
            obj.added[i].gameObject.SetActive(isShow);
        }
    }
    #endregion
}

四、平面检测分类

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.XR.ARFoundation;
using UnityEngine.XR.ARSubsystems;

/// <summary>
/// 创建人:
/// 创建时间:2022-2-11
/// 功能描述:平面检测分类
/// </summary>
[RequireComponent(typeof(ARPlane))]
[RequireComponent(typeof(MeshRenderer))]
public class PlaneClassificationLabeler : MonoBehaviour
{
    ARPlane m_ARPlane;
    MeshRenderer m_PlaneMeshRenderer;
    TextMesh m_TextMesh;
    GameObject m_TextObj;
    Vector3 m_TextFlipVec = new Vector3(0, 180, 0);

    void Awake()
    {
        m_ARPlane = GetComponent<ARPlane>();
        m_PlaneMeshRenderer = GetComponent<MeshRenderer>();

        m_TextObj = new GameObject();
        m_TextMesh = m_TextObj.AddComponent<TextMesh>();
        m_TextMesh.characterSize = 0.05f;
        m_TextMesh.color = Color.black;
    }

    void Update()
    {
        UpdateLabel();
        UpdatePlaneColor();
    }

    void UpdateLabel()
    {
        m_TextMesh.text = m_ARPlane.classification.ToString();

        m_TextObj.transform.position = m_ARPlane.center;
        m_TextObj.transform.LookAt(Camera.main.transform);
        m_TextObj.transform.Rotate(m_TextFlipVec);
    }

    void UpdatePlaneColor()
    {
        Color planeMatColor = Color.cyan;

        switch (m_ARPlane.classification)
        {
            case PlaneClassification.None:
                planeMatColor = Color.cyan;        
                break;
            case PlaneClassification.Wall:
                planeMatColor = Color.white;        
                break;
            case PlaneClassification.Floor:
                planeMatColor = Color.green;        
                break;
            case PlaneClassification.Ceiling:
                planeMatColor = Color.blue;        
                break;
            case PlaneClassification.Table:
                planeMatColor = Color.yellow;        
                break;
            case PlaneClassification.Seat:
                planeMatColor = Color.magenta;        
                break;
            case PlaneClassification.Door:
                planeMatColor = Color.red;        
                break;
            case PlaneClassification.Window:
                planeMatColor = Color.clear;        
                break;
        }

        planeMatColor.a = 0.33f;                
        m_PlaneMeshRenderer.material.color = planeMatColor;
    }

    void OnDestroy()
    {
        Destroy(m_TextObj);
    }
}

  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值