123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532 |
- // Copyright (C) 2019-2021 Alexander Bogarsukov. All rights reserved.
- // See the LICENSE.md file in the project root for more information.
- using System;
- using System.Collections.Generic;
- using System.Diagnostics;
- using UnityEditor;
- using UnityEngine;
- using UnityEngine.Rendering;
- namespace UnityFx.Outline
- {
- /// <summary>
- /// This asset is used to store references to shaders and other resources needed at runtime without having to use a Resources folder.
- /// </summary>
- /// <seealso cref="OutlineRenderer"/>
- [CreateAssetMenu(fileName = "OutlineResources", menuName = "UnityFx/Outline/Outline Resources")]
- public sealed class OutlineResources : ScriptableObject
- {
- #region data
- [SerializeField]
- private Shader _renderShader;
- [SerializeField]
- private Shader _outlineShader;
- private Material _renderMaterial;
- private Material _outlineMaterial;
- private MaterialPropertyBlock _props;
- private Mesh _fullscreenTriangleMesh;
- private float[][] _gaussSamples;
- private bool _useDrawMesh;
- #endregion
- #region interface
- /// <summary>
- /// Minimum value of outline width parameter.
- /// </summary>
- /// <seealso cref="MaxWidth"/>
- public const int MinWidth = 1;
- /// <summary>
- /// Maximum value of outline width parameter.
- /// </summary>
- /// <remarks>
- /// If the value is changed here, it should be adjusted in Outline.shader as well.
- /// </remarks>
- /// <seealso cref="MinWidth"/>
- public const int MaxWidth = 32;
- /// <summary>
- /// Minimum value of outline intensity parameter.
- /// </summary>
- /// <seealso cref="MaxIntensity"/>
- /// <seealso cref="SolidIntensity"/>
- public const int MinIntensity = 1;
- /// <summary>
- /// Maximum value of outline intensity parameter.
- /// </summary>
- /// <seealso cref="MinIntensity"/>
- /// <seealso cref="SolidIntensity"/>
- public const int MaxIntensity = 64;
- /// <summary>
- /// Value of outline intensity parameter that is treated as solid fill.
- /// </summary>
- /// <seealso cref="MinIntensity"/>
- /// <seealso cref="MaxIntensity"/>
- public const int SolidIntensity = 100;
- /// <summary>
- /// Minimum value of outline alpha cutoff parameter.
- /// </summary>
- /// <seealso cref="MaxAlphaCutoff"/>
- public const float MinAlphaCutoff = 0;
- /// <summary>
- /// Maximum value of outline alpha cutoff parameter.
- /// </summary>
- /// <seealso cref="MinAlphaCutoff"/>
- public const float MaxAlphaCutoff = 1;
- /// <summary>
- /// Name of _MainTex shader parameter.
- /// </summary>
- public const string MainTexName = "_MainTex";
- /// <summary>
- /// Name of _MaskTex shader parameter.
- /// </summary>
- public const string MaskTexName = "_MaskTex";
- /// <summary>
- /// Name of _TempTex shader parameter.
- /// </summary>
- public const string TempTexName = "_TempTex";
- /// <summary>
- /// Name of _Color shader parameter.
- /// </summary>
- public const string ColorName = "_Color";
- /// <summary>
- /// Name of _Width shader parameter.
- /// </summary>
- public const string WidthName = "_Width";
- /// <summary>
- /// Name of _Intensity shader parameter.
- /// </summary>
- public const string IntensityName = "_Intensity";
- /// <summary>
- /// Name of _Cutoff shader parameter.
- /// </summary>
- public const string AlphaCutoffName = "_Cutoff";
- /// <summary>
- /// Name of _GaussSamples shader parameter.
- /// </summary>
- public const string GaussSamplesName = "_GaussSamples";
- /// <summary>
- /// Name of the _USE_DRAWMESH shader feature.
- /// </summary>
- public const string UseDrawMeshFeatureName = "_USE_DRAWMESH";
- /// <summary>
- /// Name of the outline effect.
- /// </summary>
- public const string EffectName = "Outline";
- /// <summary>
- /// Tooltip text for <see cref="OutlineResources"/> field.
- /// </summary>
- public const string OutlineResourcesTooltip = "Outline resources to use (shaders, materials etc). Do not change defaults unless you know what you're doing.";
- /// <summary>
- /// Tooltip text for <see cref="OutlineLayerCollection"/> field.
- /// </summary>
- public const string OutlineLayerCollectionTooltip = "Collection of outline layers to use. This can be used to share outline settings between multiple cameras.";
- /// <summary>
- /// Tooltip text for outline <see cref="LayerMask"/> field.
- /// </summary>
- public const string OutlineLayerMaskTooltip = "Layer mask for outined objects.";
- /// <summary>
- /// Tooltip text for outline <see cref="LayerMask"/> field.
- /// </summary>
- public const string OutlineRenderingLayerMaskTooltip = "Rendering layer mask for outined objects.";
- /// <summary>
- /// Index of the default pass in <see cref="RenderShader"/>.
- /// </summary>
- public const int RenderShaderDefaultPassId = 0;
- /// <summary>
- /// Index of the alpha-test pass in <see cref="RenderShader"/>.
- /// </summary>
- public const int RenderShaderAlphaTestPassId = 1;
- /// <summary>
- /// Index of the HPass in <see cref="OutlineShader"/>.
- /// </summary>
- public const int OutlineShaderHPassId = 0;
- /// <summary>
- /// Index of the VPass in <see cref="OutlineShader"/>.
- /// </summary>
- public const int OutlineShaderVPassId = 1;
- /// <summary>
- /// SRP not supported message.
- /// </summary>
- internal const string SrpNotSupported = "{0} works with built-in render pipeline only. It does not support SRP (including URP and HDRP).";
- /// <summary>
- /// Post-processing not supported message.
- /// </summary>
- internal const string PpNotSupported = "{0} does not support Unity Post-processing stack v2. It might not work as expected.";
- /// <summary>
- /// Hashed name of _MainTex shader parameter.
- /// </summary>
- public readonly int MainTexId = Shader.PropertyToID(MainTexName);
- /// <summary>
- /// Texture identifier for _MainTex shader parameter.
- /// </summary>
- public readonly RenderTargetIdentifier MainTex = new RenderTargetIdentifier(MainTexName);
- /// <summary>
- /// Hashed name of _MaskTex shader parameter.
- /// </summary>
- public readonly int MaskTexId = Shader.PropertyToID(MaskTexName);
- /// <summary>
- /// Texture identifier for _MaskTex shader parameter.
- /// </summary>
- public readonly RenderTargetIdentifier MaskTex = new RenderTargetIdentifier(MaskTexName);
- /// <summary>
- /// Hashed name of _TempTex shader parameter.
- /// </summary>
- public readonly int TempTexId = Shader.PropertyToID(TempTexName);
- /// <summary>
- /// Texture identifier for _TempTex shader parameter.
- /// </summary>
- public readonly RenderTargetIdentifier TempTex = new RenderTargetIdentifier(TempTexName);
- /// <summary>
- /// Hashed name of _Color shader parameter.
- /// </summary>
- public readonly int ColorId = Shader.PropertyToID(ColorName);
- /// <summary>
- /// Hashed name of _Width shader parameter.
- /// </summary>
- public readonly int WidthId = Shader.PropertyToID(WidthName);
- /// <summary>
- /// Hashed name of _Intensity shader parameter.
- /// </summary>
- public readonly int IntensityId = Shader.PropertyToID(IntensityName);
- /// <summary>
- /// Hashed name of _Cutoff shader parameter.
- /// </summary>
- public readonly int AlphaCutoffId = Shader.PropertyToID(AlphaCutoffName);
- /// <summary>
- /// Hashed name of _GaussSamples shader parameter.
- /// </summary>
- public readonly int GaussSamplesId = Shader.PropertyToID(GaussSamplesName);
- /// <summary>
- /// Temp materials list. Used by <see cref="OutlineRenderer"/> to avoid GC allocations.
- /// </summary>
- internal readonly List<Material> TmpMaterials = new List<Material>();
- /// <summary>
- /// Gets a <see cref="Shader"/> that renders objects outlined with a solid while color.
- /// </summary>
- public Shader RenderShader
- {
- get
- {
- return _renderShader;
- }
- }
- /// <summary>
- /// Gets a <see cref="Shader"/> that renders outline around the mask, that was generated with <see cref="RenderShader"/>.
- /// </summary>
- public Shader OutlineShader
- {
- get
- {
- return _outlineShader;
- }
- }
- /// <summary>
- /// Gets a <see cref="RenderShader"/>-based material.
- /// </summary>
- public Material RenderMaterial
- {
- get
- {
- if (_renderMaterial == null)
- {
- UnityEngine.Debug.Assert(_renderShader != null, "No RenderShader is set in outline resources.", this);
- _renderMaterial = new Material(_renderShader)
- {
- name = "Outline - RenderColor",
- hideFlags = HideFlags.HideAndDontSave
- };
- }
- return _renderMaterial;
- }
- }
- /// <summary>
- /// Gets a <see cref="OutlineShader"/>-based material.
- /// </summary>
- public Material OutlineMaterial
- {
- get
- {
- if (_outlineMaterial == null)
- {
- UnityEngine.Debug.Assert(_outlineShader != null, "No OutlineShader is set in outline resources.", this);
- _outlineMaterial = new Material(_outlineShader)
- {
- name = "Outline - Main",
- hideFlags = HideFlags.HideAndDontSave
- };
- if (_useDrawMesh)
- {
- _outlineMaterial.EnableKeyword(UseDrawMeshFeatureName);
- }
- }
- return _outlineMaterial;
- }
- }
- /// <summary>
- /// Gets a <see cref="MaterialPropertyBlock"/> for <see cref="VPassBlendMaterial"/>.
- /// </summary>
- public MaterialPropertyBlock Properties
- {
- get
- {
- if (_props is null)
- {
- _props = new MaterialPropertyBlock();
- }
- return _props;
- }
- }
- /// <summary>
- /// Gets or sets a fullscreen triangle mesh. The mesh is lazy-initialized on the first access.
- /// </summary>
- /// <remarks>
- /// This is used by <see cref="OutlineRenderer"/> to avoid Blit() calls and use DrawMesh() passing
- /// this mesh as the first argument. When running on a device with Shader Model 3.5 support this
- /// should not be used at all, as the vertices are generated in vertex shader with DrawProcedural() call.
- /// </remarks>
- /// <seealso cref="OutlineRenderer"/>
- public Mesh FullscreenTriangleMesh
- {
- get
- {
- if (_fullscreenTriangleMesh == null)
- {
- _fullscreenTriangleMesh = new Mesh()
- {
- name = "Outline - FullscreenTriangle",
- hideFlags = HideFlags.HideAndDontSave,
- vertices = new Vector3[] { new Vector3(-1, -1, 0), new Vector3(3, -1, 0), new Vector3(-1, 3, 0) },
- triangles = new int[] { 0, 1, 2 }
- };
- _fullscreenTriangleMesh.UploadMeshData(true);
- }
- return _fullscreenTriangleMesh;
- }
- set
- {
- _fullscreenTriangleMesh = value;
- }
- }
- /// <summary>
- /// Gets or sets a value indicating whether <see cref="FullscreenTriangleMesh"/> is used for image effects rendering even when procedural rendering is available.
- /// </summary>
- public bool UseFullscreenTriangleMesh
- {
- get
- {
- return _useDrawMesh;
- }
- set
- {
- if (_useDrawMesh != value)
- {
- _useDrawMesh = value;
- if (_outlineMaterial)
- {
- if (_useDrawMesh)
- {
- _outlineMaterial.EnableKeyword(UseDrawMeshFeatureName);
- }
- else
- {
- _outlineMaterial.DisableKeyword(UseDrawMeshFeatureName);
- }
- }
- }
- }
- }
- /// <summary>
- /// Gets a value indicating whether the instance is in valid state.
- /// </summary>
- public bool IsValid => RenderShader && OutlineShader;
- /// <summary>
- /// Returns a <see cref="MaterialPropertyBlock"/> instance initialized with values from <paramref name="settings"/>.
- /// </summary>
- public MaterialPropertyBlock GetProperties(IOutlineSettings settings)
- {
- if (_props is null)
- {
- _props = new MaterialPropertyBlock();
- }
- _props.SetFloat(WidthId, settings.OutlineWidth);
- _props.SetColor(ColorId, settings.OutlineColor);
- if ((settings.OutlineRenderMode & OutlineRenderFlags.Blurred) != 0)
- {
- _props.SetFloat(IntensityId, settings.OutlineIntensity);
- }
- else
- {
- _props.SetFloat(IntensityId, SolidIntensity);
- }
- return _props;
- }
- /// <summary>
- /// Gets cached gauss samples for the specified outline <paramref name="width"/>.
- /// </summary>
- public float[] GetGaussSamples(int width)
- {
- var index = Mathf.Clamp(width, 1, MaxWidth) - 1;
- if (_gaussSamples is null)
- {
- _gaussSamples = new float[MaxWidth][];
- }
- if (_gaussSamples[index] is null)
- {
- _gaussSamples[index] = GetGaussSamples(width, null);
- }
- return _gaussSamples[index];
- }
- /// <summary>
- /// Resets the resources to defaults.
- /// </summary>
- public void ResetToDefaults()
- {
- _renderShader = Shader.Find("Hidden/UnityFx/OutlineColor");
- _outlineShader = Shader.Find("Hidden/UnityFx/Outline");
- }
- /// <summary>
- /// Calculates value of Gauss function for the specified <paramref name="x"/> and <paramref name="stdDev"/> values.
- /// </summary>
- /// <seealso href="https://en.wikipedia.org/wiki/Gaussian_blur"/>
- /// <seealso href="https://en.wikipedia.org/wiki/Normal_distribution"/>
- public static float Gauss(float x, float stdDev)
- {
- var stdDev2 = stdDev * stdDev * 2;
- var a = 1 / Mathf.Sqrt(Mathf.PI * stdDev2);
- var gauss = a * Mathf.Pow((float)Math.E, -x * x / stdDev2);
- return gauss;
- }
- /// <summary>
- /// Samples Gauss function for the specified <paramref name="width"/>.
- /// </summary>
- /// <seealso href="https://en.wikipedia.org/wiki/Normal_distribution"/>
- public static float[] GetGaussSamples(int width, float[] samples)
- {
- // NOTE: According to '3 sigma' rule there is no reason to have StdDev less then width / 3.
- // In practice blur looks best when StdDev is within range [width / 3, width / 2].
- var stdDev = width * 0.5f;
- if (samples is null)
- {
- samples = new float[MaxWidth];
- }
- for (var i = 0; i < width; i++)
- {
- samples[i] = Gauss(i, stdDev);
- }
- return samples;
- }
- /// <summary>
- /// Writes a console warning if SRP is detected.
- /// </summary>
- public static void LogSrpNotSupported(UnityEngine.Object obj)
- {
- if (GraphicsSettings.renderPipelineAsset)
- {
- UnityEngine.Debug.LogWarningFormat(obj, SrpNotSupported, obj.GetType().Name);
- }
- }
- /// <summary>
- /// Writes a console warning if Post Processing Stack v2 is detected.
- /// </summary>
- [Conditional("UNITY_POST_PROCESSING_STACK_V2")]
- public static void LogPpNotSupported(UnityEngine.Object obj)
- {
- UnityEngine.Debug.LogWarningFormat(obj, PpNotSupported, obj.GetType().Name);
- }
- #endregion
- #region ScriptableObject
- private void OnValidate()
- {
- if (_renderMaterial)
- {
- _renderMaterial.shader = _renderShader;
- }
- if (_outlineMaterial)
- {
- _outlineMaterial.shader = _outlineShader;
- }
- }
- #endregion
- }
- }
|