42.性能优化-CPU-脚本-预渲染隐藏相机与 RenderTexture
42.1 知识点
为什么要进行预渲染
预渲染的本质是提前生成某些画面结果,在需要时直接使用,从而避免在玩家可见的第一次渲染时产生卡顿。
主要用来解决两类问题:
CPU 到 GPU 的首帧数据上传
当模型、材质、纹理、网格第一次被真正用于渲染时,CPU 会把网格数据、纹理数据、常量缓冲等上传到 GPU 内存。该过程虽是异步的,但首次调用可能导致主线程等待上传完成,玩家在第一次看到该对象时容易感到掉帧(首帧卡顿)。Shader(着色器)首次编译
Shader 按需编译(尤其在多变体 Shader、URP、HDRP 中)。第一次使用某个 Shader Pass + Keyword 组合时,Unity 会在运行时编译,编译可能卡住 CPU,在移动端或低端机上更明显。
因此,若在加载阶段做好预渲染,正式进入游戏场景后,玩家第一次看到这些对象时就不会再出现上述卡顿。
隐藏相机 + RenderTexture 进行预渲染
一般在合适时机(例如过场景、显示加载/读条界面时),用隐藏相机 + RenderTexture 在后台对目标对象做预渲染,既完成预渲染,又不影响玩家看到的画面。
基本做法:
新建一个摄像机,建议只渲染专门用于预渲染的层。
将其
targetTexture设为一张 RenderTexture。
预渲染时激活该摄像机,让它渲染需要预渲染的对象(先预加载、再预实例化到该摄像机可见位置;实例化对象最好放到预渲染专用层)。




预渲染结束后失活摄像机,避免持续开销。
关键注意点: 新摄像机、新层级(预渲染层)、RenderTexture 三者配合使用。
实现要点:
- 创建新摄像机和新的 RenderTexture,并关联(摄像机
targetTexture = renderTexture)。 - 添加预渲染层:主摄像机不渲染该层,预渲染摄像机只渲染该层;将需要预渲染的实例(如 Cube)设为预渲染层。
预渲染流程可封装为协程:激活摄像机并绑定 RenderTexture → 预加载、预实例化并把对象放到预渲染层与摄像机可见位置 → 等待一帧(如 WaitForEndOfFrame)→ 失活摄像机并解绑 RenderTexture。示例脚本核心如下。
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class PreRenderHelper : MonoBehaviour
{
public Camera hiddenCamera;
public RenderTexture renderTexture;
/// <summary>预渲染对外接口</summary>
/// <param name="resourceNameList">资源名列表</param>
public void OnPreRender(List<string> resourceNameList)
{
StartCoroutine(OnPreRenderReally(resourceNameList));
}
IEnumerator OnPreRenderReally(List<string> resourceNameList)
{
if (hiddenCamera == null || renderTexture == null)
{
Debug.LogWarning("预渲染的摄像机和渲染纹理没有设置");
yield break;
}
hiddenCamera.targetTexture = renderTexture;
hiddenCamera.enabled = true;
// 预加载、预实例化;将实例化对象放到预渲染层并置于预渲染摄像机可见位置
yield return new WaitForEndOfFrame();
hiddenCamera.enabled = false;
hiddenCamera.targetTexture = null;
}
}
42.2 知识点代码
Lesson42_性能优化_CPU_脚本_预渲染隐藏相机与RenderTexture.cs
public class Lesson42_性能优化_CPU_脚本_预渲染隐藏相机与RenderTexture
{
#region 知识点一 为什么要进行预渲染
//预渲染的本质是提前生成某些画面结果
//然后在需要的时候直接使用结果
//主要目的是为了解决
//1.CPU 到 GPU 的首帧数据上传
// 当一个模型、材质、纹理、网格第一次被真正用于渲染时
// CPU 会把它的网格数据、纹理数据、常量缓冲等上传到 GPU 内存
// 这个过程是异步的,但首次调用可能会导致主线程等待数据上传完成
// 玩家可能会在第一次看到该对象时感到掉帧,即首帧卡顿
//2.Shader(着色器)首次编译
// Shader 是按需编译的(尤其在多变体 Shader 、URP 、 HDRP 中)
// 第一次用到某个 Shader Pass + Keyword 组合 时,Unity 会在运行时编译它
// 编译过程可能会卡 CPU,尤其在移动设备或低端机上
//所以总的来说
//如果进行了预渲染,真正进入游戏场景时,玩家第一次看到预渲染的游戏对象就不会有卡顿感了
#endregion
#region 知识点二 隐藏相机 + RenderTexture 进行预渲染
//一般在合适的时机,比如在过场景显示加载界面时(读条界面)
//我们可以利用 隐藏相机 + RenderTexture 的形式偷偷的在后台预渲染目标对象
//这样做的目的是不仅可以预渲染目标对象,还可以避免影响玩家体验
//基本做法:
//1.创建一个新的摄像机(最好只渲染一个专门用于预渲染的层)
//2.设置它的targetTexture为一个RenderTexture
//3.预渲染时,激活摄像机
// 让它渲染你想要预渲染的对象实例化到对应的位置
// 3-1.预加载(Resources、AB包、Addressables、UnityWebRequest等等)
// 3-2.预实例化(把想要预渲染的对象实例化到场景中,最好设置到一个专门用于预渲染的层级)
//4.预渲染结束后,失活摄像机,避免额外开销
//关键注意点:
//1.新摄像机
//2.新层级
//3.RenderTexture
#endregion
}
PreRenderHelper.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class PreRenderHelper : MonoBehaviour
{
public Camera hiddenCamera;
public RenderTexture renderTexture;
/// <summary>
/// 预渲染对外接口
/// </summary>
/// <param name="resNameList">资源名列表</param>
public void OnPreRender(List<string> resNameList)
{
StartCoroutine(OnPreRenderReally(resNameList));
}
IEnumerator OnPreRenderReally(List<string> resNameList)
{
//renderTexture我们目前是创建好的
//以后为了节约内存,可以需要的时候动态创建
if (hiddenCamera == null || renderTexture == null)
{
Debug.LogWarning("预渲染的摄像机和渲染纹理没有设置");
yield break;
}
hiddenCamera.targetTexture = renderTexture;
hiddenCamera.enabled = true;
//预加载
//预实例化
//需要把实例化的内容层级改到对应的 预渲染层 并且要把它的位置 放到预渲染摄像机能看到的 目标位置
yield return new WaitForEndOfFrame();
hiddenCamera.enabled = false;
hiddenCamera.targetTexture = null;
//把代码动态创建的渲染纹理 给释放移除了
}
}
转载请注明来源,欢迎对文章中的引用来源进行考证,欢迎指出任何有错误或不够清晰的表达。可以在下面评论区评论,也可以邮件至 785293209@qq.com