|
|
- using System.Collections.Generic;
- using LeTai.Effects;
- using UnityEngine;
- using UnityEngine.Rendering;
-
- namespace LeTai.TrueShadow
- {
- public class ShadowFactory
- {
- private static ShadowFactory instance;
- public static ShadowFactory Instance => instance ?? (instance = new ShadowFactory());
-
- readonly Dictionary<int, ShadowContainer> shadowCache =
- new Dictionary<int, ShadowContainer>();
-
- readonly CommandBuffer cmd;
- readonly MaterialPropertyBlock materialProps;
- readonly ScalableBlur blurProcessor;
- readonly ScalableBlurConfig blurConfig;
-
- Material cutoutMaterial;
- Material imprintPostProcessMaterial;
- Material shadowPostProcessMaterial;
-
- Material CutoutMaterial =>
- cutoutMaterial ? cutoutMaterial : cutoutMaterial = new Material(Shader.Find("Hidden/TrueShadow/Cutout"));
-
- Material ImprintPostProcessMaterial =>
- imprintPostProcessMaterial
- ? imprintPostProcessMaterial
- : imprintPostProcessMaterial = new Material(Shader.Find("Hidden/TrueShadow/ImprintPostProcess"));
-
- Material ShadowPostProcessMaterial =>
- shadowPostProcessMaterial
- ? shadowPostProcessMaterial
- : shadowPostProcessMaterial = new Material(Shader.Find("Hidden/TrueShadow/PostProcess"));
-
- private ShadowFactory()
- {
- cmd = new CommandBuffer {name = "Shadow Commands"};
- materialProps = new MaterialPropertyBlock();
- materialProps.SetVector(ShaderId.CLIP_RECT,
- new Vector4(float.NegativeInfinity, float.NegativeInfinity,
- float.PositiveInfinity, float.PositiveInfinity));
- materialProps.SetInt(ShaderId.COLOR_MASK, (int) ColorWriteMask.All); // Render shadow even if mask hide graphic
-
- ShaderProperties.Init(8);
- blurConfig = ScriptableObject.CreateInstance<ScalableBlurConfig>();
- blurConfig.hideFlags = HideFlags.HideAndDontSave;
- blurProcessor = new ScalableBlur();
- blurProcessor.Configure(blurConfig);
- }
-
- ~ShadowFactory()
- {
- cmd.Dispose();
- Utility.SafeDestroy(blurConfig);
- Utility.SafeDestroy(cutoutMaterial);
- Utility.SafeDestroy(imprintPostProcessMaterial);
- }
-
- #if LETAI_TRUESHADOW_DEBUG
- RenderTexture debugTexture;
- #endif
-
- // public int createdContainerCount;
- // public int releasedContainerCount;
-
- internal void Get(ShadowSettingSnapshot snapshot, ref ShadowContainer container)
- {
- if (float.IsNaN(snapshot.dimensions.x) || snapshot.dimensions.x < 1 ||
- float.IsNaN(snapshot.dimensions.y) || snapshot.dimensions.y < 1)
- {
- ReleaseContainer(container);
- return;
- }
-
- #if LETAI_TRUESHADOW_DEBUG
- RenderTexture.ReleaseTemporary(debugTexture);
- if (snapshot.shadow.alwaysRender)
- debugTexture = GenerateShadow(snapshot).Texture;
- #endif
-
- // Each request need a coresponding shadow texture
- // Texture may be shared by multiple elements
- // Texture are released when no longer used by any element
- // ShadowContainer keep track of texture and their usage
-
-
- int requestHash = snapshot.GetHashCode();
-
- // Case: requester can keep the same texture
- if (container?.requestHash == requestHash)
- return;
-
- ReleaseContainer(container);
-
- if (shadowCache.TryGetValue(requestHash, out var existingContainer))
- {
- // Case: requester got texture from someone else
- existingContainer.RefCount++;
- container = existingContainer;
- }
- else
- {
- // Case: requester got new unique texture
- container = shadowCache[requestHash] = GenerateShadow(snapshot);
- // Debug.Log($"Created new container for request\t{requestHash}\tTotal Created: {++createdContainerCount}\t Alive: {createdContainerCount - releasedContainerCount}");
- }
- }
-
- internal void ReleaseContainer(ShadowContainer container)
- {
- if (container == null)
- return;
-
- if (--container.RefCount > 0)
- return;
-
- RenderTexture.ReleaseTemporary(container.Texture);
- shadowCache.Remove(container.requestHash);
-
- // Debug.Log($"Released container for request\t{container.requestHash}\tTotal Released: {++releasedContainerCount}\t Alive: {createdContainerCount - releasedContainerCount}");
- }
-
- static readonly Rect UNIT_RECT = new Rect(0, 0, 1, 1);
-
- ShadowContainer GenerateShadow(ShadowSettingSnapshot snapshot)
- {
- // return GenColoredTexture(request.GetHashCode());
-
- cmd.Clear();
- cmd.BeginSample("TrueShadow:Capture");
-
- var bounds = snapshot.shadow.SpriteMesh.bounds;
- var misalignment = CalcMisalignment(snapshot.canvas, snapshot.canvasRt, snapshot.shadow.RectTransform, bounds);
-
- var padding = Mathf.CeilToInt(snapshot.size);
- var imprintViewW = Mathf.RoundToInt(snapshot.dimensions.x + misalignment.bothSS.x);
- var imprintViewH = Mathf.RoundToInt(snapshot.dimensions.y + misalignment.bothSS.y);
- var tw = imprintViewW + padding * 2;
- var th = imprintViewH + padding * 2;
-
- var shadowTex = RenderTexture.GetTemporary(tw, th, 0, RenderTextureFormat.ARGB32);
- var imprintTexDesc = shadowTex.descriptor;
- imprintTexDesc.msaaSamples = snapshot.shouldAntialiasImprint ? Mathf.Max(1, QualitySettings.antiAliasing) : 1;
- var imprintTex = RenderTexture.GetTemporary(imprintTexDesc);
-
- RenderTexture imprintTexProcessed = null;
-
- bool needProcessImprint = snapshot.shadow.IgnoreCasterColor || snapshot.shadow.Inset;
- if (needProcessImprint)
- imprintTexProcessed = RenderTexture.GetTemporary(imprintTexDesc);
-
- var texture = snapshot.shadow.Content;
- if (texture)
- materialProps.SetTexture(ShaderId.MAIN_TEX, texture);
- else
- materialProps.SetTexture(ShaderId.MAIN_TEX, Texture2D.whiteTexture);
-
- cmd.SetRenderTarget(imprintTex);
- cmd.ClearRenderTarget(true, true, snapshot.shadow.ClearColor);
-
- cmd.SetViewport(new Rect(padding, padding, imprintViewW, imprintViewH));
-
- var imprintBoundMin = (Vector2) bounds.min - misalignment.minLS;
- var imprintBoundMax = (Vector2) bounds.max + misalignment.maxLS;
- cmd.SetViewProjectionMatrices(
- Matrix4x4.identity,
- Matrix4x4.Ortho(imprintBoundMin.x, imprintBoundMax.x,
- imprintBoundMin.y, imprintBoundMax.y,
- -1, 1)
- );
-
- snapshot.shadow.ModifyShadowCastingMesh(snapshot.shadow.SpriteMesh);
- snapshot.shadow.ModifyShadowCastingMaterialProperties(materialProps);
- cmd.DrawMesh(snapshot.shadow.SpriteMesh,
- Matrix4x4.identity,
- snapshot.shadow.GetShadowCastingMaterial(),
- 0, 0,
- materialProps);
-
- if (needProcessImprint)
- {
- ImprintPostProcessMaterial.SetKeyword("BLEACH", snapshot.shadow.IgnoreCasterColor);
- ImprintPostProcessMaterial.SetKeyword("INSET", snapshot.shadow.Inset);
-
- cmd.Blit(imprintTex, imprintTexProcessed, ImprintPostProcessMaterial);
- }
-
- cmd.EndSample("TrueShadow:Capture");
-
- var needPostProcess = snapshot.shadow.Spread > 1e-3;
-
- cmd.BeginSample("TrueShadow:Cast");
- RenderTexture blurSrc = needProcessImprint ? imprintTexProcessed : imprintTex;
- RenderTexture blurDst;
- if (needPostProcess)
- blurDst = RenderTexture.GetTemporary(shadowTex.descriptor);
- else
- blurDst = shadowTex;
-
- if (snapshot.size < 1e-2)
- {
- cmd.Blit(blurSrc, blurDst);
- }
- else
- {
- blurConfig.Strength = snapshot.size;
- blurProcessor.Blur(cmd, blurSrc, UNIT_RECT, blurDst);
- }
-
- cmd.EndSample("TrueShadow:Cast");
-
- var relativeOffset = new Vector2(snapshot.canvasRelativeOffset.x / tw,
- snapshot.canvasRelativeOffset.y / th);
- var overflowAlpha = snapshot.shadow.Inset ? 1 : 0;
- if (needPostProcess)
- {
- cmd.BeginSample("TrueShadow:PostProcess");
-
- ShadowPostProcessMaterial.SetTexture(ShaderId.SHADOW_TEX, blurDst);
- ShadowPostProcessMaterial.SetVector(ShaderId.OFFSET, relativeOffset);
- ShadowPostProcessMaterial.SetFloat(ShaderId.OVERFLOW_ALPHA, overflowAlpha);
- ShadowPostProcessMaterial.SetFloat(ShaderId.ALPHA_MULTIPLIER,
- 1f / Mathf.Max(1e-6f, 1f - snapshot.shadow.Spread));
-
- cmd.SetViewport(UNIT_RECT);
- cmd.Blit(blurSrc, shadowTex, ShadowPostProcessMaterial);
-
- cmd.EndSample("TrueShadow:PostProcess");
- }
- else if (snapshot.shadow.Cutout)
- {
- cmd.BeginSample("TrueShadow:Cutout");
-
- CutoutMaterial.SetVector(ShaderId.OFFSET, relativeOffset);
- CutoutMaterial.SetFloat(ShaderId.OVERFLOW_ALPHA, overflowAlpha);
-
- cmd.SetViewport(UNIT_RECT);
- cmd.Blit(blurSrc, shadowTex, CutoutMaterial);
-
- cmd.EndSample("TrueShadow:Cutout");
- }
-
- Graphics.ExecuteCommandBuffer(cmd);
-
- RenderTexture.ReleaseTemporary(imprintTex);
- RenderTexture.ReleaseTemporary(blurSrc);
- if (needPostProcess)
- RenderTexture.ReleaseTemporary(blurDst);
-
- return new ShadowContainer(shadowTex, snapshot, padding, misalignment.minLS);
- }
-
- readonly struct PixelMisalignment
- {
- public readonly Vector2 bothSS;
- public readonly Vector2 minLS;
- public readonly Vector2 maxLS;
-
- public PixelMisalignment(Vector2 bothSS, Vector2 minLS, Vector2 maxLS)
- {
- this.bothSS = bothSS;
- this.minLS = minLS;
- this.maxLS = maxLS;
- }
- }
-
- PixelMisalignment CalcMisalignment(Canvas canvas, RectTransform canvasRt, RectTransform casterRt, Bounds meshBound)
- {
- PixelMisalignment misalignment;
-
- if (canvas.renderMode == RenderMode.WorldSpace)
- {
- misalignment = new PixelMisalignment();
- }
- else
- {
- var referenceCamera = canvas.renderMode == RenderMode.ScreenSpaceCamera ? canvas.worldCamera : null;
-
- var pxMisalignmentAtMin = casterRt.LocalToScreenPoint(meshBound.min, referenceCamera).Frac();
- var pxMisalignmentAtMax =
- Vector2.one - casterRt.LocalToScreenPoint(meshBound.max, referenceCamera).Frac();
- if (pxMisalignmentAtMax.x > 1 - 1e-5)
- pxMisalignmentAtMax.x = 0;
- if (pxMisalignmentAtMax.y > 1 - 1e-5)
- pxMisalignmentAtMax.y = 0;
-
- misalignment = new PixelMisalignment(
- pxMisalignmentAtMin + pxMisalignmentAtMax,
- canvasRt.ScreenToCanvasSize(pxMisalignmentAtMin, referenceCamera),
- canvasRt.ScreenToCanvasSize(pxMisalignmentAtMax, referenceCamera)
- );
- }
-
- return misalignment;
- }
-
- RenderTexture GenColoredTexture(int hash)
- {
- var tex = new Texture2D(1, 1);
- tex.SetPixels32(new[] {new Color32((byte) (hash >> 8), (byte) (hash >> 16), (byte) (hash >> 24), 255)});
- tex.Apply();
-
- var rt = RenderTexture.GetTemporary(1, 1);
- Graphics.Blit(tex, rt);
-
- return rt;
- }
- }
- }
|