Files
Shopping_UnityVR/Assets/UmbraSoftShadows/Runtime/Scripts/UmbraRenderFeature.cs
2026-04-16 04:58:10 +09:00

916 lines
47 KiB
C#

using System;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Experimental.Rendering;
using UnityEngine.Rendering;
using UnityEngine.Rendering.Universal;
#if UNITY_EDITOR
using UnityEditor;
#endif
namespace Umbra {
[DisallowMultipleRendererFeature("Umbra Render Feature")]
[Tooltip("Umbra Render Feature")]
internal partial class UmbraRenderFeature : ScreenSpaceShadows {
#if UNITY_EDITOR
[UnityEditor.ShaderKeywordFilter.SelectIf(true, keywordNames: ShaderKeywordStrings.MainLightShadowScreen)]
private const bool k_RequiresScreenSpaceShadowsKeyword = true;
#endif
static class ShaderParams {
readonly public static int MainTex = Shader.PropertyToID("_MainTex");
readonly public static int ShadowData = Shader.PropertyToID("_ShadowData");
readonly public static int ShadowData2 = Shader.PropertyToID("_ShadowData2");
readonly public static int ShadowData3 = Shader.PropertyToID("_ShadowData3");
readonly public static int ShadowData4 = Shader.PropertyToID("_ShadowData4");
readonly public static int BlurTemp = Shader.PropertyToID("_BlurTemp");
readonly public static int BlurTemp2 = Shader.PropertyToID("_BlurTemp2");
readonly public static int BlurScale = Shader.PropertyToID("_BlurScale");
readonly public static int BlurSpread = Shader.PropertyToID("_BlurSpread");
readonly public static int UmbraCascadeRects = Shader.PropertyToID("_UmbraCascadeRects");
readonly public static int UmbraCascadeScales = Shader.PropertyToID("_UmbraCascadeScales");
readonly public static int DownsampledDepth = Shader.PropertyToID("_DownsampledDepth");
readonly public static int NoiseTex = Shader.PropertyToID("_NoiseTex");
readonly public static int SourceSize = Shader.PropertyToID("_SourceSize");
readonly public static int BlendCascadeData = Shader.PropertyToID("_BlendCascadeData");
readonly public static int MaskTexture = Shader.PropertyToID("_MaskTex");
readonly public static int CameraDepthTexture = Shader.PropertyToID("_CameraDepthTexture");
readonly public static int CameraNormalsTexture = Shader.PropertyToID("_CameraNormalsTexture");
readonly public static int ContactShadowsSampleCount = Shader.PropertyToID("_ContactShadowsSampleCount");
readonly public static int ContactShadowsData1 = Shader.PropertyToID("_ContactShadowsData1");
readonly public static int ContactShadowsData2 = Shader.PropertyToID("_ContactShadowsData2");
readonly public static int ContactShadowsData3 = Shader.PropertyToID("_ContactShadowsData3");
readonly public static int ContactShadowsData4 = Shader.PropertyToID("_ContactShadowsData4");
readonly public static int ContactShadowsBlendMode = Shader.PropertyToID("_ContactShadowsBlend");
readonly public static int ReceiverPlaneAltitude = Shader.PropertyToID("_ReceiverPlaneAltitude");
readonly public static int OverlayShadowColor = Shader.PropertyToID("_OverlayShadowColor");
readonly public static int EarlyOutSamples = Shader.PropertyToID("_EarlyOutSamples");
readonly public static int PointLightPosition = Shader.PropertyToID("_PointLightPosition");
public static Vector3 Vector3Back = Vector3.back;
public static Vector3 Vector3Forward = Vector3.forward;
public const string SKW_LOOP_STEP_X3 = "_LOOP_STEP_X3";
public const string SKW_LOOP_STEP_X2 = "_LOOP_STEP_X2";
public const string SKW_PRESERVE_EDGES = "_PRESERVE_EDGES";
public const string SKW_BLUR_HQ = "_BLUR_HQ";
public const string SKW_NORMALS_TEXTURE = "_NORMALS_TEXTURE";
public const string SKW_CONTACT_HARDENING = "_CONTACT_HARDENING";
public const string SKW_MASK_TEXTURE = "_MASK_TEXTURE";
public const string SKW_RECEIVER_PLANE = "_RECEIVER_PLANE";
public const string SKW_USE_POINT_LIGHT = "_USE_POINT_LIGHT";
public const string SKW_CONTACT_SHADOWS_SOFT_EDGES = "_SOFT_EDGES";
}
enum Pass {
UmbraCastShadows,
BlurHoriz,
BlurVert,
BoxBlur,
ComposeWithBlending,
DownsampledDepth,
CascadeBlending,
UnityShadows,
ComposeUnity,
ContactShadows,
Compose,
DebugShadows,
ContactShadowsAfterOpaque,
OverlayShadows
}
struct CameraLocation {
public Vector3 position;
public Vector3 forward;
}
const string k_ShaderName = "Hidden/Kronnect/UmbraScreenSpaceShadows";
[Tooltip("Specify which cameras can render Umbra Soft Shadows")]
public LayerMask camerasLayerMask = -1;
public static UmbraSoftShadows settings;
public static int shadowLightIndex;
static int cachedShadowmapTimestap;
static bool usesCachedShadowmap;
readonly Dictionary<Camera, CameraLocation> cameraPrevLocation = new Dictionary<Camera, CameraLocation>();
static readonly Dictionary<Light, UmbraSoftShadows> umbraSettings = new Dictionary<Light, UmbraSoftShadows>();
Material mat;
UmbraScreenSpaceShadowsPass m_SSShadowsPass;
UmbraScreenSpaceShadowsPostPass m_SSShadowsPostPass;
UmbraDebugPass m_SSSShadowsDebugPass;
UmbraContactShadowsAfterOpaquePass m_ContactShadowsAfterOpaquePass;
UmbraOverlayShadows m_OverlayShadowsPass;
public static void RegisterUmbraLight (UmbraSoftShadows settings) {
Light light = settings.GetComponent<Light>();
if (light != null) {
if (light.type == LightType.Directional) {
umbraSettings[light] = settings;
}
else {
Debug.LogError("Umbra Soft Shadows only work on directiona light.");
}
}
}
public static void UnregisterUmbraLight (UmbraSoftShadows settings) {
Light light = settings.GetComponent<Light>();
if (light != null && umbraSettings.ContainsKey(light)) {
umbraSettings.Remove(light);
}
}
public override void Create () {
if (m_SSShadowsPass == null) {
m_SSShadowsPass = new UmbraScreenSpaceShadowsPass();
}
if (m_SSShadowsPostPass == null) {
m_SSShadowsPostPass = new UmbraScreenSpaceShadowsPostPass();
}
if (m_ContactShadowsAfterOpaquePass == null) {
m_ContactShadowsAfterOpaquePass = new UmbraContactShadowsAfterOpaquePass();
}
if (m_SSSShadowsDebugPass == null) {
m_SSSShadowsDebugPass = new UmbraDebugPass();
}
if (m_OverlayShadowsPass == null) {
m_OverlayShadowsPass = new UmbraOverlayShadows();
}
cachedShadowmapTimestap = -100;
LoadMaterial();
m_SSShadowsPass.renderPassEvent = RenderPassEvent.AfterRenderingGbuffer;
m_SSShadowsPostPass.renderPassEvent = RenderPassEvent.BeforeRenderingTransparents;
m_OverlayShadowsPass.renderPassEvent = RenderPassEvent.AfterRenderingOpaques + 1;
m_ContactShadowsAfterOpaquePass.renderPassEvent = RenderPassEvent.AfterRenderingOpaques + 1;
m_SSSShadowsDebugPass.renderPassEvent = RenderPassEvent.AfterRenderingTransparents;
}
void OnDisable () {
UmbraSoftShadows.installed = false;
}
protected override void Dispose (bool disposing) {
m_SSShadowsPass?.Dispose();
m_SSShadowsPass = null;
CoreUtils.Destroy(mat);
}
static bool SetupContactShadowsMaterial (Camera cam, UmbraProfile profile, Material mat) {
float intensityMultiplier = profile.contactShadowsIntensityMultiplier;
// Check if we should use point lights for contact shadows
mat.DisableKeyword(ShaderParams.SKW_USE_POINT_LIGHT);
if (settings.contactShadowsSource == ContactShadowsSource.PointLights) {
if (settings.pointLightsTrigger == null && Camera.main != null) {
settings.pointLightsTrigger = Camera.main.transform;
}
float fade = 0;
if (settings.pointLightsTrigger != null) {
Vector3 triggerPosition = settings.pointLightsTrigger.position;
// Find the point light that contains the trigger position
foreach (var kvp in UmbraPointLightContactShadows.umbraPointLights) {
UmbraPointLightContactShadows pointLightComponent = kvp.Value;
if (pointLightComponent == null) continue;
fade = pointLightComponent.ComputeVolumeFade(triggerPosition);
if (fade > 0) {
// Set point light data for the shader
Vector3 pointLightPosition = pointLightComponent.transform.position;
mat.SetVector(ShaderParams.PointLightPosition, new Vector4(pointLightPosition.x, pointLightPosition.y, pointLightPosition.z, 1.0f));
mat.EnableKeyword(ShaderParams.SKW_USE_POINT_LIGHT);
break; // Use the first point light found
}
}
}
if (fade <= 0) return false;
intensityMultiplier *= fade;
}
float farClipPlane = cam.farClipPlane;
UniversalRenderPipelineAsset urpAsset = GraphicsSettings.currentRenderPipeline as UniversalRenderPipelineAsset;
float shadowMaxDistance = urpAsset.shadowDistance;
mat.SetInt(ShaderParams.ContactShadowsSampleCount, profile.contactShadowsSampleCount);
float contactShadowsMaxDistance;
if (settings.profile.shadowSource == ShadowSource.UnityShadows || settings.profile.actualContactShadowsInjectionPoint == ContactShadowsInjectionPoint.AfterOpaque) {
contactShadowsMaxDistance = 1;
}
else {
contactShadowsMaxDistance = shadowMaxDistance / farClipPlane;
}
mat.SetVector(ShaderParams.ContactShadowsData1, new Vector4(profile.contactShadowsStepping, intensityMultiplier, profile.contactShadowsJitter, profile.contactShadowsDistanceFade));
mat.SetVector(ShaderParams.ContactShadowsData2, new Vector4(profile.contactShadowsStartDistance / farClipPlane, profile.contactShadowsStartDistanceFade / farClipPlane, contactShadowsMaxDistance, profile.contactShadowsNormalBias));
mat.SetVector(ShaderParams.ContactShadowsData3, new Vector4(profile.contactShadowsThicknessNear / farClipPlane, profile.contactShadowsThicknessDistanceMultiplier * 0.1f, profile.contactShadowsVignetteSize, profile.contactShadowsBias));
mat.SetVector(ShaderParams.ContactShadowsData4, new Vector4(profile.contactShadowsBiasFar + 0.0025f, profile.contactShadowsEdgeSoftness, profile.contactShadowsPlanarShadows ? 0f : 1f, 0));
mat.SetInt(ShaderParams.ContactShadowsBlendMode, settings.debugShadows ? (int)BlendMode.SrcAlpha : (int)BlendMode.OneMinusSrcAlpha);
// Enable/disable soft edges keyword
if (profile.contactShadowsSoftEdges) {
mat.EnableKeyword(ShaderParams.SKW_CONTACT_SHADOWS_SOFT_EDGES);
}
else {
mat.DisableKeyword(ShaderParams.SKW_CONTACT_SHADOWS_SOFT_EDGES);
}
return true;
}
static void SetupContactShadowsAfterOpaqueOnlyMaterial (UmbraProfile profile, Material mat) {
if ((UmbraSoftShadows.isDeferred && profile.normalsSource == NormalSource.GBufferNormals) || profile.effectiveNormalsSource == NormalSource.NormalsPass) {
mat.EnableKeyword(ShaderParams.SKW_NORMALS_TEXTURE);
}
else {
mat.DisableKeyword(ShaderParams.SKW_NORMALS_TEXTURE);
}
mat.DisableKeyword(ShaderParams.SKW_LOOP_STEP_X3);
mat.DisableKeyword(ShaderParams.SKW_LOOP_STEP_X2);
if (profile.loopStepOptimization == LoopStep.x3) {
mat.EnableKeyword(ShaderParams.SKW_LOOP_STEP_X3);
}
else if (profile.loopStepOptimization == LoopStep.x2) {
mat.EnableKeyword(ShaderParams.SKW_LOOP_STEP_X2);
}
if (profile.transparentReceiverPlane) {
mat.EnableKeyword(ShaderParams.SKW_RECEIVER_PLANE);
}
else {
mat.DisableKeyword(ShaderParams.SKW_RECEIVER_PLANE);
}
}
static bool IsOffscreenDepthTexture (ref CameraData cameraData) => cameraData.targetTexture != null && cameraData.targetTexture.format == RenderTextureFormat.Depth;
public override void AddRenderPasses (ScriptableRenderer renderer, ref RenderingData renderingData) {
UmbraSoftShadows.installed = true;
if (IsOffscreenDepthTexture(ref renderingData.cameraData))
return;
if (!LoadMaterial()) {
Debug.LogError("Umbra: can't load material");
return;
}
// Fetch settings from current main directional light
Light light = null;
shadowLightIndex = renderingData.lightData.mainLightIndex;
if (shadowLightIndex < 0) {
// fallback to static instance search in case that "Only Contact Shadows" option is used
if (UmbraSoftShadows.instance != null) {
light = UmbraSoftShadows.instance.GetComponent<Light>();
}
}
else {
light = renderingData.lightData.visibleLights[shadowLightIndex].light;
}
if (light == null) return;
if (!umbraSettings.TryGetValue(light, out settings)) return;
if (settings == null) return;
Camera cam = renderingData.cameraData.camera;
usesCachedShadowmap = cachedShadowmapTimestap == Time.frameCount - 1 && settings != null && settings.profile != null && settings.profile.frameSkipOptimization && Application.isPlaying;
if (usesCachedShadowmap) {
// test camera rotation/movement
Transform t = cam.transform;
Vector3 pos = t.position;
Vector3 fwd = t.forward;
bool camMoved = true;
if (cameraPrevLocation.TryGetValue(cam, out CameraLocation prevLocation)) {
float dx = pos.x - prevLocation.position.x;
float dy = pos.y - prevLocation.position.y;
float dz = pos.z - prevLocation.position.z;
if (dx < 0) dx = -dx;
if (dy < 0) dy = -dy;
if (dz < 0) dz = -dz;
float thresholdPosition = settings.profile.skipFrameMaxCameraDisplacement;
if (dx <= thresholdPosition && dy <= thresholdPosition && dz <= thresholdPosition) {
if (Vector3.Angle(prevLocation.forward, fwd) <= settings.profile.skipFrameMaxCameraRotation) {
camMoved = false;
}
}
}
if (camMoved) {
prevLocation.position = pos;
prevLocation.forward = fwd;
cameraPrevLocation[cam] = prevLocation;
usesCachedShadowmap = false;
}
}
UmbraSoftShadows.isDeferred = renderer is UniversalRenderer && ((UniversalRenderer)renderer).renderingModeRequested == RenderingMode.Deferred;
bool shouldEnqueue = ((camerasLayerMask & (1 << cam.gameObject.layer)) != 0) && m_SSShadowsPass.Setup(mat);
if (shouldEnqueue) {
bool allowMainLightShadows = renderingData.shadowData.supportsMainLightShadows && renderingData.lightData.mainLightIndex != -1 && renderingData.lightData.visibleLights[renderingData.lightData.mainLightIndex].light.shadowStrength > 0;
bool allowScreenSpaceShadows = allowMainLightShadows && (settings.profile == null || settings.profile.shadowSource != ShadowSource.OnlyContactShadows);
if (allowScreenSpaceShadows) {
m_SSShadowsPass.renderPassEvent = UmbraSoftShadows.isDeferred
? RenderPassEvent.AfterRenderingGbuffer
: RenderPassEvent.AfterRenderingPrePasses + 1; // We add 1 to ensure this happens after depth priming depth copy pass that might be scheduled
renderer.EnqueuePass(m_SSShadowsPass);
renderer.EnqueuePass(m_SSShadowsPostPass);
}
if (settings.profile != null) {
if (!settings.debugShadows && allowScreenSpaceShadows && settings.profile.overlayShadows && settings.profile.overlayShadowsIntensity > 0) {
renderer.EnqueuePass(m_OverlayShadowsPass);
}
if (settings.profile.contactShadows || settings.profile.shadowSource == ShadowSource.OnlyContactShadows) {
if (SetupContactShadowsMaterial(cam, settings.profile, mat)) {
if (settings.profile.shadowSource != ShadowSource.UmbraShadows || settings.profile.actualContactShadowsInjectionPoint == ContactShadowsInjectionPoint.AfterOpaque) {
SetupContactShadowsAfterOpaqueOnlyMaterial(settings.profile, mat);
m_ContactShadowsAfterOpaquePass.Setup();
renderer.EnqueuePass(m_ContactShadowsAfterOpaquePass);
}
}
}
}
if (allowScreenSpaceShadows && settings.debugShadows) {
m_SSSShadowsDebugPass.Setup(m_SSShadowsPass);
renderer.EnqueuePass(m_SSSShadowsDebugPass);
}
}
}
private bool LoadMaterial () {
if (mat != null) {
return true;
}
Shader shader = Shader.Find(k_ShaderName);
if (shader == null) {
return false;
}
mat = CoreUtils.CreateEngineMaterial(shader);
Texture2D noiseTex = Resources.Load<Texture2D>("Umbra/Textures/NoiseTex");
mat.SetTexture(ShaderParams.NoiseTex, noiseTex);
return mat != null;
}
private partial class UmbraScreenSpaceShadowsPass : ScriptableRenderPass {
static string m_ProfilerTag = "UmbraSoftShadows";
static ProfilingSampler m_ProfilingSampler = new ProfilingSampler(m_ProfilerTag);
public static Material mat;
static RTHandle m_RenderTarget, m_DownscaledRenderTarget;
static readonly Vector4[][] cascadeRects = {
new Vector4[] { new Vector4(0, 0, 1, 1), new Vector4(0, 0, 1, 1), new Vector4(0, 0, 1, 1), new Vector4(0, 0, 1, 1) },
new Vector4[] { new Vector4(0, 0, 0.5f, 1f), new Vector4(0.5f, 0, 1, 1f), new Vector4(0, 0, 1, 1), new Vector4(0, 0, 1, 1) },
new Vector4[] { new Vector4(0, 0, 0.5f, 0.5f), new Vector4(0.5f, 0, 1, 0.5f), new Vector4(0, 0.5f, 0.5f, 1), new Vector4(0.5f, 0.5f, 1, 1) },
new Vector4[] { new Vector4(0, 0, 0.5f, 0.5f), new Vector4(0.5f, 0, 1, 0.5f), new Vector4(0, 0.5f, 0.5f, 1), new Vector4(0.5f, 0.5f, 1, 1) }
};
static readonly Vector4[][] cascadeRectsWithPadding = {
new Vector4[] { new Vector4(0, 0, 1, 1), new Vector4(0, 0, 1, 1), new Vector4(0, 0, 1, 1), new Vector4(0, 0, 1, 1) },
new Vector4[] { new Vector4(0, 0, 0.5f, 1f), new Vector4(0.5f, 0, 1, 1f), new Vector4(0, 0, 1, 1), new Vector4(0, 0, 1, 1) },
new Vector4[] { new Vector4(0, 0, 0.5f, 0.5f), new Vector4(0.5f, 0, 1, 0.5f), new Vector4(0, 0.5f, 0.5f, 1), new Vector4(0.5f, 0.5f, 1, 1) },
new Vector4[] { new Vector4(0, 0, 0.5f, 0.5f), new Vector4(0.5f, 0, 1, 0.5f), new Vector4(0, 0.5f, 0.5f, 1), new Vector4(0.5f, 0.5f, 1, 1) }
};
static readonly float[] cascadeScales = { 1, 1, 1, 1 };
static RenderTextureDescriptor desc;
GraphicsFormat screenShadowTextureFormat;
public readonly Dictionary<Camera, RTHandle> shadowTextures = new Dictionary<Camera, RTHandle>();
static bool newShadowmap = true;
static readonly float[] autoCascadeScales = new float[4];
public void Dispose () {
foreach (var rt in shadowTextures.Values) {
rt?.Release();
}
shadowTextures.Clear();
m_RenderTarget?.Release();
m_DownscaledRenderTarget?.Release();
}
internal bool Setup (Material material) {
if (settings == null || !settings.enabled || settings.profile == null) return false;
mat = material;
UmbraProfile profile = settings.profile;
GraphicsFormat desiredFormat = profile.shadowSource == ShadowSource.UmbraShadows && profile.blurIterations > 0 && profile.enableContactHardening ? GraphicsFormat.R8G8_UNorm : GraphicsFormat.R8_UNorm;
#if UNITY_2023_1_OR_NEWER
screenShadowTextureFormat = SystemInfo.IsFormatSupported(desiredFormat, GraphicsFormatUsage.Linear | GraphicsFormatUsage.Render)
#else
screenShadowTextureFormat = RenderingUtils.SupportsGraphicsFormat(desiredFormat, FormatUsage.Linear | FormatUsage.Render)
#endif
? desiredFormat
: GraphicsFormat.B8G8R8A8_UNorm;
if (usesCachedShadowmap) {
ConfigureInput(ScriptableRenderPassInput.None);
}
else {
if (profile.shadowSource == ShadowSource.UmbraShadows && (profile.effectiveNormalsSource == NormalSource.NormalsPass || profile.downsample || profile.forceDepthPrepass)) {
ConfigureInput(ScriptableRenderPassInput.Depth | ScriptableRenderPassInput.Normal);
}
else {
ConfigureInput(ScriptableRenderPassInput.Depth);
}
}
return mat != null;
}
#if !UNITY_6000_3_OR_NEWER
#if UNITY_2023_3_OR_NEWER
[Obsolete]
#endif
public override void OnCameraSetup (CommandBuffer cmd, ref RenderingData renderingData) {
desc = renderingData.cameraData.cameraTargetDescriptor;
desc.depthBufferBits = 0;
desc.msaaSamples = 1;
desc.graphicsFormat = screenShadowTextureFormat;
if (settings == null || settings.profile == null) return;
UmbraProfile profile = settings.profile;
if (profile.downsample && !profile.preserveEdges) {
desc.width /= 2;
desc.height /= 2;
}
Camera cam = renderingData.cameraData.camera;
#if UNITY_EDITOR
if (profile.frameSkipOptimization && Application.isPlaying) {
#else
if (profile.frameSkipOptimization) {
#endif
newShadowmap = !shadowTextures.TryGetValue(cam, out m_RenderTarget);
}
#if UNITY_6000_0_OR_NEWER
if (RenderingUtils.ReAllocateHandleIfNeeded(ref m_RenderTarget, desc, FilterMode.Point, TextureWrapMode.Clamp, name: "_ScreenSpaceShadowmapTexture")) {
#else
if (RenderingUtils.ReAllocateIfNeeded(ref m_RenderTarget, desc, FilterMode.Point, TextureWrapMode.Clamp, name: "_ScreenSpaceShadowmapTexture")) {
#endif
newShadowmap = true;
}
if (newShadowmap) {
shadowTextures[cam] = m_RenderTarget;
}
cmd.SetGlobalTexture(m_RenderTarget.name, m_RenderTarget.nameID);
ConfigureTarget(m_RenderTarget);
ConfigureClear(ClearFlag.None, Color.white);
}
#endif
#if !UNITY_6000_3_OR_NEWER
#if UNITY_2023_3_OR_NEWER
[Obsolete]
#endif
public override void Execute (ScriptableRenderContext context, ref RenderingData renderingData) {
if (mat == null) {
Debug.LogError("Umbra material not initialized");
return;
}
UmbraProfile profile = settings.profile;
var cmd = renderingData.commandBuffer;
using (new ProfilingScope(cmd, m_ProfilingSampler)) {
int frameCount = Time.frameCount;
#if UNITY_EDITOR
bool useFrame = !Application.isPlaying || !profile.frameSkipOptimization || newShadowmap || !usesCachedShadowmap;
#else
bool useFrame = !profile.frameSkipOptimization || newShadowmap || !usesCachedShadowmap;
#endif
if (useFrame) {
cachedShadowmapTimestap = frameCount;
newShadowmap = false;
RTHandle shadowsHandle;
if (profile.downsample && profile.preserveEdges) {
desc.width /= 2;
desc.height /= 2;
#if UNITY_6000_0_OR_NEWER
RenderingUtils.ReAllocateHandleIfNeeded(ref m_DownscaledRenderTarget, desc, FilterMode.Point, TextureWrapMode.Clamp);
#else
RenderingUtils.ReAllocateIfNeeded(ref m_DownscaledRenderTarget, desc, FilterMode.Point, TextureWrapMode.Clamp);
#endif
shadowsHandle = m_DownscaledRenderTarget;
}
else {
shadowsHandle = m_RenderTarget;
}
int cascadeCount = renderingData.shadowData.mainLightShadowCascadesCount;
float farClipPlane = renderingData.cameraData.camera.farClipPlane;
UniversalRenderPipelineAsset urpAsset = GraphicsSettings.currentRenderPipeline as UniversalRenderPipelineAsset;
float shadowMaxDistance = urpAsset.shadowDistance;
Pass castShadowsPass;
if (profile.shadowSource == ShadowSource.UnityShadows) {
castShadowsPass = Pass.UnityShadows;
}
else {
castShadowsPass = Pass.UmbraCastShadows;
if (profile.transparentReceiverPlane) {
mat.EnableKeyword(ShaderParams.SKW_RECEIVER_PLANE);
cmd.SetGlobalFloat(ShaderParams.ReceiverPlaneAltitude, profile.receiverPlaneAltitude);
cmd.SetGlobalVector(ShaderParams.ShadowData3, new Vector4(10f, 0.001f, 0, profile.blurEdgeSharpness));
}
else {
mat.DisableKeyword(ShaderParams.SKW_RECEIVER_PLANE);
cmd.SetGlobalVector(ShaderParams.ShadowData3, new Vector4(profile.blurDepthAttenStart / farClipPlane, profile.blurDepthAttenLength / farClipPlane, profile.blurGrazingAttenuation, profile.blurEdgeSharpness));
}
cmd.SetGlobalVector(ShaderParams.ShadowData, new Vector4(profile.sampleCount, 1024 / Mathf.Pow(2, profile.posterization), profile.blurEdgeTolerance * 1000f, (profile.blurDepthAttenStart + profile.blurDepthAttenLength) / farClipPlane));
float shadowMaxDepth = profile.transparentReceiverPlane ? 2 : shadowMaxDistance / farClipPlane;
cmd.SetGlobalVector(ShaderParams.ShadowData2, new Vector4(1f - profile.contactStrength, profile.distantSpread, shadowMaxDepth, profile.lightSize * 0.02f));
cmd.SetGlobalVector(ShaderParams.ShadowData4, new Vector4(profile.occludersCount, profile.occludersSearchRadius * 0.02f, profile.contactStrength > 0 ? profile.contactStrengthKnee * 0.1f : 0.00001f, profile.maskScale));
cmd.SetGlobalVector(ShaderParams.SourceSize, new Vector4(desc.width, desc.height, 0, 0));
cmd.SetGlobalInt(ShaderParams.EarlyOutSamples, profile.earlyOutSamples);
if (cascadeCount > 1) {
VisibleLight shadowLight = renderingData.lightData.visibleLights[shadowLightIndex];
float shadowNearPlane = shadowLight.light.shadowNearPlane;
for (int k = 0; k < cascadeCount; k++) {
renderingData.cullResults.ComputeDirectionalShadowMatricesAndCullingPrimitives(shadowLightIndex,
k, cascadeCount, renderingData.shadowData.mainLightShadowCascadesSplit, urpAsset.mainLightShadowmapResolution, shadowNearPlane, out _, out Matrix4x4 proj,
out _);
Matrix4x4 invProj = proj.inverse;
autoCascadeScales[k] = Mathf.Abs(invProj.MultiplyPoint(ShaderParams.Vector3Back).z - invProj.MultiplyPoint(ShaderParams.Vector3Forward).z) / 100f;
}
Vector4[] cascadeRectsTemp = cascadeRectsWithPadding[cascadeCount - 1];
float texel = 1f / urpAsset.mainLightShadowmapResolution;
float padding = texel;
for (int c = 0; c < 4; c++) {
Vector4 defaultRect = cascadeRects[cascadeCount - 1][c];
Vector4 rect = cascadeRectsTemp[c];
rect.x = defaultRect.x + padding;
rect.y = defaultRect.y + padding;
rect.z = defaultRect.z - padding;
rect.w = defaultRect.w - padding;
cascadeRectsTemp[c] = rect;
}
cmd.SetGlobalVectorArray(ShaderParams.UmbraCascadeRects, cascadeRectsTemp);
cascadeScales[0] = profile.cascade1Scale * autoCascadeScales[0];
cascadeScales[1] = profile.cascade2Scale * autoCascadeScales[1];
cascadeScales[2] = profile.cascade3Scale * autoCascadeScales[2];
cascadeScales[3] = profile.cascade4Scale * autoCascadeScales[3];
cmd.SetGlobalFloatArray(ShaderParams.UmbraCascadeScales, cascadeScales);
}
if ((UmbraSoftShadows.isDeferred && profile.normalsSource == NormalSource.GBufferNormals) || profile.effectiveNormalsSource == NormalSource.NormalsPass || profile.downsample) {
mat.EnableKeyword(ShaderParams.SKW_NORMALS_TEXTURE);
}
else {
mat.DisableKeyword(ShaderParams.SKW_NORMALS_TEXTURE);
}
#if !UNITY_WEBGL
if (profile.enableContactHardening) {
mat.EnableKeyword(ShaderParams.SKW_CONTACT_HARDENING);
}
else
#endif
{
mat.DisableKeyword(ShaderParams.SKW_CONTACT_HARDENING);
}
mat.DisableKeyword(ShaderParams.SKW_LOOP_STEP_X3);
mat.DisableKeyword(ShaderParams.SKW_LOOP_STEP_X2);
if (profile.loopStepOptimization == LoopStep.x3) {
mat.EnableKeyword(ShaderParams.SKW_LOOP_STEP_X3);
}
else if (profile.loopStepOptimization == LoopStep.x2) {
mat.EnableKeyword(ShaderParams.SKW_LOOP_STEP_X2);
}
if (profile.style == Style.Textured && profile.maskTexture != null) {
mat.EnableKeyword(ShaderParams.SKW_MASK_TEXTURE);
mat.SetTexture(ShaderParams.MaskTexture, profile.maskTexture);
}
else {
mat.DisableKeyword(ShaderParams.SKW_MASK_TEXTURE);
}
}
// Resolve screen space shadows
Blitter.BlitCameraTexture(cmd, m_RenderTarget, shadowsHandle, mat, (int)castShadowsPass);
// Blend cascades 0 & 1
if (profile.shadowSource == ShadowSource.UmbraShadows) {
if (profile.blendCascades && cascadeCount > 1) {
cmd.SetGlobalVector(ShaderParams.BlendCascadeData, new Vector4(profile.cascade1BlendingStrength * 100f, profile.cascade2BlendingStrength * 100f, profile.cascade3BlendingStrength * 100f, 1f));
Blitter.BlitCameraTexture(cmd, shadowsHandle, shadowsHandle, mat, (int)Pass.CascadeBlending);
}
}
// Add contact shadows
if (profile.contactShadows) {
if (!settings.debugShadows && profile.actualContactShadowsInjectionPoint == ContactShadowsInjectionPoint.ShadowTexture) {
Blitter.BlitCameraTexture(cmd, shadowsHandle, shadowsHandle, mat, (int)Pass.ContactShadows);
}
}
// Downscale depth for upscaler
if (profile.downsample && profile.preserveEdges) {
RenderTextureDescriptor downscampledDepthDesc = desc;
downscampledDepthDesc.colorFormat = RenderTextureFormat.RFloat;
cmd.GetTemporaryRT(ShaderParams.DownsampledDepth, downscampledDepthDesc);
FullScreenBlit(cmd, ShaderParams.DownsampledDepth, mat, (int)Pass.DownsampledDepth);
mat.EnableKeyword(ShaderParams.SKW_PRESERVE_EDGES);
}
else {
mat.DisableKeyword(ShaderParams.SKW_PRESERVE_EDGES);
}
if (profile.shadowSource == ShadowSource.UnityShadows) {
if (profile.downsample && profile.preserveEdges) {
// upscale
FullScreenBlit(cmd, m_DownscaledRenderTarget, m_RenderTarget, mat, (int)Pass.ComposeUnity);
}
}
else {
if (profile.style == Style.Default && profile.blurIterations > 0) {
cmd.SetGlobalFloat(ShaderParams.BlurSpread, profile.blurSpread);
// perform blur
cmd.GetTemporaryRT(ShaderParams.BlurTemp, desc);
cmd.GetTemporaryRT(ShaderParams.BlurTemp2, desc);
cmd.SetGlobalFloat(ShaderParams.BlurScale, 1f);
RenderTargetIdentifier shadowsRT = shadowsHandle;
RenderTargetIdentifier blurredRT = ShaderParams.BlurTemp2;
if (profile.blurType == BlurType.Box) {
for (int k = 0; k < profile.blurIterations; k++) {
blurredRT = (k % 2) == 0 ? ShaderParams.BlurTemp2 : ShaderParams.BlurTemp;
FullScreenBlit(cmd, shadowsRT, blurredRT, mat, (int)Pass.BoxBlur);
shadowsRT = blurredRT;
cmd.SetGlobalFloat(ShaderParams.BlurScale, k + 1f);
}
}
else {
if (profile.blurType == BlurType.Gaussian15) {
mat.EnableKeyword(ShaderParams.SKW_BLUR_HQ);
}
else {
mat.DisableKeyword(ShaderParams.SKW_BLUR_HQ);
}
FullScreenBlit(cmd, shadowsRT, ShaderParams.BlurTemp, mat, (int)Pass.BlurHoriz);
cmd.SetGlobalFloat(ShaderParams.BlurScale, 1f);
for (int k = 0; k < profile.blurIterations - 1; k++) {
FullScreenBlit(cmd, ShaderParams.BlurTemp, ShaderParams.BlurTemp2, mat, (int)Pass.BlurVert);
cmd.SetGlobalFloat(ShaderParams.BlurScale, k + 2f);
FullScreenBlit(cmd, ShaderParams.BlurTemp2, ShaderParams.BlurTemp, mat, (int)Pass.BlurHoriz);
}
FullScreenBlit(cmd, ShaderParams.BlurTemp, ShaderParams.BlurTemp2, mat, (int)Pass.BlurVert);
}
// blit blurred shadows
FullScreenBlit(cmd, blurredRT, m_RenderTarget, mat, (int)Pass.ComposeWithBlending);
}
else if (profile.downsample && profile.preserveEdges) {
// upscale
FullScreenBlit(cmd, m_DownscaledRenderTarget, m_RenderTarget, mat, (int)Pass.Compose);
}
}
}
CoreUtils.SetKeyword(cmd, ShaderKeywordStrings.MainLightShadows, false);
CoreUtils.SetKeyword(cmd, ShaderKeywordStrings.MainLightShadowCascades, false);
CoreUtils.SetKeyword(cmd, ShaderKeywordStrings.MainLightShadowScreen, true);
}
}
#endif
static Mesh _fullScreenMesh;
static Mesh fullscreenMesh {
get {
if (_fullScreenMesh != null) {
return _fullScreenMesh;
}
float num = 1f;
float num2 = 0f;
Mesh val = new Mesh();
_fullScreenMesh = val;
_fullScreenMesh.SetVertices(new List<Vector3> {
new Vector3 (-1f, -1f, 0f),
new Vector3 (-1f, 1f, 0f),
new Vector3 (1f, -1f, 0f),
new Vector3 (1f, 1f, 0f)
});
_fullScreenMesh.SetUVs(0, new List<Vector2> {
new Vector2 (0f, num2),
new Vector2 (0f, num),
new Vector2 (1f, num2),
new Vector2 (1f, num)
});
_fullScreenMesh.SetIndices(new int[6] { 0, 1, 2, 2, 1, 3 }, (MeshTopology)0, 0, false);
_fullScreenMesh.UploadMeshData(true);
return _fullScreenMesh;
}
}
static void FullScreenBlit (CommandBuffer cmd, RenderTargetIdentifier destination, Material material, int passIndex) {
destination = new RenderTargetIdentifier(destination, 0, CubemapFace.Unknown, -1);
cmd.SetRenderTarget(destination);
cmd.DrawMesh(fullscreenMesh, Matrix4x4.identity, material, 0, passIndex);
}
static void FullScreenBlit (CommandBuffer cmd, RenderTargetIdentifier source, RenderTargetIdentifier destination, Material material, int passIndex) {
destination = new RenderTargetIdentifier(destination, 0, CubemapFace.Unknown, -1);
cmd.SetRenderTarget(destination);
cmd.SetGlobalTexture(ShaderParams.MainTex, source);
cmd.DrawMesh(fullscreenMesh, Matrix4x4.identity, material, 0, passIndex);
}
}
private partial class UmbraScreenSpaceShadowsPostPass : ScriptableRenderPass {
// Profiling tag
private static string m_ProfilerTag = "Umbra Screen Space Shadows Post Pass";
private static ProfilingSampler m_ProfilingSampler = new ProfilingSampler(m_ProfilerTag);
private static readonly RTHandle k_CurrentActive = RTHandles.Alloc(BuiltinRenderTextureType.CurrentActive);
#if !UNITY_6000_3_OR_NEWER
#if UNITY_2023_3_OR_NEWER
[Obsolete]
#endif
public override void Configure (CommandBuffer cmd, RenderTextureDescriptor cameraTextureDescriptor) {
ConfigureTarget(k_CurrentActive);
}
#endif
#if !UNITY_6000_3_OR_NEWER
#if UNITY_2023_3_OR_NEWER
[Obsolete]
#endif
public override void Execute (ScriptableRenderContext context, ref RenderingData renderingData) {
var cmd = renderingData.commandBuffer;
using (new ProfilingScope(cmd, m_ProfilingSampler)) {
ShadowData shadowData = renderingData.shadowData;
int cascadesCount = shadowData.mainLightShadowCascadesCount;
bool mainLightShadows = renderingData.shadowData.supportsMainLightShadows;
bool receiveShadowsNoCascade = mainLightShadows && cascadesCount == 1;
bool receiveShadowsCascades = mainLightShadows && cascadesCount > 1;
// Before transparent object pass, force to disable screen space shadow of main light
CoreUtils.SetKeyword(cmd, ShaderKeywordStrings.MainLightShadowScreen, false);
// then enable main light shadows with or without cascades
CoreUtils.SetKeyword(cmd, ShaderKeywordStrings.MainLightShadows, receiveShadowsNoCascade);
CoreUtils.SetKeyword(cmd, ShaderKeywordStrings.MainLightShadowCascades, receiveShadowsCascades);
}
}
#endif
}
private partial class UmbraDebugPass : ScriptableRenderPass {
// Profiling tag
private static string m_ProfilerTag = "Umbra Debug Pass";
private static ProfilingSampler m_ProfilingSampler = new ProfilingSampler(m_ProfilerTag);
RTHandle source;
static UmbraScreenSpaceShadowsPass shadowPass;
public void Setup (UmbraScreenSpaceShadowsPass shadowPass) {
UmbraDebugPass.shadowPass = shadowPass;
if (settings.debugShadows) {
ConfigureInput(ScriptableRenderPassInput.Depth);
}
}
#if !UNITY_6000_3_OR_NEWER
#if UNITY_2023_3_OR_NEWER
[Obsolete]
#endif
public override void OnCameraSetup (CommandBuffer cmd, ref RenderingData renderingData) {
source = renderingData.cameraData.renderer.cameraColorTargetHandle;
}
#if UNITY_2023_3_OR_NEWER
[Obsolete]
#endif
public override void Execute (ScriptableRenderContext context, ref RenderingData renderingData) {
Material mat = UmbraScreenSpaceShadowsPass.mat;
if (mat == null) return;
RTHandle shadows = null;
Camera cam = renderingData.cameraData.camera;
Dictionary<Camera, RTHandle> shadowTextures = shadowPass.shadowTextures;
if (shadowTextures != null) {
shadows = shadowTextures[cam];
}
if (shadows == null) return;
var cmd = renderingData.commandBuffer;
using (new ProfilingScope(cmd, m_ProfilingSampler)) {
Blitter.BlitCameraTexture(cmd, shadows, source, mat, (int)Pass.DebugShadows);
if (settings.debugShadows && settings.profile != null && settings.profile.contactShadows) {
Blitter.BlitCameraTexture(cmd, source, source, mat, (int)Pass.ContactShadowsAfterOpaque);
}
}
}
#endif
}
private partial class UmbraContactShadowsAfterOpaquePass : ScriptableRenderPass {
// Profiling tag
private static string m_ProfilerTag = "Umbra Contact Shadows After Opaque Pass";
private static ProfilingSampler m_ProfilingSampler = new ProfilingSampler(m_ProfilerTag);
RTHandle source;
public void Setup () {
ConfigureInput(ScriptableRenderPassInput.Depth);
}
#if !UNITY_6000_3_OR_NEWER
#if UNITY_2023_3_OR_NEWER
[Obsolete]
#endif
public override void OnCameraSetup (CommandBuffer cmd, ref RenderingData renderingData) {
source = renderingData.cameraData.renderer.cameraColorTargetHandle;
ConfigureTarget(source);
}
#if UNITY_2023_3_OR_NEWER
[Obsolete]
#endif
public override void Execute (ScriptableRenderContext context, ref RenderingData renderingData) {
Material mat = UmbraScreenSpaceShadowsPass.mat;
if (mat == null || source.rt == null) return;
var cmd = renderingData.commandBuffer;
using (new ProfilingScope(cmd, m_ProfilingSampler)) {
UmbraProfile profile = settings.profile;
if (profile.transparentReceiverPlane) {
cmd.SetGlobalFloat(ShaderParams.ReceiverPlaneAltitude, profile.receiverPlaneAltitude);
}
cmd.SetGlobalVector(ShaderParams.SourceSize, new Vector4(source.rt.width, source.rt.height, 0, 0));
Blitter.BlitCameraTexture(cmd, source, source, mat, (int)Pass.ContactShadowsAfterOpaque);
}
}
#endif
}
private partial class UmbraOverlayShadows : ScriptableRenderPass {
// Profiling tag
private static string m_ProfilerTag = "Umbra Overlay Shadows";
private static ProfilingSampler m_ProfilingSampler = new ProfilingSampler(m_ProfilerTag);
RTHandle source;
#if !UNITY_6000_3_OR_NEWER
#if UNITY_2023_3_OR_NEWER
[Obsolete]
#endif
public override void OnCameraSetup (CommandBuffer cmd, ref RenderingData renderingData) {
source = renderingData.cameraData.renderer.cameraColorTargetHandle;
ConfigureTarget(source);
}
#if UNITY_2023_3_OR_NEWER
[Obsolete]
#endif
public override void Execute (ScriptableRenderContext context, ref RenderingData renderingData) {
Material mat = UmbraScreenSpaceShadowsPass.mat;
if (mat == null || source == null) return;
var cmd = renderingData.commandBuffer;
using (new ProfilingScope(cmd, m_ProfilingSampler)) {
Color shadowColor = settings.profile.overlayShadowsColor;
shadowColor.a = settings.profile.overlayShadowsIntensity;
mat.SetColor(ShaderParams.OverlayShadowColor, shadowColor);
Blitter.BlitCameraTexture(cmd, source, source, mat, (int)Pass.OverlayShadows);
}
}
#endif
}
}
}