Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Expand Up @@ -1656,6 +1656,7 @@ GameObject:
m_Component:
- component: {fileID: 509122663}
- component: {fileID: 509122664}
- component: {fileID: 509122665}
m_Layer: 0
m_Name: Point Light (1)
m_TagString: Untagged
Expand Down Expand Up @@ -1699,8 +1700,8 @@ Light:
m_Resolution: -1
m_CustomResolution: -1
m_Strength: 1
m_Bias: 0.05
m_NormalBias: 0.4
m_Bias: 1
m_NormalBias: 1.3
m_NearPlane: 0.2
m_CullingMatrixOverride:
e00: 1
Expand Down Expand Up @@ -1738,6 +1739,19 @@ Light:
m_UseBoundingSphereOverride: 0
m_ShadowRadius: 0.25
m_ShadowAngle: 0
--- !u!114 &509122665
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 509122662}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 474bcb49853aa07438625e644c072ee6, type: 3}
m_Name:
m_EditorClassIdentifier:
m_UsePipelineSettings: 0
--- !u!1 &558490757
GameObject:
m_ObjectHideFlags: 0
Expand Down
Expand Up @@ -4953,6 +4953,7 @@ GameObject:
m_Component:
- component: {fileID: 1014564737}
- component: {fileID: 1014564736}
- component: {fileID: 1014564738}
m_Layer: 0
m_Name: Directional Light
m_TagString: Untagged
Expand Down Expand Up @@ -4982,8 +4983,8 @@ Light:
m_Resolution: -1
m_CustomResolution: -1
m_Strength: 1
m_Bias: 0.05
m_NormalBias: 0.4
m_Bias: 1
m_NormalBias: 1.3
m_NearPlane: 0.2
m_CullingMatrixOverride:
e00: 1
Expand Down Expand Up @@ -5036,6 +5037,19 @@ Transform:
m_Father: {fileID: 0}
m_RootOrder: 2
m_LocalEulerAnglesHint: {x: 35, y: 135, z: 0}
--- !u!114 &1014564738
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 1014564735}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 474bcb49853aa07438625e644c072ee6, type: 3}
m_Name:
m_EditorClassIdentifier:
m_UsePipelineSettings: 0
--- !u!1 &1054029614
GameObject:
m_ObjectHideFlags: 0
Expand Down
Expand Up @@ -4655,6 +4655,7 @@ GameObject:
m_Component:
- component: {fileID: 1042241898}
- component: {fileID: 1042241897}
- component: {fileID: 1042241899}
m_Layer: 0
m_Name: Directional Light
m_TagString: Untagged
Expand Down Expand Up @@ -4684,8 +4685,8 @@ Light:
m_Resolution: -1
m_CustomResolution: -1
m_Strength: 1
m_Bias: 0.05
m_NormalBias: 0.4
m_Bias: 1
m_NormalBias: 1.5
m_NearPlane: 0.2
m_CullingMatrixOverride:
e00: 1
Expand Down Expand Up @@ -4738,6 +4739,19 @@ Transform:
m_Father: {fileID: 0}
m_RootOrder: 1
m_LocalEulerAnglesHint: {x: 35, y: 135, z: 0}
--- !u!114 &1042241899
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 1042241896}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 474bcb49853aa07438625e644c072ee6, type: 3}
m_Name:
m_EditorClassIdentifier:
m_UsePipelineSettings: 0
--- !u!1 &1067356581
GameObject:
m_ObjectHideFlags: 0
Expand Down
Expand Up @@ -4968,6 +4968,7 @@ GameObject:
m_Component:
- component: {fileID: 1014564737}
- component: {fileID: 1014564736}
- component: {fileID: 1014564738}
m_Layer: 0
m_Name: Directional Light
m_TagString: Untagged
Expand Down Expand Up @@ -4997,8 +4998,8 @@ Light:
m_Resolution: -1
m_CustomResolution: -1
m_Strength: 1
m_Bias: 0.05
m_NormalBias: 0.4
m_Bias: 1
m_NormalBias: 1.5
m_NearPlane: 0.2
m_CullingMatrixOverride:
e00: 1
Expand Down Expand Up @@ -5051,6 +5052,19 @@ Transform:
m_Father: {fileID: 0}
m_RootOrder: 2
m_LocalEulerAnglesHint: {x: 35, y: 135, z: 0}
--- !u!114 &1014564738
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 1014564735}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 474bcb49853aa07438625e644c072ee6, type: 3}
m_Name:
m_EditorClassIdentifier:
m_UsePipelineSettings: 0
--- !u!1 &1054029614
GameObject:
m_ObjectHideFlags: 0
Expand Down

Large diffs are not rendered by default.

Expand Up @@ -304,6 +304,9 @@ EditorBuildSettings:
- enabled: 1
path: Assets/Scenes/145_SoftParticlesStackWithPP.unity
guid: 7b2479971db0f4b1b87c121244585fe5
- enabled: 1
path: Assets/Scenes/145_ManyAdditionalLightShadowTypes.unity
guid: 625fae218da02c440b56ea72ea59d986
- enabled: 1
path: Assets/Scenes/146_MSAA_RenderToBackbuffer.unity
guid: aee9f891d65e44daf811d30053402efd
Expand Down
Expand Up @@ -122,7 +122,7 @@ public static void GenerateAll()
guard = "_" + guard;

writer.WriteLine("//");
writer.WriteLine("// This file was automatically generated. Please don't edit by hand.");
writer.WriteLine("// This file was automatically generated. Please don't edit by hand. Execute Editor command [ Edit / Render Pipeline / Generate Shader Includes ] instead");
writer.WriteLine("//");
writer.WriteLine();
writer.WriteLine("#ifndef " + guard);
Expand Down
3 changes: 2 additions & 1 deletion com.unity.render-pipelines.universal/CHANGELOG.md
Expand Up @@ -5,6 +5,8 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/)
and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html).

## [11.0.0] - 2020-10-21
### Added
- Added real-time Point Light Shadows.

### Added
- Added a supported MSAA samples count check, so the actual supported MSAA samples count value can be assigned to RenderTexture descriptors.
Expand Down Expand Up @@ -79,7 +81,6 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.
- Fixed MSAA override on camera does not work in non-XR project if target eye is selected to both eye.

## [10.1.0] - 2020-10-12
### Added
- Added support for the Shadowmask Mixed Lighting Mode (Forward only), which supports up to four baked-shadow Lights.
- Added ComplexLit shader for advanced material features and deferred forward fallback.
- Added Clear Coat feature for ComplexLit shader and for shader graph.
Expand Down
@@ -0,0 +1,65 @@
# Shadows in the Universal Render Pipeline

The Universal Render Pipeline’s [Lights](light-component.md) can cast shadows from one GameObject onto another. They emphasize the position and scale of GameObjects, which adds a degree of depth and realism to a Scene that could otherwise look flat.

## Shadow map resolution

The resolution of a Light’s shadow map determines the size of its shadow maps. The larger the shadow map, the more precise the shadows can be, and the better the Universal Render Pipeline can capture small details in the shadow casting geometry. Rendering shadow maps at higher resolutions make them look sharper.

The number of shadow maps Universal RP renders per Light depends on the **Type** of the Light:

- A Spot Light renders one shadow map.
- A Point Light renders six shadow maps (the number of faces in a cubemap).
- A Directional Light renders one shadow map per cascade. Set the cascade count of Directional Lights from the [Universal Render Pipeline Asset](universalrp-asset.md) of your project.

Universal RP will try to use the best resolution according to the number of shadow maps that are needed in the scene, and the size of the shadow atlases.

## Shadow atlases

Universal RP renders all real-time shadows for a frame using one common shadow map atlas for all punctual light shadows (i.e shadows for Spot Lights and Point Lights), and an other shadow map atlas for Directional Light shadows.

Set the size of these atlases in your Unity Project’s [Universal Render Pipeline Asset](universalrp-asset.md). The atlas size determines the maximum resolution of shadows in your Scene.

For example, an atlas of size 1024 x 1024 can fit:

- Four shadow maps of 512 x 512 pixels.
- Sixteen shadow maps of 256 x 256 pixels.

### Matching shadow atlas resolution to Built-In RP settings

