您最多选择25个主题 主题必须以中文或者字母或数字开头,可以包含连字符 (-),并且长度不得超过35个字符
 
 
 
 

425 行
15 KiB

using UnityEngine;
using System.Collections;
using UnityEngine.Rendering;
using UnityEngine.Profiling;
using System.Collections.Generic;
using System;
namespace UnityEngine.ScriptableRenderLoop
{
[System.Serializable]
public struct ShadowSettings
{
public bool enabled;
public int shadowAtlasWidth;
public int shadowAtlasHeight;
public float maxShadowDistance;
public int directionalLightCascadeCount;
public Vector3 directionalLightCascades;
public static ShadowSettings Default
{
get
{
ShadowSettings settings;
settings.enabled = true;
settings.shadowAtlasHeight = settings.shadowAtlasWidth = 4096;
settings.directionalLightCascadeCount = 1;
settings.directionalLightCascades = new Vector3 (0.05F, 0.2F, 0.3F);
settings.directionalLightCascadeCount = 4;
settings.maxShadowDistance = 1000.0F;
return settings;
}
}
}
public struct InputShadowLightData
{
public int lightIndex;
public int shadowResolution;
}
public struct ShadowLight
{
public int shadowSliceIndex;
public int shadowSliceCount;
}
public struct ShadowSliceData
{
public Matrix4x4 shadowTransform;
public int atlasX;
public int atlasY;
public int shadowResolution;
}
public struct ShadowOutput
{
public ShadowSliceData[] shadowSlices;
public ShadowLight[] shadowLights;
public Vector4[] directionalShadowSplitSphereSqr;
public int GetShadowSliceCountLightIndex(int lightIndex)
{
return shadowLights[lightIndex].shadowSliceCount;
}
public int GetShadowSliceIndex(int lightIndex, int sliceIndex)
{
if (sliceIndex >= shadowLights[lightIndex].shadowSliceCount)
throw new System.IndexOutOfRangeException ();
return shadowLights[lightIndex].shadowSliceIndex + sliceIndex;
}
}
public struct ShadowRenderPass : IDisposable
{
ShadowSettings m_Settings;
[NonSerialized]
bool m_bFailedToPackLastTime;
int m_ShadowTexName;
const int kDepthBuffer = 24;
public ShadowRenderPass(ShadowSettings settings)
{
m_Settings = settings;
m_bFailedToPackLastTime = false;
m_ShadowTexName = Shader.PropertyToID("g_tShadowBuffer");
}
public void Dispose()
{
}
struct AtlasEntry
{
public AtlasEntry(int splitIndex, int lightIndex)
{
this.splitIndex = splitIndex;
this.lightIndex = lightIndex;
}
public int splitIndex;
public int lightIndex;
}
int CalculateNumShadowSplits(int index, ActiveLight[] lights)
{
LightType lightType = lights [index].lightType;
if (lightType == LightType.Spot)
return 1;
if (lightType == LightType.Directional)
return m_Settings.directionalLightCascadeCount;
return 6;
}
static public void ClearPackedShadows(ActiveLight[] lights, out ShadowOutput packedShadows)
{
packedShadows.directionalShadowSplitSphereSqr = null;
packedShadows.shadowSlices = null;
packedShadows.shadowLights = new ShadowLight[lights.Length];
}
//---------------------------------------------------------------------------------------------------------------------------------------------------
bool AutoPackLightsIntoShadowTexture(List<InputShadowLightData> shadowLights, ActiveLight[] lights, out ShadowOutput packedShadows)
{
Dictionary<int, InputShadowLightData> activeShadowLights = new Dictionary<int, InputShadowLightData>();
List<int> shadowIndices = new List<int>();
//@TODO: Disallow multiple directional lights
for (int i = 0; i < shadowLights.Count; i++)
{
shadowIndices.Add (shadowLights[i].lightIndex);
activeShadowLights[shadowLights[i].lightIndex] = shadowLights[i];
}
// World's stupidest sheet packer:
// 1. Sort all lights from largest to smallest
// 2. In a left->right, top->bottom pattern, fill quads until you reach the edge of the texture
// 3. Move position to x=0, y=bottomOfFirstTextureInThisRow
// 4. Goto 2.
// Yes, this will produce holes as the quads shrink, but it's good enough for now. I'll work on this more later to fill the gaps.
// Sort all lights from largest to smallest
shadowIndices.Sort(
delegate( int l1, int l2 )
{
int nCompare = 0;
// Sort shadow-casting lights by shadow resolution
nCompare = activeShadowLights[l1].shadowResolution.CompareTo(activeShadowLights[l2].shadowResolution); // Sort by shadow size
if ( nCompare == 0 ) // Same, so sort by range to stabilize sort results
nCompare = lights[l1].range.CompareTo( lights[l2].range ); // Sort by shadow size
if ( nCompare == 0 ) // Still same, so sort by instance ID to stabilize sort results
nCompare = lights[l1].light.GetInstanceID().CompareTo( lights[l2].light.GetInstanceID() );
return nCompare;
}
);
// Start filling lights into texture
List<AtlasEntry> requestedPages = new List<AtlasEntry>();
packedShadows.shadowLights = new ShadowLight[lights.Length];
for (int i = 0; i != shadowIndices.Count;i++)
{
int numShadowSplits = CalculateNumShadowSplits(shadowIndices[i], lights);
packedShadows.shadowLights[shadowIndices [i]].shadowSliceCount = numShadowSplits;
packedShadows.shadowLights[shadowIndices [i]].shadowSliceIndex = requestedPages.Count;
for (int s = 0; s < numShadowSplits; s++)
requestedPages.Add (new AtlasEntry (requestedPages.Count, shadowIndices[i]));
}
int nCurrentX = 0;
int nCurrentY = -1;
int nNextY = 0;
packedShadows.shadowSlices = new ShadowSliceData[requestedPages.Count];
packedShadows.directionalShadowSplitSphereSqr = new Vector4[4];
foreach (AtlasEntry entry in requestedPages)
{
int shadowResolution = activeShadowLights[entry.lightIndex].shadowResolution;
// Check if first texture is too wide
if ( nCurrentY == -1 )
{
if ( ( shadowResolution > m_Settings.shadowAtlasWidth ) || ( shadowResolution > m_Settings.shadowAtlasHeight ) )
{
Debug.LogError( "ERROR! Shadow packer ran out of space in the " + m_Settings.shadowAtlasWidth + "x" + m_Settings.shadowAtlasHeight + " texture!\n\n" );
m_bFailedToPackLastTime = true;
ClearPackedShadows (lights, out packedShadows);
return false;
}
}
// Goto next scanline
if ( ( nCurrentY == -1 ) || ( ( nCurrentX + shadowResolution ) > m_Settings.shadowAtlasWidth ) )
{
nCurrentX = 0;
nCurrentY = nNextY;
nNextY += shadowResolution;
}
// Check if we've run out of space
if ( ( nCurrentY + shadowResolution ) > m_Settings.shadowAtlasHeight )
{
Debug.LogError( "ERROR! Shadow packer ran out of space in the " + m_Settings.shadowAtlasWidth + "x" + m_Settings.shadowAtlasHeight + " texture!\n\n" );
m_bFailedToPackLastTime = true;
ClearPackedShadows (lights, out packedShadows);
return false;
}
// Save location to light
packedShadows.shadowSlices[entry.splitIndex].atlasX = nCurrentX;
packedShadows.shadowSlices[entry.splitIndex].atlasY = nCurrentY;
packedShadows.shadowSlices[entry.splitIndex].shadowResolution = shadowResolution;
// Move ahead
nCurrentX += shadowResolution;
//Debug.Log( "Sheet packer: " + vl.m_cachedLight.name + " ( " + vl.m_shadowX + ", " + vl.m_shadowY + " ) " + vl.m_shadowResolution + "\n\n" );
}
if ( m_bFailedToPackLastTime )
{
m_bFailedToPackLastTime = false;
Debug.Log( "SUCCESS! Shadow packer can now fit all lights into the " + m_Settings.shadowAtlasWidth + "x" + m_Settings.shadowAtlasHeight + " texture!\n\n" );
}
return requestedPages.Count != 0;
}
static List<InputShadowLightData> GetInputShadowLightData(CullResults cullResults)
{
var shadowCasters = new List<InputShadowLightData> ();
ActiveLight[] lights = cullResults.culledLights;
int directionalLightCount = 0;
for (int i = 0; i < lights.Length; i++)
{
//@TODO: ignore baked. move this logic to c++...
if (lights[i].light.shadows != LightShadows.None)
{
// Only a single directional shadow casting light is supported
if (lights [i].lightType == LightType.Directional)
{
directionalLightCount++;
if (directionalLightCount != 1)
continue;
}
AdditionalLightData additionalLight = lights [i].light.GetComponent<AdditionalLightData> ();
InputShadowLightData light;
light.lightIndex = i;
light.shadowResolution = AdditionalLightData.GetShadowResolution(additionalLight);
shadowCasters.Add (light);
}
}
return shadowCasters;
}
public void UpdateCullingParameters(ref CullingParameters parameters)
{
parameters.shadowDistance = Mathf.Min (m_Settings.maxShadowDistance, parameters.shadowDistance);
}
public void Render(RenderLoop loop, CullResults cullResults, out ShadowOutput packedShadows)
{
if (!m_Settings.enabled)
{
ClearPackedShadows(cullResults.culledLights, out packedShadows);
}
// Pack all shadow quads into the texture
if ( !AutoPackLightsIntoShadowTexture(GetInputShadowLightData(cullResults), cullResults.culledLights, out packedShadows) )
{
// No shadowing lights found, so skip all rendering
return;
}
RenderPackedShadows (loop, cullResults, ref packedShadows);
}
//---------------------------------------------------------------------------------------------------------------------------------------------------
// Render shadows
//---------------------------------------------------------------------------------------------------------------------------------------------------
void RenderPackedShadows(RenderLoop loop, CullResults cullResults, ref ShadowOutput packedShadows)
{
var setRenderTargetCommandBuffer = new CommandBuffer ();
setRenderTargetCommandBuffer.name = "Render packed shadows";
setRenderTargetCommandBuffer.GetTemporaryRT (m_ShadowTexName, m_Settings.shadowAtlasWidth, m_Settings.shadowAtlasHeight, kDepthBuffer, FilterMode.Bilinear, RenderTextureFormat.Shadowmap, RenderTextureReadWrite.Linear);
setRenderTargetCommandBuffer.SetRenderTarget (new RenderTargetIdentifier (m_ShadowTexName));
setRenderTargetCommandBuffer.ClearRenderTarget (true, true, Color.green);
loop.ExecuteCommandBuffer (setRenderTargetCommandBuffer);
setRenderTargetCommandBuffer.Dispose ();
ActiveLight[] activeLights = cullResults.culledLights;
var shadowSlices = packedShadows.shadowSlices;
// Render each light's shadow buffer into a subrect of the shared depth texture
for ( int lightIndex = 0; lightIndex < packedShadows.shadowLights.Length; lightIndex++ )
{
int shadowSliceCount = packedShadows.shadowLights[lightIndex].shadowSliceCount;
if (shadowSliceCount == 0)
continue;
Profiler.BeginSample ("Shadows.GetShadowCasterBounds");
Bounds bounds;
if (!cullResults.GetShadowCasterBounds (lightIndex, out bounds))
{
Profiler.EndSample ();
return;
}
Profiler.EndSample ();
Profiler.BeginSample ("Shadows.DrawShadows");
Matrix4x4 proj;
Matrix4x4 view;
LightType lightType = activeLights[lightIndex].lightType;
Vector3 lightDirection = activeLights[lightIndex].light.transform.forward;
var shadowNearClip = activeLights[lightIndex].light.shadowNearPlane;
int shadowSliceIndex = packedShadows.GetShadowSliceIndex (lightIndex, 0);
if (lightType == LightType.Spot)
{
DrawShadowsSettings settings = new DrawShadowsSettings(cullResults, lightIndex);
bool needRendering = cullResults.ComputeSpotShadowsMatricesAndCullingPrimitives(lightIndex, out view, out proj, out settings.splitData);
SetupShadowSplitMatrices(ref packedShadows.shadowSlices[shadowSliceIndex], proj, view);
if (needRendering)
RenderShadowSplit(ref shadowSlices[shadowSliceIndex], lightDirection, proj, view, ref loop, settings);
}
else if (lightType == LightType.Directional)
{
Vector3 splitRatio = m_Settings.directionalLightCascades;
for (int s = 0; s < 4; ++s)
packedShadows.directionalShadowSplitSphereSqr[s] = new Vector4 (0, 0, 0, float.NegativeInfinity);
for (int s = 0; s < shadowSliceCount; ++s, shadowSliceIndex++)
{
var settings = new DrawShadowsSettings(cullResults, lightIndex);
var shadowResolution = shadowSlices [shadowSliceIndex].shadowResolution;
bool needRendering = cullResults.ComputeDirectionalShadowMatricesAndCullingPrimitives(lightIndex, s, shadowSliceCount, splitRatio, shadowResolution, shadowNearClip, out view, out proj, out settings.splitData);
packedShadows.directionalShadowSplitSphereSqr[s] = settings.splitData.cullingSphere;
packedShadows.directionalShadowSplitSphereSqr[s].w *= packedShadows.directionalShadowSplitSphereSqr[s].w;
SetupShadowSplitMatrices(ref shadowSlices[shadowSliceIndex], proj, view);
if (needRendering)
RenderShadowSplit(ref shadowSlices[shadowSliceIndex], lightDirection, proj, view, ref loop, settings);
}
}
else if (lightType == LightType.Point)
{
for(int s = 0; s < shadowSliceCount; ++s, shadowSliceIndex++)
{
DrawShadowsSettings settings = new DrawShadowsSettings(cullResults, lightIndex);
bool needRendering = cullResults.ComputePointShadowsMatricesAndCullingPrimitives(lightIndex, (CubemapFace)s, 2.0f, out view, out proj, out settings.splitData);
SetupShadowSplitMatrices(ref shadowSlices[shadowSliceIndex], proj, view);
if (needRendering)
RenderShadowSplit(ref shadowSlices[shadowSliceIndex], lightDirection, proj, view, ref loop, settings);
}
}
Profiler.EndSample ();
}
}
private void SetupShadowSplitMatrices(ref ShadowSliceData lightData, Matrix4x4 proj, Matrix4x4 view)
{
Matrix4x4 matScaleBias = Matrix4x4.identity;
matScaleBias.m00 = 0.5f;
matScaleBias.m11 = 0.5f;
matScaleBias.m22 = 0.5f;
matScaleBias.m03 = 0.5f;
matScaleBias.m13 = 0.5f;
matScaleBias.m23 = 0.5f;
Matrix4x4 matTile = Matrix4x4.identity;
matTile.m00 = (float)lightData.shadowResolution / (float)m_Settings.shadowAtlasWidth;
matTile.m11 = (float)lightData.shadowResolution / (float)m_Settings.shadowAtlasHeight;
matTile.m03 = (float)lightData.atlasX / (float)m_Settings.shadowAtlasWidth;
matTile.m13 = (float)lightData.atlasY / (float)m_Settings.shadowAtlasHeight;
lightData.shadowTransform = matTile * matScaleBias * proj * view;
}
//---------------------------------------------------------------------------------------------------------------------------------------------------
private void RenderShadowSplit(ref ShadowSliceData slice, Vector3 lightDirection, Matrix4x4 proj, Matrix4x4 view, ref RenderLoop loop, DrawShadowsSettings settings)
{
var commandBuffer = new CommandBuffer();
commandBuffer.name = "ShadowSetup";
// Set viewport / matrices etc
commandBuffer.SetViewport(new Rect(slice.atlasX, slice.atlasY, slice.shadowResolution, slice.shadowResolution));
//commandBuffer.ClearRenderTarget (true, true, Color.green);
commandBuffer.SetGlobalVector("g_vLightDirWs", new Vector4(lightDirection.x, lightDirection.y, lightDirection.z));
commandBuffer.SetProjectionAndViewMatrices(proj, view);
// commandBuffer.SetGlobalDepthBias (1.0F, 1.0F);
loop.ExecuteCommandBuffer(commandBuffer);
commandBuffer.Dispose();
// Render
loop.DrawShadows(ref settings);
}
}
}