---
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」即可。

ARCameraManager 組件需要將 Light Estimation 設定 Everything。

程式碼邏輯很簡單,就是直接抓 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;
}
}
}
```