【Unity】可编程渲染管线——LWRP轻量级渲染管线剖析
前言
Unity2018中引入了可编程渲染管线(Scriptable Render Pipeline,简称SRP),是一种在Unity中通过C#脚本配置和执行渲染的方式。至2018.1版本,Unity中除了默认渲染管线,还提供了轻量级渲染管线(Lightweight Pipeline)和高清晰渲染管线(HD Pipleline)二个渲染管线。当然也支持自定义渲染管线。与高清晰渲染管线相比,轻量级渲染管线的开发已经比较成熟。
这篇文章主要是分析轻量级渲染管线的C#代码都做了哪些工作。
可编程渲染管线能做什么
为了解决仅有一个默认渲染管线,造成的可配置型、可发现性、灵活性等问题。Unity在管线设计的概念上做了转移,决定在C++端保留一个非常小的渲染内核,让C#端可以通过API暴露出更多的选择性,也就是说,Unity会提供一系列的C# API以及内置渲染管线的C#实现;这样一来,一方面可以保证C++端的代码都能严格通过各种白盒测试,另一方面C#端代码就可以在实际项目中调整,有任何问题也可以方便地进行调试。
新的管线对用户而言主要是C# 端的API以及由这些API编写的一系列定制化的内置渲染管线。而在内部实现上,引擎C++端会负责多线程实现性能关键的部分,如上图所示,而C#端负责更高层的渲染指令调度。[1]
可编程渲染管线的使用层设计
用户可以直接使用开源的内置管线,或者在内置管线的基础上进行修改,甚至直接编写定制化的管线。具体使用上渲染管线在工程中会生成特定的Asset,如下图所示,这个Asset序列化了这条管线的一些公共设置变量,并负责在运行时创建实际的渲染上下文;当这个Asset的设置变量在运行时发生变化,引擎会销毁当前上下文然后重新创建管线(这个操作在现有固定管线中无法做到)。
简单的渲染管线示例
可编程渲染管线的使用层设计中,最少需要两个类,一个是渲染管线资源,一个是渲染管线实例。
下面是一个不透明渲染管线的C#代码查看详细:
using UnityEngine;
using UnityEngine.Experimental.Rendering;
using UnityEngine.Rendering;
[ExecuteInEditMode]
public class OpaqueAssetPipe : RenderPipelineAsset
{
#if UNITY_EDITOR[UnityEditor.MenuItem("SRP-Demo/02 - Create Opaque Asset Pipeline")]static void CreateBasicAssetPipeline(){var instance = ScriptableObject.CreateInstance();UnityEditor.AssetDatabase.CreateAsset(instance, "Assets/SRP-Demo/2-OpaqueAssetPipe/OpaqueAssetPipe.asset");}
#endifprotected override IRenderPipeline InternalCreatePipeline(){return new OpaqueAssetPipeInstance();}
}public class OpaqueAssetPipeInstance : RenderPipeline
{public override void Render(ScriptableRenderContext context, Camera[] cameras){base.Render(context, cameras);foreach (var camera in cameras){ScriptableCullingParameters cullingParams;if (!CullResults.GetCullingParameters(camera, out cullingParams))continue;CullResults cull = CullResults.Cull(ref cullingParams, context);context.SetupCameraProperties(camera);var cmd = new CommandBuffer();cmd.ClearRenderTarget(true, false, Color.black);context.ExecuteCommandBuffer(cmd);cmd.Release();var settings = new DrawRendererSettings(camera, new ShaderPassName("BasicPass"));settings.sorting.flags = SortFlags.CommonOpaque;var filterSettings = new FilterRenderersSettings(true) { renderQueueRange = RenderQueueRange.opaque };context.DrawRenderers(cull.visibleRenderers, ref settings, filterSettings);context.DrawSkybox(camera);context.Submit();}}
}
这里定义了一个继承自RenderPipelineAsset的类,通过这个类创建一个渲染管线实例:继承自RenderPipeline的类。
渲染管线实例中的Render方法是渲染入口点,它需要两个参数,渲染上下文以及一个需要渲染的摄像机列表。
在Render方法中依次进行了剔除、绘制、过滤等操作,通过渲染命令缓冲向渲染上下文发送命令来实现绘制。
轻量级渲染管线
以下内容来自Unity官方在GitHub中的项目,本文将在代码中通过注释来解释渲染管线的功能。
渲染管线资源
渲染管线资源是项目中实际使用的资源,它包含一系列的可调整设置,并通过这些设置创建渲染管线实例,在设置发生变更时重新创建。
#if UNITY_EDITOR
using System;
using UnityEditor;
using UnityEditor.ProjectWindowCallback;
#endifnamespace UnityEngine.Experimental.Rendering.LightweightPipeline
{/// /// 阴影级联/// public enum ShadowCascades{NO_CASCADES = 0,TWO_CASCADES,FOUR_CASCADES,}/// /// 阴影类型/// public enum ShadowType{NO_SHADOW = 0,HARD_SHADOWS,SOFT_SHADOWS,}/// /// 阴影分辨率/// public enum ShadowResolution{_256 = 256,_512 = 512,_1024 = 1024,_2048 = 2048,_4096 = 4096}/// /// MSAA质量/// MSAA是“多重采样抗锯齿”,可以使画面更加平滑。/// 超级采样抗锯齿(Super Sampling Anti-Aliasing)的原理是把当前分辨率成倍提高,然后再把画缩放到当前的显示器上。/// 这样的做法实际上就是在显示尺寸不变的情况提高分辨率,让单个像素变得极小,这样就能够大幅减轻画面的锯齿感了。/// 不过是由于对整个显示画面的放大,因此它消耗的显示资源也是非常大的。/// 不过MSAA是寻找出物体边缘部分的像素,然后对它们进行缩放处理。/// 由于只是物体的外层像素进行缩放处理,忽略掉了不会产生锯齿的内部像素,/// 所以显卡不会像处理SSAA(超级采样抗锯齿)那样需要庞大的计算量,因此MSAA比起SSAA来更有效。/// public enum MSAAQuality{Disabled = 1,_2x = 2,_4x = 4,_8x = 8}/// /// 降采样,在ForwardLitPass中使用/// public enum Downsampling{None = 0,_2xBilinear,_4xBox,_4xBilinear}/// /// 默认材质类型/// LightweightPipelineEditorResources的资源中获得/// public enum DefaultMaterialType{Standard = 0,Particle,Terrain,UnityBuiltinDefault}public class LightweightPipelineAsset : RenderPipelineAsset, ISerializationCallbackReceiver{// 这两个路径用于查找ScriptableObject,优先在"Assets"中查找,若不存在直接取默认public static readonly string s_SearchPathProject = "Assets";public static readonly string s_SearchPathPackage = "Packages/com.unity.render-pipelines.lightweight";/// /// GetDefaultShader()方法调用默认shader的私有变量/// Shader m_DefaultShader;// Default values set when a new LightweightPipeli ne asset is created/// /// 版本号,用于开发过程的迭代,在ISerializationCallbackReceiver接口的OnAfterDeserialize()中可以将旧版数据转换为新版使用的数据/// [SerializeField] int k_AssetVersion = 3;#region Editor中包含的元素/// /// 最大逐像素光源个数/// [SerializeField] int m_MaxPixelLights = 4;/// /// 支持顶点光照/// [SerializeField] bool m_SupportsVertexLight = false;/// /// 需要深度纹理/// [SerializeField] bool m_RequireDepthTexture = false;/// /// 需要软粒子/// [SerializeField] bool m_RequireSoftParticles = false;/// /// 需要不透明贴图/// [SerializeField] bool m_RequireOpaqueTexture = false;/// /// 不透明降采样/// [SerializeField] Downsampling m_OpaqueDownsampling = Downsampling._2xBilinear;/// /// 支持HDR/// [SerializeField] bool m_SupportsHDR = false;/// /// MSAA/// [SerializeField] MSAAQuality m_MSAA = MSAAQuality._4x;/// /// 渲染比例/// [SerializeField] float m_RenderScale = 1.0f;/// /// 支持动态批处理/// [SerializeField] bool m_SupportsDynamicBatching = true;/// /// 支持定向阴影/// [SerializeField] bool m_DirectionalShadowsSupported = true;/// /// 阴影图集分辨率/// [SerializeField] ShadowResolution m_ShadowAtlasResolution = ShadowResolution._2048;/// /// 阴影距离/// [SerializeField] float m_ShadowDistance = 50.0f;/// /// 阴影级联/// [SerializeField] ShadowCascades m_ShadowCascades = ShadowCascades.FOUR_CASCADES;/// /// 二级级联分界/// [SerializeField] float m_Cascade2Split = 0.25f;/// /// 四级级联分界/// [SerializeField] Vector3 m_Cascade4Split = new Vector3(0.067f, 0.2f, 0.467f);/// /// 支持非平行光阴影/// [SerializeField] bool m_LocalShadowsSupported = true;/// /// 非平行光阴影图集分辨率/// [SerializeField] ShadowResolution m_LocalShadowsAtlasResolution = ShadowResolution._512;/// /// 支持软阴影/// [SerializeField] bool m_SoftShadowsSupported = false; #endregion//以下内容,关系到LightweightPipelineCore中的 static PipelineCapabilities s_PipelineCapabilities[SerializeField] bool m_KeepAdditionalLightVariants = true;[SerializeField] bool m_KeepVertexLightVariants = true;[SerializeField] bool m_KeepDirectionalShadowVariants = true;[SerializeField] bool m_KeepLocalShadowVariants = true;[SerializeField] bool m_KeepSoftShadowVariants = true;/// /// 4个shader :BlitShader;CopyDepthShader;ScreenSpaceShadowShader;SamplingShader;/// [SerializeField] LightweightPipelineResources m_ResourcesAsset;// Deprecated[SerializeField] ShadowType m_ShadowType = ShadowType.HARD_SHADOWS;#if UNITY_EDITOR/// /// 三个材质/// [NonSerialized]LightweightPipelineEditorResources m_EditorResourcesAsset;[MenuItem("Assets/Create/Rendering/Lightweight Pipeline Asset", priority = CoreUtils.assetCreateMenuPriority1)]static void CreateLightweightPipeline(){ProjectWindowUtil.StartNameEditingIfProjectWindowExists(0, CreateInstance(),"LightweightAsset.asset", null, null);}//[MenuItem("Assets/Create/Rendering/Lightweight Pipeline Resources", priority = CoreUtils.assetCreateMenuPriority1)]static void CreateLightweightPipelineResources(){var instance = CreateInstance();AssetDatabase.CreateAsset(instance, string.Format("Assets/{0}.asset", typeof(LightweightPipelineResources).Name));}//[MenuItem("Assets/Create/Rendering/Lightweight Pipeline Editor Resources", priority = CoreUtils.assetCreateMenuPriority1)]static void CreateLightweightPipelineEditorResources(){var instance = CreateInstance();AssetDatabase.CreateAsset(instance, string.Format("Assets/{0}.asset", typeof(LightweightPipelineEditorResources).Name));}/// /// 创建带初始化的窗口/// class CreateLightweightPipelineAsset : EndNameEditAction{public override void Action(int instanceId, string pathName, string resourceFile){var instance = CreateInstance();instance.m_EditorResourcesAsset = LoadResourceFile();instance.m_ResourcesAsset = LoadResourceFile();AssetDatabase.CreateAsset(instance, pathName);}}/// /// 加载ScriptableObject资源,先查找Asset文件夹,若不存在使用默认/// /// /// static T LoadResourceFile() where T : ScriptableObject{T resourceAsset = null;var guids = AssetDatabase.FindAssets(typeof(T).Name + " t:scriptableobject", new[] {s_SearchPathProject});foreach (string guid in guids){string path = AssetDatabase.GUIDToAssetPath(guid);resourceAsset = AssetDatabase.LoadAssetAtPath(path);if (resourceAsset != null)break;}// There's currently an issue that prevents FindAssets from find resources withing the package folder.if (resourceAsset == null){string path = s_SearchPathPackage + "/LWRP/Data/" + typeof(T).Name + ".asset";resourceAsset = AssetDatabase.LoadAssetAtPath(path);}return resourceAsset;}LightweightPipelineEditorResources editorResources{get{if (m_EditorResourcesAsset == null)m_EditorResourcesAsset = LoadResourceFile();return m_EditorResourcesAsset;}}
#endifLightweightPipelineResources resources{get{
#if UNITY_EDITORif (m_ResourcesAsset == null)m_ResourcesAsset = LoadResourceFile();
#endifreturn m_ResourcesAsset;}}/// /// 创建渲染管线的方法/// /// protected override IRenderPipeline InternalCreatePipeline(){return new LightweightPipeline(this);}/// /// 获得ScriptableObject中的材质资源/// /// /// Material GetMaterial(DefaultMaterialType materialType){
#if UNITY_EDITORif (editorResources == null)return null;switch (materialType){case DefaultMaterialType.Standard:return editorResources.DefaultMaterial;case DefaultMaterialType.Particle:return editorResources.DefaultParticleMaterial;case DefaultMaterialType.Terrain:return editorResources.DefaultTerrainMaterial;// Unity Builtin Defaultdefault:return null;}
#elsereturn null;
#endif}/// /// 获得版本号/// /// public int GetAssetVersion(){return k_AssetVersion;}/// /// 最大逐像素光源数量/// public int maxPixelLights{get { return m_MaxPixelLights; }}/// /// 支持顶点光照/// public bool supportsVertexLight{get { return m_SupportsVertexLight; }}/// /// 支持深度纹理/// public bool supportsCameraDepthTexture{get { return m_RequireDepthTexture; }}/// /// 支持软粒子/// public bool supportsSoftParticles{get { return m_RequireSoftParticles; }}/// /// 支持不透明贴图/// public bool supportsCameraOpaqueTexture{get { return m_RequireOpaqueTexture; }}/// /// 不透明降采样 Downsampling._2xBilinear/// public Downsampling opaqueDownsampling{get { return m_OpaqueDownsampling; }}/// /// 支持HDR/// public bool supportsHDR{get { return m_SupportsHDR; }}/// /// MSAA的程度/// public int msaaSampleCount{get { return (int)m_MSAA; }set { m_MSAA = (MSAAQuality)value; }}/// /// 渲染比例/// public float renderScale{get { return m_RenderScale; }set { m_RenderScale = value; }}/// /// 支持动态批处理/// public bool supportsDynamicBatching{get { return m_SupportsDynamicBatching; }}/// /// 支持定向阴影/// public bool supportsDirectionalShadows{get { return m_DirectionalShadowsSupported; }}/// /// 定向阴影图集分辨率/// public int directionalShadowAtlasResolution{get { return (int)m_ShadowAtlasResolution; }}/// /// 阴影距离/// public float shadowDistance{get { return m_ShadowDistance; }set { m_ShadowDistance = value; }}/// /// 阴影级联(1、2、4)/// public int cascadeCount{get{switch (m_ShadowCascades){case ShadowCascades.TWO_CASCADES:return 2;case ShadowCascades.FOUR_CASCADES:return 4;default:return 1;}}}/// /// 阴影级联2级/// public float cascade2Split{get { return m_Cascade2Split; }}/// /// 阴影级联4级/// public Vector3 cascade4Split{get { return m_Cascade4Split; }}/// /// 支持非平行光阴影/// public bool supportsLocalShadows{get { return m_LocalShadowsSupported; }}/// /// 非平行光阴影分辨率/// public int localShadowAtlasResolution{get { return (int)m_LocalShadowsAtlasResolution; }}/// /// 支持软阴影/// public bool supportsSoftShadows{get { return m_SoftShadowsSupported; }}/// /// 自定义Shader变量分离,False/// public bool customShaderVariantStripping{get { return false; }}//以下内容,关系到LightweightPipelineCore中的 static PipelineCapabilities s_PipelineCapabilitiespublic bool keepAdditionalLightVariants{get { return m_KeepAdditionalLightVariants; }}public bool keepVertexLightVariants{get { return m_KeepVertexLightVariants; }}public bool keepDirectionalShadowVariants{get { return m_KeepDirectionalShadowVariants; }}public bool keepLocalShadowVariants{get { return m_KeepLocalShadowVariants; }}public bool keepSoftShadowVariants{get { return m_KeepSoftShadowVariants; }}/// /// Lightweight-Default.mat/// /// public override Material GetDefaultMaterial(){return GetMaterial(DefaultMaterialType.Standard);}/// /// Lightweight-DefaultParticle.mat/// /// public override Material GetDefaultParticleMaterial(){return GetMaterial(DefaultMaterialType.Particle);}/// /// null/// /// public override Material GetDefaultLineMaterial(){return GetMaterial(DefaultMaterialType.UnityBuiltinDefault);}/// /// Lightweight-DefaultTerrain.mat/// /// public override Material GetDefaultTerrainMaterial(){return GetMaterial(DefaultMaterialType.Terrain);}/// /// null/// /// public override Material GetDefaultUIMaterial(){return GetMaterial(DefaultMaterialType.UnityBuiltinDefault);}/// /// null/// /// public override Material GetDefaultUIOverdrawMaterial(){return GetMaterial(DefaultMaterialType.UnityBuiltinDefault);}/// /// null/// /// public override Material GetDefaultUIETC1SupportedMaterial(){return GetMaterial(DefaultMaterialType.UnityBuiltinDefault);}/// /// null/// /// public override Material GetDefault2DMaterial(){return GetMaterial(DefaultMaterialType.UnityBuiltinDefault);}/// /// 获取默认shader/// LightweightShaderUtils内有静态数据/// 实际字符串为"LightweightPipeline/Standard (Physically Based)"/// Lightweight-Default.mat也使用该Shader/// /// public override Shader GetDefaultShader(){if (m_DefaultShader == null)m_DefaultShader = Shader.Find(LightweightShaderUtils.GetShaderPath(ShaderPathID.STANDARD_PBS));return m_DefaultShader;}/// /// "Hidden/LightweightPipeline/BlitShader"/// public Shader blitShader{get { return resources != null ? resources.BlitShader : null; }}/// /// "Hidden/LightweightPipeline/CopyDepthShader"/// public Shader copyDepthShader{get { return resources != null ? resources.CopyDepthShader : null; }}/// /// "Hidden/LightweightPipeline/ScreenSpaceShadows"/// public Shader screenSpaceShadowShader{get { return resources != null ? resources.ScreenSpaceShadowShader : null; }}/// /// "Hidden/LightweightPipeline/SamplingShader"/// public Shader samplingShader{get { return resources != null ? resources.SamplingShader : null; }}public void OnBeforeSerialize(){}/// /// m_ShadowType已弃用,将旧版资源的m_ShadowType转为m_SoftShadowsSupported字段/// public void OnAfterDeserialize(){if (k_AssetVersion < 3){k_AssetVersion = 3;m_SoftShadowsSupported = (m_ShadowType == ShadowType.SOFT_SHADOWS);}}}
}
下面是两个上文引用到的,用来存储资源的脚本:
using UnityEngine;public class LightweightPipelineResources : ScriptableObject
{public Shader BlitShader;public Shader CopyDepthShader;public Shader ScreenSpaceShadowShader;public Shader SamplingShader;
}
using System;
using UnityEngine;public class LightweightPipelineEditorResources : ScriptableObject
{public Material DefaultMaterial;public Material DefaultParticleMaterial;public Material DefaultTerrainMaterial;
}
渲染管线实例
渲染管线实例包含运行时逻辑、静态信息(RenderTexture、Buffer等),是渲染指令的设置处。
using System;
using System.Collections.Generic;
#if UNITY_EDITOR
using UnityEditor.Experimental.Rendering.LightweightPipeline;
#endif
using UnityEngine.Rendering;
using UnityEngine.Rendering.PostProcessing;
using UnityEngine.XR;namespace UnityEngine.Experimental.Rendering.LightweightPipeline
{public partial class LightweightPipeline : RenderPipeline{/// /// 渲染管线资源/// public LightweightPipelineAsset pipelineAsset { get; private set; }/// /// 摄像机按深度排序/// CameraComparer m_CameraComparer = new CameraComparer();/// /// 前向渲染类/// LightweightForwardRenderer m_Renderer;/// /// 剔除结果/// CullResults m_CullResults;/// /// 非平行光源编号索引,用于阴影生成/// List m_LocalLightIndices = new List();/// /// 标记逐相机渲染进行过程/// bool m_IsCameraRendering;public LightweightPipeline(LightweightPipelineAsset asset){pipelineAsset = asset;SetSupportedRenderingFeatures();SetPipelineCapabilities(asset);PerFrameBuffer._GlossyEnvironmentColor = Shader.PropertyToID("_GlossyEnvironmentColor");PerFrameBuffer._SubtractiveShadowColor = Shader.PropertyToID("_SubtractiveShadowColor");PerCameraBuffer._ScaledScreenParams = Shader.PropertyToID("_ScaledScreenParams");m_Renderer = new LightweightForwardRenderer(asset);// Let engine know we have MSAA on for cases where we support MSAA backbufferif (QualitySettings.antiAliasing != pipelineAsset.msaaSampleCount)QualitySettings.antiAliasing = pipelineAsset.msaaSampleCount;Shader.globalRenderPipeline = "LightweightPipeline";m_IsCameraRendering = false;}public override void Dispose(){base.Dispose();Shader.globalRenderPipeline = "";SupportedRenderingFeatures.active = new SupportedRenderingFeatures();#if UNITY_EDITORSceneViewDrawMode.ResetDrawMode();
#endifm_Renderer.Dispose();}public override void Render(ScriptableRenderContext context, Camera[] cameras){if (m_IsCameraRendering){Debug.LogWarning("Nested camera rendering is forbidden. If you are calling camera.Render inside OnWillRenderObject callback, use BeginCameraRender callback instead.");return;}//初始化每帧数据base.Render(context, cameras);BeginFrameRendering(cameras);GraphicsSettings.lightsUseLinearIntensity = true;SetupPerFrameShaderConstants();// Sort cameras array by camera depthArray.Sort(cameras, m_CameraComparer);foreach (Camera camera in cameras){BeginCameraRendering(camera);string renderCameraTag = "Render " + camera.name;CommandBuffer cmd = CommandBufferPool.Get(renderCameraTag);//using 结束或中途中断会调用()中类的Dispose()方法using (new ProfilingSample(cmd, renderCameraTag)){//初始化逐相机的数据CameraData cameraData;InitializeCameraData(camera, out cameraData);SetupPerCameraShaderConstants(cameraData);//剔除ScriptableCullingParameters cullingParameters;if (!CullResults.GetCullingParameters(camera, cameraData.isStereoEnabled, out cullingParameters)){CommandBufferPool.Release(cmd);continue;}cullingParameters.shadowDistance = Mathf.Min(cameraData.maxShadowDistance, camera.farClipPlane);//这里执行的new ProfilingSample(cmd, renderCameraTag)中设置的cmd.BeginSample(name);命令context.ExecuteCommandBuffer(cmd);cmd.Clear();#if UNITY_EDITORtry
#endif{m_IsCameraRendering = true;
#if UNITY_EDITOR// Emit scene view UIif (cameraData.isSceneViewCamera)ScriptableRenderContext.EmitWorldGeometryForSceneView(camera);
#endif//剔除结果CullResults.Cull(ref cullingParameters, context, ref m_CullResults);List visibleLights = m_CullResults.visibleLights;RenderingData renderingData;InitializeRenderingData(ref cameraData, visibleLights,m_Renderer.maxSupportedLocalLightsPerPass, m_Renderer.maxSupportedVertexLights,out renderingData);m_Renderer.Setup(ref context, ref m_CullResults, ref renderingData);m_Renderer.Execute(ref context, ref m_CullResults, ref renderingData);}
#if UNITY_EDITORcatch (Exception){CommandBufferPool.Release(cmd);throw;}finally
#endif{m_IsCameraRendering = false;}}//这里执行的ProfilingSample.Dispose()中设置的m_Cmd.EndSample(m_Name);命令context.ExecuteCommandBuffer(cmd);CommandBufferPool.Release(cmd);context.Submit();}}public static void RenderPostProcess(CommandBuffer cmd, PostProcessRenderContext context, ref CameraData cameraData, RenderTextureFormat colorFormat, RenderTargetIdentifier source, RenderTargetIdentifier dest, bool opaqueOnly){context.Reset();context.camera = cameraData.camera;context.source = source;context.sourceFormat = colorFormat;context.destination = dest;context.command = cmd;context.flip = cameraData.camera.targetTexture == null;if (opaqueOnly)cameraData.postProcessLayer.RenderOpaqueOnly(context);elsecameraData.postProcessLayer.Render(context);}/// /// 设置支持的渲染特征,active是静态的/// void SetSupportedRenderingFeatures(){
#if UNITY_EDITORSupportedRenderingFeatures.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}/// /// 初始化摄像机数据/// /// /// void InitializeCameraData(Camera camera, out CameraData cameraData){//接近1的pipelineAsset.renderScale(或者XRSettings.eyeTextureResolutionScale),置为1const float kRenderScaleThreshold = 0.05f;cameraData.camera = camera;bool msaaEnabled = camera.allowMSAA && pipelineAsset.msaaSampleCount > 1;if (msaaEnabled)cameraData.msaaSamples = (camera.targetTexture != null) ? camera.targetTexture.antiAliasing : pipelineAsset.msaaSampleCount;elsecameraData.msaaSamples = 1;//场景相机cameraData.isSceneViewCamera = camera.cameraType == CameraType.SceneView;//存在RT且不是场景相机cameraData.isOffscreenRender = camera.targetTexture != null && !cameraData.isSceneViewCamera;cameraData.isStereoEnabled = IsStereoEnabled(camera);cameraData.isHdrEnabled = camera.allowHDR && pipelineAsset.supportsHDR;cameraData.postProcessLayer = camera.GetComponent();cameraData.postProcessEnabled = cameraData.postProcessLayer != null && cameraData.postProcessLayer.isActiveAndEnabled;// PostProcess for VR is not working atm. Disable it for now.cameraData.postProcessEnabled &= !cameraData.isStereoEnabled;Rect cameraRect = camera.rect;cameraData.isDefaultViewport = (!(Math.Abs(cameraRect.x) > 0.0f || Math.Abs(cameraRect.y) > 0.0f ||Math.Abs(cameraRect.width) < 1.0f || Math.Abs(cameraRect.height) < 1.0f));// Discard variations lesser than kRenderScaleThreshold.// Scale is only enabled for gameview.// In XR mode, grab renderScale from XRSettings instead of SRP asset for now.// This is just a temporary change pending full integration of XR with SRPif (camera.cameraType == CameraType.Game){
#if !UNITY_SWITCHif (cameraData.isStereoEnabled){cameraData.renderScale = XRSettings.eyeTextureResolutionScale;} else
#endif{cameraData.renderScale = pipelineAsset.renderScale;}}else{cameraData.renderScale = 1.0f;}cameraData.renderScale = (Mathf.Abs(1.0f - cameraData.renderScale) < kRenderScaleThreshold) ? 1.0f : cameraData.renderScale;cameraData.requiresDepthTexture = pipelineAsset.supportsCameraDepthTexture || cameraData.isSceneViewCamera;cameraData.requiresSoftParticles = pipelineAsset.supportsSoftParticles;cameraData.requiresOpaqueTexture = pipelineAsset.supportsCameraOpaqueTexture;cameraData.opaqueTextureDownsampling = pipelineAsset.opaqueDownsampling;bool anyShadowsEnabled = pipelineAsset.supportsDirectionalShadows || pipelineAsset.supportsLocalShadows;cameraData.maxShadowDistance = (anyShadowsEnabled) ? pipelineAsset.shadowDistance : 0.0f;//这是一个额外添加的脚本LightweightAdditionalCameraData additionalCameraData = camera.gameObject.GetComponent();if (additionalCameraData != null){cameraData.maxShadowDistance = (additionalCameraData.renderShadows) ? cameraData.maxShadowDistance : 0.0f;cameraData.requiresDepthTexture &= additionalCameraData.requiresDepthTexture;cameraData.requiresOpaqueTexture &= additionalCameraData.requiresColorTexture;}else if (!cameraData.isSceneViewCamera && camera.cameraType != CameraType.Reflection && camera.cameraType != CameraType.Preview){cameraData.requiresDepthTexture = false;cameraData.requiresOpaqueTexture = false;}cameraData.requiresDepthTexture |= cameraData.postProcessEnabled;}/// /// 初始化渲染数据/// /// /// /// /// /// void InitializeRenderingData(ref CameraData cameraData, List visibleLights, int maxSupportedLocalLightsPerPass, int maxSupportedVertexLights, out RenderingData renderingData){//用于生成阴影的非平行光源m_LocalLightIndices.Clear();//有阴影投射平行光源bool hasDirectionalShadowCastingLight = false;//有阴影投射非平行光源bool hasLocalShadowCastingLight = false;//初始化阴影的相关变量if (cameraData.maxShadowDistance > 0.0f){for (int i = 0; i < visibleLights.Count; ++i){Light light = visibleLights[i].light;bool castShadows = light != null && light.shadows != LightShadows.None;if (visibleLights[i].lightType == LightType.Directional){hasDirectionalShadowCastingLight |= castShadows;}else{hasLocalShadowCastingLight |= castShadows;m_LocalLightIndices.Add(i);}}}renderingData.cameraData = cameraData;InitializeLightData(visibleLights, maxSupportedLocalLightsPerPass, maxSupportedVertexLights, out renderingData.lightData);InitializeShadowData(hasDirectionalShadowCastingLight, hasLocalShadowCastingLight, out renderingData.shadowData);renderingData.supportsDynamicBatching = pipelineAsset.supportsDynamicBatching;}/// /// 初始化阴影数据/// /// /// /// void InitializeShadowData(bool hasDirectionalShadowCastingLight, bool hasLocalShadowCastingLight, out ShadowData shadowData){// Until we can have keyword stripping forcing single cascade hard shadows on gles2bool supportsScreenSpaceShadows = SystemInfo.graphicsDeviceType != GraphicsDeviceType.OpenGLES2;//渲染平行光阴影shadowData.renderDirectionalShadows = pipelineAsset.supportsDirectionalShadows && hasDirectionalShadowCastingLight;// we resolve shadows in screenspace when cascades are enabled to save ALU as computing cascade index + shadowCoord on fragment is expensiveshadowData.requiresScreenSpaceShadowResolve = shadowData.renderDirectionalShadows && supportsScreenSpaceShadows && pipelineAsset.cascadeCount > 1;//阴影级联shadowData.directionalLightCascadeCount = (shadowData.requiresScreenSpaceShadowResolve) ? pipelineAsset.cascadeCount : 1;shadowData.directionalShadowAtlasWidth = pipelineAsset.directionalShadowAtlasResolution;shadowData.directionalShadowAtlasHeight = pipelineAsset.directionalShadowAtlasResolution;//阴影级联统一为Vector3表达switch (shadowData.directionalLightCascadeCount){case 1:shadowData.directionalLightCascades = new Vector3(1.0f, 0.0f, 0.0f);break;case 2:shadowData.directionalLightCascades = new Vector3(pipelineAsset.cascade2Split, 1.0f, 0.0f);break;default:shadowData.directionalLightCascades = pipelineAsset.cascade4Split;break;}//渲染非平行光阴影shadowData.renderLocalShadows = pipelineAsset.supportsLocalShadows && hasLocalShadowCastingLight;shadowData.localShadowAtlasWidth = shadowData.localShadowAtlasHeight = pipelineAsset.localShadowAtlasResolution;shadowData.supportsSoftShadows = pipelineAsset.supportsSoftShadows;shadowData.bufferBitCount = 16;shadowData.renderedDirectionalShadowQuality = LightShadows.None;shadowData.renderedLocalShadowQuality = LightShadows.None;}/// /// 初始化光源数据/// /// /// /// /// void InitializeLightData(List visibleLights, int maxSupportedLocalLightsPerPass, int maxSupportedVertexLights, out LightData lightData){//控制最大可见光源数量<=pipelineAsset.maxPixelLightsint visibleLightsCount = Math.Min(visibleLights.Count, pipelineAsset.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 listint mainLightPresent = (lightData.mainLightIndex >= 0) ? 1 : 0;//计算附加光数量,maxSupportedLocalLightsPerPasss是4或16int additionalPixelLightsCount = Math.Min(visibleLightsCount - mainLightPresent, maxSupportedLocalLightsPerPass);int vertexLightCount = (pipelineAsset.supportsVertexLight) ? Math.Min(visibleLights.Count, maxSupportedLocalLightsPerPass) - additionalPixelLightsCount : 0;//计算逐顶点光数量,maxSupportedVertexLights是4vertexLightCount = Math.Min(vertexLightCount, maxSupportedVertexLights);//附加光数量lightData.pixelAdditionalLightsCount = additionalPixelLightsCount;//不支持顶点光时vertexLightCount是0,最后是additionalPixelLightsCount;//支持时最后是 Math.Min(visibleLights.Count, maxSupportedLocalLightsPerPass)lightData.totalAdditionalLightsCount = additionalPixelLightsCount + vertexLightCount;lightData.visibleLights = visibleLights;lightData.visibleLocalLightIndices = m_LocalLightIndices;}/// /// 查找第一个Direction光源,返回数组中编号,若无返回-1/// /// /// // Main Light is always a directional lightint GetMainLight(List visibleLights){int totalVisibleLights = visibleLights.Count;if (totalVisibleLights == 0 || pipelineAsset.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 lightif (currLight.lightType == LightType.Directional)return i;}return -1;}/// /// 设置每帧的Shader常量_GlossyEnvironmentColor、_SubtractiveShadowColor/// void SetupPerFrameShaderConstants(){// When glossy reflections are OFF in the shader we set a constant color to use as indirect specularSphericalHarmonicsL2 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 selectedShader.SetGlobalVector(PerFrameBuffer._SubtractiveShadowColor, CoreUtils.ConvertSRGBToActiveColorSpace(RenderSettings.subtractiveShadowColor));}/// /// 设置每个相机的Shader常量_ScaledScreenParams/// /// void SetupPerCameraShaderConstants(CameraData cameraData){float cameraWidth = (float)cameraData.camera.pixelWidth * cameraData.renderScale;float cameraHeight = (float)cameraData.camera.pixelWidth * cameraData.renderScale;Shader.SetGlobalVector(PerCameraBuffer._ScaledScreenParams, new Vector4(cameraWidth, cameraHeight, 1.0f + 1.0f / cameraWidth, 1.0f + 1.0f / cameraHeight));}bool IsStereoEnabled(Camera camera){
#if !UNITY_SWITCHbool isSceneViewCamera = camera.cameraType == CameraType.SceneView;return XRSettings.isDeviceActive && !isSceneViewCamera && (camera.stereoTargetEye == StereoTargetEyeMask.Both);
#elsereturn false;
#endif}}
}
参考资料
[1] Unite 2017 | Unity可编程渲染管线剖析.
[2] RenderPipeLine.
[3] 详解可编程脚本渲染管线SRP.
[4] 轻量级渲染管线:优化实时性.
本文来自互联网用户投稿,文章观点仅代表作者本人,不代表本站立场,不承担相关法律责任。如若转载,请注明出处。 如若内容造成侵权/违法违规/事实不符,请点击【内容举报】进行投诉反馈!
