UnityGame/Library/PackageCache/com.unity.render-pipelines.core/Runtime/Utilities/TextureGradient.cs

265 lines
10 KiB
C#
Raw Normal View History

2024-10-27 10:53:47 +03:00
using System;
using System.Runtime.CompilerServices;
using UnityEngine.Experimental.Rendering;
namespace UnityEngine.Rendering
{
// Due to limitations in the builtin Gradient we need this custom wrapper.
/// <summary>
/// A wrapper around <c>Gradient</c> to automatically bake it into a texture.
/// </summary>
[Serializable]
public class TextureGradient : IDisposable
{
/// <summary>
/// Texture Size computed.
/// </summary>
[field: SerializeField, HideInInspector]
public int textureSize { get; private set; }
/// <summary>
/// Internal Gradient used to generate the Texture
/// </summary>
[SerializeField]
Gradient m_Gradient;
Texture2D m_Texture = null;
int m_RequestedTextureSize = -1;
bool m_IsTextureDirty;
bool m_Precise;
/// <summary>All color keys defined in the gradient.</summary>
[HideInInspector]
public GradientColorKey[] colorKeys => m_Gradient?.colorKeys;
/// <summary>All alpha keys defined in the gradient.</summary>
[HideInInspector]
public GradientAlphaKey[] alphaKeys => m_Gradient?.alphaKeys;
/// <summary>Controls how the gradient colors are interpolated.</summary>
[SerializeField, HideInInspector]
public GradientMode mode = GradientMode.PerceptualBlend;
/// <summary>Indicates the color space that the gradient color keys are using.</summary>
[SerializeField, HideInInspector]
public ColorSpace colorSpace = ColorSpace.Uninitialized;
/// <summary>
/// Creates a new <see cref="TextureGradient"/> from an existing <c>Gradient</c>.
/// </summary>
/// <param name="baseCurve">The source <c>Gradient</c>.</param>
public TextureGradient(Gradient baseCurve)
: this(baseCurve.colorKeys, baseCurve.alphaKeys)
{
mode = baseCurve.mode;
colorSpace = baseCurve.colorSpace;
m_Gradient.mode = baseCurve.mode;
m_Gradient.colorSpace = baseCurve.colorSpace;
}
/// <summary>
/// Creates a new <see cref="TextureCurve"/> from an arbitrary number of keyframes.
/// </summary>
/// <param name="colorKeys">An array of keyframes used to define the color of gradient.</param>
/// <param name="alphaKeys">An array of keyframes used to define the alpha of gradient.</param>
/// <param name="mode">Indicates the color space that the gradient color keys are using.</param>
/// <param name="colorSpace">Controls how the gradient colors are interpolated.</param>
/// <param name="requestedTextureSize">Texture Size used internally, if '-1' using Nyquist-Shannon limits.</param>
/// <param name="precise">if precise uses 4*Nyquist-Shannon limits, 2* otherwise.</param>
public TextureGradient(GradientColorKey[] colorKeys, GradientAlphaKey[] alphaKeys, GradientMode mode = GradientMode.PerceptualBlend, ColorSpace colorSpace = ColorSpace.Uninitialized, int requestedTextureSize = -1, bool precise = false)
{
Rebuild(colorKeys, alphaKeys, mode, colorSpace, requestedTextureSize, precise);
}
void Rebuild(GradientColorKey[] colorKeys, GradientAlphaKey[] alphaKeys, GradientMode mode, ColorSpace colorSpace, int requestedTextureSize, bool precise)
{
m_Gradient = new Gradient();
m_Gradient.mode = mode;
m_Gradient.colorSpace = colorSpace;
m_Gradient.SetKeys(colorKeys, alphaKeys);
m_Precise = precise;
m_RequestedTextureSize = requestedTextureSize;
if (requestedTextureSize > 0)
{
textureSize = requestedTextureSize;
}
else
{
float smallestDelta = 1.0f;
float[] times = new float[colorKeys.Length + alphaKeys.Length];
for (int i = 0; i < colorKeys.Length; ++i)
{
times[i] = colorKeys[i].time;
}
for (int i = 0; i < alphaKeys.Length; ++i)
{
times[colorKeys.Length + i] = alphaKeys[i].time;
}
Array.Sort(times);
// Found the smallest increment between 2 keys
for (int i = 1; i < times.Length; ++i)
{
int k0 = Math.Max(i - 1, 0);
int k1 = Math.Min(i, times.Length - 1);
float delta = Mathf.Abs(times[k0] - times[k1]);
// Do not compare if time is duplicated
if (delta > 0 && delta < smallestDelta)
smallestDelta = delta;
}
// Nyquist-Shannon
// smallestDelta: 1.00f => Sampling => 2
// smallestDelta: 0.50f => Sampling => 3
// smallestDelta: 0.33f => Sampling => 4
// smallestDelta: 0.25f => Sampling => 5
// 2x: Theoretical limits
// 4x: Preserve original frequency
// Round to the closest 4 * Nyquist-Shannon limits
// 4x for Fixed to capture sharp discontinuity
float scale;
if (precise || mode == GradientMode.Fixed)
scale = 4.0f;
else
scale = 2.0f;
float sizef = scale * Mathf.Ceil(1.0f / smallestDelta + 1.0f);
textureSize = Mathf.RoundToInt(sizef);
// Arbitrary max (1024)
textureSize = Math.Min(textureSize, 1024);
}
SetDirty();
}
/// <summary>
/// Cleans up the internal texture resource.
/// </summary>
public void Dispose()
{
//Release();
}
/// <summary>
/// Releases the internal texture resource.
/// </summary>
public void Release()
{
if (m_Texture != null)
CoreUtils.Destroy(m_Texture);
m_Texture = null;
}
/// <summary>
/// Marks the curve as dirty to trigger a redraw of the texture the next time <see cref="GetTexture"/>
/// is called.
/// </summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void SetDirty()
{
m_IsTextureDirty = true;
}
static GraphicsFormat GetTextureFormat()
{
return GraphicsFormat.R8G8B8A8_UNorm;
}
/// <summary>
/// Gets the texture representation of this Gradient.
/// </summary>
/// <returns>A texture.</returns>
public Texture2D GetTexture()
{
float step = 1.0f / (float)(textureSize - 1);
if (m_Texture != null && m_Texture.width != textureSize)
{
Object.DestroyImmediate(m_Texture);
m_Texture = null;
}
if (m_Texture == null)
{
m_Texture = new Texture2D(textureSize, 1, GetTextureFormat(), TextureCreationFlags.None);
m_Texture.name = "GradientTexture";
m_Texture.hideFlags = HideFlags.HideAndDontSave;
m_Texture.filterMode = FilterMode.Bilinear;
m_Texture.wrapMode = TextureWrapMode.Clamp;
m_Texture.anisoLevel = 0;
m_IsTextureDirty = true;
}
if (m_IsTextureDirty)
{
var pixels = new Color[textureSize];
for (int i = 0; i < textureSize; i++)
pixels[i] = Evaluate(i * step);
m_Texture.SetPixels(pixels);
m_Texture.Apply(false, false);
m_IsTextureDirty = false;
m_Texture.IncrementUpdateCount();
}
return m_Texture;
}
/// <summary>
/// Evaluate a time value on the Gradient.
/// </summary>
/// <param name="time">The time within the Gradient you want to evaluate.</param>
/// <returns>The value of the Gradient, at the point in time specified.</returns>
public Color Evaluate(float time)
{
if (textureSize <= 0)
return Color.black;
return m_Gradient.Evaluate(time);
}
/// <summary>
/// Setup Gradient with an array of color keys and alpha keys.
/// </summary>
/// <param name="colorKeys">Color keys of the gradient (maximum 8 color keys).</param>
/// <param name="alphaKeys">Alpha keys of the gradient (maximum 8 alpha keys).</param>
/// <param name="mode">Indicates the color space that the gradient color keys are using.</param>
/// <param name="colorSpace">Controls how the gradient colors are interpolated.</param>
public void SetKeys(GradientColorKey[] colorKeys, GradientAlphaKey[] alphaKeys, GradientMode mode, ColorSpace colorSpace)
{
m_Gradient.SetKeys(colorKeys, alphaKeys);
m_Gradient.mode = mode;
m_Gradient.colorSpace = colorSpace;
// Rebuild will make the TextureGradient Dirty.
Rebuild(colorKeys, alphaKeys, mode, colorSpace, m_RequestedTextureSize, m_Precise);
}
}
/// <summary>
/// A <see cref="VolumeParameter"/> that holds a <see cref="TextureGradient"/> value.
/// </summary>
[Serializable]
public class TextureGradientParameter : VolumeParameter<TextureGradient>
{
/// <summary>
/// Creates a new <see cref="TextureGradientParameter"/> instance.
/// </summary>
/// <param name="value">The initial value to store in the parameter.</param>
/// <param name="overrideState">The initial override state for the parameter.</param>
public TextureGradientParameter(TextureGradient value, bool overrideState = false)
: base(value, overrideState) { }
/// <summary>
/// Release implementation.
/// </summary>
public override void Release() => m_Value.Release();
// TODO: TextureGradient interpolation
}
}