模型工作流:自动化的模型内部三角面剔除

1. 关于自动减面

1.1 自动减面的重要性及现状

三维模型是游戏、三维家居设计、数字孪生、VR/AR等几乎所有三维软件的核心资产,模型的质量和性能从根本上决定了三维软件的画面效果和渲染性能。其中,模型减面工作是同时关乎质量和性能这两个要素的重要工作,一个好的模型减面结果,既能保证模型的渲染品质不会过多降低,又能保证模型面数降到一个合理可用的范围。

但三维模型的减面工作需要耗费大量的人力成本,通常一个模型的减面工作根据模型的复杂程度不同,需要耗费0.5到1人/天的成本,如果模型特别复杂,这个时间还会增加。因此,自动化减面工具的引入能极大提升建模人员的工作效率。

1.2 自动减面的挑战

现有的自动化减面工具,基本上都是基于在1997年发布的《Surface Simplification Using Quadric Error Metrics》(下称QEM算法)这篇论文的实现,比如当前游戏行业最流行的工具 PolygonCruncher 就是基于该论文实现的。该工具能够在保证模型拓扑形状的同时进行减面,最终减面的比例会跟该工具的设定有关,其中轮廓保护程度、纹理坐标保护程度、边角保护程度等等都会影响最终的减面比例,在我们家电场景的实际使用过程中,根据使用经验,一个能保持较高品质的减面比例通常在50%左右,这对于动辄十几万甚至上百万的模型来说,是远远达不到安全可用的要求的。

1.3 QEM算法的开创性和局限性

减面工作本质上是一个基于视觉系统和逻辑判断的事情,建模人员需要通过人眼去观察模型的三角面,并据其与模型的遮挡关系,在脑中判断出这个三角面是否可见,或者说是否应该被剔除掉。所以,想要实现自动减面,要么想办法摆脱对视觉系统的依赖,要么想办法实现一套视觉系统。而QEM算法的思路就是前者。

QEM算法创造性的通过计算出三角网格中每个边的一个特殊度量指标------二次误差,以该指标作为一个三角面是否应该减去的依据,从而绕过了视觉系统,仅通过三维数据的数据计算即可完成减面工作。具体来说,一条边的二次误差越大代表其对该局部区域拓扑结构的影响就越大,就越应该保留下来,反之就越应该减去。

但是QEM算法并非无懈可击,由于不依赖视觉系统,它只能根据对拓扑结构的影响程度来选择要减去的三角面,无法进一步根据三角面的可见性情况进行模型内部三角面的剔除。这也是QEM算法在家电模型减面的场景下表现不佳的根本原因。

关于QEM算法的具体原理,可以查看另一篇文章:《模型工作流:模型减面原理分析及优化思路

1.4 如何破局

因此,我们可以通过引入模拟的视觉系统,来实现模型内部三角面的识别和剔除,从而让模型能在QEM减面的基础上,进一步的将内部三角面减去,从而实现更高效的减面效率。实现的效果如下图所示:

2. 基于深度测试的减面

从生物学上来看,人眼最重要的两个能力是色彩识别、深度识别,其中深度识别就是指大脑能根据两个眼睛的"摄像"结果推断出视网膜中某个"像素点"的空间位置的深度关系。同样,对于减面工作来说,我们需要借助眼睛来判断一个三角面到底是暴露在外部,还是被遮挡在内部,其本质上就是判断两个像素点的深度关系。那么,只要在程序中获取到每个三角面的深度关系,那么我们就能识别出那些三角面是被遮挡的,其基本思路如下:

2.1 基本思路

物体的深度是跟人眼的位置和方向有关的,我们会引入若干个虚拟相机,将它们环绕摆放在模型四周,依次来模拟人眼观察的效果。根据模型的实际使用形态不同,相机的摆放可以灵活设定,一般设置8个朝下看+8个朝上看相机足够应对大部分情况,如下图所示。

