前言:

先表上文章来源,我仅作为笔记,自己添加一些重点和扩展。
作者:LetMe_See
链接:https://www.jianshu.com/p/fa73c0f6762d
来源:简书

什么是RenderTexture?

  • RenderTexture是unity定义的一种特殊的Texture类型,它是连接着一个FrameBufferObject(帧缓冲区对象)的存在于GPU端的Texture(Server-Side Texture),从上面对RenderTexture的解释我们了解到要先知道Texture和FrameBufferObject是什么。

    什么是Texture

  • 中文翻译叫纹理,先说一下一个纹理是如何被渲染到屏幕上的 ,起初纹理存在硬盘(RAM)里,它被cpu解压缩(数据在cpu端它就只是二进制数据),如果想要显示它,那么数据将会被发送给(上传到,cpu和gpu之间的通信可以理解成client和server之间的通信)GPU,gpu将它放在显存(VARM)中,显存中有一块内存区域叫做RenderBuffer(渲染缓存),RenderBuffer只是数据缓存,它还不能用作Texture渲染,尽管它现在已经是一个texture了,在这里 texture等待着被渲染,当要渲染这个Texture时,会生成一个FrameBuffer(帧缓存),当这个帧缓存被添加到默认的帧缓存物体上(FrameBufferObject)时,它就会被绘制到屏幕上.

  • FrameBuffer指向的是显存中RenderBuffer的地址,简单的来说,RenderBuffer需要附加在FrameBuffer上,它才能是五颜六色的图片,否则它只是显存上的一堆数据,关于RenderBuffer和FrameBuffer可以看这些文章:
  • 简单的几句话描述的渲染过程其实非常复杂耗时,所幸有许多框架精心主导着这部分数据传输,微软有DirectX,苹果有Metal ,还有OpenGL,WebGL等.在unity中通过调用Graphic.Blit()来渲染一个Texture.

    什么是FrameBufferObject?

  • 可以理解FrameBufferObject是一个集合,集合了FrameBuffer,通过快速刷新Framebuffer实现动态效果,最典型的FBO就是Unity的Main Camera,它是默认的FBO,是gpu里渲染结果的目的地.但是现代gpu通常可以创建很多其他的FBO(Unity中可以创建多个Camera),这些FBO不连接窗口区域,这种我们创建的FBO的存在目的就是允许我们将渲染结果保存在gpu的一块存储区域,待之后使用,这种用法叫做离屏渲染,这是一个非常有用的东西。Camera 输出的FBO,可以嵌在另一个FBO中,Unity中使用RenderTexture来接收FBO(可视化FBO),Game窗口是一个特殊的RenderTexture,它允许多个FBO叠加渲染,当Camera的RenderTarget都设置为null时表示输出到game窗口(没有摄像机的RenderTaget为null会显示没有摄像机进行渲染),设置不为null表示输出到某个RT.

    如何使用FrameBufferObject?

  • 1.将这个FBO上的结果传回CPU这边的贴图,在gles中的实现一般是ReadPixels()(读取像素?)这样的函数,这个函数是将当前设为可读的FBO拷贝到cpu这边的一个存储buffer。没错,如果当前设为可读的FBO是那个默认FBO,那这个函数就是在截屏,如果是你自己创建的FBO,那就把刚刚绘制到上面的结果从gpu存储拿回内存。

    1. 将这个FBO上的结果拷贝到一个gpu上的texture,在gles中的实现一般是CopyTexImage2D(),它一般是将可读的FBO的一部分拷贝到存在于gpu上的一个texture对象中,直接考到server-sider就意味着可以马上被gpu渲染使用
  • 3.将这个fbo直接关联一个gpu上的texture对象,这样就等于在绘制时就直接绘制到这个texure上,这样也省去了拷贝时间,gles中一般是使用FramebufferTexture2D()这样的接口。

    unity是如何使用FBO的?

  • Unity通过上面说的第三个方法将FBO输出到RenderTexture,在unity里要使用这个FBO,只能基于这个RenderTexture(目前我知道的是这样,可能有我不知道的用法)。

  • 在Unity固定渲染管线中(Unity2019.3以后 自定义渲染管线脱离预览版,新的通用渲染管线Camera设置发生了改变,如果依然使用固定渲染管线则以下通用),通过Camera组件来使用FBO,多摄像机使用下,根据ClearFlags来决定渲染内容:
  • image.png
  • 需要强调的是Clear操作, 多Camera下,DepthOnly 和Don’t Clear实际上都使Clear操作失效了 ,场景中会渲染多个摄像机的渲染内容

    RenderTexture的用途?

  • 1.屏幕后处理,3d游戏最基本的后处理是抗锯齿,从Unity的FrameDebugger(可以看到所有FrameBuffer,不管它们属于哪个FBO)中可以看到抗锯齿的操作在OverlayUI之前,所以各位做2d游戏的可以选择把抗锯齿关掉,其他的后处理如bloom,HDR等都是操作屏幕这个默认的RenderTexture,配合上相关效果的Material

  • 2.在Scene中直接将RT作为Texture传给其他材质球,操作是调用Material.SetTexture 为该RT,即可实现在另一个表面渲染另一个Camera的内容.可以制作后视镜功能
  • 3.copy回cpu端的内存:基本操作是在当前帧渲染完毕后(协程中, yield return new WaitForEndOfFrame()),设置RenderTexture.active为目标RenderTexture(因为当前帧已渲染过,所以该RenderTexture不会被渲染).Texture.ReadPixels保存到显存.Texture.GetRawTextureData()读回cpu内存,可以保存到硬盘或者通过互联网通信(在unity中实现的截屏,录屏,实时共享屏幕).
  • 以上2,3都属于离屏渲染的应用.

    RenderTexture的注意事项

  • 1.rendertexture的分配和销毁,如果你频繁的要new一个rt出来,那么不要直接new,而是使用RenderTexture提供的GetTemporary和ReleaseTemporary,它将在内部维护一个池,反复重用一些大小格式一样的rt资源,因为让gpu为你分配一个新的tex其实是要耗时间的。

  • 2.在将RT拷会cpu的过程中,帧数下降较多,在unity Profiler中发现Texture.ReadPixel()耗时很多,推测该方法效率很低,使用

    1. System.Diagnostics.Stopwatch sw = new System.Diagnostics.Stopwatch();
    2. sw.Start();
    3. //待测试的代码
    4. sw.Stop(); sw.Elapsed能够显示函数耗时是普通函数的700
  • 优化方案:方法耗时的原因大概是像素点太多了,降低输出的texture分辨率可以减少像素. Camera output到的RenderTexture,可以通过指定一个降低的分辨率的RenderTexture(注意!!!用于接收该renderTexture的textrue2d分辨率必须和它保持一致),猜测这也是性能较低的机器无法进行高分辨率音视频的原因,另一方面,Game窗口的RenderTexture虽然一直存在于GPU端,但是降低分辨率也能减轻gpu的压力,通过Screen.SetResolution实现.

    什么是CommandBuffer

  • 上一篇讲了Unity中的RenderTexture,提到了FrameBuffer,unity中可以通过FrameDebugger来查看FrameBuffer的内容,CommandBuffer通过字面意思 就是一条命令 ,unity提供给开发者的一个自定义的渲染命令,依赖相机,用来拓展渲染管线的渲染效果,我们可以根据参数指定CommandBuffer在绿色的点上添加执行命令:

  • image.png
  • unity中调用的枚举是:
  • image.png
  • CommandBuffer的使用:
    • 新建一个cube,使用不透明的红色的材质球,命名为RedCube,添加TestCommand.cs,拖进去绿色的材质球GreenMat,和RedCube:
  • image.png
  • Camera调用AddCommandBuffer来把指定的指令塞到自己的渲染步骤中,需要注意多Camera下CommandBuffer的绑定.
    • 未运行状态:
    • image.png
  • 运行游戏,可以看到RedCube是红色的,背景是黑色的
  • image.png
  • 打开FrameDebuger可以看到,根据CameraEvent的设置,插入的commandBuffer设置在BeforeForwardOpaque,所以绿色渲染完被clear成黑色,然后红色cube最后渲染,
  • image.png
  • 如果CameraEvent设置为AfterForwardOpaque,Clear操作放在渲染green之前

image.png

  • 画面为,先渲染了红色cube,然后调用了Clear变成了黑色,最后渲染了green.
    image.png
  • FrameDebugger画面