开发一个项目,需要将3d打印的STL类型的模型进行类似于切片操作。
具体要求如下:
对使用Solidworks等软件生产的STL文件进行处理,生成方便进行3D打印的格式。
- 首先要求能够导入需要打印的STL文件;
- 其次要求能够对STL模型进行切片,可以置切片层间距;
- 按照要求的图片格式生产每层切片的PNG图像;
导入STL模型
由于Untiy本身不支持STL模型,并且unity的一个mesh最多只能支持65000面,因此正常人也不会用Unity来开发STL文件(我也觉得很傻逼)。试了几个CSDN上的代码,bug千奇百怪(CSDN纯废物)。最后的解决方案是在UnityStore上面买了一个STL导入插件,叫Runtime STL Loader。
STL模型进行切片效果模拟
一开始的设计思路是想着按照字面意思对模型进行切片,也做了Demo发给老师看,但是后面跟老师沟通后觉得直接切片会有问题。原因是切片出来的物体不会自动消失,影响拍照,其次切出的网格可能会发生变化导致模型变形(每次切片后,切片接触面需要重新生成网格不然会形成一个空腔)。
因此打算采用类似于磨皮的方式来体现出切片的项目,使用的插件为Unity3DCrossSectionShader,UnityStore上的文件已经下架,建议在Github上下载:github.com/Dandarawy/Unity3DCrossSectionShader
Unity3DCrossSectionShader具体实现实现后面开个单章来解说,简而言之,获取一个物体的XYZ轴向量,组成的一个平面,利用Stencil缓冲区和Cull设置,剔除超出平面的部分,并且独绘制裁切面的截面颜色。如同上图第一排所示。
通过它可以实现物体从无到有的一个类似于3d的打印过程,适合拍片再进行打印。
拍照处理
前面说到,需要对模型切片后的效果进行拍照导出,这一块内容较为简单。根据老师的要求,画面只有黑白两种颜色。因此我们需要对摄像机和天空盒进行处理。
摄像机
思路就是新建一个摄像机,主摄像机只负责看,后续代码基本跟主摄像机无关。
- ClearFlags设置成soliderColor,颜色纯黑。
- 重点就是将Projection的模式设置成Orthographic,避免产生近大远小的问题。
- 在文件夹新建一个Render Texture,分辨率设置1980*1080,挂载摄像机上。
天空盒
摄像机拍摄的图片只能有黑白两色
- 场景里的光源关掉(在运行的时候代码隐藏)。
- Window->Light(ctrl+9)打开灯光设置,按照下述参考配置。
这样下来摄像机就能拍出一张黑白照(建议直接拍熊猫)
开始切片
这一块内容同样不难,根据Unity3DCrossSectionShader的原理,我们需要利用切片平面的Y轴,设置移动距离,也就是切片层间距。每移动一次,就拍一张照。并将拍的的照片保存到指定文件夹。这样我们最基础的功能-切片模拟就完成了。
RenderTexture rt = captureCamera.targetTexture;
captureCamera.clearFlags = CameraClearFlags.SolidColor;
captureCamera.backgroundColor = Color.black;
Texture2D screenShot = new Texture2D(rt.width, rt.height, TextureFormat.RGB24, false);
captureCamera.Render();
RenderTexture.active = rt;
screenShot.ReadPixels(new Rect(0, 0, rt.width, rt.height), 0, 0);
screenShot.Apply();
1. 获取目标渲染纹理
RenderTexture rt = captureCamera.targetTexture;
captureCamera
是一个Camera
对象。targetTexture
指定了摄像机的目标渲染纹理 (RenderTexture)。- 这里通过
targetTexture
获取摄像机的渲染目标纹理,后续会用来读取渲染结果。
2. 设置摄像机的清屏方式和背景色
captureCamera.clearFlags = CameraClearFlags.SolidColor;
captureCamera.backgroundColor = Color.black;
clearFlags
定义了摄像机在每次渲染前如何清除背景:CameraClearFlags.SolidColor
表示用单一颜色填充屏幕背景。
backgroundColor
定义了清屏时填充的颜色,这里是黑色。
3. 创建目标图片的存储容器
Texture2D screenShot = new Texture2D(rt.width, rt.height, TextureFormat.RGB24, false);
- 创建一个
Texture2D
对象,用来存储渲染纹理的数据。 - 参数解释:
rt.width
和rt.height
:图片的宽度和高度,与渲染纹理一致。TextureFormat.RGB24
:指定图片的格式为 RGB,不包括 Alpha 通道(每像素 24 位)。false
:不生成 mipmaps(多级细化纹理)。
4. 触发摄像机渲染
captureCamera.Render();
- 手动调用摄像机的
Render
方法,执行一次完整的场景渲染流程。 - 渲染结果会写入目标
RenderTexture
。
5. 激活目标渲染纹理
RenderTexture.active = rt;
- 将指定的
RenderTexture
设置为当前活动的渲染目标。 - 这是为了使后续操作能够直接访问
RenderTexture
的像素数据。
6. 从渲染纹理中读取像素
screenShot.ReadPixels(new Rect(0, 0, rt.width, rt.height), 0, 0);
screenShot.Apply();
ReadPixels
从当前激活的渲染纹理中读取指定矩形区域的像素数据,存储到Texture2D
中。new Rect(0, 0, rt.width, rt.height)
:指定读取的区域,从左下角 (0,0) 开始,宽度和高度为rt
的尺寸。0, 0
:目标Texture2D
的写入起点。
screenShot.Apply()
应用读取的像素数据,使其生效。
结论
Unity 开发上位机并非适用于所有场景,例如本次Unity导入STL模型的问题,也是花了功夫来研究怎么导入STL,最终也还是感谢有人开发了导入插件,不如都不知道怎么进行下一步。对于后面的图像处理很多都可以通过GPT来进行功能定制,Unity在这块还是有它的生态的。