在这之后,每个相机会进行拍摄,拍摄的内容是"整个模型"的三角面光栅化后的深度值,以下是其中3个相机的拍摄内容。这样,我们就能从图片中获取到每个像素点位置的深度值,对应的就是该像素位置下的模型表面的深度,也就是d2。

相机1

相机2

相机3

然后,我们需要在程序中,通过空间计算,获取每个三角形的在上述每个相机下的深度值,记为d1。这样,我们依次遍历模型的每一个三角形,计算每个三角形的d1和d2的大小关系,如果d1 > d2,则代表这个三角形的深度比模型表面更深,那么就意味着这个三角形在当前相机位置下是不可见的。

注意,d1的获取我们不能使用光栅化来完成,因为面的数量过大,此时CPU的效率远比GPU一个一个渲染FrameBuffer要高很多。

然后我们每个相机都重复上述操作,记录下每个相机的不可见的三角形的集合,最终把所有这些集合进行一个"取交集"的操作,就能得到在所有相机下都不可见的三角形,这样我们后续就能安全的进行内部三角面的剔除了,伪代码如下:

ini 复制代码
// 伪代码
-- 遍历每一个相机
for camera in cameras:
    -- 拍摄该相机下的整个模型的深度图
    wholeObjectDepthMap = renderCameraDepthMap camera wholeObject
    -- 遍历改模型的所有物体
    for obj in allObjects:
        -- 遍历该物体的所有三角面
        for face in obj.faces:
            -- 采样该面的关键点
            faceSamplePoints = sampleFacePoints face
            -- 遍历这些关键点
            for point in faceSamplePoints:
                -- 计算关键点的NDC坐标
                ndc = worldToNDCCoord point
                -- 计算关键点的UV坐标
                uv = NDCToUVCoord ndc
                -- 使用UV坐标采样深度图的颜色
                color = readPixel wholeObjectDepthMap uv
                -- 颜色值转深度值,即是d2
                d2 = (1.0 - color/255.0)
                -- ndc坐标系的z值就是d1(根据图形学理论)
                d1 = ndc.z
                -- 如果d1的深度大于d2的深度,那么就代表d1所在的关键点不可见
                if d1 > d2:
                    -- 该三角面的、该点,不可见

2.2 一些优化点

2.2.1 深度精度优化

为了尽可能提升深度值的精度,我们需要把相机视锥参数调整到"恰好包裹起整个物体"的状态,这样才能尽可能的减少相机的near平面和far平面的距离,以此来提升深度精度。
注意,这里我们使用正交投影相机,视锥在程序内部是一个长方体(如图中白色虚线所示),而不是锥形。

2.2.2 三角面关键点

考虑到一个三角面被遮挡的情况千奇百怪,比如可能中间被遮挡边角露出,也可能反过来。因此对三角面的关键点选取数量越多,我们的判断精度就越高。但是三角面数量越多,我们的计算耗时就越大,这需要权衡。经过实践,在三角形的边角和中心区域共6个点可以应对绝大部分情况。

2.2.3 预剔除

有一些物体它本身已经完全隐藏在另一个物体内部,此时这个物体其实可以整个抛弃,没必要再判断每个三角形的可见情况。实现思路也很简单,除了拍摄整个模型的深度图,然后每个物体也拍一个深度图。

伪代码如下:

ini 复制代码
// 伪代码
-- 遍历每一个相机
for camera in cameras:
    -- 拍摄该相机下的整个模型的深度图
    wholeObjectDepthMap = renderCameraDepthMap camera wholeObject
    -- 遍历改模型的所有物体
    for obj in allObjects:
        -- 拍摄该相机下的"单个物体"的深度图
        objectDepthMap = renderCameraDepthMap camera obj 
        objectBBoxWorld = getObjBBox obj
        objectNDCRange = worldToNDCRange objectBBoxWorld 
        -- 获取UV的坐标范围,如上图红框所示
        objectUVRange = NDCToUVRange objectNDCRange
        -- 遍历两张图在该坐标范围内的像素颜色,进行深度值的判断
        for u in objectUVRange.rows:
            for v in objectUVRange.cols:
                color2 = readPixel wholeObjectDepthMap uv
                color1 = readPixel objectDepthMap uv
                
                d2 = (1.0 - color2/255.0)
                d1 = (1.0 - color1/255.0)
                
                if d1 > d2:
                    -- continue
                else
                    -- exit 有一个像素可见,那么整个物体都可见,直接退出循环

