iT邦幫忙

2026 iThome 鐵人賽

DAY 23
0
AI Engineering

《矽墟》:我把一部科幻小說當成軟體專案來管系列 第 23

Day 23|動手之前先寫「像到什麼程度算合格」,而且目標值要小於 1

  • 分享至 

  • xImage
  •  

模組四|從 2D 到 3D(Day 20–25)

《矽墟》是我正在寫的一部科幻小說,拆成 200 回短篇連載,整部作品用管軟體專案的方式在管:敘事結構寫成規格檔,成品用腳本驗收。這個模組在做的,是其中一個角色的獨立角色誌網頁——使用者往下捲,鏡頭跟著推近拉遠、角色跟著轉;主題色則是點色票按鈕切換,角色身上的顏色跟著一起換。整頁必須是不經過任何建置流程、打開瀏覽器就能跑的單檔 HTML(chenger.html,最後真正上線的那一頁)。

角色的立體模型要從哪裡來,這個模組試了兩條路:一條是 image-to-3D(丟角色的圖片進去,生成流程直接吐出一個 3D 模型),另一條是手工路線——不生成模型,改用程式碼把角色用基本幾何體一塊一塊堆出來。Day 22 講的是 image-to-3D 的產物為什麼沒被採用。今天回到時間軸上更早的地方:手工路線動工之前的那 43 分鐘。

(時序提醒:手工路線是先做的,image-to-3D 是四天後才試的,Day 20 講過這個順序。規格檔的最後修改時間是 07-27 14:08,實作是 14:51,中間差 43 分鐘。這篇講的就是那 43 分鐘做了什麼。)

問題:「像不像」是無法收斂的

我要用基本幾何體把一個角色堆出來。這件事有一個很明顯的問題:

它一定不會完全像。

球體堆出來的頭髮,跟畫出來的頭髮不可能一樣。這不是技術問題,是這條路線的本質。

於是問題變成:要像到什麼程度?

而這個問題如果不先回答,會發生兩件事的其中一件:

  • 無限打磨:永遠可以再加一顆球,永遠可以再細一點。沒有終點。
  • 提早放棄:做到一半覺得「這根本不像」,然後整條路線被否定。

我兩種都經歷過。所以我停下來,先寫了一份契約。

怎麼做:三個欄位

契約我沒有寫在腦子裡,寫成了一份檔案:docs/chenger-sculpt-spec.json——這條手工路線的角色設定檔,一份純 JSON,記的是「這個角色該長成什麼樣、由哪些零件和材質組成」。契約放在整份檔案的最前面:

{
  "name": "Chenger",
  "source": "assets/turnarounds/char-chenger-turnaround.png",
  "subjectClass": "hybrid (chibi character + anchor prop)",
  "complexity": "complex",
  "viewLimits": "front/side/back all provided; underside of skirt and soles inferred",
  "qualityContract": {
    "targetFidelity": 0.72,
    "mustMatch": [ ... ],
    "allowedApproximation": [ ... ]
  }
}

這段是整份檔案的表頭。上面幾欄交代的是背景:叫什麼名字、照哪張圖做(source 指的是這個角色的三視圖檔——正面、側面、背面畫在同一張圖上的設定圖)、屬於哪一類、有多難、來源缺了什麼。

qualityContract 才是這篇的主角,三個部分:一定要對的、可以簡化的、目標分數。

一、mustMatch:不准變的六件事

先寫死不准動的部分。這一欄是給驗收用的,不是拿來當靈感清單——做完之後我要拿它逐條核對:

"mustMatch": [
  "chibi silhouette (~4.5 heads) with oversized twin messy buns + ahoge",
  "orange hair / white wide-sleeve kimono top / black pleated skirt color zones",
  "black-gold layered sleeve cuffs",
  "rope belt with knot + hanging charms and tassels",
  "white platform boots with black soles and orange laces",
  "black iron anchor with chain, tethered by rope"
]

六條,全部是具體的視覺特徵,不是形容詞。

注意第一條:~4.5 heads。這是頭身比,一個可以量的數字。不是「Q 版的比例」,那個沒辦法驗收。

這六條就是驗收條件。做完之後我拿著這張清單一條一條看:頭身比對嗎?丸子頭有嗎?呆毛有嗎?色塊分區對嗎?

六個 yes,就合格。

二、allowedApproximation:明確授權可以偷工的三件事

我不磨那塊粗的了,我在它正中央插了一支旗

然後是反過來的那一欄:明文寫出哪些地方,我准自己做得粗。

"allowedApproximation": [
  "face drawn as stylized mesh parts (no texture painting)",
  "skirt charm icons simplified to discs/tassels",
  "hair strands merged into clustered volumes"
]

這一欄比上一欄更重要,而且是我覺得最少人做的一件事。

它說的是:臉不用畫貼圖(texture,貼在模型表面、決定那塊表面看起來是什麼顏色什麼花紋的圖片),用幾何拼;裙子掛飾的圖案不用畫,簡化成圓盤和流蘇;頭髮不用一根根做,併成團塊。

如果沒有這一欄,會發生什麼?

我做到臉的時候會卡住。因為原圖的臉有細節,而我用幾何體做不出來。然後我會覺得自己做得不夠好,開始想辦法。 可能去研究貼圖、可能加更多幾何體、可能開始懷疑整條路線。

**有了這一欄,臉做成幾何拼貼不是妥協,是規格。**它是一開始就寫進契約的、被允許的做法。

這就是 Day 3 講的那件事:禁止要有去處。 這裡是反過來的版本:限制要有明確的豁免。

