unity如何让某个物体永远显示在最前面(3D层)并保存成型后的图像(不显示UI层)(多个摄像头的用法、三种截图)
前言
公司经营一款绘图相关的软件,近期有客户反映,尺寸标尺会被画图数据挡住。现在就来解决一下这个问题。
遮挡分为两种情况,一种是UI界面下的组件遮挡(即Canvas画布下),另一种是3D层靠近摄像头的物体遮挡后方的物体。
UI层的很好解决,越下层的物体越是最后渲染,只需要将显示在前方的物体放在最下面即可。
重点介绍3D层如何将物体显示在最前方。
首先:我们要知道3D层的渲染顺序,离摄像机距离越近,越是显示在最前方。即和人眼观察物体一样,假设有一杯子和一张桌子,杯子放在桌子前,我们会先看到杯子,然后在看到桌子。如果桌子在前,杯子在后,那么我们只能看到桌子。所以最简单的第一种方法:将想显示的物体放到最前方,即调整到Camera的距离(通常是Z轴),但是这会引发一系列的问题,例如:靠的越近,物体显示越大,在整个场景中格格不入,而且因为改了Z轴,空间位置也发生了改变,就显示在了其他的地方。所以放弃这一种方法,采用第二种:多个Camera渲染。
先来看一下我们需要用到的Camera参数。
Skybox(天空盒):默认项,在屏幕空白处显示当前摄像机的天空盒
SolidColor:屏幕空白处填充背景色
Depth only:仅深度,不是很理解什么意思,我们需要用到的模式(显示在最前方的物体)
Don’t Clera:不清除,该模式不清楚任何颜色和深度缓存,即每一帧渲染结果叠加在上一帧之上。
Culling Mask:需要渲染的层级
渲染深度:值越小,越先渲染。
看到这里,想必你已经知道如何实现了。先用一个主摄像头渲染除了你想显示在最前端的所有物体(通过图层筛选)。然后单独的用一个摄像头,Clear Flags模式为Depth only,CullingMask 为你想渲染的那个图层(游戏物体需要指定为这个图层),depth深度值为最大,即可实现效果。
效果如下:
虽然球在Cube后,但是在Game视角下看还是球在前,Cube在后。
现在解决了物体被遮挡问题,但是公司软件还有分享图片的功能,如何将成像后的图片保存下来则成了问题。因为是绘图相关软件,所以不能显示UI层,只需要保留用户绘的图片,于是又在网上搜索了不少unity截屏相关方法。
第一种:
调用unity自带的ScreenCapture类,里面有一个保存当前全屏图像的方法,但是无法局部截图,也无法筛选UI层,不满足我们的要求,PASS。
//Unity自带的截屏功能,保存当前画面,无法进行筛选 public void CaptureFunc1(string filePath) { ScreenCapture.CaptureScreenshot(filePath); Debug.Log("文件路径保存在" + filePath); }
第二种:
使用Texture2D读取屏幕像素,画了一张图片方便理解,先定义一个Rect,定义要截取的范围,从左下角开始,设置好要偏移的X、Y轴的量,然后是设定宽高截取。画了一张图方便大家理解。另外,这个需要用协程,否则会报错ReadPixels was called to read pixels from system frame buffer, while not inside drawing frame。具体原因可以参考截屏报错。此处不过多赘述。
public IEnumerator CaptureFunc2(string filePath) { yield return new WaitForEndOfFrame(); //起始的X坐标,Y坐标,宽,高 var rect = new Rect(Screen.width / 4, Screen.height / 4, Screen.width/2, Screen.height/2); Texture2D tex = new Texture2D((int)rect.width, (int)rect.height); //读取像素、后面两个参数分别是X轴的偏移量,Y轴偏移量、暂且不知道用处 tex.ReadPixels(rect,0,0); //保存数据 tex.Apply(); //将数据转换成PNG数据 byte[] bytes = tex.EncodeToPNG(); //将数据写成文件 File.WriteAllBytes(filePath,bytes); Debug.Log("Texture2D截图文件路径保存在" + filePath); //刷新unity目录 RefreshResource(); }
第三种:获取摄像机屏幕截图
这种方法是额外设置一个摄像机,对准想要截图的区域。
代码如下:
public void CameraCapture(string filePath) { var camera = mainCamera; //设置截图相机 var rect = new Rect(0, 0, Screen.width, Screen.height); RenderTexture renderTexture = new RenderTexture((int)rect.width, (int)rect.height,32); //最后一个参数不要设置为0或者-1,会导致部分物体无法渲染 camera.targetTexture = renderTexture; //设置相机的renderTexture camera.Render(); //手机开启相机的渲染 RenderTexture.active = renderTexture; //当前活动的渲染纹理 Texture2D tex = new Texture2D((int)rect.width, (int)rect.height); tex.ReadPixels(rect, 0, 0); tex.Apply(); RenderTexture.active = null; //重置当前活动的渲染纹理 camera.targetTexture = null; //重置相机的targetTexture //设置第二个截图相机 camera = extraCamera; camera.targetTexture = renderTexture; camera.Render(); RenderTexture.active = renderTexture; tex.ReadPixels(rect, 0, 0); tex.Apply(); //重置当前活动的渲染纹理 camera.targetTexture = null; camera = null; RenderTexture.active = null; //删除RenderTexture对象 Destroy(renderTexture); //写成图片文件 byte[] bytes = tex.EncodeToPNG(); File.WriteAllBytes(filePath, bytes); Debug.Log("Texture2D截图文件路径保存在" + filePath); //刷新unity目录 RefreshResource(); //回收垃圾 Resources.UnloadUnusedAssets(); GC.Collect(); }
使用摄像机截图的几个关键点:
1、如果需要将Z轴靠后的物体显示,但是前面有物体怎么办?
答:使用多摄像机,分别获取图像,然后赋予同一个Texture2D对象,代码在案例中有,注意点,RenderTexture第三个参数不能设置为0或者-1,否则会导致部分物体无法渲染(暂不知原理,百度相关参数也没有说明)
2、为什么我多摄像机成像后图片是倒过来的?
修改Camer相关参数
这几个勾项全部取消,可能会导致图片旋转,单摄像机截图没问题,多摄像机截图后合并可能会出现旋转,(暂不知原理,百度相关参数没有详细的相关说明,以后有空再琢磨)。
案例Demo
PS:这里是U3D程序员一只,日常分享工作中遇到的问题和学习笔记。
- 如何保存Matlab/Simulink中示波器显示的图像
- unity如何实现3D物体叠加到摄像头画面上
- Unity之人脸识别对比(二)获取摄像头图像以及保存
- [unity-17] Unity如何在UI层显示3D模型
- 利用Python+opencv进行视频文件的读取和保存,打开笔记本摄像头拍照保存、图像在窗口显示等操作
- OpenCV显示摄像头图像保存成bmp文件
- 演示如何实现Matplotlib绘图并保存图像但不显示图形的方法
- 演示如何实现Matplotlib绘图并保存图像但不显示图形的方法
- 如何将图像读取为numpy矩阵?如何将numpy矩阵变成图像保存?为什么cv2保存的图片是黑色的?为什么cv2显示的时候会黑屏?如何给灰度图增加一个维度?
- C# 如何用摄像头抓取图像,如何把图像保存到硬盘
- unity 截图保存及显示
- [Unity 3D教程]教你如何在3D场景中选择物体并显示轮廓效果
- android 当屏幕截图,你如何改变屏幕内容,他们俩bitmap将合并的图像被一起显示
- openCV学习笔记二:摄像头的读取,显示及按键截屏保存一帧图像
- OpenCv打开摄像头,显示图像,保存视频
- MFC编个对话框,能够实时显示摄像头捕捉的镜头, 点击确定,保存当前图像。
- unity 3d物体显示和2dUI显示相结合
- Qt环境下利用OpenCV采集摄像头图像并保存
- IDL读取显示保存图像(李英冰老师教程)
- Java(JMF)获取本地摄像头,实时显示图像