2.8 预渲染

1.预渲染简介

2.隐藏相机 + RenderTexture 进行预渲染

3.Unity API进行预渲染


1.预渲染简介

csharp 复制代码
在游戏运行前或加载时, 提前将需要渲染的资源(如模型、纹理、着色器等)上传到GPU, 从而避免在游戏运行时首次使用该资

源时出现卡顿

1).问题来源:

a.当游戏运行时, 一个模型(包括网格、纹理、材质等)第一次被渲染时, CPU需要准备数据并上传到GPU; 这个过程包括:

- 将网格数据(顶点、索引等)上传到GPU的顶点缓冲区和索引缓冲区

- 将纹理数据上传到GPU的纹理资源

- 编译着色器(如果之前没有编译过)并创建GPU上的着色器资源

- 设置常量缓冲区等

b.虽然现代图形API(如DirectX12、Vulkan)支持多线程和异步上传, 但首次上传仍然可能引起主线程的等待, 尤其是当数据

量大时, 会导致帧率下降, 也就是我们常说的"首帧卡顿"

2).解决方案

预加载: 在游戏开始前(如加载界面)或者提前在场景中不可见的地方, 主动触发这些资源的首次上传. 让GPU提前拥有这些

资源; 这样当游戏运行时需要渲染这些资源时, 就不再需要上传, 从而避免卡顿

2.隐藏相机 + RenderTexture 进行预渲染

csharp 复制代码
一般在合适的时机, 比如在过场景显示加载界面时(读条界面), 我们可以利用"隐藏相机 + RenderTexture"的形式偷偷的在

后台预渲染目标对象, 这样做的目的是不仅可以预渲染目标对象, 还可以避免影响玩家体验

a.创建一个新的摄像机(最好只渲染一个专门用于预渲染的层)

b.设置它的targetTexture为一个RenderTexture

c.预渲染时, 激活摄像机, 让它渲染你想要预渲染的对象实例化到对应的位置

- 预加载(Resources、AB包、Addressables、UnityWebRequest等等)

- 预实例化(把想要预渲染的对象实例化到场景中, 最好设置到一个专门用于预渲染的层级)

d.预渲染结束后, 失活摄像机, 避免额外开销

3.Unity API进行预渲染

csharp 复制代码
如果我们不希望创建隐藏相机, 可以通过Unity API "CommandBuffer"(命令缓冲区)触发一次CPU到GPU的数据上传; 通过

Renderer(渲染器)获取材质, 利用材质获取Pass(渲染通道), 将渲染所需的数据提前上传到GPU, 可以有效避免首次渲染时

造成的卡顿

a.该方法的优点:

- 不需要创建隐藏相机, 直接使用CommandBuffer提交绘制命令

- 用很小的RT(默认64 × 64)减少性能消耗

b.并不是所有pass都需要预热, 通常只需要预热那些在运行时可能首次渲染会导致卡顿的pass, 比如:

- 阴影相关pass(ShadowCaster)

- 复杂的光照pass(ForwardAdd)

