【Unity】使用RenderTexture為物體生成快照


 

版權聲明:本文為博主原創文章,未經博主允許不得轉載。

作者:Jimm          郵箱:junmingz@foxmail.com

 


RenderTexture的定義和作用

RenderTexture are textures that can be rendered to.

RenderTexture(下文簡稱RTT)是可以被渲染的紋理,簡稱渲染紋理。一般來說,RTT可以應用在制作動態陰影,反射以及監視攝像機(車輛后視鏡)等,另一方面可以應用到游戲截圖,背景模糊等方面,用途十分廣泛。以后這些技術都會慢慢分享到博客上,敬請期待!


RTT的用法

Camera攝像機)是Unity中非常重要的一個組件,其中有一個屬性叫做targetTexture,在設置了targetTexture后,Camera會在渲染時將其屏幕上的圖像渲染到targetTexture上。在相機渲染完成后可以讀取屏幕像素內的緩存來使用。其中,相機渲染完成有三種調用方式:

1.OnPostRender()

OnPostRender is called after a camera finished rendering the scene.

OnPostRender在相機完成渲染場景時調用。這次遇到的需求是需要為物體生成快照,做法是另外創建一個相機,在另一個位置完成渲染工作,代碼如下:

 

//快照相機
public Camera shotCam; public UITexture texture; void OnPostRender() { //設定當前RenderTexture為快照相機的targetTexture 
    RenderTexture rt = shotCam.targetTexture; RenderTexture.active = rt; Texture2D tex = new Texture2D(rt.width, rt.height); //讀取緩沖區像素信息 
    tex.ReadPixels(new Rect(0, 0, rt.width, rt.height), 0, 0); tex.Apply(); texture.mainTexture = tex; Texture2D.Destroy(tex); tex = null; }
//這里刪除的時機有問題,會導致不顯示相機渲染的圖像問題
//謝謝一位好心讀者提醒,改正后的代碼在下方
 

 

 修正后的代碼:

public Camera shotCam;
public UITexture texture;
private Texture2D tex = null;
void OnPostRender()
{
//在每次相機渲染完成時再刪除上一幀的texture
if(tex != null) { Destroy(tex); } //設定當前RenderTexture為快照相機的targetTexture RenderTexture rt = shotCam.targetTexture; RenderTexture.active = rt; tex = new Texture2D(rt.width, rt.height); //讀取緩沖區像素信息 tex.ReadPixels(new Rect(0, 0, rt.width, rt.height), 0, 0); tex.Apply(); texture.mainTexture = tex; }

 

場景中的效果如下:

QQ截圖20161012000908

2.使用協程(yield return new WaitForEndOfFrame())

yield return new WaitForEndOfFrame()

等待當前幀結束。類似於OnPostRender(),可以使用for循環來依次對多個物體進行快照,代碼如下:

public GameObject[] gos;
void Start()
{
    StartCoroutine(RenderGoTexCR());
}
IEnumerator RenderGoTexCR()
{
    int length = textures.Length;
    for (int i = 0; i < length; i++)
    {
        GameObject go = Instantiate(gos[i]);
        go.SetActive(true);
        yield return new WaitForEndOfFrame();
        RenderTexture rt = shotCam.targetTexture;
        RenderTexture.active = rt;
        Texture2D tex = new Texture2D(rt.width, rt.height, TextureFormat.ARGB32, false);
        tex.ReadPixels(new Rect(0, 0, rt.width, rt.height), 0, 0);
        tex.Apply();
        textures[i].mainTexture = tex;
        GameObject.Destroy(go);
    }
}

PS:yield語句要放在設置RenderTexture.active之前,因為只有在幀結束時shotCam的targetTexture才被正確渲染,才可以通過ReadPixels取得正確的圖像。tex在使用過后最好使用Destroy()銷毀,或者在設置mainTexture之前銷毀之前的Texture,否則就會像博主一樣寫着寫着博客發現Unity運行了一段時間就把內存吃光了哭泣的臉

場景效果如下:

QQ截圖20161012012735

3.使用Camera.Render()

我們發現當要對多個物體進行快照時OnPostRender()就沒那么好用了,因為我們需要在相機渲染前將物體展示出來(OnPreRender()),在相機渲染后取得像素信息,對於兩個函數分別處理GameObject我們是拒絕的(程序員就喜歡簡單粗暴!)。那么使用協程又有什么問題呢?細心的同學會發現,每次渲染一個物體都需要等到幀結束,那么當需要渲染的物體較多時渲染時間會明顯變長,這顯然也不是我們想要的,那么有沒有能在很短時間內完成大量物體的渲染呢?Camera.Render()給了我們福音。

Camera.Render()

手動渲染相機。廢話不多說,貼代碼:

public GameObject[] gos;
void Start()
{
    for (int i = 0; i < textures.Length; i++)
    {
        GameObject go = Instantiate(gos[i]);
        go.SetActive(true);
        textures[i].mainTexture = RenderGoTex();
        GameObject.Destroy(go);
    }
}
Texture2D RenderGoTex()
{
    RenderTexture rt = shotCam.targetTexture;
    shotCam.Render();
    RenderTexture.active = rt;
    Debug.Log(RenderTexture.active);
    Texture2D tex = new Texture2D(rt.width, rt.height, TextureFormat.ARGB32, false);
    tex.ReadPixels(new Rect(0, 0, rt.width, rt.height), 0, 0);
    tex.Apply();
    return tex;
}

Camera.Render()無需等待幀結束,它在調用時強制渲染相機,通過返回的tex進行操作即可,同樣不要忘了Texture的內存問題,場景中效果如下:

QQ截圖20161012012830

咦,怎么會發生重疊現象呢,明明在取得tex之后調用了Destroy()函數呀!這個也困擾了我一段時間,后來發現是Destroy函數的延遲問題。Destroy()函數對實際物體的銷毀會延遲到當前循環更新后,在渲染前完成的,所以我們在這一幀執行for循環時,雖然每次循環都調用了Destroy()來銷毀物體,但是實際上它們是在for循環結束后才一起銷毀的,所以為了避免此問題,我們改用DestroyImmediate(),或者先調用gameObject.SetActive(false)后再Destroy(),后者是比較推薦的做法,原因請看官方文檔

https://docs.unity3d.com/ScriptReference/Object.DestroyImmediate.html


結尾語

博主只是初入江湖的小菜,最近萌生寫博客的想法,希望能將自己在學習和工作中遇到的問題以及所感所想與大家分享,同時也是對自我的總結。最后感謝大家的支持,你們的支持就是我的動力!



免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM