需要保存照片文件,首先需要能获取到图像,关于如何获取外部设备的图像,可以参考以下链接:
Unity控制佳能单反拍照及数据获取
Unity调用外部摄像头/网络摄像头/手机摄像头
Unity体感应用开发——Kinect V2 获取彩色摄像头数据
一般我会使用以下三种方式去获取图片:
目前我使用的Unity2018,Application.CaptureScreenshot已经过时不可以使用,现在是ScreenCapture类。
void Start()
{
ScreenCapture.CaptureScreenshot(Application.streamingAssetsPath + "/demo.png", 0);
}
第一个参数是文件名:如果不写全路径,windows默认在项目的根目录下,移动端默认在Application.persistentDataPath下。文件是以PNG形式保存,但不是不可以命名为JPG。
第二个参数可以是数字,那就表示放大系数,0和1是一样的就是Editor Game设置的分辨率,或是exe被打开时的分辨率,如果是2,表示2*2,4倍于原始大小。实践证明,单纯的放大像素等级,并不能提升画质,画质如果差,生成4k的图片一样差。第二参数也可以是ScreenCapture.StereoScreenCaptureMode的枚举值,从参数上看像是为cardboard vr准备的,由于没有使用过,无法确定。
这个方法多用在整个画面都有截取需要,不存在某些需要进入画面,某些不要。
好处就是写法简单,坏处就是不能定义区域,不能根据可见内容截图,而且语句执行结束到产生完整文件,会有一定的延迟。
Texture2D CaptureScreen(Rect rect)
{
Texture2D screenShot = new Texture2D((int)rect.width, (int)rect.height, TextureFormat.RGB24,false);
// 读取屏幕像素
screenShot.ReadPixels(rect, 0, 0);
screenShot.Apply();
// 文件生成
byte[] bytes = screenShot.EncodeToPNG();
string filename = Application.streamingAssetsPath + "/demo.png";
System.IO.File.WriteAllBytes(filename, bytes);
return screenShot;
}
改变Rect可以定义截取区域,但是我几乎不用。
常用这个方法,因为可以通过设置Camera的Culling Mask,决定哪些被截屏进去,哪些不截取。
/// 要被截屏的相机
/// 截屏的区域
Texture2D CaptureCamera(Camera camera, Rect rect)
{
// 创建RenderTexture
RenderTexture rt = new RenderTexture((int)rect.width, (int)rect.height, 0);
// 临时设置相关相机的targetTexture为rt, 并手动渲染相关相机
camera.targetTexture = rt;
camera.Render();
// 激活rt读取像素。
RenderTexture.active = rt;
Texture2D screenShot = new Texture2D((int)rect.width, (int)rect.height, TextureFormat.RGB24,false);
screenShot.ReadPixels(rect, 0, 0);
screenShot.Apply();
// 重置
camera.targetTexture = null;
RenderTexture.active = null;
GameObject.Destroy(rt);
// 最后将这些纹理数据,成一个png图片文件
byte[] bytes = screenShot.EncodeToPNG();
string filename = Application.streamingAssetsPath+ "/demo.png";
System.IO.File.WriteAllBytes(filename, bytes);
return screenShot;
}
1 使用基于Camera的方法,我记得出现过当场景中3D物体,又有UGUI物体的时候,3D物体始终会被拍进去的情况。这个问题后续再遇到,我再补充。
2 我的画幅是16:9,但是我只想得到一张9:16的照片,那么方法就是改变Rect,比如
screenShot.ReadPixels(new Rect(0,r.height*(1f-scaleParm)/2f,screenShot.width,screenShot.height), 0, 0);
screenShot.Apply();
//scaleParm是横边的比例,比如 1-9/(16/9)/16
为什么会有这个需求,是因为有的时候你的设备获取的画质可能是2k以上,但是你的屏幕最高分辨率就到2k,上述方法没有办法真的给你原画质的照片,你就是需要原始的数据。
如果使用单反,参考:Unity控制佳能单反拍照及数据获取
如果使用网络摄像头,比如C1000e能到4k(实际上到了4k会延迟),目前没有办法,除非摄像头有SDK
如果使用kinect,而你的屏幕连1920*1080都没有,或者你用了Kinect Azure,可以获取到ColorImage之后,通过texture2d转bytes,转文件。