Files
SampleRenderPasses/Assets/Scripts/RenderPasses/AlbertGhostMemory/AlbertGhostMemoryPass.cs
2025-05-18 22:39:39 +03:00

481 lines
19 KiB
C#

using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Rendering;
using UnityEngine.Rendering.Universal;
using UnityEngine.XR;
namespace Golems
{
/// <summary>
/// Helper methods for ScriptableRenderPasses
/// </summary>
public static class ScriptableRenderPassHelper
{
/// <summary>
/// Helper method to create new Engine material with specified Shader
/// </summary>
public static void CreateMaterial(string shaderPath, out Material mat)
{
mat = default;
if (TryGetShader(out var shader, shaderPath))
{
mat = new Material(shader);
}
else
{
Debug.LogError($"Failed to load shader {shaderPath}");
}
}
/// <summary>
/// Wrapper for Shader.Find
/// </summary>
/// <returns>True if Shader was found and loaded</returns>
private static bool TryGetShader(out Shader shader, string path)
{
shader = default;
shader = Shader.Find(path);
return shader != null;
}
}
public class AlbertGhostMemoryPass : ScriptableRenderPass
{
private const string k_ProfilerIdent = "GhostPass";
#region Ident Consts
private const string k_AlbertGhostMemoryMaskTex = "_AlbertGhostMemoryMaskTex";
private const string k_AlbertGhostMemoryTex = "_AlbertGhostMemoryTex";
private const string k_AlbertGhostMemoryBlurredTex = "_AlbertGhostMemoryBlurredTex";
private const string k_TempTexGhost = "_TempTexGhost";
private const string k_MainText = "_MainTex";
private const string k_PerGhostAlphaProperty = "_PerGhostAlpha";
#endregion Ident Consts
#region Render Texture IDs
private readonly int m_MaskRenderTexID = Shader.PropertyToID(k_AlbertGhostMemoryMaskTex);
private readonly int m_MemoryRenderTexID = Shader.PropertyToID(k_AlbertGhostMemoryTex);
private readonly int m_BlurPassRenderTexID = Shader.PropertyToID(k_AlbertGhostMemoryBlurredTex);
private readonly int m_TempRenderTexID = Shader.PropertyToID(k_TempTexGhost);
#endregion Render Texture IDs
#region Shader Properties
private readonly int k_PerGhostAlphaPropertyId = Shader.PropertyToID(k_PerGhostAlphaProperty);
private readonly int m_LightDirectionID = Shader.PropertyToID("_LightDirection");
private readonly int m_LightColourID = Shader.PropertyToID("_LightColour");
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
private RenderTargetIdentifier m_CameraColorTargetIdent;
/// <summary>
/// All the actively registered GhostObjectBehaviours
/// Passed down via GhostObject Collection
/// </summary>
private IList<GhostObjectBehaviour> m_GhostObjectBehaviours;
private MaterialPropertyBlock m_MaterialPropertyBlock = new MaterialPropertyBlock();
private AlbertGhostMemoryCompositeSettings m_AlbertGhostMemoryCompositeSettings;
private Material m_MemoryMaterial;
private Material m_BlurMaterial;
private Camera m_Camera;
private Vector3 m_LightDirection = new Vector3(1.0f, 1.0f, 0.0f);
private Color m_LightColour = Color.white;
/// <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.5f;
/// <summary>
/// The number of Blur passes performed.
/// This effects ALL objects rendered
/// </summary>
#if UNITY_SWITCH
private int m_BlurPasses = 0;
#else
private int m_BlurPasses = 4;
#endif
//private GameSettings m_GameSettings;
/// <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>();
#if UNITY_EDITOR
private readonly HashSet<Renderer> m_EditorLoggedNullMaterialRenderers = new HashSet<Renderer>();
#endif
public bool HasSettings { get; private set; }
public AlbertGhostMemoryPass(RenderPassEvent whenToInsert,
Material memoryMaterial, Material blurMaterial)
{
renderPassEvent = whenToInsert;
//
// NOTE: the materials are passed in instead of being loaded to avoid the shader not
// being available via Shader.Find() when the shader has changed and Unity first opens.
//
m_MemoryMaterial = memoryMaterial; //TODO TOBY: replace with one found on the object / the test fake TestFakeAlvertGhostMemoryCmdShader
m_BlurMaterial = blurMaterial;
// grab the game setting for blur passes and use it here.
//Disabled for the sample
//GameSettings.GetSlowStartSystem(HandleGameSettings, out m_GameSettings, true);
}
/*
private void HandleGameSettings(GameSettings system, SlowSystemState state)
{
m_GameSettings = system;
if (system == null) return;
if (!system.TryGetValue(GameSettingsKeys.k_NumGhostBlurPasses, out int value)) return;
m_BlurPasses = value;
m_GameSettings.AddListenOnGameSettingChange(GameSettingsKeys.k_NumGhostBlurPasses, OnSettingChanged, true);
}
*/
void OnSettingChanged( int value)
{
m_BlurPasses = value;
}
public void SetSettings(AlbertGhostMemorySettings settings, AlbertGhostMemoryCompositeSettings albertGhostMemoryCompositeSettings)
{
m_LightColour = settings.LightColour;
m_LightDirection = settings.LightDirection;
m_BlurRad = settings.BlurRadius;
m_BlurRes = settings.BlurTextureResolution;
m_AlbertGhostMemoryCompositeSettings = albertGhostMemoryCompositeSettings;
HasSettings = true;
}
#region ScriptableRenderPass
public override void Configure(CommandBuffer cmd, RenderTextureDescriptor cameraTextureDescriptor)
{
if (!HasSettings)
{
return;
}
//
// Attempt to make RTs for XR, otherwise use Default Settings
//
if (!ConfigureTemporaryRenderTexturesForXR(in cmd)) ConfigureTemporaryRenderTextures(in cmd);
}
public override void Execute(ScriptableRenderContext context, ref RenderingData renderingData)
{
if (!HasSettings)
{
return;
}
//
// Reset the number of objects renderer, this is a new pass
//
GhostObjectCollection.Instance.NumberOfObjectsRendered = 0;
var cmd = CommandBufferPool.Get(k_ProfilerIdent);
cmd.Clear();
SetAllShaderParameters(in cmd);
DrawObjectsDefault(in cmd);
//
// Only bother we the rest of the process if we actually
// rendered anything using default materials
//
if (GhostObjectCollection.Instance.NumberOfObjectsRendered > 0)
{
DrawObjectsMemory(in cmd);
BlurMask(in cmd);
// make sure to set the render target back to the main camera!
CoreUtils.SetRenderTarget(cmd, m_CameraColorTargetIdent);
context.ExecuteCommandBuffer(cmd);
}
//cmd.Clear();
CommandBufferPool.Release(cmd);
}
public override void FrameCleanup(CommandBuffer cmd)
{
if (!HasSettings)
{
return;
}
cmd.ReleaseTemporaryRT(m_MemoryRenderTexID);
cmd.ReleaseTemporaryRT(m_MaskRenderTexID);
cmd.ReleaseTemporaryRT(m_BlurPassRenderTexID);
}
#endregion
public void Setup(Camera camera, RenderTargetIdentifier cameraColorTargetIdent, List<GhostObjectBehaviour> ghostObjectBehaviours)
{
m_Camera = camera;
m_CameraColorTargetIdent = cameraColorTargetIdent;
m_GhostObjectBehaviours = ghostObjectBehaviours;
}
/// <summary>
/// Sets Global and local variables used
/// across various shaders in the process
/// </summary>
private void SetAllShaderParameters(in CommandBuffer commandBuffer)
{
commandBuffer.SetGlobalVector(m_LightDirectionID, m_LightDirection);
commandBuffer.SetGlobalColor(m_LightColourID, m_LightColour);
commandBuffer.SetGlobalFloat(m_BlurResolution, m_BlurRes);
commandBuffer.SetGlobalFloat(m_BlurRadius, m_BlurRad);
}
/// <summary>
/// Draw all GhostObjects either with
/// Default materials or with the Ghost Material
/// </summary>
private void DrawAllObjects(in CommandBuffer commandBuffer, bool useMemoryMaterial = false)
{
if (m_GhostObjectBehaviours == null) return;
for (int i = 0, counti = m_GhostObjectBehaviours.Count; i < counti; ++i)
{
var ghostObjectBehaviour = m_GhostObjectBehaviours[i];
if (ghostObjectBehaviour == null)
{
continue;
}
var ghostObject = ghostObjectBehaviour.GhostObject;
if (ghostObject == null)
{
continue;
}
var renderers = ghostObject.Renderers;
if (renderers == null)
{
continue;
}
var alpha = ghostObjectBehaviour.Alpha;
Debug.Log("Alpha Ghost:" + alpha);
if (!ghostObjectBehaviour.IgnoreGlobalAlpha)
{
alpha *= m_AlbertGhostMemoryCompositeSettings.Alpha;
}
//
// No need to render anything if the Alpha for this GhostObject is 0. Although the final composite will not
// show anything we're wasting Draw calls drawing the Lit and Unlit(mask) versions of the Ghost Object Behaviours
//
if (alpha <= 0.0f) continue;
//
// Let everything know we're rendering something this Ghost pass
//
++GhostObjectCollection.Instance.NumberOfObjectsRendered;
if (ghostObjectBehaviour.ReplacementMemoryMaterials != null)
{
DrawGhostRenderer(commandBuffer, renderers, alpha, useMemoryMaterial, ghostObjectBehaviour.ReplacementMemoryMaterials);
}
else
{
DrawGhostRenderer(commandBuffer, renderers, alpha, useMemoryMaterial);
}
}
}
private void DrawGhostRenderer(CommandBuffer commandBuffer, Renderer[] renderers, float alpha,
bool useMemoryMaterial, IReadOnlyList<Material> replacementMemoryMaterials = null)
{
Debug.Log("Ghost Renderer " + renderers.Length);
for (int j = 0, countj = renderers.Length; j < countj; ++j)
{
var renderer = renderers[j];
if (renderer == null) continue;
m_MaterialPropertyBlock.SetFloat(k_PerGhostAlphaPropertyId, alpha);
renderer.SetPropertyBlock(m_MaterialPropertyBlock);
m_SharedMaterials.Clear();
renderer.GetSharedMaterials(m_SharedMaterials);
if (m_SharedMaterials.Count > 0)
{
//for (int m = 0, countm = materials.Length; m < countm; ++m)
for (int m = m_SharedMaterials.Count -1, countm = 0; m >= countm; --m)
{
if (useMemoryMaterial)
{
if (replacementMemoryMaterials != null && replacementMemoryMaterials.Count > 0 && replacementMemoryMaterials[m] != null)
{
commandBuffer.DrawRenderer(renderer,
replacementMemoryMaterials[m], m,
0);
}
else
{
commandBuffer.DrawRenderer(renderer,
m_MemoryMaterial, m,
0);
}
}
else
{
if (m_SharedMaterials[m] == null)
{
#if UNITY_EDITOR
if (!m_EditorLoggedNullMaterialRenderers.Contains(renderer))
{
Debug.LogErrorFormat(renderer, "AlbertGhostMemory: renderer {0} with null material",
renderer.name);
m_EditorLoggedNullMaterialRenderers.Add(renderer);
}
#endif
continue;
}
commandBuffer.DrawRenderer(renderer,
m_SharedMaterials[m], m,
0);
}
}
}
else
{
if (replacementMemoryMaterials != null && replacementMemoryMaterials.Count > 0 && replacementMemoryMaterials[0] != null)
{
commandBuffer.DrawRenderer(renderer,
useMemoryMaterial ? replacementMemoryMaterials[0] : renderer.sharedMaterial);
}
else
{
commandBuffer.DrawRenderer(renderer,
useMemoryMaterial ? m_MemoryMaterial : renderer.sharedMaterial);
}
}
}
}
/// <summary>
/// Draw all GhostObjects using their default material/s
/// </summary>
private void DrawObjectsDefault(in CommandBuffer commandBuffer)
{
CoreUtils.SetRenderTarget(commandBuffer, m_MemoryRenderTexID, ClearFlag.All, Color.clear);
commandBuffer.ClearRenderTarget(true, true, Color.clear);
DrawAllObjects(in commandBuffer);
}
/// <summary>
/// Draw all GhostObjects using the Ghost/Memory material
/// </summary>
private void DrawObjectsMemory(in CommandBuffer commandBuffer)
{
CoreUtils.SetRenderTarget(commandBuffer, m_MaskRenderTexID, ClearFlag.All, Color.clear);
commandBuffer.ClearRenderTarget(true, true, Color.clear);
DrawAllObjects(in commandBuffer, true);
}
/// <summary>
/// Creates the Blur mask RT. This Blurs all the GhostObjects
/// </summary>
private void BlurMask(in CommandBuffer commandBuffer)
{
commandBuffer.Blit(m_MaskRenderTexID, m_BlurPassRenderTexID);
for (int i = 0; i < m_BlurPasses; ++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_MemoryRenderTexID, renderTextureDescriptor, FilterMode.Bilinear);
cmd.GetTemporaryRT(m_MaskRenderTexID, 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)
{
#if UNITY_SWITCH
var width = 1280;
var height = 720;
#else
var width = m_Camera.scaledPixelWidth;
var height = m_Camera.scaledPixelHeight;
#endif
var aa = Mathf.Max(1, QualitySettings.antiAliasing);
cmd.GetTemporaryRT(m_MemoryRenderTexID, width, height, 24, FilterMode.Bilinear, RenderTextureFormat.ARGB32,
RenderTextureReadWrite.Default, aa);
cmd.GetTemporaryRT(m_MaskRenderTexID, width >> 1, height >> 1, 0, 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);
}
}
}