413 lines
9.7 KiB
C#
413 lines
9.7 KiB
C#
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<Camera>();
|
|
}
|
|
}
|
|
|
|
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);
|
|
}
|
|
}
|
|
}
|