什么是RenderTexture?
RenderTexture是unity定義的一種特殊的Texture類型,它是連接著一個FrameBufferObject的存在于GPU端的Texture(Server-Side Texture),從上面對RenderTexture的解釋我們了解到要先知道Texture和FrameBufferObject是什么
什么是Texture?
中文翻譯叫紋理,先說一下一個紋理是如何被渲染到屏幕上的 ,起初紋理存在硬盤(RAM)里,它被cpu解壓縮(數據在cpu端它就只是二進制數據),如果想要顯示它,那么數據將會被發(fā)送給(上傳到,cpu和gpu之間的通信可以理解成client和server之間的通信)GPU,gpu將它放在顯存(VARM)中,顯存中有一塊內存區(qū)域叫做RenderBuffer(渲染緩存),RenderBuffer只是數據緩存,它還不能用作Texture渲染,盡管它現在已經是一個texture了,在這里 texture等待著被渲染,當要渲染這個Texture時,會生成一個FrameBuffer(幀緩存),當這個幀緩存被添加到默認的幀緩存物體上(FrameBufferObject)時,它就會被繪制到屏幕上.FrameBuffer指向的是顯存中RenderBuffer的地址,簡單的來說,RenderBuffer需要附加在FrameBuffer上,它才能是五顏六色的圖片,否則它只是顯存上的一堆數據,關于RenderBuffer和FrameBuffer可以看這些文章:
Linux OpenGL 實踐篇-10-framebuffer - 北冥有魚其名為鯤 - 博客園
OpenGL ES學習之路(3.1) 著色器渲染過程邪意、渲染方式、FrameBuffer與RenderBuffer - 簡書
簡單的幾句話描述的渲染過程其實非常復雜耗時,所幸有許多框架精心主導著這部分數據傳輸,微軟有DirectX,蘋果有Metal ,還有OpenGL,WebGL等.在unity中通過調用Graphic.Blit()來渲染一個Texture.
什么是FrameBufferObject?
????????可以理解FrameBufferObject是一個集合,集合了FrameBuffer,通過快速刷新Framebuffer實現動態(tài)效果,最典型的FBO就是Unity的Main Camera,它是默認的FBO,是gpu里渲染結果的目的地.但是現代gpu通初蹇眨可以創(chuàng)建很多其他的FBO(Unity中可以創(chuàng)建多個Camera)引几,這些FBO不連接窗口區(qū)域高氮,這種我們創(chuàng)建的FBO的存在目的就是允許我們將渲染結果保存在gpu的一塊存儲區(qū)域,待之后使用,這種用法叫做離屏渲染,這是一個非常有用的東西宜岛。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毫别,那這個函數就是在截屏娃弓,如果是你自己創(chuàng)建的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設置發(fā)生了改變,如果依然使用固定渲染管線則以下通用),通過Camera組件來使用FBO,多攝像機使用下,根據ClearFlags來決定渲染內容:
需要強調的是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端的內存:基本操作是在當前幀渲染完畢后(協(xié)程中, 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中發(fā)現Texture.ReadPixel()耗時很多,推測該方法效率很低,使用
System.Diagnostics.Stopwatch sw = new System.Diagnostics.Stopwatch();
? ? ? ? sw.Start();
//待測試的代碼
sw.Stop(); sw.Elapsed能夠顯示函數耗時是普通函數的700倍
優(yōu)化方案:方法耗時的原因大概是像素點太多了,降低輸出的texture分辨率可以減少像素.?Camera output到的RenderTexture,可以通過指定一個降低的分辨率的RenderTexture(注意!!!用于接收該renderTexture的textrue2d分辨率必須和它保持一致),猜測這也是性能較低的機器無法進行高分辨率音視頻的原因,另一方面,Game窗口的RenderTexture雖然一直存在于GPU端,但是降低分辨率也能減輕gpu的壓力,通過Screen.SetResolution實現.