模組四|從 2D 到 3D(Day 20–25)
我在做的是一個角色介紹網頁:使用者往下捲,鏡頭跟著推近拉遠,畫面裡的 3D 角色跟著轉;而這一頁的主題色要能即時換掉,角色也要跟著變。整頁必須是一個不經過任何建置流程、打開瀏覽器就能跑的單檔 HTML(chenger.html,也就是最後真正上線的那一頁)。角色的立體模型要從哪裡來,是這個模組一直在試的問題。
其中一條路線是 image-to-3D(餵幾張角色的圖片進去,生成流程直接吐出一個 3D 模型)。Day 21 我把角色的三視圖上傳到官方掛在雲端的線上試用頁、借別人的 GPU 跑完一次生成,拿到了一個模型:品質不錯,角色認得出來,材質完整。
然後我發現我沒辦法用它。
這篇講的是四個問題。它們有一個共同點:全部都跟「模型生得像不像」無關,所以在評估的時候完全沒被看到。
產物是一個 GLB 檔——glTF Binary,把模型的網格、材質、貼圖全部打包進單一檔案的 3D 格式,瀏覽器可以直接載入。先把它的體積攤開來看:
char-chenger-front_seed175029927_1024.glb 13,206,732 bytes (12.59 MiB)
貼圖(另外匯出):
baseColor.png 2048 × 2048 3.13 MB
metallicRoughness_packed.png 2048 × 2048 0.80 MB
metallic.png 2048 × 2048 0.46 MB
roughness.png 2048 × 2048 0.18 MB
輸入是三張三視圖,總共約 4.35 MB。
而最後上線的那一頁:
chenger.html 865 行 35,119 bytes
34 KB。整頁(HTML、CSS、617 行 JavaScript)全部在內。
把模型檔的位元組數,除以整頁的位元組數:
13,206,732 ÷ 35,119 = 376
模型檔是最後上線整頁的 376 倍。
Day 20 我先寫了一份限制清單,把這一頁能接受什麼、不能接受什麼在動手之前就釘死;其中關於體積的那條,我寫的是「載入預算:跟一般網頁差不多」。
12.59 MiB 是什麼概念?大約是一支中等長度的手機短片。使用者點進一個角色介紹頁,要等一支短片下載完,才看得到那個角色。
而且這一頁的用途是滑進去看看這個角色長怎樣,不是一個需要 loading 畫面的體驗。
如果我在限制清單裡寫的是「資產不超過 2 MB」,這裡就直接出局了。
而且要記得時序:這時候我手上已經有一個 34 KB 的能跑版本了。 所以真正的問題不是「12.59 MiB 能不能接受」,是「12.59 MiB 換來的東西,值不值得付 376 倍」。
模型的網格結構——也就是拓撲(topology,這個模型的表面是由哪些點、哪些三角面拼出來的)——是生成模型決定的,不是我決定的。
image-to-3D 給你的是一個已經完成的結果。你可以看它、渲染它,但你沒有辦法說「頭髮這一團請多給我一點面數」或「裙襬這裡請改成單層」。
它是一整塊。要改,你得把它匯進 3D 軟體手動編輯,而那需要另一套技能和另一套工具鏈,直接違反 Day 20 的第一條限制(零 build step、零外部工具)——那條限制的意思是,這一頁必須是打開就能跑的檔案,中間不經過任何編譯、打包或第三方軟體。
生成式資產的可編輯性,跟它的品質是兩件獨立的事。 品質很高,但那是一個你不能參與的高品質。

這是決定勝負的那一個。
Day 20 的第三條限制是「主題色要能即時換」:改一個變數,模型就跟著變色。
那四張貼圖(texture,貼在模型表面的圖片,決定每一塊表面看起來是什麼顏色、什麼質感)是烤好的點陣圖。角色的橘色頭髮,是 baseColor.png 裡一片一片的橘色像素。
要換成別的顏色,我得:
這在瀏覽器裡即時做,完全不切實際。
而這條限制是我 Day 20 排序時沒有意識到、但實際上最硬的一條。
另一條路線(後面簡稱路線 B)是不生成模型,改用程式碼把角色一塊一塊堆出來。它的材質定義長這樣——chenger-sculpt-spec.json 是這條路線的角色設定檔,列出角色身上每一塊材質叫什麼、是什麼顏色:
{ "id": "hair", "channels": { "color": "0xe8863a", ... }, "themed": true },
{ "id": "clothMain", "channels": { "color": "0xf7f2e8", ... }, "themed": true },
{ "id": "clothDark", "channels": { "color": "0x1d1b22", ... }, "themed": true },
{ "id": "gold", "channels": { "color": "0xb98a2f", ... }, "themed": true },
8 組材質,其中 4 組標了 themed: true。
換主題色 = 改四個十六進位數字。
差別不在技術高低,在於顏色是資料還是像素。烤成貼圖之後,顏色就從「一個可以改的值」變成「幾百萬個像素」。

