浏览代码

Merge pull request #738 from Unity-Technologies/LW-ShadowRefactorV2

Shadow Refactor v2
/main
GitHub 7 年前
当前提交
a204c630
共有 9 个文件被更改,包括 344 次插入162 次删除
  1. 293
      ScriptableRenderPipeline/LightweightPipeline/LWRP/LightweightPipeline.cs
  2. 26
      ScriptableRenderPipeline/LightweightPipeline/LWRP/Shaders/LightweightPassShadow.hlsl
  3. 22
      ScriptableRenderPipeline/LightweightPipeline/LWRP/Shaders/LightweightShaderLibrary/Core.hlsl
  4. 10
      ScriptableRenderPipeline/LightweightPipeline/LWRP/Shaders/LightweightShaderLibrary/Lighting.hlsl
  5. 106
      ScriptableRenderPipeline/LightweightPipeline/LWRP/Shaders/LightweightShaderLibrary/Shadows.hlsl
  6. 17
      ScriptableRenderPipeline/LightweightPipeline/LWRP/Shaders/LightweightStandard.shader
  7. 7
      ScriptableRenderPipeline/LightweightPipeline/LWRP/Shaders/LightweightStandardParticles.shader
  8. 18
      ScriptableRenderPipeline/LightweightPipeline/LWRP/Shaders/LightweightStandardSimpleLighting.shader
  9. 7
      ScriptableRenderPipeline/LightweightPipeline/LWRP/Shaders/LightweightStandardTerrain.shader

293
ScriptableRenderPipeline/LightweightPipeline/LWRP/LightweightPipeline.cs


using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Text;
using UnityEngine.Experimental.GlobalIllumination;
using UnityEngine.Rendering;
using UnityEngine.Rendering.PostProcessing;

defaultShadowSettings.shadowAtlasHeight = defaultShadowSettings.shadowAtlasWidth = 4096;
defaultShadowSettings.directionalLightCascadeCount = 1;
defaultShadowSettings.directionalLightCascades = new Vector3(0.05F, 0.2F, 0.3F);
defaultShadowSettings.directionalLightCascadeCount = 4;
defaultShadowSettings.directionalLightNearPlaneOffset = 5;
defaultShadowSettings.maxShadowDistance = 1000.0F;
defaultShadowSettings.renderTextureFormat = RenderTextureFormat.Shadowmap;

public int pixelAdditionalLightsCount;
public int totalAdditionalLightsCount;
public int mainLightIndex;
public bool shadowsRendered;
public LightShadows shadowMapSampleType;
}
public enum MixedLightingSetup

private Camera m_CurrCamera;
private static readonly int kMaxCascades = 4;
private int m_ShadowCasterCascadesCount = kMaxCascades;
private const int kMaxCascades = 4;
private int m_ShadowCasterCascadesCount;
private int m_ShadowMapRTID;
private RenderTargetIdentifier m_CurrCameraColorRT;
private RenderTargetIdentifier m_ShadowMapRT;

private bool m_RequiredDepth;
private MixedLightingSetup m_MixedLightingSetup;
private const int kShadowDepthBufferBits = 16;
private const int kCameraDepthBufferBits = 32;
private const int kDepthStencilBufferBits = 32;
private Vector4 m_DirectionalShadowSplitRadii;
private ShadowSettings m_ShadowSettings = ShadowSettings.Default;
private ShadowSliceData[] m_ShadowSlices = new ShadowSliceData[kMaxCascades];

m_PostProcessRenderContext = new PostProcessRenderContext();
m_CopyTextureSupport = SystemInfo.copyTextureSupport;
for (int i = 0; i < kMaxCascades; ++i)
m_DirectionalShadowSplitDistances[i] = new Vector4(0.0f, 0.0f, 0.0f, 0.0f);
m_DirectionalShadowSplitRadii = new Vector4(0.0f, 0.0f, 0.0f, 0.0f);
// Let engine know we have MSAA on for cases where we support MSAA backbuffer
if (QualitySettings.antiAliasing != m_Asset.MSAASampleCount)

// There's no way to map shadow light indices. We need to pass in the original unsorted index.
// If no additional lights then no light sorting is performed and the indices match.
int shadowOriginalIndex = (lightData.totalAdditionalLightsCount > 0) ? GetLightUnsortedIndex(lightData.mainLightIndex) : lightData.mainLightIndex;
lightData.shadowsRendered = RenderShadows(ref m_CullResults, ref mainLight,
bool shadowsRendered = RenderShadows(ref m_CullResults, ref mainLight,
if (shadowsRendered)
{
lightData.shadowMapSampleType = (m_Asset.ShadowSetting != ShadowType.SOFT_SHADOWS)
? LightShadows.Hard
: mainLight.light.shadows;
}
else
{
lightData.shadowMapSampleType = LightShadows.None;
}
}
}
}

