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

877 行
42 KiB

using System;
using System.Collections.Generic;
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 additionalPixelLightsCount;
public int vertexLightsCount;
public int mainLightIndex;
public bool hasAdditionalLights;
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 int m_LightIndicesCount = 0;
private ComputeBuffer m_LightIndexListBuffer;
private static readonly int kMaxCascades = 4;
private int m_ShadowCasterCascadesCount = kMaxCascades;
private int m_ShadowMapTexture;
private int m_CameraColorTexture;
private int m_CameraDepthTexture;
private RenderTargetIdentifier m_ShadowMapRT;
private RenderTargetIdentifier m_CameraColorRT;
private RenderTargetIdentifier m_CameraDepthRT;
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;
private PostProcessRenderContext m_PostProcessRenderContext;
private CameraComparer m_CameraComparer = new CameraComparer();
private Mesh m_BlitQuad = null;
private Material m_BlitMaterial = null;
private int m_BlitTexID = Shader.PropertyToID("_BlitTex");
public LightweightPipeline(LightweightPipelineAsset asset)
{
m_Asset = asset;
BuildShadowSettings();
PerFrameBuffer._GlossyEnvironmentColor = Shader.PropertyToID("_GlossyEnvironmentColor");
PerFrameBuffer._AttenuationTexture = Shader.PropertyToID("_AttenuationTexture");
PerCameraBuffer._MainLightPosition = Shader.PropertyToID("_MainLightPosition");
PerCameraBuffer._MainLightColor = Shader.PropertyToID("_MainLightColor");
PerCameraBuffer._MainLightAttenuationParams = Shader.PropertyToID("_MainLightAttenuationParams");
PerCameraBuffer._MainLightSpotDir = Shader.PropertyToID("_MainLightSpotDir");
PerCameraBuffer._AdditionalLightCount = Shader.PropertyToID("_AdditionalLightCount");
PerCameraBuffer._AdditionalLightPosition = Shader.PropertyToID("_AdditionalLightPosition");
PerCameraBuffer._AdditionalLightColor = Shader.PropertyToID("_AdditionalLightColor");
PerCameraBuffer._AdditionalLightAttenuationParams = Shader.PropertyToID("_AdditionalLightAttenuationParams");
PerCameraBuffer._AdditionalLightSpotDir = Shader.PropertyToID("_AdditionalLightSpotDir");
m_ShadowMapTexture = Shader.PropertyToID("_ShadowMap");
m_CameraColorTexture = Shader.PropertyToID("_CameraRT");
m_CameraDepthTexture = Shader.PropertyToID("_CameraDepthTexture");
m_ShadowMapRT = new RenderTargetIdentifier(m_ShadowMapTexture);
m_CameraColorRT = new RenderTargetIdentifier(m_CameraColorTexture);
m_CameraDepthRT = new RenderTargetIdentifier(m_CameraDepthTexture);
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";
m_BlitQuad = LightweightUtils.CreateQuadMesh(false);
m_BlitMaterial = new Material(m_Asset.BlitShader)
{
hideFlags = HideFlags.HideAndDontSave
};
}
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;
SetupPerFrameShaderConstants(ref context);
// Sort cameras array by camera depth
Array.Sort(cameras, m_CameraComparer);
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);
#if UNITY_EDITOR
// Emit scene view UI
if (camera.cameraType == CameraType.SceneView)
ScriptableRenderContext.EmitWorldGeometryForSceneView(camera);
#endif
CullResults.Cull(ref cullingParameters, context, ref m_CullResults);
VisibleLight[] visibleLights = m_CullResults.visibleLights.ToArray();
LightData lightData;
InitializeLightData(visibleLights, out lightData);
ShadowPass(visibleLights, ref context, ref lightData);
ForwardPass(visibleLights, ref context, ref lightData, stereoEnabled);
context.Submit();
}
}
private void ShadowPass(VisibleLight[] visibleLights, ref ScriptableRenderContext context, ref LightData lightData)
{
if (m_Asset.AreShadowsEnabled() && lightData.mainLightIndex != -1)
{
VisibleLight mainLight = visibleLights[lightData.mainLightIndex];
if (mainLight.light.shadows != LightShadows.None)
lightData.shadowsRendered = RenderShadows (ref m_CullResults,
ref mainLight, lightData.mainLightIndex, ref context);
}
}
private void ForwardPass(VisibleLight[] visibleLights, ref ScriptableRenderContext context, ref LightData lightData, bool stereoEnabled)
{
PostProcessLayer postProcessLayer;
RenderingConfiguration renderingConfig = SetupRendering(out postProcessLayer);
bool postProcessEnabled = LightweightUtils.HasFlag(renderingConfig, RenderingConfiguration.PostProcess);
CommandBuffer cmd = CommandBufferPool.Get("SetupShaderConstants");
SetupShaderLightConstants(cmd, visibleLights, ref lightData, ref m_CullResults, ref context);
SetShaderKeywords(cmd, ref lightData, visibleLights);
context.ExecuteCommandBuffer(cmd);
CommandBufferPool.Release(cmd);
// Setup camera matrices
context.SetupCameraProperties(m_CurrCamera, stereoEnabled);
RendererConfiguration rendererSettings = GetRendererSettings(ref lightData);
BeginForwardRendering(ref context, renderingConfig);
RenderOpaques(ref context, rendererSettings);
context.DrawSkybox(m_CurrCamera);
if (postProcessEnabled)
RenderPostProcess(ref context, postProcessLayer, true);
RenderTransparents(ref context, rendererSettings);
if (postProcessEnabled)
RenderPostProcess(ref context, postProcessLayer, false);
EndForwardRendering(ref context, renderingConfig);
// Release temporary RT
var discardRT = CommandBufferPool.Get();
discardRT.ReleaseTemporaryRT(m_ShadowMapTexture);
discardRT.ReleaseTemporaryRT(m_CameraColorTexture);
discardRT.ReleaseTemporaryRT(m_CameraDepthTexture);
context.ExecuteCommandBuffer(discardRT);
CommandBufferPool.Release(discardRT);
}
private void RenderOpaques(ref ScriptableRenderContext context, RendererConfiguration settings)
{
var opaqueDrawSettings = new DrawRendererSettings(m_CurrCamera, m_LitPassName);
opaqueDrawSettings.sorting.flags = SortFlags.CommonOpaque;
opaqueDrawSettings.rendererConfiguration = settings;
var opaqueFilterSettings = new FilterRenderersSettings(true)
{
renderQueueRange = RenderQueueRange.opaque
};
context.DrawRenderers(m_CullResults.visibleRenderers, ref opaqueDrawSettings, opaqueFilterSettings);
}
private void RenderTransparents(ref ScriptableRenderContext context, RendererConfiguration settings)
{
var transparentSettings = new DrawRendererSettings(m_CurrCamera, m_LitPassName);
transparentSettings.SetShaderPassName(1, m_UnlitPassName);
transparentSettings.sorting.flags = SortFlags.CommonTransparent;
transparentSettings.rendererConfiguration = settings;
var transparentFilterSettings = new FilterRenderersSettings(true)
{
renderQueueRange = RenderQueueRange.transparent
};
context.DrawRenderers(m_CullResults.visibleRenderers, ref transparentSettings, transparentFilterSettings);
}
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 RenderingConfiguration SetupRendering(out PostProcessLayer postProcessLayer)
{
RenderingConfiguration renderingConfig = (XRSettings.enabled) ? RenderingConfiguration.Stereo : RenderingConfiguration.None;
if (XRSettings.eyeTextureDesc.dimension == TextureDimension.Tex2DArray)
renderingConfig |= RenderingConfiguration.IntermediateTextureArray;
m_ColorFormat = m_CurrCamera.allowHDR ? RenderTextureFormat.ARGBHalf : RenderTextureFormat.ARGB32;
postProcessLayer = m_CurrCamera.GetComponent<PostProcessLayer>();
bool intermediateTexture = false;
if (postProcessLayer != null && postProcessLayer.enabled)
{
renderingConfig |= RenderingConfiguration.PostProcess;
intermediateTexture = true;
}
// When post process is enabled we disable msaa due to lack of depth resolve
// One can still use PostFX AA
else if (m_CurrCamera.allowMSAA && m_Asset.MSAASampleCount > 1)
{
renderingConfig |= RenderingConfiguration.Msaa;
intermediateTexture = !LightweightUtils.PlatformSupportsMSAABackBuffer();
}
Rect cameraRect = m_CurrCamera.rect;
if (cameraRect.x > 0.0f || cameraRect.y > 0.0f || cameraRect.width < 1.0f || cameraRect.height < 1.0f)
intermediateTexture = true;
else
renderingConfig |= RenderingConfiguration.DefaultViewport;
intermediateTexture |= (m_CurrCamera.targetTexture != null || m_Asset.RenderScale < 1.0f || m_CurrCamera.allowHDR);
if (intermediateTexture && !LightweightUtils.HasFlag(renderingConfig, RenderingConfiguration.IntermediateTextureArray))
renderingConfig |= RenderingConfiguration.IntermediateTexture;
return renderingConfig;
}
private void InitializeLightData(VisibleLight[] visibleLights, out LightData lightData)
{
int visibleLightsCount = visibleLights.Length;
// kMaxPerObjectLights + 1 main light
int maxSupportedPixelLights = Math.Min(m_Asset.MaxSupportedPixelLights, kMaxPerObjectLights + 1);
int maxPixelLights = Math.Min(maxSupportedPixelLights, visibleLightsCount);
if (maxPixelLights <= 1)
{
lightData.mainLightIndex = maxPixelLights - 1;
lightData.additionalPixelLightsCount = 0;
}
else
{
lightData.mainLightIndex = GetMainLightIndex(visibleLights);
lightData.additionalPixelLightsCount = maxPixelLights - 1;
}
lightData.vertexLightsCount = (m_Asset.SupportsVertexLight) ? Math.Min(visibleLightsCount - maxPixelLights, kMaxPerObjectLights) : 0;
lightData.hasAdditionalLights = (lightData.additionalPixelLightsCount + lightData.vertexLightsCount) > 0;
lightData.shadowsRendered = false;
}
// How main light is decided:
// 1) If shadows are enabled and at least a shadow light is present:
// Main light is a shadow light. Directional shadow light has priority over the other supported
// shadow lights types.
// Lightweight pipeline only supports 1 single directional shadow light.
// Any additional pixel lights don't cast shadows.
//
// 2) If shadows are disabled or no shadow light is present then main light is the main brighest directional
// 3) If neither a shadow light or main light is first visible light.
private int GetMainLightIndex(VisibleLight[] lights)
{
bool shadowsEnabled = m_Asset.AreShadowsEnabled();
int mainDirectional = -1;
int mainShadowLight = -1;
for (int i = 0; i < lights.Length; ++i)
{
if (shadowsEnabled && LightweightUtils.IsSupportedShadowType (lights[i].lightType) && lights[i].light.shadows != LightShadows.None)
{
// Shadow Type priority: Soft > Hard > None
if (mainShadowLight < 0 || lights[i].light.shadows > lights[mainShadowLight].light.shadows)
mainShadowLight = i;
}
if (lights[i].lightType == LightType.Directional)
{
if (mainDirectional < 0 || lights[i].light.intensity > lights[mainDirectional].light.intensity)
mainDirectional = i;
}
}
if (mainShadowLight >= 0)
{
if (mainDirectional > 0 && lights[mainDirectional].light.shadows != LightShadows.None)
return mainDirectional;
else
return mainShadowLight;
}
if (mainDirectional > 0)
return mainDirectional;
return 0;
}
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 SetupPerFrameShaderConstants(ref ScriptableRenderContext context)
{
// When glossy reflections are OFF in the shader we set a constant color to use as indirect specular
SphericalHarmonicsL2 ambientSH = RenderSettings.ambientProbe;
Vector4 glossyEnvColor = new Vector4(ambientSH[0, 0], ambientSH[1, 0], ambientSH[2, 0]) * RenderSettings.reflectionIntensity;
CommandBuffer cmd = CommandBufferPool.Get("SetupPerFrameConstants");
cmd.SetGlobalVector(PerFrameBuffer._GlossyEnvironmentColor, glossyEnvColor);
if (m_Asset.AttenuationTexture != null) cmd.SetGlobalTexture(PerFrameBuffer._AttenuationTexture, m_Asset.AttenuationTexture);
context.ExecuteCommandBuffer(cmd);
CommandBufferPool.Release (cmd);
}
private void SetupShaderLightConstants(CommandBuffer cmd, VisibleLight[] lights, ref LightData lightData, ref CullResults cullResults, ref ScriptableRenderContext context)
{
// 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.
if (lightData.mainLightIndex != -1)
{
SetupMainLightConstants (cmd, lights, lightData.mainLightIndex, ref context);
if (lightData.shadowsRendered)
SetupShadowShaderConstants (cmd, ref context, ref lights[lightData.mainLightIndex], m_ShadowCasterCascadesCount);
}
if (lightData.hasAdditionalLights)
SetupAdditionalListConstants(cmd, lights, ref lightData, ref context);
}
private void SetupMainLightConstants(CommandBuffer cmd, VisibleLight[] lights, int lightIndex, ref ScriptableRenderContext context)
{
Vector4 lightPos, lightColor, lightSpotDir, lightAttenuationParams;
InitializeLightConstants(lights, lightIndex, out lightPos, out lightColor, out lightSpotDir, out lightAttenuationParams);
cmd.SetGlobalVector(PerCameraBuffer._MainLightPosition, lightPos);
cmd.SetGlobalColor(PerCameraBuffer._MainLightColor, lightColor);
cmd.SetGlobalVector(PerCameraBuffer._MainLightSpotDir, lightSpotDir);
cmd.SetGlobalVector(PerCameraBuffer._MainLightAttenuationParams, lightAttenuationParams);
}
private void SetupAdditionalListConstants(CommandBuffer cmd, VisibleLight[] lights, ref LightData lightData, ref ScriptableRenderContext context)
{
int totalLightCount = lights.Length;
int maxLights = Math.Min(kMaxVisibleLights, totalLightCount);
int[] lightIndexMap = m_CullResults.GetLightIndexMap();
int lightIter = 0;
for (int i = 0; i < totalLightCount; ++i)
{
if (i == lightData.mainLightIndex || lightIter >= maxLights)
{
lightIndexMap[i] = -1;
continue;
}
InitializeLightConstants(lights, i, out m_LightPositions[lightIter], out m_LightColors[lightIter], out m_LightSpotDirections[lightIter], out m_LightAttenuations[lightIter]);
lightIndexMap[i] = lightIter;
lightIter++;
}
m_CullResults.SetLightIndexMap(lightIndexMap);
cmd.SetGlobalVector(PerCameraBuffer._AdditionalLightCount, new Vector4 (lightData.additionalPixelLightsCount, lightData.vertexLightsCount, 0.0f, 0.0f));
cmd.SetGlobalVectorArray (PerCameraBuffer._AdditionalLightPosition, m_LightPositions);
cmd.SetGlobalVectorArray (PerCameraBuffer._AdditionalLightColor, m_LightColors);
cmd.SetGlobalVectorArray (PerCameraBuffer._AdditionalLightAttenuationParams, m_LightAttenuations);
cmd.SetGlobalVectorArray (PerCameraBuffer._AdditionalLightSpotDir, m_LightSpotDirections);
}
private void SetupShadowShaderConstants(CommandBuffer cmd, ref ScriptableRenderContext context, ref VisibleLight shadowLight, 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
};
cmd.SetGlobalMatrixArray("_WorldToShadow", shadowMatrices);
cmd.SetGlobalVectorArray("_DirShadowSplitSpheres", m_DirectionalShadowSplitDistances);
cmd.SetGlobalVector("_ShadowLightDirection", new Vector4(-shadowLightDir.x, -shadowLightDir.y, -shadowLightDir.z, 0.0f));
cmd.SetGlobalVector("_ShadowData", new Vector4(0.0f, bias, normalBias, 0.0f));
cmd.SetGlobalFloatArray("_PCFKernel", pcfKernel);
}
private void SetShaderKeywords(CommandBuffer cmd, ref LightData lightData, VisibleLight[] visibleLights)
{
LightweightUtils.SetKeyword(cmd, "_LIGHTWEIGHT_FORCE_LINEAR", m_Asset.ForceLinearRendering);
LightweightUtils.SetKeyword(cmd, "_VERTEX_LIGHTS", lightData.vertexLightsCount > 0);
LightweightUtils.SetKeyword(cmd, "_ATTENUATION_TEXTURE", m_Asset.AttenuationTexture != null);
int mainLightIndex = lightData.mainLightIndex;
LightweightUtils.SetKeyword (cmd, "_MAIN_DIRECTIONAL_LIGHT", mainLightIndex != -1 && visibleLights[mainLightIndex].lightType == LightType.Directional);
LightweightUtils.SetKeyword (cmd, "_MAIN_SPOT_LIGHT", mainLightIndex != -1 && visibleLights[mainLightIndex].lightType == LightType.Spot);
LightweightUtils.SetKeyword (cmd, "_MAIN_POINT_LIGHT", mainLightIndex != -1 && visibleLights[mainLightIndex].lightType == LightType.Point);
LightweightUtils.SetKeyword(cmd, "_ADDITIONAL_PIXEL_LIGHTS", lightData.additionalPixelLightsCount > 0);
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 (m_Asset.AreShadowsEnabled() && lightData.shadowsRendered)
{
int keywordIndex = (int)m_Asset.ShadowSetting - 1;
if (m_Asset.CascadeCount > 1)
keywordIndex += 2;
cmd.EnableShaderKeyword(shadowKeywords[keywordIndex]);
}
}
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_ShadowMapTexture, m_ShadowSettings.shadowAtlasWidth,
m_ShadowSettings.shadowAtlasHeight, kShadowDepthBufferBits, FilterMode.Bilinear, RenderTextureFormat.Depth);
setRenderTargetCommandBuffer.SetRenderTarget(m_ShadowMapRT);
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 BeginForwardRendering(ref ScriptableRenderContext context, RenderingConfiguration renderingConfig)
{
RenderTargetIdentifier colorRT = BuiltinRenderTextureType.CameraTarget;
RenderTargetIdentifier depthRT = BuiltinRenderTextureType.None;
// When postprocess is enabled, msaa is forced to be disabled due to lack of depth resolve.
int msaaSamples = (LightweightUtils.HasFlag(renderingConfig, RenderingConfiguration.Msaa)) ? m_Asset.MSAASampleCount : 1;
var cmd = CommandBufferPool.Get("SetCameraRenderTarget");
if (LightweightUtils.HasFlag(renderingConfig, RenderingConfiguration.IntermediateTexture))
{
int rtWidth = (int)((float)m_CurrCamera.pixelWidth * m_Asset.RenderScale);
int rtHeight = (int)((float)m_CurrCamera.pixelHeight * m_Asset.RenderScale);
if (m_CurrCamera.targetTexture == null)
{
RenderTextureDescriptor rtDesc = new RenderTextureDescriptor();
if (LightweightUtils.HasFlag(renderingConfig, RenderingConfiguration.Stereo))
{
context.StartMultiEye(m_CurrCamera);
rtDesc = XRSettings.eyeTextureDesc;
rtDesc.colorFormat = m_ColorFormat;
rtDesc.msaaSamples = msaaSamples;
cmd.GetTemporaryRT(m_CameraColorTexture, rtDesc, FilterMode.Bilinear);
}
else
{
cmd.GetTemporaryRT(m_CameraColorTexture, rtWidth, rtHeight, kCameraDepthBufferBits,
FilterMode.Bilinear, m_ColorFormat, RenderTextureReadWrite.Default, msaaSamples);
}
colorRT = m_CameraColorRT;
}
if (LightweightUtils.HasFlag(renderingConfig, RenderingConfiguration.PostProcess))
{
cmd.GetTemporaryRT(m_CameraDepthTexture, rtWidth, rtHeight, kCameraDepthBufferBits, FilterMode.Bilinear, RenderTextureFormat.Depth);
depthRT = m_CameraDepthRT;
}
}
SetupRenderTargets(cmd, colorRT, depthRT);
// 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, RenderingConfiguration renderingConfig)
{
// No additional rendering needs to be done if this is an offscren rendering camera
if (m_CurrCamera.targetTexture != null)
return;
var cmd = CommandBufferPool.Get("Blit");
if (LightweightUtils.HasFlag(renderingConfig, RenderingConfiguration.IntermediateTextureArray))
{
cmd.SetRenderTarget(BuiltinRenderTextureType.CameraTarget, 0, CubemapFace.Unknown, -1);
cmd.Blit(m_CameraColorRT, BuiltinRenderTextureType.CurrentActive);
}
else if (LightweightUtils.HasFlag(renderingConfig, RenderingConfiguration.IntermediateTexture))
{
// If PostProcessing is enabled, it is already blitted to CameraTarget.
if (!LightweightUtils.HasFlag(renderingConfig, RenderingConfiguration.PostProcess))
Blit(cmd, renderingConfig, m_CameraColorRT, BuiltinRenderTextureType.CameraTarget);
}
cmd.SetRenderTarget(BuiltinRenderTextureType.CameraTarget);
context.ExecuteCommandBuffer(cmd);
CommandBufferPool.Release(cmd);
if (LightweightUtils.HasFlag(renderingConfig, RenderingConfiguration.Stereo))
{
context.StopMultiEye(m_CurrCamera);
context.StereoEndRender(m_CurrCamera);
}
}
RendererConfiguration GetRendererSettings(ref LightData lightData)
{
RendererConfiguration settings = RendererConfiguration.PerObjectReflectionProbes | RendererConfiguration.PerObjectLightmaps | RendererConfiguration.PerObjectLightProbe;
if (lightData.hasAdditionalLights)
settings |= RendererConfiguration.PerObjectLightIndices8;
return settings;
}
private void SetupRenderTargets(CommandBuffer cmd, RenderTargetIdentifier colorRT, RenderTargetIdentifier depthRT)
{
if (m_IntermediateTextureArray)
{
if (depthRT != BuiltinRenderTextureType.None)
cmd.SetRenderTarget(colorRT, 0, CubemapFace.Unknown, -1);
else
cmd.SetRenderTarget(colorRT, depthRT, 0, CubemapFace.Unknown, -1);
}
else
{
if (depthRT != BuiltinRenderTextureType.None)
cmd.SetRenderTarget(colorRT, depthRT);
else
cmd.SetRenderTarget(colorRT);
}
}
private void RenderPostProcess(ref ScriptableRenderContext renderContext, PostProcessLayer postProcessLayer, bool opaqueOnly)
{
var postProcessCommand = CommandBufferPool.Get("Post Processing");
postProcessCommand.SetGlobalTexture("m_CameraDepthTexture", m_CameraDepthRT);
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;
if (opaqueOnly)
postProcessLayer.RenderOpaqueOnly(m_PostProcessRenderContext);
else
postProcessLayer.Render(m_PostProcessRenderContext);
renderContext.ExecuteCommandBuffer(postProcessCommand);
CommandBufferPool.Release(postProcessCommand);
}
private void Blit(CommandBuffer cmd, RenderingConfiguration renderingConfig, RenderTargetIdentifier sourceRT, RenderTargetIdentifier destRT, Material material = null)
{
if (LightweightUtils.HasFlag(renderingConfig, RenderingConfiguration.DefaultViewport))
{
cmd.Blit(sourceRT, destRT, material);
}
else
{
if (m_BlitQuad == null)
m_BlitQuad = LightweightUtils.CreateQuadMesh(false);
cmd.SetGlobalTexture(m_BlitTexID, sourceRT);
cmd.SetRenderTarget(destRT);
cmd.SetViewport(m_CurrCamera.pixelRect);
cmd.DrawMesh(m_BlitQuad, Matrix4x4.identity, m_BlitMaterial);
}
}
}
}