一、效果演示
1、PICO4 Ultra MR 發光的球
2、AR實時光照
二、實現原理
? ? ? ? PICO4 Ultra MR開發時,通過空間網格能力掃描周圍環境,然后將掃描到的環境網格材質替換為一個透明材質并停止掃描;基于Google ARCore XR Plugin和ARFoundation進行安卓手機的AR開發,通過掃描周圍環境進行平面識別,然后將掃描到的平面的材質替換為一個透明材質并停止掃描。此處透明材質可以接收光照和陰影。
三、實現過程
? ? ? ? 本教程使用Unity版本為2022.3LTS,渲染管線為URP,Built-In同樣可用。
1、創建透明材質
? ? ? ? 透明材質著色器來源于Meta的MRUK Sample,點擊此處進入其Github頁。
? ? ? ? 克隆項目至本地,用Unity國際版本打開項目,需要復制著色器及三個依賴文件至計算機本地路徑下。
(1)著色器資源準備
? ? ? ? 找到下圖所示Packages路徑下的著色器,右鍵在資源管理器中打開,復制至本地文件夾下。
? ? ? ? 找到下圖所示Packages路徑下的EnvironmentDepth文件夾,右鍵在資源管理器中打開,將EnvironmentOcclusion.cginc、URP中的EnvironmentOcclusionURP.hlsl和BiRP中的EnvironmentOcclusionBiRP.cginc復制至本地文件夾下。
? ? ? ? 資源復制完成明細如下圖所示。
(2)項目導入著色器
? ? ? ? 將復制出的著色器及其依賴資源文件拖動至Unity項目合適路徑下。此時,著色器會報錯。
? ? ? ? 此報錯為資源引用路徑所致,雙擊打開HighlightsAndShadows著色器腳本進行修改。
? ? ? ? 1)找到以下引用:
? ? ? ? 修改為:
#include "EnvironmentOcclusionURP.hlsl" //occl
? ? ? ? 2)找到以下引用:
? ? ? ? 修改為:
#include "EnvironmentOcclusionBiRP.cginc" //occl
注意:此引用有兩處,都需修改
? ? ? ? 修改完著色器腳本后,其依賴文件也會因資源引用路徑而報錯。
? ? ? ? 雙擊打開EnvironmentOcclusionBiRP文件進行修改。
? ? ? ? 找到以下引用:
? ? ? ? 修改為:
#include "EnvironmentOcclusion.cginc"
? ? ? ? 雙擊打開EnvironmentOcclusionURP文件進行修改。
? ? ? ? 找到以下引用:
? ? ? ? 修改為:
#include "EnvironmentOcclusion.cginc"
(3)創建材質
? ? ? ? 按照以上修改完相關文件后,便可以使用HighlightsAndShadows著色器。有兩種方式可以將此著色器應用于項目材質。
1)通過著色器直接創建材質
? ? ? ? 選中HighlightsAndShadows著色器,鼠標右鍵-->Create--> Material。
2)材質引用著色器
? ? ? ? 選中項目材質,在Shader設置中,選擇Meta/MRUK/Scene/HighlightsAndShadows。
(4)材質效果
? ? ? ? 可以將材質應用于Plane物體,查看光影效果。
2、PICO4 Ultra MR 發光的球實現
? ? ? ? 此處需要使用空間網格,具體配置不再詳述,參見:PICO4 Ultra MR開發 空間網格掃描 模型導出及預覽或者PICO官網空間網格
(1)材質替換及停止掃描
????????需要在掃描完成后,將網格材質替換為HighlightsAndShadows材質。首先需要找到所有網格對象,掃描生成的網格一般會作為掛載PXR_SpatialMeshManager腳本的物體的子物體,所以只需以此查找網格對象即可。具體代碼如下:
public Transform spatialMeshParent; //空間網格生成的父物體,即掛載PXR_SpatialMeshManager腳本的物體
public Material meshMaterial; //接收光照和陰影的材質private MeshRenderer[] spatialMeshes;//空間網格上的MeshRenderer數組
private XRMeshSubsystem meshSubsystem;//網格子系統void Start()
{meshSubsystem = XRGeneralSettings.Instance.Manager.ActiveLoaderAs<PXR_Loader>().meshSubsystem;
}void Update()
{if (Controller.UXR_GetKeyDown(0, XR_KeyCode.APP)){spatialMeshes = spatialMeshParent.GetComponentsInChildren<MeshRenderer>();foreach(MeshRenderer renderer in spatialMeshes){renderer.material = meshMaterial;}//停止掃描網格meshSubsystem.Stop();}
}
(2)燈光設置
? ? ? ? 創建一個點光源作為小球的子物體,并置于小球中心處。
(3)小球彈跳
? ? ? ? 若使小球不會穿過墻面并可以在地面上彈跳,需要給網格預制件添加MeshCollider組件并賦予物理材質,物理材質設置合適彈性參數;小球上則需添加SpereCollider組件和Rigidbody組件,同樣設置物理材質。
3、AR實時光照實現
? ? ? ? 基于ARFoundation進行手機AR開發,若為安卓手機,額外導入Google ARCore XR Plugin,若為蘋果手機,額外導入Apple ARKit?XR Plugin。
(1)AR環境搭建
? ? ? ? 創建XR Origin,并刪除場景里的MainCamera物體。
? ? ? ? 按照下圖所示設置并添加組件。
? ? ? ? 其中,ARPlaneManager的PlanePrefab需要手動創建并制作為預制件,賦予此處。
? ? ? ? 創建ARSession。
(2)材質替換及停止掃描
? ? ? ? 完整代碼如下。
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.XR.ARFoundation;
using UnityEngine.XR.ARSubsystems;public class ARDanceManager : MonoBehaviour
{public Material transparentMat; //接收光照和陰影的材質public GameObject dancerPrefab; //虛擬人物預制件public ARRaycastManager raycastManager; //射線碰撞檢測器public ARPlaneManager m_ARPlaneManager; //平面生成器private List<ARRaycastHit> hits;private GameObject dancerObject = null;private bool hasSpawned = false;// Start is called before the first frame updatevoid Start(){hits = new List<ARRaycastHit>();}// Update is called once per framevoid Update(){if (dancerObject == null){if (Input.touchCount == 0)return;}if (!hasSpawned){var touch = Input.GetTouch(0);if (raycastManager.Raycast(touch.position, hits, TrackableType.PlaneWithinPolygon | TrackableType.PlaneWithinBounds)){var hitPose = hits[0].pose;if (dancerObject == null)//沒有虛擬物體則生成{dancerObject = Instantiate(dancerPrefab, hitPose.position, /*hitPose.rotation*/dancerPrefab.transform.rotation);dancerObject.name = "Dancer";m_ARPlaneManager.enabled = false;SetAllPlanesTransparent();}else //有虛擬物體將虛擬物體錨定到射線碰撞點處{dancerObject.transform.position = hitPose.position;}}hasSpawned = true;}}//設置渲染的平面為透明void SetAllPlanesTransparent(){foreach (var plane in m_ARPlaneManager.trackables){plane.gameObject.GetComponent<MeshRenderer>().material = transparentMat;plane.gameObject.GetComponent<LineRenderer>().material = transparentMat;}}
}
? ? ? ? 以上腳本掛載于場景物體,賦值相應變量即可。
四、視頻教程
? ? ? ? 視頻教程參見: