1+ using System . Collections . Generic ;
2+ using UnityEngine ;
3+ using UnityEngine . Rendering ;
4+ using UnityEngine . Rendering . Universal ;
5+
6+ namespace Outline . Feature
7+ {
8+ public class MultiSelectionOutlineFeature : ScriptableRendererFeature
9+ {
10+ public struct SelectionOutlineData
11+ {
12+ public int colorId ;
13+ public List < Renderer > renderers ;
14+ }
15+
16+ [ System . Serializable ]
17+ public class MultiSelectionOutlineSettings
18+ {
19+ public bool IsEnabled = true ;
20+ public RenderPassEvent WhenToInsert = RenderPassEvent . AfterRendering ;
21+ public LayerMask Filters ;
22+ public Material MaterialToBlit ;
23+ }
24+
25+ // MUST be named "settings" (lowercase) to be shown in the Render Features inspector
26+ public MultiSelectionOutlineSettings settings = new MultiSelectionOutlineSettings ( ) ;
27+
28+ public List < SelectionOutlineData > datas
29+ {
30+ get => m_MultiSelectionOutlineRenderPass . datas ;
31+ set => m_MultiSelectionOutlineRenderPass . datas = value ;
32+ }
33+
34+ public int paletteId
35+ {
36+ get => m_MultiSelectionOutlineRenderPass . paletteId ;
37+ set => m_MultiSelectionOutlineRenderPass . paletteId = value ;
38+ }
39+
40+ RenderTargetHandle m_RenderTextureHandle ;
41+ MultiSelectionOutlineRenderPass m_MultiSelectionOutlineRenderPass ;
42+
43+ static readonly int s_ColorPalette = Shader . PropertyToID ( "_ColorPalette" ) ;
44+
45+ public override void Create ( )
46+ {
47+ m_MultiSelectionOutlineRenderPass = new MultiSelectionOutlineRenderPass (
48+ "MulitiSelection Outline pass" ,
49+ settings . WhenToInsert ,
50+ settings . Filters ,
51+ settings . MaterialToBlit
52+ ) ;
53+ }
54+
55+ // called every frame once per camera
56+ public override void AddRenderPasses ( ScriptableRenderer renderer , ref RenderingData renderingData )
57+ {
58+ if ( ! settings . IsEnabled )
59+ {
60+ // we can do nothing this frame if we want
61+ return ;
62+ }
63+
64+ m_MultiSelectionOutlineRenderPass . source = renderer . cameraColorTarget ;
65+ renderer . EnqueuePass ( m_MultiSelectionOutlineRenderPass ) ;
66+ }
67+
68+ public void ChangePalette ( Texture2D palette )
69+ {
70+ settings . MaterialToBlit . SetTexture ( s_ColorPalette , palette ) ;
71+ }
72+ }
73+
74+ class MultiSelectionOutlineRenderPass : ScriptableRenderPass
75+ {
76+ public enum PassId
77+ {
78+ SelectionPassMask = 0 ,
79+ SelectionPassOutline ,
80+ SelectionPassBlur ,
81+ SelectionPassFinal
82+ } ;
83+
84+ static readonly int s_BlurDirection = Shader . PropertyToID ( "_BlurDirection" ) ;
85+ static readonly int s_Id = Shader . PropertyToID ( "_Id" ) ;
86+ static readonly int s_PaletteId = Shader . PropertyToID ( "_PaletteId" ) ;
87+
88+ // used to label this pass in Unity's Frame Debug utility
89+ string profilerTag ;
90+
91+ Material materialToBlit ;
92+
93+ RenderTargetHandle m_maskTexture ;
94+ RenderTargetHandle m_blurTexture ;
95+ RenderTargetHandle m_finalTexture ;
96+
97+ bool isPaletteDirty ;
98+ Texture2D colorPalette ;
99+
100+ public RenderTargetIdentifier source { get ; set ; }
101+
102+ public List < MultiSelectionOutlineFeature . SelectionOutlineData > datas { get ; set ; }
103+ public int paletteId { get ; set ; }
104+
105+ public MultiSelectionOutlineRenderPass ( string profilerTag ,
106+ RenderPassEvent renderPassEvent , int layerMask , Material materialToBlit )
107+ {
108+ this . profilerTag = profilerTag ;
109+ this . renderPassEvent = renderPassEvent ;
110+ this . materialToBlit = materialToBlit ;
111+
112+ m_maskTexture . Init ( "_MaskTexture" ) ;
113+ m_blurTexture . Init ( "_BlurTexture" ) ;
114+ m_finalTexture . Init ( "_FinalTexture" ) ;
115+
116+ datas = new List < MultiSelectionOutlineFeature . SelectionOutlineData > ( ) ;
117+ paletteId = 0 ;
118+ }
119+
120+ public override void Configure ( CommandBuffer cmd , RenderTextureDescriptor cameraTextureDescriptor )
121+ {
122+ // create a temporary render texture that matches the camera
123+ cmd . GetTemporaryRT ( m_maskTexture . id , cameraTextureDescriptor ) ;
124+ cmd . GetTemporaryRT ( m_blurTexture . id , cameraTextureDescriptor ) ;
125+
126+ var tempRTD = cameraTextureDescriptor ;
127+ tempRTD . colorFormat = RenderTextureFormat . ARGB32 ;
128+ cmd . GetTemporaryRT ( m_finalTexture . id , tempRTD ) ;
129+ }
130+
131+ public override void Execute ( ScriptableRenderContext context , ref RenderingData renderingData )
132+ {
133+ if ( datas . Count > 0 )
134+ {
135+ // fetch a command buffer to use
136+ CommandBuffer cmd = CommandBufferPool . Get ( profilerTag ) ;
137+ cmd . Clear ( ) ;
138+
139+ CoreUtils . SetRenderTarget ( cmd , m_finalTexture . Identifier ( ) , ClearFlag . All , Color . clear ) ;
140+
141+ foreach ( var data in datas )
142+ {
143+ CoreUtils . SetRenderTarget ( cmd , m_maskTexture . Identifier ( ) , ClearFlag . All , Color . clear ) ;
144+
145+ var removed = new List < int > ( ) ;
146+ for ( var i = 0 ; i < data . renderers . Count ; ++ i )
147+ {
148+ var renderer = data . renderers [ i ] ;
149+ if ( renderer == null )
150+ {
151+ removed . Add ( i ) ;
152+ continue ;
153+ }
154+
155+ cmd . DrawRenderer ( renderer , materialToBlit , 0 , ( int ) PassId . SelectionPassMask ) ;
156+ }
157+
158+ foreach ( var index in removed )
159+ data . renderers . RemoveAt ( index ) ;
160+
161+ cmd . SetGlobalColor ( s_BlurDirection , new Color ( 1 , 0 , 0 , 0 ) ) ;
162+ cmd . Blit ( m_maskTexture . id , m_blurTexture . id , materialToBlit , ( int ) PassId . SelectionPassBlur ) ;
163+ CheckStereoKeyword ( ref renderingData , ref cmd ) ;
164+
165+ cmd . SetGlobalColor ( s_BlurDirection , new Color ( 0 , 1 , 0 , 0 ) ) ;
166+ cmd . Blit ( m_blurTexture . id , m_maskTexture . id , materialToBlit , ( int ) PassId . SelectionPassBlur ) ;
167+ CheckStereoKeyword ( ref renderingData , ref cmd ) ;
168+
169+ cmd . SetGlobalFloat ( s_Id , data . colorId ) ;
170+ cmd . SetGlobalFloat ( s_PaletteId , paletteId ) ;
171+ cmd . Blit ( m_maskTexture . id , m_finalTexture . id , materialToBlit , ( int ) PassId . SelectionPassOutline ) ;
172+ CheckStereoKeyword ( ref renderingData , ref cmd ) ;
173+ }
174+
175+ cmd . Blit ( m_finalTexture . id , source , materialToBlit , ( int ) PassId . SelectionPassFinal ) ;
176+ CheckStereoKeyword ( ref renderingData , ref cmd ) ;
177+
178+ context . ExecuteCommandBuffer ( cmd ) ;
179+ cmd . Clear ( ) ;
180+
181+ CommandBufferPool . Release ( cmd ) ;
182+ }
183+ }
184+
185+ void CheckStereoKeyword ( ref RenderingData renderingData , ref CommandBuffer cmd )
186+ {
187+ // Add the following code snippet after calling cmd.Blit
188+ // in 2020.2+URP, cmd.Blit has a bug to turn off stereo shader keyword. Enable keyword again manually.
189+ if ( renderingData . cameraData . isStereoEnabled )
190+ {
191+ if ( SystemInfo . supportsMultiview )
192+ cmd . EnableShaderKeyword ( "STEREO_MULTIVIEW_ON" ) ;
193+ else
194+ cmd . EnableShaderKeyword ( "STEREO_INSTANCING_ON" ) ;
195+ }
196+ else
197+ {
198+ cmd . DisableShaderKeyword ( "STEREO_MULTIVIEW_ON" ) ;
199+ cmd . DisableShaderKeyword ( "STEREO_INSTANCING_ON" ) ;
200+ }
201+ }
202+
203+ public override void FrameCleanup ( CommandBuffer cmd )
204+ {
205+ cmd . ReleaseTemporaryRT ( m_maskTexture . id ) ;
206+ cmd . ReleaseTemporaryRT ( m_blurTexture . id ) ;
207+ cmd . ReleaseTemporaryRT ( m_finalTexture . id ) ;
208+ }
209+ }
210+ }
0 commit comments