using UnityEngine; using UnityEngine.Rendering; using UnityEngine.XR.ARFoundation; /// /// A component that can be used to access the most /// recently received light estimation information /// for the physical environment as observed by an /// AR device. /// [RequireComponent(typeof(Light))] public class LightEstimation : MonoBehaviour { [SerializeField] [Tooltip("The ARCameraManager which will produce frame events containing light estimation information.")] ARCameraManager m_CameraManager; [SerializeField] Transform m_Arrow; public Transform arrow { get => m_Arrow; set => m_Arrow = value; } /// /// Get or set the ARCameraManager. /// public ARCameraManager cameraManager { get { return m_CameraManager; } set { if (m_CameraManager == value) return; if (m_CameraManager != null) m_CameraManager.frameReceived -= FrameChanged; m_CameraManager = value; if (m_CameraManager != null & enabled) m_CameraManager.frameReceived += FrameChanged; } } /// /// The estimated brightness of the physical environment, if available. /// public float? brightness { get; private set; } /// /// The estimated color temperature of the physical environment, if available. /// public float? colorTemperature { get; private set; } /// /// The estimated color correction value of the physical environment, if available. /// public Color? colorCorrection { get; private set; } /// /// The estimated direction of the main light of the physical environment, if available. /// public Vector3? mainLightDirection { get; private set; } /// /// The estimated color of the main light of the physical environment, if available. /// public Color? mainLightColor { get; private set; } /// /// The estimated intensity in lumens of main light of the physical environment, if available. /// public float? mainLightIntensityLumens { get; private set; } /// /// The estimated spherical harmonics coefficients of the physical environment, if available. /// public SphericalHarmonicsL2? sphericalHarmonics { get; private set; } void Awake () { m_Light = GetComponent(); } void OnEnable() { if (m_CameraManager != null) m_CameraManager.frameReceived += FrameChanged; // Disable the arrow to start; enable it later if we get directional light info arrow?.gameObject.SetActive(false); Application.onBeforeRender += OnBeforeRender; } void OnDisable() { Application.onBeforeRender -= OnBeforeRender; if (m_CameraManager != null) m_CameraManager.frameReceived -= FrameChanged; } void OnBeforeRender() { if (arrow && m_CameraManager) { var cameraTransform = m_CameraManager.GetComponent().transform; arrow.position = cameraTransform.position + cameraTransform.forward * .25f; } } void FrameChanged(ARCameraFrameEventArgs args) { if (args.lightEstimation.averageBrightness.HasValue) { brightness = args.lightEstimation.averageBrightness.Value; m_Light.intensity = brightness.Value; } if (args.lightEstimation.averageColorTemperature.HasValue) { colorTemperature = args.lightEstimation.averageColorTemperature.Value; m_Light.colorTemperature = colorTemperature.Value; } if (args.lightEstimation.colorCorrection.HasValue) { colorCorrection = args.lightEstimation.colorCorrection.Value; m_Light.color = colorCorrection.Value; } if (args.lightEstimation.mainLightDirection.HasValue) { mainLightDirection = args.lightEstimation.mainLightDirection; m_Light.transform.rotation = Quaternion.LookRotation(mainLightDirection.Value); if (arrow) { arrow.gameObject.SetActive(true); arrow.rotation = Quaternion.LookRotation(mainLightDirection.Value); } } else { arrow?.gameObject.SetActive(false); } 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 m_Light.color = mainLightColor.Value / Mathf.PI; m_Light.color = m_Light.color.gamma; // ARCore returns color in HDR format (can be represented as FP16 and have values above 1.0) var camera = m_CameraManager.GetComponentInParent(); 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; m_Light.intensity = args.lightEstimation.averageMainLightBrightness.Value; } if (args.lightEstimation.ambientSphericalHarmonics.HasValue) { sphericalHarmonics = args.lightEstimation.ambientSphericalHarmonics; RenderSettings.ambientMode = AmbientMode.Skybox; RenderSettings.ambientProbe = sphericalHarmonics.Value; } } Light m_Light; }