什么是RenderTexture?
RenderTexture是unity定義的一種特殊的Texture類型,它是連接著一個FrameBufferObject的存在于GPU端的Texture(Server-Side Texture),從上面對RenderTexture的解釋我們了解到要先知道Texture和FrameBufferObject是什么
什么是Texture?
中文翻譯叫紋理,先說一下一個紋理是如何被渲染到屏幕上的 ,起初紋理存在硬盤(RAM)里,它被cpu解壓縮(數(shù)據(jù)在cpu端它就只是二進(jìn)制數(shù)據(jù)),如果想要顯示它,那么數(shù)據(jù)將會被發(fā)送給(上傳到,cpu和gpu之間的通信可以理解成client和server之間的通信)GPU,gpu將它放在顯存(VARM)中,顯存中有一塊內(nèi)存區(qū)域叫做RenderBuffer(渲染緩存),RenderBuffer只是數(shù)據(jù)緩存,它還不能用作Texture渲染,盡管它現(xiàn)在已經(jīng)是一個texture了,在這里 texture等待著被渲染,當(dāng)要渲染這個Texture時,會生成一個FrameBuffer(幀緩存),當(dāng)這個幀緩存被添加到默認(rèn)的幀緩存物體上(FrameBufferObject)時,它就會被繪制到屏幕上.FrameBuffer指向的是顯存中RenderBuffer的地址,簡單的來說,RenderBuffer需要附加在FrameBuffer上,它才能是五顏六色的圖片,否則它只是顯存上的一堆數(shù)據(jù),關(guān)于RenderBuffer和FrameBuffer可以看這些文章:
Linux OpenGL 實(shí)踐篇-10-framebuffer - 北冥有魚其名為鯤 - 博客園
OpenGL ES學(xué)習(xí)之路(3.1) 著色器渲染過程、渲染方式、FrameBuffer與RenderBuffer - 簡書
簡單的幾句話描述的渲染過程其實(shí)非常復(fù)雜耗時,所幸有許多框架精心主導(dǎo)著這部分?jǐn)?shù)據(jù)傳輸,微軟有DirectX,蘋果有Metal ,還有OpenGL,WebGL等.在unity中通過調(diào)用Graphic.Blit()來渲染一個Texture.
什么是FrameBufferObject?
????????可以理解FrameBufferObject是一個集合,集合了FrameBuffer,通過快速刷新Framebuffer實(shí)現(xiàn)動態(tài)效果,最典型的FBO就是Unity的Main Camera,它是默認(rèn)的FBO,是gpu里渲染結(jié)果的目的地.但是現(xiàn)代gpu通常可以創(chuàng)建很多其他的FBO(Unity中可以創(chuàng)建多個Camera),這些FBO不連接窗口區(qū)域,這種我們創(chuàng)建的FBO的存在目的就是允許我們將渲染結(jié)果保存在gpu的一塊存儲區(qū)域,待之后使用,這種用法叫做離屏渲染,這是一個非常有用的東西。Camera 輸出的FBO,可以嵌在另一個FBO中,Unity中使用RenderTexture來接收FBO(可視化FBO),Game窗口是一個特殊的RenderTexture,它允許多個FBO疊加渲染,當(dāng)Camera的RenderTarget都設(shè)置為null時表示輸出到game窗口(沒有攝像機(jī)的RenderTaget為null會顯示沒有攝像機(jī)進(jìn)行渲染),設(shè)置不為null表示輸出到某個RT.
如何使用FrameBufferObject?
1.將這個FBO上的結(jié)果傳回CPU這邊的貼圖,在gles中的實(shí)現(xiàn)一般是ReadPixels()這樣的函數(shù),這個函數(shù)是將當(dāng)前設(shè)為可讀的FBO拷貝到cpu這邊的一個存儲buffer,沒錯如果當(dāng)前設(shè)為可讀的FBO是那個默認(rèn)FBO,那這個函數(shù)就是在截屏,如果是你自己創(chuàng)建的FBO,那就把剛剛繪制到上面的結(jié)果從gpu存儲拿回內(nèi)存。
2.?將這個FBO上的結(jié)果拷貝到一個gpu上的texture,在gles中的實(shí)現(xiàn)一般是CopyTexImage2D(),它一般是將可讀的FBO的一部分拷貝到存在于gpu上的一個texture對象中,直接考到server-sider就意味著可以馬上被gpu渲染使用
3.將這個fbo直接關(guān)聯(lián)一個gpu上的texture對象,這樣就等于在繪制時就直接繪制到這個texure上,這樣也省去了拷貝時間,gles中一般是使用FramebufferTexture2D()這樣的接口。
unity是如何使用FBO的?
Unity通過上面說的第三個方法將FBO輸出到RenderTexture,在unity里要使用這個FBO,只能基于這個RenderTexture(目前我知道的是這樣,可能有我不知道的用法).
在Unity固定渲染管線中(Unity2019.3以后 自定義渲染管線脫離預(yù)覽版,新的通用渲染管線Camera設(shè)置發(fā)生了改變,如果依然使用固定渲染管線則以下通用),通過Camera組件來使用FBO,多攝像機(jī)使用下,根據(jù)ClearFlags來決定渲染內(nèi)容:

