2 using System
.Collections
.Generic
;
5 using UnityEngine
.Rendering
.PostProcessing
;
6 using UnityEditorInternal
;
9 namespace UnityEditor
.Rendering
.PostProcessing
11 using SerializedBundleRef
= PostProcessLayer
.SerializedBundleRef
;
12 using EXRFlags
= Texture2D
.EXRFlags
;
14 [CanEditMultipleObjects
, CustomEditor(typeof(PostProcessLayer
))]
15 sealed class PostProcessLayerEditor
: BaseEditor
<PostProcessLayer
>
17 SerializedProperty m_StopNaNPropagation
;
18 #pragma warning disable 414
19 SerializedProperty m_DirectToCameraTarget
;
20 #pragma warning restore 414
21 SerializedProperty m_VolumeTrigger
;
22 SerializedProperty m_VolumeLayer
;
24 SerializedProperty m_AntialiasingMode
;
25 SerializedProperty m_TaaJitterSpread
;
26 SerializedProperty m_TaaSharpness
;
27 SerializedProperty m_TaaStationaryBlending
;
28 SerializedProperty m_TaaMotionBlending
;
29 SerializedProperty m_SmaaQuality
;
30 SerializedProperty m_FxaaFastMode
;
31 SerializedProperty m_FxaaKeepAlpha
;
33 SerializedProperty m_FogEnabled
;
34 SerializedProperty m_FogExcludeSkybox
;
36 SerializedProperty m_ShowToolkit
;
37 SerializedProperty m_ShowCustomSorter
;
39 Dictionary
<PostProcessEvent
, ReorderableList
> m_CustomLists
;
41 static GUIContent
[] s_AntialiasingMethodNames
=
43 new GUIContent("No Anti-aliasing"),
44 new GUIContent("Fast Approximate Anti-aliasing (FXAA)"),
45 new GUIContent("Subpixel Morphological Anti-aliasing (SMAA)"),
46 new GUIContent("Temporal Anti-aliasing (TAA)")
53 BreakBeforeColorGradingLinear
,
54 BreakBeforeColorGradingLog
59 m_StopNaNPropagation
= FindProperty(x
=> x
.stopNaNPropagation
);
60 m_DirectToCameraTarget
= FindProperty(x
=> x
.finalBlitToCameraTarget
);
61 m_VolumeTrigger
= FindProperty(x
=> x
.volumeTrigger
);
62 m_VolumeLayer
= FindProperty(x
=> x
.volumeLayer
);
64 m_AntialiasingMode
= FindProperty(x
=> x
.antialiasingMode
);
65 m_TaaJitterSpread
= FindProperty(x
=> x
.temporalAntialiasing
.jitterSpread
);
66 m_TaaSharpness
= FindProperty(x
=> x
.temporalAntialiasing
.sharpness
);
67 m_TaaStationaryBlending
= FindProperty(x
=> x
.temporalAntialiasing
.stationaryBlending
);
68 m_TaaMotionBlending
= FindProperty(x
=> x
.temporalAntialiasing
.motionBlending
);
69 m_SmaaQuality
= FindProperty(x
=> x
.subpixelMorphologicalAntialiasing
.quality
);
70 m_FxaaFastMode
= FindProperty(x
=> x
.fastApproximateAntialiasing
.fastMode
);
71 m_FxaaKeepAlpha
= FindProperty(x
=> x
.fastApproximateAntialiasing
.keepAlpha
);
73 m_FogEnabled
= FindProperty(x
=> x
.fog
.enabled
);
74 m_FogExcludeSkybox
= FindProperty(x
=> x
.fog
.excludeSkybox
);
76 m_ShowToolkit
= serializedObject
.FindProperty("m_ShowToolkit");
77 m_ShowCustomSorter
= serializedObject
.FindProperty("m_ShowCustomSorter");
85 public override void OnInspectorGUI()
87 serializedObject
.Update();
89 var camera
= m_Target
.GetComponent
<Camera
>();
91 #if !UNITY_2017_2_OR_NEWER
92 if (RuntimeUtilities
.isSinglePassStereoSelected
)
93 EditorGUILayout
.HelpBox("Unity 2017.2+ required for full Single-pass stereo rendering support.", MessageType
.Warning
);
100 EditorGUILayout
.PropertyField(m_StopNaNPropagation
, EditorUtilities
.GetContent("Stop NaN Propagation|Automatically replaces NaN/Inf in shaders by a black pixel to avoid breaking some effects. This will slightly affect performances and should only be used if you experience NaN issues that you can't fix. Has no effect on GLES2 platforms."));
102 #if UNITY_2019_1_OR_NEWER
103 if (!RuntimeUtilities
.scriptableRenderPipelineActive
)
104 EditorGUILayout
.PropertyField(m_DirectToCameraTarget
, EditorUtilities
.GetContent("Directly to Camera Target|Use the final blit to the camera render target for postprocessing. This has less overhead but breaks compatibility with legacy image effect that use OnRenderImage."));
107 EditorGUILayout
.Space();
110 DoCustomEffectSorter();
112 EditorUtilities
.DrawSplitter();
113 EditorGUILayout
.Space();
115 serializedObject
.ApplyModifiedProperties();
118 void DoVolumeBlending()
120 EditorGUILayout
.LabelField(EditorUtilities
.GetContent("Volume blending"), EditorStyles
.boldLabel
);
121 EditorGUI
.indentLevel
++;
123 // The layout system sort of break alignement when mixing inspector fields with
124 // custom layouted fields, do the layout manually instead
125 var indentOffset
= EditorGUI
.indentLevel
* 15f
;
126 var lineRect
= GUILayoutUtility
.GetRect(1, EditorGUIUtility
.singleLineHeight
);
127 var labelRect
= new Rect(lineRect
.x
, lineRect
.y
, EditorGUIUtility
.labelWidth
- indentOffset
, lineRect
.height
);
128 var fieldRect
= new Rect(labelRect
.xMax
, lineRect
.y
, lineRect
.width
- labelRect
.width
- 60f
, lineRect
.height
);
129 var buttonRect
= new Rect(fieldRect
.xMax
, lineRect
.y
, 60f
, lineRect
.height
);
131 EditorGUI
.PrefixLabel(labelRect
, EditorUtilities
.GetContent("Trigger|A transform that will act as a trigger for volume blending."));
132 m_VolumeTrigger
.objectReferenceValue
= (Transform
)EditorGUI
.ObjectField(fieldRect
, m_VolumeTrigger
.objectReferenceValue
, typeof(Transform
), true);
133 if (GUI
.Button(buttonRect
, EditorUtilities
.GetContent("This|Assigns the current GameObject as a trigger."), EditorStyles
.miniButton
))
134 m_VolumeTrigger
.objectReferenceValue
= m_Target
.transform
;
136 if (m_VolumeTrigger
.objectReferenceValue
== null)
137 EditorGUILayout
.HelpBox("No trigger has been set, the camera will only be affected by global volumes.", MessageType
.Info
);
139 EditorGUILayout
.PropertyField(m_VolumeLayer
, EditorUtilities
.GetContent("Layer|This camera will only be affected by volumes in the selected scene-layers."));
141 int mask
= m_VolumeLayer
.intValue
;
143 EditorGUILayout
.HelpBox("No layer has been set, the trigger will never be affected by volumes.", MessageType
.Warning
);
144 else if (mask
== -1 || ((mask
& 1) != 0))
145 EditorGUILayout
.HelpBox("Do not use \"Everything\" or \"Default\" as a layer mask as it will slow down the volume blending process! Put post-processing volumes in their own dedicated layer for best performances.", MessageType
.Warning
);
147 EditorGUI
.indentLevel
--;
149 EditorGUILayout
.Space();
152 void DoAntialiasing()
154 EditorGUILayout
.LabelField(EditorUtilities
.GetContent("Anti-aliasing"), EditorStyles
.boldLabel
);
155 EditorGUI
.indentLevel
++;
157 m_AntialiasingMode
.intValue
= EditorGUILayout
.Popup(EditorUtilities
.GetContent("Mode|The anti-aliasing method to use. FXAA is fast but low quality. SMAA works well for non-HDR scenes. TAA is a bit slower but higher quality and works well with HDR."), m_AntialiasingMode
.intValue
, s_AntialiasingMethodNames
);
159 if (m_AntialiasingMode
.intValue
== (int)PostProcessLayer
.Antialiasing
.TemporalAntialiasing
)
161 #if !UNITY_2017_3_OR_NEWER
162 if (RuntimeUtilities
.isSinglePassStereoSelected
)
163 EditorGUILayout
.HelpBox("TAA requires Unity 2017.3+ for Single-pass stereo rendering support.", MessageType
.Warning
);
166 EditorGUILayout
.PropertyField(m_TaaJitterSpread
);
167 EditorGUILayout
.PropertyField(m_TaaStationaryBlending
);
168 EditorGUILayout
.PropertyField(m_TaaMotionBlending
);
169 EditorGUILayout
.PropertyField(m_TaaSharpness
);
171 else if (m_AntialiasingMode
.intValue
== (int)PostProcessLayer
.Antialiasing
.SubpixelMorphologicalAntialiasing
)
173 if (RuntimeUtilities
.isSinglePassStereoSelected
)
174 EditorGUILayout
.HelpBox("SMAA doesn't work with Single-pass stereo rendering.", MessageType
.Warning
);
176 EditorGUILayout
.PropertyField(m_SmaaQuality
);
178 if (m_SmaaQuality
.intValue
!= (int)SubpixelMorphologicalAntialiasing
.Quality
.Low
&& EditorUtilities
.isTargetingConsolesOrMobiles
)
179 EditorGUILayout
.HelpBox("For performance reasons it is recommended to use Low Quality on mobile and console platforms.", MessageType
.Warning
);
181 else if (m_AntialiasingMode
.intValue
== (int)PostProcessLayer
.Antialiasing
.FastApproximateAntialiasing
)
183 EditorGUILayout
.PropertyField(m_FxaaFastMode
);
184 EditorGUILayout
.PropertyField(m_FxaaKeepAlpha
);
186 if (!m_FxaaFastMode
.boolValue
&& EditorUtilities
.isTargetingConsolesOrMobiles
)
187 EditorGUILayout
.HelpBox("For performance reasons it is recommended to use Fast Mode on mobile and console platforms.", MessageType
.Warning
);
190 EditorGUI
.indentLevel
--;
192 EditorGUILayout
.Space();
195 void DoFog(Camera camera
)
197 if (camera
== null || camera
.actualRenderingPath
!= RenderingPath
.DeferredShading
)
200 EditorGUILayout
.LabelField(EditorUtilities
.GetContent("Deferred Fog"), EditorStyles
.boldLabel
);
201 EditorGUI
.indentLevel
++;
203 EditorGUILayout
.PropertyField(m_FogEnabled
);
205 if (m_FogEnabled
.boolValue
)
207 EditorGUILayout
.PropertyField(m_FogExcludeSkybox
);
208 EditorGUILayout
.HelpBox("This adds fog compatibility to the deferred rendering path; actual fog settings should be set in the Lighting panel.", MessageType
.Info
);
211 EditorGUI
.indentLevel
--;
213 EditorGUILayout
.Space();
218 EditorUtilities
.DrawSplitter();
219 m_ShowToolkit
.boolValue
= EditorUtilities
.DrawHeader("Toolkit", m_ShowToolkit
.boolValue
);
221 if (m_ShowToolkit
.boolValue
)
225 if (GUILayout
.Button(EditorUtilities
.GetContent("Export frame to EXR..."), EditorStyles
.miniButton
))
227 var menu
= new GenericMenu();
228 menu
.AddItem(EditorUtilities
.GetContent("Full Frame (as displayed)"), false, () => ExportFrameToExr(ExportMode
.FullFrame
));
229 menu
.AddItem(EditorUtilities
.GetContent("Disable post-processing"), false, () => ExportFrameToExr(ExportMode
.DisablePost
));
230 menu
.AddItem(EditorUtilities
.GetContent("Break before Color Grading (Linear)"), false, () => ExportFrameToExr(ExportMode
.BreakBeforeColorGradingLinear
));
231 menu
.AddItem(EditorUtilities
.GetContent("Break before Color Grading (Log)"), false, () => ExportFrameToExr(ExportMode
.BreakBeforeColorGradingLog
));
232 menu
.ShowAsContext();
235 if (GUILayout
.Button(EditorUtilities
.GetContent("Select all layer volumes|Selects all the volumes that will influence this layer."), EditorStyles
.miniButton
))
237 var volumes
= RuntimeUtilities
.GetAllSceneObjects
<PostProcessVolume
>()
238 .Where(x
=> (m_VolumeLayer
.intValue
& (1 << x
.gameObject
.layer
)) != 0)
239 .Select(x
=> x
.gameObject
)
240 .Cast
<UnityEngine
.Object
>()
243 if (volumes
.Length
> 0)
244 Selection
.objects
= volumes
;
247 if (GUILayout
.Button(EditorUtilities
.GetContent("Select all active volumes|Selects all volumes currently affecting the layer."), EditorStyles
.miniButton
))
249 var volumes
= new List
<PostProcessVolume
>();
250 PostProcessManager
.instance
.GetActiveVolumes(m_Target
, volumes
);
252 if (volumes
.Count
> 0)
254 Selection
.objects
= volumes
255 .Select(x
=> x
.gameObject
)
256 .Cast
<UnityEngine
.Object
>()
265 void DoCustomEffectSorter()
267 EditorUtilities
.DrawSplitter();
268 m_ShowCustomSorter
.boolValue
= EditorUtilities
.DrawHeader("Custom Effect Sorting", m_ShowCustomSorter
.boolValue
);
270 if (m_ShowCustomSorter
.boolValue
)
272 bool isInPrefab
= false;
274 // Init lists if needed
275 if (m_CustomLists
== null)
277 // In some cases the editor will refresh before components which means
278 // components might not have been fully initialized yet. In this case we also
279 // need to make sure that we're not in a prefab as sorteBundles isn't a
280 // serializable object and won't exist until put on a scene.
281 if (m_Target
.sortedBundles
== null)
283 isInPrefab
= string.IsNullOrEmpty(m_Target
.gameObject
.scene
.name
);
287 // sortedBundles will be initialized and ready to use on the next frame
293 // Create a reorderable list for each injection event
294 m_CustomLists
= new Dictionary
<PostProcessEvent
, ReorderableList
>();
295 foreach (var evt
in Enum
.GetValues(typeof(PostProcessEvent
)).Cast
<PostProcessEvent
>())
297 var bundles
= m_Target
.sortedBundles
[evt
];
298 var listName
= ObjectNames
.NicifyVariableName(evt
.ToString());
300 var list
= new ReorderableList(bundles
, typeof(SerializedBundleRef
), true, true, false, false);
302 list
.drawHeaderCallback
= (rect
) =>
304 EditorGUI
.LabelField(rect
, listName
);
307 list
.drawElementCallback
= (rect
, index
, isActive
, isFocused
) =>
309 var sbr
= (SerializedBundleRef
)list
.list
[index
];
310 EditorGUI
.LabelField(rect
, sbr
.bundle
.attribute
.menuItem
);
313 list
.onReorderCallback
= (l
) =>
315 EditorUtility
.SetDirty(m_Target
);
318 m_CustomLists
.Add(evt
, list
);
327 EditorGUILayout
.HelpBox("Not supported in prefabs.", MessageType
.Info
);
332 bool anyList
= false;
333 if (m_CustomLists
!= null)
335 foreach (var kvp
in m_CustomLists
)
337 var list
= kvp
.Value
;
339 // Skip empty lists to avoid polluting the inspector
350 EditorGUILayout
.HelpBox("No custom effect loaded.", MessageType
.Info
);
356 void ExportFrameToExr(ExportMode mode
)
358 string path
= EditorUtility
.SaveFilePanel("Export EXR...", "", "Frame", "exr");
360 if (string.IsNullOrEmpty(path
))
363 EditorUtility
.DisplayProgressBar("Export EXR", "Rendering...", 0f
);
365 var camera
= m_Target
.GetComponent
<Camera
>();
366 var w
= camera
.pixelWidth
;
367 var h
= camera
.pixelHeight
;
369 var texOut
= new Texture2D(w
, h
, TextureFormat
.RGBAFloat
, false, true);
370 var target
= RenderTexture
.GetTemporary(w
, h
, 24, RenderTextureFormat
.ARGBFloat
, RenderTextureReadWrite
.Linear
);
372 var lastActive
= RenderTexture
.active
;
373 var lastTargetSet
= camera
.targetTexture
;
374 var lastPostFXState
= m_Target
.enabled
;
375 var lastBreakColorGradingState
= m_Target
.breakBeforeColorGrading
;
377 if (mode
== ExportMode
.DisablePost
)
378 m_Target
.enabled
= false;
379 else if (mode
== ExportMode
.BreakBeforeColorGradingLinear
|| mode
== ExportMode
.BreakBeforeColorGradingLog
)
380 m_Target
.breakBeforeColorGrading
= true;
382 camera
.targetTexture
= target
;
384 camera
.targetTexture
= lastTargetSet
;
386 EditorUtility
.DisplayProgressBar("Export EXR", "Reading...", 0.25f
);
388 m_Target
.enabled
= lastPostFXState
;
389 m_Target
.breakBeforeColorGrading
= lastBreakColorGradingState
;
391 if (mode
== ExportMode
.BreakBeforeColorGradingLog
)
394 var material
= new Material(Shader
.Find("Hidden/PostProcessing/Editor/ConvertToLog"));
395 var newTarget
= RenderTexture
.GetTemporary(w
, h
, 0, RenderTextureFormat
.ARGBFloat
, RenderTextureReadWrite
.Linear
);
396 Graphics
.Blit(target
, newTarget
, material
, 0);
397 RenderTexture
.ReleaseTemporary(target
);
398 DestroyImmediate(material
);
402 RenderTexture
.active
= target
;
403 texOut
.ReadPixels(new Rect(0, 0, w
, h
), 0, 0);
405 RenderTexture
.active
= lastActive
;
407 EditorUtility
.DisplayProgressBar("Export EXR", "Encoding...", 0.5f
);
409 var bytes
= texOut
.EncodeToEXR(EXRFlags
.OutputAsFloat
| EXRFlags
.CompressZIP
);
411 EditorUtility
.DisplayProgressBar("Export EXR", "Saving...", 0.75f
);
413 File
.WriteAllBytes(path
, bytes
);
415 EditorUtility
.ClearProgressBar();
416 AssetDatabase
.Refresh();
418 RenderTexture
.ReleaseTemporary(target
);
419 DestroyImmediate(texOut
);