initial commit
This commit is contained in:
parent
6715289efe
commit
788c3389af
37645 changed files with 2526849 additions and 80 deletions
|
|
@ -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("UnityEngine.Experimental.Rendering.Universal")]
|
||||
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: {fileID: 2800000, guid: ec2f01df0bb1bfe459b5562a9e71c8d4, type: 3}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
File diff suppressed because it is too large
Load diff
|
|
@ -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: e0c762e8dad9af640925a542761eb35a
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
fileFormatVersion: 2
|
||||
guid: fea15e5ce57d36d489e00e9111e1c9ae
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 28 KiB |
|
|
@ -0,0 +1,126 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 5688ab254e4c0634f8d6c8e0792331ca
|
||||
TextureImporter:
|
||||
internalIDToNameTable: []
|
||||
externalObjects: {}
|
||||
serializedVersion: 11
|
||||
mipmaps:
|
||||
mipMapMode: 0
|
||||
enableMipMap: 0
|
||||
sRGBTexture: 1
|
||||
linearTexture: 0
|
||||
fadeOut: 0
|
||||
borderMipMap: 0
|
||||
mipMapsPreserveCoverage: 0
|
||||
alphaTestReferenceValue: 0.5
|
||||
mipMapFadeDistanceStart: 1
|
||||
mipMapFadeDistanceEnd: 3
|
||||
bumpmap:
|
||||
convertToNormalMap: 0
|
||||
externalNormalMap: 0
|
||||
heightScale: 0.25
|
||||
normalMapFilter: 0
|
||||
isReadable: 0
|
||||
streamingMipmaps: 0
|
||||
streamingMipmapsPriority: 0
|
||||
vTOnly: 0
|
||||
ignoreMasterTextureLimit: 0
|
||||
grayScaleToAlpha: 0
|
||||
generateCubemap: 6
|
||||
cubemapConvolution: 0
|
||||
seamlessCubemap: 0
|
||||
textureFormat: 1
|
||||
maxTextureSize: 2048
|
||||
textureSettings:
|
||||
serializedVersion: 2
|
||||
filterMode: 1
|
||||
aniso: 1
|
||||
mipBias: 0
|
||||
wrapU: 1
|
||||
wrapV: 1
|
||||
wrapW: 1
|
||||
nPOTScale: 1
|
||||
lightmap: 0
|
||||
compressionQuality: 50
|
||||
spriteMode: 0
|
||||
spriteExtrude: 1
|
||||
spriteMeshType: 1
|
||||
alignment: 0
|
||||
spritePivot: {x: 0.5, y: 0.5}
|
||||
spritePixelsToUnits: 100
|
||||
spriteBorder: {x: 0, y: 0, z: 0, w: 0}
|
||||
spriteGenerateFallbackPhysicsShape: 1
|
||||
alphaUsage: 1
|
||||
alphaIsTransparency: 0
|
||||
spriteTessellationDetail: -1
|
||||
textureType: 0
|
||||
textureShape: 1
|
||||
singleChannelComponent: 0
|
||||
flipbookRows: 1
|
||||
flipbookColumns: 1
|
||||
maxTextureSizeSet: 0
|
||||
compressionQualitySet: 0
|
||||
textureFormatSet: 0
|
||||
ignorePngGamma: 0
|
||||
applyGammaDecoding: 0
|
||||
platformSettings:
|
||||
- serializedVersion: 3
|
||||
buildTarget: DefaultTexturePlatform
|
||||
maxTextureSize: 2048
|
||||
resizeAlgorithm: 0
|
||||
textureFormat: 4
|
||||
textureCompression: 1
|
||||
compressionQuality: 50
|
||||
crunchedCompression: 0
|
||||
allowsAlphaSplitting: 0
|
||||
overridden: 0
|
||||
androidETC2FallbackOverride: 0
|
||||
forceMaximumCompressionQuality_BC6H_BC7: 0
|
||||
- serializedVersion: 3
|
||||
buildTarget: Standalone
|
||||
maxTextureSize: 2048
|
||||
resizeAlgorithm: 0
|
||||
textureFormat: 4
|
||||
textureCompression: 1
|
||||
compressionQuality: 50
|
||||
crunchedCompression: 0
|
||||
allowsAlphaSplitting: 0
|
||||
overridden: 0
|
||||
androidETC2FallbackOverride: 0
|
||||
forceMaximumCompressionQuality_BC6H_BC7: 0
|
||||
- serializedVersion: 3
|
||||
buildTarget: Windows Store Apps
|
||||
maxTextureSize: 2048
|
||||
resizeAlgorithm: 0
|
||||
textureFormat: 4
|
||||
textureCompression: 1
|
||||
compressionQuality: 50
|
||||
crunchedCompression: 0
|
||||
allowsAlphaSplitting: 0
|
||||
overridden: 0
|
||||
androidETC2FallbackOverride: 0
|
||||
forceMaximumCompressionQuality_BC6H_BC7: 0
|
||||
spriteSheet:
|
||||
serializedVersion: 2
|
||||
sprites: []
|
||||
outline: []
|
||||
physicsShape: []
|
||||
bones: []
|
||||
spriteID:
|
||||
internalID: 0
|
||||
vertices: []
|
||||
indices:
|
||||
edges: []
|
||||
weights: []
|
||||
secondaryTextures: []
|
||||
nameFileIdTable: {}
|
||||
spritePackingTag:
|
||||
pSDRemoveMatte: 0
|
||||
pSDShowRemoveMatteOption: 0
|
||||
userData: "<?xml version=\"1.0\" encoding=\"utf-16\"?>\r\n<ExtraTextureSettings
|
||||
xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\">\r\n
|
||||
<detailMap>false</detailMap>\r\n <hemiOctNormals>true</hemiOctNormals>\r\n
|
||||
<geoRoughness>GenRoughness</geoRoughness>\r\n <geoRoughnessPow>1</geoRoughnessPow>\r\n
|
||||
<dontReadSource>false</dontReadSource>\r\n</ExtraTextureSettings>"
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
|
|
@ -0,0 +1,447 @@
|
|||
using System;
|
||||
using UnityEngine.Serialization;
|
||||
using UnityEngine.Scripting.APIUpdating;
|
||||
using UnityEngine.U2D;
|
||||
#if UNITY_EDITOR
|
||||
using UnityEditor.Experimental.SceneManagement;
|
||||
#endif
|
||||
|
||||
namespace UnityEngine.Rendering.Universal
|
||||
{
|
||||
/// <summary>
|
||||
/// Class <c>Light2D</c> is a 2D light which can be used with the 2D Renderer.
|
||||
/// </summary>
|
||||
///
|
||||
[ExecuteAlways, DisallowMultipleComponent]
|
||||
[MovedFrom("UnityEngine.Experimental.Rendering.Universal")]
|
||||
[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>
|
||||
/// The accuracy of how the normap 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 standed blending (alpha, 1-alpha)
|
||||
/// </summary>
|
||||
AlphaBlend
|
||||
}
|
||||
|
||||
private enum ComponentVersions
|
||||
{
|
||||
Version_Unserialized = 0,
|
||||
Version_1 = 1
|
||||
}
|
||||
|
||||
const ComponentVersions k_CurrentComponentVersion = ComponentVersions.Version_1;
|
||||
[SerializeField] ComponentVersions m_ComponentVersion = ComponentVersions.Version_Unserialized;
|
||||
|
||||
|
||||
#if USING_ANIMATION_MODULE
|
||||
[UnityEngine.Animations.NotKeyable]
|
||||
#endif
|
||||
[SerializeField] LightType m_LightType = LightType.Point;
|
||||
[SerializeField, FormerlySerializedAs("m_LightOperationIndex")]
|
||||
int m_BlendStyleIndex = 0;
|
||||
|
||||
[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;
|
||||
[SerializeField] bool m_LightVolumeIntensityEnabled = false;
|
||||
[SerializeField] int[] m_ApplyToSortingLayers = new int[1]; // 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;
|
||||
|
||||
[FormerlySerializedAs("m_LightCookieSprite")]
|
||||
[SerializeField] Sprite m_DeprecatedPointLightCookieSprite;
|
||||
|
||||
[SerializeField] int m_LightOrder = 0;
|
||||
|
||||
[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.
|
||||
|
||||
[SerializeField] bool m_ShadowIntensityEnabled = false;
|
||||
[Range(0, 1)]
|
||||
[SerializeField] float m_ShadowIntensity = 0.75f;
|
||||
|
||||
[SerializeField] bool m_ShadowVolumeIntensityEnabled = false;
|
||||
[Range(0, 1)]
|
||||
[SerializeField] float m_ShadowVolumeIntensity = 0.75f;
|
||||
|
||||
|
||||
Mesh m_Mesh;
|
||||
|
||||
[SerializeField]
|
||||
private LightUtility.LightMeshVertex[] m_Vertices = new LightUtility.LightMeshVertex[1];
|
||||
|
||||
[SerializeField]
|
||||
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
|
||||
int m_PreviousLightCookieSprite;
|
||||
internal Vector3 m_CachedPosition;
|
||||
|
||||
internal int[] affectedSortingLayers => m_ApplyToSortingLayers;
|
||||
|
||||
private int lightCookieSpriteInstanceID => m_LightCookieSprite?.GetInstanceID() ?? 0;
|
||||
|
||||
[SerializeField]
|
||||
Bounds m_LocalBounds;
|
||||
internal BoundingSphere boundingSphere { get; private set; }
|
||||
|
||||
internal Mesh lightMesh
|
||||
{
|
||||
get
|
||||
{
|
||||
if (null == m_Mesh)
|
||||
m_Mesh = new Mesh();
|
||||
return m_Mesh;
|
||||
}
|
||||
}
|
||||
|
||||
internal bool hasCachedMesh => (vertices.Length > 1 && indices.Length > 1);
|
||||
|
||||
internal bool forceUpdate = false;
|
||||
|
||||
/// <summary>
|
||||
/// The light's current type
|
||||
/// </summary>
|
||||
public LightType lightType
|
||||
{
|
||||
get => m_LightType;
|
||||
set
|
||||
{
|
||||
if (m_LightType != value)
|
||||
UpdateMesh();
|
||||
|
||||
m_LightType = value;
|
||||
Light2DManager.ErrorIfDuplicateGlobalLight(this);
|
||||
}
|
||||
}
|
||||
|
||||
/// <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 that the shadows are enabled
|
||||
/// </summary>
|
||||
public bool shadowsEnabled { get => m_ShadowIntensityEnabled; set => m_ShadowIntensityEnabled = 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]
|
||||
public float volumeOpacity => m_LightVolumeIntensity;
|
||||
|
||||
/// <summary>
|
||||
/// Controls the visibility of the light's volume
|
||||
/// </summary>
|
||||
public float volumeIntensity => m_LightVolumeIntensity;
|
||||
/// <summary>
|
||||
/// Enables or disables the light's volume
|
||||
/// </summary>
|
||||
public bool volumeIntensityEnabled { get => m_LightVolumeIntensityEnabled; set => m_LightVolumeIntensityEnabled = value; }
|
||||
/// <summary>
|
||||
/// The Sprite that's used by the Sprite Light type to control the shape light
|
||||
/// </summary>
|
||||
public Sprite lightCookieSprite { get { return m_LightType != LightType.Point ? m_LightCookieSprite : m_DeprecatedPointLightCookieSprite; } }
|
||||
/// <summary>
|
||||
/// Controls the brightness and distance of the fall off (edge) of the light
|
||||
/// </summary>
|
||||
public float falloffIntensity => m_FalloffIntensity;
|
||||
|
||||
[Obsolete]
|
||||
public bool alphaBlendOnOverlap { get { return m_OverlapOperation == OverlapOperation.AlphaBlend; } }
|
||||
|
||||
/// <summary>
|
||||
/// Returns the overlap operation mode.
|
||||
/// </summary>
|
||||
public OverlapOperation overlapOperation => m_OverlapOperation;
|
||||
|
||||
/// <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;
|
||||
|
||||
public bool renderVolumetricShadows => volumetricShadowsEnabled && shadowVolumeIntensity > 0;
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
internal void UpdateMesh(bool forceUpdate = false)
|
||||
{
|
||||
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(lightCookieSpriteInstanceID, ref m_PreviousLightCookieSprite);
|
||||
var shapePathHashChanged = LightUtility.CheckForChange(shapePathHash, ref m_PreviousShapePathHash);
|
||||
var lightTypeChanged = LightUtility.CheckForChange(m_LightType, ref m_PreviousLightType);
|
||||
var hashChanged = fallOffSizeChanged || parametricRadiusChanged || parametricSidesChanged ||
|
||||
parametricAngleOffsetChanged || spriteInstanceChanged || shapePathHashChanged || lightTypeChanged;
|
||||
|
||||
// Mesh Rebuilding
|
||||
if (hashChanged || forceUpdate)
|
||||
{
|
||||
switch (m_LightType)
|
||||
{
|
||||
case LightType.Freeform:
|
||||
m_LocalBounds = LightUtility.GenerateShapeMesh(this, m_ShapePath, m_ShapeLightFalloffSize);
|
||||
break;
|
||||
case LightType.Parametric:
|
||||
m_LocalBounds = LightUtility.GenerateParametricMesh(this, m_ShapeLightParametricRadius, m_ShapeLightFalloffSize, m_ShapeLightParametricAngleOffset, m_ShapeLightParametricSides);
|
||||
break;
|
||||
case LightType.Sprite:
|
||||
m_LocalBounds = UpdateSpriteMesh();
|
||||
break;
|
||||
case LightType.Point:
|
||||
m_LocalBounds = LightUtility.GenerateParametricMesh(this, 1.412135f, 0, 0, 4);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
private void Awake()
|
||||
{
|
||||
if (m_LightCookieSprite != null)
|
||||
{
|
||||
bool updateMesh = !hasCachedMesh || (m_LightType == LightType.Sprite && m_LightCookieSprite.packed);
|
||||
UpdateMesh(updateMesh);
|
||||
if (hasCachedMesh)
|
||||
{
|
||||
lightMesh.SetVertexBufferParams(vertices.Length, LightUtility.LightMeshVertex.VertexLayout);
|
||||
lightMesh.SetVertexBufferData(vertices, 0, 0, vertices.Length);
|
||||
lightMesh.SetIndices(indices, MeshTopology.Triangles, 0, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void OnEnable()
|
||||
{
|
||||
m_PreviousLightCookieSprite = lightCookieSpriteInstanceID;
|
||||
Light2DManager.RegisterLight(this);
|
||||
}
|
||||
|
||||
private void OnDisable()
|
||||
{
|
||||
Light2DManager.DeregisterLight(this);
|
||||
}
|
||||
|
||||
private void LateUpdate()
|
||||
{
|
||||
if (m_LightType == LightType.Global)
|
||||
return;
|
||||
|
||||
UpdateMesh(forceUpdate);
|
||||
UpdateBoundingSphere();
|
||||
|
||||
forceUpdate = false;
|
||||
}
|
||||
|
||||
public void OnBeforeSerialize()
|
||||
{
|
||||
m_ComponentVersion = k_CurrentComponentVersion;
|
||||
}
|
||||
|
||||
public void OnAfterDeserialize()
|
||||
{
|
||||
// Upgrade from no serialized version
|
||||
if (m_ComponentVersion == ComponentVersions.Version_Unserialized)
|
||||
{
|
||||
m_ShadowVolumeIntensityEnabled = m_ShadowVolumeIntensity > 0;
|
||||
m_ShadowIntensityEnabled = m_ShadowIntensity > 0;
|
||||
m_LightVolumeIntensityEnabled = m_LightVolumeIntensity > 0;
|
||||
m_NormalMapQuality = !m_UseNormalMap ? NormalMapQuality.Disabled : m_NormalMapQuality;
|
||||
m_ComponentVersion = ComponentVersions.Version_1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 073797afb82c5a1438f328866b10b3f0
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
|
|
@ -0,0 +1,36 @@
|
|||
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/Resources/SceneViewIcons/";
|
||||
private static readonly string[] s_LightIconFileNames = new[]
|
||||
{
|
||||
"ParametricLight.png",
|
||||
"FreeformLight.png",
|
||||
"SpriteLight.png",
|
||||
"PointLight.png",
|
||||
"GlobalLight.png"
|
||||
};
|
||||
|
||||
private void OnDrawGizmos()
|
||||
{
|
||||
Gizmos.color = Color.blue;
|
||||
Gizmos.DrawIcon(transform.position, s_IconsPath + s_LightIconFileNames[(int)m_LightType], true);
|
||||
}
|
||||
|
||||
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,127 @@
|
|||
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("UnityEngine.Experimental.Rendering.Universal")]
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Transient data
|
||||
internal bool isDirty { get; set; }
|
||||
internal bool hasRenderTarget { get; set; }
|
||||
internal RenderTargetHandle renderTargetHandle;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: f4caf3da21d83644ea1c04986c6cea14
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
|
|
@ -0,0 +1,110 @@
|
|||
using System.Collections.Generic;
|
||||
using Unity.Mathematics;
|
||||
using UnityEngine.Profiling;
|
||||
|
||||
namespace UnityEngine.Rendering.Universal
|
||||
{
|
||||
internal struct LightStats
|
||||
{
|
||||
public int totalLights;
|
||||
public int totalNormalMapUsage;
|
||||
public int totalVolumetricUsage;
|
||||
public uint blendStylesUsed;
|
||||
public uint blendStylesWithLights;
|
||||
}
|
||||
|
||||
internal interface ILight2DCullResult
|
||||
{
|
||||
List<Light2D> visibleLights { get; }
|
||||
LightStats GetLightStatsByLayer(int layer);
|
||||
bool IsSceneLit();
|
||||
}
|
||||
|
||||
internal class Light2DCullResult : ILight2DCullResult
|
||||
{
|
||||
private List<Light2D> m_VisibleLights = new List<Light2D>();
|
||||
public List<Light2D> visibleLights => m_VisibleLights;
|
||||
|
||||
public bool IsSceneLit()
|
||||
{
|
||||
if (visibleLights.Count > 0)
|
||||
return true;
|
||||
|
||||
foreach (var light in Light2DManager.lights)
|
||||
{
|
||||
if (light.lightType == Light2D.LightType.Global)
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public LightStats GetLightStatsByLayer(int layer)
|
||||
{
|
||||
var returnStats = new LightStats();
|
||||
foreach (var light in visibleLights)
|
||||
{
|
||||
if (!light.IsLitLayer(layer))
|
||||
continue;
|
||||
|
||||
returnStats.totalLights++;
|
||||
if (light.normalMapQuality != Light2D.NormalMapQuality.Disabled)
|
||||
returnStats.totalNormalMapUsage++;
|
||||
if (light.volumeIntensity > 0)
|
||||
returnStats.totalVolumetricUsage++;
|
||||
|
||||
returnStats.blendStylesUsed |= (uint)(1 << light.blendStyleIndex);
|
||||
if (light.lightType != Light2D.LightType.Global)
|
||||
returnStats.blendStylesWithLights |= (uint)(1 << light.blendStyleIndex);
|
||||
}
|
||||
|
||||
return returnStats;
|
||||
}
|
||||
|
||||
public void SetupCulling(ref ScriptableCullingParameters cullingParameters, Camera camera)
|
||||
{
|
||||
Profiler.BeginSample("Cull 2D Lights");
|
||||
m_VisibleLights.Clear();
|
||||
foreach (var light in Light2DManager.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);
|
||||
Profiler.EndSample();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 704a37a0ce184459805c0b7719a25a60
|
||||
timeCreated: 1596067202
|
||||
|
|
@ -0,0 +1,117 @@
|
|||
using System.Collections.Generic;
|
||||
|
||||
namespace UnityEngine.Rendering.Universal
|
||||
{
|
||||
internal static class Light2DManager
|
||||
{
|
||||
private static SortingLayer[] s_SortingLayers;
|
||||
|
||||
public static List<Light2D> lights { get; } = new List<Light2D>();
|
||||
|
||||
// Called during OnEnable
|
||||
public static void RegisterLight(Light2D light)
|
||||
{
|
||||
Debug.Assert(!lights.Contains(light));
|
||||
lights.Add(light);
|
||||
ErrorIfDuplicateGlobalLight(light);
|
||||
}
|
||||
|
||||
// Called during OnEnable
|
||||
public static void DeregisterLight(Light2D light)
|
||||
{
|
||||
Debug.Assert(lights.Contains(light));
|
||||
lights.Remove(light);
|
||||
}
|
||||
|
||||
public static void ErrorIfDuplicateGlobalLight(Light2D light)
|
||||
{
|
||||
if (light.lightType != Light2D.LightType.Global)
|
||||
return;
|
||||
|
||||
foreach (var sortingLayer in light.affectedSortingLayers)
|
||||
{
|
||||
// should this really trigger at runtime?
|
||||
if (ContainsDuplicateGlobalLight(sortingLayer, light.blendStyleIndex))
|
||||
Debug.LogError("More than one global light on layer " + SortingLayer.IDToName(sortingLayer) + " for light blend style index " + light.blendStyleIndex);
|
||||
}
|
||||
}
|
||||
|
||||
public static bool GetGlobalColor(int sortingLayerIndex, int blendStyleIndex, out Color color)
|
||||
{
|
||||
var foundGlobalColor = false;
|
||||
color = Color.black;
|
||||
|
||||
// This should be rewritten to search only global lights
|
||||
foreach (var light in lights)
|
||||
{
|
||||
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 = UnityEditor.SceneManagement.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, int blendStyleIndex)
|
||||
{
|
||||
var globalLightCount = 0;
|
||||
|
||||
// This should be rewritten to search only global lights
|
||||
foreach (var light in lights)
|
||||
{
|
||||
if (light.lightType == Light2D.LightType.Global &&
|
||||
light.blendStyleIndex == blendStyleIndex &&
|
||||
light.IsLitLayer(sortingLayerIndex))
|
||||
{
|
||||
#if UNITY_EDITOR
|
||||
// If we found the first global light in our prefab stage
|
||||
if (UnityEditor.SceneManagement.PrefabStageUtility.GetPrefabStage(light.gameObject) == UnityEditor.SceneManagement.PrefabStageUtility.GetCurrentPrefabStage())
|
||||
#endif
|
||||
{
|
||||
if (globalLightCount > 0)
|
||||
return true;
|
||||
|
||||
globalLightCount++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public static SortingLayer[] GetCachedSortingLayer()
|
||||
{
|
||||
if (s_SortingLayers is null)
|
||||
{
|
||||
s_SortingLayers = SortingLayer.layers;
|
||||
}
|
||||
#if UNITY_EDITOR
|
||||
// we should fix. Make a non allocating version of this
|
||||
if (!Application.isPlaying)
|
||||
s_SortingLayers = SortingLayer.layers;
|
||||
#endif
|
||||
return s_SortingLayers;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
fileFormatVersion: 2
|
||||
guid: d8151411c155429aa167611d958fbb8c
|
||||
timeCreated: 1595907591
|
||||
|
|
@ -0,0 +1,61 @@
|
|||
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;
|
||||
}
|
||||
|
||||
[Obsolete("pointLightDistance has been changed to normalMapDistance", true)]
|
||||
public float pointLightDistance => m_NormalMapDistance;
|
||||
|
||||
[Obsolete("pointLightQuality has been changed to normalMapQuality", true)]
|
||||
public NormalMapQuality pointLightQuality => m_NormalMapQuality;
|
||||
|
||||
|
||||
internal bool isPointLight => m_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,57 @@
|
|||
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;
|
||||
|
||||
/// <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.
|
||||
/// </summary>
|
||||
public float shapeLightFalloffSize => m_ShapeLightFalloffSize;
|
||||
|
||||
/// <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; }
|
||||
}
|
||||
|
||||
internal 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,522 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Unity.Collections;
|
||||
using Unity.Mathematics;
|
||||
using UnityEngine.Rendering.Universal.LibTessDotNet;
|
||||
using UnityEngine.U2D;
|
||||
|
||||
namespace UnityEngine.Rendering.Universal
|
||||
{
|
||||
internal static class LightUtility
|
||||
{
|
||||
public static bool CheckForChange(Light2D.LightType a, ref Light2D.LightType 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(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 void Tessellate(Tess tess, ElementType boundaryType, NativeArray<ushort> indices,
|
||||
NativeArray<LightMeshVertex> vertices, Color c, ref int VCount, ref int ICount)
|
||||
{
|
||||
tess.Tessellate(WindingRule.NonZero, boundaryType, 3);
|
||||
|
||||
var prevCount = VCount;
|
||||
var tessIndices = tess.Elements.Select(i => i);
|
||||
var tessVertices = tess.Vertices.Select(v =>
|
||||
new LightMeshVertex() { position = new float3(v.Position.X, v.Position.Y, 0), color = c });
|
||||
|
||||
foreach (var v in tessVertices)
|
||||
vertices[VCount++] = v;
|
||||
foreach (var i in tessIndices)
|
||||
indices[ICount++] = (ushort)(i + prevCount);
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
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));
|
||||
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)
|
||||
{
|
||||
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);
|
||||
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(2048.0f);
|
||||
clipOffset.AddPath(path, JoinType.jtRound, EndType.etClosedPolygon);
|
||||
clipOffset.Execute(ref solution, kClipperScale * offsetDistance, path.Count);
|
||||
if (solution.Count > 0)
|
||||
{
|
||||
for (int i = 0; i < solution[0].Count; ++i)
|
||||
output.Add(new Vector2(solution[0][i].X / kClipperScale, solution[0][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)
|
||||
{
|
||||
var ix = 0;
|
||||
var vcount = 0;
|
||||
var icount = 0;
|
||||
const float kClipperScale = 10000.0f;
|
||||
var mesh = light.lightMesh;
|
||||
|
||||
// todo Revisit this while we do Batching.
|
||||
var meshInteriorColor = new Color(0.0f, 0, 0, 1.0f);
|
||||
var meshExteriorColor = new Color(0.0f, 0, 0, 0.0f);
|
||||
var vertices = new NativeArray<LightMeshVertex>(shapePath.Length * 256, Allocator.Temp);
|
||||
var indices = new NativeArray<ushort>(shapePath.Length * 256, Allocator.Temp);
|
||||
|
||||
// Create shape geometry
|
||||
var inputPointCount = shapePath.Length;
|
||||
var inner = new ContourVertex[inputPointCount + 1];
|
||||
for (var i = 0; i < inputPointCount; ++i)
|
||||
inner[ix++] = new ContourVertex() { Position = new Vec3() { X = shapePath[i].x, Y = shapePath[i].y, Z = 0 } };
|
||||
inner[ix++] = inner[0];
|
||||
|
||||
var tess = new Tess();
|
||||
tess.AddContour(inner, ContourOrientation.CounterClockwise);
|
||||
Tessellate(tess, ElementType.Polygons, indices, vertices, meshInteriorColor, ref vcount, ref icount);
|
||||
|
||||
// Create falloff geometry
|
||||
List<IntPoint> path = new List<IntPoint>();
|
||||
for (var i = 0; i < inputPointCount; ++i)
|
||||
{
|
||||
var newPoint = new Vector2(inner[i].Position.X, inner[i].Position.Y) * kClipperScale;
|
||||
var addPoint = new IntPoint((System.Int64)(newPoint.x), (System.Int64)(newPoint.y));
|
||||
addPoint.N = i; addPoint.D = -1;
|
||||
path.Add(addPoint);
|
||||
}
|
||||
var lastPointIndex = inputPointCount - 1;
|
||||
|
||||
// Generate Bevels.
|
||||
List<List<IntPoint>> solution = new List<List<IntPoint>>();
|
||||
ClipperOffset clipOffset = new ClipperOffset(24.0f);
|
||||
clipOffset.AddPath(path, JoinType.jtRound, EndType.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);
|
||||
|
||||
// Tessellate.
|
||||
var bIndices = new NativeArray<ushort>(icount + (outPath.Count * 6) + 6, Allocator.Temp);
|
||||
for (int i = 0; i < icount; ++i)
|
||||
bIndices[i] = indices[i];
|
||||
var bVertices = new NativeArray<LightMeshVertex>(vcount + outPath.Count + inputPointCount, Allocator.Temp);
|
||||
for (int i = 0; i < vcount; ++i)
|
||||
bVertices[i] = vertices[i];
|
||||
|
||||
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)
|
||||
{
|
||||
bVertices[vcount++] = new LightMeshVertex()
|
||||
{
|
||||
position = new float3(inner[i].Position.X, inner[i].Position.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;
|
||||
|
||||
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;
|
||||
|
||||
bVertices[vcount++] = new LightMeshVertex()
|
||||
{
|
||||
position = new float3(currPoint.x, currPoint.y, 0),
|
||||
color = meshExteriorColor
|
||||
};
|
||||
|
||||
if (prevIndex != currIndex)
|
||||
{
|
||||
bIndices[icount++] = innerIndices[prevIndex];
|
||||
bIndices[icount++] = innerIndices[currIndex];
|
||||
bIndices[icount++] = (ushort)(vcount - 1);
|
||||
}
|
||||
|
||||
bIndices[icount++] = innerIndices[prevIndex];
|
||||
bIndices[icount++] = saveIndex;
|
||||
bIndices[icount++] = saveIndex = (ushort)(vcount - 1);
|
||||
prevIndex = currIndex;
|
||||
}
|
||||
|
||||
// Close the Loop.
|
||||
{
|
||||
bIndices[icount++] = pathStart;
|
||||
bIndices[icount++] = innerIndices[minPath];
|
||||
bIndices[icount++] = containsStart ? innerIndices[lastPointIndex] : saveIndex;
|
||||
|
||||
bIndices[icount++] = containsStart ? pathStart : saveIndex;
|
||||
bIndices[icount++] = containsStart ? saveIndex : innerIndices[minPath];
|
||||
bIndices[icount++] = containsStart ? innerIndices[lastPointIndex] : innerIndices[minPath - 1];
|
||||
}
|
||||
|
||||
TransferToMesh(bVertices, vcount, bIndices, icount, light);
|
||||
}
|
||||
else
|
||||
{
|
||||
TransferToMesh(vertices, vcount, indices, icount, light);
|
||||
}
|
||||
|
||||
return mesh.GetSubMesh(0).bounds;
|
||||
}
|
||||
|
||||
public static Bounds GenerateParametricMesh(Light2D light, float radius, float falloffDistance, float angle, int sides)
|
||||
{
|
||||
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, 0, 1);
|
||||
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, 0, 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)
|
||||
{
|
||||
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, 0, 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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: f74b40ff9aaa99a49a36a3c646ca5917
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
namespace UnityEngine.Rendering.Universal
|
||||
{
|
||||
internal interface IRenderPass2D
|
||||
{
|
||||
Renderer2DData rendererData { get; }
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
fileFormatVersion: 2
|
||||
guid: ed92912a74ef47789c37ffbe7c5672a8
|
||||
timeCreated: 1595414585
|
||||
|
|
@ -0,0 +1,46 @@
|
|||
namespace UnityEngine.Rendering.Universal
|
||||
{
|
||||
// Only to be used when Pixel Perfect Camera is present and it has Crop Frame X or Y enabled.
|
||||
// This pass simply clears BuiltinRenderTextureType.CameraTarget to black, so that the letterbox or pillarbox is black instead of garbage.
|
||||
// In the future this can be extended to draw a custom background image instead of just clearing.
|
||||
internal class PixelPerfectBackgroundPass : ScriptableRenderPass
|
||||
{
|
||||
private static readonly ProfilingSampler m_ProfilingScope = new ProfilingSampler("Pixel Perfect Background Pass");
|
||||
private bool m_SavedIsOrthographic;
|
||||
private float m_SavedOrthographicSize;
|
||||
|
||||
public PixelPerfectBackgroundPass(RenderPassEvent evt)
|
||||
{
|
||||
renderPassEvent = evt;
|
||||
}
|
||||
|
||||
public void Setup(bool savedIsOrthographic, float savedOrthographicSize)
|
||||
{
|
||||
m_SavedIsOrthographic = savedIsOrthographic;
|
||||
m_SavedOrthographicSize = savedOrthographicSize;
|
||||
}
|
||||
|
||||
public override void Execute(ScriptableRenderContext context, ref RenderingData renderingData)
|
||||
{
|
||||
var cmd = CommandBufferPool.Get();
|
||||
|
||||
using (new ProfilingScope(cmd, m_ProfilingScope))
|
||||
{
|
||||
CoreUtils.SetRenderTarget(
|
||||
cmd,
|
||||
BuiltinRenderTextureType.CameraTarget,
|
||||
RenderBufferLoadAction.DontCare,
|
||||
RenderBufferStoreAction.Store,
|
||||
ClearFlag.Color,
|
||||
Color.black);
|
||||
}
|
||||
|
||||
|
||||
context.ExecuteCommandBuffer(cmd);
|
||||
CommandBufferPool.Release(cmd);
|
||||
|
||||
renderingData.cameraData.camera.orthographic = m_SavedIsOrthographic;
|
||||
renderingData.cameraData.camera.orthographicSize = m_SavedOrthographicSize;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: d4ffb7da24670bf4687d34e0a1e19eaa
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
|
|
@ -0,0 +1,446 @@
|
|||
using System.Collections.Generic;
|
||||
using UnityEngine.Profiling;
|
||||
|
||||
namespace UnityEngine.Rendering.Universal
|
||||
{
|
||||
internal class Render2DLightingPass : ScriptableRenderPass, IRenderPass2D
|
||||
{
|
||||
private static readonly int k_HDREmulationScaleID = Shader.PropertyToID("_HDREmulationScale");
|
||||
private static readonly int k_InverseHDREmulationScaleID = Shader.PropertyToID("_InverseHDREmulationScale");
|
||||
private static readonly int k_UseSceneLightingID = Shader.PropertyToID("_UseSceneLighting");
|
||||
private static readonly int k_RendererColorID = Shader.PropertyToID("_RendererColor");
|
||||
private static readonly int k_CameraSortingLayerTextureID = Shader.PropertyToID("_CameraSortingLayerTexture");
|
||||
|
||||
private static readonly int[] k_ShapeLightTextureIDs =
|
||||
{
|
||||
Shader.PropertyToID("_ShapeLightTexture0"),
|
||||
Shader.PropertyToID("_ShapeLightTexture1"),
|
||||
Shader.PropertyToID("_ShapeLightTexture2"),
|
||||
Shader.PropertyToID("_ShapeLightTexture3")
|
||||
};
|
||||
|
||||
private static readonly ShaderTagId k_CombinedRenderingPassName = new ShaderTagId("Universal2D");
|
||||
private static readonly ShaderTagId k_NormalsRenderingPassName = new ShaderTagId("NormalsRendering");
|
||||
private static readonly ShaderTagId k_LegacyPassName = new ShaderTagId("SRPDefaultUnlit");
|
||||
private static readonly List<ShaderTagId> k_ShaderTags = new List<ShaderTagId>() { k_LegacyPassName, k_CombinedRenderingPassName };
|
||||
|
||||
private static readonly ProfilingSampler m_ProfilingDrawLights = new ProfilingSampler("Draw 2D Lights");
|
||||
private static readonly ProfilingSampler m_ProfilingDrawLightTextures = new ProfilingSampler("Draw 2D Lights Textures");
|
||||
private static readonly ProfilingSampler m_ProfilingDrawRenderers = new ProfilingSampler("Draw All Renderers");
|
||||
private static readonly ProfilingSampler m_ProfilingDrawLayerBatch = new ProfilingSampler("Draw Layer Batch");
|
||||
private static readonly ProfilingSampler m_ProfilingSamplerUnlit = new ProfilingSampler("Render Unlit");
|
||||
|
||||
Material m_BlitMaterial;
|
||||
Material m_SamplingMaterial;
|
||||
|
||||
private readonly Renderer2DData m_Renderer2DData;
|
||||
private bool m_NeedsDepth;
|
||||
private short m_CameraSortingLayerBoundsIndex;
|
||||
|
||||
public Render2DLightingPass(Renderer2DData rendererData, Material blitMaterial, Material samplingMaterial)
|
||||
{
|
||||
m_Renderer2DData = rendererData;
|
||||
m_BlitMaterial = blitMaterial;
|
||||
m_SamplingMaterial = samplingMaterial;
|
||||
|
||||
m_CameraSortingLayerBoundsIndex = GetCameraSortingLayerBoundsIndex();
|
||||
}
|
||||
|
||||
internal void Setup(bool useDepth)
|
||||
{
|
||||
m_NeedsDepth = useDepth;
|
||||
}
|
||||
|
||||
private void GetTransparencySortingMode(Camera camera, ref SortingSettings sortingSettings)
|
||||
{
|
||||
var mode = m_Renderer2DData.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 = m_Renderer2DData.transparencySortAxis;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private void CopyCameraSortingLayerRenderTexture(ScriptableRenderContext context, RenderingData renderingData, RenderBufferStoreAction mainTargetStoreAction)
|
||||
{
|
||||
var cmd = CommandBufferPool.Get();
|
||||
cmd.Clear();
|
||||
this.CreateCameraSortingLayerRenderTexture(renderingData, cmd, m_Renderer2DData.cameraSortingLayerDownsamplingMethod);
|
||||
|
||||
Material copyMaterial = m_Renderer2DData.cameraSortingLayerDownsamplingMethod == Downsampling._4xBox ? m_SamplingMaterial : m_BlitMaterial;
|
||||
RenderingUtils.Blit(cmd, colorAttachment, m_Renderer2DData.cameraSortingLayerRenderTarget.id, copyMaterial, 0, false, RenderBufferLoadAction.DontCare, RenderBufferStoreAction.Store, RenderBufferLoadAction.DontCare, RenderBufferStoreAction.DontCare);
|
||||
cmd.SetRenderTarget(colorAttachment, RenderBufferLoadAction.Load, mainTargetStoreAction,
|
||||
depthAttachment, RenderBufferLoadAction.Load, mainTargetStoreAction);
|
||||
cmd.SetGlobalTexture(k_CameraSortingLayerTextureID, m_Renderer2DData.cameraSortingLayerRenderTarget.id);
|
||||
context.ExecuteCommandBuffer(cmd);
|
||||
CommandBufferPool.Release(cmd);
|
||||
}
|
||||
|
||||
private short GetCameraSortingLayerBoundsIndex()
|
||||
{
|
||||
SortingLayer[] sortingLayers = Light2DManager.GetCachedSortingLayer();
|
||||
for (short i = 0; i < sortingLayers.Length; i++)
|
||||
{
|
||||
if (sortingLayers[i].id == m_Renderer2DData.cameraSortingLayerTextureBound)
|
||||
return (short)sortingLayers[i].value;
|
||||
}
|
||||
|
||||
return short.MinValue;
|
||||
}
|
||||
|
||||
private void DetermineWhenToResolve(int startIndex, int batchesDrawn, int batchCount, LayerBatch[] layerBatches,
|
||||
out int resolveDuringBatch, out bool resolveIsAfterCopy)
|
||||
{
|
||||
bool anyLightWithVolumetricShadows = false;
|
||||
var lights = m_Renderer2DData.lightCullResult.visibleLights;
|
||||
for (int i = 0; i < lights.Count; i++)
|
||||
{
|
||||
anyLightWithVolumetricShadows = lights[i].renderVolumetricShadows;
|
||||
if (anyLightWithVolumetricShadows)
|
||||
break;
|
||||
}
|
||||
|
||||
var lastVolumetricLightBatch = -1;
|
||||
if (anyLightWithVolumetricShadows)
|
||||
{
|
||||
for (int i = startIndex + batchesDrawn - 1; i >= startIndex; i--)
|
||||
{
|
||||
if (layerBatches[i].lightStats.totalVolumetricUsage > 0)
|
||||
{
|
||||
lastVolumetricLightBatch = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (m_Renderer2DData.useCameraSortingLayerTexture)
|
||||
{
|
||||
var cameraSortingLayerBoundsIndex = GetCameraSortingLayerBoundsIndex();
|
||||
var copyBatch = -1;
|
||||
for (int i = startIndex; i < startIndex + batchesDrawn; i++)
|
||||
{
|
||||
var layerBatch = layerBatches[i];
|
||||
if (cameraSortingLayerBoundsIndex >= layerBatch.layerRange.lowerBound && cameraSortingLayerBoundsIndex <= layerBatch.layerRange.upperBound)
|
||||
{
|
||||
copyBatch = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
resolveIsAfterCopy = copyBatch > lastVolumetricLightBatch;
|
||||
resolveDuringBatch = resolveIsAfterCopy ? copyBatch : lastVolumetricLightBatch;
|
||||
}
|
||||
else
|
||||
{
|
||||
resolveDuringBatch = lastVolumetricLightBatch;
|
||||
resolveIsAfterCopy = false;
|
||||
}
|
||||
}
|
||||
|
||||
private void Render(ScriptableRenderContext context, CommandBuffer cmd, ref RenderingData renderingData, ref FilteringSettings filterSettings, DrawingSettings drawSettings)
|
||||
{
|
||||
var activeDebugHandler = GetActiveDebugHandler(renderingData);
|
||||
if (activeDebugHandler != null)
|
||||
{
|
||||
RenderStateBlock renderStateBlock = new RenderStateBlock();
|
||||
activeDebugHandler.DrawWithDebugRenderState(context, cmd, ref renderingData, ref drawSettings, ref filterSettings, ref renderStateBlock,
|
||||
(ScriptableRenderContext ctx, ref RenderingData data, ref DrawingSettings ds, ref FilteringSettings fs, ref RenderStateBlock rsb) =>
|
||||
{
|
||||
ctx.DrawRenderers(data.cullResults, ref ds, ref fs, ref rsb);
|
||||
});
|
||||
}
|
||||
else
|
||||
{
|
||||
context.DrawRenderers(renderingData.cullResults, ref drawSettings, ref filterSettings);
|
||||
}
|
||||
}
|
||||
|
||||
private int DrawLayerBatches(
|
||||
LayerBatch[] layerBatches,
|
||||
int batchCount,
|
||||
int startIndex,
|
||||
CommandBuffer cmd,
|
||||
ScriptableRenderContext context,
|
||||
ref RenderingData renderingData,
|
||||
ref FilteringSettings filterSettings,
|
||||
ref DrawingSettings normalsDrawSettings,
|
||||
ref DrawingSettings drawSettings,
|
||||
ref RenderTextureDescriptor desc)
|
||||
{
|
||||
var debugHandler = GetActiveDebugHandler(renderingData);
|
||||
bool drawLights = debugHandler?.IsLightingActive ?? true;
|
||||
var batchesDrawn = 0;
|
||||
var rtCount = 0U;
|
||||
|
||||
// Draw lights
|
||||
using (new ProfilingScope(cmd, m_ProfilingDrawLights))
|
||||
{
|
||||
for (var i = startIndex; i < batchCount; ++i)
|
||||
{
|
||||
ref var layerBatch = ref layerBatches[i];
|
||||
|
||||
var blendStyleMask = layerBatch.lightStats.blendStylesUsed;
|
||||
var blendStyleCount = 0U;
|
||||
while (blendStyleMask > 0)
|
||||
{
|
||||
blendStyleCount += blendStyleMask & 1;
|
||||
blendStyleMask >>= 1;
|
||||
}
|
||||
|
||||
rtCount += blendStyleCount;
|
||||
|
||||
if (rtCount > LayerUtility.maxTextureCount)
|
||||
break;
|
||||
|
||||
batchesDrawn++;
|
||||
|
||||
if (layerBatch.lightStats.totalNormalMapUsage > 0)
|
||||
{
|
||||
filterSettings.sortingLayerRange = layerBatch.layerRange;
|
||||
var depthTarget = m_NeedsDepth ? depthAttachment : BuiltinRenderTextureType.None;
|
||||
this.RenderNormals(context, renderingData, normalsDrawSettings, filterSettings, depthTarget, cmd, layerBatch.lightStats);
|
||||
}
|
||||
|
||||
using (new ProfilingScope(cmd, m_ProfilingDrawLightTextures))
|
||||
{
|
||||
this.RenderLights(renderingData, cmd, layerBatch.startLayerID, ref layerBatch, ref desc);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Determine when to resolve in case we use MSAA
|
||||
var msaaEnabled = renderingData.cameraData.cameraTargetDescriptor.msaaSamples > 1;
|
||||
var isFinalBatchSet = startIndex + batchesDrawn >= batchCount;
|
||||
var resolveDuringBatch = -1;
|
||||
var resolveIsAfterCopy = false;
|
||||
if (msaaEnabled && isFinalBatchSet)
|
||||
DetermineWhenToResolve(startIndex, batchesDrawn, batchCount, layerBatches, out resolveDuringBatch, out resolveIsAfterCopy);
|
||||
|
||||
|
||||
// Draw renderers
|
||||
var blendStylesCount = m_Renderer2DData.lightBlendStyles.Length;
|
||||
using (new ProfilingScope(cmd, m_ProfilingDrawRenderers))
|
||||
{
|
||||
RenderBufferStoreAction initialStoreAction;
|
||||
if (msaaEnabled)
|
||||
initialStoreAction = resolveDuringBatch < startIndex ? RenderBufferStoreAction.Resolve : RenderBufferStoreAction.StoreAndResolve;
|
||||
else
|
||||
initialStoreAction = RenderBufferStoreAction.Store;
|
||||
cmd.SetRenderTarget(colorAttachment, RenderBufferLoadAction.Load, initialStoreAction, depthAttachment, RenderBufferLoadAction.Load, initialStoreAction);
|
||||
|
||||
for (var i = startIndex; i < startIndex + batchesDrawn; i++)
|
||||
{
|
||||
using (new ProfilingScope(cmd, m_ProfilingDrawLayerBatch))
|
||||
{
|
||||
// This is a local copy of the array element (it's a struct). Remember to add a ref here if you need to modify the real thing.
|
||||
var layerBatch = layerBatches[i];
|
||||
|
||||
if (layerBatch.lightStats.totalLights > 0)
|
||||
{
|
||||
for (var blendStyleIndex = 0; blendStyleIndex < blendStylesCount; blendStyleIndex++)
|
||||
{
|
||||
var blendStyleMask = (uint)(1 << blendStyleIndex);
|
||||
var blendStyleUsed = (layerBatch.lightStats.blendStylesUsed & blendStyleMask) > 0;
|
||||
|
||||
if (blendStyleUsed)
|
||||
{
|
||||
var identifier = layerBatch.GetRTId(cmd, desc, blendStyleIndex);
|
||||
cmd.SetGlobalTexture(k_ShapeLightTextureIDs[blendStyleIndex], identifier);
|
||||
}
|
||||
|
||||
RendererLighting.EnableBlendStyle(cmd, blendStyleIndex, blendStyleUsed);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for (var blendStyleIndex = 0; blendStyleIndex < k_ShapeLightTextureIDs.Length; blendStyleIndex++)
|
||||
{
|
||||
cmd.SetGlobalTexture(k_ShapeLightTextureIDs[blendStyleIndex], Texture2D.blackTexture);
|
||||
RendererLighting.EnableBlendStyle(cmd, blendStyleIndex, blendStyleIndex == 0);
|
||||
}
|
||||
}
|
||||
|
||||
context.ExecuteCommandBuffer(cmd);
|
||||
cmd.Clear();
|
||||
|
||||
short cameraSortingLayerBoundsIndex = GetCameraSortingLayerBoundsIndex();
|
||||
|
||||
RenderBufferStoreAction copyStoreAction;
|
||||
if (msaaEnabled)
|
||||
copyStoreAction = resolveDuringBatch == i && resolveIsAfterCopy ? RenderBufferStoreAction.Resolve : RenderBufferStoreAction.StoreAndResolve;
|
||||
else
|
||||
copyStoreAction = RenderBufferStoreAction.Store;
|
||||
// If our camera sorting layer texture bound is inside our batch we need to break up the DrawRenderers into two batches
|
||||
if (cameraSortingLayerBoundsIndex >= layerBatch.layerRange.lowerBound && cameraSortingLayerBoundsIndex < layerBatch.layerRange.upperBound && m_Renderer2DData.useCameraSortingLayerTexture)
|
||||
{
|
||||
filterSettings.sortingLayerRange = new SortingLayerRange(layerBatch.layerRange.lowerBound, cameraSortingLayerBoundsIndex);
|
||||
Render(context, cmd, ref renderingData, ref filterSettings, drawSettings);
|
||||
CopyCameraSortingLayerRenderTexture(context, renderingData, copyStoreAction);
|
||||
|
||||
filterSettings.sortingLayerRange = new SortingLayerRange((short)(cameraSortingLayerBoundsIndex + 1), layerBatch.layerRange.upperBound);
|
||||
Render(context, cmd, ref renderingData, ref filterSettings, drawSettings);
|
||||
}
|
||||
else
|
||||
{
|
||||
filterSettings.sortingLayerRange = new SortingLayerRange(layerBatch.layerRange.lowerBound, layerBatch.layerRange.upperBound);
|
||||
Render(context, cmd, ref renderingData, ref filterSettings, drawSettings);
|
||||
|
||||
if (cameraSortingLayerBoundsIndex == layerBatch.layerRange.upperBound && m_Renderer2DData.useCameraSortingLayerTexture)
|
||||
CopyCameraSortingLayerRenderTexture(context, renderingData, copyStoreAction);
|
||||
}
|
||||
|
||||
// Draw light volumes
|
||||
if (drawLights && (layerBatch.lightStats.totalVolumetricUsage > 0))
|
||||
{
|
||||
var sampleName = "Render 2D Light Volumes";
|
||||
cmd.BeginSample(sampleName);
|
||||
|
||||
RenderBufferStoreAction storeAction;
|
||||
if (msaaEnabled)
|
||||
storeAction = resolveDuringBatch == i && !resolveIsAfterCopy ? RenderBufferStoreAction.Resolve : RenderBufferStoreAction.StoreAndResolve;
|
||||
else
|
||||
storeAction = RenderBufferStoreAction.Store;
|
||||
this.RenderLightVolumes(renderingData, cmd, layerBatch.startLayerID, layerBatch.endLayerValue, colorAttachment, depthAttachment,
|
||||
RenderBufferStoreAction.Store, storeAction, false, m_Renderer2DData.lightCullResult.visibleLights);
|
||||
|
||||
cmd.EndSample(sampleName);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (var i = startIndex; i < startIndex + batchesDrawn; ++i)
|
||||
{
|
||||
ref var layerBatch = ref layerBatches[i];
|
||||
layerBatch.ReleaseRT(cmd);
|
||||
}
|
||||
|
||||
return batchesDrawn;
|
||||
}
|
||||
|
||||
public override void Execute(ScriptableRenderContext context, ref RenderingData renderingData)
|
||||
{
|
||||
var isLitView = true;
|
||||
|
||||
#if UNITY_EDITOR
|
||||
if (renderingData.cameraData.isSceneViewCamera)
|
||||
isLitView = UnityEditor.SceneView.currentDrawingSceneView.sceneLighting;
|
||||
|
||||
if (renderingData.cameraData.camera.cameraType == CameraType.Preview)
|
||||
isLitView = false;
|
||||
#endif
|
||||
var camera = renderingData.cameraData.camera;
|
||||
var filterSettings = new FilteringSettings();
|
||||
filterSettings.renderQueueRange = RenderQueueRange.all;
|
||||
filterSettings.layerMask = -1;
|
||||
filterSettings.renderingLayerMask = 0xFFFFFFFF;
|
||||
filterSettings.sortingLayerRange = SortingLayerRange.all;
|
||||
|
||||
LayerUtility.InitializeBudget(m_Renderer2DData.lightRenderTextureMemoryBudget);
|
||||
ShadowRendering.InitializeBudget(m_Renderer2DData.shadowRenderTextureMemoryBudget);
|
||||
|
||||
var isSceneLit = m_Renderer2DData.lightCullResult.IsSceneLit();
|
||||
if (isSceneLit)
|
||||
{
|
||||
var combinedDrawSettings = CreateDrawingSettings(k_ShaderTags, ref renderingData, SortingCriteria.CommonTransparent);
|
||||
var normalsDrawSettings = CreateDrawingSettings(k_NormalsRenderingPassName, ref renderingData, SortingCriteria.CommonTransparent);
|
||||
|
||||
var sortSettings = combinedDrawSettings.sortingSettings;
|
||||
GetTransparencySortingMode(camera, ref sortSettings);
|
||||
combinedDrawSettings.sortingSettings = sortSettings;
|
||||
normalsDrawSettings.sortingSettings = sortSettings;
|
||||
|
||||
var cmd = CommandBufferPool.Get();
|
||||
cmd.SetGlobalFloat(k_HDREmulationScaleID, m_Renderer2DData.hdrEmulationScale);
|
||||
cmd.SetGlobalFloat(k_InverseHDREmulationScaleID, 1.0f / m_Renderer2DData.hdrEmulationScale);
|
||||
cmd.SetGlobalFloat(k_UseSceneLightingID, isLitView ? 1.0f : 0.0f);
|
||||
cmd.SetGlobalColor(k_RendererColorID, Color.white);
|
||||
this.SetShapeLightShaderGlobals(cmd);
|
||||
|
||||
var desc = this.GetBlendStyleRenderTextureDesc(renderingData);
|
||||
|
||||
var layerBatches = LayerUtility.CalculateBatches(m_Renderer2DData.lightCullResult, out var batchCount);
|
||||
var batchesDrawn = 0;
|
||||
|
||||
for (var i = 0; i < batchCount; i += batchesDrawn)
|
||||
batchesDrawn = DrawLayerBatches(layerBatches, batchCount, i, cmd, context, ref renderingData, ref filterSettings, ref normalsDrawSettings, ref combinedDrawSettings, ref desc);
|
||||
|
||||
this.DisableAllKeywords(cmd);
|
||||
this.ReleaseRenderTextures(cmd);
|
||||
context.ExecuteCommandBuffer(cmd);
|
||||
CommandBufferPool.Release(cmd);
|
||||
}
|
||||
else
|
||||
{
|
||||
var unlitDrawSettings = CreateDrawingSettings(k_ShaderTags, ref renderingData, SortingCriteria.CommonTransparent);
|
||||
var msaaEnabled = renderingData.cameraData.cameraTargetDescriptor.msaaSamples > 1;
|
||||
var storeAction = msaaEnabled ? RenderBufferStoreAction.Resolve : RenderBufferStoreAction.Store;
|
||||
|
||||
var sortSettings = unlitDrawSettings.sortingSettings;
|
||||
GetTransparencySortingMode(camera, ref sortSettings);
|
||||
unlitDrawSettings.sortingSettings = sortSettings;
|
||||
|
||||
var cmd = CommandBufferPool.Get();
|
||||
using (new ProfilingScope(cmd, m_ProfilingSamplerUnlit))
|
||||
{
|
||||
cmd.SetRenderTarget(colorAttachment, RenderBufferLoadAction.Load, storeAction, depthAttachment, RenderBufferLoadAction.Load, storeAction);
|
||||
|
||||
cmd.SetGlobalFloat(k_UseSceneLightingID, isLitView ? 1.0f : 0.0f);
|
||||
cmd.SetGlobalColor(k_RendererColorID, Color.white);
|
||||
|
||||
for (var blendStyleIndex = 0; blendStyleIndex < k_ShapeLightTextureIDs.Length; blendStyleIndex++)
|
||||
{
|
||||
if (blendStyleIndex == 0)
|
||||
cmd.SetGlobalTexture(k_ShapeLightTextureIDs[blendStyleIndex], Texture2D.blackTexture);
|
||||
|
||||
RendererLighting.EnableBlendStyle(cmd, blendStyleIndex, blendStyleIndex == 0);
|
||||
}
|
||||
}
|
||||
|
||||
this.DisableAllKeywords(cmd);
|
||||
context.ExecuteCommandBuffer(cmd);
|
||||
|
||||
Profiler.BeginSample("Render Sprites Unlit");
|
||||
if (m_Renderer2DData.useCameraSortingLayerTexture)
|
||||
{
|
||||
filterSettings.sortingLayerRange = new SortingLayerRange(short.MinValue, m_CameraSortingLayerBoundsIndex);
|
||||
Render(context, cmd, ref renderingData, ref filterSettings, unlitDrawSettings);
|
||||
|
||||
CopyCameraSortingLayerRenderTexture(context, renderingData, storeAction);
|
||||
|
||||
filterSettings.sortingLayerRange = new SortingLayerRange(m_CameraSortingLayerBoundsIndex, short.MaxValue);
|
||||
Render(context, cmd, ref renderingData, ref filterSettings, unlitDrawSettings);
|
||||
}
|
||||
else
|
||||
{
|
||||
Render(context, cmd, ref renderingData, ref filterSettings, unlitDrawSettings);
|
||||
}
|
||||
Profiler.EndSample();
|
||||
|
||||
CommandBufferPool.Release(cmd);
|
||||
}
|
||||
|
||||
filterSettings.sortingLayerRange = SortingLayerRange.all;
|
||||
RenderingUtils.RenderObjectsWithError(context, ref renderingData.cullResults, camera, filterSettings, SortingCriteria.None);
|
||||
}
|
||||
|
||||
Renderer2DData IRenderPass2D.rendererData
|
||||
{
|
||||
get { return m_Renderer2DData; }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: c4cbc2d2d710ce84696437385ac8866c
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
fileFormatVersion: 2
|
||||
guid: f895f2b1dbb2da84e82eedb22e2620f7
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
|
|
@ -0,0 +1,154 @@
|
|||
using Unity.Mathematics;
|
||||
|
||||
namespace UnityEngine.Rendering.Universal
|
||||
{
|
||||
internal struct LayerBatch
|
||||
{
|
||||
public int startLayerID;
|
||||
public int endLayerValue;
|
||||
public SortingLayerRange layerRange;
|
||||
public LightStats lightStats;
|
||||
private unsafe fixed int renderTargetIds[4];
|
||||
private unsafe fixed bool renderTargetUsed[4];
|
||||
|
||||
public void InitRTIds(int index)
|
||||
{
|
||||
for (var i = 0; i < 4; i++)
|
||||
{
|
||||
unsafe
|
||||
{
|
||||
renderTargetUsed[i] = false;
|
||||
renderTargetIds[i] = Shader.PropertyToID($"_LightTexture_{index}_{i}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public RenderTargetIdentifier GetRTId(CommandBuffer cmd, RenderTextureDescriptor desc, int index)
|
||||
{
|
||||
unsafe
|
||||
{
|
||||
if (!renderTargetUsed[index])
|
||||
{
|
||||
cmd.GetTemporaryRT(renderTargetIds[index], desc, FilterMode.Bilinear);
|
||||
renderTargetUsed[index] = true;
|
||||
}
|
||||
return new RenderTargetIdentifier(renderTargetIds[index]);
|
||||
}
|
||||
}
|
||||
|
||||
public void ReleaseRT(CommandBuffer cmd)
|
||||
{
|
||||
for (var i = 0; i < 4; i++)
|
||||
{
|
||||
unsafe
|
||||
{
|
||||
if (!renderTargetUsed[i])
|
||||
continue;
|
||||
|
||||
cmd.ReleaseTemporaryRT(renderTargetIds[i]);
|
||||
renderTargetUsed[i] = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal static class LayerUtility
|
||||
{
|
||||
private static LayerBatch[] s_LayerBatches;
|
||||
public static uint maxTextureCount { get; private set; }
|
||||
|
||||
public static void InitializeBudget(uint maxTextureCount)
|
||||
{
|
||||
LayerUtility.maxTextureCount = math.max(4, maxTextureCount);
|
||||
}
|
||||
|
||||
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, or if they are lit but this is a shadow casting light then don't batch.
|
||||
bool lightCastsShadows = (light.lightType != Light2D.LightType.Global && light.shadowsEnabled);
|
||||
if ((light.IsLitLayer(layerId1) != light.IsLitLayer(layerId2)) || (light.IsLitLayer(layerId1) && lightCastsShadows))
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private static int FindUpperBoundInBatch(int startLayerIndex, SortingLayer[] sortingLayers, ILight2DCullResult lightCullResult)
|
||||
{
|
||||
// start checking at the next layer
|
||||
for (var i = startLayerIndex + 1; i < sortingLayers.Length; i++)
|
||||
{
|
||||
if (!CanBatchLightsInLayer(startLayerIndex, i, sortingLayers, lightCullResult))
|
||||
return i - 1;
|
||||
}
|
||||
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++)
|
||||
{
|
||||
ref var layerBatch = ref s_LayerBatches[i];
|
||||
layerBatch.InitRTIds(i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static LayerBatch[] CalculateBatches(ILight2DCullResult lightCullResult, out int batchCount)
|
||||
{
|
||||
var cachedSortingLayers = Light2DManager.GetCachedSortingLayer();
|
||||
InitializeBatchInfos(cachedSortingLayers);
|
||||
|
||||
batchCount = 0;
|
||||
for (var i = 0; i < cachedSortingLayers.Length;)
|
||||
{
|
||||
var layerToRender = cachedSortingLayers[i].id;
|
||||
var lightStats = lightCullResult.GetLightStatsByLayer(layerToRender);
|
||||
ref var layerBatch = ref s_LayerBatches[batchCount++];
|
||||
|
||||
// Find the highest layer that share the same set of lights as this layer.
|
||||
var upperLayerInBatch = FindUpperBoundInBatch(i, cachedSortingLayers, lightCullResult);
|
||||
|
||||
// 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);
|
||||
|
||||
layerBatch.startLayerID = layerToRender;
|
||||
layerBatch.endLayerValue = endLayerValue;
|
||||
layerBatch.layerRange = sortingLayerRange;
|
||||
layerBatch.lightStats = lightStats;
|
||||
|
||||
i = upperLayerInBatch + 1;
|
||||
}
|
||||
|
||||
return s_LayerBatches;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 837cbe89c0a94e149f5b494ae9c69a51
|
||||
timeCreated: 1599108159
|
||||
|
|
@ -0,0 +1,96 @@
|
|||
using UnityEngine.Experimental.Rendering;
|
||||
|
||||
namespace UnityEngine.Rendering.Universal
|
||||
{
|
||||
internal static class Light2DLookupTexture
|
||||
{
|
||||
private static Texture2D s_PointLightLookupTexture;
|
||||
|
||||
public static Texture GetLightLookupTexture()
|
||||
{
|
||||
if (s_PointLightLookupTexture == null)
|
||||
s_PointLightLookupTexture = CreatePointLightLookupTexture();
|
||||
return s_PointLightLookupTexture;
|
||||
}
|
||||
|
||||
private static Texture2D CreatePointLightLookupTexture()
|
||||
{
|
||||
const int WIDTH = 256;
|
||||
const int HEIGHT = 256;
|
||||
|
||||
var textureFormat = GraphicsFormat.R8G8B8A8_UNorm;
|
||||
if (RenderingUtils.SupportsGraphicsFormat(GraphicsFormat.R16G16B16A16_SFloat, FormatUsage.SetPixels))
|
||||
textureFormat = GraphicsFormat.R16G16B16A16_SFloat;
|
||||
else if (RenderingUtils.SupportsGraphicsFormat(GraphicsFormat.R32G32B32A32_SFloat, FormatUsage.SetPixels))
|
||||
textureFormat = GraphicsFormat.R32G32B32A32_SFloat;
|
||||
|
||||
var texture = new Texture2D(WIDTH, HEIGHT, textureFormat, TextureCreationFlags.None);
|
||||
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 s_FalloffLookupTexture;
|
||||
//#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,684 @@
|
|||
using System.Collections.Generic;
|
||||
using UnityEngine.Experimental.Rendering;
|
||||
using Unity.Collections;
|
||||
|
||||
namespace UnityEngine.Rendering.Universal
|
||||
{
|
||||
internal static class RendererLighting
|
||||
{
|
||||
private static readonly ProfilingSampler m_ProfilingSampler = new ProfilingSampler("Draw Normals");
|
||||
private static readonly ShaderTagId k_NormalsRenderingPassName = new ShaderTagId("NormalsRendering");
|
||||
private static readonly Color k_NormalClearColor = new Color(0.5f, 0.5f, 0.5f, 1.0f);
|
||||
private static readonly string k_SpriteLightKeyword = "SPRITE_LIGHT";
|
||||
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_UseAdditiveBlendingKeyword = "USE_ADDITIVE_BLENDING";
|
||||
|
||||
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")
|
||||
};
|
||||
|
||||
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_FalloffIntensityID = Shader.PropertyToID("_FalloffIntensity");
|
||||
private static readonly int k_FalloffDistanceID = Shader.PropertyToID("_FalloffDistance");
|
||||
private static readonly int k_LightColorID = Shader.PropertyToID("_LightColor");
|
||||
private static readonly int k_VolumeOpacityID = Shader.PropertyToID("_VolumeOpacity");
|
||||
private static readonly int k_CookieTexID = Shader.PropertyToID("_CookieTex");
|
||||
private static readonly int k_FalloffLookupID = Shader.PropertyToID("_FalloffLookup");
|
||||
private static readonly int k_LightPositionID = Shader.PropertyToID("_LightPosition");
|
||||
private static readonly int k_LightInvMatrixID = Shader.PropertyToID("_LightInvMatrix");
|
||||
private static readonly int k_InnerRadiusMultID = Shader.PropertyToID("_InnerRadiusMult");
|
||||
private static readonly int k_OuterAngleID = Shader.PropertyToID("_OuterAngle");
|
||||
private static readonly int k_InnerAngleMultID = Shader.PropertyToID("_InnerAngleMult");
|
||||
private static readonly int k_LightLookupID = Shader.PropertyToID("_LightLookup");
|
||||
private static readonly int k_IsFullSpotlightID = Shader.PropertyToID("_IsFullSpotlight");
|
||||
private static readonly int k_LightZDistanceID = Shader.PropertyToID("_LightZDistance");
|
||||
private static readonly int k_PointLightCookieTexID = Shader.PropertyToID("_PointLightCookieTex");
|
||||
|
||||
private static GraphicsFormat GetRenderTextureFormat()
|
||||
{
|
||||
if (!s_HasSetupRenderTextureFormatToUse)
|
||||
{
|
||||
if (SystemInfo.IsFormatSupported(GraphicsFormat.B10G11R11_UFloatPack32, FormatUsage.Linear | FormatUsage.Render))
|
||||
s_RenderTextureFormatToUse = GraphicsFormat.B10G11R11_UFloatPack32;
|
||||
else if (SystemInfo.IsFormatSupported(GraphicsFormat.R16G16B16A16_SFloat, FormatUsage.Linear | FormatUsage.Render))
|
||||
s_RenderTextureFormatToUse = GraphicsFormat.R16G16B16A16_SFloat;
|
||||
|
||||
s_HasSetupRenderTextureFormatToUse = true;
|
||||
}
|
||||
|
||||
return s_RenderTextureFormatToUse;
|
||||
}
|
||||
|
||||
public static void CreateNormalMapRenderTexture(this IRenderPass2D pass, RenderingData renderingData, CommandBuffer cmd, float renderScale)
|
||||
{
|
||||
if (renderScale != pass.rendererData.normalsRenderTargetScale)
|
||||
{
|
||||
if (pass.rendererData.isNormalsRenderTargetValid)
|
||||
{
|
||||
cmd.ReleaseTemporaryRT(pass.rendererData.normalsRenderTarget.id);
|
||||
}
|
||||
|
||||
pass.rendererData.isNormalsRenderTargetValid = true;
|
||||
pass.rendererData.normalsRenderTargetScale = renderScale;
|
||||
|
||||
var descriptor = new RenderTextureDescriptor(
|
||||
(int)(renderingData.cameraData.cameraTargetDescriptor.width * renderScale),
|
||||
(int)(renderingData.cameraData.cameraTargetDescriptor.height * renderScale));
|
||||
|
||||
descriptor.graphicsFormat = GetRenderTextureFormat();
|
||||
descriptor.useMipMap = false;
|
||||
descriptor.autoGenerateMips = false;
|
||||
descriptor.depthBufferBits = pass.rendererData.useDepthStencilBuffer ? 32 : 0;
|
||||
descriptor.msaaSamples = renderingData.cameraData.cameraTargetDescriptor.msaaSamples;
|
||||
descriptor.dimension = TextureDimension.Tex2D;
|
||||
|
||||
cmd.GetTemporaryRT(pass.rendererData.normalsRenderTarget.id, descriptor, FilterMode.Bilinear);
|
||||
}
|
||||
}
|
||||
|
||||
public static RenderTextureDescriptor GetBlendStyleRenderTextureDesc(this IRenderPass2D pass, RenderingData renderingData)
|
||||
{
|
||||
var renderTextureScale = Mathf.Clamp(pass.rendererData.lightRenderTextureScale, 0.01f, 1.0f);
|
||||
var width = (int)(renderingData.cameraData.cameraTargetDescriptor.width * renderTextureScale);
|
||||
var height = (int)(renderingData.cameraData.cameraTargetDescriptor.height * renderTextureScale);
|
||||
|
||||
var descriptor = new RenderTextureDescriptor(width, height);
|
||||
descriptor.graphicsFormat = GetRenderTextureFormat();
|
||||
descriptor.useMipMap = false;
|
||||
descriptor.autoGenerateMips = false;
|
||||
descriptor.depthBufferBits = 0;
|
||||
descriptor.msaaSamples = 1;
|
||||
descriptor.dimension = TextureDimension.Tex2D;
|
||||
|
||||
return descriptor;
|
||||
}
|
||||
|
||||
public static void CreateCameraSortingLayerRenderTexture(this IRenderPass2D pass, RenderingData renderingData, CommandBuffer cmd, Downsampling downsamplingMethod)
|
||||
{
|
||||
var renderTextureScale = 1.0f;
|
||||
if (downsamplingMethod == Downsampling._2xBilinear)
|
||||
renderTextureScale = 0.5f;
|
||||
else if (downsamplingMethod == Downsampling._4xBox || downsamplingMethod == Downsampling._4xBilinear)
|
||||
renderTextureScale = 0.25f;
|
||||
|
||||
var width = (int)(renderingData.cameraData.cameraTargetDescriptor.width * renderTextureScale);
|
||||
var height = (int)(renderingData.cameraData.cameraTargetDescriptor.height * renderTextureScale);
|
||||
|
||||
var descriptor = new RenderTextureDescriptor(width, height);
|
||||
descriptor.graphicsFormat = renderingData.cameraData.cameraTargetDescriptor.graphicsFormat;
|
||||
descriptor.useMipMap = false;
|
||||
descriptor.autoGenerateMips = false;
|
||||
descriptor.depthBufferBits = 0;
|
||||
descriptor.msaaSamples = 1;
|
||||
descriptor.dimension = TextureDimension.Tex2D;
|
||||
|
||||
cmd.GetTemporaryRT(pass.rendererData.cameraSortingLayerRenderTarget.id, descriptor, FilterMode.Bilinear);
|
||||
}
|
||||
|
||||
public static void EnableBlendStyle(CommandBuffer cmd, int blendStyleIndex, bool enabled)
|
||||
{
|
||||
var keyword = k_UseBlendStyleKeywords[blendStyleIndex];
|
||||
|
||||
if (enabled)
|
||||
cmd.EnableShaderKeyword(keyword);
|
||||
else
|
||||
cmd.DisableShaderKeyword(keyword);
|
||||
}
|
||||
|
||||
public static void DisableAllKeywords(this IRenderPass2D pass, CommandBuffer cmd)
|
||||
{
|
||||
foreach (var keyword in k_UseBlendStyleKeywords)
|
||||
{
|
||||
cmd.DisableShaderKeyword(keyword);
|
||||
}
|
||||
}
|
||||
|
||||
public static void ReleaseRenderTextures(this IRenderPass2D pass, CommandBuffer cmd)
|
||||
{
|
||||
pass.rendererData.isNormalsRenderTargetValid = false;
|
||||
pass.rendererData.normalsRenderTargetScale = 0.0f;
|
||||
cmd.ReleaseTemporaryRT(pass.rendererData.normalsRenderTarget.id);
|
||||
cmd.ReleaseTemporaryRT(pass.rendererData.shadowsRenderTarget.id);
|
||||
cmd.ReleaseTemporaryRT(pass.rendererData.cameraSortingLayerRenderTarget.id);
|
||||
}
|
||||
|
||||
public static void DrawPointLight(CommandBuffer cmd, Light2D light, Mesh lightMesh, Material material)
|
||||
{
|
||||
var scale = new Vector3(light.pointLightOuterRadius, light.pointLightOuterRadius, light.pointLightOuterRadius);
|
||||
var matrix = Matrix4x4.TRS(light.transform.position, light.transform.rotation, scale);
|
||||
cmd.DrawMesh(lightMesh, matrix, material);
|
||||
}
|
||||
|
||||
|
||||
private static bool CanCastShadows(Light2D light, int layerToRender)
|
||||
{
|
||||
return light.shadowsEnabled && light.shadowIntensity > 0 && light.IsLitLayer(layerToRender);
|
||||
}
|
||||
|
||||
private static bool CanCastVolumetricShadows(Light2D light, int endLayerValue)
|
||||
{
|
||||
var topMostLayerValue = light.GetTopMostLitLayer();
|
||||
return light.volumetricShadowsEnabled && light.shadowVolumeIntensity > 0 && topMostLayerValue == endLayerValue;
|
||||
}
|
||||
|
||||
|
||||
private static void RenderLightSet(IRenderPass2D pass, RenderingData renderingData, int blendStyleIndex, CommandBuffer cmd, int layerToRender, RenderTargetIdentifier renderTexture, List<Light2D> lights)
|
||||
{
|
||||
var maxShadowLightCount = ShadowRendering.maxTextureCount * 4;
|
||||
var requiresRTInit = true;
|
||||
|
||||
// This case should never happen, but if it does it may cause an infinite loop later.
|
||||
if (maxShadowLightCount < 1)
|
||||
{
|
||||
Debug.LogError("maxShadowTextureCount cannot be less than 1");
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
NativeArray<bool> doesLightAtIndexHaveShadows = new NativeArray<bool>(lights.Count, Allocator.Temp);
|
||||
|
||||
// Break up light rendering into batches for the purpose of shadow casting
|
||||
var lightIndex = 0;
|
||||
while (lightIndex < lights.Count)
|
||||
{
|
||||
var remainingLights = (uint)lights.Count - lightIndex;
|
||||
var batchedLights = 0;
|
||||
|
||||
// Add lights to our batch until the number of shadow textures reach the maxShadowTextureCount
|
||||
int shadowLightCount = 0;
|
||||
while (batchedLights < remainingLights && shadowLightCount < maxShadowLightCount)
|
||||
{
|
||||
int curLightIndex = lightIndex + batchedLights;
|
||||
var light = lights[curLightIndex];
|
||||
if (CanCastShadows(light, layerToRender))
|
||||
{
|
||||
doesLightAtIndexHaveShadows[curLightIndex] = false;
|
||||
if (ShadowRendering.PrerenderShadows(pass, renderingData, cmd, layerToRender, light, shadowLightCount, light.shadowIntensity))
|
||||
{
|
||||
doesLightAtIndexHaveShadows[curLightIndex] = true;
|
||||
shadowLightCount++;
|
||||
}
|
||||
}
|
||||
batchedLights++;
|
||||
}
|
||||
|
||||
|
||||
// Set the current RT to the light RT
|
||||
if (shadowLightCount > 0 || requiresRTInit)
|
||||
{
|
||||
cmd.SetRenderTarget(renderTexture, RenderBufferLoadAction.Load, RenderBufferStoreAction.Store, RenderBufferLoadAction.DontCare, RenderBufferStoreAction.DontCare);
|
||||
requiresRTInit = false;
|
||||
}
|
||||
|
||||
// Render all the lights.
|
||||
shadowLightCount = 0;
|
||||
for (var lightIndexOffset = 0; lightIndexOffset < batchedLights; lightIndexOffset++)
|
||||
{
|
||||
var light = lights[(int)(lightIndex + lightIndexOffset)];
|
||||
|
||||
if (light != null &&
|
||||
light.lightType != Light2D.LightType.Global &&
|
||||
light.blendStyleIndex == blendStyleIndex &&
|
||||
light.IsLitLayer(layerToRender))
|
||||
{
|
||||
// Render light
|
||||
var lightMaterial = pass.rendererData.GetLightMaterial(light, false);
|
||||
if (lightMaterial == null)
|
||||
continue;
|
||||
|
||||
var lightMesh = light.lightMesh;
|
||||
if (lightMesh == null)
|
||||
continue;
|
||||
|
||||
// Set the shadow texture to read from
|
||||
if (doesLightAtIndexHaveShadows[lightIndex + lightIndexOffset])
|
||||
ShadowRendering.SetGlobalShadowTexture(cmd, light, shadowLightCount++);
|
||||
else
|
||||
ShadowRendering.DisableGlobalShadowTexture(cmd);
|
||||
|
||||
|
||||
if (light.lightType == Light2D.LightType.Sprite && light.lightCookieSprite != null && light.lightCookieSprite.texture != null)
|
||||
cmd.SetGlobalTexture(k_CookieTexID, light.lightCookieSprite.texture);
|
||||
|
||||
SetGeneralLightShaderGlobals(pass, cmd, light);
|
||||
|
||||
if (light.normalMapQuality != Light2D.NormalMapQuality.Disabled || light.lightType == Light2D.LightType.Point)
|
||||
SetPointLightShaderGlobals(pass, cmd, light);
|
||||
|
||||
// Light code could be combined...
|
||||
if (light.lightType == (Light2D.LightType)Light2D.DeprecatedLightType.Parametric || light.lightType == Light2D.LightType.Freeform || light.lightType == Light2D.LightType.Sprite)
|
||||
{
|
||||
cmd.DrawMesh(lightMesh, light.transform.localToWorldMatrix, lightMaterial);
|
||||
}
|
||||
else if (light.lightType == Light2D.LightType.Point)
|
||||
{
|
||||
DrawPointLight(cmd, light, lightMesh, lightMaterial);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Release all of the temporary shadow textures
|
||||
for (var releaseIndex = shadowLightCount - 1; releaseIndex >= 0; releaseIndex--)
|
||||
ShadowRendering.ReleaseShadowRenderTexture(cmd, releaseIndex);
|
||||
|
||||
lightIndex += batchedLights;
|
||||
}
|
||||
|
||||
doesLightAtIndexHaveShadows.Dispose();
|
||||
}
|
||||
|
||||
public static void RenderLightVolumes(this IRenderPass2D pass, RenderingData renderingData, CommandBuffer cmd, int layerToRender, int endLayerValue,
|
||||
RenderTargetIdentifier renderTexture, RenderTargetIdentifier depthTexture, RenderBufferStoreAction intermediateStoreAction,
|
||||
RenderBufferStoreAction finalStoreAction, bool requiresRTInit, List<Light2D> lights)
|
||||
{
|
||||
var maxShadowLightCount = ShadowRendering.maxTextureCount * 4; // Now encodes shadows into RGBA as well as seperate textures
|
||||
|
||||
NativeArray<bool> doesLightAtIndexHaveShadows = new NativeArray<bool>(lights.Count, Allocator.Temp);
|
||||
|
||||
// This case should never happen, but if it does it may cause an infinite loop later.
|
||||
if (maxShadowLightCount < 1)
|
||||
{
|
||||
Debug.LogError("maxShadowLightCount cannot be less than 1");
|
||||
return;
|
||||
}
|
||||
|
||||
// Determine last light with volumetric shadows to be rendered if we want to use a different store action after using rendering its volumetric shadows
|
||||
int useFinalStoreActionAfter = lights.Count;
|
||||
if (intermediateStoreAction != finalStoreAction)
|
||||
{
|
||||
for (int i = lights.Count - 1; i >= 0; i--)
|
||||
{
|
||||
if (lights[i].renderVolumetricShadows)
|
||||
{
|
||||
useFinalStoreActionAfter = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Break up light rendering into batches for the purpose of shadow casting
|
||||
var lightIndex = 0;
|
||||
while (lightIndex < lights.Count)
|
||||
{
|
||||
var remainingLights = (uint)lights.Count - lightIndex;
|
||||
var batchedLights = 0;
|
||||
|
||||
// Add lights to our batch until the number of shadow textures reach the maxShadowTextureCount
|
||||
var shadowLightCount = 0;
|
||||
while (batchedLights < remainingLights && shadowLightCount < maxShadowLightCount)
|
||||
{
|
||||
int curLightIndex = lightIndex + batchedLights;
|
||||
var light = lights[curLightIndex];
|
||||
|
||||
if (CanCastVolumetricShadows(light, endLayerValue))
|
||||
{
|
||||
doesLightAtIndexHaveShadows[curLightIndex] = false;
|
||||
if (ShadowRendering.PrerenderShadows(pass, renderingData, cmd, layerToRender, light, shadowLightCount, light.shadowVolumeIntensity))
|
||||
{
|
||||
doesLightAtIndexHaveShadows[curLightIndex] = true;
|
||||
shadowLightCount++;
|
||||
}
|
||||
}
|
||||
batchedLights++;
|
||||
}
|
||||
|
||||
// Set the current RT to the light RT
|
||||
if (shadowLightCount > 0 || requiresRTInit)
|
||||
{
|
||||
var storeAction = lightIndex + batchedLights >= useFinalStoreActionAfter ? finalStoreAction : intermediateStoreAction;
|
||||
cmd.SetRenderTarget(renderTexture, RenderBufferLoadAction.Load, storeAction, depthTexture, RenderBufferLoadAction.Load, storeAction);
|
||||
requiresRTInit = false;
|
||||
}
|
||||
|
||||
// Render all the lights.
|
||||
shadowLightCount = 0;
|
||||
for (var lightIndexOffset = 0; lightIndexOffset < batchedLights; lightIndexOffset++)
|
||||
{
|
||||
var light = lights[(int)(lightIndex + lightIndexOffset)];
|
||||
|
||||
if (light.lightType == Light2D.LightType.Global)
|
||||
continue;
|
||||
|
||||
if (light.volumeIntensity <= 0.0f || !light.volumeIntensityEnabled)
|
||||
continue;
|
||||
|
||||
var topMostLayerValue = light.GetTopMostLitLayer();
|
||||
if (endLayerValue == topMostLayerValue) // this implies the layer is correct
|
||||
{
|
||||
var lightVolumeMaterial = pass.rendererData.GetLightMaterial(light, true);
|
||||
var lightMesh = light.lightMesh;
|
||||
|
||||
// Set the shadow texture to read from.
|
||||
if (doesLightAtIndexHaveShadows[lightIndex + lightIndexOffset])
|
||||
ShadowRendering.SetGlobalShadowTexture(cmd, light, shadowLightCount++);
|
||||
else
|
||||
ShadowRendering.DisableGlobalShadowTexture(cmd);
|
||||
|
||||
if (light.lightType == Light2D.LightType.Sprite && light.lightCookieSprite != null && light.lightCookieSprite.texture != null)
|
||||
cmd.SetGlobalTexture(k_CookieTexID, light.lightCookieSprite.texture);
|
||||
|
||||
SetGeneralLightShaderGlobals(pass, cmd, light);
|
||||
|
||||
// Is this needed
|
||||
if (light.normalMapQuality != Light2D.NormalMapQuality.Disabled || light.lightType == Light2D.LightType.Point)
|
||||
SetPointLightShaderGlobals(pass, cmd, light);
|
||||
|
||||
// Could be combined...
|
||||
if (light.lightType == Light2D.LightType.Parametric || light.lightType == Light2D.LightType.Freeform || light.lightType == Light2D.LightType.Sprite)
|
||||
{
|
||||
cmd.DrawMesh(lightMesh, light.transform.localToWorldMatrix, lightVolumeMaterial);
|
||||
}
|
||||
else if (light.lightType == Light2D.LightType.Point)
|
||||
{
|
||||
DrawPointLight(cmd, light, lightMesh, lightVolumeMaterial);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Release all of the temporary shadow textures
|
||||
for (var releaseIndex = shadowLightCount - 1; releaseIndex >= 0; releaseIndex--)
|
||||
ShadowRendering.ReleaseShadowRenderTexture(cmd, releaseIndex);
|
||||
|
||||
lightIndex += batchedLights;
|
||||
}
|
||||
|
||||
doesLightAtIndexHaveShadows.Dispose();
|
||||
}
|
||||
|
||||
public static void SetShapeLightShaderGlobals(this IRenderPass2D pass, CommandBuffer cmd)
|
||||
{
|
||||
for (var i = 0; i < pass.rendererData.lightBlendStyles.Length; i++)
|
||||
{
|
||||
var blendStyle = pass.rendererData.lightBlendStyles[i];
|
||||
if (i >= k_BlendFactorsPropIDs.Length)
|
||||
break;
|
||||
|
||||
cmd.SetGlobalVector(k_BlendFactorsPropIDs[i], blendStyle.blendFactors);
|
||||
cmd.SetGlobalVector(k_MaskFilterPropIDs[i], blendStyle.maskTextureChannelFilter.mask);
|
||||
cmd.SetGlobalVector(k_InvertedFilterPropIDs[i], blendStyle.maskTextureChannelFilter.inverted);
|
||||
}
|
||||
|
||||
cmd.SetGlobalTexture(k_FalloffLookupID, pass.rendererData.fallOffLookup);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
private static void SetGeneralLightShaderGlobals(IRenderPass2D pass, CommandBuffer cmd, Light2D light)
|
||||
{
|
||||
float intensity = light.intensity * light.color.a;
|
||||
Color color = intensity * light.color;
|
||||
color.a = 1.0f;
|
||||
|
||||
float volumeIntensity = light.volumeIntensity;
|
||||
|
||||
cmd.SetGlobalFloat(k_FalloffIntensityID, light.falloffIntensity);
|
||||
cmd.SetGlobalFloat(k_FalloffDistanceID, light.shapeLightFalloffSize);
|
||||
cmd.SetGlobalColor(k_LightColorID, color);
|
||||
cmd.SetGlobalFloat(k_VolumeOpacityID, volumeIntensity);
|
||||
}
|
||||
|
||||
private static void SetPointLightShaderGlobals(IRenderPass2D pass, CommandBuffer cmd, Light2D light)
|
||||
{
|
||||
// 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);
|
||||
|
||||
cmd.SetGlobalVector(k_LightPositionID, light.transform.position);
|
||||
cmd.SetGlobalMatrix(k_LightInvMatrixID, lightInverseMatrix);
|
||||
cmd.SetGlobalFloat(k_InnerRadiusMultID, innerRadiusMult);
|
||||
cmd.SetGlobalFloat(k_OuterAngleID, outerAngle);
|
||||
cmd.SetGlobalFloat(k_InnerAngleMultID, 1 / (outerAngle - innerAngle));
|
||||
cmd.SetGlobalTexture(k_LightLookupID, Light2DLookupTexture.GetLightLookupTexture());
|
||||
cmd.SetGlobalTexture(k_FalloffLookupID, pass.rendererData.fallOffLookup);
|
||||
cmd.SetGlobalFloat(k_FalloffIntensityID, light.falloffIntensity);
|
||||
cmd.SetGlobalFloat(k_IsFullSpotlightID, innerAngle == 1 ? 1.0f : 0.0f);
|
||||
|
||||
cmd.SetGlobalFloat(k_LightZDistanceID, light.normalMapDistance);
|
||||
|
||||
if (light.lightCookieSprite != null && light.lightCookieSprite.texture != null)
|
||||
cmd.SetGlobalTexture(k_PointLightCookieTexID, light.lightCookieSprite.texture);
|
||||
}
|
||||
|
||||
public static void ClearDirtyLighting(this IRenderPass2D pass, CommandBuffer cmd, uint blendStylesUsed)
|
||||
{
|
||||
for (var i = 0; i < pass.rendererData.lightBlendStyles.Length; ++i)
|
||||
{
|
||||
if ((blendStylesUsed & (uint)(1 << i)) == 0)
|
||||
continue;
|
||||
|
||||
if (!pass.rendererData.lightBlendStyles[i].isDirty)
|
||||
continue;
|
||||
|
||||
cmd.SetRenderTarget(pass.rendererData.lightBlendStyles[i].renderTargetHandle.Identifier());
|
||||
cmd.ClearRenderTarget(false, true, Color.black);
|
||||
pass.rendererData.lightBlendStyles[i].isDirty = false;
|
||||
}
|
||||
}
|
||||
|
||||
public static void RenderNormals(this IRenderPass2D pass, ScriptableRenderContext context, RenderingData renderingData, DrawingSettings drawSettings, FilteringSettings filterSettings, RenderTargetIdentifier depthTarget, CommandBuffer cmd, LightStats lightStats)
|
||||
{
|
||||
using (new ProfilingScope(cmd, m_ProfilingSampler))
|
||||
{
|
||||
// figure out the scale
|
||||
var normalRTScale = 0.0f;
|
||||
|
||||
if (depthTarget != BuiltinRenderTextureType.None)
|
||||
normalRTScale = 1.0f;
|
||||
else
|
||||
normalRTScale = Mathf.Clamp(pass.rendererData.lightRenderTextureScale, 0.01f, 1.0f);
|
||||
|
||||
pass.CreateNormalMapRenderTexture(renderingData, cmd, normalRTScale);
|
||||
|
||||
|
||||
var msaaEnabled = renderingData.cameraData.cameraTargetDescriptor.msaaSamples > 1;
|
||||
var storeAction = msaaEnabled ? RenderBufferStoreAction.Resolve : RenderBufferStoreAction.Store;
|
||||
if (depthTarget != BuiltinRenderTextureType.None)
|
||||
{
|
||||
cmd.SetRenderTarget(
|
||||
pass.rendererData.normalsRenderTarget.Identifier(),
|
||||
RenderBufferLoadAction.DontCare,
|
||||
storeAction,
|
||||
depthTarget,
|
||||
RenderBufferLoadAction.Load,
|
||||
RenderBufferStoreAction.Store);
|
||||
}
|
||||
else
|
||||
cmd.SetRenderTarget(pass.rendererData.normalsRenderTarget.Identifier(), RenderBufferLoadAction.DontCare, storeAction);
|
||||
|
||||
cmd.ClearRenderTarget(pass.rendererData.useDepthStencilBuffer, true, k_NormalClearColor);
|
||||
|
||||
context.ExecuteCommandBuffer(cmd);
|
||||
cmd.Clear();
|
||||
|
||||
drawSettings.SetShaderPassName(0, k_NormalsRenderingPassName);
|
||||
context.DrawRenderers(renderingData.cullResults, ref drawSettings, ref filterSettings);
|
||||
}
|
||||
}
|
||||
|
||||
public static void RenderLights(this IRenderPass2D pass, RenderingData renderingData, CommandBuffer cmd, int layerToRender, ref LayerBatch layerBatch, ref RenderTextureDescriptor rtDesc)
|
||||
{
|
||||
// Before rendering the lights cache some values that are expensive to get/calculate
|
||||
var culledLights = pass.rendererData.lightCullResult.visibleLights;
|
||||
for (var i = 0; i < culledLights.Count; i++)
|
||||
{
|
||||
culledLights[i].CacheValues();
|
||||
}
|
||||
|
||||
ShadowCasterGroup2DManager.CacheValues();
|
||||
|
||||
|
||||
var blendStyles = pass.rendererData.lightBlendStyles;
|
||||
|
||||
for (var i = 0; i < blendStyles.Length; ++i)
|
||||
{
|
||||
if ((layerBatch.lightStats.blendStylesUsed & (uint)(1 << i)) == 0)
|
||||
continue;
|
||||
|
||||
var sampleName = blendStyles[i].name;
|
||||
cmd.BeginSample(sampleName);
|
||||
|
||||
if (!Light2DManager.GetGlobalColor(layerToRender, i, out var clearColor))
|
||||
clearColor = Color.black;
|
||||
|
||||
var anyLights = (layerBatch.lightStats.blendStylesWithLights & (uint)(1 << i)) != 0;
|
||||
|
||||
var desc = rtDesc;
|
||||
if (!anyLights) // No lights -- create tiny texture
|
||||
desc.width = desc.height = 4;
|
||||
var identifier = layerBatch.GetRTId(cmd, desc, i);
|
||||
|
||||
cmd.SetRenderTarget(identifier,
|
||||
RenderBufferLoadAction.DontCare,
|
||||
RenderBufferStoreAction.Store,
|
||||
RenderBufferLoadAction.DontCare,
|
||||
RenderBufferStoreAction.DontCare);
|
||||
cmd.ClearRenderTarget(false, true, clearColor);
|
||||
|
||||
if (anyLights)
|
||||
{
|
||||
RenderLightSet(
|
||||
pass, renderingData,
|
||||
i,
|
||||
cmd,
|
||||
layerToRender,
|
||||
identifier,
|
||||
pass.rendererData.lightCullResult.visibleLights
|
||||
);
|
||||
}
|
||||
|
||||
cmd.EndSample(sampleName);
|
||||
}
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
var isPoint = light.isPointLight;
|
||||
var bitIndex = 0;
|
||||
var volumeBit = isVolume ? 1u << bitIndex : 0u;
|
||||
bitIndex++;
|
||||
var shapeBit = !isPoint ? 1u << bitIndex : 0u;
|
||||
bitIndex++;
|
||||
var additiveBit = light.overlapOperation == Light2D.OverlapOperation.AlphaBlend ? 0u : 1u << bitIndex;
|
||||
bitIndex++;
|
||||
var spriteBit = light.lightType == Light2D.LightType.Sprite ? 1u << bitIndex : 0u;
|
||||
bitIndex++;
|
||||
var pointCookieBit = (isPoint && light.lightCookieSprite != null && light.lightCookieSprite.texture != null) ? 1u << bitIndex : 0u;
|
||||
bitIndex++;
|
||||
var pointFastQualityBit = (isPoint && light.normalMapQuality == Light2D.NormalMapQuality.Fast) ? 1u << bitIndex : 0u;
|
||||
bitIndex++;
|
||||
var useNormalMap = light.normalMapQuality != Light2D.NormalMapQuality.Disabled ? 1u << bitIndex : 0u;
|
||||
|
||||
return pointFastQualityBit | pointCookieBit | spriteBit | additiveBit | shapeBit | volumeBit | useNormalMap;
|
||||
}
|
||||
|
||||
private static Material CreateLightMaterial(Renderer2DData rendererData, Light2D light, bool isVolume)
|
||||
{
|
||||
var isPoint = light.isPointLight;
|
||||
Material material;
|
||||
|
||||
if (isVolume)
|
||||
material = CoreUtils.CreateEngineMaterial(isPoint ? rendererData.pointLightVolumeShader : rendererData.shapeLightVolumeShader);
|
||||
else
|
||||
{
|
||||
material = CoreUtils.CreateEngineMaterial(isPoint ? rendererData.pointLightShader : rendererData.shapeLightShader);
|
||||
|
||||
if (light.overlapOperation == Light2D.OverlapOperation.Additive)
|
||||
{
|
||||
SetBlendModes(material, BlendMode.One, BlendMode.One);
|
||||
material.EnableKeyword(k_UseAdditiveBlendingKeyword);
|
||||
}
|
||||
else
|
||||
SetBlendModes(material, BlendMode.SrcAlpha, BlendMode.OneMinusSrcAlpha);
|
||||
}
|
||||
|
||||
if (light.lightType == Light2D.LightType.Sprite)
|
||||
material.EnableKeyword(k_SpriteLightKeyword);
|
||||
|
||||
if (isPoint && light.lightCookieSprite != null && light.lightCookieSprite.texture != null)
|
||||
material.EnableKeyword(k_UsePointLightCookiesKeyword);
|
||||
|
||||
if (isPoint && light.normalMapQuality == Light2D.NormalMapQuality.Fast)
|
||||
material.EnableKeyword(k_LightQualityFastKeyword);
|
||||
|
||||
if (light.normalMapQuality != Light2D.NormalMapQuality.Disabled)
|
||||
material.EnableKeyword(k_UseNormalMap);
|
||||
|
||||
return material;
|
||||
}
|
||||
|
||||
private static Material GetLightMaterial(this Renderer2DData rendererData, Light2D light, bool isVolume)
|
||||
{
|
||||
var materialIndex = GetLightMaterialIndex(light, isVolume);
|
||||
|
||||
if (!rendererData.lightMaterials.TryGetValue(materialIndex, out var material))
|
||||
{
|
||||
material = CreateLightMaterial(rendererData, light, isVolume);
|
||||
rendererData.lightMaterials[materialIndex] = material;
|
||||
}
|
||||
|
||||
return material;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 7978073cb0cd25f4f8e29b37e3f55d2a
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
|
|
@ -0,0 +1,428 @@
|
|||
using System.Collections.Generic;
|
||||
using UnityEngine.Rendering;
|
||||
using UnityEngine.Rendering.Universal;
|
||||
using UnityEngine.Scripting.APIUpdating;
|
||||
|
||||
namespace UnityEngine.Experimental.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("UnityEngine.Experimental.Rendering")]
|
||||
[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
|
||||
{
|
||||
public enum CropFrame
|
||||
{
|
||||
None,
|
||||
Pillarbox,
|
||||
Letterbox,
|
||||
Windowbox,
|
||||
StretchFill
|
||||
}
|
||||
|
||||
public enum GridSnapping
|
||||
{
|
||||
None,
|
||||
PixelSnapping,
|
||||
UpscaleRenderTexture
|
||||
}
|
||||
|
||||
public 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
|
||||
|
||||
public CropFrame cropFrame { get { return m_CropFrame; } set { m_CropFrame = value; } }
|
||||
public GridSnapping gridSnapping { get { return m_GridSnapping; } set { m_GridSnapping = value; } }
|
||||
|
||||
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", false)]
|
||||
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", false)]
|
||||
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", false)]
|
||||
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", false)]
|
||||
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", false)]
|
||||
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>
|
||||
/// 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;
|
||||
|
||||
// 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_Internal.useStretchFill ? 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;
|
||||
Matrix4x4 offsetMatrix = Matrix4x4.TRS(-offset, Quaternion.identity, new Vector3(1.0f, 1.0f, -1.0f));
|
||||
|
||||
m_Camera.worldToCameraMatrix = offsetMatrix * m_Camera.transform.worldToLocalMatrix;
|
||||
}
|
||||
|
||||
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 DEVELOPMENT_BUILD || UNITY_EDITOR
|
||||
// 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
|
||||
|
||||
|
||||
public void OnBeforeSerialize()
|
||||
{
|
||||
#if UNITY_EDITOR
|
||||
m_ComponentVersion = k_CurrentComponentVersion;
|
||||
#endif
|
||||
}
|
||||
|
||||
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,236 @@
|
|||
using System;
|
||||
using UnityEngine.Experimental.Rendering.Universal;
|
||||
|
||||
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 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;
|
||||
|
||||
// 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;
|
||||
}
|
||||
}
|
||||
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,285 @@
|
|||
using UnityEngine.Experimental.Rendering.Universal;
|
||||
using UnityEngine.Rendering.Universal.Internal;
|
||||
|
||||
namespace UnityEngine.Rendering.Universal
|
||||
{
|
||||
internal class Renderer2D : ScriptableRenderer
|
||||
{
|
||||
Render2DLightingPass m_Render2DLightingPass;
|
||||
PixelPerfectBackgroundPass m_PixelPerfectBackgroundPass;
|
||||
FinalBlitPass m_FinalBlitPass;
|
||||
Light2DCullResult m_LightCullResult;
|
||||
|
||||
private static readonly ProfilingSampler m_ProfilingSampler = new ProfilingSampler("Create Camera Textures");
|
||||
|
||||
bool m_UseDepthStencilBuffer = true;
|
||||
bool m_CreateColorTexture;
|
||||
bool m_CreateDepthTexture;
|
||||
|
||||
readonly RenderTargetHandle k_ColorTextureHandle;
|
||||
readonly RenderTargetHandle k_DepthTextureHandle;
|
||||
|
||||
Material m_BlitMaterial;
|
||||
Material m_SamplingMaterial;
|
||||
|
||||
Renderer2DData m_Renderer2DData;
|
||||
|
||||
internal bool createColorTexture => m_CreateColorTexture;
|
||||
internal bool createDepthTexture => m_CreateDepthTexture;
|
||||
|
||||
PostProcessPasses m_PostProcessPasses;
|
||||
internal ColorGradingLutPass colorGradingLutPass { get => m_PostProcessPasses.colorGradingLutPass; }
|
||||
internal PostProcessPass postProcessPass { get => m_PostProcessPasses.postProcessPass; }
|
||||
internal PostProcessPass finalPostProcessPass { get => m_PostProcessPasses.finalPostProcessPass; }
|
||||
internal RenderTargetHandle afterPostProcessColorHandle { get => m_PostProcessPasses.afterPostProcessColor; }
|
||||
internal RenderTargetHandle colorGradingLutHandle { get => m_PostProcessPasses.colorGradingLut; }
|
||||
|
||||
public Renderer2D(Renderer2DData data) : base(data)
|
||||
{
|
||||
m_BlitMaterial = CoreUtils.CreateEngineMaterial(data.blitShader);
|
||||
m_SamplingMaterial = CoreUtils.CreateEngineMaterial(data.samplingShader);
|
||||
|
||||
m_Render2DLightingPass = new Render2DLightingPass(data, m_BlitMaterial, m_SamplingMaterial);
|
||||
// we should determine why clearing the camera target is set so late in the events... sounds like it could be earlier
|
||||
m_PixelPerfectBackgroundPass = new PixelPerfectBackgroundPass(RenderPassEvent.AfterRenderingTransparents);
|
||||
m_FinalBlitPass = new FinalBlitPass(RenderPassEvent.AfterRendering + 1, m_BlitMaterial);
|
||||
|
||||
|
||||
m_PostProcessPasses = new PostProcessPasses(data.postProcessData, m_BlitMaterial);
|
||||
|
||||
m_UseDepthStencilBuffer = data.useDepthStencilBuffer;
|
||||
|
||||
// We probably should declare these names in the base class,
|
||||
// as they must be the same across all ScriptableRenderer types for camera stacking to work.
|
||||
k_ColorTextureHandle.Init("_CameraColorTexture");
|
||||
k_DepthTextureHandle.Init("_CameraDepthAttachment");
|
||||
|
||||
m_Renderer2DData = data;
|
||||
|
||||
supportedRenderingFeatures = new RenderingFeatures()
|
||||
{
|
||||
cameraStacking = true,
|
||||
};
|
||||
|
||||
m_LightCullResult = new Light2DCullResult();
|
||||
m_Renderer2DData.lightCullResult = m_LightCullResult;
|
||||
}
|
||||
|
||||
protected override void Dispose(bool disposing)
|
||||
{
|
||||
m_PostProcessPasses.Dispose();
|
||||
}
|
||||
|
||||
public Renderer2DData GetRenderer2DData()
|
||||
{
|
||||
return m_Renderer2DData;
|
||||
}
|
||||
|
||||
void CreateRenderTextures(
|
||||
ref CameraData cameraData,
|
||||
bool forceCreateColorTexture,
|
||||
FilterMode colorTextureFilterMode,
|
||||
CommandBuffer cmd,
|
||||
out RenderTargetHandle colorTargetHandle,
|
||||
out RenderTargetHandle depthTargetHandle)
|
||||
{
|
||||
ref var cameraTargetDescriptor = ref cameraData.cameraTargetDescriptor;
|
||||
|
||||
if (cameraData.renderType == CameraRenderType.Base)
|
||||
{
|
||||
m_CreateColorTexture = forceCreateColorTexture
|
||||
|| cameraData.postProcessEnabled
|
||||
|| cameraData.isHdrEnabled
|
||||
|| cameraData.isSceneViewCamera
|
||||
|| !cameraData.isDefaultViewport
|
||||
|| cameraData.requireSrgbConversion
|
||||
|| !cameraData.resolveFinalTarget
|
||||
|| m_Renderer2DData.useCameraSortingLayerTexture
|
||||
|| !Mathf.Approximately(cameraData.renderScale, 1.0f);
|
||||
|
||||
m_CreateDepthTexture = !cameraData.resolveFinalTarget && m_UseDepthStencilBuffer;
|
||||
|
||||
colorTargetHandle = m_CreateColorTexture ? k_ColorTextureHandle : RenderTargetHandle.CameraTarget;
|
||||
depthTargetHandle = m_CreateDepthTexture ? k_DepthTextureHandle : colorTargetHandle;
|
||||
|
||||
if (m_CreateColorTexture)
|
||||
{
|
||||
var colorDescriptor = cameraTargetDescriptor;
|
||||
colorDescriptor.depthBufferBits = m_CreateDepthTexture || !m_UseDepthStencilBuffer ? 0 : 32;
|
||||
cmd.GetTemporaryRT(k_ColorTextureHandle.id, colorDescriptor, colorTextureFilterMode);
|
||||
}
|
||||
|
||||
if (m_CreateDepthTexture)
|
||||
{
|
||||
var depthDescriptor = cameraTargetDescriptor;
|
||||
depthDescriptor.colorFormat = RenderTextureFormat.Depth;
|
||||
depthDescriptor.depthBufferBits = 32;
|
||||
depthDescriptor.bindMS = depthDescriptor.msaaSamples > 1 && !SystemInfo.supportsMultisampleAutoResolve && (SystemInfo.supportsMultisampledTextures != 0);
|
||||
cmd.GetTemporaryRT(k_DepthTextureHandle.id, depthDescriptor, FilterMode.Point);
|
||||
}
|
||||
}
|
||||
else // Overlay camera
|
||||
{
|
||||
// These render textures are created by the base camera, but it's the responsibility of the last overlay camera's ScriptableRenderer
|
||||
// to release the textures in its FinishRendering().
|
||||
m_CreateColorTexture = true;
|
||||
m_CreateDepthTexture = true;
|
||||
|
||||
colorTargetHandle = k_ColorTextureHandle;
|
||||
depthTargetHandle = k_DepthTextureHandle;
|
||||
}
|
||||
}
|
||||
|
||||
public override void Setup(ScriptableRenderContext context, ref RenderingData renderingData)
|
||||
{
|
||||
ref CameraData cameraData = ref renderingData.cameraData;
|
||||
ref var cameraTargetDescriptor = ref cameraData.cameraTargetDescriptor;
|
||||
bool stackHasPostProcess = renderingData.postProcessingEnabled;
|
||||
bool lastCameraInStack = cameraData.resolveFinalTarget;
|
||||
var colorTextureFilterMode = FilterMode.Bilinear;
|
||||
|
||||
PixelPerfectCamera ppc = null;
|
||||
bool ppcUsesOffscreenRT = false;
|
||||
bool ppcUpscaleRT = false;
|
||||
|
||||
bool savedIsOrthographic = renderingData.cameraData.camera.orthographic;
|
||||
float savedOrthographicSize = renderingData.cameraData.camera.orthographicSize;
|
||||
|
||||
if (DebugHandler != null)
|
||||
{
|
||||
#if UNITY_EDITOR
|
||||
UnityEditorInternal.SpriteMaskUtility.EnableDebugMode(DebugHandler.DebugDisplaySettings.MaterialSettings.DebugMaterialModeData == DebugMaterialMode.SpriteMask);
|
||||
#endif
|
||||
if (DebugHandler.AreAnySettingsActive)
|
||||
{
|
||||
stackHasPostProcess = stackHasPostProcess && DebugHandler.IsPostProcessingAllowed;
|
||||
}
|
||||
DebugHandler.Setup(context, ref cameraData);
|
||||
}
|
||||
|
||||
#if UNITY_EDITOR
|
||||
// The scene view camera cannot be uninitialized or skybox when using the 2D renderer.
|
||||
if (cameraData.cameraType == CameraType.SceneView)
|
||||
{
|
||||
renderingData.cameraData.camera.clearFlags = CameraClearFlags.SolidColor;
|
||||
}
|
||||
#endif
|
||||
|
||||
// Pixel Perfect Camera doesn't support camera stacking.
|
||||
if (cameraData.renderType == CameraRenderType.Base && lastCameraInStack)
|
||||
{
|
||||
cameraData.camera.TryGetComponent(out ppc);
|
||||
if (ppc != null && ppc.enabled)
|
||||
{
|
||||
if (ppc.offscreenRTSize != Vector2Int.zero)
|
||||
{
|
||||
ppcUsesOffscreenRT = true;
|
||||
|
||||
// Pixel Perfect Camera may request a different RT size than camera VP size.
|
||||
// In that case we need to modify cameraTargetDescriptor here so that all the passes would use the same size.
|
||||
cameraTargetDescriptor.width = ppc.offscreenRTSize.x;
|
||||
cameraTargetDescriptor.height = ppc.offscreenRTSize.y;
|
||||
}
|
||||
|
||||
renderingData.cameraData.camera.orthographic = true;
|
||||
renderingData.cameraData.camera.orthographicSize = ppc.orthographicSize;
|
||||
|
||||
colorTextureFilterMode = ppc.finalBlitFilterMode;
|
||||
ppcUpscaleRT = ppc.gridSnapping == PixelPerfectCamera.GridSnapping.UpscaleRenderTexture;
|
||||
}
|
||||
}
|
||||
|
||||
RenderTargetHandle colorTargetHandle;
|
||||
RenderTargetHandle depthTargetHandle;
|
||||
|
||||
CommandBuffer cmd = CommandBufferPool.Get();
|
||||
using (new ProfilingScope(cmd, m_ProfilingSampler))
|
||||
{
|
||||
CreateRenderTextures(ref cameraData, ppcUsesOffscreenRT, colorTextureFilterMode, cmd,
|
||||
out colorTargetHandle, out depthTargetHandle);
|
||||
}
|
||||
context.ExecuteCommandBuffer(cmd);
|
||||
CommandBufferPool.Release(cmd);
|
||||
|
||||
ConfigureCameraTarget(colorTargetHandle.Identifier(), depthTargetHandle.Identifier());
|
||||
|
||||
// Add passes from Renderer Features. - NOTE: This should be reexamined in the future. Please see feedback from this PR https://github.com/Unity-Technologies/Graphics/pull/3147/files
|
||||
isCameraColorTargetValid = true; // This is to make it possible to call ScriptableRenderer.cameraColorTarget in the custom passes.
|
||||
AddRenderPasses(ref renderingData);
|
||||
isCameraColorTargetValid = false;
|
||||
|
||||
// We generate color LUT in the base camera only. This allows us to not break render pass execution for overlay cameras.
|
||||
if (stackHasPostProcess && cameraData.renderType == CameraRenderType.Base && m_PostProcessPasses.isCreated)
|
||||
{
|
||||
colorGradingLutPass.Setup(colorGradingLutHandle);
|
||||
EnqueuePass(colorGradingLutPass);
|
||||
}
|
||||
|
||||
var needsDepth = m_CreateDepthTexture || (!m_CreateColorTexture && m_UseDepthStencilBuffer);
|
||||
m_Render2DLightingPass.Setup(needsDepth);
|
||||
m_Render2DLightingPass.ConfigureTarget(colorTargetHandle.Identifier(), depthTargetHandle.Identifier());
|
||||
EnqueuePass(m_Render2DLightingPass);
|
||||
|
||||
// When using Upscale Render Texture on a Pixel Perfect Camera, we want all post-processing effects done with a low-res RT,
|
||||
// and only upscale the low-res RT to fullscreen when blitting it to camera target. Also, final post processing pass is not run in this case,
|
||||
// so FXAA is not supported (you don't want to apply FXAA when everything is intentionally pixelated).
|
||||
bool requireFinalPostProcessPass =
|
||||
lastCameraInStack && !ppcUpscaleRT && stackHasPostProcess && cameraData.antialiasing == AntialiasingMode.FastApproximateAntialiasing;
|
||||
|
||||
bool hasPassesAfterPostProcessing = activeRenderPassQueue.Find(x => x.renderPassEvent == RenderPassEvent.AfterRenderingPostProcessing) != null;
|
||||
|
||||
if (stackHasPostProcess && m_PostProcessPasses.isCreated)
|
||||
{
|
||||
RenderTargetHandle postProcessDestHandle =
|
||||
lastCameraInStack && !ppcUpscaleRT && !requireFinalPostProcessPass ? RenderTargetHandle.CameraTarget : afterPostProcessColorHandle;
|
||||
|
||||
postProcessPass.Setup(
|
||||
cameraTargetDescriptor,
|
||||
colorTargetHandle,
|
||||
postProcessDestHandle,
|
||||
depthTargetHandle,
|
||||
colorGradingLutHandle,
|
||||
requireFinalPostProcessPass,
|
||||
postProcessDestHandle == RenderTargetHandle.CameraTarget,
|
||||
hasPassesAfterPostProcessing);
|
||||
|
||||
EnqueuePass(postProcessPass);
|
||||
colorTargetHandle = postProcessDestHandle;
|
||||
}
|
||||
|
||||
if (ppc != null && ppc.enabled && (ppc.cropFrame == PixelPerfectCamera.CropFrame.Pillarbox || ppc.cropFrame == PixelPerfectCamera.CropFrame.Letterbox || ppc.cropFrame == PixelPerfectCamera.CropFrame.Windowbox || ppc.cropFrame == PixelPerfectCamera.CropFrame.StretchFill))
|
||||
{
|
||||
m_PixelPerfectBackgroundPass.Setup(savedIsOrthographic, savedOrthographicSize);
|
||||
EnqueuePass(m_PixelPerfectBackgroundPass);
|
||||
}
|
||||
|
||||
if (requireFinalPostProcessPass && m_PostProcessPasses.isCreated)
|
||||
{
|
||||
finalPostProcessPass.SetupFinalPass(colorTargetHandle, hasPassesAfterPostProcessing);
|
||||
EnqueuePass(finalPostProcessPass);
|
||||
}
|
||||
else if (lastCameraInStack && colorTargetHandle != RenderTargetHandle.CameraTarget)
|
||||
{
|
||||
m_FinalBlitPass.Setup(cameraTargetDescriptor, colorTargetHandle);
|
||||
EnqueuePass(m_FinalBlitPass);
|
||||
}
|
||||
}
|
||||
|
||||
public override void SetupCullingParameters(ref ScriptableCullingParameters cullingParameters, ref CameraData cameraData)
|
||||
{
|
||||
cullingParameters.cullingOptions = CullingOptions.None;
|
||||
cullingParameters.isOrthographic = cameraData.camera.orthographic;
|
||||
cullingParameters.shadowDistance = 0.0f;
|
||||
m_LightCullResult.SetupCulling(ref cullingParameters, cameraData.camera);
|
||||
}
|
||||
|
||||
public override void FinishRendering(CommandBuffer cmd)
|
||||
{
|
||||
if (m_CreateColorTexture)
|
||||
cmd.ReleaseTemporaryRT(k_ColorTextureHandle.id);
|
||||
|
||||
if (m_CreateDepthTexture)
|
||||
cmd.ReleaseTemporaryRT(k_DepthTextureHandle.id);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 2c8841d47fff7714cb6671d40620dea6
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
|
|
@ -0,0 +1,177 @@
|
|||
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
|
||||
{
|
||||
[Serializable, ReloadGroup, ExcludeFromPreset]
|
||||
[MovedFrom("UnityEngine.Experimental.Rendering.Universal")]
|
||||
[HelpURL("https://docs.unity3d.com/Packages/com.unity.render-pipelines.universal@latest/index.html?subfolder=/manual/2DRendererData_overview.html")]
|
||||
public partial class Renderer2DData : ScriptableRendererData
|
||||
{
|
||||
internal enum Renderer2DDefaultMaterialType
|
||||
{
|
||||
Lit,
|
||||
Unlit,
|
||||
Custom
|
||||
}
|
||||
|
||||
[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, Reload("Shaders/2D/Light2D-Shape.shader")]
|
||||
Shader m_ShapeLightShader = null;
|
||||
|
||||
[SerializeField, Reload("Shaders/2D/Light2D-Shape-Volumetric.shader")]
|
||||
Shader m_ShapeLightVolumeShader = null;
|
||||
|
||||
[SerializeField, Reload("Shaders/2D/Light2D-Point.shader")]
|
||||
Shader m_PointLightShader = null;
|
||||
|
||||
[SerializeField, Reload("Shaders/2D/Light2D-Point-Volumetric.shader")]
|
||||
Shader m_PointLightVolumeShader = null;
|
||||
|
||||
[SerializeField, Reload("Shaders/Utils/Blit.shader")]
|
||||
Shader m_BlitShader = null;
|
||||
|
||||
[SerializeField, Reload("Shaders/Utils/Sampling.shader")]
|
||||
Shader m_SamplingShader = null;
|
||||
|
||||
[SerializeField, Reload("Shaders/2D/Shadow2D-Projected.shader")]
|
||||
Shader m_ProjectedShadowShader = null;
|
||||
|
||||
[SerializeField, Reload("Shaders/2D/Shadow2D-Shadow-Sprite.shader")]
|
||||
Shader m_SpriteShadowShader = null;
|
||||
|
||||
[SerializeField, Reload("Shaders/2D/Shadow2D-Unshadow-Sprite.shader")]
|
||||
Shader m_SpriteUnshadowShader = null;
|
||||
|
||||
[SerializeField, Reload("Shaders/2D/Shadow2D-Unshadow-Geometry.shader")]
|
||||
Shader m_GeometryUnshadowShader = null;
|
||||
|
||||
[SerializeField, Reload("Shaders/Utils/FallbackError.shader")]
|
||||
Shader m_FallbackErrorShader;
|
||||
|
||||
[SerializeField]
|
||||
PostProcessData m_PostProcessData = null;
|
||||
|
||||
[SerializeField, Reload("Runtime/2D/Data/Textures/FalloffLookupTexture.png")]
|
||||
[HideInInspector]
|
||||
private Texture2D m_FallOffLookup = 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 Texture2D fallOffLookup => m_FallOffLookup;
|
||||
internal Shader shapeLightShader => m_ShapeLightShader;
|
||||
internal Shader shapeLightVolumeShader => m_ShapeLightVolumeShader;
|
||||
internal Shader pointLightShader => m_PointLightShader;
|
||||
internal Shader pointLightVolumeShader => m_PointLightVolumeShader;
|
||||
internal Shader blitShader => m_BlitShader;
|
||||
internal Shader samplingShader => m_SamplingShader;
|
||||
internal PostProcessData postProcessData { get => m_PostProcessData; set { m_PostProcessData = value; } }
|
||||
internal Shader spriteShadowShader => m_SpriteShadowShader;
|
||||
internal Shader spriteUnshadowShader => m_SpriteUnshadowShader;
|
||||
internal Shader geometryUnshadowShader => m_GeometryUnshadowShader;
|
||||
|
||||
internal Shader projectedShadowShader => m_ProjectedShadowShader;
|
||||
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;
|
||||
|
||||
protected override ScriptableRenderer Create()
|
||||
{
|
||||
#if UNITY_EDITOR
|
||||
if (!Application.isPlaying)
|
||||
{
|
||||
ReloadAllNullProperties();
|
||||
}
|
||||
#endif
|
||||
return new Renderer2D(this);
|
||||
}
|
||||
|
||||
protected override void OnEnable()
|
||||
{
|
||||
base.OnEnable();
|
||||
|
||||
for (var i = 0; i < m_LightBlendStyles.Length; ++i)
|
||||
{
|
||||
m_LightBlendStyles[i].renderTargetHandle.Init($"_ShapeLightTexture{i}");
|
||||
}
|
||||
|
||||
normalsRenderTarget.Init("_NormalMap");
|
||||
shadowsRenderTarget.Init("_ShadowTex");
|
||||
|
||||
spriteSelfShadowMaterial = null;
|
||||
spriteUnshadowMaterial = null;
|
||||
projectedShadowMaterial = null;
|
||||
stencilOnlyShadowMaterial = null;
|
||||
}
|
||||
|
||||
// transient data
|
||||
internal Dictionary<uint, Material> lightMaterials { get; } = new Dictionary<uint, Material>();
|
||||
internal Material[] spriteSelfShadowMaterial { get; set; }
|
||||
internal Material[] spriteUnshadowMaterial { get; set; }
|
||||
internal Material[] geometryUnshadowMaterial { get; set; }
|
||||
|
||||
internal Material[] projectedShadowMaterial { get; set; }
|
||||
internal Material[] stencilOnlyShadowMaterial { get; set; }
|
||||
|
||||
internal bool isNormalsRenderTargetValid { get; set; }
|
||||
internal float normalsRenderTargetScale { get; set; }
|
||||
internal RenderTargetHandle normalsRenderTarget;
|
||||
internal RenderTargetHandle shadowsRenderTarget;
|
||||
internal RenderTargetHandle 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,111 @@
|
|||
using UnityEditor;
|
||||
|
||||
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;
|
||||
|
||||
[SerializeField, Reload("Runtime/Materials/Sprite-Lit-Default.mat")]
|
||||
Material m_DefaultLitMaterial = null;
|
||||
|
||||
[SerializeField, Reload("Runtime/Materials/Sprite-Unlit-Default.mat")]
|
||||
Material m_DefaultUnlitMaterial = null;
|
||||
|
||||
[SerializeField, Reload("Runtime/Materials/SpriteMask-Default.mat")]
|
||||
Material m_DefaultMaskMaterial = null;
|
||||
|
||||
internal override Shader GetDefaultShader()
|
||||
{
|
||||
return Shader.Find("Universal Render Pipeline/2D/Sprite-Lit-Default");
|
||||
}
|
||||
|
||||
internal override Material GetDefaultMaterial(DefaultMaterialType materialType)
|
||||
{
|
||||
if (materialType == DefaultMaterialType.Sprite || materialType == DefaultMaterialType.Particle)
|
||||
{
|
||||
if (m_DefaultMaterialType == Renderer2DDefaultMaterialType.Lit)
|
||||
return m_DefaultLitMaterial;
|
||||
else if (m_DefaultMaterialType == Renderer2DDefaultMaterialType.Unlit)
|
||||
return m_DefaultUnlitMaterial;
|
||||
else
|
||||
return m_DefaultCustomMaterial;
|
||||
}
|
||||
if (materialType == DefaultMaterialType.SpriteMask)
|
||||
{
|
||||
return m_DefaultMaskMaterial;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private void OnEnableInEditor()
|
||||
{
|
||||
// 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 = EditorPrefs.GetString(suggestedNamesKey);
|
||||
|
||||
if (string.IsNullOrEmpty(suggestedNamesPrefs))
|
||||
EditorPrefs.SetString(suggestedNamesKey, maskTex + "," + normalMap);
|
||||
else
|
||||
{
|
||||
if (!suggestedNamesPrefs.Contains(maskTex))
|
||||
suggestedNamesPrefs += ("," + maskTex);
|
||||
|
||||
if (!suggestedNamesPrefs.Contains(normalMap))
|
||||
suggestedNamesPrefs += ("," + normalMap);
|
||||
|
||||
EditorPrefs.SetString(suggestedNamesKey, suggestedNamesPrefs);
|
||||
}
|
||||
|
||||
ReloadAllNullProperties();
|
||||
}
|
||||
|
||||
private void ReloadAllNullProperties()
|
||||
{
|
||||
ResourceReloader.TryReloadAllNullIn(this, UniversalRenderPipelineAsset.packagePath);
|
||||
}
|
||||
|
||||
private void Awake()
|
||||
{
|
||||
if (m_LightBlendStyles != null)
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
fileFormatVersion: 2
|
||||
guid: c64a6e813af443b4864441f640dd1c9f
|
||||
timeCreated: 1596423828
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 5f7294749d1800e4daee980df3e624da
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
|
|
@ -0,0 +1,21 @@
|
|||
using UnityEngine.Scripting.APIUpdating;
|
||||
|
||||
|
||||
namespace UnityEngine.Rendering.Universal
|
||||
{
|
||||
[AddComponentMenu("Rendering/2D/Composite Shadow Caster 2D")]
|
||||
[MovedFrom("UnityEngine.Experimental.Rendering.Universal")]
|
||||
[ExecuteInEditMode]
|
||||
public class CompositeShadowCaster2D : ShadowCasterGroup2D
|
||||
{
|
||||
protected void OnEnable()
|
||||
{
|
||||
ShadowCasterGroup2DManager.AddGroup(this);
|
||||
}
|
||||
|
||||
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,249 @@
|
|||
using System;
|
||||
using UnityEngine.Scripting.APIUpdating;
|
||||
|
||||
namespace UnityEngine.Rendering.Universal
|
||||
{
|
||||
/// <summary>
|
||||
/// Class <c>ShadowCaster2D</c> contains properties used for shadow casting
|
||||
/// </summary>
|
||||
[ExecuteInEditMode]
|
||||
[DisallowMultipleComponent]
|
||||
[AddComponentMenu("Rendering/2D/Shadow Caster 2D")]
|
||||
[MovedFrom("UnityEngine.Experimental.Rendering.Universal")]
|
||||
public class ShadowCaster2D : ShadowCasterGroup2D, ISerializationCallbackReceiver
|
||||
{
|
||||
public enum ComponentVersions
|
||||
{
|
||||
Version_Unserialized = 0,
|
||||
Version_1 = 1
|
||||
}
|
||||
const ComponentVersions k_CurrentComponentVersion = ComponentVersions.Version_1;
|
||||
[SerializeField] ComponentVersions m_ComponentVersion = ComponentVersions.Version_Unserialized;
|
||||
|
||||
[SerializeField] bool m_HasRenderer = false;
|
||||
[SerializeField] bool m_UseRendererSilhouette = true;
|
||||
[SerializeField] bool m_CastsShadows = true;
|
||||
[SerializeField] bool m_SelfShadows = false;
|
||||
[SerializeField] int[] m_ApplyToSortingLayers = null;
|
||||
[SerializeField] Vector3[] m_ShapePath = null;
|
||||
[SerializeField] int m_ShapePathHash = 0;
|
||||
[SerializeField] Mesh m_Mesh;
|
||||
[SerializeField] int m_InstanceId;
|
||||
|
||||
internal ShadowCasterGroup2D m_ShadowCasterGroup = null;
|
||||
internal ShadowCasterGroup2D m_PreviousShadowCasterGroup = null;
|
||||
|
||||
[SerializeField]
|
||||
internal BoundingSphere m_ProjectedBoundingSphere;
|
||||
|
||||
public Mesh mesh => m_Mesh;
|
||||
public Vector3[] shapePath => m_ShapePath;
|
||||
internal int shapePathHash { get { return m_ShapePathHash; } set { m_ShapePathHash = value; } }
|
||||
|
||||
int m_PreviousShadowGroup = 0;
|
||||
bool m_PreviousCastsShadows = true;
|
||||
int m_PreviousPathHash = 0;
|
||||
|
||||
internal Vector3 m_CachedPosition;
|
||||
internal Vector3 m_CachedLossyScale;
|
||||
internal Quaternion m_CachedRotation;
|
||||
internal Matrix4x4 m_CachedShadowMatrix;
|
||||
internal Matrix4x4 m_CachedInverseShadowMatrix;
|
||||
internal Matrix4x4 m_CachedLocalToWorldMatrix;
|
||||
|
||||
internal override void CacheValues()
|
||||
{
|
||||
m_CachedPosition = transform.position;
|
||||
m_CachedLossyScale = transform.lossyScale;
|
||||
m_CachedRotation = transform.rotation;
|
||||
|
||||
m_CachedShadowMatrix = Matrix4x4.TRS(m_CachedPosition, m_CachedRotation, Vector3.one);
|
||||
m_CachedInverseShadowMatrix = m_CachedShadowMatrix.inverse;
|
||||
|
||||
m_CachedLocalToWorldMatrix = transform.localToWorldMatrix;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// If selfShadows is true, useRendererSilhoutte specifies that the renderer's sihouette should be considered part of the shadow. If selfShadows is false, useRendererSilhoutte specifies that the renderer's sihouette should be excluded from the shadow
|
||||
/// </summary>
|
||||
public bool useRendererSilhouette
|
||||
{
|
||||
set { m_UseRendererSilhouette = value; }
|
||||
get { return m_UseRendererSilhouette && m_HasRenderer; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// If true, the shadow casting shape is included as part of the shadow. If false, the shadow casting shape is excluded from the shadow.
|
||||
/// </summary>
|
||||
public bool selfShadows
|
||||
{
|
||||
set { m_SelfShadows = value; }
|
||||
get { return m_SelfShadows; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Specifies if shadows will be cast.
|
||||
/// </summary>
|
||||
public bool castsShadows
|
||||
{
|
||||
set { m_CastsShadows = value; }
|
||||
get { return m_CastsShadows; }
|
||||
}
|
||||
|
||||
static int[] SetDefaultSortingLayers()
|
||||
{
|
||||
int layerCount = SortingLayer.layers.Length;
|
||||
int[] allLayers = new int[layerCount];
|
||||
|
||||
for (int layerIndex = 0; layerIndex < layerCount; layerIndex++)
|
||||
{
|
||||
allLayers[layerIndex] = SortingLayer.layers[layerIndex].id;
|
||||
}
|
||||
|
||||
return allLayers;
|
||||
}
|
||||
|
||||
internal bool IsLit(Light2D light)
|
||||
{
|
||||
// Oddly adding and subtracting vectors is expensive here because of the new structures created...
|
||||
Vector3 deltaPos;
|
||||
deltaPos.x = m_ProjectedBoundingSphere.position.x + m_CachedPosition.x;
|
||||
deltaPos.y = m_ProjectedBoundingSphere.position.y + m_CachedPosition.y;
|
||||
deltaPos.z = m_ProjectedBoundingSphere.position.z + m_CachedPosition.z;
|
||||
|
||||
deltaPos.x = light.m_CachedPosition.x - deltaPos.x;
|
||||
deltaPos.y = light.m_CachedPosition.y - deltaPos.y;
|
||||
deltaPos.z = light.m_CachedPosition.z - deltaPos.z;
|
||||
|
||||
float distanceSq = Vector3.SqrMagnitude(deltaPos);
|
||||
|
||||
float radiiLength = light.boundingSphere.radius + m_ProjectedBoundingSphere.radius;
|
||||
return distanceSq <= (radiiLength * radiiLength);
|
||||
}
|
||||
|
||||
internal bool IsShadowedLayer(int layer)
|
||||
{
|
||||
return m_ApplyToSortingLayers != null ? Array.IndexOf(m_ApplyToSortingLayers, layer) >= 0 : false;
|
||||
}
|
||||
|
||||
private void Awake()
|
||||
{
|
||||
if (m_ApplyToSortingLayers == null)
|
||||
m_ApplyToSortingLayers = SetDefaultSortingLayers();
|
||||
|
||||
Bounds bounds = new Bounds(transform.position, Vector3.one);
|
||||
|
||||
Renderer renderer = GetComponent<Renderer>();
|
||||
if (renderer != null)
|
||||
{
|
||||
bounds = renderer.bounds;
|
||||
}
|
||||
#if USING_PHYSICS2D_MODULE
|
||||
else
|
||||
{
|
||||
Collider2D collider = GetComponent<Collider2D>();
|
||||
if (collider != null)
|
||||
bounds = collider.bounds;
|
||||
}
|
||||
#endif
|
||||
Vector3 inverseScale = Vector3.zero;
|
||||
Vector3 relOffset = transform.position;
|
||||
|
||||
if (transform.lossyScale.x != 0 && transform.lossyScale.y != 0)
|
||||
{
|
||||
inverseScale = new Vector3(1 / transform.lossyScale.x, 1 / transform.lossyScale.y);
|
||||
relOffset = new Vector3(inverseScale.x * -transform.position.x, inverseScale.y * -transform.position.y);
|
||||
}
|
||||
|
||||
if (m_ShapePath == null || m_ShapePath.Length == 0)
|
||||
{
|
||||
m_ShapePath = new Vector3[]
|
||||
{
|
||||
relOffset + new Vector3(inverseScale.x * bounds.min.x, inverseScale.y * bounds.min.y),
|
||||
relOffset + new Vector3(inverseScale.x * bounds.min.x, inverseScale.y * bounds.max.y),
|
||||
relOffset + new Vector3(inverseScale.x * bounds.max.x, inverseScale.y * bounds.max.y),
|
||||
relOffset + new Vector3(inverseScale.x * bounds.max.x, inverseScale.y * bounds.min.y),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
protected void OnEnable()
|
||||
{
|
||||
if (m_Mesh == null || m_InstanceId != GetInstanceID())
|
||||
{
|
||||
m_Mesh = new Mesh();
|
||||
m_ProjectedBoundingSphere = ShadowUtility.GenerateShadowMesh(m_Mesh, m_ShapePath);
|
||||
m_InstanceId = GetInstanceID();
|
||||
}
|
||||
|
||||
m_ShadowCasterGroup = null;
|
||||
}
|
||||
|
||||
protected void OnDisable()
|
||||
{
|
||||
ShadowCasterGroup2DManager.RemoveFromShadowCasterGroup(this, m_ShadowCasterGroup);
|
||||
}
|
||||
|
||||
public void Update()
|
||||
{
|
||||
Renderer renderer;
|
||||
m_HasRenderer = TryGetComponent<Renderer>(out renderer);
|
||||
|
||||
bool rebuildMesh = LightUtility.CheckForChange(m_ShapePathHash, ref m_PreviousPathHash);
|
||||
if (rebuildMesh)
|
||||
{
|
||||
m_ProjectedBoundingSphere = ShadowUtility.GenerateShadowMesh(m_Mesh, m_ShapePath);
|
||||
}
|
||||
|
||||
m_PreviousShadowCasterGroup = m_ShadowCasterGroup;
|
||||
bool addedToNewGroup = ShadowCasterGroup2DManager.AddToShadowCasterGroup(this, ref m_ShadowCasterGroup);
|
||||
if (addedToNewGroup && m_ShadowCasterGroup != null)
|
||||
{
|
||||
if (m_PreviousShadowCasterGroup == this)
|
||||
ShadowCasterGroup2DManager.RemoveGroup(this);
|
||||
|
||||
ShadowCasterGroup2DManager.RemoveFromShadowCasterGroup(this, m_PreviousShadowCasterGroup);
|
||||
if (m_ShadowCasterGroup == this)
|
||||
ShadowCasterGroup2DManager.AddGroup(this);
|
||||
}
|
||||
|
||||
if (LightUtility.CheckForChange(m_ShadowGroup, ref m_PreviousShadowGroup))
|
||||
{
|
||||
ShadowCasterGroup2DManager.RemoveGroup(this);
|
||||
ShadowCasterGroup2DManager.AddGroup(this);
|
||||
}
|
||||
|
||||
if (LightUtility.CheckForChange(m_CastsShadows, ref m_PreviousCastsShadows))
|
||||
{
|
||||
if (m_CastsShadows)
|
||||
ShadowCasterGroup2DManager.AddGroup(this);
|
||||
else
|
||||
ShadowCasterGroup2DManager.RemoveGroup(this);
|
||||
}
|
||||
}
|
||||
|
||||
public void OnBeforeSerialize()
|
||||
{
|
||||
m_ComponentVersion = k_CurrentComponentVersion;
|
||||
}
|
||||
|
||||
public void OnAfterDeserialize()
|
||||
{
|
||||
// Upgrade from no serialized version
|
||||
if (m_ComponentVersion == ComponentVersions.Version_Unserialized)
|
||||
{
|
||||
ShadowUtility.ComputeBoundingSphere(m_ShapePath, out m_ProjectedBoundingSphere);
|
||||
m_ComponentVersion = ComponentVersions.Version_1;
|
||||
}
|
||||
}
|
||||
|
||||
#if UNITY_EDITOR
|
||||
void Reset()
|
||||
{
|
||||
Awake();
|
||||
OnEnable();
|
||||
}
|
||||
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 7db70e0ea77f5ac47a8f4565a9406397
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
|
|
@ -0,0 +1,37 @@
|
|||
using System.Collections.Generic;
|
||||
using UnityEngine.Scripting.APIUpdating;
|
||||
|
||||
|
||||
namespace UnityEngine.Rendering.Universal
|
||||
{
|
||||
[MovedFrom("UnityEngine.Experimental.Rendering.Universal")]
|
||||
public abstract class ShadowCasterGroup2D : MonoBehaviour
|
||||
{
|
||||
[SerializeField] internal int m_ShadowGroup = 0;
|
||||
List<ShadowCaster2D> m_ShadowCasters;
|
||||
|
||||
internal virtual void CacheValues()
|
||||
{
|
||||
for (int i = 0; i < m_ShadowCasters.Count; i++)
|
||||
m_ShadowCasters[i].CacheValues();
|
||||
}
|
||||
|
||||
public List<ShadowCaster2D> GetShadowCasters() { return m_ShadowCasters; }
|
||||
|
||||
public int GetShadowGroup() { return m_ShadowGroup; }
|
||||
|
||||
public void RegisterShadowCaster2D(ShadowCaster2D shadowCaster2D)
|
||||
{
|
||||
if (m_ShadowCasters == null)
|
||||
m_ShadowCasters = new List<ShadowCaster2D>();
|
||||
|
||||
m_ShadowCasters.Add(shadowCaster2D);
|
||||
}
|
||||
|
||||
public void UnregisterShadowCaster2D(ShadowCaster2D shadowCaster2D)
|
||||
{
|
||||
if (m_ShadowCasters != null)
|
||||
m_ShadowCasters.Remove(shadowCaster2D);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: dfd932337eaf30646ab42d63eed8f822
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
|
|
@ -0,0 +1,121 @@
|
|||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
|
||||
#if UNITY_EDITOR
|
||||
using UnityEditor;
|
||||
#endif
|
||||
|
||||
namespace UnityEngine.Rendering.Universal
|
||||
{
|
||||
#if UNITY_EDITOR
|
||||
[InitializeOnLoadAttribute]
|
||||
#endif
|
||||
internal class ShadowCasterGroup2DManager
|
||||
{
|
||||
static List<ShadowCasterGroup2D> s_ShadowCasterGroups = null;
|
||||
|
||||
public static List<ShadowCasterGroup2D> shadowCasterGroups { get { return s_ShadowCasterGroups; } }
|
||||
|
||||
|
||||
#if UNITY_EDITOR
|
||||
static ShadowCasterGroup2DManager()
|
||||
{
|
||||
EditorApplication.playModeStateChanged += OnPlayModeStateChanged;
|
||||
}
|
||||
|
||||
private static void OnPlayModeStateChanged(PlayModeStateChange state)
|
||||
{
|
||||
if (s_ShadowCasterGroups != null && (state == PlayModeStateChange.ExitingEditMode || state == PlayModeStateChange.ExitingPlayMode))
|
||||
s_ShadowCasterGroups.Clear();
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
public static void CacheValues()
|
||||
{
|
||||
if (shadowCasterGroups != null)
|
||||
{
|
||||
for (int i = 0; i < shadowCasterGroups.Count; i++)
|
||||
{
|
||||
if (shadowCasterGroups[i] != null)
|
||||
shadowCasterGroups[i].CacheValues();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void AddShadowCasterGroupToList(ShadowCasterGroup2D shadowCaster, List<ShadowCasterGroup2D> list)
|
||||
{
|
||||
int positionToInsert = 0;
|
||||
for (positionToInsert = 0; positionToInsert < list.Count; positionToInsert++)
|
||||
{
|
||||
if (shadowCaster.GetShadowGroup() == list[positionToInsert].GetShadowGroup())
|
||||
break;
|
||||
}
|
||||
|
||||
list.Insert(positionToInsert, shadowCaster);
|
||||
}
|
||||
|
||||
public static void RemoveShadowCasterGroupFromList(ShadowCasterGroup2D shadowCaster, List<ShadowCasterGroup2D> list)
|
||||
{
|
||||
list.Remove(shadowCaster);
|
||||
}
|
||||
|
||||
static CompositeShadowCaster2D FindTopMostCompositeShadowCaster(ShadowCaster2D shadowCaster)
|
||||
{
|
||||
CompositeShadowCaster2D retGroup = null;
|
||||
|
||||
Transform transformToCheck = shadowCaster.transform.parent;
|
||||
while (transformToCheck != null)
|
||||
{
|
||||
CompositeShadowCaster2D currentGroup;
|
||||
if (transformToCheck.TryGetComponent<CompositeShadowCaster2D>(out currentGroup))
|
||||
retGroup = currentGroup;
|
||||
|
||||
transformToCheck = transformToCheck.parent;
|
||||
}
|
||||
|
||||
return retGroup;
|
||||
}
|
||||
|
||||
public static bool AddToShadowCasterGroup(ShadowCaster2D shadowCaster, ref ShadowCasterGroup2D shadowCasterGroup)
|
||||
{
|
||||
ShadowCasterGroup2D newShadowCasterGroup = FindTopMostCompositeShadowCaster(shadowCaster) as ShadowCasterGroup2D;
|
||||
|
||||
if (newShadowCasterGroup == null)
|
||||
newShadowCasterGroup = shadowCaster.GetComponent<ShadowCaster2D>();
|
||||
|
||||
if (newShadowCasterGroup != null && shadowCasterGroup != newShadowCasterGroup)
|
||||
{
|
||||
newShadowCasterGroup.RegisterShadowCaster2D(shadowCaster);
|
||||
shadowCasterGroup = newShadowCasterGroup;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public static void RemoveFromShadowCasterGroup(ShadowCaster2D shadowCaster, ShadowCasterGroup2D shadowCasterGroup)
|
||||
{
|
||||
if (shadowCasterGroup != null)
|
||||
shadowCasterGroup.UnregisterShadowCaster2D(shadowCaster);
|
||||
}
|
||||
|
||||
public static void AddGroup(ShadowCasterGroup2D group)
|
||||
{
|
||||
if (group == null)
|
||||
return;
|
||||
|
||||
if (s_ShadowCasterGroups == null)
|
||||
s_ShadowCasterGroups = new List<ShadowCasterGroup2D>();
|
||||
|
||||
AddShadowCasterGroupToList(group, s_ShadowCasterGroups);
|
||||
}
|
||||
|
||||
public static void RemoveGroup(ShadowCasterGroup2D group)
|
||||
{
|
||||
if (group != null && s_ShadowCasterGroups != null)
|
||||
RemoveShadowCasterGroupFromList(group, s_ShadowCasterGroups);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 9d3c965386a423a4b9b2f6b30ecad735
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
|
|
@ -0,0 +1,343 @@
|
|||
using System.Collections.Generic;
|
||||
using UnityEngine.Experimental.Rendering;
|
||||
|
||||
namespace UnityEngine.Rendering.Universal
|
||||
{
|
||||
// TODO: Culling of shadow casters, rotate color channels for shadow casting, check get material functions.
|
||||
|
||||
|
||||
internal static class ShadowRendering
|
||||
{
|
||||
private static readonly int k_LightPosID = Shader.PropertyToID("_LightPos");
|
||||
private static readonly int k_SelfShadowingID = Shader.PropertyToID("_SelfShadowing");
|
||||
private static readonly int k_ShadowStencilGroupID = Shader.PropertyToID("_ShadowStencilGroup");
|
||||
private static readonly int k_ShadowIntensityID = Shader.PropertyToID("_ShadowIntensity");
|
||||
private static readonly int k_ShadowVolumeIntensityID = Shader.PropertyToID("_ShadowVolumeIntensity");
|
||||
private static readonly int k_ShadowRadiusID = Shader.PropertyToID("_ShadowRadius");
|
||||
private static readonly int k_ShadowColorMaskID = Shader.PropertyToID("_ShadowColorMask");
|
||||
private static readonly int k_ShadowModelMatrixID = Shader.PropertyToID("_ShadowModelMatrix");
|
||||
private static readonly int k_ShadowModelInvMatrixID = Shader.PropertyToID("_ShadowModelInvMatrix");
|
||||
private static readonly int k_ShadowModelScaleID = Shader.PropertyToID("_ShadowModelScale");
|
||||
|
||||
private static readonly ProfilingSampler m_ProfilingSamplerShadows = new ProfilingSampler("Draw 2D Shadow Texture");
|
||||
private static readonly ProfilingSampler m_ProfilingSamplerShadowsA = new ProfilingSampler("Draw 2D Shadows (A)");
|
||||
private static readonly ProfilingSampler m_ProfilingSamplerShadowsR = new ProfilingSampler("Draw 2D Shadows (R)");
|
||||
private static readonly ProfilingSampler m_ProfilingSamplerShadowsG = new ProfilingSampler("Draw 2D Shadows (G)");
|
||||
private static readonly ProfilingSampler m_ProfilingSamplerShadowsB = new ProfilingSampler("Draw 2D Shadows (B)");
|
||||
|
||||
private static RenderTargetHandle[] m_RenderTargets = null;
|
||||
private static RenderTargetIdentifier[] m_LightInputTextures = null;
|
||||
private static readonly Color[] k_ColorLookup = new Color[4] { new Color(0, 0, 0, 1), new Color(0, 0, 1, 0), new Color(0, 1, 0, 0), new Color(1, 0, 0, 0) };
|
||||
private static readonly ProfilingSampler[] m_ProfilingSamplerShadowColorsLookup = new ProfilingSampler[4] { m_ProfilingSamplerShadowsA, m_ProfilingSamplerShadowsB, m_ProfilingSamplerShadowsG, m_ProfilingSamplerShadowsR };
|
||||
|
||||
public static uint maxTextureCount { get; private set; }
|
||||
|
||||
public static void InitializeBudget(uint maxTextureCount)
|
||||
{
|
||||
if (m_RenderTargets == null || m_RenderTargets.Length != maxTextureCount)
|
||||
{
|
||||
m_RenderTargets = new RenderTargetHandle[maxTextureCount];
|
||||
ShadowRendering.maxTextureCount = maxTextureCount;
|
||||
|
||||
for (int i = 0; i < maxTextureCount; i++)
|
||||
{
|
||||
unsafe
|
||||
{
|
||||
m_RenderTargets[i].id = Shader.PropertyToID($"ShadowTex_{i}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (m_LightInputTextures == null || m_LightInputTextures.Length != maxTextureCount)
|
||||
{
|
||||
m_LightInputTextures = new RenderTargetIdentifier[maxTextureCount];
|
||||
}
|
||||
}
|
||||
|
||||
private static Material[] CreateMaterials(Shader shader, int pass = 0)
|
||||
{
|
||||
const int k_ColorChannels = 4;
|
||||
Material[] materials = new Material[k_ColorChannels];
|
||||
|
||||
for (int i = 0; i < k_ColorChannels; i++)
|
||||
{
|
||||
materials[i] = CoreUtils.CreateEngineMaterial(shader);
|
||||
materials[i].SetInt(k_ShadowColorMaskID, 1 << i);
|
||||
materials[i].SetPass(pass);
|
||||
}
|
||||
|
||||
return materials;
|
||||
}
|
||||
|
||||
private static Material GetProjectedShadowMaterial(this Renderer2DData rendererData, int colorIndex)
|
||||
{
|
||||
//rendererData.projectedShadowMaterial = null;
|
||||
if (rendererData.projectedShadowMaterial == null || rendererData.projectedShadowMaterial.Length == 0 || rendererData.projectedShadowShader != rendererData.projectedShadowMaterial[0].shader)
|
||||
{
|
||||
rendererData.projectedShadowMaterial = CreateMaterials(rendererData.projectedShadowShader);
|
||||
}
|
||||
|
||||
return rendererData.projectedShadowMaterial[colorIndex];
|
||||
}
|
||||
|
||||
private static Material GetStencilOnlyShadowMaterial(this Renderer2DData rendererData, int colorIndex)
|
||||
{
|
||||
//rendererData.stencilOnlyShadowMaterial = null;
|
||||
if (rendererData.stencilOnlyShadowMaterial == null || rendererData.stencilOnlyShadowMaterial.Length == 0 || rendererData.projectedShadowShader != rendererData.stencilOnlyShadowMaterial[0].shader)
|
||||
{
|
||||
rendererData.stencilOnlyShadowMaterial = CreateMaterials(rendererData.projectedShadowShader, 1);
|
||||
}
|
||||
|
||||
return rendererData.stencilOnlyShadowMaterial[colorIndex];
|
||||
}
|
||||
|
||||
private static Material GetSpriteSelfShadowMaterial(this Renderer2DData rendererData, int colorIndex)
|
||||
{
|
||||
//rendererData.spriteSelfShadowMaterial = null;
|
||||
if (rendererData.spriteSelfShadowMaterial == null || rendererData.spriteSelfShadowMaterial.Length == 0 || rendererData.spriteShadowShader != rendererData.spriteSelfShadowMaterial[0].shader)
|
||||
{
|
||||
rendererData.spriteSelfShadowMaterial = CreateMaterials(rendererData.spriteShadowShader);
|
||||
}
|
||||
|
||||
return rendererData.spriteSelfShadowMaterial[colorIndex];
|
||||
}
|
||||
|
||||
private static Material GetSpriteUnshadowMaterial(this Renderer2DData rendererData, int colorIndex)
|
||||
{
|
||||
//rendererData.spriteUnshadowMaterial = null;
|
||||
if (rendererData.spriteUnshadowMaterial == null || rendererData.spriteUnshadowMaterial.Length == 0 || rendererData.spriteUnshadowShader != rendererData.spriteUnshadowMaterial[0].shader)
|
||||
{
|
||||
rendererData.spriteUnshadowMaterial = CreateMaterials(rendererData.spriteUnshadowShader);
|
||||
}
|
||||
|
||||
return rendererData.spriteUnshadowMaterial[colorIndex];
|
||||
}
|
||||
|
||||
private static Material GetGeometryUnshadowMaterial(this Renderer2DData rendererData, int colorIndex)
|
||||
{
|
||||
//rendererData.geometryUnshadowMaterial = null;
|
||||
if (rendererData.geometryUnshadowMaterial == null || rendererData.geometryUnshadowMaterial.Length == 0 || rendererData.geometryUnshadowShader != rendererData.geometryUnshadowMaterial[0].shader)
|
||||
{
|
||||
rendererData.geometryUnshadowMaterial = CreateMaterials(rendererData.geometryUnshadowShader);
|
||||
}
|
||||
|
||||
return rendererData.geometryUnshadowMaterial[colorIndex];
|
||||
}
|
||||
|
||||
public static void CreateShadowRenderTexture(IRenderPass2D pass, RenderingData renderingData, CommandBuffer cmdBuffer, int shadowIndex)
|
||||
{
|
||||
CreateShadowRenderTexture(pass, m_RenderTargets[shadowIndex], renderingData, cmdBuffer);
|
||||
}
|
||||
|
||||
public static bool PrerenderShadows(IRenderPass2D pass, RenderingData renderingData, CommandBuffer cmdBuffer, int layerToRender, Light2D light, int shadowIndex, float shadowIntensity)
|
||||
{
|
||||
var colorChannel = shadowIndex % 4;
|
||||
var textureIndex = shadowIndex / 4;
|
||||
|
||||
if (colorChannel == 0)
|
||||
ShadowRendering.CreateShadowRenderTexture(pass, renderingData, cmdBuffer, textureIndex);
|
||||
|
||||
bool hadShadowsToRender = RenderShadows(pass, renderingData, cmdBuffer, layerToRender, light, shadowIntensity, m_RenderTargets[textureIndex].Identifier(), colorChannel);
|
||||
|
||||
// Render the shadows for this light
|
||||
if (RenderShadows(pass, renderingData, cmdBuffer, layerToRender, light, shadowIntensity, m_RenderTargets[textureIndex].Identifier(), colorChannel))
|
||||
m_LightInputTextures[textureIndex] = m_RenderTargets[textureIndex].Identifier();
|
||||
else
|
||||
m_LightInputTextures[textureIndex] = Texture2D.blackTexture;
|
||||
|
||||
return hadShadowsToRender;
|
||||
}
|
||||
|
||||
public static void SetGlobalShadowTexture(CommandBuffer cmdBuffer, Light2D light, int shadowIndex)
|
||||
{
|
||||
var colorChannel = shadowIndex % 4;
|
||||
var textureIndex = shadowIndex / 4;
|
||||
|
||||
cmdBuffer.SetGlobalTexture("_ShadowTex", m_LightInputTextures[textureIndex]);
|
||||
cmdBuffer.SetGlobalColor(k_ShadowColorMaskID, k_ColorLookup[colorChannel]);
|
||||
cmdBuffer.SetGlobalFloat(k_ShadowIntensityID, 1 - light.shadowIntensity);
|
||||
cmdBuffer.SetGlobalFloat(k_ShadowVolumeIntensityID, 1 - light.shadowVolumeIntensity);
|
||||
}
|
||||
|
||||
public static void DisableGlobalShadowTexture(CommandBuffer cmdBuffer)
|
||||
{
|
||||
cmdBuffer.SetGlobalFloat(k_ShadowIntensityID, 1);
|
||||
cmdBuffer.SetGlobalFloat(k_ShadowVolumeIntensityID, 1);
|
||||
}
|
||||
|
||||
private static void CreateShadowRenderTexture(IRenderPass2D pass, RenderTargetHandle rtHandle, RenderingData renderingData, CommandBuffer cmdBuffer)
|
||||
{
|
||||
var renderTextureScale = Mathf.Clamp(pass.rendererData.lightRenderTextureScale, 0.01f, 1.0f);
|
||||
var width = (int)(renderingData.cameraData.cameraTargetDescriptor.width * renderTextureScale);
|
||||
var height = (int)(renderingData.cameraData.cameraTargetDescriptor.height * renderTextureScale);
|
||||
|
||||
var descriptor = new RenderTextureDescriptor(width, height);
|
||||
descriptor.useMipMap = false;
|
||||
descriptor.autoGenerateMips = false;
|
||||
descriptor.depthBufferBits = 24;
|
||||
descriptor.graphicsFormat = GraphicsFormat.R8G8B8A8_UNorm;
|
||||
descriptor.msaaSamples = 1;
|
||||
descriptor.dimension = TextureDimension.Tex2D;
|
||||
|
||||
cmdBuffer.GetTemporaryRT(rtHandle.id, descriptor, FilterMode.Bilinear);
|
||||
}
|
||||
|
||||
public static void ReleaseShadowRenderTexture(CommandBuffer cmdBuffer, int shadowIndex)
|
||||
{
|
||||
var colorChannel = shadowIndex % 4;
|
||||
var textureIndex = shadowIndex / 4;
|
||||
|
||||
if (colorChannel == 0)
|
||||
cmdBuffer.ReleaseTemporaryRT(m_RenderTargets[textureIndex].id);
|
||||
}
|
||||
|
||||
public static void SetShadowProjectionGlobals(CommandBuffer cmdBuffer, ShadowCaster2D shadowCaster)
|
||||
{
|
||||
cmdBuffer.SetGlobalVector(k_ShadowModelScaleID, shadowCaster.m_CachedLossyScale);
|
||||
cmdBuffer.SetGlobalMatrix(k_ShadowModelMatrixID, shadowCaster.m_CachedShadowMatrix);
|
||||
cmdBuffer.SetGlobalMatrix(k_ShadowModelInvMatrixID, shadowCaster.m_CachedInverseShadowMatrix);
|
||||
}
|
||||
|
||||
public static bool RenderShadows(IRenderPass2D pass, RenderingData renderingData, CommandBuffer cmdBuffer, int layerToRender, Light2D light, float shadowIntensity, RenderTargetIdentifier renderTexture, int colorBit)
|
||||
{
|
||||
using (new ProfilingScope(cmdBuffer, m_ProfilingSamplerShadows))
|
||||
{
|
||||
bool hasShadow = false;
|
||||
var shadowCasterGroups = ShadowCasterGroup2DManager.shadowCasterGroups;
|
||||
if (shadowCasterGroups != null && shadowCasterGroups.Count > 0)
|
||||
{
|
||||
// Before doing anything check to see if any of the shadow casters are visible to this light
|
||||
for (var group = 0; group < shadowCasterGroups.Count; group++)
|
||||
{
|
||||
var shadowCasterGroup = shadowCasterGroups[group];
|
||||
var shadowCasters = shadowCasterGroup.GetShadowCasters();
|
||||
|
||||
if (shadowCasters != null)
|
||||
{
|
||||
// Draw the projected shadows for the shadow caster group. Writing into the group stencil buffer bit
|
||||
for (var i = 0; i < shadowCasters.Count; i++)
|
||||
{
|
||||
var shadowCaster = shadowCasters[i];
|
||||
if (shadowCaster != null && shadowCaster.IsLit(light) && shadowCaster.IsShadowedLayer(layerToRender))
|
||||
{
|
||||
hasShadow = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (hasShadow)
|
||||
{
|
||||
cmdBuffer.SetRenderTarget(renderTexture, RenderBufferLoadAction.DontCare, RenderBufferStoreAction.Store, RenderBufferLoadAction.DontCare, RenderBufferStoreAction.DontCare);
|
||||
|
||||
using (new ProfilingScope(cmdBuffer, m_ProfilingSamplerShadowColorsLookup[colorBit]))
|
||||
{
|
||||
if (colorBit == 0)
|
||||
cmdBuffer.ClearRenderTarget(true, true, Color.clear);
|
||||
else
|
||||
cmdBuffer.ClearRenderTarget(true, false, Color.clear);
|
||||
|
||||
var shadowRadius = light.boundingSphere.radius;
|
||||
|
||||
cmdBuffer.SetGlobalVector(k_LightPosID, light.transform.position);
|
||||
cmdBuffer.SetGlobalFloat(k_ShadowRadiusID, shadowRadius);
|
||||
|
||||
|
||||
cmdBuffer.SetGlobalColor(k_ShadowColorMaskID, k_ColorLookup[colorBit]);
|
||||
var unshadowGeometryMaterial = pass.rendererData.GetGeometryUnshadowMaterial(colorBit);
|
||||
var projectedShadowsMaterial = pass.rendererData.GetProjectedShadowMaterial(colorBit);
|
||||
var selfShadowMaterial = pass.rendererData.GetSpriteSelfShadowMaterial(colorBit);
|
||||
var unshadowMaterial = pass.rendererData.GetSpriteUnshadowMaterial(colorBit);
|
||||
var setGlobalStencilMaterial = pass.rendererData.GetStencilOnlyShadowMaterial(colorBit);
|
||||
|
||||
for (var group = 0; group < shadowCasterGroups.Count; group++)
|
||||
{
|
||||
var shadowCasterGroup = shadowCasterGroups[group];
|
||||
var shadowCasters = shadowCasterGroup.GetShadowCasters();
|
||||
|
||||
if (shadowCasters != null)
|
||||
{
|
||||
for (var i = 0; i < shadowCasters.Count; i++)
|
||||
{
|
||||
var shadowCaster = shadowCasters[i];
|
||||
|
||||
if (shadowCaster.IsLit(light))
|
||||
{
|
||||
if (shadowCaster != null && projectedShadowsMaterial != null && shadowCaster.IsShadowedLayer(layerToRender))
|
||||
{
|
||||
if (shadowCaster.castsShadows)
|
||||
{
|
||||
SetShadowProjectionGlobals(cmdBuffer, shadowCaster);
|
||||
|
||||
cmdBuffer.DrawMesh(shadowCaster.mesh, shadowCaster.m_CachedLocalToWorldMatrix, unshadowGeometryMaterial, 0, 0);
|
||||
cmdBuffer.DrawMesh(shadowCaster.mesh, shadowCaster.m_CachedLocalToWorldMatrix, projectedShadowsMaterial, 0, 0);
|
||||
cmdBuffer.DrawMesh(shadowCaster.mesh, shadowCaster.m_CachedLocalToWorldMatrix, unshadowGeometryMaterial, 0, 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Draw the sprites, either as self shadowing or unshadowing
|
||||
for (var i = 0; i < shadowCasters.Count; i++)
|
||||
{
|
||||
var shadowCaster = shadowCasters[i];
|
||||
|
||||
if (shadowCaster.IsLit(light))
|
||||
{
|
||||
if (shadowCaster != null && shadowCaster.IsShadowedLayer(layerToRender))
|
||||
{
|
||||
if (shadowCaster.useRendererSilhouette)
|
||||
{
|
||||
// Draw using the sprite renderer
|
||||
var renderer = (Renderer)null;
|
||||
shadowCaster.TryGetComponent<Renderer>(out renderer);
|
||||
|
||||
if (renderer != null)
|
||||
{
|
||||
var material = shadowCaster.selfShadows ? selfShadowMaterial : unshadowMaterial;
|
||||
if (material != null)
|
||||
cmdBuffer.DrawRenderer(renderer, material);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
var meshMat = shadowCaster.m_CachedLocalToWorldMatrix;
|
||||
var material = shadowCaster.selfShadows ? selfShadowMaterial : unshadowMaterial;
|
||||
|
||||
// Draw using the shadow mesh
|
||||
if (material != null)
|
||||
cmdBuffer.DrawMesh(shadowCaster.mesh, meshMat, material);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Draw the projected shadows for the shadow caster group. Writing clearing the group stencil bit, and setting the global bit
|
||||
for (var i = 0; i < shadowCasters.Count; i++)
|
||||
{
|
||||
var shadowCaster = shadowCasters[i];
|
||||
|
||||
if (shadowCaster.IsLit(light))
|
||||
{
|
||||
if (shadowCaster != null && projectedShadowsMaterial != null && shadowCaster.IsShadowedLayer(layerToRender))
|
||||
{
|
||||
if (shadowCaster.castsShadows)
|
||||
{
|
||||
SetShadowProjectionGlobals(cmdBuffer, shadowCaster);
|
||||
cmdBuffer.DrawMesh(shadowCaster.mesh, shadowCaster.m_CachedLocalToWorldMatrix, projectedShadowsMaterial, 0, 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return hasShadow;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: d41cfd0a88e7a024797e86c60a0ff961
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
|
|
@ -0,0 +1,214 @@
|
|||
using System.Collections.Generic;
|
||||
using System;
|
||||
using System.Linq;
|
||||
using UnityEngine.Rendering.Universal.LibTessDotNet;
|
||||
|
||||
|
||||
namespace UnityEngine.Rendering.Universal
|
||||
{
|
||||
internal class ShadowUtility
|
||||
{
|
||||
internal struct Edge : IComparable<Edge>
|
||||
{
|
||||
public int vertexIndex0;
|
||||
public int vertexIndex1;
|
||||
public Vector4 tangent;
|
||||
private bool compareReversed; // This is done so that edge AB can equal edge BA
|
||||
|
||||
public void AssignVertexIndices(int vi0, int vi1)
|
||||
{
|
||||
vertexIndex0 = vi0;
|
||||
vertexIndex1 = vi1;
|
||||
compareReversed = vi0 > vi1;
|
||||
}
|
||||
|
||||
public int Compare(Edge a, Edge b)
|
||||
{
|
||||
int adjustedVertexIndex0A = a.compareReversed ? a.vertexIndex1 : a.vertexIndex0;
|
||||
int adjustedVertexIndex1A = a.compareReversed ? a.vertexIndex0 : a.vertexIndex1;
|
||||
int adjustedVertexIndex0B = b.compareReversed ? b.vertexIndex1 : b.vertexIndex0;
|
||||
int adjustedVertexIndex1B = b.compareReversed ? b.vertexIndex0 : b.vertexIndex1;
|
||||
|
||||
// Sort first by VI0 then by VI1
|
||||
int deltaVI0 = adjustedVertexIndex0A - adjustedVertexIndex0B;
|
||||
int deltaVI1 = adjustedVertexIndex1A - adjustedVertexIndex1B;
|
||||
|
||||
if (deltaVI0 == 0)
|
||||
return deltaVI1;
|
||||
else
|
||||
return deltaVI0;
|
||||
}
|
||||
|
||||
public int CompareTo(Edge edgeToCompare)
|
||||
{
|
||||
return Compare(this, edgeToCompare);
|
||||
}
|
||||
}
|
||||
|
||||
static Edge CreateEdge(int triangleIndexA, int triangleIndexB, List<Vector3> vertices, List<int> triangles)
|
||||
{
|
||||
Edge retEdge = new Edge();
|
||||
|
||||
retEdge.AssignVertexIndices(triangles[triangleIndexA], triangles[triangleIndexB]);
|
||||
|
||||
Vector3 vertex0 = vertices[retEdge.vertexIndex0];
|
||||
vertex0.z = 0;
|
||||
Vector3 vertex1 = vertices[retEdge.vertexIndex1];
|
||||
vertex1.z = 0;
|
||||
|
||||
Vector3 edgeDir = Vector3.Normalize(vertex1 - vertex0);
|
||||
retEdge.tangent = Vector3.Cross(-Vector3.forward, edgeDir);
|
||||
|
||||
return retEdge;
|
||||
}
|
||||
|
||||
static void PopulateEdgeArray(List<Vector3> vertices, List<int> triangles, List<Edge> edges)
|
||||
{
|
||||
for (int triangleIndex = 0; triangleIndex < triangles.Count; triangleIndex += 3)
|
||||
{
|
||||
edges.Add(CreateEdge(triangleIndex, triangleIndex + 1, vertices, triangles));
|
||||
edges.Add(CreateEdge(triangleIndex + 1, triangleIndex + 2, vertices, triangles));
|
||||
edges.Add(CreateEdge(triangleIndex + 2, triangleIndex, vertices, triangles));
|
||||
}
|
||||
}
|
||||
|
||||
static bool IsOutsideEdge(int edgeIndex, List<Edge> edgesToProcess)
|
||||
{
|
||||
int previousIndex = edgeIndex - 1;
|
||||
int nextIndex = edgeIndex + 1;
|
||||
int numberOfEdges = edgesToProcess.Count;
|
||||
Edge currentEdge = edgesToProcess[edgeIndex];
|
||||
|
||||
return (previousIndex < 0 || (currentEdge.CompareTo(edgesToProcess[edgeIndex - 1]) != 0)) && (nextIndex >= numberOfEdges || (currentEdge.CompareTo(edgesToProcess[edgeIndex + 1]) != 0));
|
||||
}
|
||||
|
||||
static void SortEdges(List<Edge> edgesToProcess)
|
||||
{
|
||||
edgesToProcess.Sort();
|
||||
}
|
||||
|
||||
static void CreateShadowTriangles(List<Vector3> vertices, List<Color> colors, List<int> triangles, List<Vector4> tangents, List<Edge> edges)
|
||||
{
|
||||
for (int edgeIndex = 0; edgeIndex < edges.Count; edgeIndex++)
|
||||
{
|
||||
if (IsOutsideEdge(edgeIndex, edges))
|
||||
{
|
||||
Edge edge = edges[edgeIndex];
|
||||
tangents[edge.vertexIndex1] = -edge.tangent;
|
||||
|
||||
int newVertexIndex = vertices.Count;
|
||||
vertices.Add(vertices[edge.vertexIndex0]);
|
||||
colors.Add(colors[edge.vertexIndex0]);
|
||||
|
||||
tangents.Add(-edge.tangent);
|
||||
|
||||
triangles.Add(edge.vertexIndex0);
|
||||
triangles.Add(newVertexIndex);
|
||||
triangles.Add(edge.vertexIndex1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static object InterpCustomVertexData(Vec3 position, object[] data, float[] weights)
|
||||
{
|
||||
return data[0];
|
||||
}
|
||||
|
||||
static void InitializeTangents(int tangentsToAdd, List<Vector4> tangents)
|
||||
{
|
||||
for (int i = 0; i < tangentsToAdd; i++)
|
||||
tangents.Add(Vector4.zero);
|
||||
}
|
||||
|
||||
static internal void ComputeBoundingSphere(Vector3[] shapePath, out BoundingSphere boundingSphere)
|
||||
{
|
||||
float minX = float.MaxValue;
|
||||
float maxX = float.MinValue;
|
||||
float minY = float.MaxValue;
|
||||
float maxY = float.MinValue;
|
||||
|
||||
// Add outline vertices
|
||||
int pathLength = shapePath.Length;
|
||||
for (int i = 0; i < pathLength; i++)
|
||||
{
|
||||
Vector3 vertex = shapePath[i];
|
||||
|
||||
if (minX > vertex.x)
|
||||
minX = vertex.x;
|
||||
if (maxX < vertex.x)
|
||||
maxX = vertex.x;
|
||||
|
||||
if (minY > vertex.y)
|
||||
minY = vertex.y;
|
||||
if (maxY < vertex.y)
|
||||
maxY = vertex.y;
|
||||
}
|
||||
|
||||
// Calculate bounding sphere (circle)
|
||||
Vector3 origin = new Vector2(0.5f * (minX + maxX), 0.5f * (minY + maxY));
|
||||
float deltaX = maxX - minX;
|
||||
float deltaY = maxY - minY;
|
||||
float radius = 0.5f * Mathf.Sqrt(deltaX * deltaX + deltaY * deltaY);
|
||||
|
||||
boundingSphere.position = origin;
|
||||
boundingSphere.radius = radius;
|
||||
}
|
||||
|
||||
public static BoundingSphere GenerateShadowMesh(Mesh mesh, Vector3[] shapePath)
|
||||
{
|
||||
List<Vector3> vertices = new List<Vector3>();
|
||||
List<int> triangles = new List<int>();
|
||||
List<Vector4> tangents = new List<Vector4>();
|
||||
List<Color> extrusion = new List<Color>();
|
||||
|
||||
// Create interior geometry
|
||||
int pointCount = shapePath.Length;
|
||||
var inputs = new ContourVertex[2 * pointCount];
|
||||
for (int i = 0; i < pointCount; i++)
|
||||
{
|
||||
Color extrusionData = new Color(shapePath[i].x, shapePath[i].y, shapePath[i].x, shapePath[i].y);
|
||||
int nextPoint = (i + 1) % pointCount;
|
||||
inputs[2 * i] = new ContourVertex() { Position = new Vec3() { X = shapePath[i].x, Y = shapePath[i].y, Z = 0 }, Data = extrusionData };
|
||||
|
||||
extrusionData = new Color(shapePath[i].x, shapePath[i].y, shapePath[nextPoint].x, shapePath[nextPoint].y);
|
||||
Vector2 midPoint = 0.5f * (shapePath[i] + shapePath[nextPoint]);
|
||||
inputs[2 * i + 1] = new ContourVertex() { Position = new Vec3() { X = midPoint.x, Y = midPoint.y, Z = 0 }, Data = extrusionData };
|
||||
}
|
||||
|
||||
Tess tessI = new Tess();
|
||||
tessI.AddContour(inputs, ContourOrientation.Original);
|
||||
tessI.Tessellate(WindingRule.EvenOdd, ElementType.Polygons, 3, InterpCustomVertexData);
|
||||
|
||||
var indicesI = tessI.Elements.Select(i => i).ToArray();
|
||||
var verticesI = tessI.Vertices.Select(v => new Vector3(v.Position.X, v.Position.Y, 0)).ToArray();
|
||||
var extrusionI = tessI.Vertices.Select(v => new Color(((Color)v.Data).r, ((Color)v.Data).g, ((Color)v.Data).b, ((Color)v.Data).a)).ToArray();
|
||||
|
||||
vertices.AddRange(verticesI);
|
||||
triangles.AddRange(indicesI);
|
||||
extrusion.AddRange(extrusionI);
|
||||
|
||||
InitializeTangents(vertices.Count, tangents);
|
||||
|
||||
List<Edge> edges = new List<Edge>();
|
||||
PopulateEdgeArray(vertices, triangles, edges);
|
||||
SortEdges(edges);
|
||||
CreateShadowTriangles(vertices, extrusion, triangles, tangents, edges);
|
||||
|
||||
Color[] finalExtrusion = extrusion.ToArray();
|
||||
Vector3[] finalVertices = vertices.ToArray();
|
||||
int[] finalTriangles = triangles.ToArray();
|
||||
Vector4[] finalTangents = tangents.ToArray();
|
||||
|
||||
mesh.Clear();
|
||||
mesh.vertices = finalVertices;
|
||||
mesh.triangles = finalTriangles;
|
||||
mesh.tangents = finalTangents;
|
||||
mesh.colors = finalExtrusion;
|
||||
|
||||
BoundingSphere retSphere;
|
||||
ComputeBoundingSphere(shapePath, out retSphere);
|
||||
|
||||
return retSphere;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: ed5876db37f0e544c8ef9b9802eff934
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
Loading…
Add table
Add a link
Reference in a new issue