需要強(qiáng)調(diào)的是Clear操作, 多Camera下,DepthOnly 和Don't Clear實(shí)際上都使Clear操作失效了 ,場景中會渲染多個攝像機(jī)的渲染內(nèi)容
RenderTexture的用途?
1.屏幕后處理,3d游戲最基本的后處理是抗鋸齒,從Unity的FrameDebugger(可以看到所有FrameBuffer,不管它們屬于哪個FBO)中可以看到抗鋸齒的操作在OverlayUI之前,所以各位做2d游戲的可以選擇把抗鋸齒關(guān)掉,其他的后處理如bloom,HDR等都是操作屏幕這個默認(rèn)的RenderTexture,配合上相關(guān)效果的Material?
2.在Scene中直接將RT作為Texture傳給其他材質(zhì)球,操作是調(diào)用Material.SetTexture 為該RT,即可實(shí)現(xiàn)在另一個表面渲染另一個Camera的內(nèi)容.可以制作后視鏡功能
3.copy回cpu端的內(nèi)存:基本操作是在當(dāng)前幀渲染完畢后(協(xié)程中, yield return new WaitForEndOfFrame()),設(shè)置RenderTexture.active為目標(biāo)RenderTexture(因?yàn)楫?dāng)前幀已渲染過,所以該RenderTexture不會被渲染).Texture.ReadPixels保存到顯存.Texture.GetRawTextureData()讀回cpu內(nèi)存,可以保存到硬盤或者通過互聯(lián)網(wǎng)通信(在unity中實(shí)現(xiàn)的截屏,錄屏,實(shí)時共享屏幕).
以上2,3都屬于離屏渲染的應(yīng)用.
RenderTexture的注意事項(xiàng)
1.rendertexture的分配和銷毀,如果你頻繁的要new一個rt出來,那么不要直接new,而是使用RenderTexture提供的GetTemporary和ReleaseTemporary,它將在內(nèi)部維護(hù)一個池,反復(fù)重用一些大小格式一樣的rt資源,因?yàn)樽実pu為你分配一個新的tex其實(shí)是要耗時間的。
2.在將RT拷會cpu的過程中,幀數(shù)下降較多,在unity Profiler中發(fā)現(xiàn)Texture.ReadPixel()耗時很多,推測該方法效率很低,使用
System.Diagnostics.Stopwatch sw = new System.Diagnostics.Stopwatch();
? ? ? ? sw.Start();
//待測試的代碼
sw.Stop(); sw.Elapsed能夠顯示函數(shù)耗時是普通函數(shù)的700倍
優(yōu)化方案:方法耗時的原因大概是像素點(diǎn)太多了,降低輸出的texture分辨率可以減少像素.?Camera output到的RenderTexture,可以通過指定一個降低的分辨率的RenderTexture(注意!!!用于接收該renderTexture的textrue2d分辨率必須和它保持一致),猜測這也是性能較低的機(jī)器無法進(jìn)行高分辨率音視頻的原因,另一方面,Game窗口的RenderTexture雖然一直存在于GPU端,但是降低分辨率也能減輕gpu的壓力,通過Screen.SetResolution實(shí)現(xiàn).