您最多选择25个主题
主题必须以中文或者字母或数字开头,可以包含连字符 (-),并且长度不得超过35个字符
325 行
13 KiB
325 行
13 KiB
using System;
|
|
using System.Collections.Generic;
|
|
using System.Diagnostics;
|
|
using UnityEngine.Rendering;
|
|
using UnityEngine.Rendering.PostProcessing;
|
|
|
|
namespace UnityEngine.Experimental.Rendering.LightweightPipeline
|
|
{
|
|
public sealed class ScriptableRenderer
|
|
{
|
|
// 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 static bool useComputeBufferForPerObjectLightIndices
|
|
{
|
|
get
|
|
{
|
|
// TODO: Graphics Emulation are breaking StructuredBuffers disabling it for now until
|
|
// we have a fix for it
|
|
return false;
|
|
// return SystemInfo.supportsComputeShaders &&
|
|
// SystemInfo.graphicsDeviceType != GraphicsDeviceType.OpenGLCore &&
|
|
// !Application.isMobilePlatform &&
|
|
// Application.platform != RuntimePlatform.WebGLPlayer;
|
|
}
|
|
}
|
|
|
|
public int maxVisibleLocalLights { get { return k_MaxVisibleLocalLights; } }
|
|
|
|
public int maxSupportedVertexLights { get { return k_MaxVertexLights; } }
|
|
|
|
public PostProcessRenderContext postProcessingContext { get; private set; }
|
|
|
|
public ComputeBuffer perObjectLightIndices { get; private set; }
|
|
|
|
static Mesh s_FullscreenMesh = null;
|
|
static Mesh fullscreenMesh
|
|
{
|
|
get
|
|
{
|
|
if (s_FullscreenMesh != null)
|
|
return s_FullscreenMesh;
|
|
|
|
float topV = 1.0f;
|
|
float bottomV = 0.0f;
|
|
|
|
Mesh mesh = new Mesh { name = "Fullscreen Quad" };
|
|
mesh.SetVertices(new List<Vector3>
|
|
{
|
|
new Vector3(-1.0f, -1.0f, 0.0f),
|
|
new Vector3(-1.0f, 1.0f, 0.0f),
|
|
new Vector3(1.0f, -1.0f, 0.0f),
|
|
new Vector3(1.0f, 1.0f, 0.0f)
|
|
});
|
|
|
|
mesh.SetUVs(0, new List<Vector2>
|
|
{
|
|
new Vector2(0.0f, bottomV),
|
|
new Vector2(0.0f, topV),
|
|
new Vector2(1.0f, bottomV),
|
|
new Vector2(1.0f, topV)
|
|
});
|
|
|
|
mesh.SetIndices(new[] { 0, 1, 2, 2, 1, 3 }, MeshTopology.Triangles, 0, false);
|
|
mesh.UploadMeshData(true);
|
|
return mesh;
|
|
}
|
|
}
|
|
|
|
List<ScriptableRenderPass> m_ActiveRenderPassQueue = new List<ScriptableRenderPass>();
|
|
|
|
List<ShaderPassName> m_LegacyShaderPassNames = new List<ShaderPassName>()
|
|
{
|
|
new ShaderPassName("Always"),
|
|
new ShaderPassName("ForwardBase"),
|
|
new ShaderPassName("PrepassBase"),
|
|
new ShaderPassName("Vertex"),
|
|
new ShaderPassName("VertexLMRGBM"),
|
|
new ShaderPassName("VertexLM"),
|
|
};
|
|
|
|
const string k_ReleaseResourcesTag = "Release Resources";
|
|
readonly Material[] m_Materials;
|
|
|
|
public ScriptableRenderer(LightweightPipelineAsset pipelineAsset)
|
|
{
|
|
m_Materials = new[]
|
|
{
|
|
CoreUtils.CreateEngineMaterial("Hidden/InternalErrorShader"),
|
|
CoreUtils.CreateEngineMaterial(pipelineAsset.copyDepthShader),
|
|
CoreUtils.CreateEngineMaterial(pipelineAsset.samplingShader),
|
|
CoreUtils.CreateEngineMaterial(pipelineAsset.blitShader),
|
|
CoreUtils.CreateEngineMaterial(pipelineAsset.screenSpaceShadowShader),
|
|
};
|
|
|
|
postProcessingContext = new PostProcessRenderContext();
|
|
}
|
|
|
|
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 void Execute(ScriptableRenderContext context, ref RenderingData renderingData)
|
|
{
|
|
for (int i = 0; i < m_ActiveRenderPassQueue.Count; ++i)
|
|
m_ActiveRenderPassQueue[i].Execute(this, context, ref renderingData);
|
|
|
|
DisposePasses(ref context);
|
|
}
|
|
|
|
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];
|
|
}
|
|
|
|
public void Clear()
|
|
{
|
|
m_ActiveRenderPassQueue.Clear();
|
|
}
|
|
|
|
public void EnqueuePass(ScriptableRenderPass pass)
|
|
{
|
|
m_ActiveRenderPassQueue.Add(pass);
|
|
}
|
|
|
|
public 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);
|
|
}
|
|
}
|
|
}
|
|
|
|
public void RenderPostProcess(CommandBuffer cmd, ref CameraData cameraData, RenderTextureFormat colorFormat, RenderTargetIdentifier source, RenderTargetIdentifier dest, bool opaqueOnly)
|
|
{
|
|
Camera camera = cameraData.camera;
|
|
postProcessingContext.Reset();
|
|
postProcessingContext.camera = camera;
|
|
postProcessingContext.source = source;
|
|
postProcessingContext.sourceFormat = colorFormat;
|
|
postProcessingContext.destination = dest;
|
|
postProcessingContext.command = cmd;
|
|
postProcessingContext.flip = !cameraData.isStereoEnabled && camera.targetTexture == null;
|
|
|
|
if (opaqueOnly)
|
|
cameraData.postProcessLayer.RenderOpaqueOnly(postProcessingContext);
|
|
else
|
|
cameraData.postProcessLayer.Render(postProcessingContext);
|
|
}
|
|
|
|
[Conditional("DEVELOPMENT_BUILD"), Conditional("UNITY_EDITOR")]
|
|
public void RenderObjectsWithError(ScriptableRenderContext context, ref CullResults cullResults, Camera camera, FilterRenderersSettings filterSettings, SortFlags sortFlags)
|
|
{
|
|
Material errorMaterial = GetMaterial(MaterialHandles.Error);
|
|
if (errorMaterial != null)
|
|
{
|
|
DrawRendererSettings errorSettings = new DrawRendererSettings(camera, m_LegacyShaderPassNames[0]);
|
|
for (int i = 1; i < m_LegacyShaderPassNames.Count; ++i)
|
|
errorSettings.SetShaderPassName(i, m_LegacyShaderPassNames[i]);
|
|
|
|
errorSettings.sorting.flags = sortFlags;
|
|
errorSettings.rendererConfiguration = RendererConfiguration.None;
|
|
errorSettings.SetOverrideMaterial(errorMaterial, 0);
|
|
context.DrawRenderers(cullResults.visibleRenderers, ref errorSettings, filterSettings);
|
|
}
|
|
}
|
|
|
|
public static RenderTextureDescriptor CreateRenderTextureDescriptor(ref CameraData cameraData, float scaler = 1.0f)
|
|
{
|
|
Camera camera = cameraData.camera;
|
|
RenderTextureDescriptor desc;
|
|
float renderScale = cameraData.renderScale;
|
|
|
|
if (cameraData.isStereoEnabled)
|
|
{
|
|
return XRGraphicsConfig.eyeTextureDesc;
|
|
}
|
|
else
|
|
{
|
|
desc = new RenderTextureDescriptor(camera.pixelWidth, camera.pixelHeight);
|
|
}
|
|
desc.colorFormat = cameraData.isHdrEnabled ? RenderTextureFormat.DefaultHDR :
|
|
RenderTextureFormat.Default;
|
|
desc.enableRandomWrite = false;
|
|
desc.sRGB = true;
|
|
desc.width = (int)((float)desc.width * renderScale * scaler);
|
|
desc.height = (int)((float)desc.height * renderScale * scaler);
|
|
return desc;
|
|
}
|
|
|
|
public static bool RequiresIntermediateColorTexture(ref CameraData cameraData, RenderTextureDescriptor baseDescriptor)
|
|
{
|
|
if (cameraData.isOffscreenRender)
|
|
return false;
|
|
|
|
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 || !cameraData.isDefaultViewport;
|
|
}
|
|
|
|
public static ClearFlag GetCameraClearFlag(Camera camera)
|
|
{
|
|
ClearFlag clearFlag = ClearFlag.None;
|
|
CameraClearFlags cameraClearFlags = camera.clearFlags;
|
|
if (cameraClearFlags != CameraClearFlags.Nothing)
|
|
{
|
|
clearFlag |= ClearFlag.Depth;
|
|
if (cameraClearFlags == CameraClearFlags.Color || cameraClearFlags == CameraClearFlags.Skybox)
|
|
clearFlag |= ClearFlag.Color;
|
|
}
|
|
|
|
return clearFlag;
|
|
}
|
|
|
|
public static RendererConfiguration GetRendererConfiguration(int localLightsCount)
|
|
{
|
|
RendererConfiguration configuration = RendererConfiguration.PerObjectReflectionProbes | RendererConfiguration.PerObjectLightmaps | RendererConfiguration.PerObjectLightProbe;
|
|
if (localLightsCount > 0)
|
|
{
|
|
if (useComputeBufferForPerObjectLightIndices)
|
|
configuration |= RendererConfiguration.ProvideLightIndices;
|
|
else
|
|
configuration |= RendererConfiguration.PerObjectLightIndices8;
|
|
}
|
|
|
|
return configuration;
|
|
}
|
|
|
|
public static void RenderFullscreenQuad(CommandBuffer cmd, Material material, MaterialPropertyBlock properties = null, int shaderPassId = 0)
|
|
{
|
|
cmd.DrawMesh(fullscreenMesh, Matrix4x4.identity, material, 0, shaderPassId, properties);
|
|
}
|
|
|
|
public static void CopyTexture(CommandBuffer cmd, RenderTargetIdentifier source, RenderTargetIdentifier dest, Material material)
|
|
{
|
|
// TODO: In order to issue a copyTexture we need to also check if source and dest have same size
|
|
//if (SystemInfo.copyTextureSupport != CopyTextureSupport.None)
|
|
// cmd.CopyTexture(source, dest);
|
|
//else
|
|
cmd.Blit(source, dest, material);
|
|
}
|
|
|
|
void DisposePasses(ref ScriptableRenderContext context)
|
|
{
|
|
CommandBuffer cmd = CommandBufferPool.Get(k_ReleaseResourcesTag);
|
|
|
|
for (int i = 0; i < m_ActiveRenderPassQueue.Count; ++i)
|
|
m_ActiveRenderPassQueue[i].FrameCleanup(cmd);
|
|
|
|
context.ExecuteCommandBuffer(cmd);
|
|
CommandBufferPool.Release(cmd);
|
|
}
|
|
}
|
|
}
|