using System; using UnityEngine; using UnityEngine.Rendering; namespace UnityStandardAssets.CinematicEffects { [ExecuteInEditMode] [RequireComponent(typeof(Camera))] [AddComponentMenu("Image Effects/Cinematic/Ambient Occlusion")] [ImageEffectAllowedInSceneView] public class AmbientOcclusion : MonoBehaviour { private class PropertyObserver { private bool _downsampling; private OcclusionSource _occlusionSource; private bool _ambientOnly; private bool _debug; private int _pixelWidth; private int _pixelHeight; public bool CheckNeedsReset(Settings setting, Camera camera) { return _downsampling != setting.downsampling || _occlusionSource != setting.occlusionSource || _ambientOnly != setting.ambientOnly || _debug != setting.debug || _pixelWidth != camera.pixelWidth || _pixelHeight != camera.pixelHeight; } public void Update(Settings setting, Camera camera) { _downsampling = setting.downsampling; _occlusionSource = setting.occlusionSource; _ambientOnly = setting.ambientOnly; _debug = setting.debug; _pixelWidth = camera.pixelWidth; _pixelHeight = camera.pixelHeight; } } public enum SampleCount { Lowest = 0, Low = 1, Medium = 2, High = 3, Custom = 4 } public enum OcclusionSource { DepthTexture = 0, DepthNormalsTexture = 1, GBuffer = 2 } [Serializable] public class Settings { [SerializeField] [Range(0f, 4f)] [Tooltip("Degree of darkness produced by the effect.")] public float intensity; [SerializeField] [Tooltip("Radius of sample points, which affects extent of darkened areas.")] public float radius; [SerializeField] [Tooltip("Number of sample points, which affects quality and performance.")] public SampleCount sampleCount; [SerializeField] [Tooltip("Determines the sample count when SampleCount.Custom is used.")] public int sampleCountValue; [SerializeField] [Tooltip("Halves the resolution of the effect to increase performance.")] public bool downsampling; [SerializeField] [Tooltip("If checked, the effect only affects ambient lighting.")] public bool ambientOnly; [SerializeField] [Tooltip("Source buffer on which the occlusion estimator is based.")] public OcclusionSource occlusionSource; [SerializeField] [Tooltip("Displays occlusion for debug purpose.")] public bool debug; public static Settings defaultSettings { get { Settings settings = new Settings(); settings.intensity = 1f; settings.radius = 0.3f; settings.sampleCount = SampleCount.Medium; settings.sampleCountValue = 24; settings.downsampling = false; settings.ambientOnly = false; settings.occlusionSource = OcclusionSource.DepthNormalsTexture; return settings; } } } [SerializeField] public Settings settings = Settings.defaultSettings; [SerializeField] private Shader _aoShader; private Material _aoMaterial; private CommandBuffer _aoCommands; private PropertyObserver _propertyObserver = new PropertyObserver(); [SerializeField] private Mesh _quadMesh; public bool isAmbientOnlySupported { get { return targetCamera.hdr && occlusionSource == OcclusionSource.GBuffer; } } public bool isGBufferAvailable { get { return targetCamera.actualRenderingPath == RenderingPath.DeferredShading; } } private float intensity { get { return settings.intensity; } } private float radius { get { return Mathf.Max(settings.radius, 0.0001f); } } private SampleCount sampleCount { get { return settings.sampleCount; } } private int sampleCountValue { get { switch (settings.sampleCount) { case SampleCount.Lowest: return 3; case SampleCount.Low: return 6; case SampleCount.Medium: return 12; case SampleCount.High: return 20; default: return Mathf.Clamp(settings.sampleCountValue, 1, 256); } } } private OcclusionSource occlusionSource { get { if (settings.occlusionSource == OcclusionSource.GBuffer && !isGBufferAvailable) { return OcclusionSource.DepthNormalsTexture; } return settings.occlusionSource; } } private bool downsampling { get { return settings.downsampling; } } private bool ambientOnly { get { return settings.ambientOnly && !settings.debug && isAmbientOnlySupported; } } private Shader aoShader { get { if (_aoShader == null) { _aoShader = Shader.Find("Hidden/Image Effects/Cinematic/AmbientOcclusion"); } return _aoShader; } } private Material aoMaterial { get { if (_aoMaterial == null) { _aoMaterial = ImageEffectHelper.CheckShaderAndCreateMaterial(aoShader); } return _aoMaterial; } } private CommandBuffer aoCommands { get { if (_aoCommands == null) { _aoCommands = new CommandBuffer(); _aoCommands.name = "AmbientOcclusion"; } return _aoCommands; } } private Camera targetCamera { get { return GetComponent(); } } private PropertyObserver propertyObserver { get { return _propertyObserver; } } private Mesh quadMesh { get { return _quadMesh; } } private void BuildAOCommands() { CommandBuffer commandBuffer = aoCommands; int pixelWidth = targetCamera.pixelWidth; int pixelHeight = targetCamera.pixelHeight; int num = ((!downsampling) ? 1 : 2); RenderTextureFormat format = RenderTextureFormat.ARGB32; RenderTextureReadWrite readWrite = RenderTextureReadWrite.Linear; FilterMode filter = FilterMode.Bilinear; Material material = aoMaterial; int num2 = Shader.PropertyToID("_OcclusionTexture1"); commandBuffer.GetTemporaryRT(num2, pixelWidth / num, pixelHeight / num, 0, filter, format, readWrite); commandBuffer.Blit(null, num2, material, 2); int num3 = Shader.PropertyToID("_OcclusionTexture2"); commandBuffer.GetTemporaryRT(num3, pixelWidth, pixelHeight, 0, filter, format, readWrite); commandBuffer.Blit(num2, num3, material, 4); commandBuffer.ReleaseTemporaryRT(num2); num2 = Shader.PropertyToID("_OcclusionTexture"); commandBuffer.GetTemporaryRT(num2, pixelWidth, pixelHeight, 0, filter, format, readWrite); commandBuffer.Blit(num3, num2, material, 5); commandBuffer.ReleaseTemporaryRT(num3); RenderTargetIdentifier[] colors = new RenderTargetIdentifier[2] { BuiltinRenderTextureType.GBuffer0, BuiltinRenderTextureType.CameraTarget }; commandBuffer.SetRenderTarget(colors, BuiltinRenderTextureType.CameraTarget); commandBuffer.SetGlobalTexture("_OcclusionTexture", num2); commandBuffer.DrawMesh(quadMesh, Matrix4x4.identity, material, 0, 7); commandBuffer.ReleaseTemporaryRT(num2); } private void ExecuteAOPass(RenderTexture source, RenderTexture destination) { int width = source.width; int height = source.height; int num = ((!downsampling) ? 1 : 2); RenderTextureFormat format = RenderTextureFormat.ARGB32; RenderTextureReadWrite readWrite = RenderTextureReadWrite.Linear; bool flag = occlusionSource == OcclusionSource.GBuffer; Material material = aoMaterial; RenderTexture temporary = RenderTexture.GetTemporary(width / num, height / num, 0, format, readWrite); Graphics.Blit(source, temporary, material, (int)occlusionSource); RenderTexture temporary2 = RenderTexture.GetTemporary(width, height, 0, format, readWrite); Graphics.Blit(temporary, temporary2, material, (!flag) ? 3 : 4); RenderTexture.ReleaseTemporary(temporary); temporary = RenderTexture.GetTemporary(width, height, 0, format, readWrite); Graphics.Blit(temporary2, temporary, material, 5); RenderTexture.ReleaseTemporary(temporary2); material.SetTexture("_OcclusionTexture", temporary); Graphics.Blit(source, destination, material, (!settings.debug) ? 6 : 8); RenderTexture.ReleaseTemporary(temporary); material.SetTexture("_OcclusionTexture", null); } private void UpdateMaterialProperties() { Material material = aoMaterial; material.SetFloat("_Intensity", intensity); material.SetFloat("_Radius", radius); material.SetFloat("_Downsample", (!downsampling) ? 1f : 0.5f); material.SetInt("_SampleCount", sampleCountValue); } private void OnEnable() { if (!ImageEffectHelper.IsSupported(aoShader, true, false, this)) { base.enabled = false; return; } if (ambientOnly) { targetCamera.AddCommandBuffer(CameraEvent.BeforeReflections, aoCommands); } if (occlusionSource == OcclusionSource.DepthTexture) { targetCamera.depthTextureMode |= DepthTextureMode.Depth; } if (occlusionSource != OcclusionSource.GBuffer) { targetCamera.depthTextureMode |= DepthTextureMode.DepthNormals; } } private void OnDisable() { if (_aoCommands != null) { targetCamera.RemoveCommandBuffer(CameraEvent.BeforeReflections, _aoCommands); } } private void OnDestroy() { if (Application.isPlaying) { UnityEngine.Object.Destroy(_aoMaterial); } else { UnityEngine.Object.DestroyImmediate(_aoMaterial); } } private void OnPreRender() { if (propertyObserver.CheckNeedsReset(settings, targetCamera)) { OnDisable(); OnEnable(); if (ambientOnly) { aoCommands.Clear(); BuildAOCommands(); } propertyObserver.Update(settings, targetCamera); } if (ambientOnly) { UpdateMaterialProperties(); } } [ImageEffectOpaque] private void OnRenderImage(RenderTexture source, RenderTexture destination) { if (ambientOnly) { Graphics.Blit(source, destination); return; } UpdateMaterialProperties(); ExecuteAOPass(source, destination); } } }