using System; using System.Collections.Generic; using System.Diagnostics; #if UNITY_EDITOR using UnityEditor.Experimental.Rendering.LightweightPipeline; #endif using UnityEngine.Experimental.GlobalIllumination; using UnityEngine.Rendering; using UnityEngine.Rendering.PostProcessing; using UnityEngine.XR; namespace UnityEngine.Experimental.Rendering.LightweightPipeline { public struct LightData { public int pixelAdditionalLightsCount; public int totalAdditionalLightsCount; public int mainLightIndex; public List visibleLights; public List localLightIndices; } public enum MixedLightingSetup { None = 0, ShadowMask, Subtractive, }; public static class CameraRenderTargetID { // Camera color target. Not used when camera is rendering to backbuffer or camera // is rendering to a texture (offscreen camera) public static int color; // Camera copy color texture. In case there is a single BeforeTransparent postFX // we need use copyColor RT as a work RT. public static int copyColor; // Camera depth target. Only used when post processing, soft particles, or screen space shadows are enabled. public static int depth; // If soft particles are enabled and no depth prepass is performed we need to copy depth. public static int depthCopy; // Camera opaque target. Used for accessing screen contents during transparent pass. public static int opaque; } public class LightweightPipeline : RenderPipeline { private readonly LightweightPipelineAsset m_Asset; private LightweightShadowPass m_ShadowPass; // Maximum amount of visible lights the shader can process. This controls the constant global light buffer size. // It must match the MAX_VISIBLE_LIGHTS in LightweightInput.cginc private static readonly int kMaxVisibleLocalLights = 16; // Lights are culled per-object. In platforms that don't use StructuredBuffer // the engine will set 4 light indices in the following constant unity_4LightIndices0 // Additionally the engine set unity_4LightIndices1 but LWRP doesn't use that. private static readonly int kMaxNonIndexedLocalLights = 4; private static readonly int kMaxVertexLights = 4; private const int kDepthStencilBufferBits = 32; private static readonly float kRenderScaleThreshold = 0.05f; private bool m_IsOffscreenCamera; private Vector4 kDefaultLightPosition = new Vector4(0.0f, 0.0f, 1.0f, 0.0f); private Vector4 kDefaultLightColor = Color.black; private Vector4 kDefaultLightAttenuation = new Vector4(0.0f, 1.0f, 0.0f, 1.0f); private Vector4 kDefaultLightSpotDirection = new Vector4(0.0f, 0.0f, 1.0f, 0.0f); private Vector4 kDefaultLightSpotAttenuation = new Vector4(0.0f, 1.0f, 0.0f, 0.0f); private Vector4[] m_LightPositions = new Vector4[kMaxVisibleLocalLights]; private Vector4[] m_LightColors = new Vector4[kMaxVisibleLocalLights]; private Vector4[] m_LightDistanceAttenuations = new Vector4[kMaxVisibleLocalLights]; private Vector4[] m_LightSpotDirections = new Vector4[kMaxVisibleLocalLights]; private Vector4[] m_LightSpotAttenuations = new Vector4[kMaxVisibleLocalLights]; private Camera m_CurrCamera; private RenderTargetIdentifier m_CurrCameraColorRT; private RenderTargetIdentifier m_ColorRT; private RenderTargetIdentifier m_CopyColorRT; private RenderTargetIdentifier m_DepthRT; private RenderTargetIdentifier m_CopyDepth; private RenderTargetIdentifier m_Color; private float[] m_OpaqueScalerValues = {1.0f, 0.5f, 0.25f, 0.25f}; private float m_RenderScale; private bool m_IntermediateTextureArray; private bool m_RequireDepthTexture; private bool m_RequireCopyColor; private bool m_DepthRenderBuffer; private MixedLightingSetup m_MixedLightingSetup; private ComputeBuffer m_LightIndicesBuffer; private List m_LocalLightIndices; private int m_MaxLocalLightsShadedPerPass; private bool m_UseComputeBuffer; // Pipeline pass names private static readonly ShaderPassName m_DepthPrepass = new ShaderPassName("DepthOnly"); private static readonly ShaderPassName m_LitPassName = new ShaderPassName("LightweightForward"); private static readonly ShaderPassName m_UnlitPassName = new ShaderPassName("SRPDefaultUnlit"); // Renders all shaders without a lightmode tag // Legacy pass names public static readonly ShaderPassName s_AlwaysName = new ShaderPassName("Always"); public static readonly ShaderPassName s_ForwardBaseName = new ShaderPassName("ForwardBase"); public static readonly ShaderPassName s_PrepassBaseName = new ShaderPassName("PrepassBase"); public static readonly ShaderPassName s_VertexName = new ShaderPassName("Vertex"); public static readonly ShaderPassName s_VertexLMRGBMName = new ShaderPassName("VertexLMRGBM"); public static readonly ShaderPassName s_VertexLMName = new ShaderPassName("VertexLM"); public static readonly ShaderPassName[] s_LegacyPassNames = { s_AlwaysName, s_ForwardBaseName, s_PrepassBaseName, s_VertexName, s_VertexLMRGBMName, s_VertexLMName }; private RenderTextureFormat m_ColorFormat; private PostProcessRenderContext m_PostProcessRenderContext; private PostProcessLayer m_CameraPostProcessLayer; private CameraComparer m_CameraComparer = new CameraComparer(); private Material m_BlitMaterial; private Material m_CopyDepthMaterial; private Material m_ErrorMaterial; private Material m_SamplingMaterial; private int m_SampleOffset; private int m_BlitTexID = Shader.PropertyToID("_BlitTex"); private CopyTextureSupport m_CopyTextureSupport; public LightweightPipeline(LightweightPipelineAsset asset) { m_Asset = asset; SetRenderingFeatures(); PerFrameBuffer._GlossyEnvironmentColor = Shader.PropertyToID("_GlossyEnvironmentColor"); PerFrameBuffer._SubtractiveShadowColor = Shader.PropertyToID("_SubtractiveShadowColor"); // Lights are culled per-camera. Therefore we need to reset light buffers on each camera render PerCameraBuffer._MainLightPosition = Shader.PropertyToID("_MainLightPosition"); PerCameraBuffer._MainLightColor = Shader.PropertyToID("_MainLightColor"); PerCameraBuffer._MainLightCookie = Shader.PropertyToID("_MainLightCookie"); PerCameraBuffer._WorldToLight = Shader.PropertyToID("_WorldToLight"); PerCameraBuffer._AdditionalLightCount = Shader.PropertyToID("_AdditionalLightCount"); PerCameraBuffer._AdditionalLightPosition = Shader.PropertyToID("_AdditionalLightPosition"); PerCameraBuffer._AdditionalLightColor = Shader.PropertyToID("_AdditionalLightColor"); PerCameraBuffer._AdditionalLightDistanceAttenuation = Shader.PropertyToID("_AdditionalLightDistanceAttenuation"); PerCameraBuffer._AdditionalLightSpotDir = Shader.PropertyToID("_AdditionalLightSpotDir"); PerCameraBuffer._AdditionalLightSpotAttenuation = Shader.PropertyToID("_AdditionalLightSpotAttenuation"); PerCameraBuffer._LightIndexBuffer = Shader.PropertyToID("_LightIndexBuffer"); PerCameraBuffer._ScaledScreenParams = Shader.PropertyToID("_ScaledScreenParams"); CameraRenderTargetID.color = Shader.PropertyToID("_CameraColorRT"); CameraRenderTargetID.copyColor = Shader.PropertyToID("_CameraCopyColorRT"); CameraRenderTargetID.depth = Shader.PropertyToID("_CameraDepthTexture"); CameraRenderTargetID.depthCopy = Shader.PropertyToID("_CameraCopyDepthTexture"); CameraRenderTargetID.opaque = Shader.PropertyToID("_CameraOpaqueTexture"); m_SampleOffset = Shader.PropertyToID("_SampleOffset"); m_ColorRT = new RenderTargetIdentifier(CameraRenderTargetID.color); m_CopyColorRT = new RenderTargetIdentifier(CameraRenderTargetID.copyColor); m_DepthRT = new RenderTargetIdentifier(CameraRenderTargetID.depth); m_CopyDepth = new RenderTargetIdentifier(CameraRenderTargetID.depthCopy); m_PostProcessRenderContext = new PostProcessRenderContext(); m_CopyTextureSupport = SystemInfo.copyTextureSupport; // TODO: Profile performance of using ComputeBuffer on mobiles that support it vs // fixed buffer size if (Application.isMobilePlatform || Application.platform == RuntimePlatform.WebGLPlayer) m_UseComputeBuffer = false; else m_UseComputeBuffer = true; m_LightIndicesBuffer = null; m_MaxLocalLightsShadedPerPass = m_UseComputeBuffer ? kMaxVisibleLocalLights : kMaxNonIndexedLocalLights; m_LocalLightIndices = new List(m_MaxLocalLightsShadedPerPass); m_ShadowPass = new LightweightShadowPass(m_Asset, m_MaxLocalLightsShadedPerPass); // 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_BlitMaterial = CoreUtils.CreateEngineMaterial(m_Asset.BlitShader); m_CopyDepthMaterial = CoreUtils.CreateEngineMaterial(m_Asset.CopyDepthShader); m_SamplingMaterial = CoreUtils.CreateEngineMaterial(m_Asset.SamplingShader); m_ErrorMaterial = CoreUtils.CreateEngineMaterial("Hidden/InternalErrorShader"); } public override void Dispose() { base.Dispose(); Shader.globalRenderPipeline = ""; SupportedRenderingFeatures.active = new SupportedRenderingFeatures(); CoreUtils.Destroy(m_ErrorMaterial); CoreUtils.Destroy(m_CopyDepthMaterial); CoreUtils.Destroy(m_BlitMaterial); CoreUtils.Destroy(m_SamplingMaterial); #if UNITY_EDITOR SceneViewDrawMode.ResetDrawMode(); #endif if (m_LightIndicesBuffer != null) { m_LightIndicesBuffer.Release(); m_LightIndicesBuffer = null; } } private void SetRenderingFeatures() { #if UNITY_EDITOR SupportedRenderingFeatures.active = new SupportedRenderingFeatures() { reflectionProbeSupportFlags = SupportedRenderingFeatures.ReflectionProbeSupportFlags.None, defaultMixedLightingMode = SupportedRenderingFeatures.LightmapMixedBakeMode.Subtractive, supportedMixedLightingModes = SupportedRenderingFeatures.LightmapMixedBakeMode.Subtractive, supportedLightmapBakeTypes = LightmapBakeType.Baked | LightmapBakeType.Mixed, supportedLightmapsModes = LightmapsMode.CombinedDirectional | LightmapsMode.NonDirectional, rendererSupportsLightProbeProxyVolumes = false, rendererSupportsMotionVectors = false, rendererSupportsReceiveShadows = true, rendererSupportsReflectionProbes = true }; SceneViewDrawMode.SetupDrawMode(); #endif } CullResults m_CullResults; public override void Render(ScriptableRenderContext context, Camera[] cameras) { base.Render(context, cameras); RenderPipeline.BeginFrameRendering(cameras); GraphicsSettings.lightsUseLinearIntensity = true; SetupPerFrameShaderConstants(); // Sort cameras array by camera depth Array.Sort(cameras, m_CameraComparer); foreach (Camera camera in cameras) { m_CurrCamera = camera; bool sceneViewCamera = m_CurrCamera.cameraType == CameraType.SceneView; bool stereoEnabled = IsStereoEnabled(m_CurrCamera); // Disregard variations around kRenderScaleThreshold. m_RenderScale = (Mathf.Abs(1.0f - m_Asset.RenderScale) < kRenderScaleThreshold) ? 1.0f : m_Asset.RenderScale; // XR has it's own scaling mechanism. m_RenderScale = (m_CurrCamera.cameraType == CameraType.Game && !stereoEnabled) ? m_RenderScale : 1.0f; m_IsOffscreenCamera = m_CurrCamera.targetTexture != null && m_CurrCamera.cameraType != CameraType.SceneView; SetupPerCameraShaderConstants(); RenderPipeline.BeginCameraRendering(m_CurrCamera); ScriptableCullingParameters cullingParameters; if (!CullResults.GetCullingParameters(m_CurrCamera, stereoEnabled, out cullingParameters)) continue; var cmd = CommandBufferPool.Get(""); cullingParameters.shadowDistance = Mathf.Min(m_ShadowPass.RenderingDistance, m_CurrCamera.farClipPlane); #if UNITY_EDITOR // Emit scene view UI if (sceneViewCamera) ScriptableRenderContext.EmitWorldGeometryForSceneView(camera); #endif CullResults.Cull(ref cullingParameters, context, ref m_CullResults); List visibleLights = m_CullResults.visibleLights; LightData lightData; InitializeLightData(visibleLights, out lightData); bool screenspaceShadows = m_ShadowPass.Execute(ref m_CullResults, ref lightData, ref context); FrameRenderingConfiguration frameRenderingConfiguration; SetupFrameRenderingConfiguration(out frameRenderingConfiguration, screenspaceShadows, stereoEnabled, sceneViewCamera); SetupIntermediateResources(frameRenderingConfiguration, screenspaceShadows, ref context); // SetupCameraProperties does the following: // Setup Camera RenderTarget and Viewport // VR Camera Setup and SINGLE_PASS_STEREO props // Setup camera view, proj and their inv matrices. // Setup properties: _WorldSpaceCameraPos, _ProjectionParams, _ScreenParams, _ZBufferParams, unity_OrthoParams // Setup camera world clip planes props // setup HDR keyword // Setup global time properties (_Time, _SinTime, _CosTime) context.SetupCameraProperties(m_CurrCamera, stereoEnabled); if (LightweightUtils.HasFlag(frameRenderingConfiguration, FrameRenderingConfiguration.DepthPrePass)) DepthPass(ref context, frameRenderingConfiguration); if (screenspaceShadows) m_ShadowPass.CollectShadows(m_CurrCamera, frameRenderingConfiguration, ref context); ForwardPass(frameRenderingConfiguration, ref lightData, ref context); cmd.name = "After Camera Render"; #if UNITY_EDITOR if (sceneViewCamera) CopyTexture(cmd, CameraRenderTargetID.depth, BuiltinRenderTextureType.CameraTarget, m_CopyDepthMaterial, true); #endif context.Submit(); cmd.ReleaseTemporaryRT(CameraRenderTargetID.depthCopy); cmd.ReleaseTemporaryRT(CameraRenderTargetID.depth); cmd.ReleaseTemporaryRT(CameraRenderTargetID.color); cmd.ReleaseTemporaryRT(CameraRenderTargetID.copyColor); cmd.ReleaseTemporaryRT(CameraRenderTargetID.opaque); context.ExecuteCommandBuffer(cmd); CommandBufferPool.Release(cmd); m_ShadowPass.Dispose(cmd); context.Submit(); } } private void DepthPass(ref ScriptableRenderContext context, FrameRenderingConfiguration frameRenderingConfiguration) { CommandBuffer cmd = CommandBufferPool.Get("Depth Prepass"); SetRenderTarget(cmd, m_DepthRT, ClearFlag.Depth); context.ExecuteCommandBuffer(cmd); CommandBufferPool.Release(cmd); var opaqueDrawSettings = new DrawRendererSettings(m_CurrCamera, m_DepthPrepass); opaqueDrawSettings.sorting.flags = SortFlags.CommonOpaque; var opaqueFilterSettings = new FilterRenderersSettings(true) { renderQueueRange = RenderQueueRange.opaque }; LightweightUtils.StartStereoRendering(m_CurrCamera, ref context, frameRenderingConfiguration); context.DrawRenderers(m_CullResults.visibleRenderers, ref opaqueDrawSettings, opaqueFilterSettings); LightweightUtils.StopStereoRendering(m_CurrCamera, ref context, frameRenderingConfiguration); } private void OpaqueTexturePass(ref ScriptableRenderContext context, FrameRenderingConfiguration frameRenderingConfiguration) { var opaqueScaler = m_OpaqueScalerValues[(int)m_Asset.OpaqueDownsampling]; RenderTextureDescriptor opaqueDesc = CreateRTDesc(frameRenderingConfiguration, opaqueScaler); CommandBuffer cmd = CommandBufferPool.Get("Opaque Copy"); cmd.GetTemporaryRT(CameraRenderTargetID.opaque, opaqueDesc, m_Asset.OpaqueDownsampling == Downsampling.None ? FilterMode.Point : FilterMode.Bilinear); switch(m_Asset.OpaqueDownsampling) { case Downsampling.None: cmd.Blit(m_CurrCameraColorRT, CameraRenderTargetID.opaque); break; case Downsampling._2xBilinear: cmd.Blit(m_CurrCameraColorRT, CameraRenderTargetID.opaque); break; case Downsampling._4xBox: m_SamplingMaterial.SetFloat(m_SampleOffset, 2); cmd.Blit(m_CurrCameraColorRT, CameraRenderTargetID.opaque, m_SamplingMaterial, 0); break; case Downsampling._4xBilinear: cmd.Blit(m_CurrCameraColorRT, CameraRenderTargetID.opaque); break; } SetRenderTarget(cmd, m_CurrCameraColorRT, m_DepthRT); context.ExecuteCommandBuffer(cmd); CommandBufferPool.Release(cmd); } private void ForwardPass(FrameRenderingConfiguration frameRenderingConfiguration, ref LightData lightData, ref ScriptableRenderContext context) { SetupShaderConstants(ref lightData, ref context); RendererConfiguration rendererSettings = GetRendererSettings(ref lightData); BeginForwardRendering(ref context, frameRenderingConfiguration); RenderOpaques(ref context, rendererSettings); AfterOpaque(ref context, frameRenderingConfiguration); RenderTransparents(ref context, rendererSettings); AfterTransparent(ref context, frameRenderingConfiguration); EndForwardRendering(ref context, frameRenderingConfiguration); } private void RenderOpaques(ref ScriptableRenderContext context, RendererConfiguration settings) { var opaqueDrawSettings = new DrawRendererSettings(m_CurrCamera, m_LitPassName); opaqueDrawSettings.SetShaderPassName(1, m_UnlitPassName); opaqueDrawSettings.sorting.flags = SortFlags.CommonOpaque; opaqueDrawSettings.rendererConfiguration = settings; var opaqueFilterSettings = new FilterRenderersSettings(true) { renderQueueRange = RenderQueueRange.opaque }; context.DrawRenderers(m_CullResults.visibleRenderers, ref opaqueDrawSettings, opaqueFilterSettings); // Render objects that did not match any shader pass with error shader RenderObjectsWithError(ref context, opaqueFilterSettings, SortFlags.None); if (m_CurrCamera.clearFlags == CameraClearFlags.Skybox) context.DrawSkybox(m_CurrCamera); } private void AfterOpaque(ref ScriptableRenderContext context, FrameRenderingConfiguration config) { if (m_RequireDepthTexture) { CommandBuffer cmd = CommandBufferPool.Get("After Opaque"); cmd.SetGlobalTexture(CameraRenderTargetID.depth, m_DepthRT); bool setRenderTarget = false; RenderTargetIdentifier depthRT = m_DepthRT; // TODO: There's currently an issue in the PostFX stack that has a one frame delay when an effect is enabled/disabled // when an effect is disabled, HasOpaqueOnlyEffects returns true in the first frame, however inside render the effect // state is update, causing RenderPostProcess here to not blit to FinalColorRT. Until the next frame the RT will have garbage. if (LightweightUtils.HasFlag(config, FrameRenderingConfiguration.BeforeTransparentPostProcess)) { // When only have one effect in the stack we blit to a work RT then blit it back to active color RT. // This seems like an extra blit but it saves us a depth copy/blit which has some corner cases like msaa depth resolve. if (m_RequireCopyColor) { RenderPostProcess(cmd, m_CurrCameraColorRT, m_CopyColorRT, true); cmd.Blit(m_CopyColorRT, m_CurrCameraColorRT); } else RenderPostProcess(cmd, m_CurrCameraColorRT, m_CurrCameraColorRT, true); setRenderTarget = true; SetRenderTarget(cmd, m_CurrCameraColorRT, m_DepthRT); } if (LightweightUtils.HasFlag(config, FrameRenderingConfiguration.DepthCopy)) { CopyTexture(cmd, m_DepthRT, m_CopyDepth, m_CopyDepthMaterial); depthRT = m_CopyDepth; setRenderTarget = true; } if (setRenderTarget) SetRenderTarget(cmd, m_CurrCameraColorRT, depthRT); context.ExecuteCommandBuffer(cmd); CommandBufferPool.Release(cmd); } if(m_Asset.RequireOpaqueTexture) { OpaqueTexturePass(ref context, config); } } private void RenderTransparents(ref ScriptableRenderContext context, RendererConfiguration config) { var transparentSettings = new DrawRendererSettings(m_CurrCamera, m_LitPassName); transparentSettings.SetShaderPassName(1, m_UnlitPassName); transparentSettings.sorting.flags = SortFlags.CommonTransparent; transparentSettings.rendererConfiguration = config; var transparentFilterSettings = new FilterRenderersSettings(true) { renderQueueRange = RenderQueueRange.transparent }; context.DrawRenderers(m_CullResults.visibleRenderers, ref transparentSettings, transparentFilterSettings); // Render objects that did not match any shader pass with error shader RenderObjectsWithError(ref context, transparentFilterSettings, SortFlags.None); } private void AfterTransparent(ref ScriptableRenderContext context, FrameRenderingConfiguration config) { if (!LightweightUtils.HasFlag(config, FrameRenderingConfiguration.PostProcess)) return; CommandBuffer cmd = CommandBufferPool.Get("After Transparent"); RenderPostProcess(cmd, m_CurrCameraColorRT, BuiltinRenderTextureType.CameraTarget, false); context.ExecuteCommandBuffer(cmd); CommandBufferPool.Release(cmd); } [Conditional("DEVELOPMENT_BUILD"), Conditional("UNITY_EDITOR")] private void RenderObjectsWithError(ref ScriptableRenderContext context, FilterRenderersSettings filterSettings, SortFlags sortFlags) { if (m_ErrorMaterial != null) { DrawRendererSettings errorSettings = new DrawRendererSettings(m_CurrCamera, s_LegacyPassNames[0]); for (int i = 1; i < s_LegacyPassNames.Length; ++i) errorSettings.SetShaderPassName(i, s_LegacyPassNames[i]); errorSettings.sorting.flags = sortFlags; errorSettings.rendererConfiguration = RendererConfiguration.None; errorSettings.SetOverrideMaterial(m_ErrorMaterial, 0); context.DrawRenderers(m_CullResults.visibleRenderers, ref errorSettings, filterSettings); } } private void SetupFrameRenderingConfiguration(out FrameRenderingConfiguration configuration, bool screenspaceShadows, bool stereoEnabled, bool sceneViewCamera) { configuration = (stereoEnabled) ? FrameRenderingConfiguration.Stereo : FrameRenderingConfiguration.None; if (stereoEnabled && XRSettings.eyeTextureDesc.dimension == TextureDimension.Tex2DArray) m_IntermediateTextureArray = true; else m_IntermediateTextureArray = false; bool hdrEnabled = m_Asset.SupportsHDR && m_CurrCamera.allowHDR; bool defaultRenderScale = Mathf.Approximately(GetRenderScale(), 1.0f); bool intermediateTexture = m_CurrCamera.targetTexture != null || m_CurrCamera.cameraType == CameraType.SceneView || !defaultRenderScale || hdrEnabled; m_ColorFormat = hdrEnabled ? RenderTextureFormat.DefaultHDR : RenderTextureFormat.Default; m_RequireCopyColor = false; m_DepthRenderBuffer = false; m_CameraPostProcessLayer = m_CurrCamera.GetComponent(); bool msaaEnabled = m_CurrCamera.allowMSAA && m_Asset.MSAASampleCount > 1 && (m_CurrCamera.targetTexture == null || m_CurrCamera.targetTexture.antiAliasing > 1); // TODO: PostProcessing and SoftParticles are currently not support for VR bool postProcessEnabled = m_CameraPostProcessLayer != null && m_CameraPostProcessLayer.enabled && !stereoEnabled; m_RequireDepthTexture = m_Asset.RequireDepthTexture && !stereoEnabled; if (postProcessEnabled) { m_RequireDepthTexture = true; intermediateTexture = true; configuration |= FrameRenderingConfiguration.PostProcess; if (m_CameraPostProcessLayer.HasOpaqueOnlyEffects(m_PostProcessRenderContext)) { configuration |= FrameRenderingConfiguration.BeforeTransparentPostProcess; if (m_CameraPostProcessLayer.sortedBundles[PostProcessEvent.BeforeTransparent].Count == 1) m_RequireCopyColor = true; } } if (sceneViewCamera) m_RequireDepthTexture = true; m_RequireDepthTexture = m_RequireDepthTexture || screenspaceShadows; if (msaaEnabled) { configuration |= FrameRenderingConfiguration.Msaa; intermediateTexture = intermediateTexture || !LightweightUtils.PlatformSupportsMSAABackBuffer(); } if (m_RequireDepthTexture) { // If msaa is enabled we don't use a depth renderbuffer as we might not have support to Texture2DMS to resolve depth. // Instead we use a depth prepass and whenever depth is needed we use the 1 sample depth from prepass. // Screen space shadows require depth before opaque shading. if (!msaaEnabled && !screenspaceShadows) { bool supportsDepthCopy = m_CopyTextureSupport != CopyTextureSupport.None && m_Asset.CopyDepthShader.isSupported; m_DepthRenderBuffer = true; intermediateTexture = true; // If requiring a camera depth texture we need separate depth as it reads/write to depth at same time // Post process doesn't need the copy if (!m_Asset.RequireDepthTexture && postProcessEnabled) configuration |= (supportsDepthCopy) ? FrameRenderingConfiguration.DepthCopy : FrameRenderingConfiguration.DepthPrePass; } else { configuration |= FrameRenderingConfiguration.DepthPrePass; } } Rect cameraRect = m_CurrCamera.rect; if (!(Math.Abs(cameraRect.x) > 0.0f || Math.Abs(cameraRect.y) > 0.0f || Math.Abs(cameraRect.width) < 1.0f || Math.Abs(cameraRect.height) < 1.0f)) configuration |= FrameRenderingConfiguration.DefaultViewport; else intermediateTexture = true; if (intermediateTexture) configuration |= FrameRenderingConfiguration.IntermediateTexture; } private RenderTextureDescriptor CreateRTDesc(FrameRenderingConfiguration renderingConfig, float scaler = 1.0f) { RenderTextureDescriptor desc; if (LightweightUtils.HasFlag(renderingConfig, FrameRenderingConfiguration.Stereo)) desc = XRSettings.eyeTextureDesc; else desc = new RenderTextureDescriptor(m_CurrCamera.pixelWidth, m_CurrCamera.pixelHeight); float renderScale = GetRenderScale(); desc.width = (int)((float)desc.width * renderScale * scaler); desc.height = (int)((float)desc.height * renderScale * scaler); return desc; } private void SetupIntermediateResources(FrameRenderingConfiguration renderingConfig, bool shadows, ref ScriptableRenderContext context) { CommandBuffer cmd = CommandBufferPool.Get("Setup Intermediate Resources"); int msaaSamples = (m_IsOffscreenCamera) ? Math.Min(m_CurrCamera.targetTexture.antiAliasing, m_Asset.MSAASampleCount) : m_Asset.MSAASampleCount; msaaSamples = (LightweightUtils.HasFlag(renderingConfig, FrameRenderingConfiguration.Msaa)) ? msaaSamples : 1; m_CurrCameraColorRT = BuiltinRenderTextureType.CameraTarget; if (LightweightUtils.HasFlag(renderingConfig, FrameRenderingConfiguration.IntermediateTexture) || m_RequireDepthTexture) SetupIntermediateRenderTextures(cmd, renderingConfig, shadows, msaaSamples); context.ExecuteCommandBuffer(cmd); CommandBufferPool.Release(cmd); } private void SetupIntermediateRenderTextures(CommandBuffer cmd, FrameRenderingConfiguration renderingConfig, bool shadows, int msaaSamples) { RenderTextureDescriptor baseDesc = CreateRTDesc(renderingConfig); if (m_RequireDepthTexture) { var depthRTDesc = baseDesc; depthRTDesc.colorFormat = RenderTextureFormat.Depth; depthRTDesc.depthBufferBits = kDepthStencilBufferBits; cmd.GetTemporaryRT(CameraRenderTargetID.depth, depthRTDesc, FilterMode.Bilinear); if (LightweightUtils.HasFlag(renderingConfig, FrameRenderingConfiguration.DepthCopy)) cmd.GetTemporaryRT(CameraRenderTargetID.depthCopy, depthRTDesc, FilterMode.Bilinear); m_ShadowPass.InitializeResources(cmd, baseDesc); } var colorRTDesc = baseDesc; colorRTDesc.colorFormat = m_ColorFormat; colorRTDesc.depthBufferBits = kDepthStencilBufferBits; // TODO: does the color RT always need depth? colorRTDesc.sRGB = true; colorRTDesc.msaaSamples = msaaSamples; colorRTDesc.enableRandomWrite = false; // When offscreen camera current rendertarget is CameraTarget if (!m_IsOffscreenCamera) { cmd.GetTemporaryRT(CameraRenderTargetID.color, colorRTDesc, FilterMode.Bilinear); m_CurrCameraColorRT = m_ColorRT; } // When BeforeTransparent PostFX is enabled and only one effect is in the stack we need to create a temp // color RT to blit the effect. if (m_RequireCopyColor) cmd.GetTemporaryRT(CameraRenderTargetID.copyColor, colorRTDesc, FilterMode.Point); } private void SetupShaderConstants(ref LightData lightData, ref ScriptableRenderContext context) { CommandBuffer cmd = CommandBufferPool.Get("SetupShaderConstants"); SetupShaderLightConstants(cmd, ref lightData); SetShaderKeywords(cmd, ref lightData); context.ExecuteCommandBuffer(cmd); CommandBufferPool.Release(cmd); } private void InitializeLightData(List visibleLights, out LightData lightData) { m_LocalLightIndices.Clear(); for (int i = 0; i < visibleLights.Count; ++i) { if (visibleLights[i].lightType != LightType.Directional) m_LocalLightIndices.Add(i); if (m_LocalLightIndices.Count >= m_MaxLocalLightsShadedPerPass) break; } // Clear to default all light constant data for (int i = 0; i < kMaxVisibleLocalLights; ++i) InitializeLightConstants(visibleLights, -1, out m_LightPositions[i], out m_LightColors[i], out m_LightDistanceAttenuations[i], out m_LightSpotDirections[i], out m_LightSpotAttenuations[i]); int visibleLightsCount = Math.Min(visibleLights.Count, m_Asset.MaxPixelLights); lightData.mainLightIndex = GetMainLight(visibleLights); // If we have a main light we don't shade it in the per-object light loop. We also remove it from the per-object cull list int mainLightPresent = (lightData.mainLightIndex >= 0) ? 1 : 0; int additionalPixelLightsCount = Math.Min(visibleLightsCount - mainLightPresent, m_MaxLocalLightsShadedPerPass); int vertexLightCount = (m_Asset.SupportsVertexLight) ? Math.Min(visibleLights.Count, m_MaxLocalLightsShadedPerPass) - additionalPixelLightsCount : 0; vertexLightCount = Math.Min(vertexLightCount, kMaxVertexLights); lightData.pixelAdditionalLightsCount = additionalPixelLightsCount; lightData.totalAdditionalLightsCount = additionalPixelLightsCount + vertexLightCount; lightData.visibleLights = visibleLights; lightData.localLightIndices = m_LocalLightIndices; m_MixedLightingSetup = MixedLightingSetup.None; if (lightData.totalAdditionalLightsCount > 0) { int[] perObjectLightIndexMap = m_CullResults.GetLightIndexMap(); int directionalLightCount = 0; // Disable all directional lights from the perobject light indices for (int i = 0; i < visibleLights.Count; ++i) { VisibleLight light = visibleLights[i]; if (light.lightType == LightType.Directional) perObjectLightIndexMap[i] = -1; else perObjectLightIndexMap[i] -= directionalLightCount; } m_CullResults.SetLightIndexMap(perObjectLightIndexMap); // if not using a compute buffer, engine will set indices in 2 vec4 constants // unity_4LightIndices0 and unity_4LightIndices1 if (m_UseComputeBuffer) { int lightIndicesCount = m_CullResults.GetLightIndicesCount(); if (lightIndicesCount > 0) { if (m_LightIndicesBuffer == null) { m_LightIndicesBuffer = new ComputeBuffer(lightIndicesCount, sizeof(int)); } else if (m_LightIndicesBuffer.count < lightIndicesCount) { m_LightIndicesBuffer.Release(); m_LightIndicesBuffer = new ComputeBuffer(lightIndicesCount, sizeof(int)); } m_CullResults.FillLightIndices(m_LightIndicesBuffer); } } } } // Main Light is always a directional light private int GetMainLight(List visibleLights) { int totalVisibleLights = visibleLights.Count; if (totalVisibleLights == 0 || m_Asset.MaxPixelLights == 0) return -1; for (int i = 0; i < totalVisibleLights; ++i) { VisibleLight currLight = visibleLights[i]; // Particle system lights have the light property as null. We sort lights so all particles lights // come last. Therefore, if first light is particle light then all lights are particle lights. // In this case we either have no main light or already found it. if (currLight.light == null) break; // In case no shadow light is present we will return the brightest directional light if (currLight.lightType == LightType.Directional) return i; } return -1; } private void InitializeLightConstants(List lights, int lightIndex, out Vector4 lightPos, out Vector4 lightColor, out Vector4 lightDistanceAttenuation, out Vector4 lightSpotDir, out Vector4 lightSpotAttenuation) { lightPos = kDefaultLightPosition; lightColor = kDefaultLightColor; lightDistanceAttenuation = kDefaultLightSpotAttenuation; lightSpotDir = kDefaultLightSpotDirection; lightSpotAttenuation = kDefaultLightAttenuation; // When no lights are visible, main light will be set to -1. // In this case we initialize it to default values and return if (lightIndex < 0) return; VisibleLight lightData = lights[lightIndex]; if (lightData.lightType == LightType.Directional) { Vector4 dir = -lightData.localToWorld.GetColumn(2); lightPos = new Vector4(dir.x, dir.y, dir.z, 0.0f); } else { Vector4 pos = lightData.localToWorld.GetColumn(3); lightPos = new Vector4(pos.x, pos.y, pos.z, 1.0f); } // VisibleLight.finalColor already returns color in active color space lightColor = lightData.finalColor; // Directional Light attenuation is initialize so distance attenuation always be 1.0 if (lightData.lightType != LightType.Directional) { // Light attenuation in lightweight matches the unity vanilla one. // attenuation = 1.0 / 1.0 + distanceToLightSqr * quadraticAttenuation // then a smooth factor is applied to linearly fade attenuation to light range // the attenuation smooth factor starts having effect at 80% of light range // smoothFactor = (lightRangeSqr - distanceToLightSqr) / (lightRangeSqr - fadeStartDistanceSqr) // We rewrite smoothFactor to be able to pre compute the constant terms below and apply the smooth factor // with one MAD instruction // smoothFactor = distanceSqr * (1.0 / (fadeDistanceSqr - lightRangeSqr)) + (-lightRangeSqr / (fadeDistanceSqr - lightRangeSqr) // distanceSqr * oneOverFadeRangeSqr + lightRangeSqrOverFadeRangeSqr float lightRangeSqr = lightData.range * lightData.range; float fadeStartDistanceSqr = 0.8f * 0.8f * lightRangeSqr; float fadeRangeSqr = (fadeStartDistanceSqr - lightRangeSqr); float oneOverFadeRangeSqr = 1.0f / fadeRangeSqr; float lightRangeSqrOverFadeRangeSqr = -lightRangeSqr / fadeRangeSqr; float quadAtten = 25.0f / lightRangeSqr; lightDistanceAttenuation = new Vector4(quadAtten, oneOverFadeRangeSqr, lightRangeSqrOverFadeRangeSqr, 1.0f); } if (lightData.lightType == LightType.Spot) { Vector4 dir = lightData.localToWorld.GetColumn(2); lightSpotDir = new Vector4(-dir.x, -dir.y, -dir.z, 0.0f); // Spot Attenuation with a linear falloff can be defined as // (SdotL - cosOuterAngle) / (cosInnerAngle - cosOuterAngle) // This can be rewritten as // invAngleRange = 1.0 / (cosInnerAngle - cosOuterAngle) // SdotL * invAngleRange + (-cosOuterAngle * invAngleRange) // If we precompute the terms in a MAD instruction float cosOuterAngle = Mathf.Cos(Mathf.Deg2Rad * lightData.spotAngle * 0.5f); // We neeed to do a null check for particle lights // This should be changed in the future // Particle lights will use an inline function float cosInnerAngle; if (lightData.light != null) cosInnerAngle = Mathf.Cos(LightmapperUtils.ExtractInnerCone(lightData.light) * 0.5f); else cosInnerAngle = Mathf.Cos((2.0f * Mathf.Atan(Mathf.Tan(lightData.spotAngle * 0.5f * Mathf.Deg2Rad) * (64.0f - 18.0f) / 64.0f)) * 0.5f); float smoothAngleRange = Mathf.Max(0.001f, cosInnerAngle - cosOuterAngle); float invAngleRange = 1.0f / smoothAngleRange; float add = -cosOuterAngle * invAngleRange; lightSpotAttenuation = new Vector4(invAngleRange, add, 0.0f); } Light light = lightData.light; // TODO: Add support to shadow mask if (light != null && light.bakingOutput.mixedLightingMode == MixedLightingMode.Subtractive && light.bakingOutput.lightmapBakeType == LightmapBakeType.Mixed) { if (m_MixedLightingSetup == MixedLightingSetup.None && lightData.light.shadows != LightShadows.None) { m_MixedLightingSetup = MixedLightingSetup.Subtractive; lightDistanceAttenuation.w = 0.0f; } } } private void SetupPerFrameShaderConstants() { // When glossy reflections are OFF in the shader we set a constant color to use as indirect specular SphericalHarmonicsL2 ambientSH = RenderSettings.ambientProbe; Color linearGlossyEnvColor = new Color(ambientSH[0, 0], ambientSH[1, 0], ambientSH[2, 0]) * RenderSettings.reflectionIntensity; Color glossyEnvColor = CoreUtils.ConvertLinearToActiveColorSpace(linearGlossyEnvColor); Shader.SetGlobalVector(PerFrameBuffer._GlossyEnvironmentColor, glossyEnvColor); // Used when subtractive mode is selected Shader.SetGlobalVector(PerFrameBuffer._SubtractiveShadowColor, CoreUtils.ConvertSRGBToActiveColorSpace(RenderSettings.subtractiveShadowColor)); } private void SetupPerCameraShaderConstants() { float cameraWidth = GetScaledCameraWidth(m_CurrCamera); float cameraHeight = GetScaledCameraHeight(m_CurrCamera); Shader.SetGlobalVector(PerCameraBuffer._ScaledScreenParams, new Vector4(cameraWidth, cameraHeight, 1.0f + 1.0f / cameraWidth, 1.0f + 1.0f / cameraHeight)); } private void SetupShaderLightConstants(CommandBuffer cmd, ref LightData lightData) { // 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, ref lightData); SetupAdditionalListConstants(cmd, ref lightData); } private void SetupMainLightConstants(CommandBuffer cmd, ref LightData lightData) { Vector4 lightPos, lightColor, lightDistanceAttenuation, lightSpotDir, lightSpotAttenuation; List lights = lightData.visibleLights; InitializeLightConstants(lightData.visibleLights, lightData.mainLightIndex, out lightPos, out lightColor, out lightDistanceAttenuation, out lightSpotDir, out lightSpotAttenuation); if (lightData.mainLightIndex >= 0) { VisibleLight mainLight = lights[lightData.mainLightIndex]; Light mainLightRef = mainLight.light; if (LightweightUtils.IsSupportedCookieType(mainLight.lightType) && mainLightRef.cookie != null) { Matrix4x4 lightCookieMatrix; LightweightUtils.GetLightCookieMatrix(mainLight, out lightCookieMatrix); cmd.SetGlobalTexture(PerCameraBuffer._MainLightCookie, mainLightRef.cookie); cmd.SetGlobalMatrix(PerCameraBuffer._WorldToLight, lightCookieMatrix); } } cmd.SetGlobalVector(PerCameraBuffer._MainLightPosition, new Vector4(lightPos.x, lightPos.y, lightPos.z, lightDistanceAttenuation.w)); cmd.SetGlobalVector(PerCameraBuffer._MainLightColor, lightColor); } private void SetupAdditionalListConstants(CommandBuffer cmd, ref LightData lightData) { List lights = lightData.visibleLights; if (lightData.totalAdditionalLightsCount > 0) { int localLightsCount = 0; for (int i = 0; i < lights.Count && localLightsCount < kMaxVisibleLocalLights; ++i) { VisibleLight light = lights[i]; if (light.lightType != LightType.Directional) { InitializeLightConstants(lights, i, out m_LightPositions[localLightsCount], out m_LightColors[localLightsCount], out m_LightDistanceAttenuations[localLightsCount], out m_LightSpotDirections[localLightsCount], out m_LightSpotAttenuations[localLightsCount]); localLightsCount++; } } cmd.SetGlobalVector(PerCameraBuffer._AdditionalLightCount, new Vector4(lightData.pixelAdditionalLightsCount, lightData.totalAdditionalLightsCount, 0.0f, 0.0f)); // if not using a compute buffer, engine will set indices in 2 vec4 constants // unity_4LightIndices0 and unity_4LightIndices1 if (m_UseComputeBuffer) cmd.SetGlobalBuffer("_LightIndexBuffer", m_LightIndicesBuffer); } else { cmd.SetGlobalVector(PerCameraBuffer._AdditionalLightCount, Vector4.zero); } cmd.SetGlobalVectorArray(PerCameraBuffer._AdditionalLightPosition, m_LightPositions); cmd.SetGlobalVectorArray(PerCameraBuffer._AdditionalLightColor, m_LightColors); cmd.SetGlobalVectorArray(PerCameraBuffer._AdditionalLightDistanceAttenuation, m_LightDistanceAttenuations); cmd.SetGlobalVectorArray(PerCameraBuffer._AdditionalLightSpotDir, m_LightSpotDirections); cmd.SetGlobalVectorArray(PerCameraBuffer._AdditionalLightSpotAttenuation, m_LightSpotAttenuations); } private void SetShaderKeywords(CommandBuffer cmd, ref LightData lightData) { List visibleLights = lightData.visibleLights; int mainLightIndex = lightData.mainLightIndex; int vertexLightsCount = lightData.totalAdditionalLightsCount - lightData.pixelAdditionalLightsCount; bool shadowsEnabled = m_ShadowPass.HasDirectionalShadowmap || m_ShadowPass.HasLocalLightsShadowmap; CoreUtils.SetKeyword(cmd, "_SHADOWS_ENABLED", shadowsEnabled); // TODO: Currently we are shading same keyword for directional and local lights // When we have ability to strip keywords we can use the one below instead //CoreUtils.SetKeyword(cmd, "_SHADOWS_ENABLED", m_ShadowPass.HasDirectionalShadowmap); //CoreUtils.SetKeyword(cmd, "_LOCAL_SHADOWS_ENABLED", m_ShadowPass.HasLocalLightsShadowmap); //TIM: Not used in shader for V1 to reduce keywords 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_RequireDepthTexture && m_Asset.RequireSoftParticles); } private void BeginForwardRendering(ref ScriptableRenderContext context, FrameRenderingConfiguration renderingConfig) { RenderTargetIdentifier colorRT = BuiltinRenderTextureType.CameraTarget; RenderTargetIdentifier depthRT = BuiltinRenderTextureType.None; LightweightUtils.StartStereoRendering(m_CurrCamera, ref context, renderingConfig); CommandBuffer cmd = CommandBufferPool.Get("SetCameraRenderTarget"); bool intermediateTexture = LightweightUtils.HasFlag(renderingConfig, FrameRenderingConfiguration.IntermediateTexture); if (intermediateTexture) { if (!m_IsOffscreenCamera) colorRT = m_CurrCameraColorRT; if (m_RequireDepthTexture) depthRT = m_DepthRT; } ClearFlag clearFlag = ClearFlag.None; CameraClearFlags cameraClearFlags = m_CurrCamera.clearFlags; if (cameraClearFlags != CameraClearFlags.Nothing) { clearFlag |= ClearFlag.Depth; if (cameraClearFlags == CameraClearFlags.Color || cameraClearFlags == CameraClearFlags.Skybox) clearFlag |= ClearFlag.Color; } SetRenderTarget(cmd, colorRT, depthRT, clearFlag); // If rendering to an intermediate RT we resolve viewport on blit due to offset not being supported // while rendering to a RT. if (!intermediateTexture && !LightweightUtils.HasFlag(renderingConfig, FrameRenderingConfiguration.DefaultViewport)) cmd.SetViewport(m_CurrCamera.pixelRect); context.ExecuteCommandBuffer(cmd); CommandBufferPool.Release(cmd); } private void EndForwardRendering(ref ScriptableRenderContext context, FrameRenderingConfiguration renderingConfig) { // No additional rendering needs to be done if this is an off screen rendering camera if (m_IsOffscreenCamera) return; var cmd = CommandBufferPool.Get("Blit"); if (m_IntermediateTextureArray) { cmd.Blit(m_CurrCameraColorRT, BuiltinRenderTextureType.CameraTarget); } else if (LightweightUtils.HasFlag(renderingConfig, FrameRenderingConfiguration.IntermediateTexture)) { Material blitMaterial = m_BlitMaterial; if (LightweightUtils.HasFlag(renderingConfig, FrameRenderingConfiguration.Stereo)) blitMaterial = null; // If PostProcessing is enabled, it is already blit to CameraTarget. if (!LightweightUtils.HasFlag(renderingConfig, FrameRenderingConfiguration.PostProcess)) Blit(cmd, renderingConfig, m_CurrCameraColorRT, BuiltinRenderTextureType.CameraTarget, blitMaterial); } SetRenderTarget(cmd, BuiltinRenderTextureType.CameraTarget); context.ExecuteCommandBuffer(cmd); CommandBufferPool.Release(cmd); if (LightweightUtils.HasFlag(renderingConfig, FrameRenderingConfiguration.Stereo)) { context.StopMultiEye(m_CurrCamera); context.StereoEndRender(m_CurrCamera); } } private bool IsStereoEnabled(Camera camera) { bool isSceneViewCamera = camera.cameraType == CameraType.SceneView; return XRSettings.isDeviceActive && !isSceneViewCamera && (camera.stereoTargetEye == StereoTargetEyeMask.Both); } private float GetRenderScale() { return m_RenderScale; } private float GetScaledCameraWidth(Camera camera) { return (float)camera.pixelWidth * GetRenderScale(); } private float GetScaledCameraHeight(Camera camera) { return (float)camera.pixelHeight * GetRenderScale(); } private RendererConfiguration GetRendererSettings(ref LightData lightData) { RendererConfiguration settings = RendererConfiguration.PerObjectReflectionProbes | RendererConfiguration.PerObjectLightmaps | RendererConfiguration.PerObjectLightProbe; if (lightData.totalAdditionalLightsCount > 0) { if (m_UseComputeBuffer) settings |= RendererConfiguration.ProvideLightIndices; else settings |= RendererConfiguration.PerObjectLightIndices8; } return settings; } private void RenderPostProcess(CommandBuffer cmd, RenderTargetIdentifier source, RenderTargetIdentifier dest, bool opaqueOnly) { m_PostProcessRenderContext.Reset(); m_PostProcessRenderContext.camera = m_CurrCamera; m_PostProcessRenderContext.source = source; m_PostProcessRenderContext.sourceFormat = m_ColorFormat; m_PostProcessRenderContext.destination = dest; m_PostProcessRenderContext.command = cmd; m_PostProcessRenderContext.flip = m_CurrCamera.targetTexture == null; if (opaqueOnly) { m_CameraPostProcessLayer.RenderOpaqueOnly(m_PostProcessRenderContext); } else m_CameraPostProcessLayer.Render(m_PostProcessRenderContext); } private void SetRenderTarget(CommandBuffer cmd, RenderTargetIdentifier colorRT, ClearFlag clearFlag = ClearFlag.None) { int depthSlice = (m_IntermediateTextureArray) ? -1 : 0; CoreUtils.SetRenderTarget(cmd, colorRT, clearFlag, CoreUtils.ConvertSRGBToActiveColorSpace(m_CurrCamera.backgroundColor), 0, CubemapFace.Unknown, depthSlice); } private void SetRenderTarget(CommandBuffer cmd, RenderTargetIdentifier colorRT, RenderTargetIdentifier depthRT, ClearFlag clearFlag = ClearFlag.None) { if (depthRT == BuiltinRenderTextureType.None || !m_DepthRenderBuffer) { SetRenderTarget(cmd, colorRT, clearFlag); return; } int depthSlice = (m_IntermediateTextureArray) ? -1 : 0; CoreUtils.SetRenderTarget(cmd, colorRT, depthRT, clearFlag, CoreUtils.ConvertSRGBToActiveColorSpace(m_CurrCamera.backgroundColor), 0, CubemapFace.Unknown, depthSlice); } private void Blit(CommandBuffer cmd, FrameRenderingConfiguration renderingConfig, RenderTargetIdentifier sourceRT, RenderTargetIdentifier destRT, Material material = null) { cmd.SetGlobalTexture(m_BlitTexID, sourceRT); if (LightweightUtils.HasFlag(renderingConfig, FrameRenderingConfiguration.DefaultViewport)) { cmd.Blit(sourceRT, destRT, material); } else { // Viewport doesn't work when rendering to an RT // We have to manually blit by rendering a fullscreen quad SetRenderTarget(cmd, destRT); cmd.SetViewProjectionMatrices(Matrix4x4.identity, Matrix4x4.identity); cmd.SetViewport(m_CurrCamera.pixelRect); LightweightUtils.DrawFullScreen(cmd, m_BlitMaterial); } } private void CopyTexture(CommandBuffer cmd, RenderTargetIdentifier sourceRT, RenderTargetIdentifier destRT, Material copyMaterial, bool forceBlit = false) { if (m_CopyTextureSupport != CopyTextureSupport.None && !forceBlit) cmd.CopyTexture(sourceRT, destRT); else cmd.Blit(sourceRT, destRT, copyMaterial); } } }