if (m_RequiredDepth)
{
RenderTextureDescriptor depthRTDesc = new RenderTextureDescriptor(rtWidth, rtHeight, RenderTextureFormat.Depth, kCameraDepthBufferBits);
RenderTextureDescriptor depthRTDesc = new RenderTextureDescriptor(rtWidth, rtHeight, RenderTextureFormat.Depth, kDepthStencilBufferBits);
cmd.GetTemporaryRT(CameraRenderTargetID.depth, depthRTDesc, FilterMode.Bilinear);
if (LightweightUtils.HasFlag(renderingConfig, FrameRenderingConfiguration.DepthCopy))

RenderTextureDescriptor colorRTDesc = new RenderTextureDescriptor(rtWidth, rtHeight, m_ColorFormat, kCameraDepthBufferBits);
RenderTextureDescriptor colorRTDesc = new RenderTextureDescriptor(rtWidth, rtHeight, m_ColorFormat, kDepthStencilBufferBits);
colorRTDesc.msaaSamples = msaaSamples;
colorRTDesc.enableRandomWrite = false;

int visibleLightsCount = Math.Min(visibleLights.Length, m_Asset.MaxPixelLights);
m_SortedLightIndexMap.Clear();
lightData.shadowsRendered = false;
lightData.shadowMapSampleType = LightShadows.None;
if (visibleLightsCount <= 1)
lightData.mainLightIndex = GetMainLight(visibleLights);

if (currLight.light == null)
break;
// Shadow lights are sorted by type (directional > puctual) and intensity
// The first shadow light we find in the list is the main light
// In case no shadow light is present we will return the brightest directional light
if (currLight.lightType == LightType.Directional && brighestDirectionalIndex == -1)
brighestDirectionalIndex = i;
}

// Main light has an optimized shader path for main light. This will benefit games that only care about a single light.
// Lightweight pipeline also supports only a single shadow light, if available it will be the main light.
SetupMainLightConstants(cmd, lights, lightData.mainLightIndex);
if (lightData.shadowsRendered)
SetupShadowShaderConstants(cmd, ref lights[lightData.mainLightIndex], m_ShadowCasterCascadesCount);
if (lightData.shadowMapSampleType != LightShadows.None)
SetupShadowReceiverConstants(cmd, ref lights[lightData.mainLightIndex]);
if (lightData.totalAdditionalLightsCount > 0)
SetupAdditionalListConstants(cmd, lights, ref lightData);

cmd.SetGlobalVectorArray(PerCameraBuffer._AdditionalLightSpotAttenuation, m_LightSpotAttenuations);
}
private void SetupShadowShaderConstants(CommandBuffer cmd, ref VisibleLight shadowLight, int cascadeCount)
private void SetupShadowCasterConstants(CommandBuffer cmd, ref VisibleLight visibleLight, Matrix4x4 proj, float cascadeResolution)
{
Light light = visibleLight.light;
float bias = 0.0f;
float normalBias = 0.0f;
// Use same kernel radius as built-in pipeline so we can achieve same bias results
// with the default light bias parameters.
const float kernelRadius = 3.65f;
if (visibleLight.lightType == LightType.Directional)
{
// Scale bias by cascade's world space depth range.
// Directional shadow lights have orthogonal projection.
// proj.m22 = -2 / (far - near) since the projection's depth range is [-1.0, 1.0]
// Therefore we scale it by 0.5. We keep the negative sign and only flip it in case z is
// reversed.
float sign = (SystemInfo.usesReversedZBuffer) ? 1.0f : -1.0f;
bias = light.shadowBias * proj.m22 * 0.5f * sign;
// Currently only square POT cascades resolutions are used.
// We scale normalBias
double frustumWidth = 2.0 / (double)proj.m00;
double frustumHeight = 2.0 / (double)proj.m11;
float texelSizeX = (float)(frustumWidth / (double)cascadeResolution);
float texelSizeY = (float)(frustumHeight / (double)cascadeResolution);
float texelSize = Mathf.Max(texelSizeX, texelSizeY);
// Since we are applying normal bias on caster side we want an inset normal offset
// thus we use a negative normal bias.
normalBias = -light.shadowNormalBias * texelSize * kernelRadius;
}
else if (visibleLight.lightType == LightType.Spot)
{
float sign = (SystemInfo.usesReversedZBuffer) ? -1.0f : 1.0f;
bias = light.shadowBias * sign;
normalBias = 0.0f;
}
else
{
Debug.LogWarning("Only spot and directional shadow casters are supported in lightweight pipeline");
}
Vector3 lightDirection = -visibleLight.localToWorld.GetColumn(2);
cmd.SetGlobalVector("_ShadowBias", new Vector4(bias, normalBias, 0.0f, 0.0f));
cmd.SetGlobalVector("_LightDirection", new Vector4(lightDirection.x, lightDirection.y, lightDirection.z, 0.0f));
}
private void SetupShadowReceiverConstants(CommandBuffer cmd, ref VisibleLight shadowLight)
float strength = 1.0f - light.shadowStrength;
float bias = light.shadowBias * 0.1f;
float normalBias = light.shadowNormalBias;
float nearPlane = light.shadowNearPlane;
const int maxShadowCascades = 4;
Matrix4x4[] shadowMatrices = new Matrix4x4[maxShadowCascades];
for (int i = 0; i < cascadeCount; ++i)
int cascadeCount = m_ShadowCasterCascadesCount;
Matrix4x4[] shadowMatrices = new Matrix4x4[kMaxCascades + 1];
for (int i = 0; i < kMaxCascades; ++i)
// We setup and additional a no-op WorldToShadow matrix in the last index
// because the ComputeCascadeIndex function in Shadows.hlsl can return an index
// out of bounds. (position not inside any cascade) and we want to avoid branching
Matrix4x4 noOpShadowMatrix = Matrix4x4.zero;
noOpShadowMatrix.m33 = (SystemInfo.usesReversedZBuffer) ? 1.0f : 0.0f;
shadowMatrices[kMaxCascades] = noOpShadowMatrix;
cmd.SetGlobalVector("_ShadowData", new Vector4(light.shadowStrength, 0.0f, 0.0f, 0.0f));
cmd.SetGlobalVector("_ShadowData", new Vector4(strength, bias, normalBias, nearPlane));
cmd.SetGlobalVector("_DirShadowSplitSphereRadii", m_DirectionalShadowSplitRadii);
cmd.SetGlobalVector("_ShadowOffset0", new Vector4(-invShadowResolution, -invShadowResolution, 0.0f, 0.0f));
cmd.SetGlobalVector("_ShadowOffset1", new Vector4(invShadowResolution, -invShadowResolution, 0.0f, 0.0f));
cmd.SetGlobalVector("_ShadowOffset2", new Vector4(-invShadowResolution, invShadowResolution, 0.0f, 0.0f));

