UNIT 03建模约 33 分钟 · 核心线
为什么我的机甲像几个多边体拼起来的?
「机甲表面是不是太过粗糙了,不是指贴图粗糙,是现在感觉就像几个多边体拼接一样,特别是头部和肩膀。」这是你看完红魔 v4 说的 C22。你把「贴图粗糙」和「形状粗糙」分开了,这一刀切得很准。这一课讲形状是用什么做的、为什么会一块一块,v5 又是靠哪两样东西把它变圆的。
同一个镜头,同一帧(约 33.3 秒)。左边的头盔一块一块,每块一个亮度,头顶和两腮的外轮廓是折线;右边是连续的曲面,灯带的倒影在上面是顺的,面罩的边却还是硬的。v5 同时还换了环境和漆(第 4 课讲),这一课只看形状。
03 · 1表面只是一层三角形
3D 软件里的物体没有「里面」,只有一层表面。这层表面是一张网,叫网格(mesh):一堆点(顶点,vertex),点和点之间连线(边,edge),几条边围出一小块平面(面,face)。面可以是四边形、五边形,但送去显卡画之前都要拆成三角形:一个 n 边形拆成 n − 2 个。
v5 的头盔就是这样一张网,而且是用代码「长」出来的:从下巴到头顶,沿着头部的骨骼(机甲骨架里管头的那一节,机甲动起来靠它)从下往上摆 5 个截面,每个截面是一圈 24 个点;相邻两圈之间连成四边面,最下和最上那两圈各用一个 24 边形封口。摆一串截面、再把相邻两圈连起来,这种做法叫放样(loft)。数一下:24 × 4 = 96 个四边面,拆成 192 个三角形;两个封口各拆成 22 个。一共 236 个三角形,每一个都是平的。
236 个平面拼起来,当然是一块一块的。怎么让它变圆?
03 · 2多切几刀,不就圆了?
最顺手的办法:面不够多,就多切几刀。把每个四边面切成四块,再把新切出来的点往圆滑的方向挪一点,这一步叫细分(subdivision),怎么挪第 4 节细说。切一次,三角形变成 4 倍;切三次,64 倍。这条路是对的,形状确实越切越圆。问题是要切多细,才看不出棱。
检验「顺不顺」,我们用一个很灵敏的东西:灯带的倒影。下面的头盔放在一间暗棚里,前面挂着五条横灯带,两边各一条竖的。表面每一处朝向稍有不同,倒影就跟着歪;表面一有折角,倒影就断成一截一截。
下面这个头盔先用「一个三角形整块一个亮度」的画法。把细分从 0 级拉到 2 级,三角形从 236 个变成 3,456 个(多了约 15 倍),头盔上灯带的倒影会怎样?
- 变成连续的弧线,看不出棱了
- 碎块变小、变密,但还是一格一格的,推近看更明显
- 几乎没变化:每个三角形都还是平的
2 级、3 级都还是一格一格的。每切一级,一圈的点数和上下的圈数都翻一倍:切到 3 级,一圈已经有 192 个面、一共 13,824 个三角形,倒影还是台阶,只是台阶变矮了。
- 来自材料
- 头盔的五个截面(宽、深、形状指数、正前方那道棱的高度、前后偏移)一字不改取自 v5 的 mech5.py,一圈 24 个点;细分按第 4 节那套规矩在浏览器里现算。
- 本实验的设定
- 头部骨骼长 0.19 米(原片由骨架决定);灰色清漆和七条灯带是为了看清表面顺不顺设的,不是原片的漆和灯光;这一台固定为「一个三角形一个亮度」。
- 它能证明
- 只靠多切,面越小台阶越矮,但一直是台阶,三角形每级 ×4。
- 它不能证明
- 原片那一帧的样子:v4 的真做法(十边形截面加随机抖动,见第 6 节)没有搬进来。「细到多少看不出」取决于一个面在屏幕上占几个像素,换镜头就变。
猜「变成弧线」的,看的是形状:形状确实圆了。猜「没变化」的,看的是每个面:每个面确实都是平的,可面小了,台阶也细了。两边各对一半,缺的是下一节那个东西。
03 · 3亮暗是按什么算的:法线
一小块表面有多亮,取决于它朝哪边、光从哪边来:正对着光最亮,侧过去就暗,背过去就黑(第 2 课用「点积」把这个夹角算成一个数)。「朝哪边」用一根垂直于表面、朝外的箭头表示,叫法线(normal)。
刚才那台实验里,每个三角形只有一根法线,整个三角形用同一个朝向去算亮暗,所以整块一个亮度,这叫平直着色(flat shading)。头盔中间那圈,相邻两个面的法线平均差 15°(一圈 24 个面,360° ÷ 24;弯得急的地方更多,平的地方更少)。倒影比亮暗更敏感:镜面反射时入射角等于反射角,法线转 15°,光进来那一侧的夹角变 15°,反射出去那一侧也跟着变 15°,镜子里的像一共歪 30°。所以灯带一过棱就「跳」一大截。
换个存法:不在面上存法线,在顶点上存,取这个顶点周围几个面的法线平均。画一个三角形时,三个顶点的法线在面内逐渐过渡,每个像素都用过渡出来的那根法线算亮暗。面还是那几个平面,看上去却是一整片曲面。这叫平滑着色(smooth shading),它不加一个三角形。
不细分,还是 236 个三角形,只换成平滑着色:表面上灯带的倒影顺了。那从正侧面看过去的剪影会怎样?
- 也跟着变圆
- 不变,还是那几段折线
- 变小一圈:平均过的法线把表面往里收了
剪影一点没变。从侧面看,五圈截面连成一条折线,拐角就在截面那几处,最尖的一处拐了 43°(小窗右上角的读数),换了着色还是 43°。平滑着色只改了每个像素拿哪根法线去算亮暗,顶点一个都没动;剪影是顶点的位置围出来的。
- 来自材料
- 同上一台:v5 头盔的真截面。
- 本实验的设定
- 剪影小窗是从正侧面看过去、不带近大远小的剪影;「最大折角」是轮廓线在各个拐角拐了多少度里最大的那个,越圆越小,不算下巴和头顶那两圈故意做硬的棱(第 4 节讲)。这个头盔从侧面看没有凹进去的地方,量外轮廓就够了。
- 它能证明
- 换着色方式,剪影和折角逐点不变;细分每多一级,剪影上最大的折角减半:43°、21°、11°、5°。
- 它不能证明
- v5 出图时开了「用极限曲面」:面数还是 2 级的,但每个顶点直接放到「一直细分下去」最后会落到的位置上;这里显示的是细分到第 N 级时的位置,2 级时两者最多差 1.4 毫米。
回头看 v4 那一帧:脸上一块块的亮暗,是着色和面数的问题;头顶和两腮那几道硬折线,是剪影的问题。两样要分开治,平滑着色只治得了第一样。你特别点了头部和肩膀:特写里,它们的外轮廓就是剪影。
不看上面,用一句话说:平滑着色改了什么,没改什么?
03 · 4剪影要圆,得有真的顶点:细分和折边
剪影只认顶点的位置,所以只能真的加顶点。这就是第 2 节那个「多切几刀」的用处:它加的点不是随便加,而是按一套固定的规矩往圆滑的地方放。Blender 的「表面细分」修改器用的是 Catmull-Clark 这一套:
- 每个面的中心加一个点;
- 每条边上加一个点,位置取这条边两端和两侧面中心四个点的平均;
- 原来的每个顶点,往周围这些新点那边挪一段(挪多少有一个固定的加权公式,这里不展开);
- 把每个面连成几个小四边形(四边面变四个)。一圈的点数、上下的圈数,都翻一倍。
一遍一遍做下去,形状会收敛到一张光滑的曲面上。那张粗的网只负责「捏形状」,所以常叫它笼子(cage)。v5 就是这么用的:笼子是第 1 节那 96 个四边面加两个封口,编辑时看 1 级,出图用 2 级,再配平滑着色。上一台实验把着色切到「平滑」、细分拉到 2 级,看到的就是 v5 出图的形状。
代价写在读数里:2 级是 3,456 个三角形,约是笼子的 15 倍。不是整 16 倍,是因为两个封口:一个 24 边形原来只算 22 个三角形,切一刀变成 24 个四边形,只翻了一倍多。整台机甲的每个零件都这么切,这笔面数账要算,第 10 课再讲游戏引擎怎么省它。
细分还有个副作用:它把所有棱角都往圆里拉,包括不该圆的。一个立方体不加管束地细分下去,会变成一个圆鼓鼓的团,体积只剩原来的三分之一左右。可机甲的装甲板边、头盔正前方那道 V 形的棱,要的就是硬。办法是给这些边标上折边(crease):细分时,折边上新加的点只沿着边走,不往圆里拉。折边值从 0 到 1,0 是不管,1 是一直硬下去,中间的值在头几级里保持硬、再往后才慢慢变圆。v5 的头盔下巴和头顶那两圈折边值 0.9,正前方那道棱 0.7。
- 来自材料
- v5 的截面和折边值(下巴和头顶两圈 0.9、正前方那道棱 0.7)。Blender 把折边值换算成「能硬几级」(10 × 折边值²:0.9 约 8 级,0.7 约 5 级),都撑得过这里的 3 级,所以这里一直按硬棱处理。
- 本实验的设定
- 两道加强筋(高 6 毫米、宽 24 毫米)是这台实验加的,原片的头盔上没有。04 的凹凸贴图和置换(第 5 节讲)用的是同一个「哪里高多少」的函数,凹凸在每个像素上现算;法线贴图是把这一步预先存成一张图,效果相同。
- 它能证明
- 关掉折边,正前方那道棱被磨圆,3 级时往里缩 3.5 毫米;凹凸只改亮暗、不改剪影;置换改得了剪影,但网格要够密。
- 它不能证明
- 真实工程里的置换要用的网格往往比这里的 3 级密得多,内存也跟着涨;这台实验只演原理。
试一下:02 选「平滑」,03 的折边关掉,镜头换到「推近」,看正前方那道棱。
03 · 5贴一张凹凸贴图,不行吗?
还有一条路看起来更省:几何不动,让亮暗替我们「画」细节。法线既然骗得过眼睛,就再把它改得花一点:准备一张灰度图,越白代表那里越高;画的时候,每个像素看看自己周围是在上坡还是下坡,把法线往坡的方向歪一歪,表面就「长」出了刻痕、铆钉、加强筋。这就是凹凸贴图(bump);法线贴图(normal map)干脆把每个像素歪成什么方向直接存成图(三个方向分量存进红、绿、蓝三个通道),做的是同一件事。
在上面的实验台给头盔加两道 6 毫米高的加强筋,用凹凸贴图做(04 选「凹凸贴图」)。正面看,筋的高光和暗边都有了。转到正侧面,剪影上会多出两个小鼓包吗?
- 会:筋有 6 毫米高,侧面当然看得出来
- 不会:凹凸只改了法线,顶点没动
- 要看灯从哪边来
不会。剪影还是那条光溜溜的弧线,折角一点没变。凹凸贴图从头到尾只改了「每个像素拿哪根法线算亮暗」,这和平滑着色是同一招:骗得过光照,骗不过轮廓。侧着看、掠着看时最容易穿帮,筋在剪影上不鼓,影子也投不出来。
要剪影真的鼓出来,得用置换(displacement):按同一张高低图,把顶点真的沿法线推出去。可置换只能推已经有的顶点。0 级的笼子上下只有五圈点,两道筋正好落在两圈之间,推了等于没推;细分到 3 级,上下 33 圈点,筋才被推出来,剪影上最大的折角从 5° 跳到 23°。在实验台上换成「3 级 · 置换 · 正侧面」
一分多钟的导览:凹凸和置换在同一个头盔上来回切,盯着剪影看,最后把细分降到 0 级,看置换为什么推不出来。它会接管上面的实验台 G-3,你随时可以接手。
《拾光》里撞过同一堵墙。你看着沙面的楼问:「好多的建筑都不够精致啊 像个方盒子 这个能用lovart解决吗」C37。当时的判断是:贴图能解决「墙像一张纸」,解决不了「楼像一个盒子」。于是两样一起做:Lovart 画立面贴图,再从它算出法线图和窗户遮罩,治「纸」;改模型治「盒子」:露德圣母堂按档案重做,其余 139 栋按 8 种样式加一套规则重新生成。
03 · 6v4 到 v5,到底改了什么
两版头盔的代码都在红魔项目里,摆在一起看:
| v4(mech.py) | v5(mech5.py) | |
|---|---|---|
| 截面 | 十边形(切了角的矩形),加密成一圈 20 个点 | 超椭圆,一圈 24 个点 |
| 面 | 每个四边面随机选一条对角线,拆成两个三角形 | 保持四边面 |
| 顶点 | 每个点在三个方向上各随机挪最多 1.5 毫米 | 不挪 |
| 着色 | 平直 | 平滑 |
| 细分 | 不细分 | 2 级,两头折边 0.9、正前方的棱 0.7 |
| 板件 | — | 肩甲、护臂等做成有厚度的板,板边折边 1.0 |
v4 的每一行都在制造多面体感:平直着色让每个面一个亮度;点被抖过以后,一个四边面的四个角不在一个平面上了,从哪条对角线切,两个三角形的朝向就不一样,随机选对角线等于再抖一遍。1.5 毫米听起来很小,可 v4 头盔上的小面只有两厘米多宽:相邻两个点一个往外、一个往里各 1.5 毫米,这个小面就歪了 8° 左右,镜子再翻一倍,倒影错开十几度。碎块就是这么来的。
v5 的截面换成了超椭圆:|x/a|e + |z/b|e = 1,其中 x 是左右、z 是前后(截面是横着切的一刀,上下那个方向是骨骼),a、b 是半宽和半深。e 等于 2 时就是椭圆,e 越大越像圆角矩形;v5 头盔的五个截面用的是 2.2 到 3.4。整个头盔就是「五组参数 + 一段放样的代码」,这就是程序化建模(procedural modeling):用规则和参数生成模型。同一段代码、同一组参数,每次生成的都是同一个头盔;每圈点数、截面数想要多少就改多少,重跑一遍就是同一个头盔的另一个版本。当时你在「混元 3D、Tripo、继续程序化」三条路里选了最后一条 C22,v5 就是这么来的。
最后把这一课合起来,回到那天的问题。
带走一个问题
v3 的 logo 中间,你看到「一个很粗糙的圆圈」C19:那是一颗面数很少的发光球。按这一课的模型,它粗糙在剪影上还是在亮暗上?如果当时要留着它,平滑着色、细分、凹凸贴图,哪一样修得好?(v4 的做法是干脆删掉它,改成沿 logo 描一圈光。辉光为什么会把这个毛病放大,第 5 课再说。)
不看上面,写下这一课撞上的几堵墙,再展开对照
- 以为多切几刀就圆了 → 平直着色下面再小也是台阶,三角形每级 ×4。
- 以为平滑着色把形状变圆了 → 它只改算亮暗用的法线,剪影一个顶点都没动。
- 以为细分只是「更多的面」→ 它按规矩把顶点往光滑处拉,连该硬的棱也磨圆,要用折边护住。
- 以为贴图能补形状 → 凹凸和法线贴图只骗光照;剪影要靠置换或改模型,置换还要够密的网格。