Unity中RenderTexture詳解以及它的用途

什么是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可以看這些文章:

Linux OpenGL 實踐篇-10-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通??梢詣摻ê芏嗥渌腇BO(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存儲拿回內存。

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(可以看到所有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提供的GetTemporaryReleaseTemporary,它將在內部維護一個池,反復重用一些大小格式一樣的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實現.

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容