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

382 行
17 KiB

using System;
using System.Collections.Generic;
using UnityEngine.Rendering;
using UnityEngine.Rendering.PostProcessing;
using UnityEngine.XR;
namespace UnityEngine.Experimental.Rendering.LightweightPipeline
{
public enum RenderPassHandles
{
DepthPrepass,
DirectionalShadows,
LocalShadows,
ScreenSpaceShadowResolve,
ForwardLit,
Count,
}
public enum MaterialHandles
{
Error,
DepthCopy,
Sampling,
Blit,
ScrenSpaceShadow,
Count,
}
public static class RenderTargetHandles
{
public static int Color;
public static int DepthAttachment;
public static int DepthTexture;
public static int OpaqueColor;
public static int DirectionalShadowmap;
public static int LocalShadowmap;
public static int ScreenSpaceShadowmap;
}
public class LightweightForwardRenderer
{
// 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.
const int k_MaxConstantLocalLights = 4;
// LWRP uses a fixed constant buffer to hold light data. This must match the value of
// MAX_VISIBLE_LIGHTS 16 in Input.hlsl
const int k_MaxVisibleLocalLights = 16;
const int k_MaxVertexLights = 4;
public int maxSupportedLocalLightsPerPass
{
get
{
return useComputeBufferForPerObjectLightIndices ? k_MaxVisibleLocalLights : k_MaxConstantLocalLights;
}
}
// TODO: Profile performance of using ComputeBuffer on mobiles that support it
public bool useComputeBufferForPerObjectLightIndices
{
get
{
return SystemInfo.supportsComputeShaders &&
!Application.isMobilePlatform && Application.platform != RuntimePlatform.WebGLPlayer;
}
}
public int maxVisibleLocalLights { get { return k_MaxVisibleLocalLights; } }
public int maxSupportedVertexLights { get { return k_MaxVertexLights; } }
public PostProcessRenderContext postProcessRenderContext { get; private set; }
public ComputeBuffer perObjectLightIndices { get; private set; }
public FilterRenderersSettings opaqueFilterSettings { get; private set; }
public FilterRenderersSettings transparentFilterSettings { get; private set; }
Dictionary<int, RenderTargetIdentifier> m_ResourceMap = new Dictionary<int, RenderTargetIdentifier>();
List<ScriptableRenderPass> m_ActiveShadowQueue = new List<ScriptableRenderPass>();
List<ScriptableRenderPass> m_ActiveRenderPassQueue = new List<ScriptableRenderPass>();
Material[] m_Materials;
ScriptableRenderPass[] m_RenderPassSet = new ScriptableRenderPass[(int)RenderPassHandles.Count];
public LightweightForwardRenderer(LightweightPipelineAsset pipelineAsset)
{
// RenderTexture format depends on camera and pipeline (HDR, non HDR, etc)
// Samples (MSAA) depend on camera and pipeline
RegisterSurface("_CameraColorTexture", out RenderTargetHandles.Color);
RegisterSurface("_CameraDepthAttachment", out RenderTargetHandles.DepthAttachment);
RegisterSurface("_CameraDepthTexture", out RenderTargetHandles.DepthTexture);
RegisterSurface("_CameraOpaqueTexture", out RenderTargetHandles.OpaqueColor);
RegisterSurface("_DirectionalShadowmapTexture", out RenderTargetHandles.DirectionalShadowmap);
RegisterSurface("_LocalShadowmapTexture", out RenderTargetHandles.LocalShadowmap);
RegisterSurface("_ScreenSpaceShadowMapTexture", out RenderTargetHandles.ScreenSpaceShadowmap);
m_Materials = new Material[(int)MaterialHandles.Count]
{
CoreUtils.CreateEngineMaterial("Hidden/InternalErrorShader"),
CoreUtils.CreateEngineMaterial(pipelineAsset.copyDepthShader),
CoreUtils.CreateEngineMaterial(pipelineAsset.samplingShader),
CoreUtils.CreateEngineMaterial(pipelineAsset.blitShader),
CoreUtils.CreateEngineMaterial(pipelineAsset.screenSpaceShadowShader),
};
m_RenderPassSet = new ScriptableRenderPass[(int)RenderPassHandles.Count]
{
new DepthOnlyPass(this),
new DirectionalShadowsPass(this),
new LocalShadowsPass(this),
new ScreenSpaceShadowResolvePass(this),
new ForwardLitPass(this),
};
postProcessRenderContext = new PostProcessRenderContext();
opaqueFilterSettings = new FilterRenderersSettings(true)
{
renderQueueRange = RenderQueueRange.opaque,
};
transparentFilterSettings = new FilterRenderersSettings(true)
{
renderQueueRange = RenderQueueRange.transparent,
};
}
public void Dispose()
{
if (perObjectLightIndices != null)
{
perObjectLightIndices.Release();
perObjectLightIndices = null;
}
for (int i = 0; i < m_Materials.Length; ++i)
CoreUtils.Destroy(m_Materials[i]);
}
public RenderTextureDescriptor CreateRTDesc(ref CameraData cameraData, float scaler = 1.0f)
{
Camera camera = cameraData.camera;
RenderTextureDescriptor desc;
if (cameraData.isStereoEnabled)
desc = XRSettings.eyeTextureDesc;
else
desc = new RenderTextureDescriptor(camera.pixelWidth, camera.pixelHeight);
float renderScale = cameraData.renderScale;
desc.colorFormat = cameraData.isHdrEnabled ? RenderTextureFormat.DefaultHDR :
RenderTextureFormat.Default;
desc.enableRandomWrite = false;
desc.width = (int)((float)desc.width * renderScale * scaler);
desc.height = (int)((float)desc.height * renderScale * scaler);
return desc;
}
public void Setup(ref ScriptableRenderContext context, ref CullResults cullResults, ref RenderingData renderingData)
{
Clear();
SetupPerObjectLightIndices(ref cullResults, ref renderingData.lightData);
RenderTextureDescriptor baseDescriptor = CreateRTDesc(ref renderingData.cameraData);
bool requiresCameraDepth = renderingData.cameraData.requiresDepthTexture;
bool requiresDepthPrepass = renderingData.shadowData.requiresScreenSpaceShadowResolve ||
renderingData.cameraData.isSceneViewCamera || (requiresCameraDepth && !CanCopyDepth(ref renderingData.cameraData));
// For now VR requires a depth prepass until we figure out how to properly resolve texture2DMS in stereo
requiresDepthPrepass |= renderingData.cameraData.isStereoEnabled;
CommandBuffer cmd = CommandBufferPool.Get("Setup Rendering");
if (requiresDepthPrepass)
EnqueuePass(cmd, RenderPassHandles.DepthPrepass, baseDescriptor, null, RenderTargetHandles.DepthTexture);
if (renderingData.shadowData.renderDirectionalShadows)
{
EnqueuePass(cmd, RenderPassHandles.DirectionalShadows, baseDescriptor);
if (renderingData.shadowData.requiresScreenSpaceShadowResolve)
EnqueuePass(cmd, RenderPassHandles.ScreenSpaceShadowResolve, baseDescriptor, new[] {RenderTargetHandles.ScreenSpaceShadowmap});
}
if (renderingData.shadowData.renderLocalShadows)
EnqueuePass(cmd, RenderPassHandles.LocalShadows, baseDescriptor);
bool requiresDepthAttachment = requiresCameraDepth && !requiresDepthPrepass;
bool requiresColorAttachment = RequiresColorAttachment(ref renderingData.cameraData, baseDescriptor) || requiresDepthAttachment;
int[] colorHandles = (requiresColorAttachment) ? new[] {RenderTargetHandles.Color} : null;
int depthHandle = (requiresColorAttachment) ? RenderTargetHandles.DepthAttachment : -1;
EnqueuePass(cmd, RenderPassHandles.ForwardLit, baseDescriptor, colorHandles, depthHandle, renderingData.cameraData.msaaSamples);
context.ExecuteCommandBuffer(cmd);
CommandBufferPool.Release(cmd);
}
public void Execute(ref ScriptableRenderContext context, ref CullResults cullResults, ref RenderingData renderingData)
{
// TODO: The reason we have to separate passes into two queues is because shadows require different camera
// context. We need to take a look at approaches to effectively share shadow between cameras, then we
// can move this out
for (int i = 0; i < m_ActiveShadowQueue.Count; ++i)
m_ActiveShadowQueue[i].Execute(ref context, ref cullResults, ref renderingData);
// 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(renderingData.cameraData.camera, renderingData.cameraData.isStereoEnabled);
for (int i = 0; i < m_ActiveRenderPassQueue.Count; ++i)
m_ActiveRenderPassQueue[i].Execute(ref context, ref cullResults, ref renderingData);
#if UNITY_EDITOR
if (renderingData.cameraData.isSceneViewCamera)
{
// Restore Render target for additional editor rendering.
// Note: Scene view camera always perform depth prepass
CommandBuffer cmd = CommandBufferPool.Get("Copy Depth to Camera");
CoreUtils.SetRenderTarget(cmd, BuiltinRenderTextureType.CameraTarget);
cmd.EnableShaderKeyword(LightweightKeywords.DepthNoMsaa);
cmd.DisableShaderKeyword(LightweightKeywords.DepthMsaa2);
cmd.DisableShaderKeyword(LightweightKeywords.DepthMsaa4);
cmd.Blit(GetSurface(RenderTargetHandles.DepthTexture), BuiltinRenderTextureType.CameraTarget, GetMaterial(MaterialHandles.DepthCopy));
context.ExecuteCommandBuffer(cmd);
CommandBufferPool.Release(cmd);
}
#endif
DisposePasses(ref context);
}
public RenderTargetIdentifier GetSurface(int handle)
{
RenderTargetIdentifier renderTargetID;
if (handle < 0 || !m_ResourceMap.TryGetValue(handle, out renderTargetID))
{
Debug.LogError(string.Format("Handle {0} has not any surface registered to it.", handle));
return new RenderTargetIdentifier();
}
return renderTargetID;
}
public Material GetMaterial(MaterialHandles handle)
{
int handleID = (int)handle;
if (handleID >= m_Materials.Length)
{
Debug.LogError(string.Format("Material {0} is not registered.",
Enum.GetName(typeof(MaterialHandles), handleID)));
return null;
}
return m_Materials[handleID];
}
ScriptableRenderPass GetPass(RenderPassHandles handle)
{
int handleID = (int)handle;
if (handleID >= m_RenderPassSet.Length)
{
Debug.LogError(string.Format("Render Pass {0} is not registered.",
Enum.GetName(typeof(RenderPassHandles), handleID)));
return null;
}
return m_RenderPassSet[handleID];
}
void Clear()
{
m_ActiveShadowQueue.Clear();
m_ActiveRenderPassQueue.Clear();
}
void RegisterSurface(string shaderProperty, out int handle)
{
handle = Shader.PropertyToID(shaderProperty);
m_ResourceMap.Add(handle, new RenderTargetIdentifier(handle));
}
void EnqueuePass(CommandBuffer cmd, RenderPassHandles passHandle, RenderTextureDescriptor baseDescriptor,
int[] colorAttachmentHandles = null, int depthAttachmentHandle = -1, int samples = 1)
{
ScriptableRenderPass pass = GetPass(passHandle);
pass.Setup(cmd, baseDescriptor, colorAttachmentHandles, depthAttachmentHandle, samples);
if (passHandle == RenderPassHandles.DirectionalShadows || passHandle == RenderPassHandles.LocalShadows)
m_ActiveShadowQueue.Add(pass);
else
m_ActiveRenderPassQueue.Add(pass);
}
bool RequiresColorAttachment(ref CameraData cameraData, RenderTextureDescriptor baseDescriptor)
{
bool isScaledRender = !Mathf.Approximately(cameraData.renderScale, 1.0f);
bool isTargetTexture2DArray = baseDescriptor.dimension == TextureDimension.Tex2DArray;
return cameraData.isSceneViewCamera || isScaledRender || cameraData.isHdrEnabled ||
cameraData.postProcessEnabled || cameraData.requiresOpaqueTexture || isTargetTexture2DArray;
}
bool CanCopyDepth(ref CameraData cameraData)
{
bool msaaEnabledForCamera = cameraData.msaaSamples > 1;
bool supportsTextureCopy = SystemInfo.copyTextureSupport != CopyTextureSupport.None;
bool supportsDepthTarget = SystemInfo.SupportsRenderTextureFormat(RenderTextureFormat.Depth);
bool supportsDepthCopy = !msaaEnabledForCamera && (supportsDepthTarget || supportsTextureCopy);
// TODO: We don't have support to highp Texture2DMS currently and this breaks depth precision.
// currently disabling it until shader changes kick in.
//bool msaaDepthResolve = msaaEnabledForCamera && SystemInfo.supportsMultisampledTextures != 0;
bool msaaDepthResolve = false;
return supportsDepthCopy || msaaDepthResolve;
}
void DisposePasses(ref ScriptableRenderContext context)
{
CommandBuffer cmd = CommandBufferPool.Get("Release Resources");
for (int i = 0; i < m_ActiveShadowQueue.Count; ++i)
m_ActiveShadowQueue[i].Dispose(cmd);
for (int i = 0; i < m_ActiveRenderPassQueue.Count; ++i)
m_ActiveRenderPassQueue[i].Dispose(cmd);
context.ExecuteCommandBuffer(cmd);
CommandBufferPool.Release(cmd);
}
void SetupPerObjectLightIndices(ref CullResults cullResults, ref LightData lightData)
{
if (lightData.totalAdditionalLightsCount == 0)
return;
List<VisibleLight> visibleLights = lightData.visibleLights;
int[] perObjectLightIndexMap = cullResults.GetLightIndexMap();
int directionalLightCount = 0;
// Disable all directional lights from the perobject light indices
// Pipeline handles them globally
for (int i = 0; i < visibleLights.Count; ++i)
{
VisibleLight light = visibleLights[i];
if (light.lightType == LightType.Directional)
{
perObjectLightIndexMap[i] = -1;
++directionalLightCount;
}
else
perObjectLightIndexMap[i] -= directionalLightCount;
}
cullResults.SetLightIndexMap(perObjectLightIndexMap);
// if not using a compute buffer, engine will set indices in 2 vec4 constants
// unity_4LightIndices0 and unity_4LightIndices1
if (useComputeBufferForPerObjectLightIndices)
{
int lightIndicesCount = cullResults.GetLightIndicesCount();
if (lightIndicesCount > 0)
{
if (perObjectLightIndices == null)
{
perObjectLightIndices = new ComputeBuffer(lightIndicesCount, sizeof(int));
}
else if (perObjectLightIndices.count < lightIndicesCount)
{
perObjectLightIndices.Release();
perObjectLightIndices = new ComputeBuffer(lightIndicesCount, sizeof(int));
}
cullResults.FillLightIndices(perObjectLightIndices);
}
}
}
}
}