---
# System prepended metadata

title: AR Foundation 即時光影追蹤
tags: [AR Foundation]

---

---
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;
        }
    }
}

```