官方的硬體要求寫得很清楚:
Hardware: An NVIDIA GPU with at least 24GB of memory is necessary. The code has been verified on NVIDIA A100 and H100 GPUs.
白話說:這要一張資料中心等級的顯示卡,一般的筆電、桌機跑不動。所以我是借雲端服務的機器跑的——Day 21 那次生成,就是把三視圖上傳到官方掛出來的線上試用頁,用免費配額借了一輪別人的 GPU,跑一次沒問題。
但我有 20 個角色。
如果這條路線要用在整個專案上,代表每個角色都要跑一次,而且之後每次角色設計有調整,都要重跑一次。這代表:
這條路線的產物是不可重製的。 它是一次性的,而我的專案不是一次性的。
整理一下:
| 維度 | 問題 | 我在 Day 20 有寫進限制嗎 |
|---|---|---|
| 體積 | 12.59 MiB vs 目標頁 34 KB | 有寫,但寫成形容詞,擋不住 |
| 可編輯性 | 拓撲不可控,要改得換工具鏈 | ❌ 沒寫 |
| 可變性 | 貼圖烤死,換色要重烤 | 有寫,但沒排序,沒發現它最硬 |
| 可重製性 | 需要 24GB VRAM,20 個角色不可行 | ❌ 沒寫 |
四個裡面兩個完全不在我的清單上。
而「模型生得像不像」(那個我唯一會在評估時看的東西)在這四個問題裡一次都沒出現。
這是我從這件事學到最重要的東西:評估生成式資產的時候,最容易看到的維度(品質)通常不是決定成敗的維度。
因為品質是 demo 會展示的東西,而上面這四個是要等你真的把它接進系統才會浮現的東西。
代價一:這四個問題我是逐一撞到的,不是一次列出來的。
我先發現太大,想壓縮。壓縮的過程中發現拓撲改不了。放棄壓縮之後開始接主題色,才發現貼圖是烤死的。最後想到量產才意識到 GPU 的問題。
每一步都是「解決上一個問題時撞到下一個」。 上面那張表是事後整理的,不是我當初的分析框架。
代價二:我沒有認真嘗試優化。
有一些標準做法我沒試:網格簡化、貼圖降解析度、用壓縮格式。這些可能可以把 12.59 MiB 壓到 2–3 MiB。
我沒試的原因是:即使壓縮成功,問題三(換色)和問題四(重跑)依然存在。 而問題三是我的硬限制。
所以這篇不是說「image-to-3D 的產物太大不能用」,是說在我的限制組合下,壓縮也救不了它。 別的專案結論可能完全相反。
代價三:這顆模型現在躺在硬碟上沒有任何用途。
13 MB,四張貼圖,一次生成的成本。它沒有被刪掉,因為它是這整段經驗的證物;但它在專案裡沒有任何角色。
一、評估生成式資產,用這四個問題,不要只看品質。
品質是 demo 展示的維度,這四個是整合時才會痛的維度。
二、「烤死」是評估生成式產物的關鍵詞。
任何生成流程都會把一些「可變的東西」轉成「固定的東西」。問題不是它有沒有烤,是它烤掉了哪些你之後想改的東西。
先列出「之後我會想改什麼」,再看這個流程會不會把它烤掉。
三、可重製性是一個獨立的限制,要單獨寫下來。
問題四是最容易被忽略的,因為它在你成功產出東西的當下完全沒有症狀。你剛剛才做出來,怎麼會做不出來?
判斷句:半年後換一台電腦,我能不能從原始輸入重新產出一模一樣的東西?
不能的話,這個產物就是一次性的。一次性的東西可以用在一次性的場合,但不能當成專案的地基。
明天 Day 23,講我轉向之後做的第一件事:在動手之前,先寫一份「像到什麼程度算合格」的契約。它只有三個欄位,但它把「像不像」從主觀爭論變成可以逐項打勾的驗收清單。