什么是RenderTexture?
RenderTexture是unity定义的一种特殊的Texture类型,它连接着一个FrameBufferObject的存在于GPU端的Texture(Server-Side Texture)从上面对RenderTexture的解释我们了解到要先知道Texture和FrameBufferObject是什么
什么是Texture?
中文翻译叫纹理,先说一下一个纹理是如何被渲染到屏幕上的 ,起初纹理存在硬盘(RAM)里,它被cpu解压缩,如果想要显示它,那么数据将会被发送给(上传到,cpu和gpu之间的通信可以理解成client和server之间的通信)GPU,gpu将它放在显存(VARM)中,显存中有一块内存区域叫做RenderBuffer(渲染缓存),RenderBuffer只是数据缓存,它还不能用作Texture渲染,尽管它现在已经是一个texture了,在这里 texture等待着被渲染,当要渲染这个Texture时,会生成一个FrameBuffer(帧缓存),当这个帧缓存被添加到默认的帧缓存物体上(FrameBufferObject)时,它就会被绘制到屏幕上.FrameBuffer指向的是显存中RenderBuffer的地址,简单的来说,RenderBuffer需要附加在FrameBuffer上,它才能是五颜六色的图片,否则它只是显存上的一堆数据,关于RenderBuffer和FrameBuffer可以看这些文章:
OpenGL ES学习之路(3.1) 着色器渲染过程、渲染方式、FrameBuffer与RenderBuffer - 简书
简单的几句话描述的渲染过程其实非常复杂耗时,所幸有许多框架精心主导着这部分数据传输,微软有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,当Camera的RenderTarget设置为null时表示输出到game窗口(没有摄像机的RenderTaget为null会显示没有摄像机进行渲染),设置不为null表示输出到某个RT.
如何使用FrameBufferObject?
1.将这个FBO上的结果传回CPU这边的贴图,在gles中的实现一般是ReadPixels()这样的函数,这个函数是将当前设为可读的FBO拷贝到cpu这边的一个存储buffer,没错如果当前设为可读的FBO是那个默认FBO,那这个函数就是在截屏,如果是你自己创建的FBO,那就把刚刚绘制到上面的结果从gpu存储拿回内存。
2. 将这个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来决定渲染内容:
需要强调的是Clear操作, 多Camera下,DepthOnly 和Don't Clear实际上都使Clear操作失效了 ,场景中会渲染多个摄像机的渲染内容
RenderTexture的用途?
1.屏幕后处理,3d游戏最基本的后处理是抗锯齿,从Unity的FrameDebugger中可以看到抗锯齿的操作在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()耗时很多,推测该方法效率很低,使用
System.Diagnostics.Stopwatch sw = new System.Diagnostics.Stopwatch();
sw.Start();
//待测试的代码
sw.Stop(); sw.Elapsed能够显示函数耗时是普通函数的700倍
优化方案:方法耗时的原因大概是像素点太多了,降低输出的texture分辨率可以减少像素. Camera output到的RenderTexture,可以通过指定一个降低的分辨率的RenderTexture(注意!!!用于接收该renderTexture的textrue2d分辨率必须和它保持一致),猜测这也是性能较低的机器无法进行高分辨率音视频的原因,另一方面,Game窗口的RenderTexture虽然一直存在于GPU端,但是降低分辨率也能减轻gpu的压力,通过Screen.SetResolution实现.