2.3 自动减面效果测试

基于深度测试的减面方法是通用的,因此对于各种品类的家电家具模型都有非常好的效果,相比于传统的基于QEM算法的减面方案,减面效率得到了巨大的提升。

品类 产品 原始面数 纯QEM算法方案 深度测试方案(配合QEM减面)
电饭煲 HRC-FS4042A-FN01D0028 3.3w 1.1w减面比例:66% 0.52w减面比例:84%
电水壶 FM00E0000 9.2w 2.8w减面比例:69% 0.74w减面比例:92%
吸油烟机 海尔-家用-吸油烟机-CXW-258-EC967U1 12w 7.5w减面比例:36% 0.83w减面比例:93%
净水器 卡萨帝-反渗透-净水机-600G-CRO600-DFBGU1 15w 9.6w减面比例:36% 1w减面比例:93%
用水 圆镀铬龙头HJ2107 12.5w 8.4w减面比例:33% 0.5w减面比例:96%
智能浴霸 海尔-智能浴霸-X3U1 12.6w 7.2w减面比例:43% 0.78w减面比例:93%
热水器 卡萨帝-电热-热水器-80L-CES80H-PROS3CEK02U1 39w 21w减面比例:46% 2.9w减面比例:92%
洗衣机 海尔-滚筒-洗衣机-10kg-G100318B14LS 59w 43w减面比例:27% 10w减面比例:83%
电视 卡萨帝-液晶电视机-K85E10 62w 56w减面比例:9% 13w减面比例:79%
壁挂空调 海尔-挂机-空调室内机-2匹-KFR-50GW18MEA83U1套机 66w 59w减面比例:10% 2.7w减面比例:96%
柜式空调 海尔-柜机-空调室内机-大3匹-KFR-72LW17DAA81VU1 79w 63w减面比例:20% 11w减面比例:86%
冰箱 卡萨帝-多开门-电冰箱-550L-BCD-550WLCFPA4YBU1 150w 70w减面比例:53% 17w减面比例:89%

3. 团队介绍

三翼鸟数字化技术平台-筑巢自研平台」依托实体建模技术与人工智能技术打造面向家电的智能设计平台,为海尔特色的成套家电和智慧场景提供可视可触的虚拟现实体验。智慧设计团队提供全链路设计,涵盖概念化设计、深化设计、智能仿真、快速报价、模拟施工、快速出图、交易交付、设备检修等关键环节,为全屋家电设计提供一站式解决方案。

相关推荐
刘好念1 天前
[OpenGL]使用 Compute Shader 实现矩阵点乘
c++·计算机图形学·opengl·glsl
charlee444 天前
深度科普文:细数倾斜摄影数据的缺点
三维可视化·计算机图形学·倾斜摄影
刘好念5 天前
[OpenGL]使用TransformFeedback实现粒子效果
c++·计算机图形学·opengl
刘好念11 天前
[OpenGL] Transform feedback 介绍以及使用示例
c++·计算机图形学·opengl
ttod_qzstudio13 天前
Unity中Mesh重叠顶点合并参考及其应用
unity·图形学
Ian10251 个月前
《Learn Three.js》学习(3)光源
前端·javascript·学习·webgl·图形学·三维·三维光源
刘好念1 个月前
[OpenGL]使用OpenGL+OIT实现透明效果
计算机图形学·opengl
哈市雪花1 个月前
图像处理 之 凸包和最小外围轮廓生成
图像处理·人工智能·图形学·最小外围轮廓·最小外包