mirror of
https://github.com/FriendshipIsEpic/FiE-Game.git
synced 2024-11-26 07:18:00 +01:00
348 lines
11 KiB
C#
348 lines
11 KiB
C#
|
using UnityEngine;
|
||
|
using UnityEngine.Rendering;
|
||
|
|
||
|
namespace UnityStandardAssets.CinematicEffects
|
||
|
{
|
||
|
[ExecuteInEditMode]
|
||
|
[RequireComponent(typeof(Camera))]
|
||
|
[AddComponentMenu("Image Effects/Cinematic/Ambient Occlusion")]
|
||
|
#if UNITY_5_4_OR_NEWER
|
||
|
[ImageEffectAllowedInSceneView]
|
||
|
#endif
|
||
|
public partial class AmbientOcclusion : MonoBehaviour
|
||
|
{
|
||
|
#region Public Properties
|
||
|
|
||
|
/// Effect settings.
|
||
|
[SerializeField]
|
||
|
public Settings settings = Settings.defaultSettings;
|
||
|
|
||
|
/// Checks if the ambient-only mode is supported under the current settings.
|
||
|
public bool isAmbientOnlySupported
|
||
|
{
|
||
|
get { return targetCamera.hdr && isGBufferAvailable; }
|
||
|
}
|
||
|
|
||
|
#endregion
|
||
|
|
||
|
#region Private Properties
|
||
|
|
||
|
// Properties referring to the current settings
|
||
|
|
||
|
float intensity
|
||
|
{
|
||
|
get { return settings.intensity; }
|
||
|
}
|
||
|
|
||
|
float radius
|
||
|
{
|
||
|
get { return Mathf.Max(settings.radius, 1e-4f); }
|
||
|
}
|
||
|
|
||
|
SampleCount sampleCount
|
||
|
{
|
||
|
get { return settings.sampleCount; }
|
||
|
}
|
||
|
|
||
|
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;
|
||
|
}
|
||
|
return Mathf.Clamp(settings.sampleCountValue, 1, 256);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
bool downsampling
|
||
|
{
|
||
|
get { return settings.downsampling; }
|
||
|
}
|
||
|
|
||
|
bool ambientOnly
|
||
|
{
|
||
|
get { return settings.ambientOnly && isAmbientOnlySupported; }
|
||
|
}
|
||
|
|
||
|
// AO shader
|
||
|
Shader aoShader
|
||
|
{
|
||
|
get
|
||
|
{
|
||
|
if (_aoShader == null)
|
||
|
_aoShader = Shader.Find("Hidden/Image Effects/Cinematic/AmbientOcclusion");
|
||
|
return _aoShader;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
[SerializeField] Shader _aoShader;
|
||
|
|
||
|
// Temporary aterial for the AO shader
|
||
|
Material aoMaterial
|
||
|
{
|
||
|
get
|
||
|
{
|
||
|
if (_aoMaterial == null)
|
||
|
_aoMaterial = ImageEffectHelper.CheckShaderAndCreateMaterial(aoShader);
|
||
|
return _aoMaterial;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
Material _aoMaterial;
|
||
|
|
||
|
// Command buffer for the AO pass
|
||
|
CommandBuffer aoCommands
|
||
|
{
|
||
|
get
|
||
|
{
|
||
|
if (_aoCommands == null)
|
||
|
{
|
||
|
_aoCommands = new CommandBuffer();
|
||
|
_aoCommands.name = "AmbientOcclusion";
|
||
|
}
|
||
|
return _aoCommands;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
CommandBuffer _aoCommands;
|
||
|
|
||
|
// Target camera
|
||
|
Camera targetCamera
|
||
|
{
|
||
|
get { return GetComponent<Camera>(); }
|
||
|
}
|
||
|
|
||
|
// Property observer
|
||
|
PropertyObserver propertyObserver { get; set; }
|
||
|
|
||
|
// Check if the G-buffer is available
|
||
|
bool isGBufferAvailable
|
||
|
{
|
||
|
get
|
||
|
{
|
||
|
var path = targetCamera.actualRenderingPath;
|
||
|
return path == RenderingPath.DeferredShading;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Reference to the quad mesh in the built-in assets
|
||
|
// (used in MRT blitting)
|
||
|
Mesh quadMesh
|
||
|
{
|
||
|
get { return _quadMesh; }
|
||
|
}
|
||
|
|
||
|
[SerializeField] Mesh _quadMesh;
|
||
|
|
||
|
#endregion
|
||
|
|
||
|
#region Effect Passes
|
||
|
|
||
|
// Build commands for the AO pass (used in the ambient-only mode).
|
||
|
void BuildAOCommands()
|
||
|
{
|
||
|
var cb = aoCommands;
|
||
|
|
||
|
var tw = targetCamera.pixelWidth;
|
||
|
var th = targetCamera.pixelHeight;
|
||
|
var ts = downsampling ? 2 : 1;
|
||
|
var format = RenderTextureFormat.R8;
|
||
|
var rwMode = RenderTextureReadWrite.Linear;
|
||
|
var filter = FilterMode.Bilinear;
|
||
|
|
||
|
// AO buffer
|
||
|
var m = aoMaterial;
|
||
|
var rtMask = Shader.PropertyToID("_OcclusionTexture");
|
||
|
cb.GetTemporaryRT(rtMask, tw / ts, th / ts, 0, filter, format, rwMode);
|
||
|
|
||
|
// AO estimation
|
||
|
cb.Blit((Texture)null, rtMask, m, 0);
|
||
|
|
||
|
// Blur buffer
|
||
|
var rtBlur = Shader.PropertyToID("_OcclusionBlurTexture");
|
||
|
|
||
|
// Primary blur filter (large kernel)
|
||
|
cb.GetTemporaryRT(rtBlur, tw, th, 0, filter, format, rwMode);
|
||
|
cb.SetGlobalVector("_BlurVector", Vector2.right * 2);
|
||
|
cb.Blit(rtMask, rtBlur, m, 1);
|
||
|
cb.ReleaseTemporaryRT(rtMask);
|
||
|
|
||
|
cb.GetTemporaryRT(rtMask, tw, th, 0, filter, format, rwMode);
|
||
|
cb.SetGlobalVector("_BlurVector", Vector2.up * 2 * ts);
|
||
|
cb.Blit(rtBlur, rtMask, m, 1);
|
||
|
cb.ReleaseTemporaryRT(rtBlur);
|
||
|
|
||
|
// Secondary blur filter (small kernel)
|
||
|
cb.GetTemporaryRT(rtBlur, tw, th, 0, filter, format, rwMode);
|
||
|
cb.SetGlobalVector("_BlurVector", Vector2.right * ts);
|
||
|
cb.Blit(rtMask, rtBlur, m, 2);
|
||
|
cb.ReleaseTemporaryRT(rtMask);
|
||
|
|
||
|
cb.GetTemporaryRT(rtMask, tw, th, 0, filter, format, rwMode);
|
||
|
cb.SetGlobalVector("_BlurVector", Vector2.up * ts);
|
||
|
cb.Blit(rtBlur, rtMask, m, 2);
|
||
|
cb.ReleaseTemporaryRT(rtBlur);
|
||
|
|
||
|
// Combine AO to the G-buffer.
|
||
|
var mrt = new RenderTargetIdentifier[] {
|
||
|
BuiltinRenderTextureType.GBuffer0, // Albedo, Occ
|
||
|
BuiltinRenderTextureType.CameraTarget // Ambient
|
||
|
};
|
||
|
cb.SetRenderTarget(mrt, BuiltinRenderTextureType.CameraTarget);
|
||
|
cb.SetGlobalTexture("_OcclusionTexture", rtMask);
|
||
|
cb.DrawMesh(quadMesh, Matrix4x4.identity, m, 0, 4);
|
||
|
|
||
|
cb.ReleaseTemporaryRT(rtMask);
|
||
|
}
|
||
|
|
||
|
// Execute the AO pass immediately (used in the forward mode).
|
||
|
void ExecuteAOPass(RenderTexture source, RenderTexture destination)
|
||
|
{
|
||
|
var tw = source.width;
|
||
|
var th = source.height;
|
||
|
var ts = downsampling ? 2 : 1;
|
||
|
var format = RenderTextureFormat.R8;
|
||
|
var rwMode = RenderTextureReadWrite.Linear;
|
||
|
|
||
|
// AO buffer
|
||
|
var m = aoMaterial;
|
||
|
var rtMask = RenderTexture.GetTemporary(tw / ts, th / ts, 0, format, rwMode);
|
||
|
|
||
|
// AO estimation
|
||
|
Graphics.Blit((Texture)null, rtMask, m, 0);
|
||
|
|
||
|
// Primary blur filter (large kernel)
|
||
|
var rtBlur = RenderTexture.GetTemporary(tw, th, 0, format, rwMode);
|
||
|
m.SetVector("_BlurVector", Vector2.right * 2);
|
||
|
Graphics.Blit(rtMask, rtBlur, m, 1);
|
||
|
RenderTexture.ReleaseTemporary(rtMask);
|
||
|
|
||
|
rtMask = RenderTexture.GetTemporary(tw, th, 0, format, rwMode);
|
||
|
m.SetVector("_BlurVector", Vector2.up * 2 * ts);
|
||
|
Graphics.Blit(rtBlur, rtMask, m, 1);
|
||
|
RenderTexture.ReleaseTemporary(rtBlur);
|
||
|
|
||
|
// Secondary blur filter (small kernel)
|
||
|
rtBlur = RenderTexture.GetTemporary(tw, th, 0, format, rwMode);
|
||
|
m.SetVector("_BlurVector", Vector2.right * ts);
|
||
|
Graphics.Blit(rtMask, rtBlur, m, 2);
|
||
|
RenderTexture.ReleaseTemporary(rtMask);
|
||
|
|
||
|
rtMask = RenderTexture.GetTemporary(tw, th, 0, format, rwMode);
|
||
|
m.SetVector("_BlurVector", Vector2.up * ts);
|
||
|
Graphics.Blit(rtBlur, rtMask, m, 2);
|
||
|
RenderTexture.ReleaseTemporary(rtBlur);
|
||
|
|
||
|
// Combine AO with the source.
|
||
|
m.SetTexture("_OcclusionTexture", rtMask);
|
||
|
|
||
|
if (!settings.debug)
|
||
|
Graphics.Blit(source, destination, m, 3);
|
||
|
else
|
||
|
Graphics.Blit(source, destination, m, 5);
|
||
|
|
||
|
RenderTexture.ReleaseTemporary(rtMask);
|
||
|
}
|
||
|
|
||
|
// Update the common material properties.
|
||
|
void UpdateMaterialProperties()
|
||
|
{
|
||
|
var m = aoMaterial;
|
||
|
m.shaderKeywords = null;
|
||
|
|
||
|
m.SetFloat("_Intensity", intensity);
|
||
|
m.SetFloat("_Radius", radius);
|
||
|
m.SetFloat("_TargetScale", downsampling ? 0.5f : 1);
|
||
|
|
||
|
// Use G-buffer if available.
|
||
|
if (isGBufferAvailable)
|
||
|
m.EnableKeyword("_SOURCE_GBUFFER");
|
||
|
|
||
|
// Sample count
|
||
|
if (sampleCount == SampleCount.Lowest)
|
||
|
m.EnableKeyword("_SAMPLECOUNT_LOWEST");
|
||
|
else
|
||
|
m.SetInt("_SampleCount", sampleCountValue);
|
||
|
}
|
||
|
|
||
|
#endregion
|
||
|
|
||
|
#region MonoBehaviour Functions
|
||
|
|
||
|
void OnEnable()
|
||
|
{
|
||
|
// Check if the shader is supported in the current platform.
|
||
|
if (!ImageEffectHelper.IsSupported(aoShader, true, false, this))
|
||
|
{
|
||
|
enabled = false;
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
// Register the command buffer if in the ambient-only mode.
|
||
|
if (ambientOnly)
|
||
|
targetCamera.AddCommandBuffer(CameraEvent.BeforeReflections, aoCommands);
|
||
|
|
||
|
// Requires DepthNormals when G-buffer is not available.
|
||
|
if (!isGBufferAvailable)
|
||
|
targetCamera.depthTextureMode |= DepthTextureMode.DepthNormals;
|
||
|
}
|
||
|
|
||
|
void OnDisable()
|
||
|
{
|
||
|
// Destroy all the temporary resources.
|
||
|
if (_aoMaterial != null) DestroyImmediate(_aoMaterial);
|
||
|
_aoMaterial = null;
|
||
|
|
||
|
if (_aoCommands != null)
|
||
|
targetCamera.RemoveCommandBuffer(CameraEvent.BeforeReflections, _aoCommands);
|
||
|
_aoCommands = null;
|
||
|
}
|
||
|
|
||
|
void Update()
|
||
|
{
|
||
|
if (propertyObserver.CheckNeedsReset(settings, targetCamera))
|
||
|
{
|
||
|
// Reinitialize all the resources by disabling/enabling itself.
|
||
|
// This is not very efficient way but just works...
|
||
|
OnDisable();
|
||
|
OnEnable();
|
||
|
|
||
|
// Build the command buffer if in the ambient-only mode.
|
||
|
if (ambientOnly)
|
||
|
{
|
||
|
aoCommands.Clear();
|
||
|
BuildAOCommands();
|
||
|
}
|
||
|
|
||
|
propertyObserver.Update(settings, targetCamera);
|
||
|
}
|
||
|
|
||
|
// Update the material properties (later used in the AO commands).
|
||
|
if (ambientOnly) UpdateMaterialProperties();
|
||
|
}
|
||
|
|
||
|
[ImageEffectOpaque]
|
||
|
void OnRenderImage(RenderTexture source, RenderTexture destination)
|
||
|
{
|
||
|
if (ambientOnly)
|
||
|
{
|
||
|
// Do nothing in the ambient-only mode.
|
||
|
Graphics.Blit(source, destination);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
// Execute the AO pass.
|
||
|
UpdateMaterialProperties();
|
||
|
ExecuteAOPass(source, destination);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
#endregion
|
||
|
}
|
||
|
}
|