42.预渲染隐藏相机与RenderTexture

42.性能优化-CPU-脚本-预渲染隐藏相机与 RenderTexture


42.1 知识点

为什么要进行预渲染

预渲染的本质是提前生成某些画面结果,在需要时直接使用,从而避免在玩家可见的第一次渲染时产生卡顿。

主要用来解决两类问题:

  1. CPU 到 GPU 的首帧数据上传
    当模型、材质、纹理、网格第一次被真正用于渲染时,CPU 会把网格数据、纹理数据、常量缓冲等上传到 GPU 内存。该过程虽是异步的,但首次调用可能导致主线程等待上传完成,玩家在第一次看到该对象时容易感到掉帧(首帧卡顿)。

  2. Shader(着色器)首次编译
    Shader 按需编译(尤其在多变体 Shader、URP、HDRP 中)。第一次使用某个 Shader Pass + Keyword 组合时,Unity 会在运行时编译,编译可能卡住 CPU,在移动端或低端机上更明显。

因此,若在加载阶段做好预渲染,正式进入游戏场景后,玩家第一次看到这些对象时就不会再出现上述卡顿。

隐藏相机 + RenderTexture 进行预渲染

一般在合适时机(例如过场景、显示加载/读条界面时),用隐藏相机 + RenderTexture 在后台对目标对象做预渲染,既完成预渲染,又不影响玩家看到的画面。

基本做法:

  1. 新建一个摄像机,建议只渲染专门用于预渲染的层

  2. 将其 targetTexture 设为一张 RenderTexture

  3. 预渲染时激活该摄像机,让它渲染需要预渲染的对象(先预加载、再预实例化到该摄像机可见位置;实例化对象最好放到预渲染专用层)。



  4. 预渲染结束后失活摄像机,避免持续开销。

关键注意点: 新摄像机、新层级(预渲染层)、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

×

喜欢就点赞,疼爱就打赏