您的位置:首页 > 移动开发 > Unity3D

unity如何让某个物体永远显示在最前面(3D层)并保存成型后的图像(不显示UI层)(多个摄像头的用法、三种截图)

2020-08-05 16:25 5314 查看

前言
公司经营一款绘图相关的软件,近期有客户反映,尺寸标尺会被画图数据挡住。现在就来解决一下这个问题。
遮挡分为两种情况,一种是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程序员一只,日常分享工作中遇到的问题和学习笔记。

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: