// Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #if GT_USE_URP using System.Collections.Generic; using UnityEngine; using UnityEngine.Experimental.Rendering.Universal; using UnityEngine.Rendering.Universal; namespace Microsoft.MixedReality.GraphicsTools { /// /// Manages creating and updating render features necessary for the magnification effect on the scriptable render pipeline. /// public class MagnifierManager : MonoBehaviour { [Tooltip("Scaler of the magnification.")] [SerializeField, Range(0.0f, 1.0f)] private float magnification = 0.7f; // Scaler of the magnification. public float Magnification { get => magnification; set { magnification = Mathf.Clamp01(value); ApplyMagnification(); } } [Tooltip("The name of the global shader property that drives the magnification ammount.")] [SerializeField] private string magnificationPropertyName = "MagnifierMagnification"; public string MagnificationPropertyName { get => magnificationPropertyName; set { magnificationPropertyName = value; ApplyMagnification(); } } [Tooltip("Which renderer to use in the UniversalRenderPipelineAsset.")] [SerializeField] private int rendererIndex = 0; [Tooltip("Should a DrawFullscreenFeature feature be automatically added?")] [SerializeField] private bool AutoAddDrawFullscreenFeature = true; [Tooltip("Is the magnifier in lens mode? i.e is moving based on the position of the mouse cursor")] [SerializeField] private bool inLensMode = false; public bool InLensMode { get => inLensMode; set { inLensMode = value; ApplyMagnifierCenter(); } } [Tooltip("The name of the render feature to add the Draw Fullscreen Feature after (or before) to enforce custom sorting. Adds to the end of the render feature list when empty.")] public string targetDrawFullscreenFeatureName = string.Empty; public enum AddMode { After, Before } [Tooltip("When a Target Draw Fullscreen Feature Name is specified, should it be added before or after the feature in the list?")] public AddMode targetDrawFullscreenFeatureAddMode = AddMode.After; [SerializeField] private DrawFullscreenFeature.Settings drawFullscreenSettings = new DrawFullscreenFeature.Settings() { renderPassEvent = RenderPassEvent.AfterRenderingTransparents, SourceType = BufferType.CameraColor, DestinationType = BufferType.Custom, SourceTextureId = string.Empty, DestinationTextureId = "MagnifierTexture", }; [Tooltip("Should a RenderObjects feature be automatically added?")] [SerializeField] private bool AutoAddRenderObjectsFeature = true; [Tooltip("The name of the render feature to add the Draw Objects Feature after (or before) to enforce custom sorting. Adds to the end of the render feature list when empty.")] public string targetDrawObjectsFeatureName = string.Empty; [Tooltip("When a Target Draw Objects Feature Name is specified, should it be added before or after the feature in the list?")] public AddMode targetDrawObjectsFeatureAddMode = AddMode.After; private static readonly int magnifierCenterID = Shader.PropertyToID("MagnifierCenter"); [SerializeField] private RenderObjects.RenderObjectsSettings renderObjectsSettings = new RenderObjects.RenderObjectsSettings() { Event = RenderPassEvent.AfterRenderingTransparents, filterSettings = new RenderObjects.FilterSettings() { RenderQueueType = RenderQueueType.Transparent } }; [SerializeField, HideInInspector] private Material defaultBlitMaterial; private DrawFullscreenFeature drawFullscreenFeature; private ScriptableRendererFeature renderObjectsFeature; private bool initialized = false; #if UNITY_2021_2_OR_NEWER private UniversalRendererData rendererData = null; #else private ForwardRendererData rendererData = null; #endif private LayerMask previousOpaqueLayerMask; private LayerMask previousTransparentLayerMask; private IntermediateTextureMode previousIntermediateTextureMode; private void Reset() { if (drawFullscreenSettings.BlitMaterial == null) { drawFullscreenSettings.BlitMaterial = defaultBlitMaterial; } } private void OnEnable() { if (!initialized) { rendererData = URPUtility.GetRendererData(rendererIndex); if (rendererData != null) { // Previously, URP would force rendering to go through an intermediate renderer if the Renderer had any Renderer Features active. On some platforms, this has // significant performance implications so we only want to enable this when we need it (which we do for magnification). previousIntermediateTextureMode = rendererData.intermediateTextureMode; rendererData.intermediateTextureMode = IntermediateTextureMode.Always; CreateRendererFeatures(); initialized = true; } ApplyMagnification(); ApplyMagnifierCenter(); } } private void OnDisable() { if (initialized) { if (drawFullscreenFeature != null) { rendererData.rendererFeatures.Remove(drawFullscreenFeature); drawFullscreenFeature = null; } if (renderObjectsFeature != null) { rendererData.rendererFeatures.Remove(renderObjectsFeature); renderObjectsFeature = null; } // Reset the layer masks. if (AutoAddRenderObjectsFeature) { rendererData.opaqueLayerMask = previousOpaqueLayerMask; rendererData.transparentLayerMask = previousTransparentLayerMask; } // Reset the intermediate texture mode. rendererData.intermediateTextureMode = previousIntermediateTextureMode; rendererData.SetDirty(); initialized = false; } } public void ApplyMagnification() { Shader.SetGlobalFloat(MagnificationPropertyName, 1.0f - Magnification); } private void ApplyMagnifierCenter() { if (inLensMode && Camera.main != null) { Vector3 cursorPosition; #if USE_INPUT_SYSTEM Vector2 position2D = Mouse.current != null ? Mouse.current.position.ReadValue() : Vector2.zero; cursorPosition = new Vector3(position2D.x, position2D.y, 0.0f); #else cursorPosition = Input.mousePosition; #endif // USE_INPUT_SYSTEM Vector3 viewportPostion = Camera.main.ScreenToViewportPoint(cursorPosition); Vector4 viewportPostion4 = new Vector4(viewportPostion.x, viewportPostion.y, 0, 0); Shader.SetGlobalVector(magnifierCenterID, viewportPostion4); } else { Shader.SetGlobalVector(magnifierCenterID, new Vector4(0.5f, 0.5f, 0, 0)); } } private void Update() { if (InLensMode) { ApplyMagnifierCenter(); } } private void CreateRendererFeatures() { if (AutoAddDrawFullscreenFeature) { drawFullscreenFeature = CreateMagnifierFullsreenFeature("Magnifier Draw Fullscreen", drawFullscreenSettings); InsertFeature(rendererData.rendererFeatures, drawFullscreenFeature, targetDrawFullscreenFeatureName, targetDrawFullscreenFeatureAddMode); } if (AutoAddRenderObjectsFeature) { renderObjectsFeature = CreateMagnifierRenderObjectsFeature("Magnifier Render Objects", renderObjectsSettings); InsertFeature(rendererData.rendererFeatures, renderObjectsFeature, targetDrawObjectsFeatureName, targetDrawObjectsFeatureAddMode); //Don't render the layers rendered by the RenderObjectsFeature previousOpaqueLayerMask = rendererData.opaqueLayerMask; previousTransparentLayerMask = rendererData.transparentLayerMask; rendererData.opaqueLayerMask &= ~renderObjectsSettings.filterSettings.LayerMask; rendererData.transparentLayerMask &= ~renderObjectsSettings.filterSettings.LayerMask; } rendererData.SetDirty(); } private static DrawFullscreenFeature CreateMagnifierFullsreenFeature(string name, DrawFullscreenFeature.Settings settings) { DrawFullscreenFeature feature = ScriptableObject.CreateInstance(); feature.name = name; feature.settings = settings; return feature; } private static ScriptableRendererFeature CreateMagnifierRenderObjectsFeature(string name, RenderObjects.RenderObjectsSettings settings) { RenderObjects feature = ScriptableObject.CreateInstance(); feature.name = name; feature.settings = settings; return feature; } private static void InsertFeature(List features, ScriptableRendererFeature feature, string targetName, AddMode mode) { int insertIndex = -1; if (!string.IsNullOrEmpty(targetName)) { insertIndex = features.FindIndex(x => x.name == targetName); } if (insertIndex == -1) { insertIndex = features.Count - 1; } if (mode == AddMode.After) { ++insertIndex; } features.Insert(insertIndex, feature); } } } #endif // GT_USE_URP