一份只有 mustMatch 沒有 allowedApproximation 的規格,實務上等於要求「全部都要對」,因為沒有寫下來的簡化,做的人都會覺得是自己的失職。

三、targetFidelity: 0.72

水到那條線我就把瓢放下,旁邊沒畫線的那桶正在漫出來

一個小於 1 的數字。

這個數字沒有精確的量測方法,我沒有一個演算法可以算出「這個模型跟原圖的相似度是 0.71 還是 0.73」。

它的作用不是量測,是宣告。

它宣告的是:這件事的目標本來就不是 1。

寫下 0.72,等於預先回答了那個做到一半一定會冒出來的問題(「這樣夠像嗎?」)的答案是:它不需要很像,它需要達到約定的程度。

如果我寫 1.0,或者根本不寫,那麼每一個不完美的地方都是一個未完成的任務。清單永遠清不完,因為完美不是一個狀態,是一個方向。

這件事跟 SLO(Service Level Objective,服務水準目標——一套系統事先寫明「我要做到多好」的量化門檻,例如「一年之中有 99.9% 的時間是可用的」)是同一個道理。你不會把可用性目標訂在 100%,因為那會讓每一次抖動都變成事故,而且會讓你為了最後那 0.1% 付出不合比例的代價。訂 99.9% 不是放棄品質,是讓「達標」有定義。

附帶一欄:viewLimits

契約之外還有一欄。它不規定品質,規定的是「我手上的資料到哪裡為止」:

"viewLimits": "front/side/back all provided; underside of skirt and soles inferred"

這一欄講的是來源資訊的缺口:三視圖有正面、側面、背面,但裙子底下和鞋底是沒有的。

那兩個部位只能推測。

寫下來的價值在於:之後如果有人(包括我自己)覺得裙底怪怪的,這裡已經說明它是推測的,不是做錯的。

已知的資訊缺口要明文寫出來,否則它會被誤認為是缺陷。

完整結構

把上面幾欄收在一起,這份檔案的前半段長這樣(右邊是每一欄在做什麼):

qualityContract
├─ targetFidelity      0.72          ← 目標不是 1
├─ mustMatch           6 條          ← 驗收條件
└─ allowedApproximation 3 條         ← 明確授權的簡化

viewLimits                           ← 來源資訊的缺口
subjectClass / complexity            ← 這個任務的類型與難度

然後才是 components(9 個)和 materials(8 組),那些是實作,明天講。

契約在實作前面。

代價

代價一:0.72 這個數字是我掰的。

我沒有辦法解釋為什麼是 0.72 而不是 0.7 或 0.75。它不對應任何量測。

它的功能是心理上的:一個小於 1 的具體數字,比「差不多就好」有力得多,因為它看起來像個規格。

我認為這是合理的用法,但我不想假裝它有嚴謹的基礎。它是一個有效的自我約束工具,不是一個度量。

代價二:mustMatch 的六條也是我選的。

為什麼是這六個特徵?因為我看著三視圖,覺得這六個是「拿掉就認不出是這個角色」的東西。

這個判斷本身是主觀的。契約把主觀從「驗收時」移到了「訂契約時」,沒有消滅主觀,只是把它前置到一個比較冷靜的時刻。

不過這個前置很有價值:訂契約的時候我沒有沉沒成本,驗收的時候我有。

代價三:契約沒有涵蓋動態。

這份契約全部在講靜態外觀。它沒有說「轉起來要順」、沒有說「換色之後要好看」。

而那兩件事後來都需要調整,契約幫不上忙。契約只能保護你想到要保護的東西。

帶走什麼

一、產出前先寫兩份清單:不准變的、准簡化的。

大部分人只會寫第一份。第二份才是讓事情能收工的那一份。

沒有第二份,所有沒被明文允許的簡化都會變成心裡的虧欠,而虧欠會導致無止盡的打磨或中途放棄。

這件事可以直接套用在:外包給別人做、交給 AI 生成、做原型、做 MVP。「哪些可以粗」跟「哪些要精」一樣需要白紙黑字。

二、目標值寫成小於 1 的數字。

不管是相似度、覆蓋率、完成度,寫 100% 等於沒寫,因為它不提供停止條件。

寫一個具體的、小於滿分的數字,你才有辦法回答「現在夠了嗎」。

那個數字精不精確不重要,重要的是它存在。

三、把來源的資訊缺口明文寫出來。

任何從既有素材衍生的工作,都會有「素材沒說的部分」。

  • 三視圖沒有裙底 → 只能推測
  • 需求文件沒寫錯誤處理 → 只能推測
  • 設計稿沒有 hover 狀態 → 只能推測

寫下來的推測是設計決策,沒寫下來的推測是 bug。 差別只在有沒有那一行字。

四、主觀判斷要前置到沒有沉沒成本的時刻。

驗收時判斷「這樣夠好嗎」,你已經投入了大量時間,判斷一定會被沉沒成本污染,不是過度寬鬆(不想重做)就是過度嚴苛(不甘心)。

訂契約的時候你什麼都還沒做,那是最冷靜的時刻。 把判斷放在那裡。


明天 Day 24,講契約底下的實作:怎麼把一張三視圖拆成 9 個元件。每個元件標上拓撲類型(topology,這塊東西的表面本質上屬於哪一種形狀家族)和建構策略:球體、旋轉體、褶皺錐、管狀曲線,以及一個讓 617 行程式碼能被讀懂的欄位設計。


上一篇
Day 22|拿到模型檔之後,問題才開始
下一篇
Day 24|把一張三視圖拆成 9 個元件,先分類再實作
系列文
《矽墟》:我把一部科幻小說當成軟體專案來管28
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言