First Commit
This commit is contained in:
@@ -0,0 +1,453 @@
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
using UnityEngine.Rendering;
|
||||
using UnityEngine.Rendering.Universal;
|
||||
|
||||
#if ENABLE_VR
|
||||
using UnityEngine.XR;
|
||||
#endif
|
||||
|
||||
namespace Golems
|
||||
{
|
||||
public class OutlineScriptableRenderFeature : ScriptableRendererFeature
|
||||
{
|
||||
|
||||
[SerializeField] private bool m_IsEnabled = true;
|
||||
|
||||
[SerializeField]
|
||||
private Material m_SolidUnlitMaterial = default;
|
||||
|
||||
[SerializeField]
|
||||
private Material m_BlurMaterial = default;
|
||||
|
||||
private OutlineScriptableRenderPass m_Pass = default;
|
||||
|
||||
[SerializeField] private RenderPassEvent m_RenderPassEvent = RenderPassEvent.AfterRenderingOpaques;
|
||||
|
||||
/// <summary>
|
||||
/// The number of Blur passes performed.
|
||||
/// This effects ALL objects rendered
|
||||
/// </summary>
|
||||
[Tooltip("How many times we Blur all the objects. Note, the bigger the number, the bigger the performance hit")]
|
||||
[Range(1, 10)][SerializeField] private int m_BlurPasses = 1;
|
||||
|
||||
/// <summary>
|
||||
/// Effects how far off centre each Blur sample becomes
|
||||
/// </summary>
|
||||
[Tooltip("Effects how far off centre each Blur sample becomes. Note, the bigger the number, the bigger the pixelation")]
|
||||
[Range(0.0f, 10.0f)] [SerializeField] private float m_BlurRadius = .5f;
|
||||
|
||||
/// <summary>
|
||||
/// Index into the m_BlurTextureSizes
|
||||
/// </summary>
|
||||
[Range(0,4)] [SerializeField] private int m_BlurTextureSize = 3;
|
||||
|
||||
/// <summary>
|
||||
/// Texture size of the blur textures
|
||||
/// </summary>
|
||||
private int[] m_BlurTextureSizes = new int[5] { 128, 256, 512, 1024, 2048 };
|
||||
|
||||
[Header("Outlines Occluders")]
|
||||
/// <summary>
|
||||
/// Material used to render the occluders
|
||||
/// </summary>
|
||||
[Tooltip("Material used to render the occluders")]
|
||||
[SerializeField] Material m_OutlinesOccludersMat;
|
||||
|
||||
/// <summary>
|
||||
/// Used to set the objects that will be part of the outlines occluders
|
||||
/// </summary>
|
||||
[Tooltip("Used to set the objects that will be part of the outlines occluders")]
|
||||
[SerializeField] LayerMask m_OccludersMask;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes this feature's resources.
|
||||
/// </summary>
|
||||
public override void Create()
|
||||
{
|
||||
m_Pass = new OutlineScriptableRenderPass(m_SolidUnlitMaterial, m_BlurMaterial, m_OutlinesOccludersMat, m_OccludersMask);
|
||||
|
||||
}
|
||||
|
||||
private void DebugSetEnabledState(bool state)
|
||||
{
|
||||
m_IsEnabled = state;
|
||||
}
|
||||
private bool DebugGetEnabledState()
|
||||
{
|
||||
return m_IsEnabled;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Injects the outline ScriptableRenderPass into the renderer.
|
||||
/// </summary>
|
||||
public override void AddRenderPasses(ScriptableRenderer renderer, ref RenderingData renderingData)
|
||||
{
|
||||
if (!m_IsEnabled)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
//
|
||||
// Zeroing this here in case the pass is not added
|
||||
//
|
||||
OutlineCollection.NumberOfObjectsRendered = 0;
|
||||
var outlineRenderers = OutlineCollection.RendererBundles;
|
||||
if (outlineRenderers == null || outlineRenderers.Count <= 0) return;
|
||||
|
||||
m_Pass.Setup(
|
||||
m_RenderPassEvent,
|
||||
renderingData.cameraData.camera,
|
||||
outlineRenderers,
|
||||
m_BlurPasses,
|
||||
m_BlurRadius,
|
||||
m_BlurTextureSizes[Mathf.Max(0, Mathf.Min(m_BlurTextureSize, m_BlurTextureSizes.Length-1))]);
|
||||
renderer.EnqueuePass(m_Pass);
|
||||
}
|
||||
}
|
||||
|
||||
public class OutlineScriptableRenderPass : ScriptableRenderPass
|
||||
{
|
||||
private const string k_ProfilerIdent = "Outline";
|
||||
|
||||
#region Texture Consts
|
||||
private const string k_DefaultDrawObjectsTex = "_DefaultDrawObjects";
|
||||
private const string k_SolidDrawObjectsTex = "_SolidDrawObjects";
|
||||
private const string k_BlurTex = "_BlurTex";
|
||||
private const string k_MainText = "_MainTex";
|
||||
private const string k_TempTex = "_TempTex";
|
||||
#endregion Texture Consts
|
||||
|
||||
#region Shader Fields
|
||||
private readonly int m_ColourID = Shader.PropertyToID("_Color");
|
||||
private readonly int m_AlphaID = Shader.PropertyToID("_Alpha");
|
||||
|
||||
private readonly int m_BlurResolution = Shader.PropertyToID("_BlurResolution");
|
||||
private readonly int m_BlurRadius = Shader.PropertyToID("_BlurRadius");
|
||||
private readonly int m_BlurDirectionX = Shader.PropertyToID("_BlurDirectionX");
|
||||
private readonly int m_BlurDirectionY = Shader.PropertyToID("_BlurDirectionY");
|
||||
#endregion Shader Fields
|
||||
|
||||
#region Render Texture IDs
|
||||
private readonly int m_DefaultDrawObjects = Shader.PropertyToID(k_DefaultDrawObjectsTex);
|
||||
private readonly int m_SolidDrawObjects = Shader.PropertyToID(k_SolidDrawObjectsTex);
|
||||
private readonly int m_BlurPassRenderTexID = Shader.PropertyToID(k_BlurTex);
|
||||
private readonly int m_TempRenderTexID = Shader.PropertyToID(k_TempTex);
|
||||
#endregion
|
||||
|
||||
private Camera m_Camera;
|
||||
private IList<Outline2> m_Renderers;
|
||||
private Material m_SolidUnlitMaterial;
|
||||
private Material m_BlurMaterial;
|
||||
|
||||
/// <summary>
|
||||
/// The uniform Blur texture size, calculated from the current screen size
|
||||
/// </summary>
|
||||
private int m_BlurRes;
|
||||
|
||||
/// <summary>
|
||||
/// Effects how far off centre each Blur sample becomes
|
||||
/// </summary>
|
||||
private float m_BlurRad = 1.0f;
|
||||
|
||||
/// <summary>
|
||||
/// The number of Blur passes performed.
|
||||
/// This effects ALL objects rendered
|
||||
/// </summary>
|
||||
private int m_BlurPasses = 1;
|
||||
|
||||
/// <summary>
|
||||
/// Used in DrawObjectsXYZ. We use this in conjunction with GetSharedMaterials.
|
||||
/// Avoids GC alloc from .sharedMaterials
|
||||
/// </summary>
|
||||
private readonly List<Material> m_SharedMaterials = new List<Material>();
|
||||
|
||||
/// <summary>
|
||||
/// Used to Get and Store the each Outline instances Color
|
||||
/// </summary>
|
||||
private Color m_OutlineColour = new Color(1,1,1,1);
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Occluders shader tags list
|
||||
/// </summary>
|
||||
private List<ShaderTagId> m_ShaderTagsList = new List<ShaderTagId>();
|
||||
|
||||
/// <summary>
|
||||
/// Filter settings for occluders rendering
|
||||
/// </summary>
|
||||
private FilteringSettings m_FilteringSettings;
|
||||
|
||||
/// <summary>
|
||||
/// Material used to render the occluders
|
||||
/// </summary>
|
||||
private Material m_OccludersOverrideMaterial;
|
||||
|
||||
#if UNITY_EDITOR
|
||||
private readonly HashSet<Outline2> m_EditorLoggedNullMaterialOutlines = new HashSet<Outline2>();
|
||||
private readonly HashSet<Renderer> m_EditorLoggedNullMaterialRenderers = new HashSet<Renderer>();
|
||||
#endif
|
||||
|
||||
public OutlineScriptableRenderPass(Material solidUnlitMaterial, Material blurMaterial, Material occludersOverrideMaterial, LayerMask occludersMask)
|
||||
{
|
||||
m_BlurMaterial = blurMaterial;
|
||||
m_SolidUnlitMaterial = solidUnlitMaterial;
|
||||
m_OccludersOverrideMaterial = occludersOverrideMaterial;
|
||||
|
||||
// Initialise occluders related properties
|
||||
m_ShaderTagsList.Add(new ShaderTagId("SRPDefaultUnlit"));
|
||||
m_ShaderTagsList.Add(new ShaderTagId("UniversalForward"));
|
||||
m_ShaderTagsList.Add(new ShaderTagId("UniversalForwardOnly"));
|
||||
m_ShaderTagsList.Add(new ShaderTagId("ForwardLit"));
|
||||
|
||||
m_FilteringSettings = new FilteringSettings(RenderQueueRange.opaque, occludersMask);
|
||||
}
|
||||
|
||||
public void Setup(RenderPassEvent whenToRender, Camera camera, IList<Outline2> renderers, int blurPasses,
|
||||
float blurRadius, int blurTextureSize)
|
||||
{
|
||||
renderPassEvent = whenToRender;
|
||||
m_Renderers = renderers;
|
||||
m_Camera = camera;
|
||||
m_BlurPasses = blurPasses;
|
||||
m_BlurRad = blurRadius;
|
||||
m_BlurRes = blurTextureSize;
|
||||
}
|
||||
|
||||
public override void Configure(CommandBuffer cmd, RenderTextureDescriptor cameraTextureDescriptor)
|
||||
{
|
||||
//
|
||||
// Attempt to make RTs for XR, otherwise use Default Settings
|
||||
//
|
||||
if (!ConfigureTemporaryRenderTexturesForXR(in cmd)) ConfigureTemporaryRenderTextures(in cmd);
|
||||
}
|
||||
|
||||
public override void OnCameraSetup(CommandBuffer cmd, ref RenderingData renderingData)
|
||||
{
|
||||
ConfigureTarget(m_SolidDrawObjects);
|
||||
ConfigureClear(ClearFlag.All, Color.clear);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Execute the pass. This is where custom rendering occurs. Specific details are left to the implementation
|
||||
/// </summary>
|
||||
public override void Execute(ScriptableRenderContext context, ref RenderingData renderingData)
|
||||
{
|
||||
//
|
||||
// Always make sure we reset, this is the begining of a new Outline process
|
||||
//
|
||||
OutlineCollection.NumberOfObjectsRendered = 0;
|
||||
|
||||
var cmd = CommandBufferPool.Get(k_ProfilerIdent);
|
||||
cmd.Clear();
|
||||
|
||||
//
|
||||
// First render objects using their default materials
|
||||
//
|
||||
DrawObjectsDefault(cmd);
|
||||
|
||||
//
|
||||
// OutlineCollection.NumberOfObjectsRendered is incremented
|
||||
// inside DrawObjectsDefault. If it's 0 we rendered nothing.
|
||||
//
|
||||
if (OutlineCollection.NumberOfObjectsRendered > 0)
|
||||
{
|
||||
DrawOccluders(context, ref renderingData);
|
||||
DrawObjectsSolid(cmd);
|
||||
cmd.SetGlobalFloat(m_BlurResolution, m_BlurRes);
|
||||
cmd.SetGlobalFloat(m_BlurRadius, m_BlurRad);
|
||||
DrawBlur(in cmd);
|
||||
context.ExecuteCommandBuffer(cmd);
|
||||
}
|
||||
|
||||
cmd.Clear();
|
||||
CommandBufferPool.Release(cmd);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Draw outlines occluders
|
||||
/// </summary>
|
||||
private void DrawOccluders(ScriptableRenderContext context, ref RenderingData renderingData)
|
||||
{
|
||||
// Draw the occluders into m_SolidDrawObjects
|
||||
// This pass will fill in the depth buffer so that the outlines could get occluded by the soldiers or other occluders as needed
|
||||
DrawingSettings drawingSettings = CreateDrawingSettings(m_ShaderTagsList, ref renderingData, renderingData.cameraData.defaultOpaqueSortFlags);
|
||||
|
||||
drawingSettings.overrideMaterial = m_OccludersOverrideMaterial;
|
||||
context.DrawRenderers(renderingData.cullResults, ref drawingSettings, ref m_FilteringSettings);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Draw all objects using their default material/s
|
||||
/// </summary>
|
||||
private void DrawObjectsDefault(in CommandBuffer commandBuffer)
|
||||
{
|
||||
CoreUtils.SetRenderTarget(commandBuffer, m_DefaultDrawObjects, ClearFlag.All, Color.clear);
|
||||
commandBuffer.ClearRenderTarget(true, true, Color.clear);
|
||||
|
||||
for (int i = 0, counti = m_Renderers.Count; i < counti; ++i)
|
||||
{
|
||||
var outline = m_Renderers[i];
|
||||
|
||||
//
|
||||
// Don't waste time drawing objects which are currently invisible
|
||||
//
|
||||
if (outline.Alpha <= 0) continue;
|
||||
|
||||
++OutlineCollection.NumberOfObjectsRendered;
|
||||
|
||||
var outlineRenderers = outline.Renderers;
|
||||
for (int j = 0; j < outlineRenderers.Length; j++)
|
||||
{
|
||||
var renderer = outlineRenderers[j];
|
||||
if (renderer == null) continue;
|
||||
m_SharedMaterials.Clear();
|
||||
renderer.GetSharedMaterials(m_SharedMaterials);
|
||||
|
||||
if (m_SharedMaterials.Count > 0)
|
||||
{
|
||||
for (int m = 0, countm = m_SharedMaterials.Count; m < countm; ++m)
|
||||
{
|
||||
if (m_SharedMaterials[m] == null)
|
||||
{
|
||||
#if UNITY_EDITOR
|
||||
if (!m_EditorLoggedNullMaterialOutlines.Contains(outline))
|
||||
{
|
||||
Debug.LogErrorFormat(outline, "Outline {0} has a renderer {1} with null material", outline.name, renderer.name);
|
||||
m_EditorLoggedNullMaterialOutlines.Add(outline);
|
||||
}
|
||||
if (!m_EditorLoggedNullMaterialRenderers.Contains(renderer))
|
||||
{
|
||||
Debug.LogErrorFormat(outline, "Outline {0} has a renderer {1} with null material", outline.name, renderer.name);
|
||||
m_EditorLoggedNullMaterialRenderers.Add(renderer);
|
||||
}
|
||||
#endif
|
||||
continue;
|
||||
}
|
||||
commandBuffer.DrawRenderer(renderer, m_SharedMaterials[m], m, 0);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
commandBuffer.DrawRenderer(renderer, renderer.material);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Draw all objects using the special Solid Unlit shader
|
||||
/// </summary>
|
||||
private void DrawObjectsSolid(in CommandBuffer commandBuffer)
|
||||
{
|
||||
CoreUtils.SetRenderTarget(commandBuffer, m_SolidDrawObjects, ClearFlag.None, Color.clear);
|
||||
//commandBuffer.ClearRenderTarget(true, true, Color.clear);
|
||||
|
||||
for (int i = 0, counti = m_Renderers.Count; i < counti; ++i)
|
||||
{
|
||||
var outline = m_Renderers[i];
|
||||
if (outline.Alpha <= 0) continue;
|
||||
|
||||
outline.GetColour(out m_OutlineColour);
|
||||
commandBuffer.SetGlobalColor(m_ColourID, m_OutlineColour);
|
||||
commandBuffer.SetGlobalFloat(m_AlphaID, outline.Alpha);
|
||||
|
||||
var outlineRenderers = outline.Renderers;
|
||||
for (int j = 0; j < outlineRenderers.Length; ++j)
|
||||
{
|
||||
var renderer = outlineRenderers[j];
|
||||
if (renderer == null) continue;
|
||||
m_SharedMaterials.Clear();
|
||||
renderer.GetSharedMaterials(m_SharedMaterials);
|
||||
|
||||
if (m_SharedMaterials.Count > 0)
|
||||
{
|
||||
for (int m = 0, countm = m_SharedMaterials.Count; m < countm; ++m)
|
||||
{
|
||||
commandBuffer.DrawRenderer(renderer, m_SolidUnlitMaterial, m, 0);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
commandBuffer.DrawRenderer(renderer, m_SolidUnlitMaterial);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates the Blur mask RT. Blur the Outline objects
|
||||
/// </summary>
|
||||
private void DrawBlur(in CommandBuffer commandBuffer)
|
||||
{
|
||||
commandBuffer.Blit(m_SolidDrawObjects, m_BlurPassRenderTexID);
|
||||
|
||||
for (int i = 0, counti = m_BlurPasses; i < counti; ++i)
|
||||
{
|
||||
commandBuffer.SetGlobalFloat(m_BlurDirectionX, 0);
|
||||
commandBuffer.SetGlobalFloat(m_BlurDirectionY, 1);
|
||||
|
||||
commandBuffer.SetGlobalTexture(k_MainText, m_BlurPassRenderTexID);
|
||||
commandBuffer.Blit(m_BlurPassRenderTexID, m_TempRenderTexID,
|
||||
m_BlurMaterial, 0);
|
||||
|
||||
commandBuffer.SetGlobalFloat(m_BlurDirectionX, 1);
|
||||
commandBuffer.SetGlobalFloat(m_BlurDirectionY, 0);
|
||||
|
||||
commandBuffer.SetGlobalTexture(k_MainText, m_TempRenderTexID);
|
||||
commandBuffer.Blit(m_TempRenderTexID, m_BlurPassRenderTexID,
|
||||
m_BlurMaterial, 0);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Configure Temporary RTs for XR
|
||||
/// Relies on ENABLE_VR
|
||||
/// </summary>
|
||||
/// <param name="cmd">SRF CommandBuffer</param>
|
||||
/// <returns>True if RTs were setup using XRSettings, Otherwise False.</returns>
|
||||
private bool ConfigureTemporaryRenderTexturesForXR(in CommandBuffer cmd)
|
||||
{
|
||||
#if ENABLE_VR
|
||||
if (!XRSettings.enabled) return false;
|
||||
var renderTextureDescriptor = XRSettings.eyeTextureDesc;
|
||||
cmd.GetTemporaryRT(m_DefaultDrawObjects, renderTextureDescriptor, FilterMode.Bilinear);
|
||||
cmd.GetTemporaryRT(m_SolidDrawObjects, renderTextureDescriptor, FilterMode.Bilinear);
|
||||
|
||||
renderTextureDescriptor.depthBufferBits = 0;
|
||||
|
||||
renderTextureDescriptor.width = renderTextureDescriptor.height = m_BlurRes;
|
||||
|
||||
cmd.GetTemporaryRT(m_BlurPassRenderTexID, renderTextureDescriptor, FilterMode.Bilinear);
|
||||
cmd.GetTemporaryRT(m_TempRenderTexID, renderTextureDescriptor, FilterMode.Bilinear);
|
||||
return true;
|
||||
#endif
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Configure Temporary RTs for SRF.
|
||||
/// </summary>
|
||||
/// <param name="cmd">SRF CommandBuffer</param>
|
||||
private void ConfigureTemporaryRenderTextures(in CommandBuffer cmd)
|
||||
{
|
||||
var width = m_Camera.scaledPixelWidth;
|
||||
var height = m_Camera.scaledPixelHeight;
|
||||
|
||||
var aa = Mathf.Max(1, QualitySettings.antiAliasing);
|
||||
|
||||
//
|
||||
// We can't really down sample the Solid Render. This is the render of the
|
||||
//
|
||||
cmd.GetTemporaryRT(m_DefaultDrawObjects, width, height, 24, FilterMode.Bilinear,
|
||||
RenderTextureFormat.ARGB32, RenderTextureReadWrite.Default, aa);
|
||||
|
||||
cmd.GetTemporaryRT(m_SolidDrawObjects, width >> 1, height >> 1, 24,
|
||||
FilterMode.Bilinear, RenderTextureFormat.ARGB32, RenderTextureReadWrite.Default,aa);
|
||||
|
||||
cmd.GetTemporaryRT(m_BlurPassRenderTexID, m_BlurRes, m_BlurRes, 0, FilterMode.Bilinear);
|
||||
cmd.GetTemporaryRT(m_TempRenderTexID, m_BlurRes, m_BlurRes, 0, FilterMode.Bilinear);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user