分享

解读Texture2D.ReadPixels

 雪柳花明 2016-11-01
ok… 就觉得有什么东西落下忘了记录,直到刚刚才想起,然后就马不停蹄的来到博客准备记录。 

同样,大神勿喷,不喜勿喷,欢迎拍砖,而且是狠狠的拍。

那好,如题,这次我们就来说说2D纹理类下边的读取像素并将之保存为纹理的方法,那为什么要讲这个方法呢?我是觉得这个函数的参数值的去理解,因为,倘若有的读者去做截屏处理的时候,这几个参数的把我尤为重要,当然,有的读者可能会说到Unity中的截屏直接可以用Application中的一个CaptureScreenshot ()方法,然后就完事,那么我想说的是,这个没错,可是这样的处理会将整个Game视图都截取出来,那有些情况我们并不想这样,只是想将Game视图的局部截取出来,那么这个情况下使用CaptureScreenshot就不合适了,而我们通常的做法是读取屏幕像素信息,并将之转化为PNG或JPG,那么这其中ReadPixels就显得重要了,理解好里边的参数,我们就可以随意的截取我们在屏幕中想要的部分.

ok,那有的读者可能会说,理解参数我自己看说明不久好了,或者自己百度,其实,我想说的是,我当时理解反正是理解蛮久的,脚本手册和百度中都没有找到满意的答案,所以才在这里记录。

好了,我们先不着急的讲这个函数,我们先来讲讲说如何将局部屏幕保存为PNG格式的图片。我这里的实现方式是:使用一张2D的纹理贴图,通过该贴图读取屏幕的像素信息并将之转换为PNG格式图片。就一句话的原理,估计有的读者就范晕了,不知道我在讲什么,不过,我下边通过代码的讲解就”柳暗花明”了,哈哈。

既然是使用一张2D纹理, 那么是不是需要创建出一张2D纹理,使用Texture2D类的构造函数就可以,该构造函数原型是static function Texture2D (width : int, height : int, format : TextureFormat, mipmap : bool) : Texture2D ; 

可以看到通过该构造函数可以指定该2d贴图的宽,号,格式等,具体的含义有兴趣的读者可以翻阅unity脚本手册,这里不过多阐述,因为一则比较简单,二则这不是我们的重点。

那么好了,2D贴图有了,我们就可以读取屏幕的局部像素信息保存在该贴图中了,我们可以使用Texture2D 类中的ReadPixels来获取,该函数的原型是: 

function ReadPixels (source : Rect, destX : int, destY : int, recalculateMipMaps : bool = true) : void 

官方的解释是: 

1.Read screen pixels into the saved texture data. 

2.This will copy a rectangular pixel area from the currently active RenderTexture or the view (specified by /source/) into the position defined by destX and destY. Both coordinates use pixel space - (0,0) is lower left 

Unity圣典中的翻译是: 

1.读取屏幕像素信息并存储为纹理数据. 

2.这将从当前处于激活状态的 RenderTexture 或视图(由/source/指定)复制一个由destX和destY指定的矩形像素区域。这两个坐标使用像素空间坐标 (0,0)是屏幕左下角。

不知道你们有没有看懂,反正我是没明白什么意思,2个点怎么能指定一个矩形区域呢,着实想不明白,有木有同感。555~ 在此还求大神指导迷津.

不过文章还得继续,我既然已经在写这篇文章,说明,哥是胸有成足的哦…..我是我自己的哥,哈哈哈!!!

那我们继续,参数不知道什么意思,功能总知道的吧,没错,我们使用创建出来的2D纹理贴图对象来调用ReadPixels()函数,可以知道:参数中所对应的矩形区域的像素信息就被写入到该纹理贴图对象中了。然后,我们如何将之保存为PNG格式的图片的呢?同样Texture2D类还给我提供了一个将2D纹理转换为PNG的方法,该方法的原型是: 

function EncodeToPNG () : byte[] 

可以看到,该方法返回的是一个字节数组,也就是将我们的纹理像素信息保存在该字节数组中,到此还没哟接触,我们还必须将字节数组中的信息入读文件,方法有很多可以实现,不过我这里使用File类下的WriteAllBytes方法(注意,使用file类需要包含该类的空间,要使用using System.IO),简单粗暴,封装后的语言使用就是这么方便,不要自己手动关闭资源。ok,到此,原理已经大体的讲过了,其中就一个点没有阐述,就是ReadPixel函数的参数。那,这就是我们下边将要讲述的事情.我们先上代码,在代码中我们一一试验并解释( 这样,我在上代码前,先上一张我的场景运行画面,其实就3个Cube和一个plane其他都是灯光) 



