Last active
October 11, 2025 15:48
-
-
Save ScottJDaley/6cddf0c8995ed61cac7088e22c983de1 to your computer and use it in GitHub Desktop.
Revisions
-
ScottJDaley revised this gist
Mar 29, 2022 . 3 changed files with 31 additions and 29 deletions.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -9,8 +9,9 @@ Shader "Hidden/Outline" { Properties { [HideInInspector] _MainTex ("Texture", 2D) = "white" {} _OutlineColor("Color", Color) = (1, 1, 1, 1) _OutlineWidth ("Width", Range (0, 20)) = 5 } SubShader @@ -36,7 +37,7 @@ Shader "Hidden/Outline" Ref 1 ReadMask 1 WriteMask 1 Comp Always Pass Replace } @@ -149,9 +150,11 @@ Shader "Hidden/Outline" CBUFFER_START(UnityPerMaterial) float4 _MainTex_TexelSize; half4 _OutlineColor; float _OutlineWidth; CBUFFER_END v2f vert (appdata i) { UNITY_SETUP_INSTANCE_ID(i); v2f o; @@ -245,10 +248,12 @@ Shader "Hidden/Outline" Texture2D _MainTex; CBUFFER_START(UnityPerMaterial) float4 _MainTex_TexelSize; half4 _OutlineColor; float _OutlineWidth; CBUFFER_END int2 _AxisWidth; v2f vert (appdata i) { UNITY_SETUP_INSTANCE_ID(i); v2f o; @@ -343,15 +348,14 @@ Shader "Hidden/Outline" }; Texture2D _MainTex; CBUFFER_START(UnityPerMaterial) float4 _MainTex_TexelSize; half4 _OutlineColor; float _OutlineWidth; CBUFFER_END v2f vert (appdata i) { UNITY_SETUP_INSTANCE_ID(i); v2f o; @@ -389,7 +393,6 @@ Shader "Hidden/Outline" // apply outline to alpha half4 col = _OutlineColor; col.a *= outline; // profit! return col; This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -9,9 +9,9 @@ public class OutlineFeature : ScriptableRendererFeature { public Settings OutlineSettings; private OutlinePass _outlinePass; private StencilPass _stencilPass; [Serializable] public class Settings @@ -20,11 +20,11 @@ public class Settings [ColorUsage(true, true)] public Color Color = new Color(0.2f, 0.4f, 1, 1f); [Range(0.0f, 20.0f)] public float Width = 5f; [Header("Rendering")] public LayerMask LayerMask = 0; // TODO: Try this again when render layers are working with hybrid renderer. // [Range(0, 32)] @@ -37,22 +37,21 @@ public class Settings public override void Create() { if (OutlineSettings == null) { return; } _stencilPass = new StencilPass(OutlineSettings); _outlinePass = new OutlinePass(OutlineSettings); } public override void AddRenderPasses(ScriptableRenderer renderer, ref RenderingData renderingData) { if (OutlineSettings == null) { return; } renderer.EnqueuePass(_stencilPass); renderer.EnqueuePass(_outlinePass); } } This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -4,8 +4,8 @@ ## Usage 1. Create a new Unity game object layer for the objects you wish to outline. 2. In your URP asset, turn on MSAA or disable the Depth Texture. 3. In the ForwardRendererData asset of your URP asset, add the renderer feature named "Outline Feature" 4. Set the Layer Mask to the layer created in step 1. 5. Add the objects you wish to outline to the outline layer (this can be done at runtime in ECS by setting the layer of the RenderMesh). 6. Adjust the color and width of the outlines as desired in the renderer feature settings. @@ -16,7 +16,7 @@ When I first added support for the Hyrbid Renderer, render layers were not worki ## Credits - The technique for these wide outlines comes from [Ben Golus](https://bgolus.medium.com/) which is described and implemented in [this article](https://bgolus.medium.com/the-quest-for-very-wide-outlines-ba82ed442cd9). - [Alexander Ameye](https://alexanderameye.github.io/) created the renderer feature and shader modifications to make this work in URP, original shared [here](https://twitter.com/alexanderameye/status/1332286868222775298). - [Scott Daley](https://twitter.com/GravitonPunch) modified the shader to get it working with Unity ECS and the Hybrid Renderer. ## Compatibility -
ScottJDaley revised this gist
Mar 27, 2022 . 1 changed file with 2 additions and 0 deletions.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -1,3 +1,5 @@ # Wide Outlines Renderer Feature for URP and ECS/DOTS/Hybrid Renderer  ## Usage -
ScottJDaley revised this gist
Mar 27, 2022 . 1 changed file with 2 additions and 0 deletions.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -1,3 +1,5 @@  ## Usage 1. Create a new Unity game object layer for the objects you wish to outline. 2. In the ForwardRendererData asset of your URP asset, add the renderer feature named "Outline Feature" -
ScottJDaley created this gist
Mar 27, 2022 .There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -0,0 +1,400 @@ // Original shader by @bgolus, modified slightly by @alexanderameye for URP, modified slightly more // by @gravitonpunch for ECS/DOTS/HybridRenderer. // https://twitter.com/bgolus // https://medium.com/@bgolus/the-quest-for-very-wide-outlines-ba82ed442cd9 // https://alexanderameye.github.io/ // https://twitter.com/alexanderameye/status/1332286868222775298 Shader "Hidden/Outline" { Properties { _MainTex ("Texture", 2D) = "white" {} _Transparency ("Transparency", Float) = 1 } SubShader { Tags { "RenderType"="Opaque" "RenderPipeline" = "UniversalPipeline"} Cull Off ZWrite Off ZTest Always HLSLINCLUDE #define SNORM16_MAX_FLOAT_MINUS_EPSILON ((float)(32768-2) / (float)(32768-1)) #define FLOOD_ENCODE_OFFSET float2(1.0, SNORM16_MAX_FLOAT_MINUS_EPSILON) #define FLOOD_ENCODE_SCALE float2(2.0, 1.0 + SNORM16_MAX_FLOAT_MINUS_EPSILON) #define FLOOD_NULL_POS -1.0 #define FLOOD_NULL_POS_FLOAT2 float2(FLOOD_NULL_POS, FLOOD_NULL_POS) ENDHLSL Pass // 0 { Name "STENCIL MASK" Stencil { Ref 1 ReadMask 1 WriteMask 1 Comp NotEqual Pass Replace } ColorMask 0 Blend Zero One HLSLPROGRAM #pragma multi_compile_instancing #pragma multi_compile _ DOTS_INSTANCING_ON #pragma vertex vert #pragma fragment frag #include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Core.hlsl" #pragma target 4.5 struct appdata { float4 positionOS : POSITION; #if UNITY_ANY_INSTANCING_ENABLED uint instanceID : INSTANCEID_SEMANTIC; #endif }; float4 vert (appdata i) : SV_POSITION { UNITY_SETUP_INSTANCE_ID(i); return TransformObjectToHClip(i.positionOS.xyz); } void frag () {} ENDHLSL } Pass // 1 { Name "BUFFERFILL" HLSLPROGRAM #pragma multi_compile_instancing #pragma multi_compile _ DOTS_INSTANCING_ON #pragma vertex vert #pragma fragment frag #include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Core.hlsl" #pragma target 4.5 struct appdata { float4 positionOS : POSITION; #if UNITY_ANY_INSTANCING_ENABLED uint instanceID : INSTANCEID_SEMANTIC; #endif }; float4 vert (appdata i) : SV_POSITION { UNITY_SETUP_INSTANCE_ID(i); float4 pos = TransformObjectToHClip(i.positionOS.xyz); // flip the rendering "upside down" in non OpenGL to make things easier later // you'll notice none of the later passes need to pass UVs #ifdef UNITY_UV_STARTS_AT_TOP // pos.y = -pos.y; #endif return pos; } half frag () : SV_TARGET { return 1.0; } ENDHLSL } Pass // 2 { Name "JUMPFLOODINIT" HLSLPROGRAM #pragma multi_compile_instancing #pragma multi_compile _ DOTS_INSTANCING_ON #pragma vertex vert #pragma fragment frag #include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Core.hlsl" #pragma target 4.5 struct appdata { float4 positionOS : POSITION; float2 uv : TEXCOORD0; #if UNITY_ANY_INSTANCING_ENABLED uint instanceID : INSTANCEID_SEMANTIC; #endif }; struct v2f { float4 positionCS : SV_POSITION; #if UNITY_ANY_INSTANCING_ENABLED uint instanceID : CUSTOM_INSTANCE_ID; #endif }; Texture2D _MainTex; CBUFFER_START(UnityPerMaterial) float4 _MainTex_TexelSize; CBUFFER_END v2f vert (appdata i) : SV_POSITION { UNITY_SETUP_INSTANCE_ID(i); v2f o; o.positionCS = TransformObjectToHClip(i.positionOS.xyz); return o; } float2 frag (v2f i) : SV_TARGET { // integer pixel position int2 uvInt = i.positionCS.xy; // sample silhouette texture for sobel half3x3 values; UNITY_UNROLL for(int u=0; u<3; u++) { UNITY_UNROLL for(int v=0; v<3; v++) { uint2 sampleUV = clamp(uvInt + int2(u-1, v-1), int2(0,0), (int2)_MainTex_TexelSize.zw - 1); values[u][v] = _MainTex.Load(int3(sampleUV, 0)).r; } } // calculate output position for this pixel float2 outPos = i.positionCS.xy * abs(_MainTex_TexelSize.xy) * FLOOD_ENCODE_SCALE - FLOOD_ENCODE_OFFSET; // interior, return position if (values._m11 > 0.99) return outPos; // exterior, return no position if (values._m11 < 0.01) return FLOOD_NULL_POS_FLOAT2; // sobel to estimate edge direction float2 dir = -float2( values[0][0] + values[0][1] * 2.0 + values[0][2] - values[2][0] - values[2][1] * 2.0 - values[2][2], values[0][0] + values[1][0] * 2.0 + values[2][0] - values[0][2] - values[1][2] * 2.0 - values[2][2] ); // if dir length is small, this is either a sub pixel dot or line // no way to estimate sub pixel edge, so output position if (abs(dir.x) <= 0.005 && abs(dir.y) <= 0.005) return outPos; // normalize direction dir = normalize(dir); // sub pixel offset float2 offset = dir * (1.0 - values._m11); // output encoded offset position return (i.positionCS.xy + offset) * abs(_MainTex_TexelSize.xy) * FLOOD_ENCODE_SCALE - FLOOD_ENCODE_OFFSET; } ENDHLSL } Pass // 3 { Name "JUMPFLOOD_SINGLEAXIS" HLSLPROGRAM #pragma multi_compile_instancing #pragma multi_compile _ DOTS_INSTANCING_ON #pragma vertex vert #pragma fragment frag #include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Core.hlsl" #pragma target 4.5 struct appdata { float4 positionOS : POSITION; #if UNITY_ANY_INSTANCING_ENABLED uint instanceID : INSTANCEID_SEMANTIC; #endif }; struct v2f { float4 positionCS : SV_POSITION; #if UNITY_ANY_INSTANCING_ENABLED uint instanceID : CUSTOM_INSTANCE_ID; #endif }; Texture2D _MainTex; CBUFFER_START(UnityPerMaterial) float4 _MainTex_TexelSize; CBUFFER_END int2 _AxisWidth; v2f vert (appdata i) : SV_POSITION { UNITY_SETUP_INSTANCE_ID(i); v2f o; o.positionCS = TransformObjectToHClip(i.positionOS.xyz); return o; } half2 frag (v2f i) : SV_TARGET { // integer pixel position int2 uvInt = int2(i.positionCS.xy); // initialize best distance at infinity float bestDist = 100000000; float2 bestCoord; // jump samples // only one loop UNITY_UNROLL for(int u=-1; u<=1; u++) { // calculate offset sample position int2 offsetUV = uvInt + _AxisWidth * u; // .Load() acts funny when sampling outside of bounds, so don't offsetUV = clamp(offsetUV, int2(0,0), (int2)_MainTex_TexelSize.zw - 1); // decode position from buffer float2 offsetPos = (_MainTex.Load(int3(offsetUV, 0)).rg + FLOOD_ENCODE_OFFSET) * _MainTex_TexelSize.zw / FLOOD_ENCODE_SCALE; // the offset from current position float2 disp = i.positionCS.xy - offsetPos; // square distance float dist = dot(disp, disp); // if offset position isn't a null position or is closer than the best // set as the new best and store the position if (offsetPos.x != -1.0 && dist < bestDist) { bestDist = dist; bestCoord = offsetPos; } } // if not valid best distance output null position, otherwise output encoded position return isinf(bestDist) ? FLOOD_NULL_POS_FLOAT2 : bestCoord * _MainTex_TexelSize.xy * FLOOD_ENCODE_SCALE - FLOOD_ENCODE_OFFSET; } ENDHLSL } Pass // 4 { Name "OUTLINE" Stencil { Ref 1 ReadMask 1 WriteMask 1 Comp NotEqual Pass Zero Fail Zero } Blend SrcAlpha OneMinusSrcAlpha HLSLPROGRAM #pragma multi_compile_instancing #pragma multi_compile _ DOTS_INSTANCING_ON #pragma vertex vert #pragma fragment frag #include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Core.hlsl" #pragma target 4.5 struct appdata { float4 positionOS : POSITION; #if UNITY_ANY_INSTANCING_ENABLED uint instanceID : INSTANCEID_SEMANTIC; #endif }; struct v2f { float4 positionCS : SV_POSITION; #if UNITY_ANY_INSTANCING_ENABLED uint instanceID : CUSTOM_INSTANCE_ID; #endif }; Texture2D _MainTex; half4 _OutlineColor; float _OutlineWidth; CBUFFER_START(UnityPerMaterial) float _Transparency; CBUFFER_END v2f vert (appdata i) : SV_POSITION { UNITY_SETUP_INSTANCE_ID(i); v2f o; o.positionCS = TransformObjectToHClip(i.positionOS.xyz); return o; } half4 frag (v2f i) : SV_Target { // integer pixel position int2 uvInt = int2(i.positionCS.xy); // load encoded position float2 encodedPos = _MainTex.Load(int3(uvInt, 0)).rg; // early out if null position if (encodedPos.y == -1) return half4(0,0,0,0); // decode closest position float2 nearestPos = (encodedPos + FLOOD_ENCODE_OFFSET) * abs(_ScreenParams.xy) / FLOOD_ENCODE_SCALE; // current pixel position float2 currentPos = i.positionCS.xy; // distance in pixels to closest position half dist = length(nearestPos - currentPos); // calculate outline // + 1.0 is because encoded nearest position is half a pixel inset // not + 0.5 because we want the anti-aliased edge to be aligned between pixels // distance is already in pixels, so this is already perfectly anti-aliased! half outline = saturate(_OutlineWidth - dist + 1.0); // apply outline to alpha half4 col = _OutlineColor; col.a *= outline; col.a *= _Transparency; // profit! return col; } ENDHLSL } } } This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -0,0 +1,58 @@ // Modified version of outline renderer feature by Alexander Ameye. // https://alexanderameye.github.io/ // https://twitter.com/alexanderameye/status/1332286868222775298 using System; using UnityEngine; using UnityEngine.Rendering; using UnityEngine.Rendering.Universal; public class OutlineFeature : ScriptableRendererFeature { public Settings[] OutlineSettings; private OutlinePass[] _outlinePasses; private StencilPass[] _stencilPasses; [Serializable] public class Settings { [Header("Visual")] [ColorUsage(true, true)] public Color Color = new Color(0.2f, 0.4f, 1, 1f); [Range(0.0f, 5.0f)] public float Width = 4f; [Header("Rendering")] public LayerMask LayerMask = -1; // TODO: Try this again when render layers are working with hybrid renderer. // [Range(0, 32)] // public int RenderLayer = 1; public RenderPassEvent RenderPassEvent = RenderPassEvent.AfterRenderingTransparents; public SortingCriteria SortingCriteria = SortingCriteria.CommonOpaque; } public override void Create() { _stencilPasses = new StencilPass[OutlineSettings.Length]; _outlinePasses = new OutlinePass[OutlineSettings.Length]; for (int i = 0; i < OutlineSettings.Length; i++) { Settings settings = OutlineSettings[i]; _stencilPasses[i] = new StencilPass(settings); _outlinePasses[i] = new OutlinePass(settings); } } public override void AddRenderPasses(ScriptableRenderer renderer, ref RenderingData renderingData) { for (int i = 0; i < _outlinePasses.Length; i++) { renderer.EnqueuePass(_stencilPasses[i]); renderer.EnqueuePass(_outlinePasses[i]); } } } This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -0,0 +1,131 @@ // Modified version of outline render pass by Alexander Ameye. // https://alexanderameye.github.io/ // https://twitter.com/alexanderameye/status/1332286868222775298 using System; using System.Collections.Generic; using UnityEngine; using UnityEngine.Experimental.Rendering; using UnityEngine.Rendering; using UnityEngine.Rendering.Universal; public class OutlinePass : ScriptableRenderPass { private const string ProfilerTag = "Outline Pass"; private const string ShaderName = "Hidden/Outline"; private static readonly ShaderTagId _srpDefaultUnlit = new ShaderTagId("SRPDefaultUnlit"); private static readonly ShaderTagId _universalForward = new ShaderTagId("UniversalForward"); private static readonly ShaderTagId _lightweightForward = new ShaderTagId("LightweightForward"); private static readonly List<ShaderTagId> _shaderTags = new List<ShaderTagId> { _srpDefaultUnlit, _universalForward, _lightweightForward, }; private static readonly int _silhouetteBufferID = Shader.PropertyToID("_SilhouetteBuffer"); private static readonly int _nearestPointID = Shader.PropertyToID("_NearestPoint"); private static readonly int _nearestPointPingPongID = Shader.PropertyToID("_NearestPointPingPong"); private static readonly int _axisWidthID = Shader.PropertyToID("_AxisWidth"); private static readonly int _outlineColorID = Shader.PropertyToID("_OutlineColor"); private static readonly int _outlineWidthID = Shader.PropertyToID("_OutlineWidth"); private readonly Material _bufferFillMaterial; private readonly Material _outlineMaterial; private readonly OutlineFeature.Settings _settings; private RenderTargetIdentifier _cameraColor; private FilteringSettings _filteringSettings; public OutlinePass(OutlineFeature.Settings settings) { profilingSampler = new ProfilingSampler(ProfilerTag); _settings = settings; renderPassEvent = settings.RenderPassEvent; // TODO: Try this again when render layers are working with hybrid renderer. // uint renderingLayerMask = 1u << settings.RenderLayer - 1; // _filteringSettings = new FilteringSettings(RenderQueueRange.all, settings.LayerMask.value, renderingLayerMask); _filteringSettings = new FilteringSettings(RenderQueueRange.all, settings.LayerMask.value); if (!_outlineMaterial) { _outlineMaterial = CoreUtils.CreateEngineMaterial(ShaderName); } _outlineMaterial.SetColor(_outlineColorID, settings.Color); _outlineMaterial.SetFloat(_outlineWidthID, Mathf.Max(1f, settings.Width)); } public override void OnCameraSetup(CommandBuffer cmd, ref RenderingData renderingData) { RenderTextureDescriptor descriptor = renderingData.cameraData.cameraTargetDescriptor; descriptor.graphicsFormat = GraphicsFormat.R8_UNorm; descriptor.msaaSamples = 1; descriptor.depthBufferBits = 0; descriptor.sRGB = false; descriptor.useMipMap = false; descriptor.autoGenerateMips = false; cmd.GetTemporaryRT(_silhouetteBufferID, descriptor, FilterMode.Point); descriptor.graphicsFormat = GraphicsFormat.R16G16_SNorm; cmd.GetTemporaryRT(_nearestPointID, descriptor, FilterMode.Point); cmd.GetTemporaryRT(_nearestPointPingPongID, descriptor, FilterMode.Point); ConfigureTarget(_silhouetteBufferID); ConfigureClear(ClearFlag.Color, Color.clear); _cameraColor = renderingData.cameraData.renderer.cameraColorTarget; } public override void Execute(ScriptableRenderContext context, ref RenderingData renderingData) { DrawingSettings drawingSettings = CreateDrawingSettings( _shaderTags, ref renderingData, _settings.SortingCriteria ); drawingSettings.overrideMaterial = _outlineMaterial; drawingSettings.overrideMaterialPassIndex = 1; int numMips = Mathf.CeilToInt(Mathf.Log(_settings.Width + 1.0f, 2f)); int jfaIterations = numMips - 1; // TODO: Switch to this once mismatched markers bug is fixed. // CommandBuffer cmd = CommandBufferPool.Get(ProfilerTag); CommandBuffer cmd = CommandBufferPool.Get(); using (new ProfilingScope(cmd, profilingSampler)) { context.ExecuteCommandBuffer(cmd); cmd.Clear(); context.DrawRenderers(renderingData.cullResults, ref drawingSettings, ref _filteringSettings); Blit(cmd, _silhouetteBufferID, _nearestPointID, _outlineMaterial, 2); for (int i = jfaIterations; i >= 0; i--) { float stepWidth = Mathf.Pow(2, i) + 0.5f; cmd.SetGlobalVector(_axisWidthID, new Vector2(stepWidth, 0f)); Blit(cmd, _nearestPointID, _nearestPointPingPongID, _outlineMaterial, 3); cmd.SetGlobalVector(_axisWidthID, new Vector2(0f, stepWidth)); Blit(cmd, _nearestPointPingPongID, _nearestPointID, _outlineMaterial, 3); } cmd.Blit(_nearestPointID, _cameraColor, _outlineMaterial, 4); } context.ExecuteCommandBuffer(cmd); CommandBufferPool.Release(cmd); } public override void OnCameraCleanup(CommandBuffer cmd) { if (cmd == null) { throw new ArgumentNullException("cmd"); } cmd.ReleaseTemporaryRT(_silhouetteBufferID); cmd.ReleaseTemporaryRT(_nearestPointID); cmd.ReleaseTemporaryRT(_nearestPointPingPongID); } } This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -0,0 +1,69 @@ // Modified version of stencil render pass by Alexander Ameye. // https://alexanderameye.github.io/ // https://twitter.com/alexanderameye/status/1332286868222775298 using System.Collections.Generic; using UnityEngine; using UnityEngine.Rendering; using UnityEngine.Rendering.Universal; public class StencilPass : ScriptableRenderPass { private const string ProfilerTag = "Stencil Pass"; private const string ShaderName = "Hidden/Outline"; private static readonly ShaderTagId _srpDefaultUnlit = new ShaderTagId("SRPDefaultUnlit"); private static readonly ShaderTagId _universalForward = new ShaderTagId("UniversalForward"); private static readonly ShaderTagId _lightweightForward = new ShaderTagId("LightweightForward"); private static readonly List<ShaderTagId> _shaderTags = new List<ShaderTagId> { _srpDefaultUnlit, _universalForward, _lightweightForward, }; private readonly OutlineFeature.Settings _settings; private readonly Material _stencilMaterial; private FilteringSettings _filteringSettings; public StencilPass(OutlineFeature.Settings settings) { profilingSampler = new ProfilingSampler(ProfilerTag); _settings = settings; renderPassEvent = settings.RenderPassEvent; // TODO: Try this again when render layers are working with hybrid renderer. // uint renderingLayerMask = 1u << settings.RenderLayer - 1; // _filteringSettings = new FilteringSettings(RenderQueueRange.all, settings.LayerMask.value, renderingLayerMask); _filteringSettings = new FilteringSettings(RenderQueueRange.all, settings.LayerMask.value); if (!_stencilMaterial) { _stencilMaterial = CoreUtils.CreateEngineMaterial(ShaderName); } } public override void Execute(ScriptableRenderContext context, ref RenderingData renderingData) { DrawingSettings drawingSettings = CreateDrawingSettings( _shaderTags, ref renderingData, _settings.SortingCriteria ); drawingSettings.overrideMaterial = _stencilMaterial; drawingSettings.overrideMaterialPassIndex = 0; // TODO: Switch to this once mismatched markers bug is fixed. // CommandBuffer cmd = CommandBufferPool.Get(ProfilerTag); CommandBuffer cmd = CommandBufferPool.Get(); using (new ProfilingScope(cmd, profilingSampler)) { context.ExecuteCommandBuffer(cmd); cmd.Clear(); context.DrawRenderers(renderingData.cullResults, ref drawingSettings, ref _filteringSettings); } context.ExecuteCommandBuffer(cmd); CommandBufferPool.Release(cmd); } } This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -0,0 +1,23 @@ ## Usage 1. Create a new Unity game object layer for the objects you wish to outline. 2. In the ForwardRendererData asset of your URP asset, add the renderer feature named "Outline Feature" 3. Add a new element to the "Outline Settings" of the new outline feature. 4. Set the Layer Mask to the layer created in step 1. 5. Add the objects you wish to outline to the outline layer (this can be done at runtime in ECS by setting the layer of the RenderMesh). 6. Adjust the color and width of the outlines as desired in the renderer feature settings. ## Notes When I first added support for the Hyrbid Renderer, render layers were not working correctly. This required the use of game object layers to filter out the outlined objects. This might be fixed now, but I have not tested it yet. ## Credits - The technique for these wide outlines comes from [Ben Golus](https://bgolus.medium.com/) which is described and implemented in [this article](https://bgolus.medium.com/the-quest-for-very-wide-outlines-ba82ed442cd9). - [Alexander Ameye](https://alexanderameye.github.io/) created the renderer feature and shader modifications to make this work in URP, original shared [here](https://twitter.com/alexanderameye/status/1332286868222775298). - [Scott Daley](https://twitter.com/GravitonPunch) modified the shader to get it working with Unity ECS and the Hybrid Renderer as well as add support for multiple outline layers. ## Compatibility Test with: - Unity 2020.3.30f1 - URP 10.8.1 - Entities 0.50.0-preview.24 - Hybrid Renderer 0.50.0-preview.24