private void SetShaderKeywords(CommandBuffer cmd, ref LightData lightData, VisibleLight[] visibleLights)
{
int vertexLightsCount = lightData.totalAdditionalLightsCount - lightData.pixelAdditionalLightsCount;
CoreUtils.SetKeyword(cmd, "_VERTEX_LIGHTS", vertexLightsCount > 0);
CoreUtils.SetKeyword(cmd, "_MAIN_LIGHT_COOKIE", mainLightIndex != -1 && LightweightUtils.IsSupportedCookieType(visibleLights[mainLightIndex].lightType) && visibleLights[mainLightIndex].light.cookie != null);
CoreUtils.SetKeyword(cmd, "_MAIN_DIRECTIONAL_LIGHT", mainLightIndex == -1 || visibleLights[mainLightIndex].lightType == LightType.Directional);
CoreUtils.SetKeyword(cmd, "_MAIN_SPOT_LIGHT", mainLightIndex != -1 && visibleLights[mainLightIndex].lightType == LightType.Spot);
CoreUtils.SetKeyword(cmd, "_ADDITIONAL_LIGHTS", lightData.totalAdditionalLightsCount > 0);
CoreUtils.SetKeyword(cmd, "_MIXED_LIGHTING_SHADOWMASK", m_MixedLightingSetup == MixedLightingSetup.ShadowMask);
CoreUtils.SetKeyword(cmd, "_MIXED_LIGHTING_SUBTRACTIVE", m_MixedLightingSetup == MixedLightingSetup.Subtractive);
// We have no good approach exposed to skip shader variants, e.g, ideally we would like to skip _CASCADE for all punctual lights
// We combine light and shadow classification keywords to reduce the amount of shader variants.
// Lightweight shader library declares defines based on these keywords to avoid having to check them in the shaders
// Core.hlsl defines _MAIN_LIGHT_DIRECTIONAL and _MAIN_LIGHT_SPOT (point lights can't be main light)
// Shadow.hlsl defines _SHADOWS_ENABLED, _SHADOWS_SOFT, _SHADOWS_CASCADE, _SHADOWS_PERSPECTIVE
string[] mainLightKeywords =
{
"_MAIN_LIGHT_DIRECTIONAL_SHADOW",
"_MAIN_LIGHT_DIRECTIONAL_SHADOW_CASCADE",
"_MAIN_LIGHT_DIRECTIONAL_SHADOW_SOFT",
"_MAIN_LIGHT_DIRECTIONAL_SHADOW_CASCADE_SOFT",
string[] shadowKeywords = new string[] { "_HARD_SHADOWS", "_SOFT_SHADOWS", "_HARD_SHADOWS_CASCADES", "_SOFT_SHADOWS_CASCADES" };
for (int i = 0; i < shadowKeywords.Length; ++i)
cmd.DisableShaderKeyword(shadowKeywords[i]);
"_MAIN_LIGHT_SPOT_SHADOW",
"_MAIN_LIGHT_SPOT_SHADOW_SOFT"
};
if (m_Asset.AreShadowsEnabled() && lightData.shadowsRendered)
for (int i = 0; i < mainLightKeywords.Length; ++i)
cmd.DisableShaderKeyword(mainLightKeywords[i]);
if (mainLightIndex != -1 && (lightData.shadowMapSampleType != LightShadows.None))
int keywordIndex = (int)m_Asset.ShadowSetting - 1;
if (m_Asset.CascadeCount > 1)
keywordIndex += 2;
cmd.EnableShaderKeyword(shadowKeywords[keywordIndex]);
StringBuilder keywordString = new StringBuilder("_MAIN_LIGHT");
LightType mainLightType = visibleLights[mainLightIndex].lightType;
if (mainLightType == LightType.Directional)
{
keywordString.Append("_DIRECTIONAL_SHADOW");
if (m_Asset.CascadeCount > 1)
keywordString.Append("_CASCADE");
}
else
{
keywordString.Append("_SPOT_SHADOW");
}
if (lightData.shadowMapSampleType == LightShadows.Soft)
keywordString.Append("_SOFT");
string keyword = keywordString.ToString();
cmd.EnableShaderKeyword(keyword);
CoreUtils.SetKeyword(cmd, "_MAIN_LIGHT_COOKIE", mainLightIndex != -1 && LightweightUtils.IsSupportedCookieType(visibleLights[mainLightIndex].lightType) && visibleLights[mainLightIndex].light.cookie != null);
CoreUtils.SetKeyword(cmd, "_ADDITIONAL_LIGHTS", lightData.totalAdditionalLightsCount > 0);
CoreUtils.SetKeyword(cmd, "_MIXED_LIGHTING_SUBTRACTIVE", m_MixedLightingSetup == MixedLightingSetup.Subtractive);
CoreUtils.SetKeyword(cmd, "_VERTEX_LIGHTS", vertexLightsCount > 0);
CoreUtils.SetKeyword(cmd, "SOFTPARTICLES_ON", m_Asset.RequireCameraDepthTexture);
}

