Kimi K3 如何打造數位鋼筆筆畫
我們想在 Lulucat Notes 裡加入 Freeform 那種帶方向的鋼筆筆畫。規格說明只是一張手寫截圖。經過兩個模型、一個判別問題和一個橢圓筆尖,水筆終於寫出了應有的效果。研究、程式碼和驗證由運行在 Fireworks 上的 Kimi K3 完成。

由本文介紹的 Lulucat Notes Metal 管線算繪。
Lulucat Notes 有一支普通筆和一支螢光筆。路線圖上的下一個工具是水筆——你在 Apple Notes 和 Freeform 中見過的那種帶方向的鋼筆:直向筆畫較粗,橫向筆畫較細。這個工具的規格說明不是一份文件,而是一張截圖:三行手寫文字,分別是「pencilkit」、「无边记」和「这种有方向的水笔能力」——也就是這種帶方向的水筆能力。
和之前的 Metal 管線一樣,這項工作由在 Fireworks 上執行的 Moonshot 開放模型 Kimi K3 完成:測量、建模、程式碼和驗證。人只提供截圖、回答一個問題,並在真實 iPad 上判斷手感。
規格說明是一張截圖
靜態圖片無法告訴你筆畫為什麼細;它只能告訴你有多細、在哪裡細。因此第一步是測量。我們逐行逐列掃描截圖,追蹤每道筆畫的中線漂移,從而得到方向,再用該角度的正弦把掃描寬度換算成真實寬度:
| 筆畫 | 方向 | 真實寬度 |
|---|---|---|
| 「l」的上伸筆畫(「pencilkit」) | ≈ 78° | 27.3 px |
| 「k」的豎幹(「pencilkit」) | ≈ 90° | 28 px |
| 力的左撇 | ≈ 66° | 25.7 px |
| 草書連接筆畫 | ≈ 8° | 11 px |
| 中文橫畫(橫) | ≈ 0° | 6–11 px |
粗細比約為 2.5。用
第一版:由方向決定寬度
第一版模型很直觀:為每個輸入點計算方向,透過擬合曲線將方向映射為寬度,並在取樣時把結果烘進該點的半徑。方向以因果方式估計——對弧線上最後幾個點做指數衰減加權平均,並在倍角空間中計算,使移動方向反轉時不會相互抵消。它只使用過去的點,因此即時筆畫和提交後的筆畫逐位元組一致。
建構器通過了單元檢查:合成的水平、垂直、45° 和反轉路徑都寫入了理論寬度——0.99、2.52、2.00 和 0.99 點。算繪完全不需要新增程式碼:半徑已經寫入的筆畫就是一串圓形印章,我們的 point-sprite 管線本來就能繪製它們。
在 iPad 上,人只用約十秒就否決了它:「這是一支美術筆,不是水筆。」
兩個解釋都符合這張圖
為什麼手感不對?有兩個候選解釋,而截圖無法區分它們:
- 方向鎖定。 筆尖幾何形狀強制橫筆變細、直筆變粗,無論手如何移動。這是第一版實作的機制。
- 壓力與速度。 鋼筆由壓力驅動,樣本中的模式只是手寫動力學:下行筆自然會按得更重,連接筆畫自然更快、更輕。
兩種解釋都能產生橫細直粗的截圖。區別在於用力按橫筆時會發生什麼。第一版會讓它保持細;壓感筆會把它變粗。因此我們只問人一個問題:用力按下的橫筆應該變粗嗎?
「不。橫筆保持細。」
方向鎖定得到確認。但問題還沒有解決,因為第一版同樣鎖定了方向。
答案在筆畫的末端
下一個線索在上伸筆畫的頂端。放大樣本中的「l」和「k」豎幹後,可以看到兩件事:直筆從頭到尾保持相同寬度,而筆畫末端是平的斜切口——鑿形筆尖抬離紙面時留下的形狀。不是圓點,也不是壓感收尖。

這就是人所說的「美術筆手感」。第一版建模的是樣本的外觀——把寬度作為估計方向的函數——而不是筆本身。方向估計器是一種感測器:輸入有雜訊時會抖動,遇到轉角會滯後,還會把每個筆畫末端都收成圓形。真正的筆尖沒有這些問題,因為它什麼都不計算。寬度就是幾何形狀。
第二版:橢圓筆尖
最終模型完全沒有方向估計器。筆尖是一個定向橢圓:長軸水平,短軸固定。沿筆畫路徑密集放置這個橢圓的印章,其他一切都由幾何自然產生:
-
橫筆沿著橢圓的開放邊緣移動,因此無論壓力多大,寬度始終是
——一條恆定的細線。這正符合確認過的規格。 -
直筆穿過完整長軸:寬度為
,達到最粗。 -
斜筆的寬度是垂直於行進方向的橢圓弦寬,
-
筆畫末端是橢圓切口——樣本中平坦的筆尖形末端,不需額外處理就能得到。
-
壓力只縮放長軸,
,因此壓力只會增加下行筆的墨量,永遠不會讓橫筆變粗。
根據樣本測量,我們保留
算繪只需新增一個片段著色器,其他都不用改。頂點格式——位置、直徑、顏色——已經包含全部資訊:直徑表示長軸,短軸是每個 pass 的 uniform。著色器使用與圓形印章相同的半像素覆蓋率漸變計算橢圓 SDF,這讓它在像素層級可與 Core Graphics 的參考實作(每個印章呼叫一次 fillEllipse)比較。驗證依照常規流程:包含兩道鋼筆筆畫的合成測試集分別用兩種方式算繪,再逐像素比較——結構差異為零;抽查的一道橫筆在兩個算繪器中都測得 12 px。

左邊是第一版,右邊是第二版。同樣的手寫輸入,同一條管線。末端說明了一切。
在 iPad 上,新筆立即通過了:「好,很好」——好。很好。
留下這個失誤
第一版沒有被丟掉。它是一支確實有趣的筆刷,只是不是水筆。因此,它以人給的名字——漏水的圆珠笔——作為新的實驗筆刷選單第一項發布。工具列增加了一個燒瓶按鈕,點擊後會彈出實驗項目的文字列表;新增下一項只需在註冊表中加一行。被否決的模型只要能作為明確標註的實驗發布,就不算浪費。
一個晚上的迴圈
完整的閉環——測量、建模、建構、上裝置、提問、重新建模、重新建構、驗證——花了一個晚上。運行在 Fireworks 上的 Kimi K3 負責整個技術環節:設計測量掃描,提出判別問題而不是再次猜測,在證據轉向時刪除自己的方向估計器,並在修改 app 前擴充像素差異測試工具。Fireworks 的推理速度讓閉環保持互動性——長篇 Metal 和 Swift diff、像素分析指令稿、語料工具都來得夠快,剩下的瓶頸仍然是人的判斷。
協作模式與我們在 Metal 管線那篇文章中寫的一樣,而且再次奏效:模型快速、精確;人負責品味與端到端驗證。一句基於手感的回饋——「美術筆,不是水筆」——就足以讓模型定位準確的建模錯誤,並用更簡單的設計取代它。
正確的模型比錯誤的模型更小。第二版發布時包含的活動元件比第一版少——沒有估計器,沒有平滑視窗,沒有擬合指數。筆尖不計算寬度。筆尖就是寬度。