Files
Cielonos/Assets/OtherPlugins/ChocDino/UIFX/Runtime/Scripts/Common/Compositor.cs
SoulliesOfficial d94241f36c 场景设计
2026-01-12 03:22:16 -05:00

341 lines
11 KiB
C#

//--------------------------------------------------------------------------//
// Copyright 2023-2025 Chocolate Dinosaur Ltd. All rights reserved. //
// For full documentation visit https://www.chocolatedinosaur.com //
//--------------------------------------------------------------------------//
using System.Collections.Generic;
using UnityEngine;
namespace ChocDino.UIFX
{
internal class Compositor
{
private const string CompositeShaderPath = "Hidden/ChocDino/UIFX/Composite";
private static class ShaderPass
{
internal const int AlphaBlendedToPremultipliedAlpha = 0;
}
private RenderTexture _composite;
private Matrix4x4 _projectionMatrix;
private RenderTexture _prevRT;
private Material _compositeMaterial;
private Camera _prevCamera;
private Matrix4x4 _viewMatrix;
private List<Color> _vertexColors;
private List<Color> _vertexColorsLinear;
public bool IsTextureTooLarge { get; private set; }
private static bool _isTextureFormatCreated;
private static RenderTextureFormat _defaultTextureFormat;
private static RenderTextureFormat _defaultHdrTextureFormat;
public static RenderTextureFormat DefaultTextureFormat { get { return _defaultTextureFormat; } }
public static RenderTextureFormat DefaultHdrTextureFormat { get { return _defaultHdrTextureFormat; } }
private static void CreateTextureFormats()
{
Debug.Assert(!_isTextureFormatCreated);
_defaultTextureFormat = RenderTextureFormat.Default;
if (SystemInfo.SupportsRenderTextureFormat(RenderTextureFormat.ARGBHalf))
{
_defaultTextureFormat = RenderTextureFormat.ARGBHalf;
}
if ((Filters.PerfHint & PerformanceHint.UseLessPrecision) != 0)
{
if (SystemInfo.SupportsRenderTextureFormat(RenderTextureFormat.ARGB32))
{
_defaultTextureFormat = RenderTextureFormat.ARGB32;
}
}
_defaultHdrTextureFormat = RenderTextureFormat.DefaultHDR;
if (SystemInfo.SupportsRenderTextureFormat(RenderTextureFormat.ARGBHalf))
{
_defaultHdrTextureFormat = RenderTextureFormat.ARGBHalf;
}
}
public void FreeResources()
{
_vertexColorsLinear = null;
_vertexColors = null;
RenderTextureHelper.ReleaseTemporary(ref _composite);
ObjectHelper.Destroy(ref _compositeMaterial);
}
public bool Start(Camera camera, RectInt textureRect, bool forceHdr, float canvasScale = 1f)
{
if (_compositeMaterial == null)
{
_compositeMaterial = new Material(Shader.Find(CompositeShaderPath));
Debug.Assert(_compositeMaterial != null);
}
if (!_isTextureFormatCreated)
{
CreateTextureFormats();
_isTextureFormatCreated = true;
}
RectInt scaledTextureRect = textureRect;
scaledTextureRect.xMin = Mathf.FloorToInt(textureRect.xMin * canvasScale);
scaledTextureRect.yMin = Mathf.FloorToInt(textureRect.yMin * canvasScale);
scaledTextureRect.xMax = Mathf.CeilToInt(textureRect.xMax * canvasScale);
scaledTextureRect.yMax = Mathf.CeilToInt(textureRect.yMax * canvasScale);
IsTextureTooLarge = false;
if (scaledTextureRect.width > Filters.GetMaxiumumTextureSize() || scaledTextureRect.height > Filters.GetMaxiumumTextureSize())
{
IsTextureTooLarge = true;
// Modify the texture rectangle so it fits within the maximum supported texture size.
// NOTE: This will lead to lower image quality.
{
float aspect = (float)scaledTextureRect.width / (float)scaledTextureRect.height;
float origWidth = scaledTextureRect.width;
float origHeight = scaledTextureRect.height;
scaledTextureRect.width = Mathf.Min(scaledTextureRect.width, Filters.GetMaxiumumTextureSize());
scaledTextureRect.height = Mathf.Min(scaledTextureRect.height, Filters.GetMaxiumumTextureSize());
if (aspect > 1f)
{
scaledTextureRect.height = Mathf.CeilToInt(scaledTextureRect.width / aspect);
Debug.Assert(scaledTextureRect.height <= Filters.GetMaxiumumTextureSize());
scaledTextureRect.height = Mathf.Min(scaledTextureRect.height, Filters.GetMaxiumumTextureSize());
}
else
{
scaledTextureRect.width = Mathf.CeilToInt(scaledTextureRect.height * aspect);
Debug.Assert(scaledTextureRect.width <= Filters.GetMaxiumumTextureSize());
scaledTextureRect.width = Mathf.Min(scaledTextureRect.width, Filters.GetMaxiumumTextureSize());
}
}
}
RenderTextureFormat targetFormat = _defaultTextureFormat;
if (forceHdr)
{
targetFormat = _defaultHdrTextureFormat;
}
if (_composite != null && (_composite.width != scaledTextureRect.width || _composite.height != scaledTextureRect.height || _composite.format != targetFormat))
{
RenderTextureHelper.ReleaseTemporary(ref _composite);
}
if (scaledTextureRect.width <= 0 || scaledTextureRect.height <= 0)
{
return false;
}
if (_composite == null)
{
_composite = RenderTexture.GetTemporary(scaledTextureRect.width, scaledTextureRect.height, 0, targetFormat, RenderTextureReadWrite.Linear);
_composite.wrapMode = TextureWrapMode.Clamp;
#if UNITY_EDITOR
_composite.name = "RT-Composite" + Time.frameCount;
#endif
}
// Calculate our projection matrix, but cropped to the render area
{
if (camera == null)
{
// Note: in Overlay canvas mode, the z clip planes seem to be hardcoded to range [-1000f * Canvas.scaleFactor, 1000f * Canvas.scaleFactor]
_projectionMatrix = Matrix4x4.Ortho(textureRect.xMin, textureRect.xMax, textureRect.yMin, textureRect.yMax, -1000f * canvasScale, 1000f * canvasScale);
}
else
{
Rect rect = Rect.zero;
rect.x = textureRect.x;
rect.y = textureRect.y;
rect.xMax = textureRect.xMax;
rect.yMax = textureRect.yMax;
rect.x -= camera.pixelRect.x;
rect.y -= camera.pixelRect.y;
rect.x /= camera.pixelWidth;
rect.y /= camera.pixelHeight;
rect.width /= camera.pixelWidth;
rect.height /= camera.pixelHeight;
float inverseWidth = 1f / rect.width;
float inverseHeight = 1f / rect.height;
Matrix4x4 matrix1 = Matrix4x4.Translate(new Vector3(-rect.x * 2f * inverseWidth, -rect.y * 2f * inverseHeight, 0f));
Matrix4x4 matrix2 = Matrix4x4.Translate(new Vector3(inverseWidth - 1f, inverseHeight - 1f, 0f)) * Matrix4x4.Scale(new Vector3(inverseWidth, inverseHeight, 1f));
_projectionMatrix = matrix1 * matrix2 * camera.projectionMatrix;
//_projectionMatrix = Matrix4x4.Ortho(textureRect.xMin, textureRect.xMax, textureRect.yMin, textureRect.yMax, -1000f, 1000f);
}
}
_prevRT = RenderTexture.active;
_viewMatrix = Matrix4x4.identity;
if (camera)
{
_viewMatrix = camera.worldToCameraMatrix;
}
else
{
_viewMatrix = Matrix4x4.TRS(new Vector3(0f, 0f, -10f), Quaternion.identity, Vector3.one);
}
// NOTE: Camera.current can be non-null for example when draging the Camera.Size property with the Scene view visible
// because it is rendering to the scene view. In this case flickering can occur unless we use the below logic.
if (Camera.current != null)
{
_prevCamera = Camera.current;
_prevCamera.worldToCameraMatrix = _viewMatrix;
}
RenderTexture.active = _composite;
GL.Clear(false, true, Color.clear);
return true;
}
public void End()
{
_composite.IncrementUpdateCount();
if (_prevCamera)
{
_prevCamera.ResetWorldToCameraMatrix();
_prevCamera = null;
}
RenderTexture.active = _prevRT;
}
private void SaveMeshVertexColorsAndConvertToLinear(Mesh mesh)
{
int vertexCount = mesh.vertexCount;
if (_vertexColors != null && _vertexColors.Count != vertexCount)
{
_vertexColors = null;
_vertexColorsLinear = null;
}
if (_vertexColors == null)
{
_vertexColors = new List<Color>(vertexCount);
_vertexColorsLinear = new List<Color>(vertexCount);
}
mesh.GetColors(_vertexColors);
mesh.GetColors(_vertexColorsLinear);
// In some rare cases there can be no colors
if (_vertexColorsLinear.Count > 0)
{
Debug.Assert(_vertexColorsLinear.Count == vertexCount);
for (int i = 0; i < vertexCount; i++)
{
_vertexColorsLinear[i] = _vertexColorsLinear[i].linear;
}
mesh.SetColors(_vertexColorsLinear);
}
}
private void RestoreMeshVertexColors(Mesh mesh)
{
// In some rare cases there can be no colors
if (_vertexColors.Count > 0)
{
mesh.SetColors(_vertexColors);
}
}
public void AddMesh(Transform xform, Mesh mesh, Material material, bool materialOutputPremultipliedAlpha, bool convertVertexColorsFromGammaToLinear)
{
if (!mesh || !material) return;
// When in linear color-space, Unity's UI rendering system will convert mesh vertex colors from their native gamma to linear space.
// However when we render the meshes this automatic conversion doesn't happen, so we must do it manually. For most Graphic objects
// this can be done by modifying our copy of the mesh, but for TextMeshPro we can't modify those native meshes, so we have to make
// a copy, modify it, render it, and then restore the colors.
if (convertVertexColorsFromGammaToLinear)
{
Debug.Assert(QualitySettings.activeColorSpace == ColorSpace.Linear);
SaveMeshVertexColorsAndConvertToLinear(mesh);
}
if (materialOutputPremultipliedAlpha)
{
RenderMeshDirectly(xform, mesh, material);
}
else
{
int pass = ShaderPass.AlphaBlendedToPremultipliedAlpha;
RenderMeshWithAdjustment(xform, mesh, material, pass);
}
if (convertVertexColorsFromGammaToLinear)
{
RestoreMeshVertexColors(mesh);
}
}
private void RenderMeshDirectly(Transform xform, Mesh mesh, Material material)
{
RenderTexture.active = _composite;
RenderMeshToActiveTarget(xform, mesh, material);
}
private void RenderMeshWithAdjustment(Transform xform, Mesh mesh, Material material, int pass)
{
// If the material doesn't output premultiplied-alpha then first render normally and then
// blit to convert to premuliplied-alpha and composite it into the _composite buffer.
// NOTE: this assumed a standard alpha blend and doesn't support other blend modes.
var rtRawSource = RenderTexture.GetTemporary(_composite.width, _composite.height, 0, _composite.format, RenderTextureReadWrite.Linear);
rtRawSource.wrapMode = TextureWrapMode.Clamp;
#if UNITY_EDITOR
rtRawSource.name = "RT-RawSource";
#endif
RenderTexture.active = rtRawSource;
GL.Clear(false, true, Color.clear);
RenderMeshToActiveTarget(xform, mesh, material);
rtRawSource.IncrementUpdateCount();
// Blit to fix the alpha channel and composite using pre-multiplied alpha, or another adjustment.
Graphics.Blit(rtRawSource, _composite, _compositeMaterial, pass);
RenderTextureHelper.ReleaseTemporary(ref rtRawSource);
}
private void RenderMeshToActiveTarget(Transform xform, Mesh mesh, Material material)
{
GL.PushMatrix();
GL.LoadIdentity();
GL.modelview = _viewMatrix;
GL.LoadProjectionMatrix(GL.GetGPUProjectionMatrix(_projectionMatrix, false));
for (int i = 0; i < material.passCount; i++)
{
if (material.SetPass(i))
{
if (xform)
{
Graphics.DrawMeshNow(mesh, xform.localToWorldMatrix);
}
else
{
Graphics.DrawMeshNow(mesh, Matrix4x4.identity);
}
}
}
GL.PopMatrix();
}
public RenderTexture GetTexture()
{
return _composite;
}
}
}