if (!cullResults.GetShadowCasterBounds(shadowLightIndex, out bounds))
return false;
var cmd = CommandBufferPool.Get();
cmd.name = "Render packed shadows";
cmd.GetTemporaryRT(m_ShadowMapRTID, m_ShadowSettings.shadowAtlasWidth,
m_ShadowSettings.shadowAtlasHeight, kShadowDepthBufferBits, FilterMode.Bilinear, m_ShadowSettings.renderTextureFormat);
SetRenderTarget(cmd, m_ShadowMapRT, ClearFlag.All);
context.ExecuteCommandBuffer(cmd);
CommandBufferPool.Release(cmd);
Vector3 splitRatio = m_ShadowSettings.directionalLightCascades;
bool needRendering = false;
bool success = false;
var cmd = CommandBufferPool.Get("Prepare Shadowmap");
cmd.GetTemporaryRT(m_ShadowMapRTID, m_ShadowSettings.shadowAtlasWidth,
m_ShadowSettings.shadowAtlasHeight, kDepthStencilBufferBits, FilterMode.Bilinear, m_ShadowSettings.renderTextureFormat);
SetRenderTarget(cmd, m_ShadowMapRT, ClearFlag.Depth);
needRendering = cullResults.ComputeSpotShadowMatricesAndCullingPrimitives(shadowLightIndex, out view, out proj,
success = cullResults.ComputeSpotShadowMatricesAndCullingPrimitives(shadowLightIndex, out view, out proj,
if (!needRendering)
return false;
SetupShadowSliceTransform(0, shadowResolution, proj, view);
RenderShadowSlice(ref context, 0, proj, view, settings);
if (success)
{
SetupShadowCasterConstants(cmd, ref shadowLight, proj, shadowResolution);
SetupShadowSliceTransform(0, shadowResolution, proj, view);
RenderShadowSlice(cmd, ref context, 0, proj, view, settings);
}
needRendering = cullResults.ComputeDirectionalShadowMatricesAndCullingPrimitives(shadowLightIndex,
cascadeIdx, m_ShadowCasterCascadesCount, splitRatio, shadowResolution, shadowNearPlane, out view, out proj,
success = cullResults.ComputeDirectionalShadowMatricesAndCullingPrimitives(shadowLightIndex,
cascadeIdx, m_ShadowCasterCascadesCount, m_ShadowSettings.directionalLightCascades, shadowResolution, shadowNearPlane, out view, out proj,
float cullingSphereRadius = settings.splitData.cullingSphere.w;
m_DirectionalShadowSplitDistances[cascadeIdx].w *= settings.splitData.cullingSphere.w;
m_DirectionalShadowSplitRadii[cascadeIdx] = cullingSphereRadius * cullingSphereRadius;
if (!needRendering)
return false;
if (!success)
break;
SetupShadowCasterConstants(cmd, ref shadowLight, proj, shadowResolution);
RenderShadowSlice(ref context, cascadeIdx, proj, view, settings);
RenderShadowSlice(cmd, ref context, cascadeIdx, proj, view, settings);
return false;
return true;
CommandBufferPool.Release(cmd);
return success;
// Assumes MAX_CASCADES = 4
m_ShadowSlices[cascadeIndex].atlasX = (cascadeIndex % 2) * shadowResolution;
m_ShadowSlices[cascadeIndex].atlasY = (cascadeIndex / 2) * shadowResolution;
m_ShadowSlices[cascadeIndex].shadowResolution = shadowResolution;
m_ShadowSlices[cascadeIndex].shadowTransform = Matrix4x4.identity;
if (cascadeIndex >= kMaxCascades)
{
Debug.LogError(String.Format("{0} is an invalid cascade index. Maximum of {1} cascades", cascadeIndex, kMaxCascades));
return;
}
var matScaleBias = Matrix4x4.identity;
matScaleBias.m00 = 0.5f;
matScaleBias.m11 = 0.5f;
matScaleBias.m22 = 0.5f;
matScaleBias.m03 = 0.5f;
matScaleBias.m23 = 0.5f;
matScaleBias.m13 = 0.5f;
int atlasX = (cascadeIndex % 2) * shadowResolution;
int atlasY = (cascadeIndex / 2) * shadowResolution;
float atlasWidth = (float)m_ShadowSettings.shadowAtlasWidth;
float atlasHeight = (float)m_ShadowSettings.shadowAtlasHeight;
float deviceZRangeScale = 1.0f;
// Later down the pipeline the proj matrix will be scaled to reverse-z in case of DX.
// We need account for that scale in the shadowTransform.
// Currently CullResults ComputeDirectionalShadowMatricesAndCullingPrimitives doesn't
// apply z reversal to projection matrix. We need to do it manually here.
matScaleBias.m22 = -0.5f;
{
proj.m20 = -proj.m20;
proj.m21 = -proj.m21;
proj.m22 = -proj.m22;
proj.m23 = -proj.m23;
deviceZRangeScale = 0.5f;
}
Matrix4x4 worldToShadow = proj * view;
var textureScaleAndBias = Matrix4x4.identity;
textureScaleAndBias.m00 = 0.5f;
textureScaleAndBias.m11 = 0.5f;
textureScaleAndBias.m22 = deviceZRangeScale;
textureScaleAndBias.m03 = 0.5f;
textureScaleAndBias.m23 = deviceZRangeScale;
textureScaleAndBias.m13 = 0.5f;
var matTile = Matrix4x4.identity;
matTile.m00 = (float)m_ShadowSlices[cascadeIndex].shadowResolution /
(float)m_ShadowSettings.shadowAtlasWidth;
matTile.m11 = (float)m_ShadowSlices[cascadeIndex].shadowResolution /
(float)m_ShadowSettings.shadowAtlasHeight;
matTile.m03 = (float)m_ShadowSlices[cascadeIndex].atlasX / (float)m_ShadowSettings.shadowAtlasWidth;
matTile.m13 = (float)m_ShadowSlices[cascadeIndex].atlasY / (float)m_ShadowSettings.shadowAtlasHeight;
// Apply texture scale and offset to save a MAD in shader.
worldToShadow = textureScaleAndBias * worldToShadow;
m_ShadowSlices[cascadeIndex].shadowTransform = matTile * matScaleBias * proj * view;
var cascadeAtlas = Matrix4x4.identity;
cascadeAtlas.m00 = (float)shadowResolution / atlasWidth;
cascadeAtlas.m11 = (float)shadowResolution / atlasHeight;
cascadeAtlas.m03 = (float)atlasX / atlasWidth;
cascadeAtlas.m13 = (float)atlasY / atlasHeight;
// Apply cascade scale and offset
worldToShadow = cascadeAtlas * worldToShadow;
m_ShadowSlices[cascadeIndex].atlasX = atlasX;
m_ShadowSlices[cascadeIndex].atlasY = atlasY;
m_ShadowSlices[cascadeIndex].shadowResolution = shadowResolution;
m_ShadowSlices[cascadeIndex].shadowTransform = worldToShadow;
private void RenderShadowSlice(ref ScriptableRenderContext context, int cascadeIndex,
private void RenderShadowSlice(CommandBuffer cmd, ref ScriptableRenderContext context, int cascadeIndex,
var buffer = CommandBufferPool.Get("Prepare Shadowmap Slice");
buffer.SetViewport(new Rect(m_ShadowSlices[cascadeIndex].atlasX, m_ShadowSlices[cascadeIndex].atlasY,
cmd.SetViewport(new Rect(m_ShadowSlices[cascadeIndex].atlasX, m_ShadowSlices[cascadeIndex].atlasY,
buffer.SetViewProjectionMatrices(view, proj);
context.ExecuteCommandBuffer(buffer);
cmd.SetViewProjectionMatrices(view, proj);
context.ExecuteCommandBuffer(cmd);
CommandBufferPool.Release(buffer);
cmd.Clear();
}
private int GetMaxTileResolutionInAtlas(int atlasWidth, int atlasHeight, int tileCount)

26
ScriptableRenderPipeline/LightweightPipeline/LWRP/Shaders/LightweightPassShadow.hlsl


#include "LightweightShaderLibrary/Core.hlsl"
float4 ShadowPassVertex(float4 pos : POSITION) : SV_POSITION
// x: global clip space bias, y: normal world space bias
float4 _ShadowBias;
float3 _LightDirection;
struct VertexInput
{
float4 position : POSITION;
float3 normal : NORMAL;
};
float4 ShadowPassVertex(VertexInput v) : SV_POSITION
float4 clipPos = TransformObjectToHClip(pos.xyz);
float3 positionWS = TransformObjectToWorld(v.position.xyz);
float3 normalWS = TransformObjectToWorldDir(v.normal);
float invNdotL = 1.0 - saturate(dot(_LightDirection, normalWS));
float scale = invNdotL * _ShadowBias.y;
// normal bias is negative since we want to apply an inset normal offset
positionWS = normalWS * scale.xxx + positionWS;
float4 clipPos = TransformWorldToHClip(positionWS);
// _ShadowBias.x sign depens on if platform has reversed z buffer
clipPos.z += _ShadowBias.x;
#if defined(UNITY_REVERSED_Z)
clipPos.z = min(clipPos.z, UNITY_NEAR_CLIP_VALUE);
#else

22
ScriptableRenderPipeline/LightweightPipeline/LWRP/Shaders/LightweightShaderLibrary/Core.hlsl


#include "CoreRP/ShaderLibrary/Packing.hlsl"
#include "Input.hlsl"
///////////////////////////////////////////////////////////////////////////////
// Light Classification defines //
// //
// In order to reduce shader variations main light keywords were combined //
// here we define main light type keywords. //
// Main light is either a shadow casting light or the brighest directional. //
// Lightweight pipeline doesn't support point light shadows so they can't be //
// classified as main light. //
///////////////////////////////////////////////////////////////////////////////
#if defined(_MAIN_LIGHT_DIRECTIONAL_SHADOW) || defined(_MAIN_LIGHT_DIRECTIONAL_SHADOW_CASCADE) || defined(_MAIN_LIGHT_DIRECTIONAL_SHADOW_SOFT) || defined(_MAIN_LIGHT_DIRECTIONAL_SHADOW_CASCADE_SOFT)
#define _MAIN_LIGHT_DIRECTIONAL
#endif
#if defined(_MAIN_LIGHT_SPOT_SHADOW) || defined(_MAIN_LIGHT_SPOT_SHADOW_SOFT)
#define _MAIN_LIGHT_SPOT
#endif
// In case no shadow casting light we classify main light as directional
#if !defined(_MAIN_LIGHT_DIRECTIONAL) && !defined(_MAIN_LIGHT_SPOT)
#define _MAIN_LIGHT_DIRECTIONAL
#endif
#ifdef _NORMALMAP
#define OUTPUT_NORMAL(IN, OUT) OutputTangentToWorld(IN.tangent, IN.normal, OUT.tangent, OUT.binormal, OUT.normal)
#else

10
ScriptableRenderPipeline/LightweightPipeline/LWRP/Shaders/LightweightShaderLibrary/Lighting.hlsl


// If shadows and mixed subtractive mode is enabled we need to remove direct
// light contribution from lightmap from occluded pixels so we can have dynamic objects
// casting shadows onto static correctly.
#if defined(_MIXED_LIGHTING_SUBTRACTIVE) && defined(LIGHTMAP_ON) && defined(_SHADOWS)
#if defined(_MIXED_LIGHTING_SUBTRACTIVE) && defined(LIGHTMAP_ON) && defined(_SHADOWS_ENABLED)
indirectDiffuse = SubtractDirectMainLightFromLightmap(indirectDiffuse, mainLightRealtimeAttenuation, lambert);
#endif

half CookieAttenuation(float3 worldPos)
{
#ifdef _MAIN_LIGHT_COOKIE
#ifdef _MAIN_DIRECTIONAL_LIGHT
#ifdef _MAIN_LIGHT_DIRECTIONAL
#elif defined(_MAIN_SPOT_LIGHT)
#elif defined(_MAIN_LIGHT_SPOT)
float4 projPos = mul(_WorldToLight, float4(worldPos, 1.0));
float2 cookieUV = projPos.xy / projPos.w + 0.5;
return SAMPLE_TEXTURE2D(_MainLightCookie, sampler_MainLightCookie, cookieUV).a;

inline half GetMainLightDirectionAndRealtimeAttenuation(LightInput lightInput, half3 normalWS, float3 positionWS, out half3 lightDirection)
{
#ifdef _MAIN_DIRECTIONAL_LIGHT
#if defined(_MAIN_LIGHT_DIRECTIONAL)
// Light pos holds normalized light dir
lightDirection = lightInput.position.xyz;
half attenuation = 1.0;

// Cookies and shadows are only computed for main light
attenuation *= CookieAttenuation(positionWS);
attenuation *= LIGHTWEIGHT_SHADOW_ATTENUATION(positionWS, normalWS, lightDirection);
attenuation *= RealtimeShadowAttenuation(positionWS);
return attenuation;
}

106
ScriptableRenderPipeline/LightweightPipeline/LWRP/Shaders/LightweightShaderLibrary/Shadows.hlsl


#define MAX_SHADOW_CASCADES 4
#if defined(_HARD_SHADOWS) || defined(_SOFT_SHADOWS) || defined(_HARD_SHADOWS_CASCADES) || defined(_SOFT_SHADOWS_CASCADES)
#define _SHADOWS
///////////////////////////////////////////////////////////////////////////////
// Light Classification shadow defines //
// //
// In order to reduce shader variations main light keywords were combined //
// here we define shadow keywords. //
///////////////////////////////////////////////////////////////////////////////
#if defined(_MAIN_LIGHT_DIRECTIONAL_SHADOW) || defined(_MAIN_LIGHT_DIRECTIONAL_SHADOW_CASCADE) || defined(_MAIN_LIGHT_DIRECTIONAL_SHADOW_SOFT) || defined(_MAIN_LIGHT_DIRECTIONAL_SHADOW_CASCADE_SOFT) || defined(_MAIN_LIGHT_SPOT_SHADOW) || defined(_MAIN_LIGHT_SPOT_SHADOW_SOFT)
#define _SHADOWS_ENABLED
#endif
#if defined(_MAIN_LIGHT_DIRECTIONAL_SHADOW_SOFT) || defined(_MAIN_LIGHT_DIRECTIONAL_SHADOW_CASCADE_SOFT) || defined(_MAIN_LIGHT_SPOT_SHADOW_SOFT)
#define _SHADOWS_SOFT
#if defined(_HARD_SHADOWS_CASCADES) || defined(_SOFT_SHADOWS_CASCADES)
#define _SHADOW_CASCADES
#if defined(_MAIN_LIGHT_DIRECTIONAL_SHADOW_CASCADE) || defined(_MAIN_LIGHT_DIRECTIONAL_SHADOW_CASCADE_SOFT)
#define _SHADOWS_CASCADE
#ifdef _SHADOWS
#define LIGHTWEIGHT_SHADOW_ATTENUATION(posWorld, vertexNormal, shadowDir) ComputeShadowAttenuation(posWorld, vertexNormal, shadowDir)
#else
#define LIGHTWEIGHT_SHADOW_ATTENUATION(posWorld, vertexNormal, shadowDir) 1.0h
#if defined(_MAIN_LIGHT_SPOT_SHADOW) || defined(_MAIN_LIGHT_SPOT_SHADOW_SOFT)
#define _SHADOWS_PERSPECTIVE
#endif
TEXTURE2D_SHADOW(_ShadowMap);

float4x4 _WorldToShadow[MAX_SHADOW_CASCADES];
// Last cascade is initialized with a no-op matrix. It always transforms
// shadow coord to half(0, 0, NEAR_PLANE). We use this trick to avoid
// branching since ComputeCascadeIndex can return cascade index = MAX_SHADOW_CASCADES
float4x4 _WorldToShadow[MAX_SHADOW_CASCADES + 1];
float4 _DirShadowSplitSphereRadii;
half4 _ShadowData; // (x: 1.0 - shadowStrength, y: bias, z: normal bias, w: near plane offset)
half4 _ShadowData; // (x: shadowStrength)
float ApplyDepthBias(float clipZ)
{
#ifdef UNITY_REVERSED_Z
return clipZ + _ShadowData.y;
#endif
return clipZ - _ShadowData.y;
}
float3 coord = shadowCoord.xyz /= shadowCoord.w;
coord.z = saturate(ApplyDepthBias(coord.z));
if (coord.x <= 0 || coord.x >= 1 || coord.y <= 0 || coord.y >= 1)
return 1;
#if defined(_SHADOWS_PERSPECTIVE)
shadowCoord.xyz = shadowCoord.xyz /= shadowCoord.w;
#endif
#if defined(_SOFT_SHADOWS) || defined(_SOFT_SHADOWS_CASCADES)
#ifdef _SHADOWS_SOFT
half4 attenuation;
attenuation.x = SAMPLE_TEXTURE2D_SHADOW(_ShadowMap, sampler_ShadowMap, coord + _ShadowOffset0.xyz);
attenuation.y = SAMPLE_TEXTURE2D_SHADOW(_ShadowMap, sampler_ShadowMap, coord + _ShadowOffset1.xyz);
attenuation.z = SAMPLE_TEXTURE2D_SHADOW(_ShadowMap, sampler_ShadowMap, coord + _ShadowOffset2.xyz);
attenuation.w = SAMPLE_TEXTURE2D_SHADOW(_ShadowMap, sampler_ShadowMap, coord + _ShadowOffset3.xyz);
lerp(attenuation, 1.0, _ShadowData.xxxx);
return dot(attenuation, 0.25);
half4 attenuation4;
attenuation4.x = SAMPLE_TEXTURE2D_SHADOW(_ShadowMap, sampler_ShadowMap, shadowCoord.xyz + _ShadowOffset0.xyz);
attenuation4.y = SAMPLE_TEXTURE2D_SHADOW(_ShadowMap, sampler_ShadowMap, shadowCoord.xyz + _ShadowOffset1.xyz);
attenuation4.z = SAMPLE_TEXTURE2D_SHADOW(_ShadowMap, sampler_ShadowMap, shadowCoord.xyz + _ShadowOffset2.xyz);
attenuation4.w = SAMPLE_TEXTURE2D_SHADOW(_ShadowMap, sampler_ShadowMap, shadowCoord.xyz + _ShadowOffset3.xyz);
half attenuation = dot(attenuation4, 0.25);
half attenuation = SAMPLE_TEXTURE2D_SHADOW(_ShadowMap, sampler_ShadowMap, coord);
return lerp(attenuation, 1.0, _ShadowData.x);
half attenuation = SAMPLE_TEXTURE2D_SHADOW(_ShadowMap, sampler_ShadowMap, shadowCoord.xyz);
// Apply shadow strength
attenuation = LerpWhiteTo(attenuation, _ShadowData.x);
// Shadow coords that fall out of the light frustum volume must always return attenuation 1.0
// TODO: We can set shadowmap sampler to clamptoborder when we don't have a shadow atlas and avoid xy coord bounds check
return (shadowCoord.x <= 0 || shadowCoord.x >= 1 || shadowCoord.y <= 0 || shadowCoord.y >= 1 || shadowCoord.z >= 1) ? 1.0 : attenuation;
// TODO: profile if there's a performance improvement if we avoid indexing here
float3 fromCenter0 = wpos.xyz - _DirShadowSplitSpheres[0].xyz;
float3 fromCenter1 = wpos.xyz - _DirShadowSplitSpheres[1].xyz;
float3 fromCenter2 = wpos.xyz - _DirShadowSplitSpheres[2].xyz;

float4 vDirShadowSplitSphereSqRadii;
vDirShadowSplitSphereSqRadii.x = _DirShadowSplitSpheres[0].w;
vDirShadowSplitSphereSqRadii.y = _DirShadowSplitSpheres[1].w;
vDirShadowSplitSphereSqRadii.z = _DirShadowSplitSpheres[2].w;
vDirShadowSplitSphereSqRadii.w = _DirShadowSplitSpheres[3].w;
half4 weights = half4(distances2 < vDirShadowSplitSphereSqRadii);
half4 weights = half4(distances2 < _DirShadowSplitSphereRadii);
inline half ComputeShadowAttenuation(float3 posWorld, half3 vertexNormal, half3 shadowDir)
inline float4 ComputeShadowCoord(float3 positionWS, half cascadeIndex = 0)
half NdotL = dot(vertexNormal, shadowDir);
half bias = saturate(1.0 - NdotL) * _ShadowData.z;
#ifdef _SHADOWS_CASCADE
return mul(_WorldToShadow[cascadeIndex], float4(positionWS, 1.0));
#endif
float3 posWorldOffsetNormal = posWorld + vertexNormal * bias;
return mul(_WorldToShadow[0], float4(positionWS, 1.0));
}
int cascadeIndex = 0;
#ifdef _SHADOW_CASCADES
cascadeIndex = ComputeCascadeIndex(posWorldOffsetNormal);
if (cascadeIndex >= MAX_SHADOW_CASCADES)
return 1.0;
inline half RealtimeShadowAttenuation(float3 positionWS)
{
#if !defined(_SHADOWS_ENABLED)
return 1.0;
float4 shadowCoord = mul(_WorldToShadow[cascadeIndex], float4(posWorldOffsetNormal, 1.0));
half cascadeIndex = ComputeCascadeIndex(positionWS);
float4 shadowCoord = ComputeShadowCoord(positionWS, cascadeIndex);
return SampleShadowmap(shadowCoord);
}

// 2) Allows user to define overall ambient of the scene and control situation when realtime shadow becomes too dark.
half3 realtimeShadow = max(subtractedLightmap, _SubtractiveShadowColor.xyz);
realtimeShadow = lerp(realtimeShadow, lightmap, shadowStrength);
realtimeShadow = lerp(lightmap, realtimeShadow, shadowStrength);
// 3) Pick darkest color
return min(lightmap, realtimeShadow);

17
ScriptableRenderPipeline/LightweightPipeline/LWRP/Shaders/LightweightStandard.shader


#pragma target 3.0
// -------------------------------------
// Material Keywords
#pragma shader_feature _NORMALMAP
#pragma shader_feature _ _ALPHATEST_ON _ALPHABLEND_ON _ALPHAPREMULTIPLY_ON
#pragma shader_feature _EMISSION

#pragma shader_feature _GLOSSYREFLECTIONS_OFF
#pragma shader_feature _SPECULAR_SETUP
// -------------------------------------
// Lightweight Pipeline keywords
// We have no good approach exposed to skip shader variants, e.g, ideally we would like to skip _CASCADE for all puctual lights
// Lightweight combines light classification and shadows keywords to reduce shader variants.
// Lightweight shader library declares defines based on these keywords to avoid having to check them in the shaders
// Core.hlsl defines _MAIN_LIGHT_DIRECTIONAL and _MAIN_LIGHT_SPOT (point lights can't be main light)
// Shadow.hlsl defines _SHADOWS_ENABLED, _SHADOWS_SOFT, _SHADOWS_CASCADE, _SHADOWS_PERSPECTIVE
#pragma multi_compile _ _MAIN_LIGHT_DIRECTIONAL_SHADOW _MAIN_LIGHT_DIRECTIONAL_SHADOW_CASCADE _MAIN_LIGHT_DIRECTIONAL_SHADOW_SOFT _MAIN_LIGHT_DIRECTIONAL_SHADOW_CASCADE_SOFT _MAIN_LIGHT_SPOT_SHADOW _MAIN_LIGHT_SPOT_SHADOW_SOFT
#pragma multi_compile _MAIN_DIRECTIONAL_LIGHT _MAIN_SPOT_LIGHT
#pragma multi_compile _ _VERTEX_LIGHTS
// -------------------------------------
// Unity defined keywords
#pragma multi_compile _ _HARD_SHADOWS _SOFT_SHADOWS _HARD_SHADOWS_CASCADES _SOFT_SHADOWS_CASCADES
#pragma multi_compile _ _VERTEX_LIGHTS
#pragma multi_compile_fog
#pragma vertex LitPassVertex

7
ScriptableRenderPipeline/LightweightPipeline/LWRP/Shaders/LightweightStandardParticles.shader


// No support to Distortion
// No support to Shadows
// ------------------------------------------
// Only directional light is supported for lit particles
// No shadow
// No distortion
Shader "LightweightPipeline/Particles/Standard"
{
Properties

#pragma vertex ParticlesLitVertex
#pragma fragment ParticlesLitFragment
#pragma multi_compile __ SOFTPARTICLES_ON
#pragma multi_compile _MAIN_DIRECTIONAL_LIGHT _MAIN_SPOT_LIGHT
#pragma target 3.5
#pragma shader_feature _ _ALPHATEST_ON _ALPHABLEND_ON _ALPHAPREMULTIPLY_ON _ALPHAMODULATE_ON

18
ScriptableRenderPipeline/LightweightPipeline/LWRP/Shaders/LightweightStandardSimpleLighting.shader


// Required to compile gles 2.0 with standard srp library
#pragma prefer_hlslcc gles
#pragma target 3.0
// -------------------------------------
// Material Keywords
#pragma shader_feature _ _ALPHATEST_ON _ALPHABLEND_ON
#pragma shader_feature _ _SPECGLOSSMAP _SPECULAR_COLOR
#pragma shader_feature _ _GLOSSINESS_FROM_BASE_ALPHA

// -------------------------------------
// Lightweight Pipeline keywords
// We have no good approach exposed to skip shader variants, e.g, ideally we would like to skip _CASCADE for all puctual lights
// Lightweight combines light classification and shadows keywords to reduce shader variants.
// Lightweight shader library declares defines based on these keywords to avoid having to check them in the shaders
// Core.hlsl defines _MAIN_LIGHT_DIRECTIONAL and _MAIN_LIGHT_SPOT (point lights can't be main light)
// Shadow.hlsl defines _SHADOWS_ENABLED, _SHADOWS_SOFT, _SHADOWS_CASCADE, _SHADOWS_PERSPECTIVE
#pragma multi_compile _ _MAIN_LIGHT_DIRECTIONAL_SHADOW _MAIN_LIGHT_DIRECTIONAL_SHADOW_CASCADE _MAIN_LIGHT_DIRECTIONAL_SHADOW_SOFT _MAIN_LIGHT_DIRECTIONAL_SHADOW_CASCADE_SOFT _MAIN_LIGHT_SPOT_SHADOW _MAIN_LIGHT_SPOT_SHADOW_SOFT
#pragma multi_compile _MAIN_DIRECTIONAL_LIGHT _MAIN_SPOT_LIGHT
#pragma multi_compile _ _VERTEX_LIGHTS
// -------------------------------------
// Unity defined keywords
#pragma multi_compile _ _HARD_SHADOWS _SOFT_SHADOWS _HARD_SHADOWS_CASCADES _SOFT_SHADOWS_CASCADES
#pragma multi_compile _ _VERTEX_LIGHTS
#pragma multi_compile_fog
#pragma vertex LitPassVertex

7
ScriptableRenderPipeline/LightweightPipeline/LWRP/Shaders/LightweightStandardTerrain.shader


#pragma vertex SplatmapVert
#pragma fragment SpatmapFragment
#pragma multi_compile _ _MAIN_LIGHT_DIRECTIONAL_SHADOW _MAIN_LIGHT_DIRECTIONAL_SHADOW_CASCADE _MAIN_LIGHT_DIRECTIONAL_SHADOW_SOFT _MAIN_LIGHT_DIRECTIONAL_SHADOW_CASCADE_SOFT _MAIN_LIGHT_SPOT_SHADOW _MAIN_LIGHT_SPOT_SHADOW_SOFT
#pragma multi_compile _MAIN_DIRECTIONAL_LIGHT _MAIN_SPOT_LIGHT
#pragma multi_compile _ _VERTEX_LIGHTS
#pragma multi_compile _ _HARD_SHADOWS _SOFT_SHADOWS _HARD_SHADOWS_CASCADES _SOFT_SHADOWS_CASCADES
#pragma multi_compile _ _VERTEX_LIGHTS
#pragma multi_compile_fog
#pragma multi_compile_fog
#include "LightweightShaderLibrary/Lighting.hlsl"

正在加载...
取消
保存