关于Unity的截屏方法


序:开始决定写东西,内容不限,关于工作内容的,关于自己业余研究的,关于生活的...因为只有写下来,思路才会更清晰,印象才会更深刻,更可以Share。ps:我是个女码农,现在从事Unity游戏、应用的开发。不要试图问一个女生为什么选择这个职业,做了就是做了,并且要做好。


Ok,well这次简短点,我最近碰到一个关于Unity截屏的问题。用过的人很快会想到两种截屏的方式:

  1. Application.CaptureScreenshot(string file name),这会截取当前屏幕保存为png格式,存放在Application.dataPath(PC/Mac),Application.persistantDataPath(移动设备)。如果需要获取图片用作显示,或保存在系统照片集下,就需要通过www的方式读取后再做处理,不是很方便。

  2. Texture2D.ReadPixels(Rect source, int destX, int destY, bool recaculateMipmaps=true), 这会读取指定区域内的屏幕像素至一张texture中,用于UI显示、保存等。相比方法一更方便直观。我们大多数都是采用该方法。

但是,最近碰到的问题是,在一些低端性能差点的Android设备上,用以上两种方式截出来的图是黑色(像素值全是0),用了coroutine方式WaitEndOfFrame,试图修改Texture2D的format、size,放在Monobehavior.OnPostRender()中...试过想到的各种方法均无效,也没有报warning、error。就想到尝试用第三种方式来实现:RenderTexture。

通过将Camera的内容渲染到一张RT中,然后用Texture2D.ReadPixels()方法从当前的RT中读取,结果可行!代码如下:

public static Texture2D CaptureCamera(Rect rect,  params Camera[] cameras)
{
    RenderTexture rt = new RenderTexture ((int)Screen.width, (int)Screen.height, 24);
    //临时设置相关相机的targetTexture为rt, 并手动渲染相关相机
    for (int i=0; i<cameras.Length; i++)
    {
        if(cameras[i] != null)
        {
            cameras[i].targetTexture=rt;
            cameras[i].Render();
        }
    }

   //激活这个rt, 并从中中读取像素。
   RenderTexture.active = rt;
   Texture2D screenShot = new Texture2D((int)rect.width, (int)rect.height, TextureFormat.RGB24, false);
   screenShot.ReadPixels(rect, 0, 0);   //注:这个时候,它是从RenderTexture.active中读取像素
   screenShot.Apply();
   
   //重置相关参数,以使用camera继续在屏幕上显示
   for(int i=0; i<cameras.Length; i++)
   {
       if(cameras[i] !=null)
          cameras[i].targetTexture = null;
   }
            
   RenderTexture.active = null;
   GameObject.Destroy(rt);

   return screenShot;
}

分析原因,有如下推断:Android渲染采用了双缓冲,而前两种方式是从绘制到当前屏幕的framebuffer中读数据,当设备运行慢时,在Unity调用接口所读到的数据是从正在写入的framebuffer里面的,还没有swap送入屏幕显示,所以读到的像素值为0。采用RenderTexture方式将Camera绘制内容写入texture中,从该texture中读取数据能够解决上述问题,确保数据有效。
这是我的一个推断,可能是其他原因,搜索以及debug无果,希望以后的开发中能彻底知晓原因。

推荐阅读更多精彩内容