265 lines
10 KiB
C#
265 lines
10 KiB
C#
|
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
|
||
|
}
|
||
|
}
|