这是更个Game游戏视图的画面,有点粗糙,不过,这不是我们的重点,只是当演示而已罢了 

OK, 再上代码

using UnityEngine;
using System.Collections;
using System.IO;

public class printScreen : MonoBehaviour
{

  private Texture2D screenShot;

  private bool shoot = false;

  void Start()
  {
//实例化一张你到透明通道的大小为256*256的贴图
    screenShot = new Texture2D(256,256,TextureFormat.RGB24, false);

  }

  void Update()
  {
  //点击鼠标左键的时候 截屏并保存为png图片
    if (Input.GetKeyUp(KeyCode.Mouse0))
    {
      StartCoroutine(CaptureScreenshot());
    }
  }

  void OnGUI()
  {
  //将截出的图片以填充的方式绘制到屏幕中Rect(10,10,256,256)的区域内
    if (shoot)
    {
      GUI.DrawTexture(new Rect(10,10,256,256),screenShot,ScaleMode.StretchToFill);
    }
  }

  IEnumerator CaptureScreenshot()
  {
  //只在每一帧渲染完成后才读取屏幕信息
    yield return new WaitForEndOfFrame();

    //读取屏幕像素信息并存储为纹理数据
    screenShot.ReadPixels(new Rect(0, 0, Screen.width, Screen.height), 0, 0);
    screenShot.Apply();// 这一句必须有,像素信息并没有保存在2D纹理贴图中

    //读取将这些纹理数据,成一个png图片文件
    byte[] bytes = screenShot.EncodeToPNG();
    string filename = Application.dataPath +"/"+Time.time.ToString()+ ".png";

//写入文件 并且指定路径,因为使用该函数写入文件,同名的文件会被覆盖,所以,在路径中使用Time.time只是为了不出现同名文件而已, 没有什么实际的意义,只是当作个测试 
    File.WriteAllBytes(filename, bytes);
    shoot = true;
  }
}


我们可以看到ReadPixels(new Rect(0,0,Screen.width,Screen.height),0,0),2D贴图的大小为256*256,我们先运行游戏,看看截出的图片是样的效果 




可以看到的是图片的大小是256*256,是从Game试图的左下角为(0,0)截图大为Screen.width*Screen.height,而由于我们的纹理大小是256*256,所以,只要我们纹理大小足够大,我们就是可以保存足够大的屏幕信息,当然,这个我们待会在试验。那么,有的读者可能说了,我如果不想要从Game视图的左下角(0,0)出截屏在保存为图片呢,比如说想从屏幕中间的位置开始截图256*256大小的屏幕,那怎么办呢?很好,我们可以修改ReadPixels(new Rect(0,0,Screen.width,Screen.height),0,0)中的Rect,OK,我们再来做试验,将之改为ReadPixels(new Rect(Screen.width*0.5f,Screen.height*0.5f,256,256),0,0),然后,我们运行看看到底是不是这样子的




对比一下原图,我这里将原图做了一下标记如图 




这样做了一下标记,不言而名了吧,这里总结一下,ReadPixels函数中的第一个参数Rect类型的参数我个人理解代表的是:读取的屏幕像素矩形区域是『以Game视图的左下角为原点的矩形区域』。 

ok,是不是有种豁然开朗的感觉,还没完,感觉完了,但实际上真没完,怎么没完呢,没错,就是后面两个参数又代表什么呢,两个int型的参数,一个是destX,一个是destY,啥话不说,直接试验,我们在上边的基础上修改ReadPixels的参数为ReadPixels(new Rect(0,0,Screen.width,Screen.height),10,10), 就是第二个和第三个参数改为10,运行,我们看看 




和上边的图片对比,可以看到,图片大小依然是256*256,但是有了一个10*10的 空白,这就足以说明,destX和destY是在参数1即Rect所指定的区域上进行的偏移。

到此,基本上的参数已经讲完,只有剩最后一个bool类型的参数,可以不用去管它按默认的就好了。

Over!!!,欢迎留言,欢迎拍砖,我们下次见….. 

    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多