您最多选择25个主题
主题必须以中文或者字母或数字开头,可以包含连字符 (-),并且长度不得超过35个字符
764 行
35 KiB
764 行
35 KiB
using System;
|
|
using UnityEngine.Rendering;
|
|
using UnityEngine.Rendering.PostProcessing;
|
|
using UnityEngine.XR;
|
|
|
|
namespace UnityEngine.Experimental.Rendering.LightweightPipeline
|
|
{
|
|
[Serializable]
|
|
public class ShadowSettings
|
|
{
|
|
public bool enabled;
|
|
public int shadowAtlasWidth;
|
|
public int shadowAtlasHeight;
|
|
|
|
public float maxShadowDistance;
|
|
public int directionalLightCascadeCount;
|
|
public Vector3 directionalLightCascades;
|
|
public float directionalLightNearPlaneOffset;
|
|
|
|
static ShadowSettings defaultShadowSettings = null;
|
|
|
|
public static ShadowSettings Default
|
|
{
|
|
get
|
|
{
|
|
if (defaultShadowSettings == null)
|
|
{
|
|
defaultShadowSettings = new ShadowSettings();
|
|
defaultShadowSettings.enabled = true;
|
|
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;
|
|
}
|
|
return defaultShadowSettings;
|
|
}
|
|
}
|
|
}
|
|
|
|
public struct ShadowSliceData
|
|
{
|
|
public Matrix4x4 shadowTransform;
|
|
public int atlasX;
|
|
public int atlasY;
|
|
public int shadowResolution;
|
|
}
|
|
|
|
public struct LightData
|
|
{
|
|
public int pixelLightsCount;
|
|
public int vertexLightsCount;
|
|
public int shadowLightIndex;
|
|
public bool isSingleLight;
|
|
public bool shadowsRendered;
|
|
}
|
|
|
|
public class LightweightPipeline : RenderPipeline
|
|
{
|
|
private readonly LightweightPipelineAsset m_Asset;
|
|
|
|
// Max amount of visible lights. This controls the lights constant buffers in shader but not the max shaded lights.
|
|
// Lights are set per-object and the max shaded lights for each object are controlled by the max pixel lights in pipeline asset and kMaxVertexLights.
|
|
private static readonly int kMaxVisibleLights = 16;
|
|
private static readonly int kMaxPerObjectLights = 4;
|
|
|
|
private Vector4[] m_LightPositions = new Vector4[kMaxVisibleLights];
|
|
private Vector4[] m_LightColors = new Vector4[kMaxVisibleLights];
|
|
private Vector4[] m_LightAttenuations = new Vector4[kMaxVisibleLights];
|
|
private Vector4[] m_LightSpotDirections = new Vector4[kMaxVisibleLights];
|
|
|
|
private Camera m_CurrCamera = null;
|
|
private LightType m_SingleLightType = LightType.Directional;
|
|
|
|
private int m_LightIndicesCount = 0;
|
|
private ComputeBuffer m_LightIndexListBuffer;
|
|
|
|
private static readonly int kMaxCascades = 4;
|
|
private int m_ShadowCasterCascadesCount = kMaxCascades;
|
|
private int m_ShadowMapProperty;
|
|
private int m_CameraRTProperty;
|
|
private RenderTargetIdentifier m_ShadowMapRTID;
|
|
private RenderTargetIdentifier m_CameraRTID;
|
|
|
|
private bool m_RenderToIntermediateTarget = false;
|
|
private bool m_IntermediateTextureArray = false;
|
|
|
|
private const int kShadowDepthBufferBits = 16;
|
|
private const int kCameraDepthBufferBits = 32;
|
|
private Vector4[] m_DirectionalShadowSplitDistances = new Vector4[kMaxCascades];
|
|
|
|
private ShadowSettings m_ShadowSettings = ShadowSettings.Default;
|
|
private ShadowSliceData[] m_ShadowSlices = new ShadowSliceData[kMaxCascades];
|
|
|
|
private static readonly ShaderPassName m_LitPassName = new ShaderPassName("LightweightForward");
|
|
private static readonly ShaderPassName m_UnlitPassName = new ShaderPassName("SRPDefaultUnlit");
|
|
|
|
private RenderTextureFormat m_ColorFormat = RenderTextureFormat.ARGB32;
|
|
private PostProcessRenderContext m_PostProcessRenderContext;
|
|
|
|
public LightweightPipeline(LightweightPipelineAsset asset)
|
|
{
|
|
m_Asset = asset;
|
|
|
|
BuildShadowSettings();
|
|
m_ShadowMapProperty = Shader.PropertyToID("_ShadowMap");
|
|
m_CameraRTProperty = Shader.PropertyToID("_CameraRT");
|
|
m_ShadowMapRTID = new RenderTargetIdentifier(m_ShadowMapProperty);
|
|
m_CameraRTID = new RenderTargetIdentifier(m_CameraRTProperty);
|
|
m_PostProcessRenderContext = new PostProcessRenderContext();
|
|
|
|
// Let engine know we have MSAA on for cases where we support MSAA backbuffer
|
|
if (QualitySettings.antiAliasing != m_Asset.MSAASampleCount)
|
|
QualitySettings.antiAliasing = m_Asset.MSAASampleCount;
|
|
|
|
Shader.globalRenderPipeline = "LightweightPipeline";
|
|
}
|
|
|
|
public override void Dispose()
|
|
{
|
|
base.Dispose();
|
|
|
|
Shader.globalRenderPipeline = "";
|
|
if (m_LightIndexListBuffer != null)
|
|
{
|
|
m_LightIndexListBuffer.Dispose();
|
|
m_LightIndexListBuffer = null;
|
|
m_LightIndicesCount = 0;
|
|
}
|
|
}
|
|
|
|
CullResults m_CullResults;
|
|
public override void Render(ScriptableRenderContext context, Camera[] cameras)
|
|
{
|
|
base.Render(context, cameras);
|
|
|
|
bool stereoEnabled = XRSettings.isDeviceActive;
|
|
|
|
// TODO: This is at the moment required for all pipes. We should not implicitly change user project settings
|
|
// instead this should be forced when using SRP, since all SRP use linear lighting.
|
|
GraphicsSettings.lightsUseLinearIntensity = true;
|
|
|
|
foreach (Camera camera in cameras)
|
|
{
|
|
m_CurrCamera = camera;
|
|
|
|
ScriptableCullingParameters cullingParameters;
|
|
if (!CullResults.GetCullingParameters(m_CurrCamera, stereoEnabled, out cullingParameters))
|
|
continue;
|
|
|
|
cullingParameters.shadowDistance = Mathf.Min(m_ShadowSettings.maxShadowDistance, m_CurrCamera.farClipPlane);
|
|
CullResults.Cull(ref cullingParameters, context,ref m_CullResults);
|
|
|
|
VisibleLight[] visibleLights = m_CullResults.visibleLights.ToArray();
|
|
|
|
LightData lightData;
|
|
InitializeLightData(visibleLights, out lightData);
|
|
|
|
// Render Shadow Map
|
|
if (lightData.shadowLightIndex > -1)
|
|
lightData.shadowsRendered = RenderShadows(ref m_CullResults, ref visibleLights[lightData.shadowLightIndex], lightData.shadowLightIndex, ref context);
|
|
|
|
// Setup camera matrices and RT
|
|
context.SetupCameraProperties(m_CurrCamera, stereoEnabled);
|
|
|
|
// Setup light and shadow shader constants
|
|
SetupShaderLightConstants(visibleLights, ref lightData, ref m_CullResults, ref context);
|
|
if (lightData.shadowsRendered)
|
|
SetupShadowShaderConstants(ref context, ref visibleLights[lightData.shadowLightIndex], lightData.shadowLightIndex, m_ShadowCasterCascadesCount);
|
|
SetShaderKeywords(ref lightData, ref context);
|
|
|
|
RendererConfiguration configuration = RendererConfiguration.PerObjectReflectionProbes;
|
|
if (m_Asset.EnableLightmap)
|
|
configuration |= RendererConfiguration.PerObjectLightmaps;
|
|
|
|
if (m_Asset.EnableAmbientProbe)
|
|
configuration |= RendererConfiguration.PerObjectLightProbe;
|
|
|
|
if (!lightData.isSingleLight)
|
|
configuration |= RendererConfiguration.PerObjectLightIndices8;
|
|
|
|
PostProcessLayer postProcessLayer = GetCurrCameraPostProcessLayer();
|
|
bool postProcessEnabled = postProcessLayer != null && postProcessLayer.enabled;
|
|
m_RenderToIntermediateTarget = postProcessEnabled || GetRenderToIntermediateTarget();
|
|
|
|
BeginForwardRendering(ref context, stereoEnabled);
|
|
|
|
var litDrawSettings = new DrawRendererSettings(m_CurrCamera, m_LitPassName);
|
|
litDrawSettings.sorting.flags = SortFlags.CommonOpaque;
|
|
litDrawSettings.rendererConfiguration = configuration;
|
|
|
|
var unlitDrawSettings = new DrawRendererSettings(m_CurrCamera, m_UnlitPassName);
|
|
unlitDrawSettings.sorting.flags = SortFlags.CommonTransparent;
|
|
|
|
// Render Opaques
|
|
var opaqueFilterSettings = new FilterRenderersSettings(true) {renderQueueRange = RenderQueueRange.opaque};
|
|
|
|
context.DrawRenderers(m_CullResults.visibleRenderers, ref litDrawSettings, opaqueFilterSettings);
|
|
|
|
// TODO: Check skybox shader
|
|
context.DrawSkybox(m_CurrCamera);
|
|
|
|
// Render Alpha blended
|
|
var transparentFilterSettings = new FilterRenderersSettings(true) {renderQueueRange = RenderQueueRange.transparent};
|
|
|
|
litDrawSettings.sorting.flags = SortFlags.CommonTransparent;
|
|
context.DrawRenderers(m_CullResults.visibleRenderers, ref litDrawSettings, transparentFilterSettings);
|
|
context.DrawRenderers(m_CullResults.visibleRenderers, ref unlitDrawSettings, transparentFilterSettings);
|
|
|
|
if (postProcessEnabled)
|
|
RenderPostProcess(ref context, postProcessLayer);
|
|
|
|
EndForwardRendering(ref context, stereoEnabled, postProcessEnabled);
|
|
|
|
// Release temporary RT
|
|
var discardRT = CommandBufferPool.Get();
|
|
discardRT.ReleaseTemporaryRT(m_ShadowMapProperty);
|
|
discardRT.ReleaseTemporaryRT(m_CameraRTProperty);
|
|
context.ExecuteCommandBuffer(discardRT);
|
|
CommandBufferPool.Release(discardRT);
|
|
}
|
|
|
|
context.Submit();
|
|
}
|
|
|
|
private void BuildShadowSettings()
|
|
{
|
|
m_ShadowSettings = ShadowSettings.Default;
|
|
m_ShadowSettings.directionalLightCascadeCount = m_Asset.CascadeCount;
|
|
|
|
m_ShadowSettings.shadowAtlasWidth = m_Asset.ShadowAtlasResolution;
|
|
m_ShadowSettings.shadowAtlasHeight = m_Asset.ShadowAtlasResolution;
|
|
m_ShadowSettings.maxShadowDistance = m_Asset.ShadowDistance;
|
|
|
|
switch (m_ShadowSettings.directionalLightCascadeCount)
|
|
{
|
|
case 1:
|
|
m_ShadowSettings.directionalLightCascades = new Vector3(1.0f, 0.0f, 0.0f);
|
|
break;
|
|
|
|
case 2:
|
|
m_ShadowSettings.directionalLightCascades = new Vector3(m_Asset.Cascade2Split, 1.0f, 0.0f);
|
|
break;
|
|
|
|
default:
|
|
m_ShadowSettings.directionalLightCascades = m_Asset.Cascade4Split;
|
|
break;
|
|
}
|
|
}
|
|
|
|
private void InitializeLightData(VisibleLight[] lights, out LightData lightData)
|
|
{
|
|
int lightsCount = lights.Length;
|
|
int maxPerPixelLights = Math.Min(m_Asset.MaxSupportedPixelLights, kMaxPerObjectLights);
|
|
lightData.pixelLightsCount = Math.Min(lightsCount, maxPerPixelLights);
|
|
lightData.vertexLightsCount = (m_Asset.SupportsVertexLight) ? Math.Min(lightsCount - lightData.pixelLightsCount, kMaxPerObjectLights) : 0;
|
|
|
|
// TODO: Handle Vertex lights in this case
|
|
lightData.isSingleLight = lightData.pixelLightsCount <= 1;
|
|
if (lightData.isSingleLight)
|
|
m_SingleLightType = (lightData.pixelLightsCount == 1) ? lights[0].lightType : LightType.Directional;
|
|
|
|
lightData.shadowsRendered = false;
|
|
|
|
InitializeMainShadowLightIndex(lights, out lightData.shadowLightIndex);
|
|
}
|
|
|
|
private void SetupShaderLightConstants(VisibleLight[] lights, ref LightData lightData, ref CullResults cullResults, ref ScriptableRenderContext context)
|
|
{
|
|
if (lightData.isSingleLight)
|
|
SetupShaderSingleLightConstants(lights, (lightData.pixelLightsCount > 0) ? 0 : -1, ref context);
|
|
else
|
|
SetupShaderLightListConstants(lights, ref lightData, ref context);
|
|
}
|
|
|
|
private void InitializeLightConstants(VisibleLight[] lights, int lightIndex, out Vector4 lightPos, out Vector4 lightColor, out Vector4 lightSpotDir,
|
|
out Vector4 lightAttenuationParams)
|
|
{
|
|
lightPos = Vector4.zero;
|
|
lightColor = Color.black;
|
|
lightAttenuationParams = new Vector4(0.0f, 1.0f, 0.0f, 0.0f);
|
|
lightSpotDir = new Vector4(0.0f, 0.0f, 1.0f, 0.0f);
|
|
|
|
// When no lights are available in the pipeline or maxPixelLights is set to 0
|
|
// In this case we want to initialize the lightData to default values and return
|
|
if (lightIndex < 0)
|
|
return;
|
|
|
|
VisibleLight light = lights[lightIndex];
|
|
if (light.lightType == LightType.Directional)
|
|
{
|
|
Vector4 dir = -light.localToWorld.GetColumn(2);
|
|
lightPos = new Vector4(dir.x, dir.y, dir.z, 0.0f);
|
|
}
|
|
else
|
|
{
|
|
Vector4 pos = light.localToWorld.GetColumn(3);
|
|
lightPos = new Vector4(pos.x, pos.y, pos.z, 1.0f);
|
|
}
|
|
|
|
lightColor = light.finalColor;
|
|
|
|
float rangeSq = light.range * light.range;
|
|
float quadAtten = 0.0f;
|
|
if (light.lightType != LightType.Directional)
|
|
quadAtten = (m_Asset.AttenuationTexture != null) ? 1.0f : 25.0f / rangeSq;
|
|
|
|
if (light.lightType == LightType.Spot)
|
|
{
|
|
Vector4 dir = light.localToWorld.GetColumn(2);
|
|
lightSpotDir = new Vector4(-dir.x, -dir.y, -dir.z, 0.0f);
|
|
|
|
float spotAngle = Mathf.Deg2Rad * light.spotAngle;
|
|
float cosOuterAngle = Mathf.Cos(spotAngle * 0.5f);
|
|
float cosInneAngle = Mathf.Cos(spotAngle * 0.25f);
|
|
float angleRange = cosInneAngle - cosOuterAngle;
|
|
lightAttenuationParams = new Vector4(cosOuterAngle,
|
|
Mathf.Approximately(angleRange, 0.0f) ? 1.0f : angleRange, quadAtten, rangeSq);
|
|
}
|
|
else
|
|
{
|
|
lightSpotDir = new Vector4(0.0f, 0.0f, 1.0f, 0.0f);
|
|
lightAttenuationParams = new Vector4(-1.0f, 1.0f, quadAtten, rangeSq);
|
|
}
|
|
}
|
|
|
|
private void SetupShaderSingleLightConstants(VisibleLight[] lights, int lightIndex, ref ScriptableRenderContext context)
|
|
{
|
|
Vector4 lightPos, lightColor, lightSpotDir, lightAttenuationParams;
|
|
InitializeLightConstants(lights, lightIndex, out lightPos, out lightColor, out lightSpotDir, out lightAttenuationParams);
|
|
|
|
CommandBuffer cmd = new CommandBuffer() { name = "SetupSingleLightConstants" };
|
|
cmd.SetGlobalVector("_LightPosition", lightPos);
|
|
cmd.SetGlobalColor("_LightColor", lightColor);
|
|
cmd.SetGlobalVector("_LightSpotDir", lightSpotDir);
|
|
cmd.SetGlobalVector("_LightAttenuationParams", lightAttenuationParams);
|
|
if (m_Asset.AttenuationTexture != null) cmd.SetGlobalTexture("_AttenuationTexture", m_Asset.AttenuationTexture);
|
|
context.ExecuteCommandBuffer(cmd);
|
|
cmd.Dispose();
|
|
}
|
|
|
|
private void SetupShaderLightListConstants(VisibleLight[] lights, ref LightData lightData, ref ScriptableRenderContext context)
|
|
{
|
|
int maxLights = Math.Min(kMaxVisibleLights, lights.Length);
|
|
|
|
for (int i = 0; i < maxLights; ++i)
|
|
InitializeLightConstants(lights, i, out m_LightPositions[i], out m_LightColors[i], out m_LightSpotDirections[i], out m_LightAttenuations[i]);
|
|
|
|
// Lightweight pipeline only upload kMaxVisibleLights to shader cbuffer.
|
|
// We tell the pipe to disable remaining lights by setting it to -1.
|
|
int[] lightIndexMap = m_CullResults.GetLightIndexMap();
|
|
for (int i = kMaxVisibleLights; i < lightIndexMap.Length; ++i)
|
|
lightIndexMap[i] = -1;
|
|
m_CullResults.SetLightIndexMap(lightIndexMap);
|
|
|
|
CommandBuffer cmd = CommandBufferPool.Get("SetupLightShaderConstants");
|
|
cmd.SetGlobalVector("globalLightCount", new Vector4 (lightData.pixelLightsCount, lightData.vertexLightsCount, 0.0f, 0.0f));
|
|
cmd.SetGlobalVectorArray ("globalLightPos", m_LightPositions);
|
|
cmd.SetGlobalVectorArray ("globalLightColor", m_LightColors);
|
|
cmd.SetGlobalVectorArray ("globalLightAtten", m_LightAttenuations);
|
|
cmd.SetGlobalVectorArray ("globalLightSpotDir", m_LightSpotDirections);
|
|
if (m_Asset.AttenuationTexture != null) cmd.SetGlobalTexture("_AttenuationTexture", m_Asset.AttenuationTexture);
|
|
context.ExecuteCommandBuffer(cmd);
|
|
CommandBufferPool.Release(cmd);
|
|
}
|
|
|
|
private void SetShaderKeywords(ref LightData lightData, ref ScriptableRenderContext context)
|
|
{
|
|
CommandBuffer cmd = new CommandBuffer() { name = "SetShaderKeywords" };
|
|
SetShaderKeywords(cmd, lightData.shadowsRendered, lightData.isSingleLight, lightData.vertexLightsCount > 0);
|
|
context.ExecuteCommandBuffer(cmd);
|
|
cmd.Dispose();
|
|
}
|
|
|
|
private bool RenderShadows(ref CullResults cullResults, ref VisibleLight shadowLight, int shadowLightIndex, ref ScriptableRenderContext context)
|
|
{
|
|
m_ShadowCasterCascadesCount = m_ShadowSettings.directionalLightCascadeCount;
|
|
|
|
if (shadowLight.lightType == LightType.Spot)
|
|
m_ShadowCasterCascadesCount = 1;
|
|
|
|
int shadowResolution = GetMaxTileResolutionInAtlas(m_ShadowSettings.shadowAtlasWidth, m_ShadowSettings.shadowAtlasHeight, m_ShadowCasterCascadesCount);
|
|
|
|
Bounds bounds;
|
|
if (!cullResults.GetShadowCasterBounds(shadowLightIndex, out bounds))
|
|
return false;
|
|
|
|
var setRenderTargetCommandBuffer = CommandBufferPool.Get();
|
|
setRenderTargetCommandBuffer.name = "Render packed shadows";
|
|
setRenderTargetCommandBuffer.GetTemporaryRT(m_ShadowMapProperty, m_ShadowSettings.shadowAtlasWidth,
|
|
m_ShadowSettings.shadowAtlasHeight, kShadowDepthBufferBits, FilterMode.Bilinear, RenderTextureFormat.Depth);
|
|
setRenderTargetCommandBuffer.SetRenderTarget(m_ShadowMapRTID);
|
|
setRenderTargetCommandBuffer.ClearRenderTarget(true, true, Color.black);
|
|
context.ExecuteCommandBuffer(setRenderTargetCommandBuffer);
|
|
CommandBufferPool.Release(setRenderTargetCommandBuffer);
|
|
|
|
float shadowNearPlane = m_Asset.ShadowNearOffset;
|
|
Vector3 splitRatio = m_ShadowSettings.directionalLightCascades;
|
|
|
|
Matrix4x4 view, proj;
|
|
var settings = new DrawShadowsSettings(cullResults, shadowLightIndex);
|
|
bool needRendering = false;
|
|
|
|
if (shadowLight.lightType == LightType.Spot)
|
|
{
|
|
needRendering = cullResults.ComputeSpotShadowMatricesAndCullingPrimitives(shadowLightIndex, out view, out proj,
|
|
out settings.splitData);
|
|
|
|
if (!needRendering)
|
|
return false;
|
|
|
|
SetupShadowSliceTransform(0, shadowResolution, proj, view);
|
|
RenderShadowSlice(ref context, 0, proj, view, settings);
|
|
}
|
|
else if (shadowLight.lightType == LightType.Directional)
|
|
{
|
|
for (int cascadeIdx = 0; cascadeIdx < m_ShadowCasterCascadesCount; ++cascadeIdx)
|
|
{
|
|
needRendering = cullResults.ComputeDirectionalShadowMatricesAndCullingPrimitives(shadowLightIndex,
|
|
cascadeIdx, m_ShadowCasterCascadesCount, splitRatio, shadowResolution, shadowNearPlane, out view, out proj,
|
|
out settings.splitData);
|
|
|
|
m_DirectionalShadowSplitDistances[cascadeIdx] = settings.splitData.cullingSphere;
|
|
m_DirectionalShadowSplitDistances[cascadeIdx].w *= settings.splitData.cullingSphere.w;
|
|
|
|
if (!needRendering)
|
|
return false;
|
|
|
|
SetupShadowSliceTransform(cascadeIdx, shadowResolution, proj, view);
|
|
RenderShadowSlice(ref context, cascadeIdx, proj, view, settings);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
Debug.LogWarning("Only spot and directional shadow casters are supported in lightweight pipeline");
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
private void SetupShadowSliceTransform(int cascadeIndex, int shadowResolution, Matrix4x4 proj, Matrix4x4 view)
|
|
{
|
|
// 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;
|
|
|
|
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;
|
|
|
|
// 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.
|
|
if (SystemInfo.usesReversedZBuffer)
|
|
matScaleBias.m22 = -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;
|
|
|
|
m_ShadowSlices[cascadeIndex].shadowTransform = matTile * matScaleBias * proj * view;
|
|
}
|
|
|
|
private void RenderShadowSlice(ref ScriptableRenderContext context, int cascadeIndex,
|
|
Matrix4x4 proj, Matrix4x4 view, DrawShadowsSettings settings)
|
|
{
|
|
var buffer = CommandBufferPool.Get("Prepare Shadowmap Slice");
|
|
buffer.SetViewport(new Rect(m_ShadowSlices[cascadeIndex].atlasX, m_ShadowSlices[cascadeIndex].atlasY,
|
|
m_ShadowSlices[cascadeIndex].shadowResolution, m_ShadowSlices[cascadeIndex].shadowResolution));
|
|
buffer.SetViewProjectionMatrices(view, proj);
|
|
context.ExecuteCommandBuffer(buffer);
|
|
|
|
context.DrawShadows(ref settings);
|
|
CommandBufferPool.Release(buffer);
|
|
}
|
|
|
|
private int GetMaxTileResolutionInAtlas(int atlasWidth, int atlasHeight, int tileCount)
|
|
{
|
|
int resolution = Mathf.Min(atlasWidth, atlasHeight);
|
|
if (tileCount > Mathf.Log(resolution))
|
|
{
|
|
Debug.LogError(
|
|
String.Format(
|
|
"Cannot fit {0} tiles into current shadowmap atlas of size ({1}, {2}). ShadowMap Resolution set to zero.",
|
|
tileCount, atlasWidth, atlasHeight));
|
|
return 0;
|
|
}
|
|
|
|
int currentTileCount = atlasWidth / resolution * atlasHeight / resolution;
|
|
while (currentTileCount < tileCount)
|
|
{
|
|
resolution = resolution >> 1;
|
|
currentTileCount = atlasWidth / resolution * atlasHeight / resolution;
|
|
}
|
|
return resolution;
|
|
}
|
|
|
|
private void SetupShadowShaderConstants(ref ScriptableRenderContext context, ref VisibleLight shadowLight, int shadowLightIndex, int cascadeCount)
|
|
{
|
|
Vector3 shadowLightDir = Vector3.Normalize(shadowLight.localToWorld.GetColumn(2));
|
|
|
|
float bias = shadowLight.light.shadowBias * 0.1f;
|
|
float normalBias = shadowLight.light.shadowNormalBias;
|
|
float shadowResolution = m_ShadowSlices[0].shadowResolution;
|
|
|
|
const int maxShadowCascades = 4;
|
|
Matrix4x4[] shadowMatrices = new Matrix4x4[maxShadowCascades];
|
|
for (int i = 0; i < cascadeCount; ++i)
|
|
shadowMatrices[i] = (cascadeCount >= i) ? m_ShadowSlices[i].shadowTransform : Matrix4x4.identity;
|
|
|
|
// TODO: shadow resolution per cascade in case cascades endup being supported.
|
|
float invShadowResolution = 1.0f / shadowResolution;
|
|
float[] pcfKernel =
|
|
{
|
|
-0.5f * invShadowResolution, 0.5f * invShadowResolution,
|
|
0.5f * invShadowResolution, 0.5f * invShadowResolution,
|
|
-0.5f * invShadowResolution, -0.5f * invShadowResolution,
|
|
0.5f * invShadowResolution, -0.5f * invShadowResolution
|
|
};
|
|
|
|
var setupShadow = CommandBufferPool.Get("SetupShadowShaderConstants");
|
|
setupShadow.SetGlobalMatrixArray("_WorldToShadow", shadowMatrices);
|
|
setupShadow.SetGlobalVectorArray("_DirShadowSplitSpheres", m_DirectionalShadowSplitDistances);
|
|
setupShadow.SetGlobalVector("_ShadowLightDirection", new Vector4(-shadowLightDir.x, -shadowLightDir.y, -shadowLightDir.z, 0.0f));
|
|
setupShadow.SetGlobalVector("_ShadowData", new Vector4(shadowLightIndex, bias, normalBias, 0.0f));
|
|
setupShadow.SetGlobalFloatArray("_PCFKernel", pcfKernel);
|
|
context.ExecuteCommandBuffer(setupShadow);
|
|
CommandBufferPool.Release(setupShadow);
|
|
}
|
|
|
|
private void SetKeyword(CommandBuffer cmd, string keyword, bool enable)
|
|
{
|
|
if (enable)
|
|
cmd.EnableShaderKeyword(keyword);
|
|
else
|
|
cmd.DisableShaderKeyword(keyword);
|
|
}
|
|
|
|
private void SetShaderKeywords(CommandBuffer cmd, bool renderShadows, bool singleLight, bool vertexLightSupport)
|
|
{
|
|
SetKeyword(cmd, "LIGHTWEIGHT_LINEAR", m_Asset.ForceLinearRendering);
|
|
SetKeyword(cmd, "_VERTEX_LIGHTS", vertexLightSupport);
|
|
SetKeyword(cmd, "_ATTENUATION_TEXTURE", m_Asset.AttenuationTexture != null);
|
|
SetKeyword(cmd, "_LIGHT_PROBES_ON", m_Asset.EnableAmbientProbe);
|
|
SetKeyword(cmd, "LIGHTWEIGHT_LINEAR", m_Asset.ForceLinearRendering);
|
|
|
|
if (!singleLight)
|
|
{
|
|
SetKeyword(cmd, "_SINGLE_DIRECTIONAL_LIGHT", false);
|
|
SetKeyword(cmd, "_SINGLE_SPOT_LIGHT", false);
|
|
SetKeyword(cmd, "_SINGLE_POINT_LIGHT", false);
|
|
}
|
|
else
|
|
{
|
|
switch (m_SingleLightType)
|
|
{
|
|
case LightType.Directional:
|
|
SetKeyword(cmd, "_SINGLE_DIRECTIONAL_LIGHT", true);
|
|
SetKeyword(cmd, "_SINGLE_SPOT_LIGHT", false);
|
|
SetKeyword(cmd, "_SINGLE_POINT_LIGHT", false);
|
|
break;
|
|
|
|
case LightType.Spot:
|
|
SetKeyword(cmd, "_SINGLE_DIRECTIONAL_LIGHT", false);
|
|
SetKeyword(cmd, "_SINGLE_SPOT_LIGHT", true);
|
|
SetKeyword(cmd, "_SINGLE_POINT_LIGHT", false);
|
|
break;
|
|
|
|
case LightType.Point:
|
|
SetKeyword(cmd, "_SINGLE_DIRECTIONAL_LIGHT", false);
|
|
SetKeyword(cmd, "_SINGLE_SPOT_LIGHT", false);
|
|
SetKeyword(cmd, "_SINGLE_POINT_LIGHT", true);
|
|
break;
|
|
}
|
|
}
|
|
|
|
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]);
|
|
|
|
if (renderShadows && m_Asset.CurrShadowType != ShadowType.NO_SHADOW)
|
|
{
|
|
int keywordIndex = (int)m_Asset.CurrShadowType - 1;
|
|
if (m_Asset.CascadeCount > 1)
|
|
keywordIndex += 2;
|
|
cmd.EnableShaderKeyword(shadowKeywords[keywordIndex]);
|
|
}
|
|
}
|
|
|
|
private void InitializeMainShadowLightIndex(VisibleLight[] lights, out int shadowIndex)
|
|
{
|
|
shadowIndex = -1;
|
|
if (m_Asset.CurrShadowType == ShadowType.NO_SHADOW)
|
|
return;
|
|
|
|
float maxIntensity = -1;
|
|
for (int i = 0; i < lights.Length; ++i)
|
|
{
|
|
Light light = lights[i].light;
|
|
if (light.shadows != LightShadows.None && IsSupportedShadowType(light.type) && light.intensity > maxIntensity)
|
|
{
|
|
shadowIndex = i;
|
|
maxIntensity = light.intensity;
|
|
}
|
|
}
|
|
}
|
|
|
|
private bool IsSupportedShadowType(LightType type)
|
|
{
|
|
return (type == LightType.Directional || type == LightType.Spot);
|
|
}
|
|
|
|
private void BeginForwardRendering(ref ScriptableRenderContext context, bool stereoEnabled)
|
|
{
|
|
if (stereoEnabled)
|
|
context.StartMultiEye(m_CurrCamera);
|
|
|
|
var cmd = CommandBufferPool.Get("SetCameraRenderTarget");
|
|
if (m_RenderToIntermediateTarget)
|
|
{
|
|
if (m_CurrCamera.targetTexture == null)
|
|
{
|
|
m_IntermediateTextureArray = false;
|
|
if (stereoEnabled)
|
|
{
|
|
RenderTextureDescriptor xrDesc = XRSettings.eyeTextureDesc;
|
|
xrDesc.depthBufferBits = kCameraDepthBufferBits;
|
|
xrDesc.colorFormat = m_ColorFormat;
|
|
xrDesc.msaaSamples = m_Asset.MSAASampleCount;
|
|
|
|
m_IntermediateTextureArray = (xrDesc.dimension == TextureDimension.Tex2DArray);
|
|
|
|
cmd.GetTemporaryRT(m_CameraRTProperty, xrDesc, FilterMode.Bilinear);
|
|
}
|
|
else
|
|
{
|
|
cmd.GetTemporaryRT(m_CameraRTProperty, Screen.width, Screen.height, kCameraDepthBufferBits,
|
|
FilterMode.Bilinear, m_ColorFormat, RenderTextureReadWrite.Default, m_Asset.MSAASampleCount);
|
|
}
|
|
|
|
if (m_IntermediateTextureArray)
|
|
cmd.SetRenderTarget(m_CameraRTID, 0, CubemapFace.Unknown, -1);
|
|
else
|
|
cmd.SetRenderTarget(m_CameraRTID);
|
|
}
|
|
else
|
|
{
|
|
cmd.SetRenderTarget(new RenderTargetIdentifier(m_CurrCamera.activeTexture));
|
|
}
|
|
}
|
|
else
|
|
{
|
|
cmd.SetRenderTarget(BuiltinRenderTextureType.CurrentActive);
|
|
}
|
|
|
|
if (!stereoEnabled)
|
|
{
|
|
Rect viewport = m_CurrCamera.rect;
|
|
if (m_CurrCamera.targetTexture == null)
|
|
{
|
|
viewport.x *= Screen.width;
|
|
viewport.y *= Screen.height;
|
|
viewport.width *= Screen.width;
|
|
viewport.height *= Screen.height;
|
|
}
|
|
else
|
|
{
|
|
viewport = new Rect(0.0f, 0.0f, m_CurrCamera.targetTexture.width, m_CurrCamera.targetTexture.height);
|
|
}
|
|
|
|
cmd.SetViewport(viewport);
|
|
}
|
|
|
|
// Clear RenderTarget to avoid tile initialization on mobile GPUs
|
|
// https://community.arm.com/graphics/b/blog/posts/mali-performance-2-how-to-correctly-handle-framebuffers
|
|
if (m_CurrCamera.clearFlags != CameraClearFlags.Nothing)
|
|
{
|
|
bool clearDepth = (m_CurrCamera.clearFlags != CameraClearFlags.Nothing);
|
|
bool clearColor = (m_CurrCamera.clearFlags == CameraClearFlags.Color || m_CurrCamera.clearFlags == CameraClearFlags.Skybox);
|
|
cmd.ClearRenderTarget(clearDepth, clearColor, m_CurrCamera.backgroundColor.linear);
|
|
}
|
|
|
|
context.ExecuteCommandBuffer(cmd);
|
|
CommandBufferPool.Release(cmd);
|
|
}
|
|
|
|
private void EndForwardRendering(ref ScriptableRenderContext context, bool stereoEnabled, bool postProcessing)
|
|
{
|
|
|
|
if (m_RenderToIntermediateTarget || postProcessing)
|
|
{
|
|
var cmd = CommandBufferPool.Get("Blit");
|
|
if (m_IntermediateTextureArray)
|
|
{
|
|
cmd.SetRenderTarget(BuiltinRenderTextureType.CameraTarget, 0, CubemapFace.Unknown, -1);
|
|
cmd.Blit(m_CameraRTID, BuiltinRenderTextureType.CurrentActive);
|
|
}
|
|
// If PostProcessing is enabled, it is already blitted to CameraTarget.
|
|
else if (!postProcessing)
|
|
cmd.Blit(BuiltinRenderTextureType.CurrentActive, BuiltinRenderTextureType.CameraTarget);
|
|
|
|
cmd.SetRenderTarget(BuiltinRenderTextureType.CameraTarget);
|
|
context.ExecuteCommandBuffer(cmd);
|
|
CommandBufferPool.Release(cmd);
|
|
}
|
|
|
|
if (stereoEnabled)
|
|
{
|
|
context.StopMultiEye(m_CurrCamera);
|
|
context.StereoEndRender(m_CurrCamera);
|
|
}
|
|
}
|
|
|
|
private void RenderPostProcess(ref ScriptableRenderContext renderContext, PostProcessLayer postProcessLayer)
|
|
{
|
|
var postProcessCommand = CommandBufferPool.Get("Post Processing");
|
|
m_PostProcessRenderContext.Reset();
|
|
m_PostProcessRenderContext.camera = m_CurrCamera;
|
|
m_PostProcessRenderContext.source = BuiltinRenderTextureType.CurrentActive;
|
|
m_PostProcessRenderContext.sourceFormat = m_ColorFormat;
|
|
m_PostProcessRenderContext.destination = BuiltinRenderTextureType.CameraTarget;
|
|
m_PostProcessRenderContext.command = postProcessCommand;
|
|
m_PostProcessRenderContext.flip = true;
|
|
|
|
postProcessLayer.Render(m_PostProcessRenderContext);
|
|
renderContext.ExecuteCommandBuffer(postProcessCommand);
|
|
CommandBufferPool.Release(postProcessCommand);
|
|
}
|
|
|
|
private bool GetRenderToIntermediateTarget()
|
|
{
|
|
bool allowMSAA = m_CurrCamera.allowMSAA && m_Asset.MSAASampleCount > 1 && !PlatformSupportsMSAABackBuffer();
|
|
if (m_CurrCamera.cameraType == CameraType.SceneView || allowMSAA || m_CurrCamera.targetTexture != null)
|
|
return true;
|
|
|
|
return false;
|
|
}
|
|
|
|
private PostProcessLayer GetCurrCameraPostProcessLayer()
|
|
{
|
|
return m_CurrCamera.GetComponent<PostProcessLayer>();
|
|
}
|
|
|
|
private bool PlatformSupportsMSAABackBuffer()
|
|
{
|
|
#if UNITY_ANDROID || UNITY_IPHONE || UNITY_TVOS || UNITY_SAMSUNGTV
|
|
return true;
|
|
#else
|
|
return false;
|
|
#endif
|
|
}
|
|
}
|
|
}
|