Adaug proiectul Unity

This commit is contained in:
2026-11-01 23:39:55 +02:00
parent cb14d9d37d
commit 863eac8f2c
23463 changed files with 5412601 additions and 0 deletions
@@ -0,0 +1,9 @@
using System.Runtime.CompilerServices;
[assembly: InternalsVisibleTo("Universal2DGraphicsTests")]
[assembly: InternalsVisibleTo("Universal2DEditorTests")]
[assembly: InternalsVisibleTo("Unity.RenderPipelines.Universal.Editor")]
[assembly: InternalsVisibleTo("Unity.RenderPipelines.Universal.2D.Editor.Overrides")]
[assembly: InternalsVisibleTo("Unity.RenderPipelines.Universal.Editor.Tests")]
[assembly: InternalsVisibleTo("Unity.RenderPipelines.Universal.Runtime.Tests")]
[assembly: InternalsVisibleTo("Unity.GraphicTests.Performance.Universal.Runtime")]
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 50139ce6dfc5481409cd02c43fbc842f
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,20 @@
using UnityEngine.Scripting.APIUpdating;
namespace UnityEngine.Rendering.Universal
{
/// <summary>
/// (Deprecated) An add-on module for Cinemachine Virtual Camera that tweaks the orthographic size
/// of the virtual camera. It detects the presence of the Pixel Perfect Camera component and use the
/// settings from that Pixel Perfect Camera to correct the orthographic size so that pixel art
/// sprites would appear pixel perfect when the virtual camera becomes live.
/// </summary>
[AddComponentMenu("")] // Hide in menu
[MovedFrom(true, "UnityEngine.Experimental.Rendering.Universal", "Unity.RenderPipelines.Universal.Runtime")]
public class CinemachineUniversalPixelPerfect : MonoBehaviour
{
void OnEnable()
{
Debug.LogError("CinemachineUniversalPixelPerfect is now deprecated and doesn't function properly. Instead, use the one from Cinemachine v2.4.0 or newer.");
}
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 9d020dac04a1b2b44a3a36eb8abe528f
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: -1
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 490481dd6f7a2a84b8c28566abbc677b
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 7bd441e8ae54da04b805cef8d06b4536
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,18 @@
namespace UnityEngine.Rendering.Universal
{
internal class Universal2DRenderingData : ContextItem
{
internal Renderer2DData renderingData;
internal LayerBatch[] layerBatches;
internal int batchCount;
public override void Reset()
{
renderingData = null;
layerBatches = null;
batchCount = 0;
}
}
}
@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: cfd8db254ecd63d45bc135530bd0c325
@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 2e4bcb6764ed4a7439184ab357aa4fb2
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,790 @@
using System;
using UnityEngine.Serialization;
using UnityEngine.Scripting.APIUpdating;
using UnityEngine.U2D;
using UnityEngine.Rendering.RenderGraphModule;
using System.Collections.Generic;
namespace UnityEngine.Rendering.Universal
{
/// <summary>
/// Class <c>Light2D</c> is a 2D light which can be used with the 2D Renderer.
/// </summary>
///
[ExecuteAlways, DisallowMultipleComponent]
[Icon("UnityEngine/Light Icon")]
[MovedFrom(true, "UnityEngine.Experimental.Rendering.Universal", "Unity.RenderPipelines.Universal.Runtime")]
[AddComponentMenu("Rendering/2D/Light 2D")]
[HelpURL("https://docs.unity3d.com/Packages/com.unity.render-pipelines.universal@latest/index.html?subfolder=/manual/2DLightProperties.html")]
public sealed partial class Light2D : Light2DBase, ISerializationCallbackReceiver
{
/// <summary>
/// Deprecated Light types that are no supported. Please migrate to either Freeform or Point lights.
/// </summary>
public enum DeprecatedLightType
{
/// <summary>
/// N-gon shaped lights.
/// </summary>
Parametric = 0,
}
/// <summary>
/// An enumeration of the types of light
/// </summary>
public enum LightType
{
/// <summary>
/// N-gon shaped lights. Deprecated.
/// </summary>
Parametric = 0,
/// <summary>
/// The shape of the light is based on a user defined closed shape with multiple points.
/// </summary>
Freeform = 1,
/// <summary>
/// The shape of the light is based on a Sprite.
/// </summary>
Sprite = 2,
/// <summary>
/// The shape of light is circular and can also be configured into a pizza shape.
/// </summary>
Point = 3,
/// <summary>
/// Shapeless light that affects the entire screen.
/// </summary>
Global = 4,
/// <summary>
/// Light completely supplied by a provider
/// </summary>
Provider = 5,
}
/// <summary>
/// The accuracy of how the normal map calculation.
/// </summary>
public enum NormalMapQuality
{
/// <summary>
/// Normal map not used.
/// </summary>
Disabled = 2,
/// <summary>
/// Faster calculation with less accuracy suited for small shapes on screen.
/// </summary>
Fast = 0,
/// <summary>
/// Accurate calculation useful for better output on bigger shapes on screen.
/// </summary>
Accurate = 1
}
/// <summary>
/// Determines how the final color is calculated when multiple lights overlap each other
/// </summary>
public enum OverlapOperation
{
/// <summary>
/// Colors are added together
/// </summary>
Additive,
/// <summary>
/// Colors are blended using standard blending (alpha, 1-alpha)
/// </summary>
AlphaBlend
}
private enum ComponentVersions
{
Version_Unserialized = 0,
Version_1 = 1,
Version_2 = 2,
Version_3 = 3,
Version_4 = 4
}
const ComponentVersions k_CurrentComponentVersion = ComponentVersions.Version_4;
[SerializeField] ComponentVersions m_ComponentVersion = ComponentVersions.Version_Unserialized;
static Bounds kEmptyBounds = new Bounds(Vector3.zero, Vector3.zero);
#if USING_ANIMATION_MODULE
[UnityEngine.Animations.NotKeyable]
#endif
[SerializeField] LightType m_LightType = LightType.Point;
[SerializeField, FormerlySerializedAs("m_LightOperationIndex")]
int m_BlendStyleIndex = 0;
[SerializeReference] Light2DProvider m_Light2DProvider;
#if UNITY_EDITOR
[SerializeReference] Light2DProviderSources m_SelectionSources = new Light2DProviderSources();
#endif
[SerializeField] float m_FalloffIntensity = 0.5f;
[ColorUsage(true)]
[SerializeField] Color m_Color = Color.white;
[SerializeField] float m_Intensity = 1;
[FormerlySerializedAs("m_LightVolumeOpacity")]
[SerializeField] float m_LightVolumeIntensity = 1.0f;
[FormerlySerializedAs("m_LightVolumeIntensityEnabled")]
[SerializeField] bool m_LightVolumeEnabled = false;
[SerializeField] int[] m_ApplyToSortingLayers; // These are sorting layer IDs. If we need to update this at runtime make sure we add code to update global lights
[Reload("Textures/2D/Sparkle.png")]
[SerializeField] Sprite m_LightCookieSprite;
[Obsolete("Use m_LightCookieSprite instead")]
[SerializeField] Sprite m_DeprecatedPointLightCookieSprite;
[SerializeField] int m_LightOrder = 0;
[SerializeField] bool m_AlphaBlendOnOverlap = false; // This is now deprecated. Keep it here for backwards compatibility.
[SerializeField] OverlapOperation m_OverlapOperation = OverlapOperation.Additive;
[FormerlySerializedAs("m_PointLightDistance")]
[SerializeField] float m_NormalMapDistance = 3.0f;
#if USING_ANIMATION_MODULE
[UnityEngine.Animations.NotKeyable]
#endif
[FormerlySerializedAs("m_PointLightQuality")]
[SerializeField] NormalMapQuality m_NormalMapQuality = NormalMapQuality.Disabled;
[SerializeField] bool m_UseNormalMap = false; // This is now deprecated. Keep it here for backwards compatibility.
[FormerlySerializedAs("m_ShadowIntensityEnabled")]
[SerializeField] bool m_ShadowsEnabled = true;
[Range(0, 1)]
[SerializeField] float m_ShadowIntensity = 0.75f;
[Range(0, 1)]
[SerializeField] float m_ShadowSoftness = 0.3f;
[Range(0, 1)]
[SerializeField] float m_ShadowSoftnessFalloffIntensity = 0.5f;
[SerializeField] bool m_ShadowVolumeIntensityEnabled = false;
[Range(0, 1)]
[SerializeField] float m_ShadowVolumeIntensity = 0.75f;
[SerializeField] RenderingLayerMask m_RenderingLayersMask = RenderingLayerMask.defaultRenderingLayerMask;
Mesh m_Mesh;
// m_Mesh either points at a Mesh this component allocated through lightMesh, or at one handed over by
// Light2DProvider.GetMesh(). That contract only returns a mesh to render and does not transfer ownership,
// so provider meshes may be shared between lights and must never be destroyed from here.
bool m_OwnsMesh;
[NonSerialized]
private LightUtility.LightMeshVertex[] m_Vertices = new LightUtility.LightMeshVertex[1];
[NonSerialized]
private ushort[] m_Triangles = new ushort[1];
internal LightUtility.LightMeshVertex[] vertices { get { return m_Vertices; } set { m_Vertices = value; } }
internal ushort[] indices { get { return m_Triangles; } set { m_Triangles = value; } }
// Transients
EntityId m_PreviousLightCookieSprite;
internal Vector3 m_CachedPosition;
// We use Blue Channel of LightMesh's vertex color to indicate Slot Index.
int m_BatchSlotIndex = 0;
internal int batchSlotIndex { get { return m_BatchSlotIndex; } set { m_BatchSlotIndex = value; } }
private EntityId lightCookieSpriteEntityId => lightCookieSprite?.GetEntityId() ?? EntityId.None;
internal bool useCookieSprite => (lightType == LightType.Point || lightType == LightType.Sprite) && (lightCookieSprite != null && lightCookieSprite.texture != null);
internal RTHandle m_CookieSpriteTexture = null;
internal TextureHandle m_CookieSpriteTextureHandle;
[SerializeField]
Bounds m_LocalBounds;
internal BoundingSphere boundingSphere { get; private set; }
internal Mesh lightMesh
{
get
{
if (null == m_Mesh)
{
m_Mesh = new Mesh();
m_OwnsMesh = true;
}
return m_Mesh;
}
}
// Takes the mesh a Light2DProvider handed us, releasing any mesh we allocated ourselves first so replacing
// it does not leak.
void SetProviderMesh(Mesh providerMesh)
{
// A provider is free to hand back the mesh we already hold, in which case ownership is unchanged.
if (m_Mesh == providerMesh)
return;
if (m_OwnsMesh)
CoreUtils.Destroy(m_Mesh);
m_Mesh = providerMesh;
m_OwnsMesh = false;
}
internal bool hasCachedMesh => (vertices.Length > 1 && indices.Length > 1);
internal bool forceUpdate = false;
/// <summary>
/// The light's current type
/// </summary>
public LightType lightType
{
get { return (LightType)m_LightType; }
set
{
m_LightType = value;
#if UNITY_EDITOR
if((int)m_LightType != (int)LightType.Provider)
m_SelectionSources.selectedHashCode = (int)m_LightType;
#endif
}
}
/// <summary>
/// Gets or sets the current light2DProvider
/// </summary>
public Light2DProvider light2DProvider
{
get { return m_Light2DProvider; }
set { m_Light2DProvider = value; }
}
/// <summary>
/// The lights current operation index
/// </summary>
public int blendStyleIndex { get => m_BlendStyleIndex; set => m_BlendStyleIndex = value; }
/// <summary>
/// Specifies the darkness of the shadow
/// </summary>
public float shadowIntensity { get => m_ShadowIntensity; set => m_ShadowIntensity = Mathf.Clamp01(value); }
/// <summary>
/// Specifies the softness of the soft shadow
/// </summary>
public float shadowSoftness { get => m_ShadowSoftness; set => m_ShadowSoftness = value; }
/// <summary>
/// Specifies that the shadows are enabled
/// </summary>
public bool shadowsEnabled { get => m_ShadowsEnabled; set => m_ShadowsEnabled = value; }
/// <summary>
/// Specifies the darkness of the shadow
/// </summary>
public float shadowVolumeIntensity { get => m_ShadowVolumeIntensity; set => m_ShadowVolumeIntensity = Mathf.Clamp01(value); }
/// <summary>
/// Specifies that the volumetric shadows are enabled
/// </summary>
public bool volumetricShadowsEnabled { get => m_ShadowVolumeIntensityEnabled; set => m_ShadowVolumeIntensityEnabled = value; }
/// <summary>
/// The lights current color
/// </summary>
public Color color { get => m_Color; set => m_Color = value; }
/// <summary>
/// The lights current intensity
/// </summary>
public float intensity { get => m_Intensity; set => m_Intensity = value; }
/// <summary>
/// The lights current intensity
/// </summary>
///
[Obsolete("#from(2021.1)")]
public float volumeOpacity => m_LightVolumeIntensity;
/// <summary>
/// Controls the visibility of the light's volume
/// </summary>
public float volumeIntensity { get => m_LightVolumeIntensity; set => m_LightVolumeIntensity = value; }
/// <summary>
/// Enables or disables the light's volume
/// </summary>
///
[Obsolete("#from(2023.1)")]
public bool volumeIntensityEnabled { get => m_LightVolumeEnabled; set => m_LightVolumeEnabled = value; }
/// <summary>
/// Enables or disables the light's volume
/// </summary>
///
public bool volumetricEnabled { get => m_LightVolumeEnabled; set => m_LightVolumeEnabled = value; }
/// <summary>
/// The Sprite that's used by the Sprite Light type to control the shape light
/// </summary>
public Sprite lightCookieSprite { get { return m_LightCookieSprite; } set => m_LightCookieSprite = value; }
/// <summary>
/// Controls the brightness and distance of the fall off (edge) of the light
/// </summary>
public float falloffIntensity { get => m_FalloffIntensity; set => m_FalloffIntensity = Mathf.Clamp(value, 0, 1); }
/// <summary>
/// Controls the falloff for soft shadows
/// </summary>
public float shadowSoftnessFalloffIntensity { get => m_ShadowSoftnessFalloffIntensity; set => m_ShadowSoftnessFalloffIntensity = Mathf.Clamp(value, 0, 1); }
/// <summary>
/// Checks if the alpha overlap operation is alpha blend.
/// This is obsolete.
/// </summary>
[Obsolete("#from(2021.1)")]
public bool alphaBlendOnOverlap { get { return m_OverlapOperation == OverlapOperation.AlphaBlend; } }
/// <summary>
/// Controls the overlap operation mode.
/// </summary>
public OverlapOperation overlapOperation { get => m_OverlapOperation; set => m_OverlapOperation = value; }
/// <summary>
/// Gets or sets the light order. The lightOrder determines the order in which the lights are rendered onto the light textures.
/// </summary>
public int lightOrder { get => m_LightOrder; set => m_LightOrder = value; }
/// <summary>
/// The simulated z distance of the light from the surface used in normal map calculation.
/// </summary>
public float normalMapDistance => m_NormalMapDistance;
/// <summary>
/// Returns the calculation quality for the normal map rendering. Please refer to NormalMapQuality.
/// </summary>
public NormalMapQuality normalMapQuality => m_NormalMapQuality;
/// <summary>
/// Returns if volumetric shadows should be rendered.
/// </summary>
public bool renderVolumetricShadows => volumetricShadowsEnabled && shadowVolumeIntensity > 0;
/// <summary>
/// Gets or sets the target sorting layers for the light. Contains an array of sorting layer IDs.
/// </summary>
public int[] targetSortingLayers
{
get => m_ApplyToSortingLayers;
set
{
var layers = new List<int>();
foreach (var layerID in value)
{
if (SortingLayer.IsValid(layerID))
layers.Add(layerID);
}
m_ApplyToSortingLayers = layers.ToArray();
}
}
/// <summary>
/// Gets or sets the rendering layer mask for the light.
/// </summary>
public RenderingLayerMask renderingLayerMask
{
get { return m_RenderingLayersMask; }
set
{
m_RenderingLayersMask = value;
}
}
bool IsValidLayer(string name)
{
// Have this check as SortingLayer.NameToID returns 0 (default layer) if layer is not found
foreach (var layer in Light2DManager.GetCachedSortingLayer())
{
if (layer.name == name)
return true;
}
return false;
}
/// <summary>
/// Adds a target sorting layer to the light.
/// </summary>
/// <param name="layerName">The sorting layer name to be added.</param>
/// <returns>Returns true if the sorting layer is added. Returns false if the layer name is invalid or has already been added.</returns>
public bool AddTargetSortingLayer(string layerName)
{
var layers = new List<int>(m_ApplyToSortingLayers);
var id = SortingLayer.NameToID(layerName);
// Invalid or duplicate layerID
if (!IsValidLayer(layerName) || layers.Contains(id))
return false;
layers.Add(id);
m_ApplyToSortingLayers = layers.ToArray();
return true;
}
/// <summary>
/// Adds a target sorting layer to the light.
/// </summary>
/// <param name="layerID">The sorting layer ID to be added.</param>
/// <returns>Returns true if the sorting layer is added. Returns false if the layer ID is invalid or has already been added.</returns>
public bool AddTargetSortingLayer(int layerID)
{
return AddTargetSortingLayer(SortingLayer.IDToName(layerID));
}
/// <summary>
/// Removes a target sorting layer from the light.
/// </summary>
/// <param name="layerName">The sorting layer name to be removed.</param>
/// <returns>Returns true if the sorting layer is removed. Returns false if the layer name is invalid or doesn't exist.</returns>
public bool RemoveTargetSortingLayer(string layerName)
{
var layers = new List<int>(m_ApplyToSortingLayers);
var id = SortingLayer.NameToID(layerName);
// Invalid or layerID does not exist
if (!IsValidLayer(layerName) || !layers.Contains(id))
return false;
layers.Remove(id);
m_ApplyToSortingLayers = layers.ToArray();
return true;
}
/// <summary>
/// Removes a target sorting layer from the light.
/// </summary>
/// <param name="layerID">The sorting layer ID to be removed.</param>
/// <returns>Returns true if the sorting layer is removed. Returns false if the layer ID is invalid or doesn't exist.</returns>
public bool RemoveTargetSortingLayer(int layerID)
{
return RemoveTargetSortingLayer(SortingLayer.IDToName(layerID));
}
internal void MarkForUpdate()
{
forceUpdate = true;
}
internal void CacheValues()
{
m_CachedPosition = transform.position;
}
internal int GetTopMostLitLayer()
{
var largestIndex = Int32.MinValue;
var largestLayer = 0;
var layers = Light2DManager.GetCachedSortingLayer();
for (var i = 0; i < m_ApplyToSortingLayers.Length; ++i)
{
for (var layer = layers.Length - 1; layer >= largestLayer; --layer)
{
if (layers[layer].id == m_ApplyToSortingLayers[i])
{
largestIndex = layers[layer].value;
largestLayer = layer;
}
}
}
return largestIndex;
}
internal Bounds UpdateSpriteMesh()
{
if (m_LightCookieSprite == null && (m_Vertices.Length != 1 || m_Triangles.Length != 1))
{
m_Vertices = new LightUtility.LightMeshVertex[1];
m_Triangles = new ushort[1];
}
return LightUtility.GenerateSpriteMesh(this, m_LightCookieSprite, LightBatch.GetBatchColor());
}
internal void UpdateBatchSlotIndex()
{
if (lightMesh && lightMesh.colors != null && lightMesh.colors.Length != 0)
m_BatchSlotIndex = LightBatch.GetBatchSlotIndex(lightMesh.colors[0].b);
}
internal bool NeedsColorIndexBaking()
{
if (lightMesh && LightBatch.isBatchingSupported)
{
if (lightMesh.colors.Length != 0)
return lightMesh.colors[0].b == 0;
}
return false;
}
internal Bounds UpdateProviderMesh()
{
SetProviderMesh(light2DProvider.GetMesh());
if (m_Mesh != null)
return m_Mesh.bounds;
else
return kEmptyBounds;
}
internal void UpdateCookieSpriteTexture()
{
m_CookieSpriteTexture?.Release();
if (useCookieSprite)
m_CookieSpriteTexture = RTHandles.Alloc(lightCookieSprite.texture);
}
internal void UpdateMesh(bool forceUpdate = false)
{
var lightTypeChanged = LightUtility.CheckForChange(lightType, ref m_PreviousLightType);
if (lightType != LightType.Provider)
{
var shapePathHash = LightUtility.GetShapePathHash(shapePath);
var fallOffSizeChanged = LightUtility.CheckForChange(m_ShapeLightFalloffSize, ref m_PreviousShapeLightFalloffSize);
var parametricRadiusChanged = LightUtility.CheckForChange(m_ShapeLightParametricRadius, ref m_PreviousShapeLightParametricRadius);
var parametricSidesChanged = LightUtility.CheckForChange(m_ShapeLightParametricSides, ref m_PreviousShapeLightParametricSides);
var parametricAngleOffsetChanged = LightUtility.CheckForChange(m_ShapeLightParametricAngleOffset, ref m_PreviousShapeLightParametricAngleOffset);
var spriteInstanceChanged = LightUtility.CheckForChange(lightCookieSpriteEntityId, ref m_PreviousLightCookieSprite);
var shapePathHashChanged = LightUtility.CheckForChange(shapePathHash, ref m_PreviousShapePathHash);
var hashChanged = fallOffSizeChanged || parametricRadiusChanged || parametricSidesChanged ||
parametricAngleOffsetChanged || spriteInstanceChanged || shapePathHashChanged || lightTypeChanged || NeedsColorIndexBaking();
// Mesh Rebuilding
if (hashChanged || forceUpdate)
{
var batchChannelColor = LightBatch.GetBatchColor();
switch (lightType)
{
case LightType.Freeform:
m_LocalBounds = LightUtility.GenerateShapeMesh(this, m_ShapePath, m_ShapeLightFalloffSize, batchChannelColor);
break;
case LightType.Parametric:
m_LocalBounds = LightUtility.GenerateParametricMesh(this, m_ShapeLightParametricRadius, m_ShapeLightFalloffSize, m_ShapeLightParametricAngleOffset, m_ShapeLightParametricSides, batchChannelColor);
break;
case LightType.Sprite:
m_LocalBounds = UpdateSpriteMesh();
break;
case LightType.Point:
m_LocalBounds = LightUtility.GenerateParametricMesh(this, 1.412135f, 0, 0, 4, batchChannelColor);
break;
}
UpdateCookieSpriteTexture();
UpdateBatchSlotIndex();
}
}
else
{
if (light2DProvider != null)
{
if (m_Mesh != null)
{
SetProviderMesh(light2DProvider.GetMesh());
m_LocalBounds = m_Mesh.bounds;
}
}
else
{
m_Mesh.Clear();
m_LocalBounds = kEmptyBounds;
}
}
}
internal void UpdateBoundingSphere()
{
if (isPointLight)
{
boundingSphere = new BoundingSphere(transform.position, m_PointLightOuterRadius);
return;
}
var maxBound = transform.TransformPoint(Vector3.Max(m_LocalBounds.max, m_LocalBounds.max + (Vector3)m_ShapeLightFalloffOffset));
var minBound = transform.TransformPoint(Vector3.Min(m_LocalBounds.min, m_LocalBounds.min + (Vector3)m_ShapeLightFalloffOffset));
var center = 0.5f * (maxBound + minBound);
var radius = Vector3.Magnitude(maxBound - center);
boundingSphere = new BoundingSphere(center, radius);
}
internal bool IsLitLayer(int layer)
{
if (m_ApplyToSortingLayers == null)
return false;
for (var i = 0; i < m_ApplyToSortingLayers.Length; i++)
if (m_ApplyToSortingLayers[i] == layer)
return true;
return false;
}
internal Matrix4x4 GetMatrix()
{
var matrix = transform.localToWorldMatrix;
if (lightType == Light2D.LightType.Point)
{
var scale = new Vector3(pointLightOuterRadius, pointLightOuterRadius, pointLightOuterRadius);
matrix = Matrix4x4.TRS(transform.position, transform.rotation, scale);
}
return matrix;
}
private void Awake()
{
// Default target sorting layers to "All"
if (m_ApplyToSortingLayers == null)
{
m_ApplyToSortingLayers = new int[SortingLayer.layers.Length];
for (int i = 0; i < m_ApplyToSortingLayers.Length; ++i)
m_ApplyToSortingLayers[i] = SortingLayer.layers[i].id;
}
}
void OnEnable()
{
m_PreviousLightCookieSprite = lightCookieSpriteEntityId;
Light2DManager.RegisterLight(this);
UpdateCookieSpriteTexture();
#if UNITY_EDITOR
SortingLayer.onLayerAdded += OnSortingLayerAdded;
SortingLayer.onLayerRemoved += OnSortingLayerRemoved;
#endif
}
private void OnDestroy()
{
// Only the mesh we allocated ourselves is ours to destroy; a provider owns the one it returned.
if (m_OwnsMesh)
CoreUtils.Destroy(m_Mesh);
m_Mesh = null;
m_OwnsMesh = false;
}
private void OnDisable()
{
Light2DManager.DeregisterLight(this);
m_CookieSpriteTexture?.Release();
#if UNITY_EDITOR
SortingLayer.onLayerAdded -= OnSortingLayerAdded;
SortingLayer.onLayerRemoved -= OnSortingLayerRemoved;
#endif
}
private void LateUpdate()
{
if (lightType == LightType.Global)
return;
UpdateMesh(forceUpdate);
UpdateBoundingSphere();
forceUpdate = false;
}
#if UNITY_EDITOR
private void OnSortingLayerAdded(SortingLayer layer)
{
var newArray = new int[m_ApplyToSortingLayers.Length + 1];
for (int i = 0; i < m_ApplyToSortingLayers.Length; i++)
{
newArray[i] = m_ApplyToSortingLayers[i];
}
newArray[m_ApplyToSortingLayers.Length] = layer.id;
m_ApplyToSortingLayers = newArray;
}
private void OnSortingLayerRemoved(SortingLayer layer)
{
var tempList = new List<int>();
foreach (var x in m_ApplyToSortingLayers)
{
if (x != layer.id && SortingLayer.IsValid(x))
tempList.Add(x);
}
m_ApplyToSortingLayers = tempList.ToArray();
}
#endif
/// <summary>
/// OnBeforeSerialize implementation.
/// </summary>
public void OnBeforeSerialize()
{
m_ComponentVersion = k_CurrentComponentVersion;
}
/// <summary>
/// OnAfterSerialize implementation.
/// </summary>
public void OnAfterDeserialize()
{
// Upgrade from no serialized version
if (m_ComponentVersion == ComponentVersions.Version_Unserialized)
{
m_ShadowVolumeIntensityEnabled = m_ShadowVolumeIntensity > 0;
m_ShadowsEnabled = m_ShadowIntensity > 0;
m_LightVolumeEnabled = m_LightVolumeIntensity > 0;
m_NormalMapQuality = !m_UseNormalMap ? NormalMapQuality.Disabled : m_NormalMapQuality;
m_OverlapOperation = m_AlphaBlendOnOverlap ? OverlapOperation.AlphaBlend : m_OverlapOperation;
m_ComponentVersion = ComponentVersions.Version_1;
}
if (m_ComponentVersion < ComponentVersions.Version_2)
{
m_ShadowSoftness = 0;
}
if (m_ComponentVersion < ComponentVersions.Version_3)
{
#if UNITY_EDITOR
m_SelectionSources.selectedHashCode = (int)m_LightType;
#endif
}
if (m_ComponentVersion < ComponentVersions.Version_4)
{
#pragma warning disable CS0618
if (m_LightType == LightType.Point && (object)m_DeprecatedPointLightCookieSprite != null)
{
m_LightCookieSprite = m_DeprecatedPointLightCookieSprite;
}
#pragma warning restore CS0618
}
}
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 073797afb82c5a1438f328866b10b3f0
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,43 @@
using System.Collections.Generic;
namespace UnityEngine.Rendering.Universal
{
public sealed partial class Light2D
{
#if UNITY_EDITOR
private const string s_IconsPath = "Packages/com.unity.render-pipelines.universal/Editor/2D/ResourceIcons/SceneViewIcons/";
private static readonly string[] s_LightIconFileNames = new[]
{
"ParametricLight.png",
"FreeformLight.png",
"SpriteLight.png",
"PointLight.png",
"GlobalLight.png"
};
private void OnDrawGizmos()
{
if (lightType != LightType.Provider)
{
Gizmos.color = Color.blue;
Gizmos.DrawIcon(transform.position, s_IconsPath + s_LightIconFileNames[(int)lightType], true);
}
else
{
light2DProvider.OnDrawGizmos(transform);
}
}
void Reset()
{
m_ShapePath = new Vector3[] { new Vector3(-0.5f, -0.5f), new Vector3(0.5f, -0.5f), new Vector3(0.5f, 0.5f), new Vector3(-0.5f, 0.5f) };
}
internal List<Vector2> GetFalloffShape()
{
return LightUtility.GetOutlinePath(m_ShapePath, m_ShapeLightFalloffSize);
}
#endif
}
}
@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 14716551160f4757bf2332c27a53ec34
timeCreated: 1595907042
@@ -0,0 +1,122 @@
using System;
using UnityEngine.Scripting.APIUpdating;
namespace UnityEngine.Rendering.Universal
{
/// <summary>
/// Controls how the light texture is used when rendering Sprites and other 2D renderers.
/// </summary>
[Serializable]
[MovedFrom(true, "UnityEngine.Experimental.Rendering.Universal", "Unity.RenderPipelines.Universal.Runtime")]
public struct Light2DBlendStyle
{
internal enum TextureChannel
{
None = 0,
R = 1,
G = 2,
B = 3,
A = 4,
OneMinusR = 5,
OneMinusG = 6,
OneMinusB = 7,
OneMinusA = 8
}
internal struct MaskChannelFilter
{
public Vector4 mask { get; private set; }
public Vector4 inverted { get; private set; }
public MaskChannelFilter(Vector4 m, Vector4 i)
{
mask = m;
inverted = i;
}
}
internal enum BlendMode
{
Additive = 0,
Multiply = 1,
Subtractive = 2
}
[Serializable]
internal struct BlendFactors
{
public float multiplicative;
public float additive;
}
/// <summary>
/// Returns the name of the blend style
/// </summary>
public string name;
[SerializeField]
internal TextureChannel maskTextureChannel;
[SerializeField]
internal BlendMode blendMode;
internal Vector2 blendFactors
{
get
{
var result = new Vector2();
switch (blendMode)
{
case BlendMode.Additive:
result.x = 0.0f;
result.y = 1.0f;
break;
case BlendMode.Multiply:
result.x = 1.0f;
result.y = 0.0f;
break;
case BlendMode.Subtractive:
result.x = 0.0f;
result.y = -1.0f;
break;
default:
result.x = 1.0f;
result.y = 0.0f;
break;
}
return result;
}
}
internal MaskChannelFilter maskTextureChannelFilter
{
get
{
switch (maskTextureChannel)
{
case TextureChannel.R:
return new MaskChannelFilter(new Vector4(1, 0, 0, 0), new Vector4(0, 0, 0, 0));
case TextureChannel.OneMinusR:
return new MaskChannelFilter(new Vector4(1, 0, 0, 0), new Vector4(1, 0, 0, 0));
case TextureChannel.G:
return new MaskChannelFilter(new Vector4(0, 1, 0, 0), new Vector4(0, 0, 0, 0));
case TextureChannel.OneMinusG:
return new MaskChannelFilter(new Vector4(0, 1, 0, 0), new Vector4(0, 1, 0, 0));
case TextureChannel.B:
return new MaskChannelFilter(new Vector4(0, 0, 1, 0), new Vector4(0, 0, 0, 0));
case TextureChannel.OneMinusB:
return new MaskChannelFilter(new Vector4(0, 0, 1, 0), new Vector4(0, 0, 1, 0));
case TextureChannel.A:
return new MaskChannelFilter(new Vector4(0, 0, 0, 1), new Vector4(0, 0, 0, 0));
case TextureChannel.OneMinusA:
return new MaskChannelFilter(new Vector4(0, 0, 0, 1), new Vector4(0, 0, 0, 1));
case TextureChannel.None:
default:
return new MaskChannelFilter(Vector4.zero, Vector4.zero);
}
}
}
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: f4caf3da21d83644ea1c04986c6cea14
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,208 @@
using System.Collections.Generic;
using Unity.Mathematics;
using UnityEngine.Profiling;
namespace UnityEngine.Rendering.Universal
{
internal struct LightStats
{
public int totalLights;
public int totalShadowLights;
public int totalShadows;
public int totalNormalMapUsage;
public int totalVolumetricUsage;
public int totalVolumetricShadowUsage;
public uint blendStylesUsed;
public uint blendStylesWithLights;
public bool useLights { get { return totalLights > 0; } }
public bool useShadows { get { return totalShadows > 0; } }
public bool useVolumetricLights { get { return totalVolumetricUsage > 0; } }
public bool useVolumetricShadowLights { get { return totalVolumetricShadowUsage > 0; } }
public bool useNormalMap { get { return totalNormalMapUsage > 0; } }
}
internal interface ILight2DCullResult
{
List<Light2D> visibleLights { get; }
HashSet<ShadowCasterGroup2D> visibleShadows { get; }
LightStats GetLightStatsByLayer(int layerID, ref LayerBatch layer);
bool IsSceneLit();
#if UNITY_EDITOR
// Determine if culling result is based of game camera
bool IsGameView();
#endif
}
internal class Light2DCullResult : ILight2DCullResult
{
private List<Light2D> m_VisibleLights = new List<Light2D>();
public List<Light2D> visibleLights => m_VisibleLights;
private HashSet<ShadowCasterGroup2D> m_VisibleShadows = new HashSet<ShadowCasterGroup2D>();
public HashSet<ShadowCasterGroup2D> visibleShadows => m_VisibleShadows;
bool m_IsSceneLit;
#if UNITY_EDITOR
bool m_IsGameView;
#endif
// Checks if the scene has any lights, even if they are culled
public bool IsSceneLit()
{
return m_IsSceneLit;
}
#if UNITY_EDITOR
public bool IsGameView()
{
return m_IsGameView;
}
#endif
public LightStats GetLightStatsByLayer(int layerID, ref LayerBatch layer)
{
layer.lights.Clear();
layer.shadowIndices.Clear();
layer.shadowCasters.Clear();
var returnStats = new LightStats();
foreach (var light in visibleLights)
{
if (!light.IsLitLayer(layerID))
continue;
if (light.normalMapQuality != Light2D.NormalMapQuality.Disabled)
returnStats.totalNormalMapUsage++;
if (light.volumeIntensity > 0 && light.volumetricEnabled)
returnStats.totalVolumetricUsage++;
if (RendererLighting.CanCastVolumetricShadows(light, layer.endLayerValue))
returnStats.totalVolumetricShadowUsage++;
returnStats.blendStylesUsed |= (uint)(1 << light.blendStyleIndex);
if (light.lightType != Light2D.LightType.Global)
returnStats.blendStylesWithLights |= (uint)(1 << light.blendStyleIndex);
// Check if layer has shadows
bool isShadowed = false;
if (RendererLighting.CanCastShadows(light, layerID))
{
foreach (var group in visibleShadows)
{
var shadowCasters = group.GetShadowCasters();
if (shadowCasters != null)
{
foreach (var shadowCaster in shadowCasters)
{
if (shadowCaster.IsLit(light) && shadowCaster.IsShadowedLayer(layerID))
{
isShadowed = true;
returnStats.totalShadows++;
if (!layer.shadowCasters.Contains(group))
layer.shadowCasters.Add(group);
}
}
}
}
}
if (isShadowed)
{
returnStats.totalShadowLights++;
layer.shadowIndices.Add(layer.lights.Count);
}
returnStats.totalLights++;
layer.lights.Add(light);
}
return returnStats;
}
public void SetupCulling(ref ScriptableCullingParameters cullingParameters, Camera camera)
{
#if UNITY_EDITOR
m_IsGameView = UniversalRenderPipeline.IsGameCamera(camera);
#endif
Profiler.BeginSample("Cull 2D Lights and Shadow Casters");
m_VisibleLights.Clear();
m_IsSceneLit = false;
if (Light2DManager.TryGetLights(camera.gameObject, out var lights))
{
m_IsSceneLit = lights.Count > 0;
foreach (var light in lights)
{
if ((camera.cullingMask & (1 << light.gameObject.layer)) == 0)
continue;
#if UNITY_EDITOR
if (!UnityEditor.SceneManagement.StageUtility.IsGameObjectRenderedByCamera(light.gameObject, camera))
continue;
#endif
if (light.lightType == Light2D.LightType.Global)
{
m_VisibleLights.Add(light);
continue;
}
Profiler.BeginSample("Test Planes");
var position = light.boundingSphere.position;
var culled = false;
for (var i = 0; i < cullingParameters.cullingPlaneCount; ++i)
{
var plane = cullingParameters.GetCullingPlane(i);
// most of the time is spent getting world position
var distance = math.dot(position, plane.normal) + plane.distance;
if (distance < -light.boundingSphere.radius)
{
culled = true;
break;
}
}
Profiler.EndSample();
if (culled)
continue;
m_VisibleLights.Add(light);
}
}
// must be sorted here because light order could change
m_VisibleLights.Sort((l1, l2) => l1.lightOrder - l2.lightOrder);
m_VisibleShadows.Clear();
if (ShadowCasterGroup2DManager.shadowCasterGroups != null)
{
foreach (var group in ShadowCasterGroup2DManager.shadowCasterGroups)
{
var shadowCasters = group.GetShadowCasters();
if (shadowCasters != null)
{
foreach (var shadowCaster in shadowCasters)
{
// Cull against visible lights in the scene
foreach (var light in m_VisibleLights)
{
if (shadowCaster.IsLit(light) && !m_VisibleShadows.Contains(group))
{
m_VisibleShadows.Add(group);
break;
}
}
if (m_VisibleShadows.Contains(group))
break;
}
}
}
}
Profiler.EndSample();
}
}
}
@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 704a37a0ce184459805c0b7719a25a60
timeCreated: 1596067202
@@ -0,0 +1,227 @@
using System.Collections.Generic;
#if UNITY_EDITOR
using UnityEditor.SceneManagement;
using UnityEngine.SceneManagement;
#endif
namespace UnityEngine.Rendering.Universal
{
internal static class Light2DManager
{
private static SortingLayer[] s_SortingLayers;
static List<Light2D> s_Lights = new List<Light2D>(); // Scene/Game/Prefab view
#if UNITY_EDITOR
static Dictionary<SceneHandle, List<Light2D>> s_LightsEditorOnly = new Dictionary<SceneHandle, List<Light2D>>(); // Preview view
#endif
// GetLights return different list depending on the context of the GameObject requesting (Camera or Light2D)
// We determine if the GameObject is part of the preview scene through IsPreviewSceneObject
// Scene, Game, Prefab view uses s_Lights
// Preview view uses s_LightsEditorOnly
static List<Light2D> GetOrCreateLights(GameObject obj)
{
#if UNITY_EDITOR
if (IsPreview(obj, out var key))
{
if (!s_LightsEditorOnly.TryGetValue(key, out var list))
{
list = new List<Light2D>();
s_LightsEditorOnly.Add(key, list);
}
return s_LightsEditorOnly[key];
}
#endif
return s_Lights;
}
internal static bool TryGetLights(GameObject obj, out List<Light2D> lights)
{
#if UNITY_EDITOR
if (IsPreview(obj, out var key))
return s_LightsEditorOnly.TryGetValue(key, out lights);
#endif
lights = s_Lights;
return true;
}
internal static void RemoveLight(Light2D light)
{
#if UNITY_EDITOR
if (IsPreview(light.gameObject, out var key))
{
if (s_LightsEditorOnly.TryGetValue(key, out var list))
{
Debug.Assert(list.Contains(light));
list.Remove(light);
if (list.Count == 0)
s_LightsEditorOnly.Remove(key);
return;
}
}
#endif
Debug.Assert(s_Lights.Contains(light));
s_Lights.Remove(light);
}
#if UNITY_EDITOR
static bool IsPreview(GameObject obj, out SceneHandle handle)
{
handle = obj.scene.handle;
var isPreview = EditorSceneManager.IsPreviewSceneObject(obj);
// Prefab scene
var prefabStage = PrefabStageUtility.GetPrefabStage(obj);
if (prefabStage != null && isPreview)
{
if (obj.TryGetComponent<Camera>(out var cam))
isPreview = cam.cameraType == CameraType.Preview;
else
isPreview = false;
}
return isPreview;
}
#endif
#if UNITY_EDITOR
[RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.SubsystemRegistration)]
static void ResetStaticsOnLoad()
{
s_SortingLayers = null;
s_Lights.Clear();
s_LightsEditorOnly.Clear();
}
#endif
internal static void Initialize()
{
#if UNITY_EDITOR
SortingLayer.onLayerChanged += OnSortingLayerChanged;
#endif
}
internal static void Dispose()
{
#if UNITY_EDITOR
SortingLayer.onLayerChanged -= OnSortingLayerChanged;
#endif
}
// Called during OnEnable
public static void RegisterLight(Light2D light)
{
var lights = GetOrCreateLights(light.gameObject);
Debug.Assert(!lights.Contains(light));
lights.Add(light);
ErrorIfDuplicateGlobalLight(light);
}
// Called during OnEnable
public static void DeregisterLight(Light2D light)
{
RemoveLight(light);
}
public static void ErrorIfDuplicateGlobalLight(Light2D light)
{
if (light.lightType != Light2D.LightType.Global)
return;
foreach (var sortingLayer in light.targetSortingLayers)
{
// should this really trigger at runtime?
if (ContainsDuplicateGlobalLight(sortingLayer, light))
Debug.LogError("More than one global light on layer " + SortingLayer.IDToName(sortingLayer) + " for light blend style index " + light.blendStyleIndex);
}
}
internal static bool GetGlobalColor(int sortingLayerIndex, int blendStyleIndex, Camera cam, out Color color)
{
var foundGlobalColor = false;
color = Color.black;
// This should be rewritten to search only global lights
foreach (var light in GetOrCreateLights(cam.gameObject))
{
if (light.lightType != Light2D.LightType.Global ||
light.blendStyleIndex != blendStyleIndex ||
!light.IsLitLayer(sortingLayerIndex))
continue;
var inCurrentPrefabStage = true;
#if UNITY_EDITOR
// If we found the first global light in our prefab stage
inCurrentPrefabStage = PrefabStageUtility.GetCurrentPrefabStage()?.IsPartOfPrefabContents(light.gameObject) ?? true;
#endif
if (inCurrentPrefabStage)
{
color = light.color * light.intensity;
return true;
}
else
{
if (!foundGlobalColor)
{
color = light.color * light.intensity;
foundGlobalColor = true;
}
}
}
return foundGlobalColor;
}
private static bool ContainsDuplicateGlobalLight(int sortingLayerIndex, Light2D inLight)
{
var globalLightCount = 0;
// This should be rewritten to search only global lights
foreach (var light in GetOrCreateLights(inLight.gameObject))
{
if (light.lightType == Light2D.LightType.Global &&
light.blendStyleIndex == inLight.blendStyleIndex &&
light.IsLitLayer(sortingLayerIndex))
{
#if UNITY_EDITOR
// If we found the first global light in our prefab stage
if (PrefabStageUtility.GetPrefabStage(light.gameObject) == PrefabStageUtility.GetCurrentPrefabStage())
#endif
{
if (globalLightCount > 0)
return true;
globalLightCount++;
}
}
}
return false;
}
public static SortingLayer[] GetCachedSortingLayer()
{
if (s_SortingLayers is null)
s_SortingLayers = SortingLayer.layers;
return s_SortingLayers;
}
#if UNITY_EDITOR
internal static void OnSortingLayerChanged()
{
// Update sorting layers that were added or removed or changed order
s_SortingLayers = SortingLayer.layers;
}
#endif
}
}
@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: d8151411c155429aa167611d958fbb8c
timeCreated: 1595907591
@@ -0,0 +1,68 @@
using System;
namespace UnityEngine.Rendering.Universal
{
public sealed partial class Light2D
{
[SerializeField] float m_PointLightInnerAngle = 360.0f;
[SerializeField] float m_PointLightOuterAngle = 360.0f;
[SerializeField] float m_PointLightInnerRadius = 0.0f;
[SerializeField] float m_PointLightOuterRadius = 1.0f;
/// <summary>
/// The inner angle of the point light shape. The bigger the angle, the wider the gap.
/// The gap between the innner and outer angle will determine the size of the light's penumbra.
/// </summary>
public float pointLightInnerAngle
{
get => m_PointLightInnerAngle;
set => m_PointLightInnerAngle = value;
}
/// <summary>
/// The angle that determins the shape of the inner light area.
/// The gap between the innner and outer angle will determine the size of the light's penumbra.
/// </summary>
public float pointLightOuterAngle
{
get => m_PointLightOuterAngle;
set => m_PointLightOuterAngle = value;
}
/// <summary>
/// The radius of the inner light area that has full brightness.
/// The gap between the inner and outer radius will determine the size of the light's penumbra.
/// </summary>
public float pointLightInnerRadius
{
get => m_PointLightInnerRadius;
set => m_PointLightInnerRadius = value;
}
/// <summary>
/// The outer radius that determines the size of the light.
/// The gap between the inner and outer radius will determine the size of the light's penumbra.
/// </summary>
public float pointLightOuterRadius
{
get => m_PointLightOuterRadius;
set => m_PointLightOuterRadius = value;
}
/// <summary>
/// The point light distance.
/// This is obsolete and has been changed to normalMapDistance.
/// </summary>
[Obsolete("pointLightDistance has been changed to normalMapDistance #from(2021.1) #breakingFrom(2021.1)", true)]
public float pointLightDistance => m_NormalMapDistance;
/// <summary>
/// The quality of the point light.
/// This is obsolete and has been changed to normalMapQuality.
/// </summary>
[Obsolete("pointLightQuality has been changed to normalMapQuality #from(2021.1) #breakingFrom(2021.1)", true)]
public NormalMapQuality pointLightQuality => m_NormalMapQuality;
internal bool isPointLight => lightType == LightType.Point;
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 1202285d83908604784202e4a57aa93c
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,68 @@
namespace UnityEngine.Rendering.Universal
{
public sealed partial class Light2D
{
[SerializeField] int m_ShapeLightParametricSides = 5;
[SerializeField] float m_ShapeLightParametricAngleOffset = 0.0f;
[SerializeField] float m_ShapeLightParametricRadius = 1.0f;
[SerializeField] float m_ShapeLightFalloffSize = 0.50f;
[SerializeField] Vector2 m_ShapeLightFalloffOffset = Vector2.zero;
[SerializeField] Vector3[] m_ShapePath = null;
float m_PreviousShapeLightFalloffSize = -1;
int m_PreviousShapeLightParametricSides = -1;
float m_PreviousShapeLightParametricAngleOffset = -1;
float m_PreviousShapeLightParametricRadius = -1;
int m_PreviousShapePathHash = -1;
LightType m_PreviousLightType = LightType.Parametric;
internal const float k_MaxFalloffSize = 1000000f;
/// <summary>
/// The number of sides in the parametric shape.
/// </summary>
public int shapeLightParametricSides => m_ShapeLightParametricSides;
/// <summary>
/// The offset of the shape from the light's origin.
/// </summary>
public float shapeLightParametricAngleOffset => m_ShapeLightParametricAngleOffset;
/// <summary>
/// The size of the shape.
/// </summary>
public float shapeLightParametricRadius
{
get { return m_ShapeLightParametricRadius; }
internal set { m_ShapeLightParametricRadius = value; }
}
/// <summary>
/// The size of the fall-off area. Bigger value corresponds to bigger fall off size.
/// Possible values between 0 and 1000000.
/// </summary>
public float shapeLightFalloffSize
{
get { return m_ShapeLightFalloffSize; }
set { m_ShapeLightFalloffSize = Mathf.Clamp(value, 0, k_MaxFalloffSize); }
}
/// <summary>
/// Returns the path that represents the shape light. Values are in object space.
/// </summary>
public Vector3[] shapePath
{
get { return m_ShapePath; }
internal set { m_ShapePath = value; }
}
/// <summary>
/// Set the shape that represents the freeform light. Values are in object space.
/// </summary>
/// <param name="path">Array of Vector3 defining the shape.</param>
public void SetShapePath(Vector3[] path)
{
m_ShapePath = path;
}
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 074e9a8a8d8919e469b8dd79c7dc69a9
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,622 @@
using System;
using System.Collections.Generic;
using Unity.Collections;
using Unity.Mathematics;
using UnityEngine.U2D;
#if USING_2DCOMMON
using UnityEngine.U2D.Common;
#endif
namespace UnityEngine.Rendering.Universal
{
// Per Light parameters to batch.
struct PerLight2D
{
internal float4x4 InvMatrix;
internal float4 Color;
internal float4 Position;
internal float FalloffIntensity;
internal float FalloffDistance;
internal float OuterAngle;
internal float InnerAngle;
internal float InnerRadiusMult;
internal float VolumeOpacity;
internal float ShadowIntensity;
internal int LightType;
};
internal static class LightUtility
{
public static int ProviderToHash(Provider2D provider, Component component)
{
if (provider != null && component != null)
return StringToHash(provider.GetType().Name + component.GetType().Name);
else
return 0;
}
public static int StringToHash(string str)
{
return (int)URP2D_Crc32.Compute(str);
}
public static bool CheckForChange(Light2D.LightType a, ref Light2D.LightType b)
{
var changed = a != b;
b = a;
return changed;
}
public static bool CheckForChange(Component a, ref Component b)
{
var changed = a != b;
b = a;
return changed;
}
public static bool CheckForChange(int a, ref int b)
{
var changed = a != b;
b = a;
return changed;
}
public static bool CheckForChange(EntityId a, ref EntityId b)
{
var changed = a != b;
b = a;
return changed;
}
public static bool CheckForChange(float a, ref float b)
{
var changed = a != b;
b = a;
return changed;
}
public static bool CheckForChange(bool a, ref bool b)
{
var changed = a != b;
b = a;
return changed;
}
private enum PivotType
{
PivotBase,
PivotCurve,
PivotIntersect,
PivotSkip,
PivotClip
};
[Serializable]
internal struct LightMeshVertex
{
public Vector3 position;
public Color color;
public Vector2 uv;
public static readonly VertexAttributeDescriptor[] VertexLayout = new[]
{
new VertexAttributeDescriptor(VertexAttribute.Position, VertexAttributeFormat.Float32, 3),
new VertexAttributeDescriptor(VertexAttribute.Color, VertexAttributeFormat.Float32, 4),
new VertexAttributeDescriptor(VertexAttribute.TexCoord0, VertexAttributeFormat.Float32, 2),
};
}
static bool TestPivot(List<IntPoint> path, int activePoint, long lastPoint)
{
for (int i = activePoint; i < path.Count; ++i)
{
if (path[i].N > lastPoint)
return true;
}
return (path[activePoint].N == -1);
}
// Degenerate Pivots at the End Points.
static List<IntPoint> DegeneratePivots(List<IntPoint> path, List<IntPoint> inPath, ref int interiorStart)
{
List<IntPoint> degenerate = new List<IntPoint>();
var minN = path[0].N;
var maxN = path[0].N;
for (int i = 1; i < path.Count; ++i)
{
if (path[i].N != -1)
{
minN = Math.Min(minN, path[i].N);
maxN = Math.Max(maxN, path[i].N);
}
}
for (long i = 0; i < minN; ++i)
{
IntPoint ins = path[(int)minN];
ins.N = i;
degenerate.Add(ins);
}
degenerate.AddRange(path.GetRange(0, path.Count));
interiorStart = degenerate.Count;
for (long i = maxN + 1; i < inPath.Count; ++i)
{
IntPoint ins = inPath[(int)i];
ins.N = i;
degenerate.Add(ins);
}
return degenerate;
}
// Ensure that we get a valid path from 0.
static List<IntPoint> SortPivots(List<IntPoint> outPath, List<IntPoint> inPath)
{
List<IntPoint> sorted = new List<IntPoint>();
var min = outPath[0].N;
var max = outPath[0].N;
var minIndex = 0;
bool newMin = true;
for (int i = 1; i < outPath.Count; ++i)
{
if (max > outPath[i].N && newMin && outPath[i].N != -1)
{
min = max = outPath[i].N;
minIndex = i;
newMin = false;
}
else if (outPath[i].N >= max)
{
max = outPath[i].N;
newMin = true;
}
}
sorted.AddRange(outPath.GetRange(minIndex, (outPath.Count - minIndex)));
sorted.AddRange(outPath.GetRange(0, minIndex));
return sorted;
}
// Ensure that all points eliminated due to overlaps and intersections are accounted for Tessellation.
static List<IntPoint> FixPivots(List<IntPoint> outPath, List<IntPoint> inPath, ref int interiorStart)
{
var path = SortPivots(outPath, inPath);
long pivotPoint = path[0].N;
// Connect Points for Overlaps.
for (int i = 1; i < path.Count; ++i)
{
var j = (i == path.Count - 1) ? 0 : (i + 1);
var prev = path[i - 1];
var curr = path[i];
var next = path[j];
if (prev.N > curr.N)
{
var incr = TestPivot(path, i, pivotPoint);
if (incr)
{
if (prev.N == next.N)
curr.N = prev.N;
else
curr.N = (pivotPoint + 1) < inPath.Count ? (pivotPoint + 1) : 0;
curr.D = 3;
path[i] = curr;
}
}
pivotPoint = path[i].N;
}
// Insert Skipped Points.
for (int i = 1; i < path.Count - 1;)
{
var prev = path[i - 1];
var curr = path[i];
var next = path[i + 1];
if (curr.N - prev.N > 1)
{
if (curr.N == next.N)
{
IntPoint ins = curr;
ins.N = (ins.N - 1);
path[i] = ins;
}
else
{
IntPoint ins = curr;
ins.N = (ins.N - 1);
path.Insert(i, ins);
}
}
else
{
i++;
}
}
path = DegeneratePivots(path, inPath, ref interiorStart);
return path;
}
// Rough shape only used in Inspector for quick preview.
internal static List<Vector2> GetOutlinePath(Vector3[] shapePath, float offsetDistance)
{
const float kClipperScale = 10000.0f;
List<IntPoint> path = new List<IntPoint>();
List<Vector2> output = new List<Vector2>();
for (var i = 0; i < shapePath.Length; ++i)
{
var newPoint = new Vector2(shapePath[i].x, shapePath[i].y) * kClipperScale;
path.Add(new IntPoint((System.Int64)(newPoint.x), (System.Int64)(newPoint.y)));
}
List<List<IntPoint>> solution = new List<List<IntPoint>>();
ClipperOffset clipOffset = new ClipperOffset(24.0f);
clipOffset.AddPath(path, JoinTypes.jtRound, EndTypes.etClosedPolygon);
clipOffset.Execute(ref solution, kClipperScale * offsetDistance, path.Count);
if (solution.Count > 0)
{
int interiorStart = 0;
var outPath = solution[0];
outPath = FixPivots(outPath, path, ref interiorStart);
for (int i = 0; i < outPath.Count; ++i)
output.Add(new Vector2(outPath[i].X / kClipperScale, outPath[i].Y / kClipperScale));
}
return output;
}
static void TransferToMesh(NativeArray<LightMeshVertex> vertices, int vertexCount, NativeArray<ushort> indices,
int indexCount, Light2D light)
{
var mesh = light.lightMesh;
mesh.SetVertexBufferParams(vertexCount, LightMeshVertex.VertexLayout);
mesh.SetVertexBufferData(vertices, 0, 0, vertexCount);
mesh.SetIndices(indices, 0, indexCount, MeshTopology.Triangles, 0, true);
light.vertices = new LightMeshVertex[vertexCount];
NativeArray<LightMeshVertex>.Copy(vertices, light.vertices, vertexCount);
light.indices = new ushort[indexCount];
NativeArray<ushort>.Copy(indices, light.indices, indexCount);
}
public static Bounds GenerateShapeMesh(Light2D light, Vector3[] shapePath, float falloffDistance, float batchColor)
{
const float kClipperScale = 10000.0f;
var restoreState = Random.state;
Random.InitState(123456); // for deterministic output
// todo Revisit this while we do Batching.
var meshInteriorColor = new Color(0, 0, batchColor, 1.0f);
var meshExteriorColor = new Color(0, 0, batchColor, 0.0f);
// Create shape geometry based on edges
int inEdgeCount = shapePath.Length;
NativeArray<int2> tessInEdges = new NativeArray<int2>(inEdgeCount, Allocator.Temp);
NativeArray<float2> tessInVertices = new NativeArray<float2>(inEdgeCount, Allocator.Temp);
for (int i = 0; i < inEdgeCount; ++i)
{
int edgeEnd = i + 1;
if (edgeEnd == inEdgeCount)
edgeEnd = 0;
int2 edge = new int2(i, edgeEnd);
tessInEdges[i] = edge;
int index = edge.x;
tessInVertices[index] = new float2(shapePath[index].x, shapePath[index].y);
}
// Do tessellation
NativeArray<int> tessOutIndices = new NativeArray<int>(tessInEdges.Length * 8, Allocator.Temp);
NativeArray<float2> tessOutVertices = new NativeArray<float2>(tessInEdges.Length * 8, Allocator.Temp);
NativeArray<int2> tessOutEdges = new NativeArray<int2>(tessInEdges.Length * 8, Allocator.Temp);
int tessOutVertexCount = 0;
int tessOutIndexCount = 0;
#if USING_2DCOMMON
int tessOutEdgeCount = 0;
// runPlanarGraph must stay true: the user can drag a Freeform shape until its edges cross,
// and the tessellator overruns its edge buffer unless crossing edges are split first.
UnityEngine.U2D.Common.UTess.ModuleHandle.Tessellate(Allocator.Temp, tessInVertices, tessInEdges, ref tessOutVertices, out tessOutVertexCount, ref tessOutIndices, out tessOutIndexCount, ref tessOutEdges, out tessOutEdgeCount, true);
#endif
// Create falloff geometry with random noise to account for collinear points
var inputPointCount = shapePath.Length;
List<IntPoint> path = new List<IntPoint>();
for (var i = 0; i < inputPointCount; ++i)
{
var nx = (System.Int64)((double)shapePath[i].x * (double)kClipperScale);
var ny = (System.Int64)((double)shapePath[i].y * (double)kClipperScale);
var addPoint = new IntPoint(nx + Random.Range(-10, 10), ny + Random.Range(-10, 10));
addPoint.N = i; addPoint.D = -1;
path.Add(addPoint);
}
var lastPointIndex = inputPointCount - 1;
var interiorStartPoint = 0;
// Generate Bevels.
List<List<IntPoint>> solution = new List<List<IntPoint>>();
ClipperOffset clipOffset = new ClipperOffset(24.0f);
clipOffset.AddPath(path, JoinTypes.jtRound, EndTypes.etClosedPolygon);
clipOffset.Execute(ref solution, kClipperScale * falloffDistance, path.Count);
if (solution.Count > 0)
{
// Fix path for Pivots.
var outPath = solution[0];
var minPath = (long)inputPointCount;
for (int i = 0; i < outPath.Count; ++i)
minPath = (outPath[i].N != -1) ? Math.Min(minPath, outPath[i].N) : minPath;
var containsStart = minPath == 0;
outPath = FixPivots(outPath, path, ref interiorStartPoint);
// Size accounts for light mesh + falloff geometry(outer + inner)
int totalOutVertices = tessOutVertexCount + outPath.Count + inputPointCount;
int totalOutIndices = tessOutIndexCount + (outPath.Count * 6) + 6;
var outVertices = new NativeArray<LightMeshVertex>(totalOutVertices, Allocator.Temp);
var outIndices = new NativeArray<ushort>(totalOutIndices, Allocator.Temp);
for (int i = 0; i < tessOutIndexCount; ++i)
outIndices[i] = (ushort)tessOutIndices[i];
for (int i = 0; i < tessOutVertexCount; ++i)
{
outVertices[i] = new LightMeshVertex()
{
position = new float3(tessOutVertices[i].x, tessOutVertices[i].y, 0),
color = meshInteriorColor
};
}
var vcount = tessOutVertexCount;
var icount = tessOutIndexCount;
var innerIndices = new ushort[inputPointCount];
// Inner Vertices. (These may or may not be part of the created path. Beware!!)
for (int i = 0; i < inputPointCount; ++i)
{
outVertices[vcount++] = new LightMeshVertex()
{
position = new float3(shapePath[i].x, shapePath[i].y, 0),
color = meshInteriorColor
};
innerIndices[i] = (ushort)(vcount - 1);
}
var saveIndex = (ushort)vcount;
var pathStart = saveIndex;
var prevIndex = outPath[0].N == -1 ? 0 : outPath[0].N;
// Outer Vertices
for (int i = 0; i < outPath.Count; ++i)
{
var curr = outPath[i];
var currPoint = new float2(curr.X / kClipperScale, curr.Y / kClipperScale);
var currIndex = curr.N == -1 ? 0 : curr.N;
outVertices[vcount++] = new LightMeshVertex()
{
position = new float3(currPoint.x, currPoint.y, 0),
color = (interiorStartPoint > i) ? meshExteriorColor : meshInteriorColor
};
if (prevIndex != currIndex)
{
outIndices[icount++] = innerIndices[prevIndex];
outIndices[icount++] = innerIndices[currIndex];
outIndices[icount++] = (ushort)(vcount - 1);
}
outIndices[icount++] = innerIndices[prevIndex];
outIndices[icount++] = saveIndex;
outIndices[icount++] = saveIndex = (ushort)(vcount - 1);
prevIndex = currIndex;
}
// Close the Loop.
{
outIndices[icount++] = pathStart;
outIndices[icount++] = innerIndices[minPath];
outIndices[icount++] = containsStart ? innerIndices[lastPointIndex] : saveIndex;
outIndices[icount++] = containsStart ? pathStart : saveIndex;
outIndices[icount++] = containsStart ? saveIndex : innerIndices[minPath];
// Last Triangle. todo: Remove Clipper Usage and use SpriteShape Geometry Generator for falloff potentially.
if (containsStart)
{
var kTolerance = 0.001f;
var connectingPoint = innerIndices[lastPointIndex];
// End point detection is tricky and depends on convexity of shape. Simple test is to just check the vertices and detect.
var testA = MathF.Abs(outVertices[connectingPoint].position.x - outVertices[outIndices[icount - 1]].position.x) > kTolerance || MathF.Abs(outVertices[connectingPoint].position.y - outVertices[outIndices[icount - 1]].position.y) > kTolerance;
var testB = MathF.Abs(outVertices[connectingPoint].position.x - outVertices[outIndices[icount - 2]].position.x) > kTolerance || MathF.Abs(outVertices[connectingPoint].position.y - outVertices[outIndices[icount - 2]].position.y) > kTolerance;
if (!testA || !testB)
connectingPoint = (ushort)(interiorStartPoint + inputPointCount + tessOutVertexCount - 1);
outIndices[icount++] = connectingPoint;
}
else
outIndices[icount++] = innerIndices[minPath - 1];
}
TransferToMesh(outVertices, vcount, outIndices, icount, light);
}
Random.state = restoreState;
return light.lightMesh.GetSubMesh(0).bounds;
}
public static Bounds GenerateParametricMesh(Light2D light, float radius, float falloffDistance, float angle, int sides, float batchColor)
{
var angleOffset = Mathf.PI / 2.0f + Mathf.Deg2Rad * angle;
if (sides < 3)
{
radius = 0.70710678118654752440084436210485f * radius;
sides = 4;
}
if (sides == 4)
{
angleOffset = Mathf.PI / 4.0f + Mathf.Deg2Rad * angle;
}
var vertexCount = 1 + 2 * sides;
var indexCount = 3 * 3 * sides;
var vertices = new NativeArray<LightMeshVertex>(vertexCount, Allocator.Temp);
var triangles = new NativeArray<ushort>(indexCount, Allocator.Temp);
var centerIndex = (ushort)(2 * sides);
var mesh = light.lightMesh;
// Only Alpha value in Color channel is ever used. May remove it or keep it for batching params in the future.
var color = new Color(0, 0, batchColor, 1.0f);
vertices[centerIndex] = new LightMeshVertex
{
position = float3.zero,
color = color
};
var radiansPerSide = 2 * Mathf.PI / sides;
var min = new float3(float.MaxValue, float.MaxValue, 0);
var max = new float3(float.MinValue, float.MinValue, 0);
for (var i = 0; i < sides; i++)
{
var endAngle = (i + 1) * radiansPerSide;
var extrudeDir = new float3(math.cos(endAngle + angleOffset), math.sin(endAngle + angleOffset), 0);
var endPoint = radius * extrudeDir;
var vertexIndex = (2 * i + 2) % (2 * sides);
vertices[vertexIndex] = new LightMeshVertex
{
position = endPoint,
color = new Color(extrudeDir.x, extrudeDir.y, batchColor, 0)
};
vertices[vertexIndex + 1] = new LightMeshVertex
{
position = endPoint,
color = color
};
// Triangle 1 (Tip)
var triangleIndex = 9 * i;
triangles[triangleIndex] = (ushort)(vertexIndex + 1);
triangles[triangleIndex + 1] = (ushort)(2 * i + 1);
triangles[triangleIndex + 2] = centerIndex;
// Triangle 2 (Upper Top Left)
triangles[triangleIndex + 3] = (ushort)(vertexIndex);
triangles[triangleIndex + 4] = (ushort)(2 * i);
triangles[triangleIndex + 5] = (ushort)(2 * i + 1);
// Triangle 2 (Bottom Top Left)
triangles[triangleIndex + 6] = (ushort)(vertexIndex + 1);
triangles[triangleIndex + 7] = (ushort)(vertexIndex);
triangles[triangleIndex + 8] = (ushort)(2 * i + 1);
min = math.min(min, endPoint + extrudeDir * falloffDistance);
max = math.max(max, endPoint + extrudeDir * falloffDistance);
}
mesh.SetVertexBufferParams(vertexCount, LightMeshVertex.VertexLayout);
mesh.SetVertexBufferData(vertices, 0, 0, vertexCount);
mesh.SetIndices(triangles, MeshTopology.Triangles, 0, false);
light.vertices = new LightMeshVertex[vertexCount];
NativeArray<LightMeshVertex>.Copy(vertices, light.vertices, vertexCount);
light.indices = new ushort[indexCount];
NativeArray<ushort>.Copy(triangles, light.indices, indexCount);
return new Bounds
{
min = min,
max = max
};
}
public static Bounds GenerateSpriteMesh(Light2D light, Sprite sprite, float batchColor)
{
var mesh = light.lightMesh;
if (sprite == null)
{
mesh.Clear();
return new Bounds(Vector3.zero, Vector3.zero);
}
// this needs to be called before getting UV at the line below.
// Venky fixed it, enroute to trunk
var uvs = sprite.uv;
var srcVertices = sprite.GetVertexAttribute<Vector3>(VertexAttribute.Position);
var srcUVs = sprite.GetVertexAttribute<Vector2>(VertexAttribute.TexCoord0);
var srcIndices = sprite.GetIndices();
var center = 0.5f * (sprite.bounds.min + sprite.bounds.max);
var vertices = new NativeArray<LightMeshVertex>(srcIndices.Length, Allocator.Temp);
var color = new Color(0, 0, batchColor, 1);
for (var i = 0; i < srcVertices.Length; i++)
{
vertices[i] = new LightMeshVertex
{
position = new Vector3(srcVertices[i].x, srcVertices[i].y, 0),
color = color,
uv = srcUVs[i]
};
}
mesh.SetVertexBufferParams(vertices.Length, LightMeshVertex.VertexLayout);
mesh.SetVertexBufferData(vertices, 0, 0, vertices.Length);
mesh.SetIndices(srcIndices, MeshTopology.Triangles, 0, true);
light.vertices = new LightMeshVertex[vertices.Length];
NativeArray<LightMeshVertex>.Copy(vertices, light.vertices, vertices.Length);
light.indices = new ushort[srcIndices.Length];
NativeArray<ushort>.Copy(srcIndices, light.indices, srcIndices.Length);
return mesh.GetSubMesh(0).bounds;
}
public static int GetShapePathHash(Vector3[] path)
{
unchecked
{
int hashCode = (int)2166136261;
if (path != null)
{
foreach (var point in path)
hashCode = hashCode * 16777619 ^ point.GetHashCode();
}
else
{
hashCode = 0;
}
return hashCode;
}
}
/// <summary>
/// Checks if another enabled Global light already exists in the scene
/// </summary>
internal static void CheckForExistingGlobalLight(GameObject obj)
{
#if UNITY_EDITOR
if (Light2DManager.TryGetLights(obj, out var lights))
{
foreach (var light in lights)
{
if (light.lightType == Light2D.LightType.Global && light.enabled)
{
Light2DManager.ErrorIfDuplicateGlobalLight(light);
}
}
}
#endif
}
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: fd90885134624894ba845bbc692698a0
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: d9f6628807013a2458d91f3c291f75aa
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,31 @@
using System;
using System.Collections.Generic;
using Unity.Collections;
using UnityEngine;
namespace UnityEngine.Rendering.Universal
{
/// <summary>
/// Class <c>Light2DProvider</c> has methods called in URP 2D which can be used to create custom lights.
/// </summary>
[Serializable]
public class Light2DProvider : Provider2D
{
/// <summary>
/// Returns the display name shown in the Light Type dropdown of the Light2D component.
/// </summary>
/// <returns>The GUIContent used in the Light2D Light Type dropdown.</returns>
public virtual GUIContent ProviderName() { return new GUIContent("Custom Light Provider", "Implemented by " + this.GetType().Name); }
/// <summary>
/// Returns the mesh used to render the Light2D.
/// </summary>
/// <returns>The mesh used for rendering.</returns>
public virtual Mesh GetMesh() { return null; }
/// <summary>
/// Internal entry point used by the rendering pipeline to retrieve the provider name.
/// </summary>
internal override GUIContent Internal_ProviderName(string componentName) { return ProviderName(); }
}
}
@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: fbffb490ff4824544a472e97ee0a6679
@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: f74b40ff9aaa99a49a36a3c646ca5917
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: f895f2b1dbb2da84e82eedb22e2620f7
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,50 @@
#if UNITY_EDITOR
using UnityEngine.Rendering.RenderGraphModule;
#endif
namespace UnityEngine.Rendering.Universal
{
static class LayerDebug
{
#if UNITY_EDITOR
static internal bool enabled => FrameDebugger.enabled || RenderGraph.isRenderGraphViewerActive;
static internal string GetDebugLayerNames(LayerBatch layerBatch)
{
var debugNames = string.Empty;
var sortingLayers = Light2DManager.GetCachedSortingLayer();
foreach (var layer in sortingLayers)
{
if (layerBatch.IsValueWithinLayerRange(layer.value))
{
if (debugNames == string.Empty)
debugNames = layer.name;
else
debugNames += " | " + layer.name;
}
}
return debugNames;
}
#endif
static internal void FormatPassName(LayerBatch layerBatch, ref string passName)
{
#if UNITY_EDITOR
if (enabled)
passName += " - " + GetDebugLayerNames(layerBatch);
#endif
}
static internal ProfilingSampler GetProfilingSampler(string passName, ProfilingSampler sampler)
{
#if UNITY_EDITOR
if (enabled)
return new ProfilingSampler(passName);
#endif
return sampler;
}
}
}
@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: a2edf664af334144a8ad0aed989753d3
@@ -0,0 +1,219 @@
using Unity.Mathematics;
using System.Collections.Generic;
namespace UnityEngine.Rendering.Universal
{
internal class LayerBatch
{
#if UNITY_EDITOR
internal int startIndex;
internal int endIndex;
#endif
internal int startLayerID;
internal int endLayerValue;
internal SortingLayerRange layerRange;
internal LightStats lightStats;
internal bool useNormals;
internal List<Light2D> lights = new List<Light2D>();
internal List<int> shadowIndices = new List<int>();
internal List<ShadowCasterGroup2D> shadowCasters = new List<ShadowCasterGroup2D>();
internal int[] activeBlendStylesIndices;
// This range check uses SortingLayer.value.
internal bool IsValueWithinLayerRange(int value)
{
return value >= layerRange.lowerBound && value <= layerRange.upperBound;
}
}
internal static class LayerUtility
{
private static LayerBatch[] s_LayerBatches;
#if UNITY_EDITOR
[RuntimeInitializeOnLoadMethod]
static void ResetStaticsOnLoad()
{
s_LayerBatches = null;
}
#endif
private static bool CanBatchLightsInLayer(int layerIndex1, int layerIndex2, SortingLayer[] sortingLayers, ILight2DCullResult lightCullResult)
{
var layerId1 = sortingLayers[layerIndex1].id;
var layerId2 = sortingLayers[layerIndex2].id;
foreach (var light in lightCullResult.visibleLights)
{
// If the lit layers are different don't batch.
if (light.IsLitLayer(layerId1) != light.IsLitLayer(layerId2))
return false;
}
foreach (var group in lightCullResult.visibleShadows)
{
foreach (var shadowCaster in group.GetShadowCasters())
{
// Don't batch when the layer has different shadow casters
if (shadowCaster.IsShadowedLayer(layerId1) != shadowCaster.IsShadowedLayer(layerId2))
return false;
}
}
return true;
}
private static bool CanBatchCameraSortingLayer(int startLayerIndex, SortingLayer[] sortingLayers, Renderer2DData rendererData)
{
if (rendererData.useCameraSortingLayerTexture)
{
var cameraSortingLayerBoundsIndex = rendererData.GetCameraSortingLayerBoundsIndex();
return sortingLayers[startLayerIndex].value == cameraSortingLayerBoundsIndex;
}
return false;
}
private static int FindUpperBoundInBatch(int startLayerIndex, SortingLayer[] sortingLayers, Renderer2DData rendererData)
{
// break layer if camera sorting layer is active
if (CanBatchCameraSortingLayer(startLayerIndex, sortingLayers, rendererData))
return startLayerIndex;
// start checking at the next layer
for (var i = startLayerIndex + 1; i < sortingLayers.Length; i++)
{
if (!CanBatchLightsInLayer(startLayerIndex, i, sortingLayers, rendererData.lightCullResult))
return i - 1;
if (CanBatchCameraSortingLayer(i, sortingLayers, rendererData))
return i;
}
return sortingLayers.Length - 1;
}
private static void InitializeBatchInfos(SortingLayer[] cachedSortingLayers)
{
var count = cachedSortingLayers.Length;
var needInit = s_LayerBatches == null;
if (s_LayerBatches is null)
{
s_LayerBatches = new LayerBatch[count];
}
#if UNITY_EDITOR
// we should fix. Make a non allocating version of this
if (!Application.isPlaying && s_LayerBatches.Length != count)
{
s_LayerBatches = new LayerBatch[count];
needInit = true;
}
#endif
if (needInit)
{
for (var i = 0; i < s_LayerBatches.Length; i++)
s_LayerBatches[i] = new LayerBatch();
}
}
public static LayerBatch[] CalculateBatches(Renderer2DData rendererData, out int batchCount)
{
var cachedSortingLayers = Light2DManager.GetCachedSortingLayer();
InitializeBatchInfos(cachedSortingLayers);
bool anyNormals = false;
batchCount = 0;
for (var i = 0; i < cachedSortingLayers.Length;)
{
var layerToRender = cachedSortingLayers[i].id;
var layerBatch = s_LayerBatches[batchCount++];
var lightStats = rendererData.lightCullResult.GetLightStatsByLayer(layerToRender, ref layerBatch);
// Find the highest layer that share the same set of lights and shadows as this layer.
var upperLayerInBatch = FindUpperBoundInBatch(i, cachedSortingLayers, rendererData);
// Some renderers override their sorting layer value with short.MinValue or short.MaxValue.
// When drawing the first sorting layer, we should include the range from short.MinValue to layerValue.
// Similarly, when drawing the last sorting layer, include the range from layerValue to short.MaxValue.
var startLayerValue = (short)cachedSortingLayers[i].value;
var lowerBound = (i == 0) ? short.MinValue : startLayerValue;
var endLayerValue = (short)cachedSortingLayers[upperLayerInBatch].value;
var upperBound = (upperLayerInBatch == cachedSortingLayers.Length - 1) ? short.MaxValue : endLayerValue;
// Renderer within this range share the same set of lights so they should be rendered together.
var sortingLayerRange = new SortingLayerRange(lowerBound, upperBound);
#if UNITY_EDITOR
layerBatch.startIndex = i;
layerBatch.endIndex = upperLayerInBatch;
#endif
layerBatch.startLayerID = layerToRender;
layerBatch.endLayerValue = endLayerValue;
layerBatch.layerRange = sortingLayerRange;
layerBatch.lightStats = lightStats;
anyNormals |= layerBatch.lightStats.useNormalMap;
i = upperLayerInBatch + 1;
}
// Account for Sprite Mask and normal map usage as there might be masks on a different layer that need to mask out the normals
for (var i = 0; i < batchCount; ++i)
{
var layerBatch = s_LayerBatches[i];
var hasSpriteMask = SpriteMaskUtility.HasSpriteMaskInLayerRange(layerBatch.layerRange);
layerBatch.useNormals = layerBatch.lightStats.useNormalMap || (anyNormals && hasSpriteMask);
}
SetupActiveBlendStyles();
return s_LayerBatches;
}
public static void GetFilterSettings(Renderer2DData rendererData, LayerBatch layerBatch, out FilteringSettings filterSettings)
{
filterSettings = FilteringSettings.defaultValue;
filterSettings.renderQueueRange = RenderQueueRange.all;
filterSettings.layerMask = rendererData.layerMask;
filterSettings.renderingLayerMask = 0xFFFFFFFF;
filterSettings.sortingLayerRange = layerBatch.layerRange;
}
static void SetupActiveBlendStyles()
{
// Calculate active blend styles to save on total light textures allocated
for (int i = 0; i < s_LayerBatches.Length; ++i)
{
ref var layer = ref s_LayerBatches[i];
// Determine number of blend styles used
int size = 0;
for (var blendStyleIndex = 0; blendStyleIndex < RendererLighting.k_ShapeLightTextureIDs.Length; blendStyleIndex++)
{
var blendStyleMask = (uint)(1 << blendStyleIndex);
var blendStyleUsed = (layer.lightStats.blendStylesUsed & blendStyleMask) > 0;
if (blendStyleUsed)
size++;
}
if (layer.activeBlendStylesIndices == null || layer.activeBlendStylesIndices.Length != size)
layer.activeBlendStylesIndices = new int[size];
// Save indices so we don't have to copy the whole Light2DBlendStyle struct
var index = 0;
for (var blendStyleIndex = 0; blendStyleIndex < RendererLighting.k_ShapeLightTextureIDs.Length; blendStyleIndex++)
{
var blendStyleMask = (uint)(1 << blendStyleIndex);
var blendStyleUsed = (layer.lightStats.blendStylesUsed & blendStyleMask) > 0;
if (blendStyleUsed)
layer.activeBlendStylesIndices[index++] = blendStyleIndex;
}
}
}
}
}
@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 837cbe89c0a94e149f5b494ae9c69a51
timeCreated: 1599108159
@@ -0,0 +1,166 @@
using UnityEngine.Experimental.Rendering;
namespace UnityEngine.Rendering.Universal
{
internal static class Light2DLookupTexture
{
internal static readonly string k_LightLookupProperty = "_LightLookup";
internal static readonly string k_FalloffLookupProperty = "_FalloffLookup";
internal static readonly int k_LightLookupID = Shader.PropertyToID(k_LightLookupProperty);
internal static readonly int k_FalloffLookupID = Shader.PropertyToID(k_FalloffLookupProperty);
private static RTHandle s_LightLookupRTHandle = null;
private static RTHandle s_FalloffRTHandle = null;
#if UNITY_EDITOR
[RuntimeInitializeOnLoadMethod]
static void ResetStaticsOnLoad()
{
Release();
}
#endif
public static RTHandle GetLightLookupTexture_Rendergraph()
{
if (s_LightLookupRTHandle == null || s_LightLookupRTHandle?.externalTexture == null)
{
var lightLookupTexture = CreatePointLightLookupTexture();
s_LightLookupRTHandle?.Release();
s_LightLookupRTHandle = RTHandles.Alloc(lightLookupTexture);
}
return s_LightLookupRTHandle;
}
public static RTHandle GetFallOffLookupTexture_Rendergraph()
{
if (s_FalloffRTHandle == null || s_FalloffRTHandle?.externalTexture == null)
{
var fallOffLookupTexture = CreateFalloffLookupTexture();
s_FalloffRTHandle?.Release();
s_FalloffRTHandle = RTHandles.Alloc(fallOffLookupTexture);
}
return s_FalloffRTHandle;
}
public static void Release()
{
s_FalloffRTHandle?.Release();
s_LightLookupRTHandle?.Release();
s_FalloffRTHandle = null;
s_LightLookupRTHandle = null;
}
private static Texture2D CreatePointLightLookupTexture()
{
const int WIDTH = 256;
const int HEIGHT = 256;
var textureFormat = GraphicsFormat.R8G8B8A8_UNorm;
if (SystemInfo.IsFormatSupported(GraphicsFormat.R16G16B16A16_SFloat, GraphicsFormatUsage.SetPixels))
textureFormat = GraphicsFormat.R16G16B16A16_SFloat;
else if (SystemInfo.IsFormatSupported(GraphicsFormat.R32G32B32A32_SFloat, GraphicsFormatUsage.SetPixels))
textureFormat = GraphicsFormat.R32G32B32A32_SFloat;
var texture = new Texture2D(WIDTH, HEIGHT, textureFormat, TextureCreationFlags.None);
texture.name = k_LightLookupProperty;
texture.filterMode = FilterMode.Bilinear;
texture.wrapMode = TextureWrapMode.Clamp;
var center = new Vector2(WIDTH / 2.0f, HEIGHT / 2.0f);
for (var y = 0; y < HEIGHT; y++)
{
for (var x = 0; x < WIDTH; x++)
{
var pos = new Vector2(x, y);
var distance = Vector2.Distance(pos, center);
var relPos = pos - center;
var direction = center - pos;
direction.Normalize();
// red = 1-0 distance
// green = 1-0 angle
// blue = direction.x
// alpha = direction.y
float red;
if (x == WIDTH - 1 || y == HEIGHT - 1)
red = 0;
else
red = Mathf.Clamp(1 - (2.0f * distance / WIDTH), 0.0f, 1.0f);
var cosAngle = Vector2.Dot(Vector2.down, relPos.normalized);
var angle = Mathf.Acos(cosAngle) / Mathf.PI; // 0-1
var green = Mathf.Clamp(1 - angle, 0.0f, 1.0f);
var blue = direction.x;
var alpha = direction.y;
var color = new Color(red, green, blue, alpha);
texture.SetPixel(x, y, color);
}
}
texture.Apply();
return texture;
}
private static Texture2D CreateFalloffLookupTexture()
{
const int WIDTH = 2048;
const int HEIGHT = 192;
const GraphicsFormat textureFormat = GraphicsFormat.R8G8B8A8_SRGB;
var texture = new Texture2D(WIDTH, HEIGHT - 64, textureFormat, TextureCreationFlags.None);
texture.name = k_FalloffLookupProperty;
texture.filterMode = FilterMode.Bilinear;
texture.wrapMode = TextureWrapMode.Clamp;
for (var y = 0; y < HEIGHT; y++)
{
var baseValue = (float)(y + 32) / (HEIGHT + 64);
var lineValue = -baseValue + 1;
var exponent = Mathf.Log(lineValue) / Mathf.Log(baseValue);
for (var x = 0; x < WIDTH; x++)
{
var t = (float)x / WIDTH;
var red = Mathf.Pow(t, exponent);
var color = new Color(red, 0, 0, 1);
if (y >= 32 && y < 160)
texture.SetPixel(x, y - 32, color);
}
}
texture.Apply();
return texture;
}
//#if UNITY_EDITOR
// [MenuItem("Light2D Debugging/Write Light Texture")]
// static public void WriteLightTexture()
// {
// var path = EditorUtility.SaveFilePanel("Save texture as PNG", "", "LightLookupTexture.exr", "png");
// CreatePointLightLookupTexture();
// byte[] imgData = s_PointLightLookupTexture.EncodeToEXR(Texture2D.EXRFlags.CompressRLE);
// if (imgData != null)
// File.WriteAllBytes(path, imgData);
// }
// [MenuItem("Light2D Debugging/Write Falloff Texture")]
// static public void WriteCurveTexture()
// {
// var path = EditorUtility.SaveFilePanel("Save texture as PNG", "", "FalloffLookupTexture.png", "png");
// CreateFalloffLookupTexture();
// byte[] imgData = s_FalloffLookupTexture.EncodeToPNG();
// if (imgData != null)
// File.WriteAllBytes(path, imgData);
// }
//#endif
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: fed3c64eff8b51649bfa3da654d69182
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,262 @@
using Unity.Collections;
using Unity.Mathematics;
using Unity.Collections.LowLevel.Unsafe;
using UnityEngine.Rendering.Universal.U2D.Profiler;
namespace UnityEngine.Rendering.Universal
{
internal class LightBuffer
{
static readonly internal int kMax = 2048 * 8;
static readonly internal int kCount = 1;
static readonly internal int kLightMod = 64;
static readonly internal int kBatchMax = 256;
private GraphicsBuffer m_GraphicsBuffer;
private NativeArray<int> m_Markers = new NativeArray<int>(kBatchMax, Allocator.Persistent, NativeArrayOptions.ClearMemory);
private NativeArray<PerLight2D> m_NativeBuffer = new NativeArray<PerLight2D>(kMax, Allocator.Persistent, NativeArrayOptions.ClearMemory);
internal GraphicsBuffer graphicsBuffer
{
get
{
if (null == m_GraphicsBuffer)
m_GraphicsBuffer = new GraphicsBuffer(GraphicsBuffer.Target.Structured, kMax, UnsafeUtility.SizeOf<PerLight2D>());
return m_GraphicsBuffer;
}
}
internal NativeArray<int> lightMarkers
{
get
{
return m_Markers;
}
}
internal NativeArray<PerLight2D> nativeBuffer
{
get
{
return m_NativeBuffer;
}
}
internal void Release()
{
m_GraphicsBuffer.Release();
m_GraphicsBuffer = null;
}
internal void Reset()
{
unsafe { UnsafeUtility.MemClear(m_Markers.GetUnsafePtr(), UnsafeUtility.SizeOf<int>() * LightBuffer.kBatchMax); }
unsafe { UnsafeUtility.MemClear(m_NativeBuffer.GetUnsafePtr(), UnsafeUtility.SizeOf<PerLight2D>() * LightBuffer.kMax); }
}
}
// The idea is to avoid CPU cost when rendering meshes with the same shader (Consider this a light-weight SRP batcher). To identify the Mesh instance ID in the Light Buffer we utilize a Slot Index
// identified from the Blue Channel of the Vertex Colors (Solely used for this purpose). This can batch a maximum of kLightMod meshes in best-case scenario. Simple but no optizations have been added yet
internal class LightBatch
{
static readonly int k_BufferOffset = Shader.PropertyToID("_BatchBufferOffset");
static int sBatchIndexCounter = 0; // For LightMesh asset conditioning to facilitate batching.
private static int batchLightMod => LightBuffer.kLightMod;
private static float batchRunningIndex => (sBatchIndexCounter++) % LightBuffer.kLightMod / (float)LightBuffer.kLightMod;
// Should be in Sync with USE_STRUCTURED_BUFFER_FOR_LIGHT2D_DATA
public static bool isBatchingSupported => false;
private int[] subsets = new int[LightBuffer.kMax];
private Mesh[] lightMeshes = new Mesh[LightBuffer.kMax];
private Matrix4x4[] matrices = new Matrix4x4[LightBuffer.kMax];
private LightBuffer[] lightBuffer = new LightBuffer[LightBuffer.kCount];
private Light2D cachedLight;
private Material cachedMaterial;
private int hashCode = 0;
private int lightCount = 0;
private int maxIndex = 0;
private int batchCount = 0;
private int activeCount = 0;
#if UNITY_EDITOR
[RuntimeInitializeOnLoadMethod]
static void ResetStaticsOnLoad()
{
sBatchIndexCounter = 0;
}
#endif
internal NativeArray<PerLight2D> nativeBuffer
{
get
{
if (null == lightBuffer[activeCount])
lightBuffer[activeCount] = new LightBuffer();
return lightBuffer[activeCount].nativeBuffer;
}
}
internal GraphicsBuffer graphicsBuffer
{
get
{
if (null == lightBuffer[activeCount])
lightBuffer[activeCount] = new LightBuffer();
return lightBuffer[activeCount].graphicsBuffer;
}
}
internal NativeArray<int> lightMarker
{
get
{
if (null == lightBuffer[activeCount])
lightBuffer[activeCount] = new LightBuffer();
return lightBuffer[activeCount].lightMarkers;
}
}
internal PerLight2D GetLight(int index) => nativeBuffer[index];
internal static int batchSlotIndex => (int)(batchRunningIndex * LightBuffer.kLightMod);
#if UNITY_EDITOR
static bool kRegisterCallback = false;
#endif
internal void SetLight(int index, PerLight2D light)
{
var buffer = nativeBuffer;
buffer[index] = light;
}
internal static float GetBatchColor()
{
return (float)batchSlotIndex / (float)batchLightMod;
}
internal static int GetBatchSlotIndex(float channelColor)
{
return (int)(channelColor * LightBuffer.kLightMod);
}
static int Hash(Light2D light, Material material)
{
unchecked
{
int _hashCode = (int)2166136261;
_hashCode = _hashCode * 16777619 ^ material.GetHashCode();
_hashCode = _hashCode * 16777619 ^ (light.lightCookieSprite == null ? 0 : light.lightCookieSprite.GetHashCode());
return _hashCode;
}
}
void Validate()
{
#if UNITY_EDITOR
if (!kRegisterCallback)
UnityEditor.AssemblyReloadEvents.beforeAssemblyReload += Release;
kRegisterCallback = true;
#endif
}
internal void Release()
{
for (int i = 0; i < LightBuffer.kCount; ++i)
lightBuffer[i]?.Release();
}
void ResetInternals()
{
for (int i = 0; i < LightBuffer.kCount; ++i)
if (null != lightBuffer[i])
lightBuffer[i].Reset();
}
void SetBuffer()
{
Validate();
graphicsBuffer.SetData(nativeBuffer, lightCount, lightCount, math.min(LightBuffer.kBatchMax, LightBuffer.kMax - lightCount));
}
internal int SlotIndex(int x)
{
return lightCount + x;
}
internal void Reset()
{
if (isBatchingSupported)
{
maxIndex = 0;
hashCode = 0;
batchCount = 0;
lightCount = 0;
activeCount = 0;
Shader.SetGlobalBuffer("_Light2DBuffer", graphicsBuffer);
}
}
internal bool CanBatch(Light2D light, Material material, int index, out int lightHash)
{
if (material == null || light.lightMesh == null)
{
lightHash = 0;
return false;
}
lightHash = Hash(light, material);
hashCode = (hashCode == 0) ? lightHash : hashCode;
if (batchCount == 0)
{
hashCode = lightHash;
}
else if (hashCode != lightHash || SlotIndex(index) >= LightBuffer.kMax || lightMarker[index] == 1)
{
hashCode = lightHash;
return false;
}
return true;
}
internal bool AddBatch(Light2D light, Material material, Matrix4x4 mat, Mesh mesh, int subset, int lightHash, int index)
{
Debug.Assert(lightHash == hashCode);
cachedLight = light;
cachedMaterial = material;
matrices[batchCount] = mat;
lightMeshes[batchCount] = mesh;
subsets[batchCount] = subset;
batchCount++;
maxIndex = math.max(maxIndex, index);
var lightMark = lightMarker;
lightMark[index] = 1;
return true;
}
internal void Flush(RasterCommandBuffer cmd)
{
if (batchCount > 0)
{
using (new ProfilingScope(cmd, ProfilerMarkers.s_ProfilingDrawBatched))
{
SetBuffer();
cmd.SetGlobalInt(k_BufferOffset, lightCount);
cmd.DrawMultipleMeshes(matrices, lightMeshes, subsets, batchCount, cachedMaterial, -1, null);
}
lightCount = lightCount + maxIndex + 1;
}
#if ENABLE_PROFILER && PROFILER_INSTALLED
if (Renderer2D.canProfilerCapture)
ProfilerMarkers.s_U2DLightBatchCounterValue.Value++;
#endif
for (int i = 0; i < batchCount; ++i)
lightMeshes[i] = null;
ResetInternals();
batchCount = 0;
maxIndex = 0;
}
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: c67e061ed96500e4ba8ddeabc5f46958
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,364 @@
using UnityEngine.Experimental.Rendering;
using Unity.Mathematics;
namespace UnityEngine.Rendering.Universal
{
internal static class RendererLighting
{
public static readonly Color k_NormalClearColor = new Color(0.5f, 0.5f, 0.5f, 1.0f);
private static readonly string k_UsePointLightCookiesKeyword = "USE_POINT_LIGHT_COOKIES";
private static readonly string k_LightQualityFastKeyword = "LIGHT_QUALITY_FAST";
private static readonly string k_UseNormalMap = "USE_NORMAL_MAP";
private static readonly string k_UseShadowMap = "USE_SHADOW_MAP";
private static readonly string k_UseAdditiveBlendingKeyword = "USE_ADDITIVE_BLENDING";
private static readonly string k_UseVolumetric = "USE_VOLUMETRIC";
private static readonly string[] k_UseBlendStyleKeywords =
{
"USE_SHAPE_LIGHT_TYPE_0", "USE_SHAPE_LIGHT_TYPE_1", "USE_SHAPE_LIGHT_TYPE_2", "USE_SHAPE_LIGHT_TYPE_3"
};
private static readonly int[] k_BlendFactorsPropIDs =
{
Shader.PropertyToID("_ShapeLightBlendFactors0"),
Shader.PropertyToID("_ShapeLightBlendFactors1"),
Shader.PropertyToID("_ShapeLightBlendFactors2"),
Shader.PropertyToID("_ShapeLightBlendFactors3")
};
private static readonly int[] k_MaskFilterPropIDs =
{
Shader.PropertyToID("_ShapeLightMaskFilter0"),
Shader.PropertyToID("_ShapeLightMaskFilter1"),
Shader.PropertyToID("_ShapeLightMaskFilter2"),
Shader.PropertyToID("_ShapeLightMaskFilter3")
};
private static readonly int[] k_InvertedFilterPropIDs =
{
Shader.PropertyToID("_ShapeLightInvertedFilter0"),
Shader.PropertyToID("_ShapeLightInvertedFilter1"),
Shader.PropertyToID("_ShapeLightInvertedFilter2"),
Shader.PropertyToID("_ShapeLightInvertedFilter3")
};
public static readonly string[] k_ShapeLightTextureIDs =
{
"_ShapeLightTexture0",
"_ShapeLightTexture1",
"_ShapeLightTexture2",
"_ShapeLightTexture3"
};
private static GraphicsFormat s_RenderTextureFormatToUse = GraphicsFormat.R8G8B8A8_UNorm;
private static bool s_HasSetupRenderTextureFormatToUse;
private static readonly int k_SrcBlendID = Shader.PropertyToID("_SrcBlend");
private static readonly int k_DstBlendID = Shader.PropertyToID("_DstBlend");
private static readonly int k_CookieTexID = Shader.PropertyToID("_CookieTex");
private static readonly int k_PointLightCookieTexID = Shader.PropertyToID("_PointLightCookieTex");
private static readonly int k_L2DInvMatrix = Shader.PropertyToID("L2DInvMatrix");
private static readonly int k_L2DColor = Shader.PropertyToID("L2DColor");
private static readonly int k_L2DPosition = Shader.PropertyToID("L2DPosition");
private static readonly int k_L2DFalloffIntensity = Shader.PropertyToID("L2DFalloffIntensity");
private static readonly int k_L2DFalloffDistance = Shader.PropertyToID("L2DFalloffDistance");
private static readonly int k_L2DOuterAngle = Shader.PropertyToID("L2DOuterAngle");
private static readonly int k_L2DInnerAngle = Shader.PropertyToID("L2DInnerAngle");
private static readonly int k_L2DInnerRadiusMult = Shader.PropertyToID("L2DInnerRadiusMult");
private static readonly int k_L2DVolumeOpacity = Shader.PropertyToID("L2DVolumeOpacity");
private static readonly int k_L2DShadowIntensity = Shader.PropertyToID("L2DShadowIntensity");
private static readonly int k_L2DLightType = Shader.PropertyToID("L2DLightType");
private static readonly int k_L2DLightRenderingLayer = Shader.PropertyToID("L2DLightRenderingLayer");
// Light Batcher.
internal static LightBatch lightBatch = new LightBatch();
#if UNITY_EDITOR
[RuntimeInitializeOnLoadMethod]
static void ResetStaticsOnLoad()
{
s_HasSetupRenderTextureFormatToUse = false;
s_RenderTextureFormatToUse = GraphicsFormat.R8G8B8A8_UNorm;
lightBatch.Release();
lightBatch = new LightBatch();
}
#endif
internal static GraphicsFormat GetRenderTextureFormat()
{
if (!s_HasSetupRenderTextureFormatToUse)
{
// UUM-41070: We require `Linear | Render` but with the deprecated FormatUsage this was checking `Blend`
// For now, we keep checking for `Blend` until the performance hit of doing the correct checks is evaluated
if (SystemInfo.IsFormatSupported(GraphicsFormat.B10G11R11_UFloatPack32, GraphicsFormatUsage.Blend))
s_RenderTextureFormatToUse = GraphicsFormat.B10G11R11_UFloatPack32;
else if (SystemInfo.IsFormatSupported(GraphicsFormat.R16G16B16A16_SFloat, GraphicsFormatUsage.Blend))
s_RenderTextureFormatToUse = GraphicsFormat.R16G16B16A16_SFloat;
s_HasSetupRenderTextureFormatToUse = true;
}
return s_RenderTextureFormatToUse;
}
internal static void EnableBlendStyle(IRasterCommandBuffer cmd, int blendStyleIndex, bool enabled)
{
var keyword = k_UseBlendStyleKeywords[blendStyleIndex];
if (enabled)
cmd.EnableShaderKeyword(keyword);
else
cmd.DisableShaderKeyword(keyword);
}
internal static void DisableAllKeywords(IRasterCommandBuffer cmd)
{
foreach (var keyword in k_UseBlendStyleKeywords)
{
cmd.DisableShaderKeyword(keyword);
}
}
internal static void GetTransparencySortingMode(Renderer2DData rendererData, Camera camera, ref SortingSettings sortingSettings)
{
var mode = rendererData.transparencySortMode;
if (mode == TransparencySortMode.Default)
{
mode = camera.orthographic ? TransparencySortMode.Orthographic : TransparencySortMode.Perspective;
}
switch (mode)
{
case TransparencySortMode.Perspective:
sortingSettings.distanceMetric = DistanceMetric.Perspective;
break;
case TransparencySortMode.Orthographic:
sortingSettings.distanceMetric = DistanceMetric.Orthographic;
break;
default:
sortingSettings.distanceMetric = DistanceMetric.CustomAxis;
sortingSettings.customAxis = rendererData.transparencySortAxis;
break;
}
}
internal static bool CanCastShadows(Light2D light, int layerToRender)
{
return light.shadowsEnabled && light.shadowIntensity > 0 && light.IsLitLayer(layerToRender);
}
internal static bool CanCastVolumetricShadows(Light2D light, int endLayerValue)
{
return light.volumeIntensity > 0 && light.volumetricEnabled && light.renderVolumetricShadows && light.GetTopMostLitLayer() == endLayerValue;
}
internal static void SetLightShaderGlobals(IRasterCommandBuffer cmd, Light2DBlendStyle[] lightBlendStyles, int[] blendStyleIndices)
{
for (var i = 0; i < blendStyleIndices.Length; i++)
{
var blendStyleIndex = blendStyleIndices[i];
if (blendStyleIndex >= k_BlendFactorsPropIDs.Length)
break;
var blendStyle = lightBlendStyles[blendStyleIndex];
cmd.SetGlobalVector(k_BlendFactorsPropIDs[blendStyleIndex], blendStyle.blendFactors);
cmd.SetGlobalVector(k_MaskFilterPropIDs[blendStyleIndex], blendStyle.maskTextureChannelFilter.mask);
cmd.SetGlobalVector(k_InvertedFilterPropIDs[blendStyleIndex], blendStyle.maskTextureChannelFilter.inverted);
}
}
private static float GetNormalizedInnerRadius(Light2D light)
{
return light.pointLightInnerRadius / light.pointLightOuterRadius;
}
private static float GetNormalizedAngle(float angle)
{
return (angle / 360.0f);
}
private static void GetScaledLightInvMatrix(Light2D light, out Matrix4x4 retMatrix)
{
var outerRadius = light.pointLightOuterRadius;
var lightScale = Vector3.one;
var outerRadiusScale = new Vector3(lightScale.x * outerRadius, lightScale.y * outerRadius, lightScale.z * outerRadius);
var transform = light.transform;
var scaledLightMat = Matrix4x4.TRS(transform.position, transform.rotation, outerRadiusScale);
retMatrix = Matrix4x4.Inverse(scaledLightMat);
}
internal static void SetPerLightShaderGlobals(IRasterCommandBuffer cmd, Light2D light, int slot, bool isVolumetric, bool hasShadows, bool batchingSupported)
{
float intensity = light.intensity * light.color.a;
Color color = intensity * light.color;
color.a = 1.0f;
float volumeIntensity = light.volumetricEnabled ? light.volumeIntensity : 1.0f;
if (batchingSupported)
{
// Batched Params.
PerLight2D perLight = lightBatch.GetLight(slot);
perLight.Position = new float4(light.transform.position, light.normalMapDistance);
perLight.FalloffIntensity = light.falloffIntensity;
perLight.FalloffDistance = light.shapeLightFalloffSize;
perLight.Color = new float4(color.r, color.g, color.b, color.a);
perLight.VolumeOpacity = volumeIntensity;
perLight.LightType = (int)light.lightType;
perLight.ShadowIntensity = 1.0f;
if (hasShadows)
perLight.ShadowIntensity = isVolumetric ? (1 - light.shadowVolumeIntensity) : (1 - light.shadowIntensity);
lightBatch.SetLight(slot, perLight);
}
else
{
cmd.SetGlobalVector(k_L2DPosition, new float4(light.transform.position, light.normalMapDistance));
cmd.SetGlobalFloat(k_L2DFalloffIntensity, light.falloffIntensity);
cmd.SetGlobalFloat(k_L2DFalloffDistance, light.shapeLightFalloffSize);
cmd.SetGlobalColor(k_L2DColor, color);
cmd.SetGlobalFloat(k_L2DVolumeOpacity, volumeIntensity);
cmd.SetGlobalInt(k_L2DLightType, (int)light.lightType);
cmd.SetGlobalInt(k_L2DLightRenderingLayer, (int)light.renderingLayerMask.value);
cmd.SetGlobalFloat(k_L2DShadowIntensity, hasShadows ? (isVolumetric ? (1 - light.shadowVolumeIntensity) : (1 - light.shadowIntensity)) : 1);
}
if (hasShadows)
ShadowRendering.SetGlobalShadowProp(cmd);
}
internal static void SetPerPointLightShaderGlobals(IRasterCommandBuffer cmd, Light2D light, int slot, bool batchingSupported)
{
// This is used for the lookup texture
GetScaledLightInvMatrix(light, out var lightInverseMatrix);
var innerRadius = GetNormalizedInnerRadius(light);
var innerAngle = GetNormalizedAngle(light.pointLightInnerAngle);
var outerAngle = GetNormalizedAngle(light.pointLightOuterAngle);
var innerRadiusMult = 1 / (1 - innerRadius);
if (batchingSupported)
{
// Batched Params.
PerLight2D perLight = lightBatch.GetLight(slot);
perLight.InvMatrix = new float4x4(lightInverseMatrix.GetColumn(0), lightInverseMatrix.GetColumn(1), lightInverseMatrix.GetColumn(2), lightInverseMatrix.GetColumn(3));
perLight.InnerRadiusMult = innerRadiusMult;
perLight.InnerAngle = innerAngle;
perLight.OuterAngle = outerAngle;
lightBatch.SetLight(slot, perLight);
}
else
{
cmd.SetGlobalMatrix(k_L2DInvMatrix, lightInverseMatrix);
cmd.SetGlobalFloat(k_L2DInnerRadiusMult, innerRadiusMult);
cmd.SetGlobalFloat(k_L2DInnerAngle, innerAngle);
cmd.SetGlobalFloat(k_L2DOuterAngle, outerAngle);
}
}
internal static void SetCookieShaderProperties(Light2D light, MaterialPropertyBlock properties)
{
if (light.useCookieSprite && light.m_CookieSpriteTextureHandle.IsValid())
properties.SetTexture(light.lightType == Light2D.LightType.Sprite ? k_CookieTexID : k_PointLightCookieTexID, light.m_CookieSpriteTextureHandle);
}
private static void SetBlendModes(Material material, BlendMode src, BlendMode dst)
{
material.SetFloat(k_SrcBlendID, (float)src);
material.SetFloat(k_DstBlendID, (float)dst);
}
private static uint GetLightMaterialIndex(Light2D light, bool isVolume, bool useShadows)
{
var isPoint = light.isPointLight;
var bitIndex = 0;
var volumeBit = isVolume ? 1u << bitIndex : 0u;
bitIndex++;
var shapeBit = (isVolume && !isPoint) ? 1u << bitIndex : 0u;
bitIndex++;
var additiveBit = light.overlapOperation == Light2D.OverlapOperation.AlphaBlend ? 0u : 1u << bitIndex;
bitIndex++;
var pointCookieBit = (isPoint && light.lightCookieSprite != null && light.lightCookieSprite.texture != null) ? 1u << bitIndex : 0u;
bitIndex++;
var fastQualityBit = (light.normalMapQuality == Light2D.NormalMapQuality.Fast) ? 1u << bitIndex : 0u;
bitIndex++;
var useNormalMap = light.normalMapQuality != Light2D.NormalMapQuality.Disabled ? 1u << bitIndex : 0u;
bitIndex++;
var useShadowMap = useShadows ? 1u << bitIndex : 0u;
return fastQualityBit | pointCookieBit | additiveBit | shapeBit | volumeBit | useNormalMap | useShadowMap;
}
private static Material CreateLightMaterial(Renderer2DData rendererData, Light2D light, bool isVolume, bool useShadows)
{
if (!GraphicsSettings.TryGetRenderPipelineSettings<Renderer2DResources>(out var resources))
return null;
var isPoint = light.isPointLight;
Material material = CoreUtils.CreateEngineMaterial(resources.lightShader);
if (!isVolume)
{
if (light.overlapOperation == Light2D.OverlapOperation.Additive)
{
SetBlendModes(material, BlendMode.One, BlendMode.One);
material.EnableKeyword(k_UseAdditiveBlendingKeyword);
}
else
SetBlendModes(material, BlendMode.SrcAlpha, BlendMode.OneMinusSrcAlpha);
}
else
{
material.EnableKeyword(k_UseVolumetric);
if (light.lightType == Light2D.LightType.Point)
SetBlendModes(material, BlendMode.One, BlendMode.One);
else
SetBlendModes(material, BlendMode.SrcAlpha, BlendMode.One);
}
if (isPoint && light.lightCookieSprite != null && light.lightCookieSprite.texture != null)
material.EnableKeyword(k_UsePointLightCookiesKeyword);
if (light.normalMapQuality == Light2D.NormalMapQuality.Fast)
material.EnableKeyword(k_LightQualityFastKeyword);
if (light.normalMapQuality != Light2D.NormalMapQuality.Disabled)
material.EnableKeyword(k_UseNormalMap);
if (useShadows)
material.EnableKeyword(k_UseShadowMap);
return material;
}
internal static Material GetLightMaterial(this Renderer2DData rendererData, Light2D light, bool isVolume, bool useShadows)
{
var materialIndex = GetLightMaterialIndex(light, isVolume, useShadows);
if (!rendererData.lightMaterials.TryGetValue(materialIndex, out var material))
{
material = CreateLightMaterial(rendererData, light, isVolume, useShadows);
rendererData.lightMaterials[materialIndex] = material;
}
return material;
}
internal static short GetCameraSortingLayerBoundsIndex(this Renderer2DData rendererData)
{
SortingLayer[] sortingLayers = Light2DManager.GetCachedSortingLayer();
for (short i = 0; i < sortingLayers.Length; i++)
{
if (sortingLayers[i].id == rendererData.cameraSortingLayerTextureBound)
return (short)sortingLayers[i].value;
}
return short.MinValue;
}
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 7978073cb0cd25f4f8e29b37e3f55d2a
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,502 @@
using UnityEngine.Scripting.APIUpdating;
namespace UnityEngine.Rendering.Universal
{
/// <summary>
/// The Pixel Perfect Camera component ensures your pixel art remains crisp and clear at different resolutions, and stable in motion.
/// </summary>
[ExecuteInEditMode]
[DisallowMultipleComponent]
[AddComponentMenu("Rendering/2D/Pixel Perfect Camera")]
[RequireComponent(typeof(Camera))]
[MovedFrom(true, "UnityEngine.Experimental.Rendering.Universal")]
[Icon("Packages/com.unity.render-pipelines.universal/Editor/2D/ResourceIcons/PixelPerfectCamera.png")]
[HelpURL("https://docs.unity3d.com/Packages/com.unity.render-pipelines.universal@latest/index.html?subfolder=/manual/2d-pixelperfect.html%23properties")]
public class PixelPerfectCamera : MonoBehaviour, IPixelPerfectCamera, ISerializationCallbackReceiver
{
/// <summary>
/// An enumeration of the types of display cropping.
/// </summary>
public enum CropFrame
{
/// <summary>
/// No cropping.
/// </summary>
None,
/// <summary>
/// Black borders added to the left and right of viewport to match Reference Resolution.
/// </summary>
Pillarbox,
/// <summary>
/// Black borders added to the top and bottom of viewport to match Reference Resolution.
/// </summary>
Letterbox,
/// <summary>
/// Black borders added to all sides of viewport to match Reference Resolution.
/// </summary>
Windowbox,
/// <summary>
/// Expands the viewport to fit the screen resolution while maintaining the viewport's aspect ratio.
/// </summary>
StretchFill
}
/// <summary>
/// Determines how pixels are snapped to the grid.
/// </summary>
public enum GridSnapping
{
/// <summary>
/// No snapping.
/// </summary>
None,
/// <summary>
/// Prevent subpixel movement and make Sprites appear to move in pixel-by-pixel increments.
/// </summary>
PixelSnapping,
/// <summary>
/// The scene is rendered to a temporary texture set as close as possible to the Reference Resolution, while maintaining the full screen aspect ratio. This temporary texture is then upscaled to fit the full screen.
/// </summary>
UpscaleRenderTexture
}
/// <summary>
/// Defines the filter mode use to render the final render target.
/// </summary>
public enum PixelPerfectFilterMode
{
/// <summary>
/// Uses point filter to upscale to closest multiple of Reference Resolution, followed by bilinear filter to the target resolution.
/// </summary>
RetroAA,
/// <summary>
/// Uses point filter to upscale to target resolution.
/// </summary>
Point,
}
private enum ComponentVersions
{
Version_Unserialized = 0,
Version_1 = 1
}
#if UNITY_EDITOR
const ComponentVersions k_CurrentComponentVersion = ComponentVersions.Version_1;
[SerializeField] ComponentVersions m_ComponentVersion = ComponentVersions.Version_Unserialized;
#endif
/// <summary>
/// Defines how the output display will be cropped.
/// </summary>
public CropFrame cropFrame { get { return m_CropFrame; } set { m_CropFrame = value; } }
/// <summary>
/// Defines if pixels will be locked to a grid determined by assetsPPU.
/// </summary>
public GridSnapping gridSnapping { get { return m_GridSnapping; } set { m_GridSnapping = value; } }
/// <summary>
/// The target orthographic size of the camera.
/// </summary>
public float orthographicSize { get { return m_Internal.orthoSize; } }
/// <summary>
/// Match this value to to the Pixels Per Unit values of all Sprites within the Scene.
/// </summary>
public int assetsPPU { get { return m_AssetsPPU; } set { m_AssetsPPU = value > 0 ? value : 1; } }
/// <summary>
/// The original horizontal resolution your Assets are designed for.
/// </summary>
public int refResolutionX { get { return m_RefResolutionX; } set { m_RefResolutionX = value > 0 ? value : 1; } }
/// <summary>
/// Original vertical resolution your Assets are designed for.
/// </summary>
public int refResolutionY { get { return m_RefResolutionY; } set { m_RefResolutionY = value > 0 ? value : 1; } }
/// <summary>
/// Set to true to have the Scene rendered to a temporary texture set as close as possible to the Reference Resolution,
/// while maintaining the full screen aspect ratio. This temporary texture is then upscaled to fit the full screen.
/// </summary>
[System.Obsolete("Use gridSnapping instead #from(2021.2)")]
public bool upscaleRT
{
get
{
return m_GridSnapping == GridSnapping.UpscaleRenderTexture;
}
set
{
m_GridSnapping = value ? GridSnapping.UpscaleRenderTexture : GridSnapping.None;
}
}
/// <summary>
/// Set to true to prevent subpixel movement and make Sprites appear to move in pixel-by-pixel increments.
/// Only applicable when upscaleRT is false.
/// </summary>
[System.Obsolete("Use gridSnapping instead #from(2021.2)")]
public bool pixelSnapping
{
get
{
return m_GridSnapping == GridSnapping.PixelSnapping;
}
set
{
m_GridSnapping = value ? GridSnapping.PixelSnapping : GridSnapping.None;
}
}
/// <summary>
/// Set to true to crop the viewport with black bars to match refResolutionX in the horizontal direction.
/// </summary>
[System.Obsolete("Use cropFrame instead #from(2021.2)")]
public bool cropFrameX
{
get
{
return m_CropFrame == CropFrame.StretchFill || m_CropFrame == CropFrame.Windowbox || m_CropFrame == CropFrame.Pillarbox;
}
set
{
if (value)
{
if (m_CropFrame == CropFrame.None)
m_CropFrame = CropFrame.Pillarbox;
else if (m_CropFrame == CropFrame.Letterbox)
m_CropFrame = CropFrame.Windowbox;
}
else
{
if (m_CropFrame == CropFrame.Pillarbox)
m_CropFrame = CropFrame.None;
else if (m_CropFrame == CropFrame.Windowbox || m_CropFrame == CropFrame.StretchFill)
m_CropFrame = CropFrame.Letterbox;
}
}
}
/// <summary>
/// Set to true to crop the viewport with black bars to match refResolutionY in the vertical direction.
/// </summary>
[System.Obsolete("Use cropFrame instead #from(2021.2)")]
public bool cropFrameY
{
get
{
return m_CropFrame == CropFrame.StretchFill || m_CropFrame == CropFrame.Windowbox || m_CropFrame == CropFrame.Letterbox;
}
set
{
if (value)
{
if (m_CropFrame == CropFrame.None)
m_CropFrame = CropFrame.Letterbox;
else if (m_CropFrame == CropFrame.Pillarbox)
m_CropFrame = CropFrame.Windowbox;
}
else
{
if (m_CropFrame == CropFrame.Letterbox)
m_CropFrame = CropFrame.None;
else if (m_CropFrame == CropFrame.Windowbox || m_CropFrame == CropFrame.StretchFill)
m_CropFrame = CropFrame.Pillarbox;
}
}
}
/// <summary>
/// Set to true to expand the viewport to fit the screen resolution while maintaining the viewport's aspect ratio.
/// Only applicable when both cropFrameX and cropFrameY are true.
/// </summary>
[System.Obsolete("Use cropFrame instead. #from(2021.2)")]
public bool stretchFill
{
get
{
return m_CropFrame == CropFrame.StretchFill;
}
set
{
if (value)
m_CropFrame = CropFrame.StretchFill;
else
m_CropFrame = CropFrame.Windowbox;
}
}
/// <summary>
/// Ratio of the rendered Sprites compared to their original size (readonly).
/// </summary>
public int pixelRatio
{
get
{
if (m_CinemachineCompatibilityMode)
{
if (m_GridSnapping == GridSnapping.UpscaleRenderTexture)
return m_Internal.zoom * m_Internal.cinemachineVCamZoom;
else
return m_Internal.cinemachineVCamZoom;
}
else
{
return m_Internal.zoom;
}
}
}
/// <summary>
/// Returns if an upscale pass is required.
/// </summary>
public bool requiresUpscalePass
{
get
{
return m_Internal.requiresUpscaling;
}
}
/// <summary>
/// Round a arbitrary position to an integer pixel position. Works in world space.
/// </summary>
/// <param name="position"> The position you want to round.</param>
/// <returns>
/// The rounded pixel position.
/// Depending on the values of upscaleRT and pixelSnapping, it could be a screen pixel position or an art pixel position.
/// </returns>
public Vector3 RoundToPixel(Vector3 position)
{
float unitsPerPixel = m_Internal.unitsPerPixel;
if (unitsPerPixel == 0.0f)
return position;
Vector3 result;
result.x = Mathf.Round(position.x / unitsPerPixel) * unitsPerPixel;
result.y = Mathf.Round(position.y / unitsPerPixel) * unitsPerPixel;
result.z = Mathf.Round(position.z / unitsPerPixel) * unitsPerPixel;
return result;
}
/// <summary>
/// Find a pixel-perfect orthographic size as close to targetOrthoSize as possible. Used by Cinemachine to solve compatibility issues with Pixel Perfect Camera.
/// </summary>
/// <param name="targetOrthoSize">Orthographic size from the live Cinemachine Virtual Camera.</param>
/// <returns>The corrected orthographic size.</returns>
public float CorrectCinemachineOrthoSize(float targetOrthoSize)
{
m_CinemachineCompatibilityMode = true;
if (m_Internal == null)
return targetOrthoSize;
else
return m_Internal.CorrectCinemachineOrthoSize(targetOrthoSize);
}
[SerializeField] int m_AssetsPPU = 100;
[SerializeField] int m_RefResolutionX = 320;
[SerializeField] int m_RefResolutionY = 180;
[SerializeField] CropFrame m_CropFrame;
[SerializeField] GridSnapping m_GridSnapping;
[SerializeField] PixelPerfectFilterMode m_FilterMode = PixelPerfectFilterMode.RetroAA;
// These are obsolete. They are here only for migration.
#if UNITY_EDITOR
[SerializeField] bool m_UpscaleRT;
[SerializeField] bool m_PixelSnapping;
[SerializeField] bool m_CropFrameX;
[SerializeField] bool m_CropFrameY;
[SerializeField] bool m_StretchFill;
#endif
Camera m_Camera;
PixelPerfectCameraInternal m_Internal;
bool m_CinemachineCompatibilityMode;
internal FilterMode finalBlitFilterMode
{
get
{
return m_FilterMode == PixelPerfectFilterMode.RetroAA ? FilterMode.Bilinear : FilterMode.Point;
}
}
internal Vector2Int offscreenRTSize
{
get
{
return new Vector2Int(m_Internal.offscreenRTWidth, m_Internal.offscreenRTHeight);
}
}
Vector2Int cameraRTSize
{
get
{
var targetTexture = m_Camera.targetTexture;
return targetTexture == null ? new Vector2Int(Screen.width, Screen.height) : new Vector2Int(targetTexture.width, targetTexture.height);
}
}
// Snap camera position to pixels using Camera.worldToCameraMatrix.
void PixelSnap()
{
Vector3 cameraPosition = m_Camera.transform.position;
Vector3 roundedCameraPosition = RoundToPixel(cameraPosition);
Vector3 offset = roundedCameraPosition - cameraPosition;
offset.z = -offset.z;
// Get world to local camera matrix without scale
var invPos = Matrix4x4.TRS(cameraPosition + offset, Quaternion.identity, Vector3.one).inverse;
var invRot = Matrix4x4.Rotate(m_Camera.transform.rotation).inverse;
var scaleMatrix = Matrix4x4.Scale(new Vector3(1.0f, 1.0f, -1.0f));
// Calculate inverse TRS
m_Camera.worldToCameraMatrix = scaleMatrix * invRot * invPos;
}
void Awake()
{
m_Camera = GetComponent<Camera>();
m_Internal = new PixelPerfectCameraInternal(this);
// Case 1249076: Initialize internals immediately after the scene is loaded,
// as the Cinemachine extension may need them before OnBeginContextRendering is called.
UpdateCameraProperties();
}
void UpdateCameraProperties()
{
var rtSize = cameraRTSize;
m_Internal.CalculateCameraProperties(rtSize.x, rtSize.y);
if (m_Internal.useOffscreenRT)
m_Camera.pixelRect = m_Internal.CalculateFinalBlitPixelRect(rtSize.x, rtSize.y);
else
m_Camera.rect = new Rect(0.0f, 0.0f, 1.0f, 1.0f);
}
void OnBeginCameraRendering(ScriptableRenderContext context, Camera camera)
{
if (camera == m_Camera)
{
UpdateCameraProperties();
PixelSnap();
if (!m_CinemachineCompatibilityMode)
{
m_Camera.orthographicSize = m_Internal.orthoSize;
}
UnityEngine.U2D.PixelPerfectRendering.pixelSnapSpacing = m_Internal.unitsPerPixel;
}
}
void OnEndCameraRendering(ScriptableRenderContext context, Camera camera)
{
if (camera == m_Camera)
UnityEngine.U2D.PixelPerfectRendering.pixelSnapSpacing = 0.0f;
}
void OnEnable()
{
m_CinemachineCompatibilityMode = false;
RenderPipelineManager.beginCameraRendering += OnBeginCameraRendering;
RenderPipelineManager.endCameraRendering += OnEndCameraRendering;
}
internal void OnDisable()
{
RenderPipelineManager.beginCameraRendering -= OnBeginCameraRendering;
RenderPipelineManager.endCameraRendering -= OnEndCameraRendering;
m_Camera.rect = new Rect(0.0f, 0.0f, 1.0f, 1.0f);
m_Camera.ResetWorldToCameraMatrix();
}
#if UNITY_ENABLE_CHECKS
// Show on-screen warning about invalid render resolutions.
void OnGUI()
{
Color oldColor = GUI.color;
GUI.color = Color.red;
Vector2Int renderResolution = Vector2Int.zero;
renderResolution.x = m_Internal.useOffscreenRT ? m_Internal.offscreenRTWidth : m_Camera.pixelWidth;
renderResolution.y = m_Internal.useOffscreenRT ? m_Internal.offscreenRTHeight : m_Camera.pixelHeight;
if (renderResolution.x % 2 != 0 || renderResolution.y % 2 != 0)
{
string warning = string.Format("Rendering at an odd-numbered resolution ({0} * {1}). Pixel Perfect Camera may not work properly in this situation.", renderResolution.x, renderResolution.y);
GUILayout.Box(warning);
}
var targetTexture = m_Camera.targetTexture;
Vector2Int rtSize = targetTexture == null ? new Vector2Int(Screen.width, Screen.height) : new Vector2Int(targetTexture.width, targetTexture.height);
if (rtSize.x < refResolutionX || rtSize.y < refResolutionY)
{
GUILayout.Box("Target resolution is smaller than the reference resolution. Image may appear stretched or cropped.");
}
GUI.color = oldColor;
}
#endif
/// <summary>
/// OnBeforeSerialize implementation.
/// </summary>
public void OnBeforeSerialize()
{
#if UNITY_EDITOR
m_ComponentVersion = k_CurrentComponentVersion;
#endif
}
/// <summary>
/// OnAfterSerialize implementation.
/// </summary>
public void OnAfterDeserialize()
{
#if UNITY_EDITOR
// Upgrade from no serialized version
if (m_ComponentVersion == ComponentVersions.Version_Unserialized)
{
if (m_UpscaleRT)
m_GridSnapping = GridSnapping.UpscaleRenderTexture;
else if (m_PixelSnapping)
m_GridSnapping = GridSnapping.PixelSnapping;
if (m_CropFrameX && m_CropFrameY)
{
if (m_StretchFill)
m_CropFrame = CropFrame.StretchFill;
else
m_CropFrame = CropFrame.Windowbox;
}
else if (m_CropFrameX)
{
m_CropFrame = CropFrame.Pillarbox;
}
else if (m_CropFrameY)
{
m_CropFrame = CropFrame.Letterbox;
}
else
{
m_CropFrame = CropFrame.None;
}
m_ComponentVersion = ComponentVersions.Version_1;
}
#endif
}
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: c88f5cead0c0b2a4eb05b5900433f8d1
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {fileID: 2800000, guid: 7cba34d70552e144586a4d3ce4a33de5, type: 3}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,247 @@
using System;
namespace UnityEngine.Rendering.Universal
{
internal interface IPixelPerfectCamera
{
int assetsPPU { get; set; }
int refResolutionX { get; set; }
int refResolutionY { get; set; }
bool upscaleRT { get; set; }
bool pixelSnapping { get; set; }
bool cropFrameX { get; set; }
bool cropFrameY { get; set; }
bool stretchFill { get; set; }
}
[Serializable]
internal class PixelPerfectCameraInternal : ISerializationCallbackReceiver
{
// Case 1061634:
// In order for this class to survive hot reloading, we need to make the fields serializable.
// Unity can't serialize an interface object, but does properly serialize UnityEngine.Object.
// So we cast the reference to PixelPerfectCamera (which inherits UnityEngine.Object)
// before serialization happens, and restore the interface reference after deserialization.
[NonSerialized]
IPixelPerfectCamera m_Component;
PixelPerfectCamera m_SerializableComponent;
internal float originalOrthoSize;
internal bool hasPostProcessLayer;
internal bool cropFrameXAndY = false;
internal bool cropFrameXOrY = false;
internal bool useStretchFill = false;
internal int zoom = 1;
internal bool useOffscreenRT = false;
internal int offscreenRTWidth = 0;
internal int offscreenRTHeight = 0;
internal Rect pixelRect = Rect.zero;
internal float orthoSize = 1.0f;
internal float unitsPerPixel = 0.0f;
internal int cinemachineVCamZoom = 1;
internal bool requiresUpscaling = false;
internal PixelPerfectCameraInternal(IPixelPerfectCamera component)
{
m_Component = component;
}
public void OnBeforeSerialize()
{
m_SerializableComponent = m_Component as PixelPerfectCamera;
}
public void OnAfterDeserialize()
{
if (m_SerializableComponent != null)
m_Component = m_SerializableComponent;
}
internal void CalculateCameraProperties(int screenWidth, int screenHeight)
{
int assetsPPU = m_Component.assetsPPU;
int refResolutionX = m_Component.refResolutionX;
int refResolutionY = m_Component.refResolutionY;
bool upscaleRT = m_Component.upscaleRT;
bool pixelSnapping = m_Component.pixelSnapping;
bool cropFrameX = m_Component.cropFrameX;
bool cropFrameY = m_Component.cropFrameY;
bool stretchFill = m_Component.stretchFill;
cropFrameXAndY = cropFrameY && cropFrameX;
cropFrameXOrY = cropFrameY || cropFrameX;
useStretchFill = cropFrameXAndY && stretchFill;
requiresUpscaling = useStretchFill;
// zoom level (PPU scale)
int verticalZoom = screenHeight / refResolutionY;
int horizontalZoom = screenWidth / refResolutionX;
zoom = Math.Max(1, Math.Min(verticalZoom, horizontalZoom));
// off-screen RT
useOffscreenRT = false;
offscreenRTWidth = 0;
offscreenRTHeight = 0;
if (cropFrameXOrY)
{
useOffscreenRT = true;
if (!upscaleRT)
{
if (cropFrameXAndY)
{
offscreenRTWidth = zoom * refResolutionX;
offscreenRTHeight = zoom * refResolutionY;
}
else if (cropFrameY)
{
offscreenRTWidth = screenWidth;
offscreenRTHeight = zoom * refResolutionY;
}
else // crop frame X
{
offscreenRTWidth = zoom * refResolutionX;
offscreenRTHeight = screenHeight;
}
}
else
{
if (cropFrameXAndY)
{
offscreenRTWidth = refResolutionX;
offscreenRTHeight = refResolutionY;
}
else if (cropFrameY)
{
offscreenRTWidth = screenWidth / zoom / 2 * 2; // Make sure it's an even number by / 2 * 2.
offscreenRTHeight = refResolutionY;
}
else // crop frame X
{
offscreenRTWidth = refResolutionX;
offscreenRTHeight = screenHeight / zoom / 2 * 2; // Make sure it's an even number by / 2 * 2.
}
}
}
else if (upscaleRT && zoom > 1)
{
useOffscreenRT = true;
offscreenRTWidth = screenWidth / zoom / 2 * 2; // Make sure it's an even number by / 2 * 2.
offscreenRTHeight = screenHeight / zoom / 2 * 2;
}
// viewport
if (useOffscreenRT)
{
// When we ask the render pipeline to create the offscreen RT for us, the size of the RT is determined by VP size.
// That's why we set the VP size to be (m_OffscreenRTWidth, m_OffscreenRTHeight) here.
pixelRect = new Rect(0.0f, 0.0f, offscreenRTWidth, offscreenRTHeight);
}
else
pixelRect = Rect.zero;
// orthographic size
if (cropFrameY)
orthoSize = (refResolutionY * 0.5f) / assetsPPU;
else if (cropFrameX)
{
float aspect = (pixelRect == Rect.zero) ? (float)screenWidth / screenHeight : pixelRect.width / pixelRect.height;
orthoSize = ((refResolutionX / aspect) * 0.5f) / assetsPPU;
}
else if (upscaleRT && zoom > 1)
orthoSize = (offscreenRTHeight * 0.5f) / assetsPPU;
else
{
float pixelHeight = (pixelRect == Rect.zero) ? screenHeight : pixelRect.height;
orthoSize = (pixelHeight * 0.5f) / (zoom * assetsPPU);
}
// Camera pixel grid spacing
if (upscaleRT || (!upscaleRT && pixelSnapping))
unitsPerPixel = 1.0f / assetsPPU;
else
unitsPerPixel = 1.0f / (zoom * assetsPPU);
}
internal Rect CalculateFinalBlitPixelRect(int screenWidth, int screenHeight)
{
// This VP is used when the internal temp RT is blitted back to screen.
Rect pixelRect = new Rect();
if (useStretchFill)
{
// stretch (fit either width or height)
float screenAspect = (float)screenWidth / screenHeight;
float cameraAspect = (float)m_Component.refResolutionX / m_Component.refResolutionY;
if (screenAspect > cameraAspect)
{
pixelRect.height = screenHeight;
pixelRect.width = screenHeight * cameraAspect;
pixelRect.x = (screenWidth - (int)pixelRect.width) / 2;
pixelRect.y = 0;
}
else
{
pixelRect.width = screenWidth;
pixelRect.height = screenWidth / cameraAspect;
pixelRect.y = (screenHeight - (int)pixelRect.height) / 2;
pixelRect.x = 0;
}
// Doesn't require scaling if ref resolution is a multiple of target resolution
if (screenWidth % m_Component.refResolutionX == 0)
{
requiresUpscaling = cameraAspect < screenAspect;
}
else if (screenHeight % m_Component.refResolutionY == 0)
{
requiresUpscaling = cameraAspect > screenAspect;
}
}
else
{
// center
if (m_Component.upscaleRT)
{
pixelRect.height = zoom * offscreenRTHeight;
pixelRect.width = zoom * offscreenRTWidth;
}
else
{
pixelRect.height = offscreenRTHeight;
pixelRect.width = offscreenRTWidth;
}
pixelRect.x = (screenWidth - (int)pixelRect.width) / 2;
pixelRect.y = (screenHeight - (int)pixelRect.height) / 2;
}
return pixelRect;
}
// Find a pixel-perfect orthographic size as close to targetOrthoSize as possible.
internal float CorrectCinemachineOrthoSize(float targetOrthoSize)
{
float correctedOrthoSize;
if (m_Component.upscaleRT)
{
cinemachineVCamZoom = Math.Max(1, Mathf.RoundToInt(orthoSize / targetOrthoSize));
correctedOrthoSize = orthoSize / cinemachineVCamZoom;
}
else
{
cinemachineVCamZoom = Math.Max(1, Mathf.RoundToInt(zoom * orthoSize / targetOrthoSize));
correctedOrthoSize = zoom * orthoSize / cinemachineVCamZoom;
}
// In this case the actual zoom level is cinemachineVCamZoom instead of zoom.
if (!m_Component.upscaleRT && !m_Component.pixelSnapping)
unitsPerPixel = 1.0f / (cinemachineVCamZoom * m_Component.assetsPPU);
return correctedOrthoSize;
}
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: adcb49d2b04b1654b85e627fe5dde64f
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 11a70b6b97154411847bf26f0726152d
timeCreated: 1773110209
@@ -0,0 +1,87 @@
#if ENABLE_PROFILER && PROFILER_INSTALLED
using System;
using System.Collections.Generic;
using Unity.Profiling;
namespace UnityEngine.Rendering.Universal.U2D.Profiler
{
[Serializable]
struct MeshFrameData
{
public EntityId gameObjectEntityId;
public int triangleCount;
public int vertexCount;
}
class MeshFrameDataProfilerEmitter
{
List<MeshFrameData> m_FrameDataList = new List<MeshFrameData>();
int m_TriangleCount = 0;
int m_Tag;
ProfilerCounterValue<int> m_MeshCounter;
public MeshFrameDataProfilerEmitter(int tag, ProfilerCounterValue<int> meshCounter)
{
m_Tag = tag;
m_MeshCounter = meshCounter;
}
public void Capture(GameObject go, Mesh mesh)
{
if (!UnityEngine.Profiling.Profiler.enabled)
return;
// Don't add duplicates
for (int i = 0; i < m_FrameDataList.Count; ++i)
{
if (m_FrameDataList[i].gameObjectEntityId == go.GetEntityId())
return;
}
if (mesh != null)
{
var meshData = Mesh.AcquireReadOnlyMeshData(mesh);
var triangleCount = 0;
var vertexCount = 0;
for (int i = 0; i < meshData.Length; i++)
{
var m = meshData[i];
int indexCount = 0;
if (m.indexFormat == UnityEngine.Rendering.IndexFormat.UInt16)
{
var indexData = m.GetIndexData<ushort>();
indexCount = indexData.Length;
}
else
{
var indexData = m.GetIndexData<uint>();
indexCount = indexData.Length;
}
triangleCount += indexCount / 3;
vertexCount += m.vertexCount;
}
m_TriangleCount += triangleCount;
var frameData = new MeshFrameData()
{
gameObjectEntityId = go.GetEntityId(),
triangleCount = triangleCount,
vertexCount = vertexCount,
};
m_FrameDataList.Add(frameData);
meshData.Dispose();
}
}
public void Emit()
{
if (!UnityEngine.Profiling.Profiler.enabled)
return;
UnityEngine.Profiling.Profiler.EmitFrameMetaData(ProfilerMarkers.k_2DGraphicProfilerProjectId, m_Tag, m_FrameDataList.ToArray());
m_MeshCounter.Value = m_TriangleCount;
m_TriangleCount = 0;
m_FrameDataList.Clear();
}
}
}
#endif
@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: e1ff83c8166949f6b18db235eff60cf8
timeCreated: 1773121961
@@ -0,0 +1,31 @@
using System;
using System.Collections.Generic;
namespace UnityEngine.Rendering.Universal.U2D.Profiler
{
class MeshRenderFrameDataProfilerEmitter
{
List<EntityId> m_FrameDataList = new List<EntityId>();
int m_Tag;
public MeshRenderFrameDataProfilerEmitter(int tag)
{
m_Tag = tag;
}
public void Capture(EntityId entityId)
{
if (!UnityEngine.Profiling.Profiler.enabled)
return;
m_FrameDataList.Add(entityId);
}
public void Emit()
{
if (!UnityEngine.Profiling.Profiler.enabled)
return;
UnityEngine.Profiling.Profiler.EmitFrameMetaData(ProfilerMarkers.k_2DGraphicProfilerProjectId, m_Tag, m_FrameDataList.ToArray());
m_FrameDataList.Clear();
}
}
}
@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 5e9904b465dd472cb54afffbd556cb57
timeCreated: 1774614307
@@ -0,0 +1,99 @@
using System;
using Unity.Profiling;
namespace UnityEngine.Rendering.Universal.U2D.Profiler
{
static class ProfilerMarkers
{
public static readonly Guid k_2DGraphicProfilerProjectId = new Guid("a7f8c3d4-9e2b-4a1c-8f6d-3b9e7a5c2d1f");
// Light Pass Markers
public static readonly string s_LightPass = "Light2D Pass";
public static readonly string s_LightSRTPass = "Light2D SRT Pass";
public static readonly string s_LightVolumetricPass = "Light2D Volumetric Pass";
public static readonly string s_Light2DBatcher = "Light2D Batcher";
public static readonly ProfilingSampler s_ProfilingSampler = new ProfilingSampler(s_LightPass);
public static readonly ProfilingSampler s_ProfilingSampleSRT = new ProfilingSampler(s_LightSRTPass);
public static readonly ProfilingSampler s_ProfilingSamplerVolume = new ProfilingSampler(s_LightVolumetricPass);
public static readonly ProfilingSampler s_ProfilingDrawBatched = new ProfilingSampler(s_Light2DBatcher);
// Shadow Pass Markers
public static readonly string s_ShadowTexture = "Draw 2D Shadow Texture";
public static readonly ProfilingSampler s_ProfilingSamplerShadows = new ProfilingSampler(s_ShadowTexture);
// Render Pass Markers
public static readonly string s_SetGlobalProperties = "SetGlobalProperties";
public static readonly string s_RenderPass = "Renderer2D Pass";
public static readonly string s_SetLightBlendTexture = "SetLightBlendTextures";
public static readonly string s_UpscalePass = "Upscale2D Pass";
public static readonly string s_DrawUpscale = "Draw Upscale";
public static readonly string s_MormalPass = "Normal2D Pass";
public static readonly string s_ShadowPass = "Shadow2D UnsafePass";
public static readonly string s_ShadowVolumetricPass = "Shadow2D Volumetric UnsafePass";
public static readonly string s_CopyCameraSortingLayerPass = "CopyCameraSortingLayer Pass";
public static readonly string s_Copy = "Copy";
public static readonly ProfilingSampler s_ProfilingSamplerSetGlobalProperties = new ProfilingSampler(s_SetGlobalProperties);
public static readonly ProfilingSampler s_ProfilingSamplerRenderPass = new ProfilingSampler(s_RenderPass);
public static readonly ProfilingSampler s_ProfilingSamplerSetLightBlendTexture = new ProfilingSampler(s_SetLightBlendTexture);
public static readonly ProfilingSampler s_ProfilingSamplerUpscalePass = new ProfilingSampler(s_UpscalePass);
public static readonly ProfilingSampler s_ProfilingSamplerDrawUpscale = new ProfilingSampler(s_DrawUpscale);
public static readonly ProfilingSampler s_ProfilingSamplerNormalPass = new ProfilingSampler(s_MormalPass);
public static readonly ProfilingSampler s_ProfilingSamplerShadowPass = new ProfilingSampler(s_ShadowPass);
public static readonly ProfilingSampler s_ProfilingSamplerShadowVolumetricPass = new ProfilingSampler(s_ShadowVolumetricPass);
public static readonly ProfilingSampler s_ProfilingSamplerCopyCameraSortingLayerPass = new ProfilingSampler(s_CopyCameraSortingLayerPass);
public static readonly ProfilingSampler s_ProfilingSamplerCopy = new ProfilingSampler(s_Copy);
public const string k_U2DNormalMapProfilerCounterName = "Normal Textures";
public const string k_U2DLightProfilerCounterName = "Light Textures";
public const string k_U2DShadowProfilerCounterName = "Shadow Textures";
public const string k_U2DShadowCasterCounterName = "Shadow Casters Draw Calls";
public const string k_U2DShadowVerticesCounterName = "Shadow Triangles";
public const string k_U2DLightBatchCounterName = "Light Batches";
public const string k_U2DLightTriangleCounterName = "Light Triangles";
#if ENABLE_PROFILER && PROFILER_INSTALLED
public enum ProfilerFrameDataTag
{
ShadowFrameData = 0,
LightFrameData = 1,
LightRenderFrameData = 2,
ShadowRenderFrameData = 3
}
public static readonly ProfilerCounterValue<int> s_U2DNormalMapProfilerCounterValue =
new ProfilerCounterValue<int>(ProfilerCategory.U2D, k_U2DNormalMapProfilerCounterName, ProfilerMarkerDataUnit.Count,
ProfilerCounterOptions.FlushOnEndOfFrame | ProfilerCounterOptions.ResetToZeroOnFlush);
public static readonly ProfilerCounterValue<int> s_U2DLightProfilerCounterValue =
new ProfilerCounterValue<int>(ProfilerCategory.U2D, k_U2DLightProfilerCounterName, ProfilerMarkerDataUnit.Count,
ProfilerCounterOptions.FlushOnEndOfFrame | ProfilerCounterOptions.ResetToZeroOnFlush);
public static readonly ProfilerCounterValue<int> s_U2DShadowProfilerCounterValue =
new ProfilerCounterValue<int>(ProfilerCategory.U2D, k_U2DShadowProfilerCounterName, ProfilerMarkerDataUnit.Count,
ProfilerCounterOptions.FlushOnEndOfFrame | ProfilerCounterOptions.ResetToZeroOnFlush);
public static readonly ProfilerCounterValue<int> s_U2DShadowCasterCounterValue =
new ProfilerCounterValue<int>(ProfilerCategory.U2D, k_U2DShadowCasterCounterName, ProfilerMarkerDataUnit.Count,
ProfilerCounterOptions.FlushOnEndOfFrame | ProfilerCounterOptions.ResetToZeroOnFlush);
public static readonly ProfilerCounterValue<int> s_U2DShadowVerticesCounterValue =
new ProfilerCounterValue<int>(ProfilerCategory.U2D, k_U2DShadowVerticesCounterName, ProfilerMarkerDataUnit.Count,
ProfilerCounterOptions.FlushOnEndOfFrame | ProfilerCounterOptions.ResetToZeroOnFlush);
public static readonly ProfilerCounterValue<int> s_U2DLightBatchCounterValue =
new ProfilerCounterValue<int>(ProfilerCategory.U2D, k_U2DLightBatchCounterName, ProfilerMarkerDataUnit.Count,
ProfilerCounterOptions.FlushOnEndOfFrame | ProfilerCounterOptions.ResetToZeroOnFlush);
public static readonly ProfilerCounterValue<int> s_U2DLightTriangleCounterValue =
new ProfilerCounterValue<int>(ProfilerCategory.U2D, k_U2DLightTriangleCounterName, ProfilerMarkerDataUnit.Count,
ProfilerCounterOptions.FlushOnEndOfFrame | ProfilerCounterOptions.ResetToZeroOnFlush);
public static MeshFrameDataProfilerEmitter s_LightMeshFrameData = new ((int)ProfilerFrameDataTag.LightFrameData, s_U2DLightTriangleCounterValue);
public static MeshRenderFrameDataProfilerEmitter s_LightRenderFrameData = new ((int)ProfilerFrameDataTag.LightRenderFrameData);
public static MeshFrameDataProfilerEmitter s_ShadowMeshFrameData = new ((int)ProfilerFrameDataTag.ShadowFrameData, s_U2DShadowVerticesCounterValue);
public static MeshRenderFrameDataProfilerEmitter s_ShadowRenderFrameData = new ((int)ProfilerFrameDataTag.ShadowRenderFrameData);
#endif
}
}
@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 19dc6d7acb114482bdbc10c16b44cdc4
timeCreated: 1773110223
@@ -0,0 +1,36 @@
using System;
namespace UnityEngine.Rendering.Universal
{
/// <summary>
/// Base class for 2D shadow shape providers.
/// </summary>
[Serializable]
public abstract class Provider2D
{
/// <summary>
/// Returns the priority used to order this provider in the ShadowCaster2D Casting Option dropdown.
/// </summary>
/// <returns>The menu priority for this provider.</returns>
public virtual int MenuPriority() { return 0; }
/// <summary>
/// Indicates whether this provider reads data from a component on the GameObject.
/// </summary>
/// <returns>True if the provider uses component data; otherwise, false.</returns>
public virtual bool UsesComponentData() { return true; }
/// <summary>
/// Called when the provider is initialized.
/// </summary>
public virtual void OnAwake() { }
#if UNITY_EDITOR
public virtual void OnSelected() { }
public virtual void OnDrawGizmos(Transform transform) {}
#endif
internal abstract GUIContent Internal_ProviderName(string componentName);
internal virtual bool Internal_IsRequiredComponentData(Component sourceComponent) { return sourceComponent is Transform; }
}
}
@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: 445455d37e7bc0048801afd5048170e8
@@ -0,0 +1,174 @@
using System;
using System.Collections.Generic;
using UnityEngine.Scripting.APIUpdating;
using UnityEngine.Serialization;
#if UNITY_EDITOR
using UnityEditor;
using UnityEditor.ProjectWindowCallback;
#endif
namespace UnityEngine.Rendering.Universal
{
/// <summary>
/// Class <c>Renderer2DData</c> contains resources for a <c>Renderer2D</c>.
/// </summary>
[Serializable, ReloadGroup, ExcludeFromPreset]
[MovedFrom(true, "UnityEngine.Experimental.Rendering.Universal", "Unity.RenderPipelines.Universal.Runtime")]
[URPHelpURL("urp/2DRendererData-overview")]
public partial class Renderer2DData : ScriptableRendererData
{
internal enum Renderer2DDefaultMaterialType
{
Lit,
Unlit,
Custom
}
[SerializeField]
LayerMask m_LayerMask = -1;
[SerializeField]
TransparencySortMode m_TransparencySortMode = TransparencySortMode.Default;
[SerializeField]
Vector3 m_TransparencySortAxis = Vector3.up;
[SerializeField]
float m_HDREmulationScale = 1;
[SerializeField, Range(0.01f, 1.0f)]
float m_LightRenderTextureScale = 0.5f;
[SerializeField, FormerlySerializedAs("m_LightOperations")]
Light2DBlendStyle[] m_LightBlendStyles = null;
[SerializeField]
bool m_UseDepthStencilBuffer = true;
[SerializeField]
bool m_UseCameraSortingLayersTexture = false;
[SerializeField]
int m_CameraSortingLayersTextureBound = 0;
[SerializeField]
Downsampling m_CameraSortingLayerDownsamplingMethod = Downsampling.None;
[SerializeField]
uint m_MaxLightRenderTextureCount = 16;
[SerializeField]
uint m_MaxShadowRenderTextureCount = 1;
[SerializeField]
PostProcessData m_PostProcessData = null;
/// <summary>
/// HDR Emulation Scale allows platforms to use HDR lighting by compressing the number of expressible colors in exchange for extra intensity range.
/// Scale describes this extra intensity range. Increasing this value too high may cause undesirable banding to occur.
/// </summary>
public float hdrEmulationScale => m_HDREmulationScale;
internal float lightRenderTextureScale => m_LightRenderTextureScale;
/// <summary>
/// Returns a list Light2DBlendStyle
/// </summary>
public Light2DBlendStyle[] lightBlendStyles => m_LightBlendStyles;
internal bool useDepthStencilBuffer => m_UseDepthStencilBuffer;
internal PostProcessData postProcessData { get => m_PostProcessData; set { m_PostProcessData = value; } }
internal TransparencySortMode transparencySortMode => m_TransparencySortMode;
internal Vector3 transparencySortAxis => m_TransparencySortAxis;
internal uint lightRenderTextureMemoryBudget => m_MaxLightRenderTextureCount;
internal uint shadowRenderTextureMemoryBudget => m_MaxShadowRenderTextureCount;
internal bool useCameraSortingLayerTexture => m_UseCameraSortingLayersTexture;
internal int cameraSortingLayerTextureBound => m_CameraSortingLayersTextureBound;
internal Downsampling cameraSortingLayerDownsamplingMethod => m_CameraSortingLayerDownsamplingMethod;
internal LayerMask layerMask => m_LayerMask;
internal bool useRenderingLayers { get { return UniversalRenderPipeline.asset.useRenderingLayers; } }
internal RenderingLayerUtils.MaskSize renderingLayersMaskSize { get; set; }
/// <summary>
/// Creates the instance of the Renderer2D.
/// </summary>
/// <returns>The instance of Renderer2D</returns>
protected override ScriptableRenderer Create()
{
#if UNITY_EDITOR
if (!Application.isPlaying)
{
ReloadAllNullProperties();
}
#endif
UnityEngine.RenderAs2DUtil.InitializeCanRenderAs2D();
return new Renderer2D(this);
}
internal void Dispose()
{
UnityEngine.RenderAs2DUtil.DisposeCanRenderAs2D();
foreach(var mat in lightMaterials)
CoreUtils.Destroy(mat.Value);
lightMaterials.Clear();
CoreUtils.Destroy(spriteSelfShadowMaterial);
CoreUtils.Destroy(spriteUnshadowMaterial);
CoreUtils.Destroy(spriteSelfShadowMaterialSkinned);
CoreUtils.Destroy(spriteUnshadowMaterialSkinned);
CoreUtils.Destroy(geometrySelfShadowMaterial);
CoreUtils.Destroy(geometryUnshadowMaterial);
CoreUtils.Destroy(projectedShadowMaterial);
CoreUtils.Destroy(projectedUnshadowMaterial);
}
/// <summary>
/// OnEnable implementation.
/// </summary>
protected override void OnEnable()
{
base.OnEnable();
geometrySelfShadowMaterial = null;
geometryUnshadowMaterial = null;
spriteSelfShadowMaterial = null;
spriteUnshadowMaterial = null;
spriteSelfShadowMaterialSkinned = null;
spriteUnshadowMaterialSkinned = null;
projectedShadowMaterial = null;
projectedUnshadowMaterial = null;
}
// transient data
internal Dictionary<uint, Material> lightMaterials { get; } = new Dictionary<uint, Material>();
/// <summary>
/// Clears the cached light materials, forcing them to be recreated.
/// Called when shader variants or settings change.
/// </summary>
internal void ClearLightMaterialCache()
{
foreach (var mat in lightMaterials.Values)
{
if (mat != null)
CoreUtils.Destroy(mat);
}
lightMaterials.Clear();
}
internal Material spriteSelfShadowMaterial { get; set; }
internal Material spriteUnshadowMaterial { get; set; }
internal Material spriteSelfShadowMaterialSkinned { get; set; }
internal Material spriteUnshadowMaterialSkinned { get; set; }
internal Material geometrySelfShadowMaterial { get; set; }
internal Material geometryUnshadowMaterial { get; set; }
internal Material projectedShadowMaterial { get; set; }
internal Material projectedUnshadowMaterial { get; set; }
internal RTHandle normalsRenderTarget;
internal RTHandle cameraSortingLayerRenderTarget;
// this shouldn've been in RenderingData along with other cull results
internal ILight2DCullResult lightCullResult { get; set; }
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 11145981673336645838492a2d98e247
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,131 @@
namespace UnityEngine.Rendering.Universal
{
public partial class Renderer2DData
{
#if UNITY_EDITOR
[SerializeField]
Renderer2DDefaultMaterialType m_DefaultMaterialType = Renderer2DDefaultMaterialType.Lit;
[SerializeField, Reload("Runtime/Materials/Sprite-Lit-Default.mat")]
Material m_DefaultCustomMaterial = null;
internal override Shader GetDefaultShader()
{
if (!GraphicsSettings.TryGetRenderPipelineSettings<Renderer2DResources>(out var resources))
return null;
return resources.defaultLitMaterial.shader;
}
internal override Material GetDefaultMaterial(DefaultMaterialType materialType)
{
if (!GraphicsSettings.TryGetRenderPipelineSettings<Renderer2DResources>(out var resources))
return null;
switch (materialType)
{
case DefaultMaterialType.Default:
{
return m_DefaultMaterialType switch
{
Renderer2DDefaultMaterialType.Lit => resources.defaultMesh2DLitMaterial,
Renderer2DDefaultMaterialType.Unlit => resources.defaultMesh2DLitMaterial,
_ => m_DefaultCustomMaterial
};
}
case DefaultMaterialType.Sprite:
case DefaultMaterialType.Particle:
{
return m_DefaultMaterialType switch
{
Renderer2DDefaultMaterialType.Lit => resources.defaultLitMaterial,
Renderer2DDefaultMaterialType.Unlit => resources.defaultUnlitMaterial,
_ => m_DefaultCustomMaterial
};
}
case DefaultMaterialType.SpriteMask:
return resources.defaultMaskMaterial;
default:
return null;
}
}
private void InitializeSpriteEditorPrefs()
{
// Provide a list of suggested texture property names to Sprite Editor via EditorPrefs.
const string suggestedNamesKey = "SecondarySpriteTexturePropertyNames";
const string maskTex = "_MaskTex";
const string normalMap = "_NormalMap";
string suggestedNamesPrefs = UnityEditor.EditorPrefs.GetString(suggestedNamesKey);
if (string.IsNullOrEmpty(suggestedNamesPrefs))
UnityEditor.EditorPrefs.SetString(suggestedNamesKey, maskTex + "," + normalMap);
else
{
if (!suggestedNamesPrefs.Contains(maskTex))
suggestedNamesPrefs += ("," + maskTex);
if (!suggestedNamesPrefs.Contains(normalMap))
suggestedNamesPrefs += ("," + normalMap);
UnityEditor.EditorPrefs.SetString(suggestedNamesKey, suggestedNamesPrefs);
}
ReloadAllNullProperties();
}
private void ReloadAllNullProperties()
{
ResourceReloader.TryReloadAllNullIn(this, UniversalRenderPipelineAsset.packagePath);
}
void RebuildBlendStyles(bool force = false)
{
// Initialize Editor Prefs for Sprite Editor
InitializeSpriteEditorPrefs();
// Initialize Light Blend Styles
if (m_LightBlendStyles != null && !force)
{
for (int i = 0; i < m_LightBlendStyles.Length; ++i)
{
ref var blendStyle = ref m_LightBlendStyles[i];
// Custom blend mode (99) now falls back to Multiply.
if ((int) blendStyle.blendMode == 99)
blendStyle.blendMode = Light2DBlendStyle.BlendMode.Multiply;
}
return;
}
m_LightBlendStyles = new Light2DBlendStyle[4];
m_LightBlendStyles[0].name = "Multiply";
m_LightBlendStyles[0].blendMode = Light2DBlendStyle.BlendMode.Multiply;
m_LightBlendStyles[1].name = "Additive";
m_LightBlendStyles[1].blendMode = Light2DBlendStyle.BlendMode.Additive;
m_LightBlendStyles[2].name = "Multiply with Mask";
m_LightBlendStyles[2].blendMode = Light2DBlendStyle.BlendMode.Multiply;
m_LightBlendStyles[2].maskTextureChannel = Light2DBlendStyle.TextureChannel.R;
m_LightBlendStyles[3].name = "Additive with Mask";
m_LightBlendStyles[3].blendMode = Light2DBlendStyle.BlendMode.Additive;
m_LightBlendStyles[3].maskTextureChannel = Light2DBlendStyle.TextureChannel.R;
}
private void Awake()
{
RebuildBlendStyles();
}
void Reset()
{
RebuildBlendStyles(true);
}
#endif
}
}
@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: c64a6e813af443b4864441f640dd1c9f
timeCreated: 1596423828
@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 6410ec30b9d85cf40b53826342e69ac4
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,184 @@
using System;
namespace UnityEngine.Rendering.Universal
{
/// <summary>
/// Specifies the execution timing for a <c>ScriptableRenderPass2D</c> within the 2D renderer pipeline.
/// This enum allows you to control when a custom render pass is injected relative to key stages
/// of the 2D rendering process, such as normals, shadows, lights, sprites, and post-processing.
/// </summary>
public enum RenderPassEvent2D
{
/// <summary>
/// Executes a <c>ScriptableRenderPass2D</c> before rendering any other passes in the pipeline.
/// Camera matrices and stereo rendering are not set up at this point.
/// Use this to draw to custom input textures used later in the pipeline, for example, LUT textures.
/// </summary>
BeforeRendering = 0,
/// <summary>
/// Executes a <c>ScriptableRenderPass2D</c> before rendering normals.
/// You can target sorting layers when using this event.
/// </summary>
BeforeRenderingNormals = 100,
/// <summary>
/// Executes a <c>ScriptableRenderPass2D</c> after rendering normals.
/// You can target sorting layers when using this event.
/// </summary>
AfterRenderingNormals = 200,
/// <summary>
/// Executes a <c>ScriptableRenderPass2D</c> before rendering shadows.
/// You can target sorting layers when using this event.
/// </summary>
BeforeRenderingShadows = 300,
/// <summary>
/// Executes a <c>ScriptableRenderPass2D</c> after rendering shadows.
/// You can target sorting layers when using this event.
/// </summary>
AfterRenderingShadows = 400,
/// <summary>
/// Executes a <c>ScriptableRenderPass2D</c> before rendering lights.
/// You can target sorting layers when using this event.
/// </summary>
BeforeRenderingLights = 500,
/// <summary>
/// Executes a <c>ScriptableRenderPass2D</c> after rendering lights.
/// You can target sorting layers when using this event.
/// </summary>
AfterRenderingLights = 600,
/// <summary>
/// Executes a <c>ScriptableRenderPass2D</c> before rendering sprites.
/// You can target sorting layers when using this event.
/// </summary>
BeforeRenderingSprites = 700,
/// <summary>
/// Executes a <c>ScriptableRenderPass2D</c> after rendering sprites.
/// You can target sorting layers when using this event.
/// </summary>
AfterRenderingSprites = 800,
/// <summary>
/// Executes a <c>ScriptableRenderPass2D</c> before rendering post-processing.
/// </summary>
BeforeRenderingPostProcessing = 900,
/// <summary>
/// Executes a <c>ScriptableRenderPass2D</c> after rendering post-processing.
/// </summary>
AfterRenderingPostProcessing = 1000,
/// <summary>
/// Executes a <c>ScriptableRenderPass2D</c> after all rendering is complete.
/// </summary>
AfterRendering = 1100,
}
internal static class RenderPassEvents2DEnumValues
{
// We cache the values in this array at construction time to avoid runtime allocations, which we would cause if we accessed valuesInternal directly.
public static int[] values;
#if UNITY_EDITOR
[RuntimeInitializeOnLoadMethod()]
static void ResetStaticsOnLoad()
{
}
#endif
static RenderPassEvents2DEnumValues()
{
Array valuesInternal = Enum.GetValues(typeof(RenderPassEvent2D));
values = new int[valuesInternal.Length];
int index = 0;
foreach (int value in valuesInternal)
{
values[index] = value;
index++;
}
}
}
/// <summary>
/// <c>ScriptableRenderPass2D</c> implements a logical rendering pass with which you can extend the 2D renderer.
/// </summary>
public abstract class ScriptableRenderPass2D : ScriptableRenderPass
{
/// <summary>
/// The event that occurs when the render pass executes with the 2d renderer.
/// </summary>
public RenderPassEvent2D renderPassEvent2D { get; set; }
/// <summary>
/// The sorting layer that the render pass executes on the 2d renderer.
/// </summary>
public int renderPassSortingLayerID { get; set; }
static internal int GetRenderPassEventRange(RenderPassEvent2D renderPassEvent2D)
{
int numEvents = RenderPassEvents2DEnumValues.values.Length;
int currentIndex = 0;
// Find the index of the renderPassEvent in the values array.
for (int i = 0; i < numEvents; ++i)
{
if (RenderPassEvents2DEnumValues.values[currentIndex] == (int)renderPassEvent2D)
break;
currentIndex++;
}
if (currentIndex >= numEvents)
{
Debug.LogError("GetRenderPassEventRange: invalid renderPassEvent2D value cannot be found in the RenderPassEvent2D enumeration");
return 0;
}
if (currentIndex + 1 >= numEvents)
return 50; // If this was the enum's last event, add 50 as the range.
int nextValue = RenderPassEvents2DEnumValues.values[currentIndex + 1];
return nextValue - (int)renderPassEvent2D;
}
static internal bool IsSortingLayerEvent(RenderPassEvent2D renderPassEvent)
{
return renderPassEvent >= RenderPassEvent2D.BeforeRenderingNormals && renderPassEvent <= RenderPassEvent2D.AfterRenderingSprites;
}
}
static internal class ScriptableRenderPass2DExtension
{
static internal void GetInjectionPoint2D(this ScriptableRenderPass renderPass, out RenderPassEvent2D rpEvent, out int rpLayer)
{
ScriptableRenderPass2D renderPass2D = renderPass as ScriptableRenderPass2D;
if (renderPass2D == null)
{
rpLayer = int.MinValue;
if (renderPass.renderPassEvent <= RenderPassEvent.BeforeRenderingTransparents)
rpEvent = RenderPassEvent2D.BeforeRendering;
else if (renderPass.renderPassEvent <= RenderPassEvent.BeforeRenderingPostProcessing)
rpEvent = RenderPassEvent2D.BeforeRenderingPostProcessing;
else if (renderPass.renderPassEvent <= RenderPassEvent.AfterRenderingPostProcessing)
rpEvent = RenderPassEvent2D.AfterRenderingPostProcessing;
else
rpEvent = RenderPassEvent2D.AfterRendering;
}
else
{
rpEvent = renderPass2D.renderPassEvent2D;
rpLayer = renderPass2D.renderPassSortingLayerID;
}
}
}
}
@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: 43645433359a9a8488056103f59e201d
@@ -0,0 +1,24 @@
namespace UnityEngine.Rendering.Universal
{
/// <summary>
/// You can add a <c>ScriptableRendererFeature2D</c> to the <c>ScriptableRenderer</c>. Use this scriptable renderer feature to inject render passes into the 2D renderer.
/// </summary>
/// <seealso cref="ScriptableRenderer"/>
/// <seealso cref="ScriptableRenderPass2D"/>
[ExcludeFromPreset]
[SupportedOnRenderer(typeof(Renderer2DData))]
public abstract partial class ScriptableRendererFeature2D : ScriptableRendererFeature
{
/// <summary>
/// Specifies at which injection point the pass will be rendered.
/// </summary>
[HideInInspector]
public RenderPassEvent2D injectionPoint2D = RenderPassEvent2D.BeforeRendering;
/// <summary>
/// Specifies the sorting layer in which Unity injects the pass.
/// </summary>
[HideInInspector]
public int sortingLayerID = 0;
}
}
@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: 47ca7de5076d2e34fbc2e25f05744f10
@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 1db62f4f87b95214e98261b68289adba
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,82 @@
using System;
using UnityEngine.Rendering.RenderGraphModule;
using CommonResourceData = UnityEngine.Rendering.Universal.UniversalResourceData;
namespace UnityEngine.Rendering.Universal
{
internal class CameraRenderingLayerMaskPass : ScriptableRenderPass
{
static readonly string k_RenderingLayerMaskPass = "RenderingLayerMask Pass";
internal static readonly string k_RenderingLayersTextureName = "_CameraRenderingLayersTexture";
private static readonly ProfilingSampler m_ProfilingSampler = new ProfilingSampler(k_RenderingLayerMaskPass);
private static readonly int k_RenderingLayerMaskID = Shader.PropertyToID(k_RenderingLayersTextureName);
Material m_Material = null;
internal CameraRenderingLayerMaskPass()
{
if (GraphicsSettings.TryGetRenderPipelineSettings<Renderer2DResources>(out var resources))
{
m_Material = CoreUtils.CreateEngineMaterial(resources.renderingLayerMaskShader);
}
}
internal void Dispose()
{
CoreUtils.Destroy(m_Material);
}
private class PassData
{
internal RendererListHandle rendererList;
}
private static void Execute(RasterCommandBuffer cmd, PassData passData)
{
cmd.DrawRendererList(passData.rendererList);
}
internal void Render(RenderGraph graph, ContextContainer frameData)
{
Renderer2DData rendererData = frameData.Get<Universal2DRenderingData>().renderingData;
if (!rendererData.useRenderingLayers || m_Material == null)
return;
Universal2DResourceData universal2DResourceData = frameData.Get<Universal2DResourceData>();
CommonResourceData commonResourceData = frameData.Get<CommonResourceData>();
UniversalRenderingData renderingData = frameData.Get<UniversalRenderingData>();
UniversalCameraData cameraData = frameData.Get<UniversalCameraData>();
UniversalLightData lightData = frameData.Get<UniversalLightData>();
using (var builder = graph.AddRasterRenderPass<PassData>(k_RenderingLayerMaskPass, out var passData, LayerDebug.GetProfilingSampler(passName, m_ProfilingSampler)))
{
// Render all
var filterSettings = new FilteringSettings(RenderQueueRange.all);
filterSettings.layerMask = rendererData.layerMask;
var drawSettings = CreateDrawingSettings(DrawRenderer2DPass.k_ShaderTags, renderingData, cameraData, lightData, SortingCriteria.CommonTransparent);
drawSettings.overrideMaterial = m_Material; // render with override RenderingLayerMask.shader
var sortSettings = drawSettings.sortingSettings;
RendererLighting.GetTransparencySortingMode(rendererData, cameraData.camera, ref sortSettings);
drawSettings.sortingSettings = sortSettings;
builder.AllowPassCulling(false);
builder.SetRenderAttachment(universal2DResourceData.renderingLayersTexture, 0);
var param = new RendererListParams(renderingData.cullResults, drawSettings, filterSettings);
passData.rendererList = graph.CreateRendererList(param);
builder.UseRendererList(passData.rendererList);
builder.SetGlobalTextureAfterPass(universal2DResourceData.renderingLayersTexture, k_RenderingLayerMaskID);
builder.SetRenderFunc(static (PassData data, RasterGraphContext context) =>
{
Execute(context.cmd, data);
});
}
}
}
}
@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: 6687e5bb66c445b4ab45af224ef4a18c
@@ -0,0 +1,72 @@
using UnityEngine.Experimental.Rendering;
using UnityEngine.Rendering.RenderGraphModule;
using UnityEngine.Rendering.Universal.U2D.Profiler;
namespace UnityEngine.Rendering.Universal
{
internal class CopyCameraSortingLayerPass : ScriptableRenderPass
{
internal static readonly string k_CameraSortingLayerTexture = "_CameraSortingLayerTexture";
internal static readonly int k_CameraSortingLayerTextureId = Shader.PropertyToID(k_CameraSortingLayerTexture);
Material m_BlitMaterial;
public CopyCameraSortingLayerPass(Material blitMaterial)
{
m_BlitMaterial = blitMaterial;
}
public static void ConfigureDescriptor(Downsampling downsamplingMethod, ref RenderTextureDescriptor descriptor, out FilterMode filterMode)
{
descriptor.msaaSamples = 1;
descriptor.depthStencilFormat = GraphicsFormat.None;
if (downsamplingMethod == Downsampling._2xBilinear)
{
descriptor.width /= 2;
descriptor.height /= 2;
}
else if (downsamplingMethod == Downsampling._4xBox || downsamplingMethod == Downsampling._4xBilinear)
{
descriptor.width /= 4;
descriptor.height /= 4;
}
filterMode = downsamplingMethod == Downsampling.None || downsamplingMethod == Downsampling._4xBox ? FilterMode.Point : FilterMode.Bilinear;
}
private static void Execute(RasterCommandBuffer cmd, RTHandle source, Material blitMaterial)
{
using (new ProfilingScope(cmd, ProfilerMarkers.s_ProfilingSamplerCopy))
{
Vector2 viewportScale = source.useScaling ? new Vector2(source.rtHandleProperties.rtHandleScale.x, source.rtHandleProperties.rtHandleScale.y) : Vector2.one;
Blitter.BlitTexture(cmd, source, viewportScale, blitMaterial, source.rt.filterMode == FilterMode.Bilinear ? 1 : 0);
}
}
class PassData
{
internal TextureHandle source;
internal Material blitMaterial;
}
public void Render(RenderGraph graph, ContextContainer frameData)
{
UniversalResourceData commonResourceData = frameData.Get<UniversalResourceData>();
Universal2DResourceData universal2DResourceData = frameData.Get<Universal2DResourceData>();
using (var builder = graph.AddRasterRenderPass<PassData>(ProfilerMarkers.s_CopyCameraSortingLayerPass, out var passData, ProfilerMarkers.s_ProfilingSamplerCopyCameraSortingLayerPass))
{
passData.source = commonResourceData.activeColorTexture;
passData.blitMaterial = m_BlitMaterial;
builder.SetRenderAttachment(universal2DResourceData.cameraSortingLayerTexture, 0);
builder.UseTexture(passData.source);
builder.AllowPassCulling(false);
builder.SetRenderFunc(static (PassData data, RasterGraphContext context) =>
{
Execute(context.cmd, data.source, data.blitMaterial);
});
}
}
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 1e2567cd71037a647ad53fc83080600b
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,270 @@
using UnityEngine.Rendering.RenderGraphModule;
using UnityEngine.Rendering.Universal.U2D.Profiler;
using CommonResourceData = UnityEngine.Rendering.Universal.UniversalResourceData;
namespace UnityEngine.Rendering.Universal
{
internal class DrawLight2DPass : ScriptableRenderPass
{
internal static readonly int k_InverseHDREmulationScaleID = Shader.PropertyToID("_InverseHDREmulationScale");
internal static readonly string k_NormalMapID = "_NormalMap";
internal static readonly string k_ShadowMapID = "_ShadowTex";
TextureHandle[] intermediateTexture = new TextureHandle[1];
internal static MaterialPropertyBlock s_PropertyBlock = new MaterialPropertyBlock();
#if UNITY_EDITOR
[RuntimeInitializeOnLoadMethod]
static void ResetStaticsOnLoad()
{
s_PropertyBlock = new MaterialPropertyBlock();
}
#endif
internal void Setup(RenderGraph renderGraph, ContextContainer frameData)
{
var renderingData = frameData.Get<Universal2DRenderingData>().renderingData;
foreach (var light in renderingData.lightCullResult.visibleLights)
{
if (light.useCookieSprite && light.m_CookieSpriteTexture != null)
light.m_CookieSpriteTextureHandle = renderGraph.ImportTexture(light.m_CookieSpriteTexture);
#if ENABLE_PROFILER && PROFILER_INSTALLED
if (Renderer2D.canProfilerCapture)
ProfilerMarkers.s_LightMeshFrameData.Capture(light.gameObject, light.lightMesh);
#endif
}
}
static bool TryGetShadowIndex(ref LayerBatch layerBatch, int lightIndex, out int shadowIndex)
{
shadowIndex = 0;
for (int i = 0; i < layerBatch.shadowIndices.Count; ++i)
{
if (layerBatch.shadowIndices[i] == lightIndex)
{
shadowIndex = i;
return true;
}
}
return false;
}
private static void Execute(RasterCommandBuffer cmd, PassData passData, LayerBatch layerBatch, int lightTextureIndex)
{
cmd.SetGlobalFloat(k_InverseHDREmulationScaleID, 1.0f / passData.rendererData.hdrEmulationScale);
var blendStyleIndex = layerBatch.activeBlendStylesIndices[lightTextureIndex];
var blendOpName = passData.rendererData.lightBlendStyles[blendStyleIndex].name;
cmd.BeginSample(blendOpName);
var indicesIndex = Renderer2D.supportsMRT ? lightTextureIndex : 0;
if (!passData.isVolumetric)
RendererLighting.EnableBlendStyle(cmd, indicesIndex, true);
// Enable Rendering Layers
if (passData.useRenderingLayers)
cmd.EnableKeyword(ShaderGlobalKeywords.LightLayers);
var lights = passData.layerBatch.lights;
for (int j = 0; j < lights.Count; ++j)
{
var light = lights[j];
// Check if light is valid
if (light == null ||
light.lightType == Light2D.LightType.Global ||
light.blendStyleIndex != blendStyleIndex)
continue;
// Check if light is volumetric
if (passData.isVolumetric &&
(light.volumeIntensity <= 0.0f ||
!light.volumetricEnabled ||
layerBatch.endLayerValue != light.GetTopMostLitLayer()))
continue;
var useShadows = (!passData.isVolumetric && passData.layerBatch.lightStats.useShadows) || (passData.isVolumetric && passData.layerBatch.lightStats.useVolumetricShadowLights);
useShadows &= layerBatch.shadowIndices.Contains(j);
var lightMaterial = passData.rendererData.GetLightMaterial(light, passData.isVolumetric, useShadows);
var lightMesh = light.lightMesh;
// For Batching.
var index = light.batchSlotIndex;
var slotIndex = RendererLighting.lightBatch.SlotIndex(index);
bool canBatch = RendererLighting.lightBatch.CanBatch(light, lightMaterial, index, out int lightHash);
bool breakBatch = !canBatch;
if (breakBatch && LightBatch.isBatchingSupported)
RendererLighting.lightBatch.Flush(cmd);
if (passData.layerBatch.lightStats.useNormalMap)
s_PropertyBlock.SetTexture(k_NormalMapID, passData.normalMap);
if (useShadows && TryGetShadowIndex(ref layerBatch, j, out var shadowIndex))
s_PropertyBlock.SetTexture(k_ShadowMapID, passData.shadowTextures[shadowIndex]);
if (!passData.isVolumetric || (passData.isVolumetric && light.volumetricEnabled))
RendererLighting.SetCookieShaderProperties(light, s_PropertyBlock);
// Set shader global properties
RendererLighting.SetPerLightShaderGlobals(cmd, light, slotIndex, passData.isVolumetric, useShadows, LightBatch.isBatchingSupported);
if (light.normalMapQuality != Light2D.NormalMapQuality.Disabled || light.lightType == Light2D.LightType.Point)
RendererLighting.SetPerPointLightShaderGlobals(cmd, light, slotIndex, LightBatch.isBatchingSupported);
if (LightBatch.isBatchingSupported)
{
RendererLighting.lightBatch.AddBatch(light, lightMaterial, light.GetMatrix(), lightMesh, 0, lightHash, index);
RendererLighting.lightBatch.Flush(cmd);
}
else
{
#if ENABLE_PROFILER && PROFILER_INSTALLED
if (Renderer2D.canProfilerCapture)
ProfilerMarkers.s_U2DLightBatchCounterValue.Value++;
#endif
cmd.DrawMesh(lightMesh, light.GetMatrix(), lightMaterial, 0, 0, s_PropertyBlock);
}
#if ENABLE_PROFILER && PROFILER_INSTALLED
if (Renderer2D.canProfilerCapture)
ProfilerMarkers.s_LightRenderFrameData.Capture(light.gameObject.GetEntityId());
#endif
}
RendererLighting.EnableBlendStyle(cmd, indicesIndex, false);
// Disable Rendering Layers
if (passData.useRenderingLayers)
cmd.DisableKeyword(ShaderGlobalKeywords.LightLayers);
cmd.EndSample(blendOpName);
}
internal class PassData
{
internal LayerBatch layerBatch;
internal Renderer2DData rendererData;
internal bool isVolumetric;
internal TextureHandle normalMap;
internal TextureHandle[] shadowTextures;
internal int lightTextureIndex;
internal bool useRenderingLayers;
internal RenderingLayerUtils.MaskSize maskSize;
}
void InitializeRenderPass(IRasterRenderGraphBuilder builder, ContextContainer frameData, PassData passData, int batchIndex, bool isVolumetric = false)
{
Universal2DResourceData universal2DResourceData = frameData.Get<Universal2DResourceData>();
CommonResourceData commonResourceData = frameData.Get<CommonResourceData>();
Renderer2DData rendererData = frameData.Get<Universal2DRenderingData>().renderingData;
var layerBatch = frameData.Get<Universal2DRenderingData>().layerBatches[batchIndex];
intermediateTexture[0] = commonResourceData.activeColorTexture;
if (layerBatch.lightStats.useNormalMap)
builder.UseTexture(universal2DResourceData.normalsTexture[batchIndex]);
if (layerBatch.lightStats.useShadows)
{
passData.shadowTextures = universal2DResourceData.shadowTextures[batchIndex];
for (var i = 0; i < passData.shadowTextures.Length; i++)
builder.UseTexture(passData.shadowTextures[i]);
}
foreach (var light in layerBatch.lights)
{
if (light == null || !light.m_CookieSpriteTextureHandle.IsValid())
continue;
if (!isVolumetric || (isVolumetric && light.volumetricEnabled))
builder.UseTexture(light.m_CookieSpriteTextureHandle);
}
if (rendererData.useRenderingLayers)
builder.UseTexture(universal2DResourceData.renderingLayersTexture);
passData.useRenderingLayers = rendererData.useRenderingLayers;
passData.maskSize = rendererData.renderingLayersMaskSize;
passData.layerBatch = layerBatch;
passData.rendererData = rendererData;
passData.isVolumetric = isVolumetric;
passData.normalMap = layerBatch.lightStats.useNormalMap ? universal2DResourceData.normalsTexture[batchIndex] : TextureHandle.nullHandle;
builder.AllowGlobalStateModification(true);
}
internal void Render(RenderGraph graph, ContextContainer frameData, int batchIndex, bool isVolumetric = false)
{
Universal2DResourceData universal2DResourceData = frameData.Get<Universal2DResourceData>();
UniversalCameraData cameraData = frameData.Get<UniversalCameraData>();
var layerBatch = frameData.Get<Universal2DRenderingData>().layerBatches[batchIndex];
// Check for lighting in scene/prefab/preview camera
var isLightingActive = Renderer2D.IsSceneViewOrPreviewLightingActive(cameraData);
if (!layerBatch.lightStats.useLights ||
isVolumetric && !layerBatch.lightStats.useVolumetricLights ||
!isLightingActive)
return;
// Render single RTs by for apis that don't support MRTs
if (!isVolumetric && !Renderer2D.supportsMRT)
{
var passName = ProfilerMarkers.s_LightSRTPass;
LayerDebug.FormatPassName(layerBatch, ref passName);
for (var i = 0; i < layerBatch.activeBlendStylesIndices.Length; ++i)
{
using (var builder = graph.AddRasterRenderPass<PassData>(passName, out var passData, LayerDebug.GetProfilingSampler(passName, ProfilerMarkers.s_ProfilingSampleSRT)))
{
InitializeRenderPass(builder, frameData, passData, batchIndex, isVolumetric);
var lightTextures = universal2DResourceData.lightTextures[batchIndex];
builder.SetRenderAttachment(lightTextures[i], 0);
passData.lightTextureIndex = i;
builder.SetRenderFunc(static (PassData data, RasterGraphContext context) =>
{
RenderingLayerUtils.SetupProperties(context.cmd, data.maskSize);
Execute(context.cmd, data, data.layerBatch, data.lightTextureIndex);
});
}
}
}
else
{
var passName = !isVolumetric ? ProfilerMarkers.s_LightPass : ProfilerMarkers.s_LightVolumetricPass;
var profilingSampler = !isVolumetric ? ProfilerMarkers.s_ProfilingSampler : ProfilerMarkers.s_ProfilingSamplerVolume;
LayerDebug.FormatPassName(layerBatch, ref passName);
// Default Raster Pass with MRTs
using (var builder = graph.AddRasterRenderPass<PassData>(passName, out var passData, LayerDebug.GetProfilingSampler(passName, profilingSampler)))
{
InitializeRenderPass(builder, frameData, passData, batchIndex, isVolumetric);
var lightTextures = !isVolumetric ? universal2DResourceData.lightTextures[batchIndex] : intermediateTexture;
for (var i = 0; i < lightTextures.Length; i++)
builder.SetRenderAttachment(lightTextures[i], i);
builder.SetRenderFunc(static (PassData data, RasterGraphContext context) =>
{
RenderingLayerUtils.SetupProperties(context.cmd, data.maskSize);
for (var i = 0; i < data.layerBatch.activeBlendStylesIndices.Length; ++i)
Execute(context.cmd, data, data.layerBatch, i);
});
}
}
}
}
}
@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 718db304c67a4e7692ab540c060259b2
timeCreated: 1652943248
@@ -0,0 +1,70 @@
using System;
using UnityEngine.Rendering.RenderGraphModule;
using UnityEngine.Rendering.Universal.U2D.Profiler;
using CommonResourceData = UnityEngine.Rendering.Universal.UniversalResourceData;
namespace UnityEngine.Rendering.Universal
{
internal class DrawNormal2DPass : ScriptableRenderPass
{
private static readonly ShaderTagId k_NormalsRenderingPassName = new ShaderTagId("NormalsRendering");
private class PassData
{
internal RendererListHandle rendererList;
}
private static void Execute(RasterCommandBuffer cmd, PassData passData)
{
cmd.DrawRendererList(passData.rendererList);
}
public void Render(RenderGraph graph, ContextContainer frameData, int batchIndex)
{
Universal2DResourceData universal2DResourceData = frameData.Get<Universal2DResourceData>();
CommonResourceData commonResourceData = frameData.Get<CommonResourceData>();
Renderer2DData rendererData = frameData.Get<Universal2DRenderingData>().renderingData;
var layerBatch = frameData.Get<Universal2DRenderingData>().layerBatches[batchIndex];
if (!layerBatch.useNormals)
return;
UniversalRenderingData renderingData = frameData.Get<UniversalRenderingData>();
UniversalCameraData cameraData = frameData.Get<UniversalCameraData>();
UniversalLightData lightData = frameData.Get<UniversalLightData>();
var passName = ProfilerMarkers.s_MormalPass;
LayerDebug.FormatPassName(layerBatch, ref passName);
using (var builder = graph.AddRasterRenderPass<PassData>(passName, out var passData, LayerDebug.GetProfilingSampler(passName, ProfilerMarkers.s_ProfilingSamplerNormalPass)))
{
LayerUtility.GetFilterSettings(rendererData, layerBatch, out var filterSettings);
var drawSettings = CreateDrawingSettings(k_NormalsRenderingPassName, renderingData, cameraData, lightData, SortingCriteria.CommonTransparent);
var sortSettings = drawSettings.sortingSettings;
RendererLighting.GetTransparencySortingMode(rendererData, cameraData.camera, ref sortSettings);
drawSettings.sortingSettings = sortSettings;
builder.AllowPassCulling(false);
builder.SetRenderAttachment(universal2DResourceData.normalsTexture[batchIndex], 0);
// Depth needed for sprite mask stencil or z test for 3d meshes
if (commonResourceData.activeDepthTexture.IsValid())
{
var depth = universal2DResourceData.normalsDepth.IsValid() ? universal2DResourceData.normalsDepth : commonResourceData.activeDepthTexture;
builder.SetRenderAttachmentDepth(depth);
}
var param = new RendererListParams(renderingData.cullResults, drawSettings, filterSettings);
passData.rendererList = graph.CreateRendererList(param);
builder.UseRendererList(passData.rendererList);
builder.SetRenderFunc(static (PassData data, RasterGraphContext context) =>
{
Execute(context.cmd, data);
});
}
}
}
}
@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: b256dac03c4645f593e9cb8ecbc51419
timeCreated: 1652930589
@@ -0,0 +1,209 @@
using System.Collections.Generic;
using UnityEngine.Rendering.RenderGraphModule;
using UnityEngine.Rendering.Universal.U2D.Profiler;
using CommonResourceData = UnityEngine.Rendering.Universal.UniversalResourceData;
namespace UnityEngine.Rendering.Universal
{
internal class DrawRenderer2DPass : ScriptableRenderPass
{
private static readonly ShaderTagId k_CombinedRenderingPassName = new ShaderTagId("Universal2D");
private static readonly ShaderTagId k_LegacyPassName = new ShaderTagId("SRPDefaultUnlit");
internal static readonly List<ShaderTagId> k_ShaderTags =
new List<ShaderTagId>() {k_LegacyPassName, k_CombinedRenderingPassName};
private static readonly int k_HDREmulationScaleID = Shader.PropertyToID("_HDREmulationScale");
private static readonly int k_RendererColorID = Shader.PropertyToID("_RendererColor");
private static void Execute(RasterGraphContext context, PassData passData)
{
var cmd = context.cmd;
var blendStylesCount = passData.blendStyleIndices.Length;
cmd.SetGlobalFloat(k_HDREmulationScaleID, passData.hdrEmulationScale);
cmd.SetGlobalColor(k_RendererColorID, Color.white);
RendererLighting.SetLightShaderGlobals(cmd, passData.lightBlendStyles, passData.blendStyleIndices);
#if UNITY_EDITOR
if (passData.isLightingActive)
#endif
{
if (passData.layerUseLights)
{
for (var i = 0; i < blendStylesCount; i++)
{
var blendStyleIndex = passData.blendStyleIndices[i];
RendererLighting.EnableBlendStyle(cmd, blendStyleIndex, true);
}
}
else if (passData.isSceneLit)
{
RendererLighting.EnableBlendStyle(cmd, 0, true);
}
}
if (passData.activeDebugHandler)
{
passData.debugRendererLists.DrawWithRendererList(cmd);
}
else
{
// Draw all renderers in layer batch
cmd.DrawRendererList(passData.rendererList);
}
RendererLighting.DisableAllKeywords(cmd);
}
class SetGlobalPassData
{
internal TextureHandle[] lightTextures;
}
class PassData
{
internal Light2DBlendStyle[] lightBlendStyles;
internal int[] blendStyleIndices;
internal float hdrEmulationScale;
internal bool isSceneLit;
internal bool layerUseLights;
internal TextureHandle[] lightTextures;
internal RendererListHandle rendererList;
internal DebugRendererLists debugRendererLists;
internal bool activeDebugHandler;
#if UNITY_EDITOR
internal bool isLightingActive; // Required for prefab view and preview camera
#endif
}
public void Render(RenderGraph graph, ContextContainer frameData, int batchIndex, ref FilteringSettings filterSettings)
{
UniversalRenderingData renderingData = frameData.Get<UniversalRenderingData>();
UniversalCameraData cameraData = frameData.Get<UniversalCameraData>();
UniversalLightData lightData = frameData.Get<UniversalLightData>();
Universal2DResourceData universal2DResourceData = frameData.Get<Universal2DResourceData>();
CommonResourceData commonResourceData = frameData.Get<CommonResourceData>();
Universal2DRenderingData rendering2DData = frameData.Get<Universal2DRenderingData>();
Renderer2DData rendererData = rendering2DData.renderingData;
var layerBatch = rendering2DData.layerBatches[batchIndex];
// Check for lighting in scene/prefab/preview camera
var isLightingActive = Renderer2D.IsSceneViewOrPreviewLightingActive(cameraData);
// Preset global light textures for first batch
if (batchIndex == 0)
{
using (var builder = graph.AddRasterRenderPass<SetGlobalPassData>(ProfilerMarkers.s_SetLightBlendTexture, out var passData, ProfilerMarkers.s_ProfilingSamplerSetLightBlendTexture))
{
if (layerBatch.lightStats.useLights && isLightingActive)
{
passData.lightTextures = universal2DResourceData.lightTextures[batchIndex];
for (var i = 0; i < passData.lightTextures.Length; i++)
builder.UseTexture(passData.lightTextures[i]);
}
SetGlobalLightTextures(graph, builder, frameData, batchIndex, isLightingActive);
builder.AllowGlobalStateModification(true);
builder.SetRenderFunc(static (SetGlobalPassData data, RasterGraphContext context) =>
{
});
}
}
// Renderer Pass
var passName = ProfilerMarkers.s_RenderPass;
LayerDebug.FormatPassName(layerBatch, ref passName);
using (var builder = graph.AddRasterRenderPass<PassData>(passName, out var passData, LayerDebug.GetProfilingSampler(passName, ProfilerMarkers.s_ProfilingSamplerRenderPass)))
{
passData.lightBlendStyles = rendererData.lightBlendStyles;
passData.blendStyleIndices = layerBatch.activeBlendStylesIndices;
passData.hdrEmulationScale = rendererData.hdrEmulationScale;
passData.isSceneLit = rendererData.lightCullResult.IsSceneLit();
passData.layerUseLights = layerBatch.lightStats.useLights;
#if UNITY_EDITOR
passData.isLightingActive = isLightingActive;
#endif
var drawSettings = CreateDrawingSettings(k_ShaderTags, renderingData, cameraData, lightData, SortingCriteria.CommonTransparent);
var sortSettings = drawSettings.sortingSettings;
RendererLighting.GetTransparencySortingMode(rendererData, cameraData.camera, ref sortSettings);
drawSettings.sortingSettings = sortSettings;
var activeDebugHandler = GetActiveDebugHandler(cameraData);
passData.activeDebugHandler = activeDebugHandler != null;
if (activeDebugHandler != null)
{
var renderStateBlock = new RenderStateBlock(RenderStateMask.Nothing);
passData.debugRendererLists = activeDebugHandler.CreateRendererListsWithDebugRenderState(graph,
ref renderingData.cullResults, ref drawSettings, ref filterSettings, ref renderStateBlock);
passData.debugRendererLists.PrepareRendererListForRasterPass(builder);
}
else
{
var param = new RendererListParams(renderingData.cullResults, drawSettings, filterSettings);
passData.rendererList = graph.CreateRendererList(param);
builder.UseRendererList(passData.rendererList);
}
if (passData.layerUseLights && isLightingActive)
{
passData.lightTextures = universal2DResourceData.lightTextures[batchIndex];
for (var i = 0; i < passData.lightTextures.Length; i++)
builder.UseTexture(passData.lightTextures[i]);
}
if (rendererData.useCameraSortingLayerTexture)
builder.UseTexture(universal2DResourceData.cameraSortingLayerTexture);
// Set color and depth attachments
builder.SetRenderAttachment(commonResourceData.activeColorTexture, 0);
if (commonResourceData.activeDepthTexture.IsValid())
builder.SetRenderAttachmentDepth(commonResourceData.activeDepthTexture);
builder.AllowGlobalStateModification(true);
// Post set global light textures for next renderer pass
var nextBatch = batchIndex + 1;
if (nextBatch < universal2DResourceData.lightTextures.Length)
SetGlobalLightTextures(graph, builder, frameData, nextBatch, isLightingActive);
builder.SetRenderFunc(static (PassData data, RasterGraphContext context) =>
{
Execute(context, data);
});
}
}
void SetGlobalLightTextures(RenderGraph graph, IRasterRenderGraphBuilder builder, ContextContainer frameData, int batchIndex, bool isLightingActive)
{
UniversalCameraData cameraData = frameData.Get<UniversalCameraData>();
Renderer2DData rendererData = frameData.Get<Universal2DRenderingData>().renderingData;
var layerBatch = frameData.Get<Universal2DRenderingData>().layerBatches[batchIndex];
var lightTextures = frameData.Get<Universal2DResourceData>().lightTextures[batchIndex];
if (isLightingActive)
{
if (layerBatch.lightStats.useLights)
{
for (var i = 0; i < lightTextures.Length; i++)
{
var blendStyleIndex = layerBatch.activeBlendStylesIndices[i];
builder.SetGlobalTextureAfterPass(lightTextures[i], Shader.PropertyToID(RendererLighting.k_ShapeLightTextureIDs[blendStyleIndex]));
}
}
else if (rendererData.lightCullResult.IsSceneLit())
{
for (var i = 0; i < RendererLighting.k_ShapeLightTextureIDs.Length; i++)
builder.SetGlobalTextureAfterPass(graph.defaultResources.blackTexture, Shader.PropertyToID(RendererLighting.k_ShapeLightTextureIDs[i]));
}
}
}
}
}
@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: e0f10937bae94291b66fd98a6d300c08
timeCreated: 1653032228
@@ -0,0 +1,83 @@
using System;
using System.Collections.Generic;
using UnityEngine.Experimental.Rendering;
using UnityEngine.Rendering.RenderGraphModule;
using UnityEngine.Rendering.Universal.U2D.Profiler;
using CommonResourceData = UnityEngine.Rendering.Universal.UniversalResourceData;
namespace UnityEngine.Rendering.Universal
{
internal class DrawShadow2DPass : ScriptableRenderPass
{
private static void ExecuteShadowPass(UnsafeCommandBuffer cmd, PassData passData, Light2D light, int batchIndex)
{
cmd.SetRenderTarget(passData.shadowTextures[batchIndex], passData.shadowDepth);
// Reusing the depth/stencil so we have to clear it
cmd.ClearRenderTarget(RTClearFlags.All, Color.clear, 1, 0);
var projectedShadowMaterial = passData.rendererData.GetProjectedShadowMaterial();
var projectedUnshadowMaterial = passData.rendererData.GetProjectedUnshadowMaterial();
ShadowRendering.PrerenderShadows(cmd, passData.rendererData, ref passData.layerBatch, light, 0, light.shadowIntensity);
}
internal class PassData
{
internal LayerBatch layerBatch;
internal Renderer2DData rendererData;
internal TextureHandle[] shadowTextures;
internal TextureHandle shadowDepth;
internal bool isVolumetric;
}
public void Render(RenderGraph graph, ContextContainer frameData, int batchIndex, bool isVolumetric = false)
{
Universal2DResourceData universal2DResourceData = frameData.Get<Universal2DResourceData>();
CommonResourceData commonResourceData = frameData.Get<CommonResourceData>();
Renderer2DData rendererData = frameData.Get<Universal2DRenderingData>().renderingData;
var layerBatch = frameData.Get<Universal2DRenderingData>().layerBatches[batchIndex];
if (!layerBatch.lightStats.useShadows ||
isVolumetric && !layerBatch.lightStats.useVolumetricShadowLights)
return;
var passName = !isVolumetric ? ProfilerMarkers.s_ShadowPass : ProfilerMarkers.s_ShadowVolumetricPass;
var profilingSampler = !isVolumetric ? ProfilerMarkers.s_ProfilingSamplerShadowPass : ProfilerMarkers.s_ProfilingSamplerShadowVolumetricPass;
LayerDebug.FormatPassName(layerBatch, ref passName);
using (var builder = graph.AddUnsafePass<PassData>(passName, out var passData, LayerDebug.GetProfilingSampler(passName, profilingSampler)))
{
passData.isVolumetric = isVolumetric;
passData.layerBatch = layerBatch;
passData.rendererData = rendererData;
passData.shadowTextures = universal2DResourceData.shadowTextures[batchIndex];
passData.shadowDepth = universal2DResourceData.shadowDepth;
for (var i = 0; i < passData.shadowTextures.Length; i++)
builder.UseTexture(passData.shadowTextures[i], AccessFlags.Write);
builder.UseTexture(passData.shadowDepth, AccessFlags.Write);
builder.AllowGlobalStateModification(true);
builder.SetRenderFunc(static (PassData data, UnsafeGraphContext context) =>
{
for (int i = 0; i < data.layerBatch.shadowIndices.Count; ++i)
{
var cmd = context.cmd;
var index = data.layerBatch.shadowIndices[i];
var light = data.layerBatch.lights[index];
if (data.isVolumetric && !RendererLighting.CanCastVolumetricShadows(light, data.layerBatch.endLayerValue))
continue;
// Shadow Pass
ExecuteShadowPass(cmd, data, light, i);
}
});
}
}
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 75b765822461a574aa8530ff269a3686
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,55 @@
using UnityEngine.Rendering.RenderGraphModule;
using UnityEngine.Rendering.Universal.U2D.Profiler;
namespace UnityEngine.Rendering.Universal
{
internal class GlobalPropertiesPass : ScriptableRenderPass
{
class PassData
{
internal Vector2Int screenParams;
}
internal static void Setup(RenderGraph graph, ContextContainer frameData, bool useLights)
{
Universal2DResourceData universal2DResourceData = frameData.Get<Universal2DResourceData>();
UniversalCameraData cameraData = frameData.Get<UniversalCameraData>();
Renderer2DData rendererData = frameData.Get<Universal2DRenderingData>().renderingData;
using (var builder = graph.AddRasterRenderPass<PassData>(ProfilerMarkers.s_SetGlobalProperties, out var passData, ProfilerMarkers.s_ProfilingSamplerSetGlobalProperties))
{
// Set screenParams when pixel perfect camera is used with the reference resolution
passData.screenParams = Vector2Int.zero;
cameraData.camera.TryGetComponent(out PixelPerfectCamera pixelPerfectCamera);
if (pixelPerfectCamera != null && pixelPerfectCamera.enabled && pixelPerfectCamera.offscreenRTSize != Vector2Int.zero)
passData.screenParams = pixelPerfectCamera.offscreenRTSize;
if (useLights)
{
// Set light lookup and fall off textures as global
var lightLookupTexture = graph.ImportTexture(Light2DLookupTexture.GetLightLookupTexture_Rendergraph());
var fallOffTexture = graph.ImportTexture(Light2DLookupTexture.GetFallOffLookupTexture_Rendergraph());
builder.SetGlobalTextureAfterPass(lightLookupTexture, Light2DLookupTexture.k_LightLookupID);
builder.SetGlobalTextureAfterPass(fallOffTexture, Light2DLookupTexture.k_FalloffLookupID);
}
if (rendererData.useCameraSortingLayerTexture)
builder.SetGlobalTextureAfterPass(universal2DResourceData.cameraSortingLayerTexture, CopyCameraSortingLayerPass.k_CameraSortingLayerTextureId);
builder.AllowGlobalStateModification(true);
builder.SetRenderFunc(static (PassData data, RasterGraphContext context) =>
{
if (data.screenParams != Vector2Int.zero)
{
var cameraWidth = data.screenParams.x;
var cameraHeight = data.screenParams.y;
context.cmd.SetGlobalVector(ShaderPropertyId.screenParams, new Vector4(cameraWidth, cameraHeight, 1.0f + 1.0f / cameraWidth, 1.0f + 1.0f / cameraHeight));
}
});
}
}
}
}
@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: 3c49b0c9c0fdfac469942832f71ce5b1
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 8f8544f075b7a2f4fa7324c965608fb2
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,54 @@
using UnityEngine.Rendering.RenderGraphModule;
using UnityEngine.Rendering.Universal.U2D.Profiler;
namespace UnityEngine.Rendering.Universal
{
internal class UpscalePass : ScriptableRenderPass
{
Material m_BlitMaterial;
private class PassData
{
internal TextureHandle source;
internal Material blitMaterial;
}
public UpscalePass(RenderPassEvent evt, Material blitMaterial)
{
renderPassEvent = evt;
m_BlitMaterial = blitMaterial;
}
private static void ExecutePass(RasterCommandBuffer cmd, RTHandle source, Material blitMaterial)
{
using (new ProfilingScope(cmd, ProfilerMarkers.s_ProfilingSamplerDrawUpscale))
{
Vector2 viewportScale = source.useScaling ? new Vector2(source.rtHandleProperties.rtHandleScale.x, source.rtHandleProperties.rtHandleScale.y) : Vector2.one;
Blitter.BlitTexture(cmd, source, viewportScale, blitMaterial, source.rt.filterMode == FilterMode.Bilinear ? 1 : 0);
}
}
public void Render(RenderGraph graph, Camera camera, in TextureHandle cameraColorAttachment, in TextureHandle upscaleHandle)
{
camera.TryGetComponent<PixelPerfectCamera>(out var ppc);
if (ppc == null || !ppc.enabled || !ppc.requiresUpscalePass)
return;
using (var builder = graph.AddRasterRenderPass<PassData>(ProfilerMarkers.s_UpscalePass, out var passData, ProfilerMarkers.s_ProfilingSamplerUpscalePass))
{
passData.source = cameraColorAttachment;
passData.blitMaterial = m_BlitMaterial;
builder.SetRenderAttachment(upscaleHandle, 0);
builder.UseTexture(cameraColorAttachment);
builder.AllowPassCulling(false);
builder.SetRenderFunc(static (PassData data, RasterGraphContext context) =>
{
ExecutePass(context.cmd, data.source, data.blitMaterial);
});
}
}
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 6994c3a2393b6ee4a9b963abd8f7b656
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 5f7294749d1800e4daee980df3e624da
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,32 @@
using UnityEngine.Scripting.APIUpdating;
namespace UnityEngine.Rendering.Universal
{
/// <summary>
/// Class for 2D composite shadow casters.
/// </summary>
[CoreRPHelpURL("2DShadows", "com.unity.render-pipelines.universal")]
[Icon("UnityEngine/UI/Shadow Icon")]
[AddComponentMenu("Rendering/2D/Composite Shadow Caster 2D")]
[MovedFrom(false, "UnityEngine.Experimental.Rendering.Universal", "com.unity.render-pipelines.universal")]
[ExecuteInEditMode]
public class CompositeShadowCaster2D : ShadowCasterGroup2D
{
/// <summary>
/// This function is called when the object becomes enabled and active.
/// </summary>
protected void OnEnable()
{
ShadowCasterGroup2DManager.AddGroup(this);
}
/// <summary>
/// This function is called when the behaviour becomes disabled.
/// </summary>
protected void OnDisable()
{
ShadowCasterGroup2DManager.RemoveGroup(this);
}
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 38f48a8d66b2adc46887f23a3a88ac01
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 964353991afb13346a30f35dc459a9be
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,90 @@
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
namespace UnityEngine.Rendering.Universal
{
/// <summary>
/// Class <c>ShadowShape2DProvider</c> has methods called by a <c>ShadowCaster2D</c> to determine if it should be listed as a Casting Option, and to provide geometry if it is the active <c>ShadowShape2DProvider</c>
/// </summary>
[Serializable]
public abstract class ShadowShape2DProvider : Provider2D
{
/// <summary>
/// Gets the name to be listed in the <c>ShadowCaster2D</c> Casting Option drop down.
/// </summary>
/// <param name="componentName">The name of component associated with the provider.</param>
/// <returns>The string to be listed in the <c>ShadowCaster2D</c> Casting Option drop down.</returns>
public virtual GUIContent ProviderName(string componentName) { return new GUIContent(componentName, "Implemented by " + this.GetType().Name); }
/// <summary>
/// Called for the active <c>ShadowShape2DProvider</c> when the <c>ShadowCaster2D</c> becomes enabled
/// </summary>
/// <param name="sourceComponent">The component associated with the provider</param>
/// <param name="persistantShadowShape">An instance of <c>ShadowShape2D</c> that is used by the <c>ShadowCaster2D</c></param>
public virtual void Enabled(Component sourceComponent, ShadowShape2D persistantShadowShape) {}
/// <summary>
/// Called for the active <c>ShadowShape2DProvider</c> when the <c>ShadowCaster2D</c> becomes disabled
/// </summary>
/// <param name="sourceComponent">The component associated with the provider</param>
/// <param name="persistantShadowShape">An instance of <c>ShadowShape2D</c> that is used by the <c>ShadowCaster2D</c></param>
public virtual void Disabled(Component sourceComponent, ShadowShape2D persistantShadowShape) {}
/// <summary>
/// Called when the <c>ShadowShape2DProvider</c> is selected as the active Casting Option.
/// </summary>
/// <param name="sourceComponent">The component associated with the provider</param>
/// <param name="persistantShadowShape">An instance of <c>ShadowShape2D</c> that is used by the <c>ShadowCaster2D</c></param>
public virtual void OnInitialized(Component sourceComponent, ShadowShape2D persistantShadowShape) { }
[Obsolete("Use latest OnBeforeRender")]
public virtual void OnBeforeRender(Component sourceComponent, Bounds worldCullingBounds, ShadowShape2D persistantShadowShape) { }
/// <summary>
/// Called before 2D lighting is rendered each frame
/// </summary>
/// <param name="camera">The camera being rendered</param>
/// <param name="sourceComponent">The component associated with the provider</param>
/// <param name="worldCullingBounds">The bounds enclosing the region of the view frustum and all visible lights</param>
/// <param name="persistantShadowShape">An instance of <c>ShadowShape2D</c> that is used by the <c>ShadowCaster2D</c></param>
public virtual void OnBeforeRender(Camera camera, Component sourceComponent, Bounds worldCullingBounds, ShadowShape2D persistantShadowShape) { }
/// <summary>
/// Called for each component on a <c>ShadowCaster2D's</c> <c>GameObject</c>. Returns true if the provided component is the data source of the <c>ShadowShapeProvider</c>.
/// </summary>
/// <param name="sourceComponent">The component to test as a source</param>
/// <returns>Returns true if sourceComponent is the data source of the <c>ShadowShapeProvider</c>.</returns>
public abstract bool IsRequiredComponentData(Component sourceComponent);
internal override GUIContent Internal_ProviderName(string componentName) { return ProviderName(componentName); }
internal override bool Internal_IsRequiredComponentData(Component sourceComponent) { return IsRequiredComponentData(sourceComponent); }
// Cached stable identifier hash for this provider's concrete type. Computed lazily on
// first access and cached for the lifetime of the instance, so per-frame change detection
// does not pay the string/CRC cost. Uses assembly name + full type name so two providers
// sharing a class name in different assemblies do not collide, and excludes assembly
// version so version bumps do not invalidate the hash.
[NonSerialized] int m_TypeIdentifierHash;
internal int TypeIdentifierHash
{
get
{
if (m_TypeIdentifierHash == 0)
{
Type type = GetType();
string qualified = type.Assembly.GetName().Name + "::" + type.FullName;
int hash = unchecked((int)URP2D_Crc32.Compute(qualified));
// Reserve 0 as "uncomputed" sentinel.
m_TypeIdentifierHash = hash == 0 ? 1 : hash;
}
return m_TypeIdentifierHash;
}
}
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: cb2be5655921e3c4c8c8bd8d647227d4
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,364 @@
#if USING_PHYSICS2D
using System;
using System.Collections.Generic;
using System.Runtime.CompilerServices;
using Unity.Collections;
namespace UnityEngine.Rendering.Universal
{
[Serializable]
class ShadowShape2DProvider_Collider2D : ShadowShape2DProvider
{
const float k_InitialTrim = 0.05f;
// Shadow state.
struct MinMaxBounds
{
public Vector3 min;
public Vector3 max;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool Intersects(ref MinMaxBounds bounds)
{
return
min.x <= bounds.max.x &&
max.x >= bounds.min.x &&
min.y <= bounds.max.y &&
max.y >= bounds.min.y &&
min.z <= bounds.max.z &&
max.z >= bounds.min.z;
}
public MinMaxBounds(ref Bounds bounds)
{
min = bounds.min;
max = bounds.max;
}
};
// Compare bounds.
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static bool CompareApproximately(ref Bounds a, ref Bounds b)
{
return !(((a.min - b.min).sqrMagnitude > Mathf.Epsilon) || ((a.max - b.max).sqrMagnitude > Mathf.Epsilon));
}
// Rotate bounds.
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static void TransformBounds2D(Matrix4x4 transform, ref Bounds bounds)
{
var center = transform.MultiplyPoint(bounds.center);
var extents = bounds.extents;
var axisX = transform.MultiplyVector(new Vector3(extents.x, 0f, 0f));
var axisY = transform.MultiplyVector(new Vector3(0f, extents.y, 0f));
extents.x = MathF.Abs(axisX.x) + MathF.Abs(axisY.x);
extents.y = MathF.Abs(axisX.y) + MathF.Abs(axisY.y);
bounds = new Bounds { center = center, extents = extents };
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static void ClearShapes(ShadowShape2D persistantShapeObject)
{
// Indicate no shape data.
persistantShapeObject.SetShape(new NativeArray<Vector3>(), new NativeArray<int>(), ShadowShape2D.OutlineTopology.Lines, ShadowShape2D.WindingOrder.CounterClockwise);
}
private List<Bounds> m_ShadowShapeBounds;
private List<MinMaxBounds> m_ShadowShapeMinMaxBounds;
private MinMaxBounds m_ShadowCombinedShapeMinMaxBounds;
private Bounds m_LastWorldCullingBounds;
private Matrix4x4 m_LastColliderSpace;
private bool m_ShadowDirty = true;
private UInt32 m_ShadowStateHash = 0;
private PhysicsShapeGroup2D m_ShadowShapeGroup;
private void CalculateShadows(Collider2D collider, ShadowShape2D persistantShapeObject, Bounds worldCullingBounds)
{
// Create a shadow shape group if not available yet.
if (m_ShadowShapeGroup == null)
m_ShadowShapeGroup = new PhysicsShapeGroup2D(collider.shapeCount);
// Create a shadow shape bounds if not available.
if (m_ShadowShapeBounds == null)
m_ShadowShapeBounds = new List<Bounds>(collider.shapeCount);
// Create a shadow shape min/max bounds if not available.
if (m_ShadowShapeMinMaxBounds == null)
m_ShadowShapeMinMaxBounds = new List<MinMaxBounds>();
// Fetch collider space.
var attachedBody = collider.attachedRigidbody;
var colliderSpace = attachedBody ? attachedBody.localToWorldMatrix : Matrix4x4.identity;
// If the shape hash has changed, grab a new potential visible geometry group.
var shapeHash = collider.GetShapeHash();
if (shapeHash != m_ShadowStateHash)
{
// Set the current shadow shape hash.
m_ShadowStateHash = shapeHash;
// Clear the shapes.
m_ShadowShapeGroup.Clear();
// Finish if we don't have any shapes to return.
if (collider.shapeCount == 0)
{
// Clear the shapes.
ClearShapes(persistantShapeObject);
return;
}
// Get shapes and finish if none are returned.
if (collider.GetShapes(m_ShadowShapeGroup) == 0)
return;
// Set the last world culling bounds.
m_LastWorldCullingBounds = worldCullingBounds;
// Fetch the shadow bounds.
var combinedBounds = collider.GetShapeBounds(m_ShadowShapeBounds, useRadii: true, useWorldSpace: false);
m_ShadowCombinedShapeMinMaxBounds = new MinMaxBounds(ref combinedBounds);
// Calculate min/max bounds only once.
m_ShadowShapeMinMaxBounds.Clear();
m_ShadowShapeMinMaxBounds.Capacity = m_ShadowShapeBounds.Capacity;
for (var i = 0; i < m_ShadowShapeBounds.Count; ++i)
{
var shapeBounds = m_ShadowShapeBounds[i];
m_ShadowShapeMinMaxBounds.Add(new MinMaxBounds(ref shapeBounds));
}
// Flag the shadow as dirty.
m_ShadowDirty = true;
}
else
{
// If the collider space nor the culling bounds have not changed then finish.
if (colliderSpace.Equals(m_LastColliderSpace) &&
CompareApproximately(ref m_LastWorldCullingBounds, ref worldCullingBounds))
{
// No, so finish.
return;
}
// Yes, so store the last world culling bounds.
m_LastWorldCullingBounds = worldCullingBounds;
// Flag the shadow as dirty.
m_ShadowDirty = true;
}
// Store the last collider space.
m_LastColliderSpace = colliderSpace;
// Finish if the shadow data hasn't changed or we have no shadow shapes.
if (!m_ShadowDirty || m_ShadowShapeGroup.shapeCount == 0)
return;
// Shadow is no longer going to be dirty.
m_ShadowDirty = false;
// Does the collider intersect the culling area at all?
TransformBounds2D(Matrix4x4.Inverse(colliderSpace), ref worldCullingBounds);
var worldCullingMinMaxBounds = new MinMaxBounds(ref worldCullingBounds);
if (!m_ShadowCombinedShapeMinMaxBounds.Intersects(ref worldCullingMinMaxBounds))
{
// No, so clear the shapes.
ClearShapes(persistantShapeObject);
return;
}
// Fetch a copy of the shapes and vertices.
var shapeCount = m_ShadowShapeGroup.shapeCount;
var shapeGroupShapes = m_ShadowShapeGroup.groupShapes;
var shapeGroupVertices = m_ShadowShapeGroup.groupVertices;
// Create visible shape indices.
var visibleShapeIndices = new NativeArray<int>(shapeCount, Allocator.Temp, NativeArrayOptions.UninitializedMemory);
// We have to iterate the shapes to figure out both the vertex and index counts
// because we're dealing with NativeArray which we have to specify a size for up-front.ebug
var vertexCount = 0;
var indexCount = 0;
var visibleShapeCount = 0;
for (var i = 0; i < shapeCount; ++i)
{
// Does this shape intersect the world culling bounds?
if (m_ShadowShapeMinMaxBounds[i].Intersects(ref worldCullingMinMaxBounds))
{
// Yes, so we can use it.
var shape = shapeGroupShapes[i];
var shapeVertexCount = shape.vertexCount;
var shapeType = shape.shapeType;
vertexCount += shapeVertexCount;
// Handle each specific shape type.
switch (shapeType)
{
case PhysicsShapeType2D.Circle:
case PhysicsShapeType2D.Capsule:
indexCount += 2;
break;
case PhysicsShapeType2D.Polygon:
indexCount += 2 * shapeVertexCount;
break;
case PhysicsShapeType2D.Edges:
{
// Calculate if this defines open edges i.e. the start/end vertex are not coincident.
var startVertex = shapeGroupVertices[shape.vertexStartIndex];
var endVertex = shapeGroupVertices[shape.vertexStartIndex + shape.vertexCount - 1];
var openEdges = (endVertex - startVertex).sqrMagnitude > Mathf.Epsilon;
// If we're open then we have one less index.
indexCount += 2 * (openEdges ? shapeVertexCount - 1 : shapeVertexCount);
break;
}
}
// Add the shape to the visible shape indices.
visibleShapeIndices[visibleShapeCount++] = i;
}
}
// Do we have any visible shapes?
if (visibleShapeCount > 0)
{
// Yes, so create the shadow outputs.
var radii = new NativeArray<float>(vertexCount, Allocator.Temp);
var vertices = new NativeArray<Vector3>(vertexCount, Allocator.Temp);
var indices = new NativeArray<int>(indexCount, Allocator.Temp);
// Iterate all the visible shapes.
var vertexIndex = 0;
var indiceIndex = 0;
for (var i = 0; i < visibleShapeCount; ++i)
{
// Fetch the shape state.
var shape = shapeGroupShapes[visibleShapeIndices[i]];
var shapeType = shape.shapeType;
var radius = shape.radius;
var shapeVertexIndex = shape.vertexStartIndex;
var shapeVertexCount = shape.vertexCount;
// Handle each specific shape type.
switch (shapeType)
{
case PhysicsShapeType2D.Circle:
{
radii[vertexIndex] = radius;
indices[indiceIndex++] = vertexIndex;
indices[indiceIndex++] = vertexIndex;
vertices[vertexIndex++] = shapeGroupVertices[shapeVertexIndex];
break;
}
case PhysicsShapeType2D.Capsule:
{
radii[vertexIndex] = radius;
indices[indiceIndex++] = vertexIndex;
vertices[vertexIndex++] = shapeGroupVertices[shapeVertexIndex++];
radii[vertexIndex] = radius;
indices[indiceIndex++] = vertexIndex;
vertices[vertexIndex++] = shapeGroupVertices[shapeVertexIndex++];
break;
}
case PhysicsShapeType2D.Polygon:
{
var startIndex = vertexIndex;
var edgeIndex = vertexIndex;
for (var n = 0; n < (shapeVertexCount - 1); ++n)
{
radii[vertexIndex] = radius;
vertices[vertexIndex++] = shapeGroupVertices[shapeVertexIndex++];
indices[indiceIndex++] = edgeIndex++;
indices[indiceIndex++] = edgeIndex;
}
radii[vertexIndex] = radius;
vertices[vertexIndex++] = shapeGroupVertices[shapeVertexIndex++];
indices[indiceIndex++] = edgeIndex;
indices[indiceIndex++] = startIndex;
break;
}
case PhysicsShapeType2D.Edges:
{
var startIndex = vertexIndex;
var edgeIndex = vertexIndex;
for (var n = 0; n < (shapeVertexCount - 1); ++n)
{
radii[vertexIndex] = radius;
vertices[vertexIndex++] = shapeGroupVertices[shapeVertexIndex++];
indices[indiceIndex++] = edgeIndex++;
indices[indiceIndex++] = edgeIndex;
}
radii[vertexIndex] = radius;
vertices[vertexIndex++] = shapeGroupVertices[shapeVertexIndex++];
// Calculate if this defines open edges i.e. the start/end vertex are not coincident.
// NOTE: We're forced to calculate this again because we have no way to persist this from the index counting earlier.
var startVertex = shapeGroupVertices[shape.vertexStartIndex];
var endVertex = shapeGroupVertices[shape.vertexStartIndex + shape.vertexCount - 1];
var openEdges = (endVertex - startVertex).sqrMagnitude > Mathf.Epsilon;
if (!openEdges)
{
indices[indiceIndex++] = edgeIndex;
indices[indiceIndex++] = startIndex;
}
break;
}
}
}
// Calculate transformation required to move the Collider geometry into shadow-space.
var toShadowSpace = collider.transform.worldToLocalMatrix * colliderSpace;
Renderer renderer;
bool createInteriorGeometry = !collider.TryGetComponent<Renderer>(out renderer);
// Set the shadow shape.
persistantShapeObject.SetShape(vertices, indices, radii, toShadowSpace, ShadowShape2D.WindingOrder.CounterClockwise, true, createInteriorGeometry);
// Clean up.
indices.Dispose();
vertices.Dispose();
radii.Dispose();
}
else
{
// No, so clear the shapes.
ClearShapes(persistantShapeObject);
}
// Clean up.
visibleShapeIndices.Dispose();
}
//============================================================================================================
// Public
//============================================================================================================
public override bool IsRequiredComponentData(Component sourceComponent) { return sourceComponent is Collider2D; }
public override void OnInitialized(Component sourceComponent, ShadowShape2D persistantShadowShapeData)
{
m_ShadowStateHash = default;
m_ShadowCombinedShapeMinMaxBounds = default;
m_LastColliderSpace = Matrix4x4.identity;
}
public override void OnBeforeRender(Camera camera, Component sourceComponent, Bounds worldCullingBounds, ShadowShape2D persistantShadowShape)
{
Collider2D collider = (Collider2D)sourceComponent;
CalculateShadows(collider, persistantShadowShape, worldCullingBounds);
}
}
}
#endif
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 25ee3a4bc58576c42aa35f5e3b411125
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,146 @@
using System;
using System.Collections;
using System.Collections.Generic;
using Unity.Collections;
using Unity.Collections.LowLevel.Unsafe;
using UnityEngine;
using UnityEngine.U2D;
namespace UnityEngine.Rendering.Universal
{
[Serializable]
class ShadowShape2DProvider_SpriteRenderer : ShadowShape2DProvider
{
const float k_InitialTrim = 0.05f;
ShadowShape2D m_PersistantShapeData;
SpriteDrawMode m_CurrentDrawMode;
Vector2 m_CurrentDrawModeSize;
void SetFullRectShapeData(SpriteRenderer spriteRenderer, ShadowShape2D shadowShape2D)
{
// Draw shadows for the full rect of the sprite...
if (spriteRenderer.drawMode != SpriteDrawMode.Simple)
{
Sprite sprite = spriteRenderer.sprite;
Vector2 srSize = spriteRenderer.size;
Vector3 pivot = new Vector2(srSize.x * sprite.pivot.x / sprite.rect.width, srSize.y * sprite.pivot.y / sprite.rect.height);
Rect rect = new Rect(-pivot, new Vector2(srSize.x, srSize.y));
NativeArray<Vector3> vertices = new NativeArray<Vector3>(4, Allocator.Temp);
NativeArray<int> indices = new NativeArray<int>(8, Allocator.Temp);
vertices[0] = new Vector3(rect.min.x, rect.min.y);
vertices[1] = new Vector3(rect.min.x, rect.max.y);
vertices[2] = new Vector3(rect.max.x, rect.max.y);
vertices[3] = new Vector3(rect.max.x, rect.min.y);
indices[0] = 0;
indices[1] = 1;
indices[2] = 1;
indices[3] = 2;
indices[4] = 2;
indices[5] = 3;
indices[6] = 3;
indices[7] = 0;
shadowShape2D.SetShape(vertices, indices, ShadowShape2D.OutlineTopology.Lines);
vertices.Dispose();
indices.Dispose();
}
}
void SetPersistantShapeData(Sprite sprite, ShadowShape2D shadowShape2D, NativeSlice<Vector3> vertexSlice)
{
if (shadowShape2D != null)
{
NativeArray<ushort> ushortIndices = sprite.GetIndices();
NativeArray<int> indices = new NativeArray<int>(ushortIndices.Length, Allocator.Temp);
NativeArray<Vector3> vertices = new NativeArray<Vector3>(vertexSlice.Length, Allocator.Temp);
for (int i = 0; i < indices.Length; i++)
indices[i] = ushortIndices[i];
for (int i = 0; i < vertices.Length; i++)
vertices[i] = vertexSlice[i];
shadowShape2D.SetShape(vertices, indices, ShadowShape2D.OutlineTopology.Triangles);
vertices.Dispose();
indices.Dispose();
}
}
void TryToSetPersistantShapeData(SpriteRenderer spriteRenderer, ShadowShape2D persistantShadowShape, bool force)
{
if (spriteRenderer != null && spriteRenderer.sprite != null)
{
if (spriteRenderer.drawMode != SpriteDrawMode.Simple && (spriteRenderer.size.x != m_CurrentDrawModeSize.x || spriteRenderer.size.y != m_CurrentDrawModeSize.y || spriteRenderer.drawMode != m_CurrentDrawMode || force))
{
m_CurrentDrawModeSize = spriteRenderer.size;
SetFullRectShapeData(spriteRenderer, persistantShadowShape);
}
else if (spriteRenderer.drawMode != m_CurrentDrawMode || force)
{
Sprite sprite = spriteRenderer.sprite;
NativeSlice<Vector3> vertexSlice = sprite.GetVertexAttribute<Vector3>(UnityEngine.Rendering.VertexAttribute.Position);
SetPersistantShapeData(sprite, m_PersistantShapeData, vertexSlice);
}
m_CurrentDrawMode = spriteRenderer.drawMode;
}
}
void UpdatePersistantShapeData(SpriteRenderer spriteRenderer)
{
TryToSetPersistantShapeData(spriteRenderer, m_PersistantShapeData, true);
}
//============================================================================================================
// Public
//============================================================================================================
public override int MenuPriority() { return 1; } // give higher than default menu priority
public override bool IsRequiredComponentData(Component sourceComponent) { return sourceComponent is SpriteRenderer; }
public override void OnInitialized(Component sourceComponent, ShadowShape2D persistantShadowShape)
{
SpriteRenderer spriteRenderer = (SpriteRenderer)sourceComponent;
m_PersistantShapeData = persistantShadowShape as ShadowMesh2D;
if (spriteRenderer.sprite != null)
{
float trimEdge = ShadowShapeProvider2DUtility.GetTrimEdgeFromBounds(spriteRenderer.bounds, k_InitialTrim);
persistantShadowShape.SetDefaultTrim(trimEdge);
}
TryToSetPersistantShapeData(spriteRenderer, persistantShadowShape, true);
}
public override void OnBeforeRender(Camera camera, Component sourceComponent, Bounds worldCullingBounds, ShadowShape2D persistantShadowShape)
{
SpriteRenderer spriteRenderer = (SpriteRenderer)sourceComponent;
persistantShadowShape.SetFlip(spriteRenderer.flipX, spriteRenderer.flipY);
TryToSetPersistantShapeData(spriteRenderer, persistantShadowShape, false);
}
public override void Enabled(Component sourceComponent, ShadowShape2D persistantShadowShape)
{
SpriteRenderer spriteRenderer = (SpriteRenderer)sourceComponent;
m_PersistantShapeData = persistantShadowShape;
spriteRenderer.RegisterSpriteChangeCallback(UpdatePersistantShapeData);
}
public override void Disabled(Component sourceComponent, ShadowShape2D persistantShadowShape)
{
SpriteRenderer spriteRenderer = (SpriteRenderer)sourceComponent;
spriteRenderer.UnregisterSpriteChangeCallback(UpdatePersistantShapeData);
}
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 881ec97c8823ab046b7d532464c636f3
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,98 @@
#if USING_SPRITESHAPE
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using Unity.Collections;
using Unity.Collections.LowLevel.Unsafe;
using UnityEngine.U2D;
using Unity.Mathematics;
namespace UnityEngine.Rendering.Universal
{
[Serializable]
internal class ShadowShape2DProvider_SpriteShape : ShadowShape2DProvider
{
const float k_InitialTrim = 0.02f;
internal void UpdateShadows(SpriteShapeController spriteShapeController, ShadowShape2D persistantShapeData)
{
NativeArray<float2> shadowData = spriteShapeController.GetShadowShapeData();
int shadowDataCount = shadowData.Length;
if (shadowDataCount > 0)
{
bool isClosed = shadowData[0].x == shadowData[shadowDataCount - 1].x && shadowData[0].y == shadowData[shadowDataCount - 1].y;
int vertexCount = isClosed ? shadowDataCount - 1 : shadowDataCount;
int indexArraySize = 2 * shadowDataCount;
NativeArray<Vector3> vertices = new NativeArray<Vector3>(vertexCount, Allocator.Temp);
NativeArray<int> indices = new NativeArray<int>(indexArraySize-2, Allocator.Temp);
// Copy vertices
for (int i = 0; i < vertexCount; i++)
{
vertices[i] = new Vector3(shadowData[i].x, shadowData[i].y, 0);
}
// Copy indices
for (int i = 0; i < shadowDataCount - 1; i++)
{
int startIndex = 2 * i;
indices[startIndex] = i;
indices[startIndex + 1] = i+1;
}
if(isClosed)
{
int lastEdgeIndex = 2 * vertexCount;
indices[lastEdgeIndex - 1] = 0;
}
persistantShapeData.SetShape(vertices, indices, ShadowShape2D.OutlineTopology.Lines);
vertices.Dispose();
indices.Dispose();
}
shadowData.Dispose();
}
public override int MenuPriority() { return 10; } // give higher than default menu priority
public override void Enabled(Component sourceComponent, ShadowShape2D persistantShadowShape)
{
((SpriteShapeController)sourceComponent).ForceShadowShapeUpdate(true);
}
public override void Disabled(Component sourceComponent, ShadowShape2D persistantShadowShape)
{
((SpriteShapeController)sourceComponent).ForceShadowShapeUpdate(false);
}
public override bool IsRequiredComponentData(Component sourceComponent)
{
return sourceComponent as SpriteShapeController;
}
public override void OnInitialized(Component sourceComponent, ShadowShape2D persistantShadowShape)
{
SpriteShapeController spriteShapeController = (SpriteShapeController)sourceComponent;
SpriteShapeRenderer spriteShapeRenderer;
spriteShapeController.TryGetComponent<SpriteShapeRenderer>(out spriteShapeRenderer);
float trimEdge = ShadowShapeProvider2DUtility.GetTrimEdgeFromBounds(spriteShapeRenderer.bounds, k_InitialTrim);
persistantShadowShape.SetDefaultTrim(trimEdge);
UpdateShadows(spriteShapeController, persistantShadowShape);
}
public override void OnBeforeRender(Camera camera, Component sourceComponent, Bounds worldCullingBounds, ShadowShape2D persistantShadowShape)
{
UpdateShadows((SpriteShapeController)sourceComponent, persistantShadowShape);
}
}
}
#endif
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 387b120d24fbd4f4294bd52bb2104930
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,96 @@
#if USING_2DANIMATION
using System;
using System.Collections;
using System.Collections.Generic;
using Unity.Collections;
using Unity.Collections.LowLevel.Unsafe;
using UnityEngine;
using UnityEngine.U2D;
using UnityEngine.U2D.Animation;
namespace UnityEngine.Rendering.Universal
{
[Serializable]
class ShadowShape2DProvider_SpriteSkin : ShadowShape2DProvider
{
const float k_InitialTrim = 0.05f;
ShadowShape2D m_PersistantShapeData;
int m_LastDeformedVertexHash;
void TryToSetPersistantShapeData(SpriteSkin spriteSkin, ShadowShape2D persistantShadowShape, bool force)
{
if (spriteSkin == null)
return;
// For CPU deformation the outline vertices are backed by a buffer that is only populated after the
// first deformation pass has run. Reading it before then hands uninitialized memory to the shadow
// shape, so wait until the SpriteSkin reports a completed deformation. OnBeforeRender picks the
// shape up on the first frame after that, so nothing is permanently skipped.
if (!spriteSkin.HasCurrentDeformedVertices() || spriteSkin.vertexDeformationHash == 0)
return;
persistantShadowShape.SetShape(spriteSkin.outlineVertices, spriteSkin.outlineIndices, ShadowShape2D.OutlineTopology.Lines);
}
void UpdatePersistantShapeData(SpriteRenderer spriteRenderer)
{
SpriteSkin spriteSkin;
spriteRenderer.TryGetComponent<SpriteSkin>(out spriteSkin);
if (spriteSkin != null)
{
TryToSetPersistantShapeData(spriteSkin, m_PersistantShapeData, true);
}
}
//============================================================================================================
// Public
//============================================================================================================
public override int MenuPriority() { return 10; } // give higher than default menu priority
public override bool IsRequiredComponentData(Component sourceComponent) { return sourceComponent is SpriteSkin; }
#if USING_2DANIMATION_13_0_1_OR_ABOVE
public override void Enabled(Component sourceComponent, ShadowShape2D persistantShadowShape)
{
if (sourceComponent.TryGetComponent(out SpriteSkin spriteSkin))
{
spriteSkin.RegisterOutlineDependency();
}
}
public override void Disabled(Component sourceComponent, ShadowShape2D persistantShadowShape)
{
if (sourceComponent.TryGetComponent(out SpriteSkin spriteSkin))
{
spriteSkin.UnregisterOutlineDependency();
}
}
#endif
public override void OnInitialized(Component sourceComponent, ShadowShape2D persistantShadowShape)
{
SpriteSkin spriteSkin = (SpriteSkin)sourceComponent;
SpriteRenderer spriteRenderer;
spriteSkin.TryGetComponent<SpriteRenderer>(out spriteRenderer);
float trimEdge = ShadowShapeProvider2DUtility.GetTrimEdgeFromBounds(spriteRenderer.bounds, k_InitialTrim);
persistantShadowShape.SetDefaultTrim(trimEdge);
TryToSetPersistantShapeData(spriteSkin, persistantShadowShape, true);
}
public override void OnBeforeRender(Camera camera, Component sourceComponent, Bounds worldCullingBounds, ShadowShape2D persistantShadowShape)
{
SpriteSkin spriteSkin = (SpriteSkin)sourceComponent;
if (spriteSkin != null && spriteSkin.vertexDeformationHash != m_LastDeformedVertexHash)
{
SpriteRenderer spriteRenderer;
spriteSkin.TryGetComponent<SpriteRenderer>(out spriteRenderer);
persistantShadowShape.SetFlip(spriteRenderer.flipX, spriteRenderer.flipY);
TryToSetPersistantShapeData(spriteSkin, persistantShadowShape, false);
m_LastDeformedVertexHash = spriteSkin.vertexDeformationHash;
}
}
}
}
#endif
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: b4742283930118c448f26ae5f6d6b52b
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,21 @@
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
internal static class ShadowShapeProvider2DUtility
{
static public float GetTrimEdgeFromBounds(Bounds bounds, float trimMultipler)
{
Vector3 size = bounds.size;
// Pick the smaller side
float trimEdge = trimMultipler * (size.x < size.y ? size.x : size.y);
// Clean up the trim value to one significant digit
float multiplier = Mathf.Pow(10, -Mathf.Floor(Mathf.Log10(trimEdge)));
trimEdge = Mathf.Floor(trimEdge * multiplier) / multiplier;
return trimEdge;
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: e428475e2bdb81f499cdbe3c59953975
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 1fd29b02cf863634796f4fa1d42ab58d
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,96 @@
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using Unity.Collections;
namespace UnityEngine.Rendering.Universal
{
internal struct EdgeDictionary : IEdgeStore
{
static private Dictionary<ShadowEdge, int> m_EdgeDictionary = new Dictionary<ShadowEdge, int>(new EdgeComparer()); // This is done so we don't create garbage allocating and deallocating a dictionary object
#if UNITY_EDITOR
[RuntimeInitializeOnLoadMethod]
static void ResetStaticsOnLoad()
{
m_EdgeDictionary.Clear();
}
#endif
private class EdgeComparer : IEqualityComparer<ShadowEdge>
{
public bool Equals(ShadowEdge edge0, ShadowEdge edge1)
{
return (edge0.v0 == edge1.v0 && edge0.v1 == edge1.v1) || (edge0.v1 == edge1.v0 && edge0.v0 == edge1.v1);
}
public int GetHashCode(ShadowEdge edge)
{
int v0 = edge.v0;
int v1 = edge.v1;
if (edge.v1 < edge.v0)
{
v0 = edge.v1;
v1 = edge.v0;
}
int hashCode = v0 << 15 | v1;
return hashCode.GetHashCode();
}
}
public NativeArray<ShadowEdge> GetOutsideEdges(NativeArray<Vector3> vertices, NativeArray<int> indices)
{
m_EdgeDictionary.Clear();
m_EdgeDictionary.EnsureCapacity(indices.Length);
for (int i = 0; i < indices.Length; i += 3)
{
int v0Index = indices[i];
int v1Index = indices[i + 1];
int v2Index = indices[i + 2];
ShadowEdge edge0 = new ShadowEdge(v0Index, v1Index);
ShadowEdge edge1 = new ShadowEdge(v1Index, v2Index);
ShadowEdge edge2 = new ShadowEdge(v2Index, v0Index);
// When a key comparison is made edges (A, B) and (B, A) are equal (see EdgeComparer)
if (m_EdgeDictionary.ContainsKey(edge0))
m_EdgeDictionary[edge0] = m_EdgeDictionary[edge0] + 1;
else
m_EdgeDictionary.Add(edge0, 1);
if (m_EdgeDictionary.ContainsKey(edge1))
m_EdgeDictionary[edge1] = m_EdgeDictionary[edge1] + 1;
else
m_EdgeDictionary.Add(edge1, 1);
if (m_EdgeDictionary.ContainsKey(edge2))
m_EdgeDictionary[edge2] = m_EdgeDictionary[edge2] + 1;
else
m_EdgeDictionary.Add(edge2, 1);
}
// Count the outside edges
int outsideEdges = 0;
foreach (KeyValuePair<ShadowEdge, int> keyValuePair in m_EdgeDictionary)
{
if (keyValuePair.Value == 1)
outsideEdges++;
}
// Create unsorted edges array
int edgeIndex = 0;
NativeArray<ShadowEdge> edges = new NativeArray<ShadowEdge>(outsideEdges, Allocator.Temp);
foreach (KeyValuePair<ShadowEdge, int> keyValuePair in m_EdgeDictionary)
{
if (keyValuePair.Value == 1)
{
edges[edgeIndex++] = keyValuePair.Key;
}
}
return edges;
}
}
}

Some files were not shown because too many files have changed in this diff Show More