2025-06-08 00:39:11 +09:00

317 lines
18 KiB
C#

using UnityEngine;
using UnityEditor;
using UnityEngine.Rendering.Universal;
using UnityEngine.Rendering;
namespace VolumetricFogAndMist2 {
[CustomEditor(typeof(VolumetricFogManager))]
public class VolumetricFogManagerEditor : Editor {
SerializedProperty sun, moon, includeTransparent, depthPeeling, transparentCullMode, includeSemiTransparent, alphaCutOff, semiTransparentCullMode, 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");
depthPeeling = serializedObject.FindProperty("depthPeeling");
transparentCullMode = serializedObject.FindProperty("transparentCullMode");
includeSemiTransparent = serializedObject.FindProperty("includeSemiTransparent");
alphaCutOff = serializedObject.FindProperty("alphaCutOff");
semiTransparentCullMode = serializedObject.FindProperty("semiTransparentCullMode");
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();
var pipe = GraphicsSettings.currentRenderPipeline 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.BeginHorizontal();
EditorGUILayout.PropertyField(flipDepthTexture);
if (flipDepthTexture.boolValue) {
GUILayout.Label("(Applies only in build)");
}
GUILayout.FlexibleSpace();
EditorGUILayout.EndHorizontal();
EditorGUILayout.PropertyField(mainManager);
EditorGUILayout.EndVertical();
EditorGUILayout.Separator();
EditorGUILayout.BeginVertical();
EditorGUILayout.BeginHorizontal();
EditorGUILayout.LabelField(new GUIContent("Custom Depth Pre-Pass", "Support for transparent or semi-transparent objects that need custom depth pass. Click help button for more info."), EditorStyles.boldLabel);
if (GUILayout.Button(new GUIContent("Help", "Open transparency support help page in browser"), GUILayout.Width(60))) {
Application.OpenURL("https://kronnect.com/guides/volumetric-fog-urp-special-features/#ftoc-heading-9");
}
EditorGUILayout.EndHorizontal();
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"));
EditorGUILayout.PropertyField(depthPeeling);
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.PropertyField(semiTransparentCullMode, new GUIContent("Cull Mode"));
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 || (includeTransparent.intValue != 0 && depthPeeling.boolValue)) {
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/depth peeling). 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<NoiseGenerator>("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));
}
}
}