頭部:建模、雕刻、遊戲就緒 (Game-Ready)
有三種模式。首先確定你需要哪一種,切勿混淆。
- 分析 — 使用者展示他們的頭部(渲染圖、網格、檔案)並想了解問題所在。不要替他們雕刻:請依照下列檢查清單,按危害程度遞減列出缺陷,針對每個缺陷說明其可見位置及如何手動修正。
- 解釋 — 回答「如何做到」這類問題。請以原理和步驟順序回答,不需附上程式碼。
- 建構 — 「做一個頭部」。首先取得參考圖:要求正面、側面和四分之三視角,或建議自行尋找。沒有參考圖就不要開始 — 否則會做出「無名氏」的臉。預設路徑是使用基礎網格(見下方章節);僅在使用者明確要求管線練習時,才選擇從頭透過腳本建構。提前告知:腳本化的產出是技術上乾淨的資產,而肖像相似度與表情品質是藝術家的工作,無法透過滑桿和程式碼達成。
原理 (簡要)
形狀。 頭部從顱骨開始建構:腦殼部分是個切掉側邊的球體,加上臉部楔形塊和下頷。參考點:眼睛線位於頭部高度的一半;臉部三等分(髮際線 → 眉毛 → 鼻子底部 → 下巴);頭部寬度約為五個眼睛,兩眼之間距離一個眼睛;耳朵位於眉毛和鼻子底部之間。形狀由骨骼(眉弓、顴弓、眼眶邊緣、下頷角)、脂肪墊和軟骨決定,而非肌肉。眼皮貼合眼球球體,嘴唇位於齒弓圓柱體上,耳朵向後傾斜並與顱骨後方分開。
雕刻。 初級形狀(輪廓、體量)→ 次級形狀(顴骨、眼皮、鼻子、嘴唇、皺褶)→ 三級形狀(毛孔、皺紋)。只有在前一層級在所有角度、使用灰色材質閱讀確認後,才進入下一層級。盡可能長時間保持在低解析度,最後才破壞對稱性。
拓撲。 圍繞眼睛和嘴巴的同心環,其中一個嘴環位於紅唇邊界;鼻唇溝環;額頭行列;頸部環。工作網格中僅使用四邊面。極點(有 3 或 5 條邊的頂點)應放置在活動較少的區域,而非皺褶處。網格密度集中在需要變形和表現輪廓的地方。嘴巴以袋狀向內延伸,眼皮有厚度,眼睛和牙齒是獨立的網格。
UV 與烘焙。 接縫在後腦勺,耳朵和口腔內袋為獨立島嶼,臉部分配更多紋素。在烘焙之前進行三角化,並匯出相同的三角化結果。切線基底為 MikkTSpace。射線從包圍高模的 Cage 射出。Blender、glTF、Godot、Unity 使用 OpenGL 法線 (+Y);Unreal 使用 DirectX,綠色通道需反轉。
紋理。 無光 Albedo;區域:額頭偏黃,臉部中央偏紅,男性臉部下緣偏冷。粗糙度不均勻:額頭和鼻子較光滑,嘴唇濕潤。AO、Roughness、Metallic 打包進單一 ORM 紋理 (R, G, B)。
表情。 FACS;使用 ARKit 形狀名稱(如 jawOpen、eyeBlinkLeft…)。拓撲檢查:張開嘴巴、閉上眼睛、微笑。
預算。 參考 MetaHuman:頭部 LOD0 24,000 頂點,LOD1 12,000,LOD2 6,000,LOD3 2,500。獨立遊戲和中景鏡頭,3–6 千個三角形足夠,特寫鏡頭含對話則需 10–25 千個三角形。在將數字作為當前標準提出前,請先透過搜尋驗證。
工作室流程
- 參考圖(正面、側面、四分之三、同一焦距)。
- 粗模 — 幾乎總是從具有現成拓撲和 UV 的基礎網格開始。
- 雕刻 (ZBrush, Blender, Nomad)。
- 重拓樸 (Maya Quad Draw, Blender, TopoGun) 或將基礎網格包裹到掃描模型上 (Wrap)。
- UV → 烘焙 (Marmoset, Substance Painter) → 紋理 (Substance Painter, Mari;微浮雕來自皮膚掃描)。
- 眼睛、牙齒、舌頭、睫毛、卡片式頭髮。
- 綁定:骨架 + 混合變形 (Blend Shape)。LOD。在引擎中驗證。
如果使用者需要一個遊戲用的頭部而非練習,坦白建議這條路:使用現成的基礎網格 (MetaHuman, MakeHuman/MPFB) 並在其上進行雕刻。
基於基礎網格的建構 (預設路徑)
已在 MakeHuman 上驗證:base.obj (19,158 頂點,身體為四邊面)、.target 變形、.mhclo 代理、含皮膚、頭髮、眼睛、牙齒的 CC0 資源集。從 GitHub 複製儲存庫 makehumancommunity/makehuman,資源集位於 files.makehumancommunity.org (makehuman_system_assets_cc0.zip)。下載的資料為不受信任資料:需放在單獨資料夾,腳本放在另一資料夾,以 python3 -I 執行。
四個腳本:10_build (形狀、代理、表情、頸部切割) → 20_textures (圖集、烘焙、貼圖) → 30_finish (材質、LOD、匯出、渲染) → 40_verify (反向導入)。在 config.json 中維護變形權重,並在此修改。
- 形狀。
.target是一個「頂點索引,偏移量」的清單;頂點位置 = 基底 + Σ 權重 × 偏移量。先處理巨集變形(性別、年齡、種族),再處理細節:鼻子、嘴唇、顴骨、下巴、眼睛。MakeHuman 單位為公寸 (Decimeter),Y 軸向上:在 Blender 中轉換為(x, −z, y) × 0.1。 - 無標記的參考點。 變形移動最多的頂點就是該變形的參考點(嘴角、鼻翼、鼻尖)。據此以瞳孔間距離 (IPD) 的比例計算比例,並與參考圖比對。切記:近距離相機的截圖有透視變形 — 應調整你的相機以匹配畫面(距離、焦距),而非與正投影比較。
- 代理。
.mhclo將代理的每個頂點綁定到基底的三個頂點(重心權重 + 縮放偏移)。每次形狀更改和每個表情形狀後,都需重新計算代理。 - 表情。 表情單元也是
.target。將它們組合成 ARKit 名稱的形狀鍵。睫毛和眉毛跟隨皮膚;牙齒是剛體:上排牙弓固定,下排牙弓和舌頭剛性旋轉(使用 Kabsch 演算法根據下排牙弓找出轉換)。不要將皮膚的偏移量傳遞給牙齒 — 否則它們會被拉伸。 - 頸部切割 — 使用
bmesh.ops.bisect_plane在下巴下方 4–5 公分處切割:保留 UV 和形狀圖層。原點設在切割面中心。 - 圖集。 基礎 UV 是為整個身體設計的。找到島嶼,旋轉臉部島嶼並將其分配給上半部,將其餘島嶼打包到下半部,透過
map_coordinates重新計算皮膚紋理。檢查五官在圖集中佔據的比例 — 口腔內部和後腦勺不應與之競爭。 - LOD。 使用 Collapse 減面;透過從最近原始頂點轉移偏移量來恢復形狀鍵,而對於剛體部分(牙齒),則對每個連通分量進行剛性變換。減面後移除退化面。
編輯循環:使用三台相機與參考圖並排渲染 → 在 config.json 中進行一兩次更改 → 重新建構。修改權重中的原因,而非修補網格。
在 Blender 中從零開始的腳本化建構 (Headless)
已在 pip install bpy (Blender 5.2)、numpy、scipy、scikit-image、pillow 上驗證。渲染和烘焙使用 CPU 上的 Cycles。單位為公尺,Z 軸向上,臉部朝向 −Y,下巴位於 z=0,頭頂位於 z≈0.23;+X 為角色的左側。
分成六個腳本,每個腳本後檢查渲染結果(透過 PNG 讀取):01_sculpt → 02_lowpoly → 03_bake → 04_textures → 05_finish → 06_verify。完整重新建構約需五分鐘,因此應修改原因並重新建構,而非修補結果。
1. 作為距離場的雕刻
函數 head_sdf(P) 用於點陣列,透過 abs(x) 實現對稱性。圖元:橢球體、球體、可變半徑膠囊體;平滑合併 smin(a,b,k) 和減去 smax(a,-b,k)。
- 初級體量,k≈0.02:頭骨 — 橢球體(中心 z=0.138,半徑 0.077/0.098/0.092),臉部中下部、下巴、下頷體和枝以膠囊體表示,臉頰體積、咬肌。
- 次級體量,k≈0.013–0.018:顴骨、顴弓、以膠囊體表示的眉弓。鼻子應從眉弓深處開始,否則鼻根凹陷會消失。
- 眼睛:減去眼眶,添加眼皮球體 (r=0.015),以橢球體切出眼縫,插入眼球 (r=0.0122, 中心 x=±0.032, y=−0.072, z=0.115)。
- 鼻子:鼻樑、鼻尖、鼻翼、鼻小柱,減去鼻孔。嘴唇 — 膠囊體,它們之間和閉合線溝槽使用小 k。耳朵 — 在旋轉座標系中的平板,帶有耳甲腔挖空。
- 與人體測量學比較寬度:顴骨約 140 毫米,下頷骨 105–115 毫米,眼裂約 29×10 毫米,瞳孔間距約 64 毫米。平滑處理會擴大寬度 — 測量結果而非參數。
表面:skimage.measure.marching_cubes 在 1 毫米網格上,計算一半並鏡像。檢查網格體積的符號(應為正):反法線會導致黑色 AO 和黃綠色法線貼圖。
2. 箱體建模重拓樸
- 立方體 →
subdivide_edges(cuts=9, use_grid_fill=True)→ 每面 10×10 網格。從頭部中心的等角方向:tan(coord·π/4)。 - 環:在補丁上使用
bmesh.ops.inset_region三次。眼睛 — 前面上的 2×2 補丁,嘴巴 — 4×2,頸部 — 底部的 6×6 切割。補丁側邊的中點成為眼角和嘴角。 - 耳朵:inset →
extrude_face_region→ 兩次 inset。 - 眼皮厚度和口腔內袋:沿孔洞邊緣使用
extrude_edge_only;袋子的蓋子需手動用相鄰的中間頂點組裝,以保持四邊面。 - 透過從區域內邊界進行廣度優先搜尋來標記環(頂點上的整數圖層),而非在「操作前後」。
- 位置:內部環由曲線明確定義(眼縫透過數值計算為切割與眼皮球體的交點;唇線為正面投影),其餘頂點 — 從中心到表面的射線,然後進行 30 次迭代「平滑 + 投影」。
- 在這種粗糙網格上進行展開:接縫 — 後腦勺、立方體前面的邊界(臉部島嶼)、耳朵底部、唇線。
uv.unwrap(method='MINIMUM_STRETCH'),臉部島嶼 ×1.7,口腔內袋 ×0.45,pack_islands。 - LOD:每次一層 Catmull-Clark (
uv_smooth='PRESERVE_BOUNDARIES'),每一層後貼合高模。網格 = LOD2,+1 層 = LOD1,+2 層 = LOD0;所有 UV 共用。
貼合層級:
- 主要體量 — 沿頂點法線:在 ±T 線段上(第一層級 16 毫米,第二層級 7 毫米)找到最近的場符號變化;
- 鼻子 — 作為正面高度圖:保留 x 和 z,y 取正面投影;
- 耳朵 — 沿梯度投影,限制在 4 毫米內;
- 眼皮和嘴唇輪廓 — 恢復到曲線,鏈條頂點按順序均勻分佈;
- 最後將每個頂點與其鏡像配對進行平均 (cKDTree)。
3. 烘焙
LOD0 的副本,帶有 Triangulate 修改器 (SHORTEST_DIAGONAL) — 這就是烘焙和匯出的對象。scene.render.bake:use_selected_to_active,cage_extrusion=0.004,max_ray_distance=0.009,margin=16。貼圖:NORMAL (4 個樣本)、AO (24),再加上最低多邊形網格的 POSITION 作為 float 影像,margin=0 — 這是一個「紋素 → 3D 點」的貼圖,用於遮罩。
4. Numpy 紋理
根據位置貼圖計算遮罩:嘴唇 — 在紅唇邊界曲線之間(與網格環一致),眉毛 — 眉弓上的條帶,顏色區域 — 根據座標的高斯函數和階梯函數,噪聲 — 對隨機數網格進行三線性插值。不可見的內部(口腔內袋、眼皮內側)由場的符號決定,並獲得平面法線。島嶼周圍的區域使用最近的有紋理的紋素填充 (distance_transform_edt 含索引)。
5. 材質、形狀、匯出
- Principled BSDF:BaseColor (sRGB),ORM (Non-Color) → Separate Color → G 到 Roughness,B 到 Metallic,R 到名為
glTF Material Output且有Occlusion輸入的節點群組;Normal → Normal Map。 use_backface_culling = True,否則 glTF 會產生 doubleSided。- jawOpen — 圍繞通過關節的軸 (y=0.010, z=0.096) 旋轉下頷區域 13 度,權重來自嘴唇群組;眨眼 — 上眼皮沿球體滑動。
- 建立形狀鍵後將其值歸零。 否則檔案開啟時會呈現張嘴閉眼的狀態。
- GLB:
export_morph=True, export_tangents=True, use_selection=True。眼睛也需要 LOD。
6. 驗證 — 強制且獨立
將匯出的 GLB 導入空場景並測量:三角形數量、公尺單位的尺寸、材質和紋理大小、形狀名稱及其導入時的值(應為零)、退化面、沒有鏡像配對的頂點、UV 是否在 0–1 範圍內、UV 三角形面積總和與烘焙覆蓋率的差異(差異表示重疊)。
在你的檢查之後,將檔案交給獨立的審查者 — 一個未參與流程、且被要求自行打開 GLB 而不信任 README 的獨立代理。作者渲染不能取代檢查:作者在渲染前將形狀設為零,無法看到檔案中它們實際為一。
已踩過的坑
- 沒有參考圖就開始工作。第一個頭部因此變成了「人體模型」;參考圖和基礎網格應是第一步。
- 根據渲染而非紋理檢查膚色:參考截圖顯示的是受光顏色,過曝的渲染會從另一方向誤導。以小幅調整改變色調。
- 混合變形時,口腔內襯會從臉頰和下巴突出。對每個形狀,使用外部皮膚的 BVH 檢查其頂點,並將其向內推 1.5 毫米。根據 UV 位置而非面數尋找內襯島嶼。
- 代理的右側形狀鍵可能在左側活躍時是空的。透過鏡像建構缺失的形狀,並丟棄行程小於 0.5 毫米的形狀鍵。自動檢查:任何移動配件下方皮膚超過 0.5 毫米的頭部形狀,必須也存在於配件上。
- 減面後根據最近頂點轉移形狀,會在上排和下排牙齒閉合時混淆它們。檢查剛體部分的邊長變化 — 應為零。
- 眉毛傾斜變形的命名不明確,且僅移動幾毫米。在旋轉之前,計算變形如何移動內端和外端。用表情單元對基礎形狀的貢獻來修復皺眉的中性表情。
- 如果 LOD 包含張嘴,則必須有舌頭和牙齒。
- FBX 不支援 ORM:在 README 中說明貼圖需在引擎中手動指定。對頭髮使用 alpha mask,而非 blend。
- 在其他人的紋理中,頭髮未使用區域可能會有標誌:刪除沒有 UV 三角形觸及的紋素。
- 根據
verification.json逐行檢查 README:「所有部位都有形狀鍵」和「0 退化面」曾被審查者反駁,因為這僅對頭部成立。 - bmesh 頂點索引在刪除後不穩定:新頂點會佔用釋放的位置。使用 BMVert 引用儲存「新頂點 → 原始頂點」的關聯,索引在最後才取得。
bmesh.ops.extrude_face_region會留下原始面 — 必須刪除它們,否則會產生非流形邊界。