You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
470 lines
17 KiB
470 lines
17 KiB
using System; |
|
using System.Collections.Generic; |
|
using UnityEngine.Rendering; |
|
|
|
namespace UnityEngine.PostProcessing |
|
{ |
|
using DebugMode = BuiltinDebugViewsModel.Mode; |
|
|
|
#if UNITY_5_4_OR_NEWER |
|
[ImageEffectAllowedInSceneView] |
|
#endif |
|
[RequireComponent(typeof(Camera)), DisallowMultipleComponent, ExecuteInEditMode] |
|
[AddComponentMenu("Effects/Post-Processing Behaviour", -1)] |
|
public class PostProcessingBehaviour : MonoBehaviour |
|
{ |
|
// Inspector fields |
|
public PostProcessingProfile profile; |
|
|
|
public Func<Vector2, Matrix4x4> jitteredMatrixFunc; |
|
|
|
// Internal helpers |
|
Dictionary<Type, KeyValuePair<CameraEvent, CommandBuffer>> m_CommandBuffers; |
|
List<PostProcessingComponentBase> m_Components; |
|
Dictionary<PostProcessingComponentBase, bool> m_ComponentStates; |
|
|
|
MaterialFactory m_MaterialFactory; |
|
RenderTextureFactory m_RenderTextureFactory; |
|
PostProcessingContext m_Context; |
|
Camera m_Camera; |
|
PostProcessingProfile m_PreviousProfile; |
|
|
|
bool m_RenderingInSceneView = false; |
|
|
|
// Effect components |
|
BuiltinDebugViewsComponent m_DebugViews; |
|
AmbientOcclusionComponent m_AmbientOcclusion; |
|
ScreenSpaceReflectionComponent m_ScreenSpaceReflection; |
|
FogComponent m_FogComponent; |
|
MotionBlurComponent m_MotionBlur; |
|
TaaComponent m_Taa; |
|
EyeAdaptationComponent m_EyeAdaptation; |
|
DepthOfFieldComponent m_DepthOfField; |
|
BloomComponent m_Bloom; |
|
ChromaticAberrationComponent m_ChromaticAberration; |
|
ColorGradingComponent m_ColorGrading; |
|
UserLutComponent m_UserLut; |
|
GrainComponent m_Grain; |
|
VignetteComponent m_Vignette; |
|
DitheringComponent m_Dithering; |
|
FxaaComponent m_Fxaa; |
|
|
|
void OnEnable() |
|
{ |
|
m_CommandBuffers = new Dictionary<Type, KeyValuePair<CameraEvent, CommandBuffer>>(); |
|
m_MaterialFactory = new MaterialFactory(); |
|
m_RenderTextureFactory = new RenderTextureFactory(); |
|
m_Context = new PostProcessingContext(); |
|
|
|
// Keep a list of all post-fx for automation purposes |
|
m_Components = new List<PostProcessingComponentBase>(); |
|
|
|
// Component list |
|
m_DebugViews = AddComponent(new BuiltinDebugViewsComponent()); |
|
m_AmbientOcclusion = AddComponent(new AmbientOcclusionComponent()); |
|
m_ScreenSpaceReflection = AddComponent(new ScreenSpaceReflectionComponent()); |
|
m_FogComponent = AddComponent(new FogComponent()); |
|
m_MotionBlur = AddComponent(new MotionBlurComponent()); |
|
m_Taa = AddComponent(new TaaComponent()); |
|
m_EyeAdaptation = AddComponent(new EyeAdaptationComponent()); |
|
m_DepthOfField = AddComponent(new DepthOfFieldComponent()); |
|
m_Bloom = AddComponent(new BloomComponent()); |
|
m_ChromaticAberration = AddComponent(new ChromaticAberrationComponent()); |
|
m_ColorGrading = AddComponent(new ColorGradingComponent()); |
|
m_UserLut = AddComponent(new UserLutComponent()); |
|
m_Grain = AddComponent(new GrainComponent()); |
|
m_Vignette = AddComponent(new VignetteComponent()); |
|
m_Dithering = AddComponent(new DitheringComponent()); |
|
m_Fxaa = AddComponent(new FxaaComponent()); |
|
|
|
// Prepare state observers |
|
m_ComponentStates = new Dictionary<PostProcessingComponentBase, bool>(); |
|
|
|
foreach (var component in m_Components) |
|
m_ComponentStates.Add(component, false); |
|
|
|
useGUILayout = false; |
|
} |
|
|
|
void OnPreCull() |
|
{ |
|
// All the per-frame initialization logic has to be done in OnPreCull instead of Update |
|
// because [ImageEffectAllowedInSceneView] doesn't trigger Update events... |
|
|
|
m_Camera = GetComponent<Camera>(); |
|
|
|
if (profile == null || m_Camera == null) |
|
return; |
|
|
|
#if UNITY_EDITOR |
|
// Track the scene view camera to disable some effects we don't want to see in the |
|
// scene view |
|
// Currently disabled effects : |
|
// - Temporal Antialiasing |
|
// - Depth of Field |
|
// - Motion blur |
|
m_RenderingInSceneView = UnityEditor.SceneView.currentDrawingSceneView != null |
|
&& UnityEditor.SceneView.currentDrawingSceneView.camera == m_Camera; |
|
#endif |
|
|
|
// Prepare context |
|
var context = m_Context.Reset(); |
|
context.profile = profile; |
|
context.renderTextureFactory = m_RenderTextureFactory; |
|
context.materialFactory = m_MaterialFactory; |
|
context.camera = m_Camera; |
|
|
|
// Prepare components |
|
m_DebugViews.Init(context, profile.debugViews); |
|
m_AmbientOcclusion.Init(context, profile.ambientOcclusion); |
|
m_ScreenSpaceReflection.Init(context, profile.screenSpaceReflection); |
|
m_FogComponent.Init(context, profile.fog); |
|
m_MotionBlur.Init(context, profile.motionBlur); |
|
m_Taa.Init(context, profile.antialiasing); |
|
m_EyeAdaptation.Init(context, profile.eyeAdaptation); |
|
m_DepthOfField.Init(context, profile.depthOfField); |
|
m_Bloom.Init(context, profile.bloom); |
|
m_ChromaticAberration.Init(context, profile.chromaticAberration); |
|
m_ColorGrading.Init(context, profile.colorGrading); |
|
m_UserLut.Init(context, profile.userLut); |
|
m_Grain.Init(context, profile.grain); |
|
m_Vignette.Init(context, profile.vignette); |
|
m_Dithering.Init(context, profile.dithering); |
|
m_Fxaa.Init(context, profile.antialiasing); |
|
|
|
// Handles profile change and 'enable' state observers |
|
if (m_PreviousProfile != profile) |
|
{ |
|
DisableComponents(); |
|
m_PreviousProfile = profile; |
|
} |
|
|
|
CheckObservers(); |
|
|
|
// Find out which camera flags are needed before rendering begins |
|
// Note that motion vectors will only be available one frame after being enabled |
|
var flags = context.camera.depthTextureMode; |
|
foreach (var component in m_Components) |
|
{ |
|
if (component.active) |
|
flags |= component.GetCameraFlags(); |
|
} |
|
|
|
context.camera.depthTextureMode = flags; |
|
|
|
// Temporal antialiasing jittering, needs to happen before culling |
|
if (!m_RenderingInSceneView && m_Taa.active && !profile.debugViews.willInterrupt) |
|
m_Taa.SetProjectionMatrix(jitteredMatrixFunc); |
|
} |
|
|
|
void OnPreRender() |
|
{ |
|
if (profile == null) |
|
return; |
|
|
|
// Command buffer-based effects should be set-up here |
|
TryExecuteCommandBuffer(m_DebugViews); |
|
TryExecuteCommandBuffer(m_AmbientOcclusion); |
|
TryExecuteCommandBuffer(m_ScreenSpaceReflection); |
|
TryExecuteCommandBuffer(m_FogComponent); |
|
|
|
if (!m_RenderingInSceneView) |
|
TryExecuteCommandBuffer(m_MotionBlur); |
|
} |
|
|
|
void OnPostRender() |
|
{ |
|
if (profile == null || m_Camera == null) |
|
return; |
|
|
|
if (!m_RenderingInSceneView && m_Taa.active && !profile.debugViews.willInterrupt) |
|
m_Context.camera.ResetProjectionMatrix(); |
|
} |
|
|
|
// Classic render target pipeline for RT-based effects |
|
void OnRenderImage(RenderTexture source, RenderTexture destination) |
|
{ |
|
if (profile == null || m_Camera == null) |
|
{ |
|
Graphics.Blit(source, destination); |
|
return; |
|
} |
|
|
|
// Uber shader setup |
|
bool uberActive = false; |
|
bool fxaaActive = m_Fxaa.active; |
|
bool taaActive = m_Taa.active && !m_RenderingInSceneView; |
|
bool dofActive = m_DepthOfField.active && !m_RenderingInSceneView; |
|
|
|
var uberMaterial = m_MaterialFactory.Get("Hidden/Post FX/Uber Shader"); |
|
uberMaterial.shaderKeywords = null; |
|
|
|
var src = source; |
|
var dst = destination; |
|
|
|
if (taaActive) |
|
{ |
|
var tempRT = m_RenderTextureFactory.Get(src); |
|
m_Taa.Render(src, tempRT); |
|
src = tempRT; |
|
} |
|
|
|
#if UNITY_EDITOR |
|
// Render to a dedicated target when monitors are enabled so they can show information |
|
// about the final render. |
|
// At runtime the output will always be the backbuffer or whatever render target is |
|
// currently set on the camera. |
|
if (profile.monitors.onFrameEndEditorOnly != null) |
|
dst = m_RenderTextureFactory.Get(src); |
|
#endif |
|
|
|
Texture autoExposure = GraphicsUtils.whiteTexture; |
|
if (m_EyeAdaptation.active) |
|
{ |
|
uberActive = true; |
|
autoExposure = m_EyeAdaptation.Prepare(src, uberMaterial); |
|
} |
|
|
|
uberMaterial.SetTexture("_AutoExposure", autoExposure); |
|
|
|
if (dofActive) |
|
{ |
|
uberActive = true; |
|
m_DepthOfField.Prepare(src, uberMaterial, taaActive, m_Taa.jitterVector, m_Taa.model.settings.taaSettings.motionBlending); |
|
} |
|
|
|
if (m_Bloom.active) |
|
{ |
|
uberActive = true; |
|
m_Bloom.Prepare(src, uberMaterial, autoExposure); |
|
} |
|
|
|
uberActive |= TryPrepareUberImageEffect(m_ChromaticAberration, uberMaterial); |
|
uberActive |= TryPrepareUberImageEffect(m_ColorGrading, uberMaterial); |
|
uberActive |= TryPrepareUberImageEffect(m_Vignette, uberMaterial); |
|
uberActive |= TryPrepareUberImageEffect(m_UserLut, uberMaterial); |
|
|
|
var fxaaMaterial = fxaaActive |
|
? m_MaterialFactory.Get("Hidden/Post FX/FXAA") |
|
: null; |
|
|
|
if (fxaaActive) |
|
{ |
|
fxaaMaterial.shaderKeywords = null; |
|
TryPrepareUberImageEffect(m_Grain, fxaaMaterial); |
|
TryPrepareUberImageEffect(m_Dithering, fxaaMaterial); |
|
|
|
if (uberActive) |
|
{ |
|
var output = m_RenderTextureFactory.Get(src); |
|
Graphics.Blit(src, output, uberMaterial, 0); |
|
src = output; |
|
} |
|
|
|
m_Fxaa.Render(src, dst); |
|
} |
|
else |
|
{ |
|
uberActive |= TryPrepareUberImageEffect(m_Grain, uberMaterial); |
|
uberActive |= TryPrepareUberImageEffect(m_Dithering, uberMaterial); |
|
|
|
if (uberActive) |
|
{ |
|
if (!GraphicsUtils.isLinearColorSpace) |
|
uberMaterial.EnableKeyword("UNITY_COLORSPACE_GAMMA"); |
|
|
|
Graphics.Blit(src, dst, uberMaterial, 0); |
|
} |
|
} |
|
|
|
if (!uberActive && !fxaaActive) |
|
Graphics.Blit(src, dst); |
|
|
|
#if UNITY_EDITOR |
|
if (profile.monitors.onFrameEndEditorOnly != null) |
|
{ |
|
Graphics.Blit(dst, destination); |
|
|
|
var oldRt = RenderTexture.active; |
|
profile.monitors.onFrameEndEditorOnly(dst); |
|
RenderTexture.active = oldRt; |
|
} |
|
#endif |
|
|
|
m_RenderTextureFactory.ReleaseAll(); |
|
} |
|
|
|
void OnGUI() |
|
{ |
|
if (Event.current.type != EventType.Repaint) |
|
return; |
|
|
|
if (profile == null || m_Camera == null) |
|
return; |
|
|
|
if (m_EyeAdaptation.active && profile.debugViews.IsModeActive(DebugMode.EyeAdaptation)) |
|
m_EyeAdaptation.OnGUI(); |
|
else if (m_ColorGrading.active && profile.debugViews.IsModeActive(DebugMode.LogLut)) |
|
m_ColorGrading.OnGUI(); |
|
else if (m_UserLut.active && profile.debugViews.IsModeActive(DebugMode.UserLut)) |
|
m_UserLut.OnGUI(); |
|
} |
|
|
|
void OnDisable() |
|
{ |
|
// Clear command buffers |
|
foreach (var cb in m_CommandBuffers.Values) |
|
{ |
|
m_Camera.RemoveCommandBuffer(cb.Key, cb.Value); |
|
cb.Value.Dispose(); |
|
} |
|
|
|
m_CommandBuffers.Clear(); |
|
|
|
// Clear components |
|
if (profile != null) |
|
DisableComponents(); |
|
|
|
m_Components.Clear(); |
|
|
|
// Factories |
|
m_MaterialFactory.Dispose(); |
|
m_RenderTextureFactory.Dispose(); |
|
GraphicsUtils.Dispose(); |
|
} |
|
|
|
public void ResetTemporalEffects() |
|
{ |
|
m_Taa.ResetHistory(); |
|
m_MotionBlur.ResetHistory(); |
|
m_EyeAdaptation.ResetHistory(); |
|
} |
|
|
|
#region State management |
|
|
|
List<PostProcessingComponentBase> m_ComponentsToEnable = new List<PostProcessingComponentBase>(); |
|
List<PostProcessingComponentBase> m_ComponentsToDisable = new List<PostProcessingComponentBase>(); |
|
|
|
void CheckObservers() |
|
{ |
|
foreach (var cs in m_ComponentStates) |
|
{ |
|
var component = cs.Key; |
|
var state = component.GetModel().enabled; |
|
|
|
if (state != cs.Value) |
|
{ |
|
if (state) m_ComponentsToEnable.Add(component); |
|
else m_ComponentsToDisable.Add(component); |
|
} |
|
} |
|
|
|
for (int i = 0; i < m_ComponentsToDisable.Count; i++) |
|
{ |
|
var c = m_ComponentsToDisable[i]; |
|
m_ComponentStates[c] = false; |
|
c.OnDisable(); |
|
} |
|
|
|
for (int i = 0; i < m_ComponentsToEnable.Count; i++) |
|
{ |
|
var c = m_ComponentsToEnable[i]; |
|
m_ComponentStates[c] = true; |
|
c.OnEnable(); |
|
} |
|
|
|
m_ComponentsToDisable.Clear(); |
|
m_ComponentsToEnable.Clear(); |
|
} |
|
|
|
void DisableComponents() |
|
{ |
|
foreach (var component in m_Components) |
|
{ |
|
var model = component.GetModel(); |
|
if (model != null && model.enabled) |
|
component.OnDisable(); |
|
} |
|
} |
|
|
|
#endregion |
|
|
|
#region Command buffer handling & rendering helpers |
|
// Placeholders before the upcoming Scriptable Render Loop as command buffers will be |
|
// executed on the go so we won't need of all that stuff |
|
CommandBuffer AddCommandBuffer<T>(CameraEvent evt, string name) |
|
where T : PostProcessingModel |
|
{ |
|
var cb = new CommandBuffer { name = name }; |
|
var kvp = new KeyValuePair<CameraEvent, CommandBuffer>(evt, cb); |
|
m_CommandBuffers.Add(typeof(T), kvp); |
|
m_Camera.AddCommandBuffer(evt, kvp.Value); |
|
return kvp.Value; |
|
} |
|
|
|
void RemoveCommandBuffer<T>() |
|
where T : PostProcessingModel |
|
{ |
|
KeyValuePair<CameraEvent, CommandBuffer> kvp; |
|
var type = typeof(T); |
|
|
|
if (!m_CommandBuffers.TryGetValue(type, out kvp)) |
|
return; |
|
|
|
m_Camera.RemoveCommandBuffer(kvp.Key, kvp.Value); |
|
m_CommandBuffers.Remove(type); |
|
kvp.Value.Dispose(); |
|
} |
|
|
|
CommandBuffer GetCommandBuffer<T>(CameraEvent evt, string name) |
|
where T : PostProcessingModel |
|
{ |
|
CommandBuffer cb; |
|
KeyValuePair<CameraEvent, CommandBuffer> kvp; |
|
|
|
if (!m_CommandBuffers.TryGetValue(typeof(T), out kvp)) |
|
{ |
|
cb = AddCommandBuffer<T>(evt, name); |
|
} |
|
else if (kvp.Key != evt) |
|
{ |
|
RemoveCommandBuffer<T>(); |
|
cb = AddCommandBuffer<T>(evt, name); |
|
} |
|
else cb = kvp.Value; |
|
|
|
return cb; |
|
} |
|
|
|
void TryExecuteCommandBuffer<T>(PostProcessingComponentCommandBuffer<T> component) |
|
where T : PostProcessingModel |
|
{ |
|
if (component.active) |
|
{ |
|
var cb = GetCommandBuffer<T>(component.GetCameraEvent(), component.GetName()); |
|
cb.Clear(); |
|
component.PopulateCommandBuffer(cb); |
|
} |
|
else RemoveCommandBuffer<T>(); |
|
} |
|
|
|
bool TryPrepareUberImageEffect<T>(PostProcessingComponentRenderTexture<T> component, Material material) |
|
where T : PostProcessingModel |
|
{ |
|
if (!component.active) |
|
return false; |
|
|
|
component.Prepare(material); |
|
return true; |
|
} |
|
|
|
T AddComponent<T>(T component) |
|
where T : PostProcessingComponentBase |
|
{ |
|
m_Components.Add(component); |
|
return component; |
|
} |
|
|
|
#endregion |
|
} |
|
}
|
|
|