- 自定义的后期处理pass
csharp 复制代码
/// <summary>
/// 对指定 Renderer 使用 CommandBuffer 进行一次离屏渲染(哑渲染),
/// 以触发指定 Pass 的 Shader 编译、资源上传等预热操作。
/// </summary>
/// <param name="r">需要预热的 Renderer(其材质会被用来渲染)</param>
/// <param name="passName">Shader Pass 名(如 "ForwardBase", "ShadowCaster" 等)</param>
/// <param name="width">临时 RenderTexture 宽度(默认 64),越小越节约性能</param>
/// <param name="height">临时 RenderTexture 高度(默认 64),越小越节约性能</param>
public static void WarmupRenderer(Renderer r, string passName, int width = 64, int height = 64)
{
    // 获取 Renderer 使用的共享材质(不会实例化)
    Material mat = r.sharedMaterial;

    // 查找该材质中指定 Pass 的索引
    int pass = mat.FindPass(passName);
    if (pass < 0)
    {
        // 如果没找到该 Pass,打印警告并退出
        Debug.LogWarning($"Pass 没有找到: {passName}");
        return;
    }
	
	// 1. 新建一个"GPU任务清单"(CommandBuffer)
	// 就像你写一张购物清单,上面列好要GPU做的所有事,批量执行更高效
    
    // 创建一个 CommandBuffer(命令缓冲区),用来批量提交 GPU 绘制指令
    // name名字可以随意自定义,它的作用只是在调试工具中可以显示出来,方便你知道这个缓冲区的作用
    CommandBuffer cb = new CommandBuffer { name = $"Warmup Renderer:{r.name}:{passName}" };

	// 2.给"临时画布"编一个唯一编号(tempID)
	// 相当于给一张临时画纸分配一个唯一的工号,方便GPU快速找到它,不用靠字符串"喊名字"(效率更高)
    
    // 为临时 RenderTexture 申请一个 ID(Shader 属性 ID)
    // 把字符串转换成一个唯一的整数ID,这个字符串也可以自定义
    int tempID = Shader.PropertyToID("_WarmupRT");

    // 3. 申请一张"临时画布"(临时RenderTexture)
	// 向Unity借一张指定大小、格式的空白画纸,用完后Unity会自动回收,不用你手动清理
    
    // 申请一个临时 RenderTexture(指定宽高、无 MSAA、ARGB32 格式)
    cb.GetTemporaryRT(tempID, width, height, 0, FilterMode.Point, RenderTextureFormat.ARGB32);

	// 4. 告诉GPU:接下来的绘制,都画在这张临时画布上
    // 设置该临时 RenderTexture 作为渲染目标
    cb.SetRenderTarget(tempID);

	// 5. 先把画布擦干净(清空颜色和深度信息)
	// 就像画画前先把画纸擦白,避免有残留的污渍影响效果
    
    // 清空 RenderTexture(清颜色 & 深度)
    cb.ClearRenderTarget(true, true, Color.clear);

	// 6. 告诉GPU:把指定的模型(Renderer),用指定的材质,画在这张画布上
	// 相当于让GPU"空画一遍"模型,重点不是画出好看的图,而是让GPU熟悉这个绘制流程
    
    // 7. 立刻把这份"任务清单"交给GPU,让GPU马上执行
	// 这一步是关键,相当于你把购物清单交给店员,店员开始按清单备货

    // 使用该 Renderer 和材质,使用指定索引子网格,指定索引渲染通道 进行绘制
    cb.DrawRenderer(r, mat, 0, pass);

    // 立即执行这个 CommandBuffer(提交给 GPU)
    // 关键步骤
    Graphics.ExecuteCommandBuffer(cb);

    // 释放临时 RenderTexture 资源(避免显存泄漏)
    cb.ReleaseTemporaryRT(tempID);

    // 释放命令缓冲区本身
    cb.Release();
}
csharp 复制代码
注意事项:

预加载会增加内存占用(GPU内存)和加载时间, 所以需要权衡

不是所有资源都需要预加载, 通常只预加载那些在游戏过程中肯定会用到的、且较大的资源("加载耗时大于3ms")
相关推荐
呆呆敲代码的小Y8 小时前
【Unity 实用工具篇】 | Book Page Curl 快速实现翻书效果
游戏·unity·游戏引擎·u3d·免费游戏·翻书插件
AC梦21 小时前
unity中如何将UI上的字高清显示
ui·unity
小贺儿开发1 天前
Unity3D 智慧城市管理平台
数据库·人工智能·unity·智慧城市·数据可视化
June bug2 天前
【领域知识】休闲游戏一次发版全流程:Google Play + Apple App Store
unity
星夜泊客2 天前
C# 基础:为什么类可以在静态方法中创建自己的实例?
开发语言·经验分享·笔记·unity·c#·游戏引擎
dzj20212 天前
PointerEnter、PointerExit、PointerDown、PointerUp——鼠标点击物体,则开始旋转,鼠标离开或者松开物体,则停止旋转
unity·pointerdown·pointerup
心前阳光2 天前
Unity 模拟父子关系
android·unity·游戏引擎
在路上看风景2 天前
26. Mipmap
unity
咸鱼永不翻身2 天前
Unity视频资源压缩详解
unity·游戏引擎·音视频
在路上看风景2 天前
4.2 OverDraw
unity