第3章 URP Renderer配置
理论讲解
Forward Renderer vs Deferred Renderer
在Unity的URP中,渲染器(Renderer)是负责执行实际渲染操作的核心组件。URP主要使用Forward Renderer,这与传统的Forward Rendering和Deferred Rendering有着本质的区别。
Forward Renderer(前向渲染)
Forward Renderer是URP默认使用的渲染器类型,它采用逐物体渲染的方式。对于每个物体,渲染管线会执行以下步骤:
- 几何处理:顶点着色器处理顶点数据
- 光照计算:在片元着色器中计算光照效果
- 输出到帧缓冲:将最终颜色输出到屏幕
Forward Renderer的优势:
- 内存占用较低,适合移动设备
- 透明物体渲染效果好
- 支持复杂的材质和着色器
- 实现简单,易于理解和调试
Forward Renderer的局限性:
- 每个像素可能被多次光照计算(光照数量多时性能下降)
- 不适合大量动态光源的场景
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
| using UnityEngine; using UnityEngine.Rendering; using UnityEngine.Rendering.Universal;
public class ForwardRendererExample : MonoBehaviour { private ScriptableRenderer renderer; private ScriptableRenderContext renderContext; void Start() { var urpAsset = GraphicsSettings.renderPipelineAsset as UniversalRenderPipelineAsset; if(urpAsset != null) { renderer = urpAsset.scriptableRenderer; Debug.Log($"Renderer Type: {renderer.GetType().Name}"); } } void OnPreRender() { renderContext = RenderPipeline.beginContextRendering; } }
|
Deferred Renderer(延迟渲染)
虽然URP主要使用Forward Renderer,但理解Deferred Rendering的概念对全面了解渲染管线很重要。Deferred Rendering将渲染过程分为两个阶段:
- 几何阶段:渲染所有几何体,将材质属性(法线、漫反射、镜面反射等)存储到G-Buffer中
- 光照阶段:基于G-Buffer中的信息计算光照效果
Deferred Rendering的优势:
- 光照计算与物体数量无关
- 适合大量动态光源的场景
- 光照效果质量高
Deferred Rendering的局限性:
- 内存占用高(需要多个G-Buffer)
- 透明物体渲染复杂
- 不适合移动设备
Renderer Features系统介绍
Renderer Features是URP中一个强大的扩展系统,允许开发者在渲染管线中插入自定义的渲染逻辑。Renderer Features可以用于实现各种效果,如后处理、特殊渲染、性能优化等。
Renderer Features的主要特点:
- 可以在渲染管线的特定阶段插入自定义逻辑
- 支持多种渲染事件(RenderPassEvent)
- 可以访问渲染上下文和渲染数据
- 便于复用和模块化
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88
| using UnityEngine; using UnityEngine.Rendering; using UnityEngine.Rendering.Universal;
public class CustomRendererFeature : ScriptableRendererFeature { [System.Serializable] public class CustomSettings { public RenderPassEvent renderPassEvent = RenderPassEvent.AfterRenderingOpaques; public Shader shader = null; } public CustomSettings settings = new CustomSettings(); private CustomRenderPass renderPass; public override void Create() { renderPass = new CustomRenderPass(settings.renderPassEvent, settings.shader); } public override void AddRenderPasses(ScriptableRenderer renderer, ref RenderingData renderingData) { renderer.EnqueuePass(renderPass); } }
public class CustomRenderPass : ScriptableRenderPass { private Material material; private Shader shader; private RenderTargetIdentifier source; private RenderTargetIdentifier destination; public CustomRenderPass(RenderPassEvent renderPassEvent, Shader shader) { this.renderPassEvent = renderPassEvent; this.shader = shader; if(shader == null) { Debug.LogError("Custom shader is null!"); return; } this.material = new Material(shader); } public override void Configure(CommandBuffer cmd, RenderTextureDescriptor cameraTextureDescriptor) { ConfigureTarget(destination); ConfigureClear(ClearFlag.None, Color.black); } public override void Execute(ScriptableRenderContext context, ref RenderingData renderingData) { if(material == null || renderingData.cameraData.cameraType != CameraType.Game) { return; } CommandBuffer cmd = CommandBufferPool.Get("Custom Render Pass"); source = renderingData.cameraData.renderer.cameraColorTarget; destination = RenderTargetHandle.CameraTarget.Identifier(); cmd.Blit(source, destination, material); context.ExecuteCommandBuffer(cmd); CommandBufferPool.Release(cmd); } public override void FrameCleanup(CommandBuffer cmd) { if(material != null) { CoreUtils.Destroy(material); material = null; } } }
|
Render Objects功能详解
Render Objects是URP中的一个Renderer Feature,用于渲染特定对象集合。它允许开发者指定一组对象进行特殊渲染,而不受常规渲染管线的限制。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50
| using UnityEngine; using UnityEngine.Rendering.Universal;
public class RenderObjectsExample : MonoBehaviour { [Header("Render Objects Settings")] public LayerMask layerMask = 0; public SortingCriteria sortingCriteria = SortingCriteria.CommonOpaque; public int overrideMaterialPassIndex = 0; [Header("Objects to Render")] public GameObject[] objectsToRender; private RenderObjects renderObjectsFeature; void Start() { SetupRenderObjects(); } void SetupRenderObjects() { var settings = new RenderObjects.Settings(); settings.layerMask = layerMask; settings.sortingCriteria = sortingCriteria; settings.overrideMaterial = null; settings.overrideMaterialPassIndex = overrideMaterialPassIndex; renderObjectsFeature = new RenderObjects(); renderObjectsFeature.settings = settings; foreach(var obj in objectsToRender) { obj.layer = GetFirstSetBit(layerMask); } } int GetFirstSetBit(LayerMask mask) { for(int i = 0; i < 32; i++) { if((mask.value & (1 << i)) != 0) return i; } return 0; } }
|
Decal Renderer Feature
Decal Renderer Feature用于在物体表面渲染贴花效果,如血迹、弹孔、涂鸦等。它允许在不修改原始模型的情况下添加细节。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39
| using UnityEngine; using UnityEngine.Rendering.Universal;
public class DecalSystemExample : MonoBehaviour { [Header("Decal Settings")] public Material decalMaterial; public float size = 1.0f; public float fadeDistance = 10.0f; [Header("Decal Objects")] public Transform[] decalPositions; void Start() { CreateDecals(); } void CreateDecals() { foreach(var pos in decalPositions) { CreateDecalAtPosition(pos.position, pos.rotation); } } void CreateDecalAtPosition(Vector3 position, Quaternion rotation) { GameObject decalObj = new GameObject("Decal"); decalObj.transform.position = position; decalObj.transform.rotation = rotation; decalObj.transform.localScale = Vector3.one * size; var decal = decalObj.AddComponent<UniversalAdditionalDecalData>(); decal.material = decalMaterial; decal.fadeFactor = fadeDistance; } }
|
Screen Space Ambient Occlusion (SSAO)
SSAO是一种屏幕空间环境光遮蔽技术,用于模拟物体间的环境光遮蔽效果,增强场景的深度感和真实感。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50
| using UnityEngine; using UnityEngine.Rendering.Universal;
public class SSAOManager : MonoBehaviour { [Header("SSAO Settings")] [Range(0.0f, 3.0f)] public float intensity = 1.0f; [Range(0.0f, 1.0f)] public float radius = 0.3f; [Range(0.0f, 2.0f)] public float sampleCount = 1.0f; private Volume volume; private SSAO ssao; void Start() { SetupSSAO(); } void SetupSSAO() { GameObject volumeObj = new GameObject("SSAO Volume"); volume = volumeObj.AddComponent<Volume>(); ssao = ScriptableObject.CreateInstance<SSAO>(); volume.profile = ScriptableObject.CreateInstance<VolumeProfile>(); volume.profile.Add(ssao); ssao.intensity.value = intensity; ssao.radius.value = radius; ssao.sampleCount.value = (SSAOQuality) Mathf.RoundToInt(sampleCount); volume.isGlobal = true; } void Update() { if(ssao != null) { ssao.intensity.value = intensity; ssao.radius.value = radius; } } }
|
自定义Renderer Feature基础
创建自定义Renderer Feature的步骤:
- 继承ScriptableRendererFeature类
- 创建自定义ScriptableRenderPass
- 在Create()方法中初始化渲染通道
- 在AddRenderPasses()方法中添加渲染通道
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89
| using UnityEngine; using UnityEngine.Rendering; using UnityEngine.Rendering.Universal;
public class CustomPostProcessFeature : ScriptableRendererFeature { [System.Serializable] public class PostProcessSettings { public RenderPassEvent renderPassEvent = RenderPassEvent.BeforeRenderingPostProcessing; public Shader shader; public float intensity = 1.0f; } public PostProcessSettings settings = new PostProcessSettings(); private PostProcessPass pass; public override void Create() { if(settings.shader == null) { Debug.LogError("Post-process shader is null!"); return; } pass = new PostProcessPass(settings); } public override void AddRenderPasses(ScriptableRenderer renderer, ref RenderingData renderingData) { if(renderingData.cameraData.cameraType != CameraType.Game) return; renderer.EnqueuePass(pass); } }
public class PostProcessPass : ScriptableRenderPass { private PostProcessFeature.PostProcessSettings settings; private Material material; private RTHandle tempRT; public PostProcessPass(PostProcessFeature.PostProcessSettings settings) { this.settings = settings; if(settings.shader != null) { material = CoreUtils.CreateEngineMaterial(settings.shader); } } public override void Configure(CommandBuffer cmd, RenderTextureDescriptor cameraTextureDescriptor) { ConfigureTarget(RTHandles.CameraColor); ConfigureClear(ClearFlag.None, Color.black); } public override void Execute(ScriptableRenderContext context, ref RenderingData renderingData) { if(material == null) return; CommandBuffer cmd = CommandBufferPool.Get("Custom Post Process"); material.SetFloat("_Intensity", settings.intensity); RenderTargetIdentifier source = renderingData.cameraData.renderer.cameraColorTarget; RenderTargetIdentifier destination = RenderTargetHandle.CameraTarget.Identifier(); Blitter.BlitCameraBuffer(cmd, source, destination, material, 0); context.ExecuteCommandBuffer(cmd); CommandBufferPool.Release(cmd); } public override void FrameCleanup(CommandBuffer cmd) { if(material != null) { CoreUtils.Destroy(material); material = null; } } }
|
代码示例
完整的Renderer Feature实现
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116
| using UnityEngine; using UnityEngine.Rendering; using UnityEngine.Rendering.Universal;
public class EdgeDetectionFeature : ScriptableRendererFeature { [System.Serializable] public class EdgeDetectionSettings { public RenderPassEvent renderPassEvent = RenderPassEvent.BeforeRenderingPostProcessing; public Shader shader; [Range(0.0f, 1.0f)] public float edgeThreshold = 0.2f; [Range(0.0f, 1.0f)] public float edgeColorIntensity = 1.0f; } public EdgeDetectionSettings settings = new EdgeDetectionSettings(); private EdgeDetectionPass edgeDetectionPass; public override void Create() { if(settings.shader == null) { settings.shader = Shader.Find("Universal Render Pipeline/Custom/EdgeDetection"); if(settings.shader == null) { Debug.LogError("Edge Detection shader not found!"); return; } } edgeDetectionPass = new EdgeDetectionPass(settings); } public override void AddRenderPasses(ScriptableRenderer renderer, ref RenderingData renderingData) { if(renderingData.cameraData.cameraType != CameraType.Game) return; renderer.EnqueuePass(edgeDetectionPass); } }
public class EdgeDetectionPass : ScriptableRenderPass { private EdgeDetectionFeature.EdgeDetectionSettings settings; private Material material; private RTHandle tempRT; public EdgeDetectionPass(EdgeDetectionFeature.EdgeDetectionSettings settings) { this.settings = settings; this.renderPassEvent = settings.renderPassEvent; if(settings.shader != null) { material = CoreUtils.CreateEngineMaterial(settings.shader); } } public override void Configure(CommandBuffer cmd, RenderTextureDescriptor cameraTextureDescriptor) { tempRT = RTHandles.Alloc( cameraTextureDescriptor.width, cameraTextureDescriptor.height, cameraTextureDescriptor.msaaSamples, cameraTextureDescriptor.graphicsFormat, FilterMode.Bilinear, TextureWrapMode.Clamp, name: "EdgeDetectionTexture" ); ConfigureTarget(tempRT); ConfigureClear(ClearFlag.All, Color.black); } public override void Execute(ScriptableRenderContext context, ref RenderingData renderingData) { if(material == null) return; CommandBuffer cmd = CommandBufferPool.Get("Edge Detection Pass"); material.SetFloat("_EdgeThreshold", settings.edgeThreshold); material.SetFloat("_EdgeColorIntensity", settings.edgeColorIntensity); RenderTargetIdentifier source = renderingData.cameraData.renderer.cameraColorTarget; cmd.Blit(source, tempRT.nameID, material, 0); cmd.Blit(tempRT.nameID, source); context.ExecuteCommandBuffer(cmd); CommandBufferPool.Release(cmd); } public override void FrameCleanup(CommandBuffer cmd) { if(tempRT != null) { tempRT.Release(); tempRT = null; } if(material != null) { CoreUtils.Destroy(material); material = null; } } }
|
Renderer配置管理器
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104
| using UnityEngine; using UnityEngine.Rendering.Universal;
public class RendererConfigurationManager : MonoBehaviour { [Header("Renderer References")] public UniversalRenderPipelineAsset urpAsset; public ScriptableRenderer renderer; [Header("Feature Management")] public ScriptableRendererFeature[] features; public bool[] featureEnabled; [Header("Performance Settings")] public bool enableDynamicBatching = true; public bool enableGPUInstancing = true; public bool enableLODs = true; void Start() { ConfigureRenderer(); } public void ConfigureRenderer() { if(urpAsset == null) { urpAsset = GraphicsSettings.renderPipelineAsset as UniversalRenderPipelineAsset; } if(urpAsset != null) { renderer = urpAsset.scriptableRenderer; ConfigureFeatures(); ConfigurePerformanceSettings(); Debug.Log("Renderer configured successfully"); } else { Debug.LogError("No URP Asset found!"); } } void ConfigureFeatures() { if(renderer == null || features == null || featureEnabled == null) return; if(features.Length != featureEnabled.Length) { Debug.LogWarning("Features and enabled arrays have different lengths!"); return; } for(int i = 0; i < features.Length; i++) { if(features[i] != null) { Debug.Log($"Feature {features[i].name} {(featureEnabled[i] ? "enabled" : "disabled")}"); } } } void ConfigurePerformanceSettings() { if(urpAsset != null) { urpAsset.supportsDynamicBatching = enableDynamicBatching; urpAsset.supportsGPUInstancing = enableGPUInstancing; } } public void AddFeature(ScriptableRendererFeature feature) { if(renderer != null && feature != null) { Debug.Log($"Attempting to add feature: {feature.name}"); } } public string GetRendererInfo() { if(renderer != null) { return $"Renderer Type: {renderer.GetType().Name}\n" + $"Feature Count: {renderer.rendererFeatures.Count}"; } return "No renderer found"; } }
|
实践练习
练习1:创建自定义模糊效果Renderer Feature
目标:创建一个具有模糊效果的Renderer Feature
步骤:
- 创建模糊着色器
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71
| Shader "Universal Render Pipeline/Custom/BlurEffect" { Properties { _MainTex ("Texture", 2D) = "white" {} _BlurSize ("Blur Size", Range(0, 10)) = 1.0 _Intensity ("Intensity", Range(0, 1)) = 1.0 } SubShader { Tags { "RenderType"="Opaque" "RenderPipeline" = "UniversalPipeline"} LOD 100 Pass { Name "BlurPass" HLSLPROGRAM #pragma vertex vert #pragma fragment frag #include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Core.hlsl" struct Attributes { float4 positionOS : POSITION; float2 uv : TEXCOORD0; }; struct Varyings { float4 positionCS : SV_POSITION; float2 uv : TEXCOORD0; }; TEXTURE2D(_MainTex); SAMPLER(sampler_MainTex); float4 _MainTex_TexelSize; float _BlurSize; float _Intensity; Varyings vert (Attributes v) { Varyings o; o.positionCS = TransformWorldToHClip(v.positionOS.xyz); o.uv = v.uv; return o; } half4 frag (Varyings i) : SV_Target { half4 color = 0; float2 texelSize = _MainTex_TexelSize.xy; // 简单的模糊效果 for(int x = -2; x <= 2; x++) { for(int y = -2; y <= 2; y++) { float2 offset = float2(x, y) * texelSize * _BlurSize; color += SAMPLE_TEXTURE2D(_MainTex, sampler_MainTex, i.uv + offset); } } color /= 25; // 25个采样点 half4 original = SAMPLE_TEXTURE2D(_MainTex, sampler_MainTex, i.uv); return lerp(original, color, _Intensity); } ENDHLSL } } }
|
- 创建模糊Renderer Feature
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112
| using UnityEngine; using UnityEngine.Rendering; using UnityEngine.Rendering.Universal;
public class BlurFeature : ScriptableRendererFeature { [System.Serializable] public class BlurSettings { public RenderPassEvent renderPassEvent = RenderPassEvent.BeforeRenderingPostProcessing; public Shader blurShader; [Range(0.0f, 5.0f)] public float blurSize = 1.0f; [Range(0.0f, 1.0f)] public float intensity = 0.5f; } public BlurSettings settings = new BlurSettings(); private BlurRenderPass blurPass; public override void Create() { if(settings.blurShader == null) { settings.blurShader = Shader.Find("Universal Render Pipeline/Custom/BlurEffect"); } blurPass = new BlurRenderPass(settings); } public override void AddRenderPasses(ScriptableRenderer renderer, ref RenderingData renderingData) { if(renderingData.cameraData.cameraType != CameraType.Game) return; renderer.EnqueuePass(blurPass); } }
public class BlurRenderPass : ScriptableRenderPass { private BlurFeature.BlurSettings settings; private Material material; private RTHandle tempRT; public BlurRenderPass(BlurFeature.BlurSettings settings) { this.settings = settings; this.renderPassEvent = settings.renderPassEvent; if(settings.blurShader != null) { material = CoreUtils.CreateEngineMaterial(settings.blurShader); } } public override void Configure(CommandBuffer cmd, RenderTextureDescriptor cameraTextureDescriptor) { int width = cameraTextureDescriptor.width; int height = cameraTextureDescriptor.height; tempRT = RTHandles.Alloc( width, height, cameraTextureDescriptor.msaaSamples, cameraTextureDescriptor.graphicsFormat, FilterMode.Bilinear, TextureWrapMode.Clamp, name: "BlurTexture" ); ConfigureTarget(tempRT); ConfigureClear(ClearFlag.All, Color.black); } public override void Execute(ScriptableRenderContext context, ref RenderingData renderingData) { if(material == null) return; CommandBuffer cmd = CommandBufferPool.Get("Blur Pass"); material.SetFloat("_BlurSize", settings.blurSize); material.SetFloat("_Intensity", settings.intensity); RenderTargetIdentifier source = renderingData.cameraData.renderer.cameraColorTarget; cmd.Blit(source, tempRT.nameID, material, 0); cmd.Blit(tempRT.nameID, source); context.ExecuteCommandBuffer(cmd); CommandBufferPool.Release(cmd); } public override void FrameCleanup(CommandBuffer cmd) { if(tempRT != null) { tempRT.Release(); tempRT = null; } if(material != null) { CoreUtils.Destroy(material); material = null; } } }
|
练习2:创建性能监控Renderer Feature
目标:创建一个实时监控渲染性能的Renderer Feature
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98
| using UnityEngine; using UnityEngine.Rendering; using UnityEngine.Rendering.Universal; using System.Collections.Generic;
public class PerformanceMonitorFeature : ScriptableRendererFeature { [System.Serializable] public class PerformanceSettings { public RenderPassEvent renderPassEvent = RenderPassEvent.AfterRendering; public bool enableFrameTimeLogging = false; public float logInterval = 1.0f; } public PerformanceSettings settings = new PerformanceSettings(); private PerformanceMonitorPass pass; public override void Create() { pass = new PerformanceMonitorPass(settings); } public override void AddRenderPasses(ScriptableRenderer renderer, ref RenderingData renderingData) { if(renderingData.cameraData.cameraType != CameraType.Game) return; renderer.EnqueuePass(pass); } }
public class PerformanceMonitorPass : ScriptableRenderPass { private PerformanceMonitorFeature.PerformanceSettings settings; private float lastLogTime = 0f; private List<float> frameTimes = new List<float>(); private const int MAX_SAMPLES = 60; public PerformanceMonitorPass(PerformanceMonitorFeature.PerformanceSettings settings) { this.settings = settings; this.renderPassEvent = settings.renderPassEvent; } public override void Execute(ScriptableRenderContext context, ref RenderingData renderingData) { float currentFrameTime = Time.unscaledDeltaTime; frameTimes.Add(currentFrameTime); if(frameTimes.Count > MAX_SAMPLES) { frameTimes.RemoveAt(0); } if(settings.enableFrameTimeLogging && Time.time - lastLogTime >= settings.logInterval) { LogPerformanceInfo(); lastLogTime = Time.time; } } private void LogPerformanceInfo() { if(frameTimes.Count == 0) return; float totalTime = 0f; float minTime = float.MaxValue; float maxTime = float.MinValue; foreach(float time in frameTimes) { totalTime += time; minTime = Mathf.Min(minTime, time); maxTime = Mathf.Max(maxTime, time); } float avgFrameTime = totalTime / frameTimes.Count; float avgFPS = 1.0f / avgFrameTime; float minFPS = 1.0f / maxTime; float maxFPS = 1.0f / minTime; Debug.Log($"[Performance Monitor]\n" + $"Avg FPS: {avgFPS:F1}\n" + $"Min FPS: {minFPS:F1}\n" + $"Max FPS: {maxFPS:F1}\n" + $"Avg Frame Time: {avgFrameTime * 1000:F1}ms\n" + $"Frame Samples: {frameTimes.Count}"); } public override void FrameCleanup(CommandBuffer cmd) { frameTimes.Clear(); } }
|
总结
本章详细介绍了URP中Renderer的配置和使用方法。通过理论讲解、代码示例和实践练习,我们学习了:
Forward Renderer vs Deferred Renderer:理解了URP主要使用的Forward Renderer的工作原理,以及它与Deferred Renderer的区别和适用场景。
Renderer Features系统:掌握了如何使用Renderer Features来扩展渲染管线功能,包括创建自定义渲染通道、配置渲染事件等。
Render Objects功能:了解了如何使用Render Objects来渲染特定对象集合。
特殊效果Renderer Features:学习了Decal和SSAO等特殊效果的实现方法。
自定义Renderer Feature:掌握了创建自定义Renderer Feature的完整流程,从继承ScriptableRendererFeature到实现ScriptableRenderPass。
Renderer是URP渲染管线的核心执行组件,Renderer Features系统为开发者提供了强大的扩展能力。通过合理使用Renderer Features,可以实现各种特殊渲染效果,如后处理、特殊光照、性能监控等。
在下一章中,我们将深入探讨URP着色器的基础知识,包括URP Shader的结构、与Built-in Shader的区别,以及如何使用ShaderGraph在URP中创建着色器。着色器是渲染管线中负责视觉效果的关键组件,掌握URP着色器的使用对于实现高质量的视觉效果至关重要。