--- tags: AR Foundation --- # AR Foundation 即時光影追蹤 參考網站 https://tutorialsforar.com/using-light-estimation-in-ar-using-arkit-and-arcore-with-unity/ 如果只是把虛擬物件放到攝影機畫面中,虛擬物件缺少陰影並且反光不符合現實環境的光源,使用者會認為物件不夠真實(但要做到完全相同,不太可能,等技術發展吧)。我們可以使用 AR Foundation 的 Light Estimation API 來對現場光進行光照估計,進行即時光影追蹤,以現實世界的光照資訊改變光源的照射。 原理是用手機的感知裝置來感應環境光源,透過光線估算來做虛擬物件的渲染。以下程式範例適用於 iOS 和 Android,可以追蹤的項目如下: * 亮度 – 我們物理環境的估計亮度 * 色溫 * 顏色校正 * 主光源方向 * 淺色 * 以流明為單位的主光強度 * 球面諧波 – 基本上是不同照明參數和係數的數據表示 總之不用管這些東西真好,只需要在 Unity 中使用直線光物件(Light -> Directional Light)即可,請在直線光物件中附加以下 LightEstimation 程式碼,並且將「AR Session Origin」 的「AR Camera」物件拖進 LightEstimation 的「AR Camera Manager」即可。 ![](https://i.imgur.com/pV4sZTm.png) ARCameraManager 組件需要將 Light Estimation 設定 Everything。 ![](https://i.imgur.com/TZuR8RJ.png) 程式碼邏輯很簡單,就是直接抓 ARCameraManager 環境光資訊,直接去修改直線光物件的數值,講實在話這不能做到完美的效果,但已經很不錯了。 ## LightEstimation ```csharp= using UnityEngine; using UnityEngine.Rendering; using UnityEngine.XR.ARFoundation; [RequireComponent(typeof(Light))] public class LightEstimation : MonoBehaviour { [SerializeField] private ARCameraManager arCameraManager; Light mainLight; /// <summary> /// The estimated brightness of the physical environment, if available. /// </summary> public float? brightness { get; private set; } /// <summary> /// The estimated color temperature of the physical environment, if available. /// </summary> public float? colorTemperature { get; private set; } /// <summary> /// The estimated color correction value of the physical environment, if available. /// </summary> public Color? colorCorrection { get; private set; } /// <summary> /// The estimated direction of the main light of the physical environment, if available. /// </summary> public Vector3? mainLightDirection { get; private set; } /// <summary> /// The estimated color of the main light of the physical environment, if available. /// </summary> public Color? mainLightColor { get; private set; } /// <summary> /// The estimated intensity in lumens of main light of the physical environment, if available. /// </summary> public float? mainLightIntensityLumens { get; private set; } /// <summary> /// The estimated spherical harmonics coefficients of the physical environment, if available. /// </summary> public SphericalHarmonicsL2? sphericalHarmonics { get; private set; } void Awake() { mainLight = GetComponent<Light>(); } void OnEnable() { if (arCameraManager != null) arCameraManager.frameReceived += FrameChanged; } void OnDisable() { if (arCameraManager != null) arCameraManager.frameReceived -= FrameChanged; } void FrameChanged(ARCameraFrameEventArgs args) { if (args.lightEstimation.averageBrightness.HasValue) { brightness = args.lightEstimation.averageBrightness.Value; mainLight.intensity = brightness.Value; } if (args.lightEstimation.averageColorTemperature.HasValue) { colorTemperature = args.lightEstimation.averageColorTemperature.Value; mainLight.colorTemperature = colorTemperature.Value; } if (args.lightEstimation.colorCorrection.HasValue) { colorCorrection = args.lightEstimation.colorCorrection.Value; mainLight.color = colorCorrection.Value; } if (args.lightEstimation.mainLightDirection.HasValue) { mainLightDirection = args.lightEstimation.mainLightDirection; mainLight.transform.rotation = Quaternion.LookRotation(mainLightDirection.Value); } if (args.lightEstimation.mainLightColor.HasValue) { mainLightColor = args.lightEstimation.mainLightColor; #if PLATFORM_ANDROID // ARCore needs to apply energy conservation term (1 / PI) and be placed in gamma mainLight.color = mainLightColor.Value / Mathf.PI; mainLight.color = mainLight.color.gamma; // ARCore returns color in HDR format (can be represented as FP16 and have values above 1.0) var camera = arCameraManager.GetComponentInParent<Camera>(); if (camera == null || !camera.allowHDR) { Debug.LogWarning($"HDR Rendering is not allowed. Color values returned could be above the maximum representable value."); } #endif } if (args.lightEstimation.mainLightIntensityLumens.HasValue) { mainLightIntensityLumens = args.lightEstimation.mainLightIntensityLumens; mainLight.intensity = args.lightEstimation.averageMainLightBrightness.Value; } if (args.lightEstimation.ambientSphericalHarmonics.HasValue) { sphericalHarmonics = args.lightEstimation.ambientSphericalHarmonics; RenderSettings.ambientMode = AmbientMode.Skybox; RenderSettings.ambientProbe = sphericalHarmonics.Value; } } } ```