using UnityEngine; using UnityEditor; using UnityEngine.Rendering.Universal; namespace VolumetricFogAndMist2 { [CustomEditor(typeof(VolumetricFogManager))] public class VolumetricFogManagerEditor : Editor { SerializedProperty sun, moon, includeTransparent, transparentCullMode, includeSemiTransparent, alphaCutOff, flipDepthTexture, mainManager; SerializedProperty scattering, scatteringThreshold, scatteringIntensity, scatteringAbsorption, scatteringTint, scatteringHighQuality; SerializedProperty downscaling, downscalingEdgeDepthThreshold, blurPasses, blurDownscaling, blurSpread, blurHDR, blurEdgePreserve, blurEdgeDepthThreshold, ditherStrength; bool toggleOptimizeBuild; VolumetricFogShaderOptions shaderAdvancedOptionsInfo; int maxIterations; private void OnEnable() { sun = serializedObject.FindProperty("sun"); moon = serializedObject.FindProperty("moon"); includeTransparent = serializedObject.FindProperty("includeTransparent"); transparentCullMode = serializedObject.FindProperty("transparentCullMode"); includeSemiTransparent = serializedObject.FindProperty("includeSemiTransparent"); alphaCutOff = serializedObject.FindProperty("alphaCutOff"); flipDepthTexture = serializedObject.FindProperty("flipDepthTexture"); mainManager = serializedObject.FindProperty("mainManager"); scattering = serializedObject.FindProperty("scattering"); scatteringThreshold = serializedObject.FindProperty("scatteringThreshold"); scatteringIntensity = serializedObject.FindProperty("scatteringIntensity"); scatteringAbsorption = serializedObject.FindProperty("scatteringAbsorption"); scatteringTint = serializedObject.FindProperty("scatteringTint"); scatteringHighQuality = serializedObject.FindProperty("scatteringHighQuality"); downscaling = serializedObject.FindProperty("downscaling"); downscalingEdgeDepthThreshold = serializedObject.FindProperty("downscalingEdgeDepthThreshold"); blurPasses = serializedObject.FindProperty("blurPasses"); blurDownscaling = serializedObject.FindProperty("blurDownscaling"); blurSpread = serializedObject.FindProperty("blurSpread"); blurHDR = serializedObject.FindProperty("blurHDR"); blurEdgePreserve = serializedObject.FindProperty("blurEdgePreserve"); blurEdgeDepthThreshold = serializedObject.FindProperty("blurEdgeDepthThreshold"); ditherStrength = serializedObject.FindProperty("ditherStrength"); ScanAdvancedOptions(); } public override void OnInspectorGUI() { EditorGUILayout.Separator(); UniversalRenderPipelineAsset pipe = UnityEngine.Rendering.GraphicsSettings.renderPipelineAsset as UniversalRenderPipelineAsset; if (pipe == null) { EditorGUILayout.HelpBox("Please assign the Universal Rendering Pipeline asset (go to Project Settings -> Graphics). You can use the UniversalRenderPipelineAsset included in the demo folder or create a new pipeline asset (check documentation for step by step setup).", MessageType.Error); return; } if (QualitySettings.renderPipeline != null) { pipe = QualitySettings.renderPipeline as UniversalRenderPipelineAsset; } if (!pipe.supportsCameraDepthTexture) { EditorGUILayout.HelpBox("Depth Texture option is required in Universal Rendering Pipeline asset!", MessageType.Error); if (GUILayout.Button("Go to Universal Rendering Pipeline Asset")) { Selection.activeObject = pipe; } EditorGUILayout.Separator(); GUI.enabled = false; } if (VolumetricFogEditor.lastEditingFog != null) { if (GUILayout.Button("<< Back To Last Volumetric Fog")) { Selection.SetActiveObjectWithContext(VolumetricFogEditor.lastEditingFog, null); GUIUtility.ExitGUI(); } } serializedObject.Update(); EditorGUILayout.BeginVertical(); EditorGUILayout.LabelField("General Settings", EditorStyles.boldLabel); EditorGUILayout.PropertyField(sun); EditorGUILayout.PropertyField(moon); EditorGUILayout.PropertyField(flipDepthTexture); EditorGUILayout.PropertyField(mainManager); EditorGUILayout.EndVertical(); EditorGUILayout.Separator(); EditorGUILayout.BeginVertical(); EditorGUILayout.LabelField(new GUIContent("Custom Depth Pre-Pass", "Support for transparent or semi-transparent objects or needed custom depth pass."), EditorStyles.boldLabel); int transparentLayerMask = includeTransparent.intValue; DrawSectionField(includeTransparent, new GUIContent("Transparent Objects", "Specify which layers contain transparent objects that should be covered by fog"), transparentLayerMask != 0); if (transparentLayerMask != 0) { EditorGUI.indentLevel++; EditorGUILayout.PropertyField(transparentCullMode, new GUIContent("Cull Mode")); EditorGUI.indentLevel--; } int includeSemiTransparentMask = includeSemiTransparent.intValue; DrawSectionField(includeSemiTransparent, new GUIContent("Alpha Clipping", "Specify which smi-transparent objects (cutout materials) should be covered by fog."), includeSemiTransparentMask != 0); if (includeSemiTransparentMask != 0) { EditorGUI.indentLevel++; EditorGUILayout.PropertyField(alphaCutOff, new GUIContent("Alpha CutOff"), true); EditorGUILayout.BeginHorizontal(); EditorGUILayout.LabelField(new GUIContent(""), GUIContent.none); if (GUILayout.Button("Refresh")) { DepthRenderPrePassFeature.DepthRenderPass.FindAlphaClippingRenderers(); } EditorGUILayout.EndHorizontal(); EditorGUI.indentLevel--; } if (includeTransparent.intValue != 0 || includeSemiTransparent.intValue != 0) { if (!DepthRenderPrePassFeature.installed) { EditorGUILayout.HelpBox("Transparent option requires 'DepthRendererPrePass Feature' added to the Universal Rendering Pipeline asset. Check the documentation for instructions.", MessageType.Warning); if (pipe != null && GUILayout.Button("Show Pipeline Asset")) Selection.activeObject = pipe; } if ((includeTransparent.intValue & includeSemiTransparent.intValue) != 0) { EditorGUILayout.HelpBox("The options 'Transparent Objects' and 'Alpha Clipping' should not overlap and include same objects. Make sure the specified layers are different in each option.", MessageType.Warning); } } else if (DepthRenderPrePassFeature.installed) { EditorGUILayout.HelpBox("No transparent objects included. Remove 'DepthRendererPrePass Feature' from the Universal Rendering Pipeline asset to save performance.", MessageType.Warning); if (pipe != null && GUILayout.Button("Show Pipeline Asset")) Selection.activeObject = pipe; } EditorGUILayout.EndVertical(); EditorGUILayout.Separator(); EditorGUILayout.BeginVertical(); EditorGUILayout.LabelField(new GUIContent("Final Composition", "Support for off-screen rendering and composition to screen target. Allows optimizations like downsampling."), EditorStyles.boldLabel); EditorGUI.BeginChangeCheck(); DrawSectionField(scattering, new GUIContent(scattering.displayName, scattering.tooltip), scattering.floatValue > 0); if (scattering.floatValue > 0) { EditorGUI.indentLevel++; EditorGUILayout.PropertyField(scatteringIntensity, new GUIContent("Brightness")); EditorGUILayout.PropertyField(scatteringThreshold, new GUIContent("Brightness Threshold")); EditorGUILayout.PropertyField(scatteringAbsorption, new GUIContent("Absorption")); EditorGUILayout.PropertyField(scatteringTint, new GUIContent("Tint Color")); EditorGUILayout.PropertyField(scatteringHighQuality, new GUIContent("High Quality")); EditorGUI.indentLevel--; } DrawSectionField(downscaling, new GUIContent(downscaling.displayName, downscaling.tooltip), downscaling.floatValue > 1); if (downscaling.floatValue > 1f) { EditorGUI.indentLevel++; EditorGUILayout.PropertyField(downscalingEdgeDepthThreshold, new GUIContent("Edge Threshold")); EditorGUI.indentLevel--; } DrawSectionField(blurPasses, new GUIContent(blurPasses.displayName, blurPasses.tooltip), blurPasses.intValue > 0); if (blurPasses.intValue > 0) { EditorGUI.indentLevel++; EditorGUILayout.PropertyField(blurDownscaling, new GUIContent("Downscaling")); EditorGUILayout.PropertyField(blurSpread, new GUIContent("Spread")); EditorGUILayout.PropertyField(blurEdgePreserve, new GUIContent("Preserve Edges")); if (blurEdgePreserve.boolValue) { EditorGUILayout.PropertyField(blurEdgeDepthThreshold, new GUIContent("Edge Threshold")); } EditorGUI.indentLevel--; } EditorGUILayout.PropertyField(blurHDR, new GUIContent("HDR")); if (EditorGUI.EndChangeCheck()) { EditorApplication.delayCall += () => UnityEditorInternal.InternalEditorUtility.RepaintAllViews(); } EditorGUILayout.PropertyField(ditherStrength); if (blurPasses.intValue > 0 || downscaling.floatValue > 1 || scattering.floatValue > 0) { if (!VolumetricFogRenderFeature.installed) { EditorGUILayout.HelpBox("These options require 'Volumetric Fog Render Feature' added to the Universal Rendering Pipeline asset. Check the documentation for instructions.", MessageType.Warning); if (pipe != null && GUILayout.Button("Show Pipeline Asset")) Selection.activeObject = pipe; } EditorGUILayout.HelpBox("When downscaling, blur or scattering options are enabled, fog volumes ignore render queue value. Select the render pass event in the Volumetric Fog Render Feature.", MessageType.Info); } else if (VolumetricFogRenderFeature.installed) { EditorGUILayout.HelpBox("No final composition options used (downscaling/blur/scattering). Consider removing 'Volumetric Fog Render Feature' from the Universal Rendering Pipeline asset to save performance.", MessageType.Warning); if (pipe != null && GUILayout.Button("Show Pipeline Asset")) Selection.activeObject = pipe; } EditorGUILayout.EndVertical(); EditorGUILayout.Separator(); bool shaderOptionsOpen = toggleOptimizeBuild; if (shaderOptionsOpen) { EditorGUILayout.BeginVertical(GUI.skin.box); } if (GUILayout.Button(toggleOptimizeBuild ? "Hide Shader Options" : "Shader Options", GUILayout.Width(150))) { toggleOptimizeBuild = !toggleOptimizeBuild; } if (toggleOptimizeBuild && shaderAdvancedOptionsInfo != null) { int optionsCount = shaderAdvancedOptionsInfo.options.Length; for (int k = 0; k < optionsCount; k++) { ShaderAdvancedOption option = shaderAdvancedOptionsInfo.options[k]; if (option.hasValue) continue; EditorGUILayout.BeginHorizontal(); EditorGUILayout.LabelField("", GUILayout.Width(10)); bool prevState = option.enabled; bool newState = EditorGUILayout.Toggle(prevState, GUILayout.Width(18)); if (prevState != newState) { shaderAdvancedOptionsInfo.options[k].enabled = newState; shaderAdvancedOptionsInfo.pendingChanges = true; GUIUtility.ExitGUI(); return; } EditorGUILayout.LabelField(option.name); EditorGUILayout.EndHorizontal(); EditorGUILayout.BeginHorizontal(); EditorGUILayout.LabelField("", GUILayout.Width(10)); EditorGUILayout.LabelField("", GUILayout.Width(18)); GUIStyle wrapStyle = new GUIStyle(GUI.skin.label); wrapStyle.wordWrap = true; EditorGUILayout.LabelField(option.description, wrapStyle); EditorGUILayout.EndHorizontal(); } EditorGUILayout.BeginHorizontal(); EditorGUILayout.LabelField("", GUILayout.Width(10)); EditorGUI.BeginChangeCheck(); float prevLabelWidth = EditorGUIUtility.labelWidth; EditorGUIUtility.labelWidth = 100; maxIterations = EditorGUILayout.IntField(new GUIContent("Max Iterations", "The maximum number of raymarching steps."), maxIterations, GUILayout.Width(175)); if (EditorGUI.EndChangeCheck()) { shaderAdvancedOptionsInfo.pendingChanges = true; } EditorGUIUtility.labelWidth = prevLabelWidth; EditorGUILayout.EndHorizontal(); EditorGUILayout.Separator(); EditorGUILayout.BeginHorizontal(); if (GUILayout.Button("Refresh", GUILayout.Width(60))) { ScanAdvancedOptions(); GUIUtility.ExitGUI(); return; } if (!shaderAdvancedOptionsInfo.pendingChanges) GUI.enabled = false; if (GUILayout.Button("Save Changes", GUILayout.Width(110))) { shaderAdvancedOptionsInfo.SetOptionValue("MAX_ITERATIONS", maxIterations); shaderAdvancedOptionsInfo.UpdateAdvancedOptionsFile(); GUIUtility.ExitGUI(); } GUI.enabled = true; EditorGUILayout.EndHorizontal(); } if (shaderOptionsOpen) { EditorGUILayout.EndVertical(); } EditorGUILayout.Separator(); EditorGUILayout.BeginVertical(); EditorGUILayout.LabelField("Managers", EditorStyles.boldLabel); EditorGUILayout.BeginHorizontal(); GUILayout.Label("Point Light Manager", GUILayout.Width(EditorGUIUtility.labelWidth)); if (GUILayout.Button("Open", GUILayout.Width(150))) { Selection.activeGameObject = VolumetricFogManager.pointLightManager.gameObject; } EditorGUILayout.EndHorizontal(); EditorGUILayout.BeginHorizontal(); GUILayout.Label("Fog Void Manager", GUILayout.Width(EditorGUIUtility.labelWidth)); if (GUILayout.Button("Open", GUILayout.Width(150))) { Selection.activeGameObject = VolumetricFogManager.fogVoidManager.gameObject; } EditorGUILayout.EndHorizontal(); EditorGUILayout.EndVertical(); EditorGUILayout.Separator(); EditorGUILayout.BeginVertical(); EditorGUILayout.LabelField("Tools", EditorStyles.boldLabel); EditorGUILayout.BeginHorizontal(); GUILayout.Label("Noise Generator", GUILayout.Width(EditorGUIUtility.labelWidth)); if (GUILayout.Button("Open", GUILayout.Width(150))) { NoiseGenerator window = EditorWindow.GetWindow("Noise Generator", true); window.minSize = new Vector2(400, 400); window.Show(); } EditorGUILayout.EndHorizontal(); EditorGUILayout.EndVertical(); serializedObject.ApplyModifiedProperties(); } void ScanAdvancedOptions() { if (shaderAdvancedOptionsInfo == null) { shaderAdvancedOptionsInfo = new VolumetricFogShaderOptions(); } shaderAdvancedOptionsInfo.ReadOptions(); maxIterations = shaderAdvancedOptionsInfo.GetOptionValue("MAX_ITERATIONS"); } void DrawSectionField(SerializedProperty property, GUIContent content, bool active) { EditorGUILayout.PropertyField(property, new GUIContent(active ? content.text + " •" : content.text, content.tooltip)); } } }