634 lines
16 KiB
C#
634 lines
16 KiB
C#
using UnityEngine;
|
|
using UnityEngine.Rendering;
|
|
using UnityEngine.Serialization;
|
|
using UnityEngine.XR;
|
|
using System.Collections;
|
|
using System.Collections.Generic;
|
|
#if UNITY_5_5_OR_NEWER
|
|
using UnityEngine.Profiling;
|
|
#endif
|
|
|
|
namespace HighlightingSystem
|
|
{
|
|
public enum BlurDirections : int
|
|
{
|
|
Diagonal,
|
|
Straight,
|
|
All
|
|
}
|
|
|
|
public enum AntiAliasing : int
|
|
{
|
|
QualitySettings,
|
|
Disabled,
|
|
MSAA2x,
|
|
MSAA4x,
|
|
MSAA8x,
|
|
}
|
|
|
|
[DisallowMultipleComponent]
|
|
[RequireComponent(typeof(Camera))]
|
|
public class HighlightingBase : MonoBehaviour
|
|
{
|
|
#region Static Fields and Constants
|
|
static protected readonly Color colorClear = new Color(0f, 0f, 0f, 0f);
|
|
static protected readonly string renderBufferName = "HighlightingSystem";
|
|
static protected readonly Matrix4x4 identityMatrix = Matrix4x4.identity;
|
|
static protected readonly string keywordStraightDirections = "STRAIGHT_DIRECTIONS";
|
|
static protected readonly string keywordAllDirections = "ALL_DIRECTIONS";
|
|
static protected readonly string profileHighlightingSystem = "HighlightingSystem";
|
|
protected const CameraEvent queue = CameraEvent.BeforeImageEffectsOpaque;
|
|
|
|
static protected Camera currentCamera;
|
|
static protected HashSet<HighlighterRenderer> visibleRenderers = new HashSet<HighlighterRenderer>();
|
|
#endregion
|
|
|
|
#region Accessors
|
|
// True if supported on this platform
|
|
public bool isSupported
|
|
{
|
|
get
|
|
{
|
|
return CheckSupported(false);
|
|
}
|
|
}
|
|
|
|
public float fillAlpha
|
|
{
|
|
get { return _fillAlpha; }
|
|
set
|
|
{
|
|
value = Mathf.Clamp01(value);
|
|
if (_fillAlpha != value)
|
|
{
|
|
if (Application.isPlaying)
|
|
{
|
|
cutMaterial.SetFloat(ShaderPropertyID._HighlightingFillAlpha, value);
|
|
}
|
|
|
|
_fillAlpha = value;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Highlighting buffer size downsample factor
|
|
public int downsampleFactor
|
|
{
|
|
get { return _downsampleFactor; }
|
|
set
|
|
{
|
|
if (_downsampleFactor != value)
|
|
{
|
|
// Is power of two check
|
|
if ((value != 0) && ((value & (value - 1)) == 0))
|
|
{
|
|
_downsampleFactor = value;
|
|
}
|
|
else
|
|
{
|
|
Debug.LogWarning("HighlightingSystem : Prevented attempt to set incorrect downsample factor value.");
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Blur iterations
|
|
public int iterations
|
|
{
|
|
get { return _iterations; }
|
|
set
|
|
{
|
|
if (_iterations != value)
|
|
{
|
|
_iterations = value;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Blur minimal spread
|
|
public float blurMinSpread
|
|
{
|
|
get { return _blurMinSpread; }
|
|
set
|
|
{
|
|
if (_blurMinSpread != value)
|
|
{
|
|
_blurMinSpread = value;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Blur spread per iteration
|
|
public float blurSpread
|
|
{
|
|
get { return _blurSpread; }
|
|
set
|
|
{
|
|
if (_blurSpread != value)
|
|
{
|
|
_blurSpread = value;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Blurring intensity for the blur material
|
|
public float blurIntensity
|
|
{
|
|
get { return _blurIntensity; }
|
|
set
|
|
{
|
|
if (_blurIntensity != value)
|
|
{
|
|
_blurIntensity = value;
|
|
if (Application.isPlaying)
|
|
{
|
|
blurMaterial.SetFloat(ShaderPropertyID._HighlightingIntensity, _blurIntensity);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
public BlurDirections blurDirections
|
|
{
|
|
get { return _blurDirections; }
|
|
set
|
|
{
|
|
if (_blurDirections != value)
|
|
{
|
|
_blurDirections = value;
|
|
if (Application.isPlaying)
|
|
{
|
|
blurMaterial.SetKeyword(keywordStraightDirections, _blurDirections == BlurDirections.Straight);
|
|
blurMaterial.SetKeyword(keywordAllDirections, _blurDirections == BlurDirections.All);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Blitter component reference (optional)
|
|
public HighlightingBlitter blitter
|
|
{
|
|
get { return _blitter; }
|
|
set
|
|
{
|
|
if (_blitter != value)
|
|
{
|
|
if (_blitter != null)
|
|
{
|
|
_blitter.Unregister(this);
|
|
}
|
|
_blitter = value;
|
|
if (_blitter != null)
|
|
{
|
|
_blitter.Register(this);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
public AntiAliasing antiAliasing
|
|
{
|
|
get { return _antiAliasing; }
|
|
set
|
|
{
|
|
if (_antiAliasing != value)
|
|
{
|
|
_antiAliasing = value;
|
|
}
|
|
}
|
|
}
|
|
#endregion
|
|
|
|
#region Protected Fields
|
|
protected CommandBuffer renderBuffer;
|
|
|
|
protected RenderTextureDescriptor cachedDescriptor;
|
|
|
|
[SerializeField]
|
|
protected float _fillAlpha = 0f;
|
|
|
|
[FormerlySerializedAs("downsampleFactor")]
|
|
[SerializeField]
|
|
protected int _downsampleFactor = 4;
|
|
|
|
[FormerlySerializedAs("iterations")]
|
|
[SerializeField]
|
|
protected int _iterations = 2;
|
|
|
|
[FormerlySerializedAs("blurMinSpread")]
|
|
[SerializeField]
|
|
protected float _blurMinSpread = 0.65f;
|
|
|
|
[FormerlySerializedAs("blurSpread")]
|
|
[SerializeField]
|
|
protected float _blurSpread = 0.25f;
|
|
|
|
[SerializeField]
|
|
protected float _blurIntensity = 0.3f;
|
|
|
|
[SerializeField]
|
|
protected BlurDirections _blurDirections = BlurDirections.Diagonal;
|
|
|
|
[SerializeField]
|
|
protected HighlightingBlitter _blitter;
|
|
|
|
[SerializeField]
|
|
protected AntiAliasing _antiAliasing = AntiAliasing.QualitySettings;
|
|
|
|
// RenderTargetidentifier for the highlightingBuffer RenderTexture
|
|
protected RenderTargetIdentifier highlightingBufferID;
|
|
protected RenderTargetIdentifier blur1ID;
|
|
protected RenderTargetIdentifier blur2ID;
|
|
|
|
// RenderTexture with highlighting buffer
|
|
protected RenderTexture highlightingBuffer = null;
|
|
|
|
// Camera reference
|
|
protected Camera cam = null;
|
|
|
|
// Material parameters
|
|
protected const int BLUR = 0;
|
|
protected const int CUT = 1;
|
|
protected const int COMP = 2;
|
|
static protected readonly string[] shaderPaths = new string[]
|
|
{
|
|
"Hidden/Highlighted/Blur",
|
|
"Hidden/Highlighted/Cut",
|
|
"Hidden/Highlighted/Composite",
|
|
};
|
|
static protected Shader[] shaders;
|
|
static protected Material[] materials;
|
|
|
|
// Dynamic materials
|
|
protected Material blurMaterial;
|
|
protected Material cutMaterial;
|
|
protected Material compMaterial;
|
|
|
|
static protected bool initialized = false;
|
|
#endregion
|
|
|
|
#region MonoBehaviour
|
|
//
|
|
protected virtual void OnEnable()
|
|
{
|
|
Initialize();
|
|
|
|
if (!CheckSupported(true))
|
|
{
|
|
enabled = false;
|
|
Debug.LogError("HighlightingSystem : Highlighting System has been disabled due to unsupported Unity features on the current platform!");
|
|
return;
|
|
}
|
|
|
|
blur1ID = new RenderTargetIdentifier(ShaderPropertyID._HighlightingBlur1);
|
|
blur2ID = new RenderTargetIdentifier(ShaderPropertyID._HighlightingBlur2);
|
|
|
|
blurMaterial = new Material(materials[BLUR]);
|
|
cutMaterial = new Material(materials[CUT]);
|
|
compMaterial = new Material(materials[COMP]);
|
|
|
|
// Set initial material properties
|
|
blurMaterial.SetKeyword(keywordStraightDirections, _blurDirections == BlurDirections.Straight);
|
|
blurMaterial.SetKeyword(keywordAllDirections, _blurDirections == BlurDirections.All);
|
|
blurMaterial.SetFloat(ShaderPropertyID._HighlightingIntensity, _blurIntensity);
|
|
cutMaterial.SetFloat(ShaderPropertyID._HighlightingFillAlpha, _fillAlpha);
|
|
|
|
renderBuffer = new CommandBuffer();
|
|
renderBuffer.name = renderBufferName;
|
|
|
|
cam = GetComponent<Camera>();
|
|
|
|
cam.depthTextureMode |= DepthTextureMode.Depth;
|
|
|
|
cam.AddCommandBuffer(queue, renderBuffer);
|
|
|
|
if (_blitter != null)
|
|
{
|
|
_blitter.Register(this);
|
|
}
|
|
|
|
EndOfFrame.AddListener(OnEndOfFrame);
|
|
}
|
|
|
|
//
|
|
protected virtual void OnDisable()
|
|
{
|
|
if (renderBuffer != null)
|
|
{
|
|
cam.RemoveCommandBuffer(queue, renderBuffer);
|
|
renderBuffer = null;
|
|
}
|
|
|
|
if (highlightingBuffer != null && highlightingBuffer.IsCreated())
|
|
{
|
|
highlightingBuffer.Release();
|
|
highlightingBuffer = null;
|
|
}
|
|
|
|
if (_blitter != null)
|
|
{
|
|
_blitter.Unregister(this);
|
|
}
|
|
|
|
EndOfFrame.RemoveListener(OnEndOfFrame);
|
|
}
|
|
|
|
//
|
|
protected virtual void OnPreCull()
|
|
{
|
|
currentCamera = cam;
|
|
visibleRenderers.Clear();
|
|
}
|
|
|
|
//
|
|
protected virtual void OnPreRender()
|
|
{
|
|
Profiler.BeginSample("HighlightingSystem.OnPreRender");
|
|
|
|
var descriptor = GetDescriptor();
|
|
|
|
if (highlightingBuffer == null || !Equals(cachedDescriptor, descriptor))
|
|
{
|
|
if (highlightingBuffer != null)
|
|
{
|
|
if (highlightingBuffer.IsCreated())
|
|
{
|
|
highlightingBuffer.Release();
|
|
}
|
|
highlightingBuffer = null;
|
|
}
|
|
|
|
cachedDescriptor = descriptor;
|
|
|
|
highlightingBuffer = new RenderTexture(cachedDescriptor);
|
|
highlightingBuffer.filterMode = FilterMode.Point;
|
|
highlightingBuffer.wrapMode = TextureWrapMode.Clamp;
|
|
|
|
if (!highlightingBuffer.Create())
|
|
{
|
|
Debug.LogError("HighlightingSystem : UpdateHighlightingBuffer() : Failed to create highlightingBuffer RenderTexture!");
|
|
}
|
|
|
|
highlightingBufferID = new RenderTargetIdentifier(highlightingBuffer);
|
|
compMaterial.SetTexture(ShaderPropertyID._HighlightingBuffer, highlightingBuffer);
|
|
}
|
|
|
|
RebuildCommandBuffer();
|
|
Profiler.EndSample();
|
|
}
|
|
|
|
// Do not remove!
|
|
// Having this method in this script is necessary to support multiple cameras with different clear flags even in case custom blitter is being used.
|
|
// Also, CommandBuffer is bound to CameraEvent.BeforeImageEffectsOpaque event,
|
|
// so Unity will spam 'depthSurface == NULL || rcolorZero->backBuffer == depthSurface->backBuffer' error even if MSAA is enabled
|
|
protected virtual void OnRenderImage(RenderTexture src, RenderTexture dst)
|
|
{
|
|
Profiler.BeginSample("HighlightingSystem.OnRenderImage");
|
|
if (blitter == null)
|
|
{
|
|
Blit(src, dst);
|
|
}
|
|
else
|
|
{
|
|
Graphics.Blit(src, dst);
|
|
}
|
|
Profiler.EndSample();
|
|
}
|
|
|
|
//
|
|
protected virtual void OnEndOfFrame()
|
|
{
|
|
currentCamera = null;
|
|
visibleRenderers.Clear();
|
|
}
|
|
#endregion
|
|
|
|
#region Internal
|
|
//
|
|
[UnityEngine.Internal.ExcludeFromDocs]
|
|
static public void SetVisible(HighlighterRenderer renderer)
|
|
{
|
|
// Another camera may intercept rendering and send it's own OnWillRenderObject events (i.e. water rendering).
|
|
// Also, VR Camera with Multi Pass Stereo Rendering Method renders twice per frame (once for each eye),
|
|
// but OnWillRenderObject is called once so we have to check.
|
|
if (Camera.current != currentCamera) { return; }
|
|
|
|
// Add to the list of renderers visible for the current Highlighting renderer
|
|
visibleRenderers.Add(renderer);
|
|
}
|
|
|
|
//
|
|
[UnityEngine.Internal.ExcludeFromDocs]
|
|
static public bool GetVisible(HighlighterRenderer renderer)
|
|
{
|
|
return visibleRenderers.Contains(renderer);
|
|
}
|
|
|
|
//
|
|
static protected void Initialize()
|
|
{
|
|
if (initialized) { return; }
|
|
|
|
// Initialize shaders and materials
|
|
int l = shaderPaths.Length;
|
|
shaders = new Shader[l];
|
|
materials = new Material[l];
|
|
for (int i = 0; i < l; i++)
|
|
{
|
|
Shader shader = Shader.Find(shaderPaths[i]);
|
|
shaders[i] = shader;
|
|
|
|
Material material = new Material(shader);
|
|
materials[i] = material;
|
|
}
|
|
|
|
initialized = true;
|
|
}
|
|
|
|
//
|
|
protected virtual RenderTextureDescriptor GetDescriptor()
|
|
{
|
|
RenderTextureDescriptor descriptor;
|
|
|
|
// RTT
|
|
var targetTexture = cam.targetTexture;
|
|
if (targetTexture != null)
|
|
{
|
|
descriptor = targetTexture.descriptor;
|
|
}
|
|
// VR
|
|
else if (XRSettings.enabled)
|
|
{
|
|
descriptor = XRSettings.eyeTextureDesc;
|
|
}
|
|
// Normal
|
|
else
|
|
{
|
|
descriptor = new RenderTextureDescriptor(cam.pixelWidth, cam.pixelHeight, RenderTextureFormat.ARGB32, 24);
|
|
}
|
|
|
|
// Overrides
|
|
descriptor.colorFormat = RenderTextureFormat.ARGB32;
|
|
descriptor.sRGB = QualitySettings.activeColorSpace == ColorSpace.Linear;
|
|
descriptor.useMipMap = false;
|
|
descriptor.msaaSamples = GetAA(targetTexture);
|
|
|
|
return descriptor;
|
|
}
|
|
|
|
//
|
|
protected virtual bool Equals(RenderTextureDescriptor x, RenderTextureDescriptor y)
|
|
{
|
|
return x.width == y.width && x.height == y.height && x.msaaSamples == y.msaaSamples; // TODO compare all fields?
|
|
}
|
|
|
|
//
|
|
protected virtual int GetAA(RenderTexture targetTexture)
|
|
{
|
|
int aa = 1;
|
|
switch (_antiAliasing)
|
|
{
|
|
case AntiAliasing.QualitySettings:
|
|
// Set aa value to 1 in case camera is in DeferredLighting or DeferredShading Rendering Path
|
|
if (cam.actualRenderingPath == RenderingPath.DeferredLighting || cam.actualRenderingPath == RenderingPath.DeferredShading)
|
|
{
|
|
aa = 1;
|
|
}
|
|
else
|
|
{
|
|
if (targetTexture == null)
|
|
{
|
|
aa = QualitySettings.antiAliasing;
|
|
if (aa == 0) { aa = 1; }
|
|
}
|
|
else
|
|
{
|
|
aa = targetTexture.antiAliasing;
|
|
}
|
|
}
|
|
break;
|
|
case AntiAliasing.Disabled:
|
|
aa = 1;
|
|
break;
|
|
case AntiAliasing.MSAA2x:
|
|
aa = 2;
|
|
break;
|
|
case AntiAliasing.MSAA4x:
|
|
aa = 4;
|
|
break;
|
|
case AntiAliasing.MSAA8x:
|
|
aa = 8;
|
|
break;
|
|
}
|
|
return aa;
|
|
}
|
|
|
|
//
|
|
protected virtual bool CheckSupported(bool verbose)
|
|
{
|
|
bool supported = true;
|
|
|
|
// Image Effects supported?
|
|
if (!SystemInfo.supportsImageEffects)
|
|
{
|
|
if (verbose) { Debug.LogError("HighlightingSystem : Image effects is not supported on this platform!"); }
|
|
supported = false;
|
|
}
|
|
|
|
// Required Render Texture Format supported?
|
|
if (!SystemInfo.SupportsRenderTextureFormat(RenderTextureFormat.ARGB32))
|
|
{
|
|
if (verbose) { Debug.LogError("HighlightingSystem : RenderTextureFormat.ARGB32 is not supported on this platform!"); }
|
|
supported = false;
|
|
}
|
|
|
|
// HighlightingOpaque shader supported?
|
|
if (!HighlighterCore.opaqueShader.isSupported)
|
|
{
|
|
if (verbose) { Debug.LogError("HighlightingSystem : HighlightingOpaque shader is not supported on this platform!"); }
|
|
supported = false;
|
|
}
|
|
|
|
// HighlightingTransparent shader supported?
|
|
if (!HighlighterCore.transparentShader.isSupported)
|
|
{
|
|
if (verbose) { Debug.LogError("HighlightingSystem : HighlightingTransparent shader is not supported on this platform!"); }
|
|
supported = false;
|
|
}
|
|
|
|
// Highlighting shaders supported?
|
|
for (int i = 0; i < shaders.Length; i++)
|
|
{
|
|
Shader shader = shaders[i];
|
|
if (!shader.isSupported)
|
|
{
|
|
if (verbose) { Debug.LogError("HighlightingSystem : Shader '" + shader.name + "' is not supported on this platform!"); }
|
|
supported = false;
|
|
}
|
|
}
|
|
|
|
return supported;
|
|
}
|
|
|
|
//
|
|
protected virtual void RebuildCommandBuffer()
|
|
{
|
|
renderBuffer.Clear();
|
|
|
|
renderBuffer.BeginSample(profileHighlightingSystem);
|
|
|
|
// Prepare and clear render target
|
|
renderBuffer.SetRenderTarget(highlightingBufferID);
|
|
renderBuffer.ClearRenderTarget(true, true, colorClear);
|
|
|
|
// Fill buffer with highlighters rendering commands
|
|
HighlighterCore.FillBuffer(renderBuffer);
|
|
|
|
RenderTextureDescriptor desc = cachedDescriptor;
|
|
desc.width = highlightingBuffer.width / _downsampleFactor;
|
|
desc.height = highlightingBuffer.height / _downsampleFactor;
|
|
desc.depthBufferBits = 0;
|
|
|
|
// Create two buffers for blurring the image
|
|
renderBuffer.GetTemporaryRT(ShaderPropertyID._HighlightingBlur1, desc, FilterMode.Bilinear);
|
|
renderBuffer.GetTemporaryRT(ShaderPropertyID._HighlightingBlur2, desc, FilterMode.Bilinear);
|
|
|
|
renderBuffer.Blit(highlightingBufferID, blur1ID);
|
|
|
|
// Blur the small texture
|
|
bool oddEven = true;
|
|
for (int i = 0; i < _iterations; i++)
|
|
{
|
|
float off = _blurMinSpread + _blurSpread * i;
|
|
renderBuffer.SetGlobalFloat(ShaderPropertyID._HighlightingBlurOffset, off);
|
|
|
|
if (oddEven)
|
|
{
|
|
renderBuffer.Blit(blur1ID, blur2ID, blurMaterial);
|
|
}
|
|
else
|
|
{
|
|
renderBuffer.Blit(blur2ID, blur1ID, blurMaterial);
|
|
}
|
|
|
|
oddEven = !oddEven;
|
|
}
|
|
|
|
// Upscale blurred texture and cut stencil from it
|
|
renderBuffer.Blit(oddEven ? blur1ID : blur2ID, highlightingBufferID, cutMaterial);
|
|
|
|
// Cleanup
|
|
renderBuffer.ReleaseTemporaryRT(ShaderPropertyID._HighlightingBlur1);
|
|
renderBuffer.ReleaseTemporaryRT(ShaderPropertyID._HighlightingBlur2);
|
|
|
|
renderBuffer.EndSample(profileHighlightingSystem);
|
|
}
|
|
|
|
// Blit highlighting result to the destination RenderTexture
|
|
public virtual void Blit(RenderTexture src, RenderTexture dst)
|
|
{
|
|
Graphics.Blit(src, dst, compMaterial);
|
|
}
|
|
#endregion
|
|
}
|
|
} |