In projects that used the **Built-In Render Pipeline**, you controlled shadow maps resolution by selecting a shadow resolution level ("Low", "Medium", "High", "Very High") in your project's Quality Settings.
For each shadow map, Unity then decided which resolution to actually use, based on the algorithm explained in the [Built-In RP Manual Page about Shadow Mapping](https://docs.unity3d.com/Manual/shadow-mapping.html).
You could then inspect in the [Frame Debugger](https://docs.unity3d.com/Manual/FrameDebugger.html) the resolution actually used for a specific shadow map.

In **Universal Render Pipeline**, you specify the resolution of the Shadow Atlases. Therefore you can control the amount of video memory your application will allocate for shadows.

If you want to make sure that the resolution Universal RP uses for a specific punctual light shadow in your project, will not go under a specific value: Consider the number of shadow maps required in the scene, and select a big enough shadow atlas resolution.

For example: if your scene has four Spot Lights and one Point light ; and you want each shadow map resolution to be at least 256x256.
Your scene needs to render ten shadow maps (one for each Spot Light, and six for the Point Light), each with resolution 256x256.
Using a shadow atlas of size 512x512 would not be enough, because it can contain only four maps of size 256x256. Therefore, you should use a shadow atlas of size 1024x1024, that can contain up to sixteen maps of size 256x256.




## Shadow Bias

Shadow maps are essentially textures projected from the point of view of the Light. Universal RP uses a bias in the projection so that the shadow casting geometry does not self-shadow itself.

In Universal RP, each individual Light component controls its own shadow biasing using the following parameters:

- **Depth Bias**
- **Normal Bias**
- **Near Plane**

Find these settings under the **Shadows** section. If properties are not visible, change the Bias setting from "Use Pipeline Settings" to "Custom" to expose them.

Using high shadow bias values may result in light "leaking" through Meshes. This is where there is a visible gap between the shadow and its caster, and leads to shadow shapes that do not accurately represent their casters.


## Performance

Here are some example of frame times (observed with SRP package revision 78d514f756c and Unity 2020.2.0b):
- Impact of adding a Point Light with hard shadows at position (2, 1, 1) to [Universal RP Project Template](https://docs.unity3d.com/Manual/ProjectTemplates.html) scene on Nintendo Switch (handheld mode): +3.3ms (total frame time 22.7ms)
- Impact of adding a Point Light with hard shadows at position (2, 1, 1) to [Universal RP Project Template](https://docs.unity3d.com/Manual/ProjectTemplates.html) scene on Galaxy S20+: +1.9ms (total frame time 22.2ms)
- Impact of adding a Point Light with hard shadows at position (2, 1, 1) to [Universal RP Project Template](https://docs.unity3d.com/Manual/ProjectTemplates.html) scene on PC - GeForce RTX 2080 Ti (Full HD): +0.1ms (total frame time 16.6ms)
Expand Up @@ -28,6 +28,7 @@
* [Light component reference](light-component)
* [Lighting Mode](urp-lighting-mode.md)
* [The Universal Additional Light Data component](universal-additional-light-data.md)
* [Shadows in the Universal Render Pipeline](Shadows-in-URP.md)
* [Cameras](cameras.md)
* [The Universal Additional Camera Data component](universal-additional-camera-data.md)
* [Render Type](camera-types-and-render-type.md)
Expand Down
Expand Up @@ -39,7 +39,7 @@ This table provides an overview of the current features supported in the Univers
| Vertex Lights | Yes | Yes |
| SH Lights | Yes | In research |
| ***Realtime Shadows*** | | |
| *Light Types*<br/>Directional<br/>Spot<br/>Point<br/>Area | <br/>Yes<br/>Yes<br/>Yes<br/>Not supported | <br/>Yes - only 1<br/>Yes<br/>In research<br/>Not supported |
| *Light Types*<br/>Directional<br/>Spot<br/>Point<br/>Area | <br/>Yes<br/>Yes<br/>Yes<br/>Not supported | <br/>Yes - only 1<br/>Yes<br/>Yes<br/>Not supported |
| *Shadow Projection*<br/>Stable Fit<br/>Close Fit | <br/>Yes<br/>Yes | <br/>Yes<br>In research |
| *Shadow Cascades*<br/>Number of Cascades<br/>Control by Percentage<br/>Control by Distance | <br/>1, 2 or 4<br/>Yes<br/>Not supported | <br/>1–4<br/>Yes<br/>Yes |
| *Shadow Resolve Type*<br/>Lighting Pass<br/>Screen Space Pass | <br/>Yes<br/>Yes | <br/>Yes<br/>No |
Expand Down
Expand Up @@ -10,7 +10,7 @@ This page describes how to upgrade from an older version of the Universal Render

## Upgrading from URP 7.2.x and later releases

1. URP 10.x.x does not support the package Post-Processing Stack v2. If your Project uses the package Post-Processing Stack v2, migrate the effects that use that package first.
1. URP 11.x.x does not support the package Post-Processing Stack v2. If your Project uses the package Post-Processing Stack v2, migrate the effects that use that package first.

### DepthNormals Pass

Expand Down Expand Up @@ -53,6 +53,12 @@ To support SSAO in custom shader, add the `DepthNormals` Pass and the `_SCREEN_S

If your custom shader implements custom lighting functions, use the function `GetScreenSpaceAmbientOcclusion(float2 normalizedScreenSpaceUV)` to get the `AmbientOcclusionFactor` value for your lighting calculations.

### Shadow Normal Bias

In 11.0.x the formula used to apply Shadow Normal Bias has been slightly fix in order to work better with punctual lights.
As a result, to match exactly shadow outlines from earlier revisions, the parameter might to be adjusted in some scenes. Typically, using 1.4 instead of 1.0 for a Directional light is usually enough.


## Upgrading from URP 7.0.x-7.1.x

1. Upgrade to URP 7.2.0 first. Refer to [Upgrading to version 7.2.0 of the Universal Render Pipeline](upgrade-guide-7-2-0).
Expand Down
@@ -1,5 +1,9 @@
#if (SHADERPASS == SHADERPASS_SHADOWCASTER)
// Shadow Casting Light geometric parameters. These variables are used when applying the shadow Normal Bias and are set by UnityEngine.Rendering.Universal.ShadowUtils.SetupShadowCasterConstantBuffer in com.unity.render-pipelines.universal/Runtime/ShadowUtils.cs
// For Directional lights, _LightDirection is used when applying shadow Normal Bias.
// For Spot lights and Point lights, _LightPosition is used to compute the actual light direction because it is different at each shadow caster geometry vertex.
float3 _LightDirection;
float3 _LightPosition;
#endif

Varyings BuildVaryings(Attributes input)
Expand Down Expand Up @@ -62,11 +66,16 @@ Varyings BuildVaryings(Attributes input)

#if (SHADERPASS == SHADERPASS_SHADOWCASTER)
// Define shadow pass specific clip position for Universal
output.positionCS = TransformWorldToHClip(ApplyShadowBias(positionWS, normalWS, _LightDirection));
#if _CASTING_PUNCTUAL_LIGHT_SHADOW
float3 lightDirectionWS = normalize(_LightPosition - positionWS);
#else
float3 lightDirectionWS = _LightDirection;
#endif
output.positionCS = TransformWorldToHClip(ApplyShadowBias(positionWS, normalWS, lightDirectionWS));
#if UNITY_REVERSED_Z
output.positionCS.z = min(output.positionCS.z, output.positionCS.w * UNITY_NEAR_CLIP_VALUE);
output.positionCS.z = min(output.positionCS.z, UNITY_NEAR_CLIP_VALUE);
#else
output.positionCS.z = max(output.positionCS.z, output.positionCS.w * UNITY_NEAR_CLIP_VALUE);
output.positionCS.z = max(output.positionCS.z, UNITY_NEAR_CLIP_VALUE);
#endif
#elif (SHADERPASS == SHADERPASS_META)
output.positionCS = MetaVertexPosition(float4(input.positionOS, 0), input.uv1, input.uv2, unity_LightmapST, unity_DynamicLightmapST);
Expand Down
Expand Up @@ -382,6 +382,7 @@ static class CorePasses
// Conditional State
renderStates = CoreRenderStates.ShadowCaster,
pragmas = CorePragmas.Instanced,
keywords = CoreKeywords.ShadowCaster,
includes = CoreIncludes.ShadowCaster,
};
}
Expand Down Expand Up @@ -723,6 +724,15 @@ static class CoreKeywordDescriptors
scope = KeywordScope.Global,
};

public static readonly KeywordDescriptor CastingPunctualLightShadow = new KeywordDescriptor()
{
displayName = "Casting Punctual Light Shadow",
referenceName = "_CASTING_PUNCTUAL_LIGHT_SHADOW",
type = KeywordType.Boolean,
definition = KeywordDefinition.MultiCompile,
scope = KeywordScope.Global,
};

public static readonly KeywordDescriptor AdditionalLights = new KeywordDescriptor()
{
displayName = "Additional Lights",
Expand Down Expand Up @@ -837,6 +847,16 @@ static class CoreKeywordDescriptors
}
#endregion

#region Keywords
static class CoreKeywords
{
public static readonly KeywordCollection ShadowCaster = new KeywordCollection
{
{ CoreKeywordDescriptors.CastingPunctualLightShadow },
};
}
#endregion

#region FieldDescriptors
static class CoreFields
{
Expand Down
10 changes: 10 additions & 0 deletions com.unity.render-pipelines.universal/Editor/ShaderPreprocessor.cs
Expand Up @@ -46,6 +46,7 @@ internal class ShaderPreprocessor : IPreprocessShaders
ShaderKeyword m_AdditionalLightShadows = new ShaderKeyword(ShaderKeywordStrings.AdditionalLightShadows);
ShaderKeyword m_DeferredAdditionalLightShadows = new ShaderKeyword(ShaderKeywordStrings._DEFERRED_ADDITIONAL_LIGHT_SHADOWS);
ShaderKeyword m_CascadeShadows = new ShaderKeyword(ShaderKeywordStrings.MainLightShadowCascades);
ShaderKeyword m_CastingPunctualLightShadow = new ShaderKeyword(ShaderKeywordStrings.CastingPunctualLightShadow);
ShaderKeyword m_SoftShadows = new ShaderKeyword(ShaderKeywordStrings.SoftShadows);
ShaderKeyword m_MixedLightingSubtractive = new ShaderKeyword(ShaderKeywordStrings.MixedLightingSubtractive);
ShaderKeyword m_LightmapShadowMixing = new ShaderKeyword(ShaderKeywordStrings.LightmapShadowMixing);
Expand Down Expand Up @@ -105,6 +106,9 @@ bool StripUnusedFeatures(ShaderFeatures features, Shader shader, ShaderSnippetDa

if (compilerData.shaderKeywordSet.IsEnabled(m_CascadeShadows))
return true;

if (snippetData.passType == PassType.ShadowCaster && !compilerData.shaderKeywordSet.IsEnabled(m_CastingPunctualLightShadow))
return true;
}

if (!IsFeatureEnabled(features, ShaderFeatures.SoftShadows) &&
Expand All @@ -127,11 +131,17 @@ bool StripUnusedFeatures(ShaderFeatures features, Shader shader, ShaderSnippetDa
!IsFeatureEnabled(features, ShaderFeatures.MixedLighting))
return true;


// No additional light shadows
bool isAdditionalLightShadow = compilerData.shaderKeywordSet.IsEnabled(m_AdditionalLightShadows);
if (!IsFeatureEnabled(features, ShaderFeatures.AdditionalLightShadows) && isAdditionalLightShadow)
return true;

bool isPunctualLightShadowCasterPass = (snippetData.passType == PassType.ShadowCaster) && compilerData.shaderKeywordSet.IsEnabled(m_CastingPunctualLightShadow);
if (!IsFeatureEnabled(features, ShaderFeatures.AdditionalLightShadows) && isPunctualLightShadowCasterPass)
if (compilerData.shaderCompilerPlatform != ShaderCompilerPlatform.GLES3x) // [Work-around] We do not strip this variant on GLES3 because it could make some scenes crash current Unity GLES3 AndroidPlayer on OnePlus6T - TODO: remove this line once https://issuetracker.unity3d.com/product/unity/issues/guid/1293454/ is fixed
return true;

bool isDeferredAdditionalShadow = compilerData.shaderKeywordSet.IsEnabled(m_DeferredAdditionalLightShadows);
if (!IsFeatureEnabled(features, ShaderFeatures.AdditionalLightShadows) && isDeferredAdditionalShadow)
return true;
Expand Down
Expand Up @@ -27,10 +27,10 @@ class Styles
public readonly GUIContent DisabledLightWarning = EditorGUIUtility.TrTextContent("Lighting has been disabled in at least one Scene view. Any changes applied to lights in the Scene will not be updated in these views until Lighting has been enabled again.");
public readonly GUIContent SunSourceWarning = EditorGUIUtility.TrTextContent("This light is set as the current Sun Source, which requires a directional light. Go to the Lighting Window's Environment settings to edit the Sun Source.");

public readonly GUIContent ShadowsNotSupportedWarning = EditorGUIUtility.TrTextContent("Realtime shadows for point lights are not supported. Either disable shadows or set the light mode to Baked.");
public static readonly GUIContent ShadowRealtimeSettings = EditorGUIUtility.TrTextContent("Realtime Shadows", "Settings for realtime direct shadows.");
public static readonly GUIContent ShadowStrength = EditorGUIUtility.TrTextContent("Strength", "Controls how dark the shadows cast by the light will be.");
public static readonly GUIContent ShadowNearPlane = EditorGUIUtility.TrTextContent("Near Plane", "Controls the value for the near clip plane when rendering shadows. Currently clamped to 0.1 units or 1% of the lights range property, whichever is lower.");
public static readonly GUIContent ShadowNormalBias = EditorGUIUtility.TrTextContent("Normal", "Controls the distance shadow caster vertices are offset along their normals when rendering shadow maps. Currently ignored for Point Lights.");

public static GUIContent shadowBias = EditorGUIUtility.TrTextContent("Bias", "Select if the Bias should use the settings from the Pipeline Asset or Custom settings.");
public static int[] optionDefaultValues = { 0, 1 };
Expand All @@ -54,8 +54,8 @@ class Styles
public bool dirOptionsValue { get { return typeIsSame && lightProperty.type == LightType.Directional; } }
public bool areaOptionsValue { get { return typeIsSame && (lightProperty.type == LightType.Rectangle || lightProperty.type == LightType.Disc); } }

// Point light realtime shadows not supported
public bool runtimeOptionsValue { get { return typeIsSame && (lightProperty.type != LightType.Rectangle && lightProperty.type != LightType.Point && !settings.isCompletelyBaked); } }
// Area light shadows not supported
public bool runtimeOptionsValue { get { return typeIsSame && (lightProperty.type != LightType.Rectangle && !settings.isCompletelyBaked); } }
public bool bakedShadowRadius { get { return typeIsSame && (lightProperty.type == LightType.Point || lightProperty.type == LightType.Spot) && settings.isBakedOrMixed; } }
public bool bakedShadowAngle { get { return typeIsSame && lightProperty.type == LightType.Directional && settings.isBakedOrMixed; } }
public bool shadowOptionsValue { get { return shadowTypeIsSame && lightProperty.shadows != LightShadows.None; } }
Expand All @@ -66,16 +66,6 @@ class Styles

public bool isShadowEnabled { get { return settings.shadowsType.intValue != 0; } }

public bool realtimeShadowsWarningValue
{
get
{
return typeIsSame && lightProperty.type == LightType.Point &&
shadowTypeIsSame && isShadowEnabled &&
lightmappingTypeIsSame && !settings.isCompletelyBaked;
}
}

UniversalAdditionalLightData m_AdditionalLightData;
SerializedObject m_AdditionalLightDataSO;

Expand Down Expand Up @@ -254,7 +244,7 @@ void DrawAdditionalShadowData()
{
EditorGUI.indentLevel++;
EditorGUILayout.Slider(settings.shadowsBias, 0f, 10f, "Depth");
EditorGUILayout.Slider(settings.shadowsNormalBias, 0f, 10f, "Normal");
EditorGUILayout.Slider(settings.shadowsNormalBias, 0f, 10f, Styles.ShadowNormalBias);
EditorGUI.indentLevel--;

m_AdditionalLightDataSO.ApplyModifiedProperties();
Expand Down Expand Up @@ -322,9 +312,6 @@ void ShadowsGUI()
if (bakingWarningValue)
EditorGUILayout.HelpBox(s_Styles.BakingWarning.text, MessageType.Warning);

if (realtimeShadowsWarningValue)
EditorGUILayout.HelpBox(s_Styles.ShadowsNotSupportedWarning.text, MessageType.Warning);

EditorGUILayout.Space();
}

Expand Down
Expand Up @@ -135,7 +135,7 @@ public partial class UniversalRenderPipelineAsset : RenderPipelineAsset, ISerial
[SerializeField] LightRenderingMode m_AdditionalLightsRenderingMode = LightRenderingMode.PerPixel;
[SerializeField] int m_AdditionalLightsPerObjectLimit = 4;
[SerializeField] bool m_AdditionalLightShadowsSupported = false;
[SerializeField] ShadowResolution m_AdditionalLightsShadowmapResolution = ShadowResolution._512;
[SerializeField] ShadowResolution m_AdditionalLightsShadowmapResolution = ShadowResolution._2048;

// Shadows Settings
[SerializeField] float m_ShadowDistance = 50.0f;
Expand Down
11 changes: 11 additions & 0 deletions com.unity.render-pipelines.universal/Runtime/Deprecated.cs
Expand Up @@ -13,6 +13,17 @@ public abstract partial class ScriptableRenderPass
public virtual void FrameCleanup(CommandBuffer cmd) => OnCameraCleanup(cmd);
}

namespace Internal
{
public partial class AdditionalLightsShadowCasterPass
{
[Obsolete("AdditionalLightsShadowCasterPass.m_AdditionalShadowsBufferId was deprecated. Shadow slice matrix is now passed to the GPU using an entry in buffer m_AdditionalLightsWorldToShadow_SSBO", false)]
public static int m_AdditionalShadowsBufferId;
[Obsolete("AdditionalLightsShadowCasterPass.m_AdditionalShadowsIndicesId was deprecated. Shadow slice index is now passed to the GPU using last member of an entry in buffer m_AdditionalShadowParams_SSBO", false)]
public static int m_AdditionalShadowsIndicesId;
}
}

[Obsolete("This is obsolete, please use shadowCascadeCount instead.", false)]
[MovedFrom("UnityEngine.Rendering.LWRP")] public enum ShadowCascadesOption
{
Expand Down
Expand Up @@ -594,6 +594,10 @@ public override void SetupLights(ScriptableRenderContext context, ref RenderingD
else
{
// We set the number of maximum visible lights allowed and we add one for the mainlight...
//
// Note: However ScriptableRenderContext.Cull() does not differentiate between light types.
// If there is no active main light in the scene, ScriptableRenderContext.Cull() might return ( cullingParameters.maximumVisibleLights ) visible additional lights.
// i.e ScriptableRenderContext.Cull() might return ( UniversalRenderPipeline.maxVisibleAdditionalLights + 1 ) visible additional lights !
cullingParameters.maximumVisibleLights = UniversalRenderPipeline.maxVisibleAdditionalLights + 1;
}
cullingParameters.shadowDistance = cameraData.maxShadowDistance;
Expand Down

Large diffs are not rendered by default.

Expand Up @@ -10,7 +10,6 @@ public class MainLightShadowCasterPass : ScriptableRenderPass
private static class MainLightShadowConstantBuffer
{
public static int _WorldToShadow;
public static int _ShadowParams;
public static int _CascadeShadowSplitSpheres0;
public static int _CascadeShadowSplitSpheres1;
public static int _CascadeShadowSplitSpheres2;
Expand All @@ -25,7 +24,7 @@ private static class MainLightShadowConstantBuffer

const int k_MaxCascades = 4;
const int k_ShadowmapBufferBits = 16;
float m_MaxShadowDistance;
Vector4 m_MainLightShadowParams;
int m_ShadowmapWidth;
int m_ShadowmapHeight;
int m_ShadowCasterCascadesCount;
Expand All @@ -50,7 +49,6 @@ public MainLightShadowCasterPass(RenderPassEvent evt)
m_CascadeSplitDistances = new Vector4[k_MaxCascades];

MainLightShadowConstantBuffer._WorldToShadow = Shader.PropertyToID("_MainLightWorldToShadow");
MainLightShadowConstantBuffer._ShadowParams = Shader.PropertyToID("_MainLightShadowParams");
MainLightShadowConstantBuffer._CascadeShadowSplitSpheres0 = Shader.PropertyToID("_CascadeShadowSplitSpheres0");
MainLightShadowConstantBuffer._CascadeShadowSplitSpheres1 = Shader.PropertyToID("_CascadeShadowSplitSpheres1");
MainLightShadowConstantBuffer._CascadeShadowSplitSpheres2 = Shader.PropertyToID("_CascadeShadowSplitSpheres2");
Expand Down Expand Up @@ -105,13 +103,13 @@ public bool Setup(ref RenderingData renderingData)
{
bool success = ShadowUtils.ExtractDirectionalLightMatrix(ref renderingData.cullResults, ref renderingData.shadowData,
shadowLightIndex, cascadeIndex, m_ShadowmapWidth, m_ShadowmapHeight, shadowResolution, light.shadowNearPlane,
out m_CascadeSplitDistances[cascadeIndex], out m_CascadeSlices[cascadeIndex], out m_CascadeSlices[cascadeIndex].viewMatrix, out m_CascadeSlices[cascadeIndex].projectionMatrix);
out m_CascadeSplitDistances[cascadeIndex], out m_CascadeSlices[cascadeIndex]);

if (!success)
return false;
}

m_MaxShadowDistance = renderingData.cameraData.maxShadowDistance * renderingData.cameraData.maxShadowDistance;
m_MainLightShadowParams = ShadowUtils.GetMainLightShadowParams(ref renderingData);

return true;
}
Expand Down Expand Up @@ -174,11 +172,14 @@ void RenderMainLightCascadeShadowmap(ref ScriptableRenderContext context, ref Cu

for (int cascadeIndex = 0; cascadeIndex < m_ShadowCasterCascadesCount; ++cascadeIndex)
{
//settings.splitData = m_CascadeSlices[cascadeIndex].splitData; // NOTE: currently DrawShadows culls more casters if no ShadowSplitData.cullingPlanes are set (version cds 8652678b), so it is currently better to not pass the m_CascadeSlices[cascadeIndex].splitData object returned by CullingResults.ComputeDirectionalShadowMatricesAndCullingPrimitives (change introduced in 8bf71cf). Culling is only based on the ShadowSplitData.cullingSphere distances.
var splitData = settings.splitData;
splitData.cullingSphere = m_CascadeSplitDistances[cascadeIndex];
settings.splitData = splitData;

Vector4 shadowBias = ShadowUtils.GetShadowBias(ref shadowLight, shadowLightIndex, ref shadowData, m_CascadeSlices[cascadeIndex].projectionMatrix, m_CascadeSlices[cascadeIndex].resolution);
ShadowUtils.SetupShadowCasterConstantBuffer(cmd, ref shadowLight, shadowBias);
CoreUtils.SetKeyword(cmd, ShaderKeywordStrings.CastingPunctualLightShadow, false);
ShadowUtils.RenderShadowSlice(cmd, ref context, ref m_CascadeSlices[cascadeIndex],
ref settings, m_CascadeSlices[cascadeIndex].projectionMatrix, m_CascadeSlices[cascadeIndex].viewMatrix);
}
Expand All @@ -197,9 +198,6 @@ void RenderMainLightCascadeShadowmap(ref ScriptableRenderContext context, ref Cu

void SetupMainLightShadowReceiverConstants(CommandBuffer cmd, VisibleLight shadowLight, bool supportsSoftShadows)
{
Light light = shadowLight.light;
bool softShadows = shadowLight.light.shadows == LightShadows.Soft && supportsSoftShadows;

int cascadeCount = m_ShadowCasterCascadesCount;
for (int i = 0; i < cascadeCount; ++i)
m_MainLightShadowMatrices[i] = m_CascadeSlices[i].shadowTransform;
Expand All @@ -216,18 +214,10 @@ void SetupMainLightShadowReceiverConstants(CommandBuffer cmd, VisibleLight shado
float invShadowAtlasHeight = 1.0f / m_ShadowmapHeight;
float invHalfShadowAtlasWidth = 0.5f * invShadowAtlasWidth;
float invHalfShadowAtlasHeight = 0.5f * invShadowAtlasHeight;
float softShadowsProp = softShadows ? 1.0f : 0.0f;

//To make the shadow fading fit into a single MAD instruction:
//distanceCamToPixel2 * oneOverFadeDist + minusStartFade (single MAD)
float startFade = m_MaxShadowDistance * 0.9f;
float oneOverFadeDist = 1 / (m_MaxShadowDistance - startFade);
float minusStartFade = -startFade * oneOverFadeDist;


cmd.SetGlobalTexture(m_MainLightShadowmap.id, m_MainLightShadowmapTexture);
cmd.SetGlobalMatrixArray(MainLightShadowConstantBuffer._WorldToShadow, m_MainLightShadowMatrices);
cmd.SetGlobalVector(MainLightShadowConstantBuffer._ShadowParams, new Vector4(light.shadowStrength, softShadowsProp, oneOverFadeDist, minusStartFade));
ShadowUtils.SetupShadowReceiverConstantBuffer(cmd, m_MainLightShadowParams);

if (m_ShadowCasterCascadesCount > 1)
{
Expand Down
Expand Up @@ -66,7 +66,7 @@ internal static bool useStructuredBuffer
get
{
// TODO: For now disabling SSBO until figure out Vulkan binding issues.
// When enabling this also enable it in shader side in Input.hlsl
// When enabling this also enable USE_STRUCTURED_BUFFER_FOR_LIGHT_DATA in shader side in Input.hlsl
return false;

// We don't use SSBO in D3D because we can't figure out without adding shader variants if platforms is D3D10.
Expand Down
16 changes: 8 additions & 8 deletions com.unity.render-pipelines.universal/Runtime/ShaderData.cs
Expand Up @@ -9,8 +9,8 @@ class ShaderData : IDisposable
ComputeBuffer m_LightDataBuffer = null;
ComputeBuffer m_LightIndicesBuffer = null;

ComputeBuffer m_ShadowDataBuffer = null;
ComputeBuffer m_ShadowIndicesBuffer = null;
ComputeBuffer m_AdditionalLightShadowParamsStructuredBuffer = null;
ComputeBuffer m_AdditionalLightShadowSliceMatricesStructuredBuffer = null;

ShaderData()
{
Expand All @@ -31,8 +31,8 @@ public void Dispose()
{
DisposeBuffer(ref m_LightDataBuffer);
DisposeBuffer(ref m_LightIndicesBuffer);
DisposeBuffer(ref m_ShadowDataBuffer);
DisposeBuffer(ref m_ShadowIndicesBuffer);
DisposeBuffer(ref m_AdditionalLightShadowParamsStructuredBuffer);
DisposeBuffer(ref m_AdditionalLightShadowSliceMatricesStructuredBuffer);
}

internal ComputeBuffer GetLightDataBuffer(int size)
Expand All @@ -45,14 +45,14 @@ internal ComputeBuffer GetLightIndicesBuffer(int size)
return GetOrUpdateBuffer<int>(ref m_LightIndicesBuffer, size);
}

internal ComputeBuffer GetShadowDataBuffer(int size)
internal ComputeBuffer GetAdditionalLightShadowParamsStructuredBuffer(int size)
{
return GetOrUpdateBuffer<ShaderInput.ShadowData>(ref m_ShadowDataBuffer, size);
return GetOrUpdateBuffer<Vector4>(ref m_AdditionalLightShadowParamsStructuredBuffer, size);
}

internal ComputeBuffer GetShadowIndicesBuffer(int size)
internal ComputeBuffer GetAdditionalLightShadowSliceMatricesStructuredBuffer(int size)
{
return GetOrUpdateBuffer<int>(ref m_ShadowIndicesBuffer, size);
return GetOrUpdateBuffer<Matrix4x4>(ref m_AdditionalLightShadowSliceMatricesStructuredBuffer, size);
}

ComputeBuffer GetOrUpdateBuffer<T>(ref ComputeBuffer buffer, int size) where T : struct
Expand Down
120 changes: 105 additions & 15 deletions com.unity.render-pipelines.universal/Runtime/ShadowUtils.cs
Expand Up @@ -11,6 +11,7 @@ namespace UnityEngine.Rendering.Universal
public int offsetX;
public int offsetY;
public int resolution;
public ShadowSplitData splitData; // splitData contains culling information

public void Clear()
{
Expand Down Expand Up @@ -38,18 +39,23 @@ static ShadowUtils()

public static bool ExtractDirectionalLightMatrix(ref CullingResults cullResults, ref ShadowData shadowData, int shadowLightIndex, int cascadeIndex, int shadowmapWidth, int shadowmapHeight, int shadowResolution, float shadowNearPlane, out Vector4 cascadeSplitDistance, out ShadowSliceData shadowSliceData, out Matrix4x4 viewMatrix, out Matrix4x4 projMatrix)
{
ShadowSplitData splitData;
bool result = ExtractDirectionalLightMatrix(ref cullResults, ref shadowData, shadowLightIndex, cascadeIndex, shadowmapWidth, shadowmapHeight, shadowResolution, shadowNearPlane, out cascadeSplitDistance, out shadowSliceData);
viewMatrix = shadowSliceData.viewMatrix;
projMatrix = shadowSliceData.projectionMatrix;
return result;
}

public static bool ExtractDirectionalLightMatrix(ref CullingResults cullResults, ref ShadowData shadowData, int shadowLightIndex, int cascadeIndex, int shadowmapWidth, int shadowmapHeight, int shadowResolution, float shadowNearPlane, out Vector4 cascadeSplitDistance, out ShadowSliceData shadowSliceData)
{
bool success = cullResults.ComputeDirectionalShadowMatricesAndCullingPrimitives(shadowLightIndex,
cascadeIndex, shadowData.mainLightShadowCascadesCount, shadowData.mainLightShadowCascadesSplit, shadowResolution, shadowNearPlane, out viewMatrix, out projMatrix,
out splitData);
cascadeIndex, shadowData.mainLightShadowCascadesCount, shadowData.mainLightShadowCascadesSplit, shadowResolution, shadowNearPlane, out shadowSliceData.viewMatrix, out shadowSliceData.projectionMatrix,
out shadowSliceData.splitData);

cascadeSplitDistance = splitData.cullingSphere;
cascadeSplitDistance = shadowSliceData.splitData.cullingSphere;
shadowSliceData.offsetX = (cascadeIndex % 2) * shadowResolution;
shadowSliceData.offsetY = (cascadeIndex / 2) * shadowResolution;
shadowSliceData.resolution = shadowResolution;
shadowSliceData.viewMatrix = viewMatrix;
shadowSliceData.projectionMatrix = projMatrix;
shadowSliceData.shadowTransform = GetShadowTransform(projMatrix, viewMatrix);
shadowSliceData.shadowTransform = GetShadowTransform(shadowSliceData.projectionMatrix, shadowSliceData.viewMatrix);

// If we have shadow cascades baked into the atlas we bake cascade transform
// in each shadow matrix to save shader ALU and L/S
Expand All @@ -59,10 +65,33 @@ public static bool ExtractDirectionalLightMatrix(ref CullingResults cullResults,
return success;
}

public static bool ExtractSpotLightMatrix(ref CullingResults cullResults, ref ShadowData shadowData, int shadowLightIndex, out Matrix4x4 shadowMatrix, out Matrix4x4 viewMatrix, out Matrix4x4 projMatrix)
public static bool ExtractSpotLightMatrix(ref CullingResults cullResults, ref ShadowData shadowData, int shadowLightIndex, out Matrix4x4 shadowMatrix, out Matrix4x4 viewMatrix, out Matrix4x4 projMatrix, out ShadowSplitData splitData)
{
bool success = cullResults.ComputeSpotShadowMatricesAndCullingPrimitives(shadowLightIndex, out viewMatrix, out projMatrix, out splitData); // returns false if input parameters are incorrect (rare)
shadowMatrix = GetShadowTransform(projMatrix, viewMatrix);
return success;
}

public static bool ExtractPointLightMatrix(ref CullingResults cullResults, ref ShadowData shadowData, int shadowLightIndex, CubemapFace cubemapFace, float fovBias, out Matrix4x4 shadowMatrix, out Matrix4x4 viewMatrix, out Matrix4x4 projMatrix, out ShadowSplitData splitData)
{
ShadowSplitData splitData;
bool success = cullResults.ComputeSpotShadowMatricesAndCullingPrimitives(shadowLightIndex, out viewMatrix, out projMatrix, out splitData);
bool success = cullResults.ComputePointShadowMatricesAndCullingPrimitives(shadowLightIndex, cubemapFace, fovBias, out viewMatrix, out projMatrix, out splitData); // returns false if input parameters are incorrect (rare)

// In native API CullingResults.ComputeSpotShadowMatricesAndCullingPrimitives there is code that inverts the 3rd component of shadow-casting spot light's "world-to-local" matrix (it was so since its original addition to the code base):
// https://github.cds.internal.unity3d.com/unity/unity/commit/34813e063526c4be0ef0448dfaae3a911dd8be58#diff-cf0b417fc6bd8ee2356770797e628cd4R331
// (the same transformation has also always been used in the Built-In Render Pipeline)
//
// However native API CullingResults.ComputePointShadowMatricesAndCullingPrimitives does not contain this transformation.
// As a result, the view matrices returned for a point light shadow face, and for a spot light with same direction as that face, have opposite 3rd component.
//
// This causes normalBias to be incorrectly applied to shadow caster vertices during the point light shadow pass.
// To counter this effect, we invert the point light shadow view matrix component here:
{
viewMatrix.m10 = -viewMatrix.m10;
viewMatrix.m11 = -viewMatrix.m11;
viewMatrix.m12 = -viewMatrix.m12;
viewMatrix.m13 = -viewMatrix.m13;
}

shadowMatrix = GetShadowTransform(projMatrix, viewMatrix);
return success;
}
Expand All @@ -71,6 +100,8 @@ public static bool ExtractSpotLightMatrix(ref CullingResults cullResults, ref Sh
ref ShadowSliceData shadowSliceData, ref ShadowDrawingSettings settings,
Matrix4x4 proj, Matrix4x4 view)
{
cmd.SetGlobalDepthBias(1.0f, 2.5f); // these values match HDRP defaults (see https://github.com/Unity-Technologies/Graphics/blob/9544b8ed2f98c62803d285096c91b44e9d8cbc47/com.unity.render-pipelines.high-definition/Runtime/Lighting/Shadow/HDShadowAtlas.cs#L197 )

cmd.SetViewport(new Rect(shadowSliceData.offsetX, shadowSliceData.offsetY, shadowSliceData.resolution, shadowSliceData.resolution));
cmd.SetViewProjectionMatrices(view, proj);
context.ExecuteCommandBuffer(cmd);
Expand All @@ -79,6 +110,8 @@ public static bool ExtractSpotLightMatrix(ref CullingResults cullResults, ref Sh
cmd.DisableScissorRect();
context.ExecuteCommandBuffer(cmd);
cmd.Clear();

cmd.SetGlobalDepthBias(0.0f, 0.0f); // Restore previous depth bias values
}

public static void RenderShadowSlice(CommandBuffer cmd, ref ScriptableRenderContext context,
Expand Down Expand Up @@ -136,11 +169,26 @@ public static Vector4 GetShadowBias(ref VisibleLight shadowLight, int shadowLigh
// handle this. For now, as a poor approximation we do a constant bias and compute the size of
// the frustum as if it was orthogonal considering the size at mid point between near and far planes.
// Depending on how big the light range is, it will be good enough with some tweaks in bias
frustumSize = Mathf.Tan(shadowLight.spotAngle * 0.5f * Mathf.Deg2Rad) * shadowLight.range;
frustumSize = Mathf.Tan(shadowLight.spotAngle * 0.5f * Mathf.Deg2Rad) * shadowLight.range; // half-width (in world-space units) of shadow frustum's "far plane"
}
else if (shadowLight.lightType == LightType.Point)
{
// [Copied from above case:]
// "For perspective projections, shadow texel size varies with depth
// It will only work well if done in receiver side in the pixel shader. Currently UniversalRP
// do bias on caster side in vertex shader. When we add shader quality tiers we can properly
// handle this. For now, as a poor approximation we do a constant bias and compute the size of
// the frustum as if it was orthogonal considering the size at mid point between near and far planes.
// Depending on how big the light range is, it will be good enough with some tweaks in bias"
// Note: HDRP uses normalBias both in HDShadowUtils.CalcGuardAnglePerspective and HDShadowAlgorithms/EvalShadow_NormalBias (receiver bias)
float fovBias = Internal.AdditionalLightsShadowCasterPass.GetPointLightShadowFrustumFovBiasInDegrees((int)shadowResolution, (shadowLight.light.shadows == LightShadows.Soft));
// Note: the same fovBias was also used to compute ShadowUtils.ExtractPointLightMatrix
float cubeFaceAngle = 90 + fovBias;
frustumSize = Mathf.Tan(cubeFaceAngle * 0.5f * Mathf.Deg2Rad) * shadowLight.range; // half-width (in world-space units) of shadow frustum's "far plane"
}
else
{
Debug.LogWarning("Only spot and directional shadow casters are supported in universal pipeline");
Debug.LogWarning("Only point, spot and directional shadow casters are supported in universal pipeline");
frustumSize = 0.0f;
}

Expand All @@ -149,13 +197,19 @@ public static Vector4 GetShadowBias(ref VisibleLight shadowLight, int shadowLigh
float depthBias = -shadowData.bias[shadowLightIndex].x * texelSize;
float normalBias = -shadowData.bias[shadowLightIndex].y * texelSize;

if (shadowData.supportsSoftShadows)
// The current implementation of NormalBias in Universal RP is the same as in Unity Built-In RP (i.e moving shadow caster vertices along normals when projecting them to the shadow map).
// This does not work well with Point Lights, which is why NormalBias value is hard-coded to 0.0 in Built-In RP (see value of unity_LightShadowBias.z in FrameDebugger, and native code that sets it: https://github.cds.internal.unity3d.com/unity/unity/blob/a9c916ba27984da43724ba18e70f51469e0c34f5/Runtime/Camera/Shadows.cpp#L1686 )
// We follow the same convention in Universal RP:
if (shadowLight.lightType == LightType.Point)
normalBias = 0.0f;

if (shadowData.supportsSoftShadows && shadowLight.light.shadows == LightShadows.Soft)
{
// TODO: depth and normal bias assume sample is no more than 1 texel away from shadowmap
// This is not true with PCF. Ideally we need to do either
// cone base bias (based on distance to center sample)
// or receiver place bias based on derivatives.
// For now we scale it by the PCF kernel size (5x5)
// For now we scale it by the PCF kernel size of non-mobile platforms (5x5)
const float kernelRadius = 2.5f;
depthBias *= kernelRadius;
normalBias *= kernelRadius;
Expand All @@ -166,9 +220,44 @@ public static Vector4 GetShadowBias(ref VisibleLight shadowLight, int shadowLigh

public static void SetupShadowCasterConstantBuffer(CommandBuffer cmd, ref VisibleLight shadowLight, Vector4 shadowBias)
{
Vector3 lightDirection = -shadowLight.localToWorldMatrix.GetColumn(2);
cmd.SetGlobalVector("_ShadowBias", shadowBias);

// Light direction is currently used in shadow caster pass to apply shadow normal offset (normal bias).
Vector3 lightDirection = -shadowLight.localToWorldMatrix.GetColumn(2);
cmd.SetGlobalVector("_LightDirection", new Vector4(lightDirection.x, lightDirection.y, lightDirection.z, 0.0f));

// For punctual lights, computing light direction at each vertex position provides more consistent results (shadow shape does not change when "rotating the point light" for example)
Vector3 lightPosition = shadowLight.localToWorldMatrix.GetColumn(3);
cmd.SetGlobalVector("_LightPosition", new Vector4(lightPosition.x, lightPosition.y, lightPosition.z, 1.0f));
}

internal static Vector4 GetMainLightShadowParams(ref RenderingData renderingData)
{
// Main Light shadow params
float mainLightShadowStrength = 0f;
float mainLightSoftShadowsProp = 0f;
if (renderingData.lightData.mainLightIndex != -1)
{
mainLightShadowStrength = renderingData.lightData.visibleLights[renderingData.lightData.mainLightIndex].light.shadowStrength;

if (renderingData.lightData.visibleLights[renderingData.lightData.mainLightIndex].light.shadows == LightShadows.Soft && renderingData.shadowData.supportsSoftShadows)
mainLightSoftShadowsProp = 1f;
}

// Shadow params used by both MainLight and AdditionalLights
float maxShadowDistance = renderingData.cameraData.maxShadowDistance * renderingData.cameraData.maxShadowDistance;
//To make the shadow fading fit into a single MAD instruction:
//distanceCamToPixel2 * oneOverFadeDist + minusStartFade (single MAD)
float startFade = maxShadowDistance * 0.9f;
float oneOverFadeDist = 1 / (maxShadowDistance - startFade);
float minusStartFade = -startFade * oneOverFadeDist;

return new Vector4(mainLightShadowStrength, mainLightSoftShadowsProp, oneOverFadeDist, minusStartFade);
}

internal static void SetupShadowReceiverConstantBuffer(CommandBuffer cmd, Vector4 mainLightShadowParams)
{
cmd.SetGlobalVector("_MainLightShadowParams", mainLightShadowParams);
}

public static RenderTexture GetTemporaryShadowTexture(int width, int height, int bits)
Expand Down Expand Up @@ -201,6 +290,7 @@ static Matrix4x4 GetShadowTransform(Matrix4x4 proj, Matrix4x4 view)
textureScaleAndBias.m03 = 0.5f;
textureScaleAndBias.m23 = 0.5f;
textureScaleAndBias.m13 = 0.5f;
// textureScaleAndBias maps texture space coordinates from [-1,1] to [0,1]

// Apply texture scale and offset to save a MAD in shader.
return textureScaleAndBias * worldToShadow;
Expand Down
Expand Up @@ -123,9 +123,9 @@ public static int maxPerObjectLights
}

// These limits have to match same limits in Input.hlsl
const int k_MaxVisibleAdditionalLightsMobileShaderLevelLessThan45 = 16;
const int k_MaxVisibleAdditionalLightsMobile = 32;
const int k_MaxVisibleAdditionalLightsNonMobile = 256;
internal const int k_MaxVisibleAdditionalLightsMobileShaderLevelLessThan45 = 16;
internal const int k_MaxVisibleAdditionalLightsMobile = 32;
internal const int k_MaxVisibleAdditionalLightsNonMobile = 256;
public static int maxVisibleAdditionalLights
{
get
Expand Down Expand Up @@ -871,8 +871,8 @@ static void InitializeAdditionalCameraData(Camera camera, UniversalAdditionalCam

Light light = visibleLights[i].light;

// UniversalRP doesn't support additional directional lights or point light shadows yet
if (visibleLights[i].lightType == LightType.Spot && light != null && light.shadows != LightShadows.None)
// UniversalRP doesn't support additional directional light shadows yet
if ((visibleLights[i].lightType == LightType.Spot || visibleLights[i].lightType == LightType.Point) && light != null && light.shadows != LightShadows.None)
{
additionalLightsCastShadows = true;
break;
Expand Down
Expand Up @@ -298,6 +298,7 @@ public static class ShaderKeywordStrings
{
public static readonly string MainLightShadows = "_MAIN_LIGHT_SHADOWS";
public static readonly string MainLightShadowCascades = "_MAIN_LIGHT_SHADOWS_CASCADE";
public static readonly string CastingPunctualLightShadow = "_CASTING_PUNCTUAL_LIGHT_SHADOW"; // This is used during shadow map generation to differentiate between directional and punctual light shadows, as they use different formulas to apply Normal Bias
public static readonly string AdditionalLightsVertex = "_ADDITIONAL_LIGHTS_VERTEX";
public static readonly string AdditionalLightsPixel = "_ADDITIONAL_LIGHTS";
public static readonly string AdditionalLightShadows = "_ADDITIONAL_LIGHT_SHADOWS";
Expand Down
19 changes: 19 additions & 0 deletions com.unity.render-pipelines.universal/ShaderLibrary/Deprecated.cs
@@ -0,0 +1,19 @@
// This file should be used as a container for things on its
// way to being deprecated and removed in future releases

using System;

namespace UnityEngine.Rendering.Universal
{
public static partial class ShaderInput
{
//Even when RenderingUtils.useStructuredBuffer is true we do not this structure anymore, because in shader side worldToShadowMatrix and shadowParams must be stored in arrays of different sizes
// To specify shader-side shadow matrices and shadow parameters, see code in AdditionalLightsShadowCasterPass.SetupAdditionalLightsShadowReceiverConstants
[Obsolete("ShaderInput.ShadowData was deprecated. Shadow slice matrices and per-light shadow parameters are now passed to the GPU using entries in buffers m_AdditionalLightsWorldToShadow_SSBO and m_AdditionalShadowParams_SSBO", false)]
public struct ShadowData
{
public Matrix4x4 worldToShadowMatrix;
public Vector4 shadowParams;
}
}
}
16 changes: 16 additions & 0 deletions com.unity.render-pipelines.universal/ShaderLibrary/Deprecated.hlsl
Expand Up @@ -22,4 +22,20 @@
// w is used for knowing whether the object is opaque(1) or alpha blended(0)
half4 _DrawObjectPassData;

#if USE_STRUCTURED_BUFFER_FOR_LIGHT_DATA
// _AdditionalShadowsIndices was deprecated - To get the first shadow slice index for a light, use GetAdditionalLightShadowParams(lightIndex).w [see Shadows.hlsl]
#define _AdditionalShadowsIndices _AdditionalShadowParams_SSBO
// _AdditionalShadowsBuffer was deprecated - To access a shadow slice's matrix, use _AdditionalLightsWorldToShadow_SSBO[shadowSliceIndex] - To access other shadow parameters, use GetAdditionalLightShadowParams(int lightIndex) [see Shadows.hlsl]
#define _AdditionalShadowsBuffer _AdditionalLightsWorldToShadow_SSBO
#endif

// Deprecated: even when USE_STRUCTURED_BUFFER_FOR_LIGHT_DATA is defined we do not this structure anymore, because worldToShadowMatrix and shadowParams must be stored in arrays of different sizes
// To get the first shadow slice index for a light, use GetAdditionalLightShadowParams(lightIndex).w [see Shadows.hlsl]
// To access other shadow parameters, use GetAdditionalLightShadowParams(int lightIndex)[see Shadows.hlsl]
struct ShadowData
{
float4x4 worldToShadowMatrix; // per-shadow-slice
float4 shadowParams; // per-casting-light
};

#endif // UNIVERSAL_DEPRECATED_INCLUDED
3 changes: 3 additions & 0 deletions com.unity.render-pipelines.universal/ShaderLibrary/Input.hlsl
Expand Up @@ -3,9 +3,12 @@

#define MAX_VISIBLE_LIGHTS_UBO 32
#define MAX_VISIBLE_LIGHTS_SSBO 256

// Keep in sync with RenderingUtils.useStructuredBuffer
#define USE_STRUCTURED_BUFFER_FOR_LIGHT_DATA 0

#include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/ShaderTypes.cs.hlsl"
#include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Deprecated.hlsl"

#if defined(SHADER_API_MOBILE) && (defined(SHADER_API_GLES) || defined(SHADER_API_GLES30))
#define MAX_VISIBLE_LIGHTS 16
Expand Down
Expand Up @@ -156,7 +156,7 @@ Light GetAdditionalPerObjectLight(int perObjectLightIndex, float3 positionWS)
Light light;
light.direction = lightDirection;
light.distanceAttenuation = attenuation;
light.shadowAttenuation = 1.0;
light.shadowAttenuation = 1.0; // This value can later be overridden in GetAdditionalLight(uint i, float3 positionWS, half4 shadowMask)
light.color = color;

return light;
Expand Down Expand Up @@ -232,7 +232,7 @@ Light GetAdditionalLight(uint i, float3 positionWS, half4 shadowMask)
#else
half4 occlusionProbeChannels = _AdditionalLightsOcclusionProbes[perObjectLightIndex];
#endif
light.shadowAttenuation = AdditionalLightShadow(perObjectLightIndex, positionWS, shadowMask, occlusionProbeChannels);
light.shadowAttenuation = AdditionalLightShadow(perObjectLightIndex, positionWS, light.direction, shadowMask, occlusionProbeChannels);

return light;
}
Expand Down
@@ -1,6 +1,6 @@
namespace UnityEngine.Rendering.Universal
{
public static class ShaderInput
public static partial class ShaderInput
{
[GenerateHLSL(PackingRules.Exact, false)]
public struct LightData
Expand All @@ -11,15 +11,5 @@ public struct LightData
public Vector4 spotDirection;
public Vector4 occlusionProbeChannels;
}

[GenerateHLSL(PackingRules.Exact, false)]
public struct ShadowData
{
public Matrix4x4 worldToShadowMatrix;

// x: shadow strength
// y: 1 if soft shadows, 0 otherwise
public Vector4 shadowParams;
}
}
}
@@ -1,5 +1,5 @@
//
// This file was automatically generated. Please don't edit by hand.
// This file was automatically generated. Please don't edit by hand. Execute Editor command [ Edit / Render Pipeline / Generate Shader Includes ] instead
//

#ifndef SHADERTYPES_CS_HLSL
Expand All @@ -15,13 +15,5 @@ struct LightData
float4 occlusionProbeChannels;
};

// Generated from UnityEngine.Rendering.Universal.ShaderInput+ShadowData
// PackingRules = Exact
struct ShadowData
{
float4x4 worldToShadowMatrix;
float4 shadowParams;
};


#endif
100 changes: 77 additions & 23 deletions com.unity.render-pipelines.universal/ShaderLibrary/Shadows.hlsl
Expand Up @@ -72,37 +72,58 @@ half4 _MainLightShadowOffset0;
half4 _MainLightShadowOffset1;
half4 _MainLightShadowOffset2;
half4 _MainLightShadowOffset3;
half4 _MainLightShadowParams; // (x: shadowStrength, y: 1.0 if soft shadows, 0.0 otherwise, z: oneOverFadeDist, w: minusStartFade)
half4 _MainLightShadowParams; // (x: shadowStrength, y: 1.0 if soft shadows, 0.0 otherwise, z: oneOverFadeDist, w: minusStartFade) - xy are used by MainLight only, yz are used by MainLight AND AdditionalLights
float4 _MainLightShadowmapSize; // (xy: 1/width and 1/height, zw: width and height)
#ifndef SHADER_API_GLES3
CBUFFER_END
#endif

#if USE_STRUCTURED_BUFFER_FOR_LIGHT_DATA
StructuredBuffer<ShadowData> _AdditionalShadowsBuffer;
StructuredBuffer<int> _AdditionalShadowsIndices;

StructuredBuffer<float4> _AdditionalShadowParams_SSBO; // Per-light data - TODO: test if splitting _AdditionalShadowParams_SSBO[lightIndex].w into a separate StructuredBuffer<int> buffer is faster
StructuredBuffer<float4x4> _AdditionalLightsWorldToShadow_SSBO; // Per-shadow-slice-data - A shadow casting light can have 6 shadow slices (if it's a point light)

half4 _AdditionalShadowOffset0;
half4 _AdditionalShadowOffset1;
half4 _AdditionalShadowOffset2;
half4 _AdditionalShadowOffset3;
float4 _AdditionalShadowmapSize; // (xy: 1/width and 1/height, zw: width and height)

#else


#if defined(SHADER_API_MOBILE) || (defined(SHADER_API_GLCORE) && !defined(SHADER_API_SWITCH)) || defined(SHADER_API_GLES) || defined(SHADER_API_GLES3) // Workaround for bug on Nintendo Switch where SHADER_API_GLCORE is mistakenly defined
// Point lights can use 6 shadow slices, but on some mobile GPUs performance decrease drastically with uniform blocks bigger than 8kb. This number ensures size of buffer AdditionalLightShadows stays reasonable.
// It also avoids shader compilation errors on SHADER_API_GLES30 devices where max number of uniforms per shader GL_MAX_FRAGMENT_UNIFORM_VECTORS is low (224)
// Keep in sync with MAX_PUNCTUAL_LIGHT_SHADOW_SLICES_IN_UBO in AdditionalLightsShadowCasterPass.cs
#define MAX_PUNCTUAL_LIGHT_SHADOW_SLICES_IN_UBO (MAX_VISIBLE_LIGHTS)
#else
// Point lights can use 6 shadow slices, but on some platforms max uniform block size is 64kb. This number ensures size of buffer AdditionalLightShadows does not exceed this 64kb limit.
// Keep in sync with MAX_PUNCTUAL_LIGHT_SHADOW_SLICES_IN_UBO in AdditionalLightsShadowCasterPass.cs
#define MAX_PUNCTUAL_LIGHT_SHADOW_SLICES_IN_UBO 545
#endif

// GLES3 causes a performance regression in some devices when using CBUFFER.
#ifndef SHADER_API_GLES3
CBUFFER_START(AdditionalLightShadows)
#endif
float4x4 _AdditionalLightsWorldToShadow[MAX_VISIBLE_LIGHTS];
half4 _AdditionalShadowParams[MAX_VISIBLE_LIGHTS];

half4 _AdditionalShadowParams[MAX_VISIBLE_LIGHTS]; // Per-light data
float4x4 _AdditionalLightsWorldToShadow[MAX_PUNCTUAL_LIGHT_SHADOW_SLICES_IN_UBO]; // Per-shadow-slice-data

half4 _AdditionalShadowOffset0;
half4 _AdditionalShadowOffset1;
half4 _AdditionalShadowOffset2;
half4 _AdditionalShadowOffset3;
float4 _AdditionalShadowmapSize; // (xy: 1/width and 1/height, zw: width and height)

#ifndef SHADER_API_GLES3
CBUFFER_END
#endif

#endif


float4 _ShadowBias; // x: depth bias, y: normal bias

#define BEYOND_SHADOW_FAR(shadowCoord) shadowCoord.z <= 0.0 || shadowCoord.z >= 1.0
Expand All @@ -119,22 +140,32 @@ struct ShadowSamplingData
ShadowSamplingData GetMainLightShadowSamplingData()
{
ShadowSamplingData shadowSamplingData;

// shadowOffsets are used in SampleShadowmapFiltered #if defined(SHADER_API_MOBILE) || defined(SHADER_API_SWITCH)
shadowSamplingData.shadowOffset0 = _MainLightShadowOffset0;
shadowSamplingData.shadowOffset1 = _MainLightShadowOffset1;
shadowSamplingData.shadowOffset2 = _MainLightShadowOffset2;
shadowSamplingData.shadowOffset3 = _MainLightShadowOffset3;

// shadowmapSize is used in SampleShadowmapFiltered for other platforms
shadowSamplingData.shadowmapSize = _MainLightShadowmapSize;

return shadowSamplingData;
}

ShadowSamplingData GetAdditionalLightShadowSamplingData()
{
ShadowSamplingData shadowSamplingData;

// shadowOffsets are used in SampleShadowmapFiltered #if defined(SHADER_API_MOBILE) || defined(SHADER_API_SWITCH)
shadowSamplingData.shadowOffset0 = _AdditionalShadowOffset0;
shadowSamplingData.shadowOffset1 = _AdditionalShadowOffset1;
shadowSamplingData.shadowOffset2 = _AdditionalShadowOffset2;
shadowSamplingData.shadowOffset3 = _AdditionalShadowOffset3;

// shadowmapSize is used in SampleShadowmapFiltered for other platforms
shadowSamplingData.shadowmapSize = _AdditionalShadowmapSize;

return shadowSamplingData;
}

Expand All @@ -150,10 +181,12 @@ half4 GetMainLightShadowParams()
// ShadowParams
// x: ShadowStrength
// y: 1.0 if shadow is soft, 0.0 otherwise
// z: 1.0 if cast by a point light (6 shadow slices), 0.0 if cast by a spot light (1 shadow slice)
// w: first shadow slice index for this light, there can be 6 in case of point lights. (-1 for non-shadow-casting-lights)
half4 GetAdditionalLightShadowParams(int lightIndex)
{
#if USE_STRUCTURED_BUFFER_FOR_LIGHT_DATA
return _AdditionalShadowsBuffer[lightIndex].shadowParams;
return _AdditionalShadowParams_SSBO[lightIndex];
#else
return _AdditionalShadowParams[lightIndex];
#endif
Expand Down Expand Up @@ -215,13 +248,17 @@ real SampleShadowmap(TEXTURE2D_SHADOW_PARAM(ShadowMap, sampler_ShadowMap), float
real attenuation;
real shadowStrength = shadowParams.x;

// TODO: We could branch on if this light has soft shadows (shadowParams.y) to save perf on some platforms.
#ifdef _SHADOWS_SOFT
attenuation = SampleShadowmapFiltered(TEXTURE2D_SHADOW_ARGS(ShadowMap, sampler_ShadowMap), shadowCoord, samplingData);
#else
// 1-tap hardware comparison
attenuation = SAMPLE_TEXTURE2D_SHADOW(ShadowMap, sampler_ShadowMap, shadowCoord.xyz);
if(shadowParams.y != 0)
{
attenuation = SampleShadowmapFiltered(TEXTURE2D_SHADOW_ARGS(ShadowMap, sampler_ShadowMap), shadowCoord, samplingData);
}
else
#endif
{
// 1-tap hardware comparison
attenuation = SAMPLE_TEXTURE2D_SHADOW(ShadowMap, sampler_ShadowMap, shadowCoord.xyz);
}

attenuation = LerpWhiteTo(attenuation, shadowStrength);

Expand Down Expand Up @@ -268,29 +305,40 @@ half MainLightRealtimeShadow(float4 shadowCoord)
return SampleShadowmap(TEXTURE2D_ARGS(_MainLightShadowmapTexture, sampler_MainLightShadowmapTexture), shadowCoord, shadowSamplingData, shadowParams, false);
}

half AdditionalLightRealtimeShadow(int lightIndex, float3 positionWS)
// returns 0.0 if position is in light's shadow
// returns 1.0 if position is in light
half AdditionalLightRealtimeShadow(int lightIndex, float3 positionWS, half3 lightDirection)
{
#if !defined(ADDITIONAL_LIGHT_CALCULATE_SHADOWS)
return 1.0h;
#endif

ShadowSamplingData shadowSamplingData = GetAdditionalLightShadowSamplingData();

#if USE_STRUCTURED_BUFFER_FOR_LIGHT_DATA
lightIndex = _AdditionalShadowsIndices[lightIndex];
half4 shadowParams = GetAdditionalLightShadowParams(lightIndex);

int shadowSliceIndex = shadowParams.w;

// We have to branch here as otherwise we would sample buffer with lightIndex == -1.
// However this should be ok for platforms that store light in SSBO.
UNITY_BRANCH
if (lightIndex < 0)
if (shadowSliceIndex < 0)
return 1.0;

float4 shadowCoord = mul(_AdditionalShadowsBuffer[lightIndex].worldToShadowMatrix, float4(positionWS, 1.0));
half isPointLight = shadowParams.z;

UNITY_BRANCH
if (isPointLight)
{
// This is a point light, we have to find out which shadow slice to sample from
float cubemapFaceId = CubeMapFaceID(-lightDirection);
shadowSliceIndex += cubemapFaceId;
}

#if USE_STRUCTURED_BUFFER_FOR_LIGHT_DATA
float4 shadowCoord = mul(_AdditionalLightsWorldToShadow_SSBO[shadowSliceIndex], float4(positionWS, 1.0));
#else
float4 shadowCoord = mul(_AdditionalLightsWorldToShadow[lightIndex], float4(positionWS, 1.0));
float4 shadowCoord = mul(_AdditionalLightsWorldToShadow[shadowSliceIndex], float4(positionWS, 1.0));
#endif

half4 shadowParams = GetAdditionalLightShadowParams(lightIndex);
return SampleShadowmap(TEXTURE2D_ARGS(_AdditionalLightsShadowmapTexture, sampler_AdditionalLightsShadowmapTexture), shadowCoord, shadowSamplingData, shadowParams, true);
}

Expand Down Expand Up @@ -348,9 +396,9 @@ half MainLightShadow(float4 shadowCoord, float3 positionWS, half4 shadowMask, ha
return MixRealtimeAndBakedShadows(realtimeShadow, bakedShadow, shadowFade);
}

half AdditionalLightShadow(int lightIndex, float3 positionWS, half4 shadowMask, half4 occlusionProbeChannels)
half AdditionalLightShadow(int lightIndex, float3 positionWS, half3 lightDirection, half4 shadowMask, half4 occlusionProbeChannels)
{
half realtimeShadow = AdditionalLightRealtimeShadow(lightIndex, positionWS);
half realtimeShadow = AdditionalLightRealtimeShadow(lightIndex, positionWS, lightDirection);

#ifdef CALCULATE_BAKED_SHADOWS
half bakedShadow = BakedShadow(shadowMask, occlusionProbeChannels);
Expand Down Expand Up @@ -407,7 +455,7 @@ half GetMainLightShadowStrength()
half GetAdditionalLightShadowStrenth(int lightIndex)
{
#if USE_STRUCTURED_BUFFER_FOR_LIGHT_DATA
return _AdditionalShadowsBuffer[lightIndex].shadowParams.x;
return _AdditionalShadowParams_SSBO[lightIndex].x;
#else
return _AdditionalShadowParams[lightIndex].x;
#endif
Expand All @@ -420,4 +468,10 @@ real SampleShadowmap(float4 shadowCoord, TEXTURE2D_SHADOW_PARAM(ShadowMap, sampl
return SampleShadowmap(TEXTURE2D_SHADOW_ARGS(ShadowMap, sampler_ShadowMap), shadowCoord, samplingData, shadowParams, isPerspectiveProjection);
}

// Deprecated: Use AdditionalLightRealtimeShadow(int lightIndex, float3 positionWS, half3 lightDirection) in Shadows.hlsl instead, as it supports Point Light shadows
half AdditionalLightRealtimeShadow(int lightIndex, float3 positionWS)
{
return AdditionalLightRealtimeShadow(lightIndex, positionWS, half3(1, 0, 0));
}

#endif
Expand Up @@ -163,6 +163,12 @@ Shader "Universal Render Pipeline/Complex Lit"
#pragma multi_compile_instancing
#pragma multi_compile _ DOTS_INSTANCING_ON

// -------------------------------------
// Universal Pipeline keywords

// This is used during shadow map generation to differentiate between directional and punctual light shadows, as they use different formulas to apply Normal Bias
#pragma multi_compile_vertex _ _CASTING_PUNCTUAL_LIGHT_SHADOW

#pragma vertex ShadowPassVertex
#pragma fragment ShadowPassFragment

Expand Down
12 changes: 12 additions & 0 deletions com.unity.render-pipelines.universal/Shaders/Lit.shader
Expand Up @@ -165,6 +165,12 @@ Shader "Universal Render Pipeline/Lit"
#pragma multi_compile_instancing
#pragma multi_compile _ DOTS_INSTANCING_ON

// -------------------------------------
// Universal Pipeline keywords

// This is used during shadow map generation to differentiate between directional and punctual light shadows, as they use different formulas to apply Normal Bias
#pragma multi_compile_vertex _ _CASTING_PUNCTUAL_LIGHT_SHADOW

#pragma vertex ShadowPassVertex
#pragma fragment ShadowPassFragment

Expand Down Expand Up @@ -444,6 +450,12 @@ Shader "Universal Render Pipeline/Lit"
#pragma shader_feature_local_fragment _ALPHATEST_ON
#pragma shader_feature_local_fragment _SMOOTHNESS_TEXTURE_ALBEDO_CHANNEL_A

// -------------------------------------
// Universal Pipeline keywords

// This is used during shadow map generation to differentiate between directional and punctual light shadows, as they use different formulas to apply Normal Bias
#pragma multi_compile_vertex _ _CASTING_PUNCTUAL_LIGHT_SHADOW

#pragma vertex ShadowPassVertex
#pragma fragment ShadowPassFragment

Expand Down
Expand Up @@ -108,6 +108,12 @@ Shader "Universal Render Pipeline/Nature/SpeedTree7"
#define DEPTH_ONLY
#define SHADOW_CASTER

// -------------------------------------
// Universal Pipeline keywords

// This is used during shadow map generation to differentiate between directional and punctual light shadows, as they use different formulas to apply Normal Bias
#pragma multi_compile_vertex _ _CASTING_PUNCTUAL_LIGHT_SHADOW

#include "SpeedTree7Input.hlsl"
#include "SpeedTree7Passes.hlsl"
ENDHLSL
Expand Down
Expand Up @@ -121,7 +121,14 @@ SpeedTreeVertexDepthOutput SpeedTree7VertDepth(SpeedTreeVertexInput input)

#ifdef SHADOW_CASTER
half3 normalWS = TransformObjectToWorldNormal(input.normal);
output.clipPos = TransformWorldToHClip(ApplyShadowBias(vertexInput.positionWS, normalWS, _LightDirection));

#if _CASTING_PUNCTUAL_LIGHT_SHADOW
float3 lightDirectionWS = normalize(_LightPosition - vertexInput.positionWS);
#else
float3 lightDirectionWS = _LightDirection;
#endif

output.clipPos = TransformWorldToHClip(ApplyShadowBias(vertexInput.positionWS, normalWS, lightDirectionWS));
#else
output.clipPos = vertexInput.positionCS;
#endif
Expand Down
Expand Up @@ -30,6 +30,11 @@ SAMPLER(sampler_MainTex);
#endif

half4 _Color;

// Shadow Casting Light geometric parameters. These variables are used when applying the shadow Normal Bias and are set by UnityEngine.Rendering.Universal.ShadowUtils.SetupShadowCasterConstantBuffer in com.unity.render-pipelines.universal/Runtime/ShadowUtils.cs
// For Directional lights, _LightDirection is used when applying shadow Normal Bias.
// For Spot lights and Point lights, _LightPosition is used to compute the actual light direction because it is different at each shadow caster geometry vertex.
float3 _LightDirection;
float3 _LightPosition;

#endif
Expand Up @@ -176,7 +176,14 @@ SpeedTreeVertexDepthOutput SpeedTree7VertDepth(SpeedTreeVertexInput input)

#ifdef SHADOW_CASTER
half3 normalWS = TransformObjectToWorldNormal(input.normal);
output.clipPos = TransformWorldToHClip(ApplyShadowBias(vertexInput.positionWS, normalWS, _LightDirection));

#if _CASTING_PUNCTUAL_LIGHT_SHADOW
float3 lightDirectionWS = normalize(_LightPosition - vertexInput.positionWS);
#else
float3 lightDirectionWS = _LightDirection;
#endif

output.clipPos = TransformWorldToHClip(ApplyShadowBias(vertexInput.positionWS, normalWS, lightDirectionWS));
#else
output.clipPos = vertexInput.positionCS;
#endif
Expand Down