Lulucat

Kimi K3 สร้างลายเส้นปากกาเฟาน์เทนดิจิทัลได้อย่างไร

Gaoge ZhangGaoge Zhang

เราต้องการลายเส้นปากกาเฟาน์เทนแบบมีทิศทางเหมือน Freeform ใน Lulucat Notes สเปกมีเพียงภาพหน้าจอของลายมือ หลังผ่านสองโมเดล คำถามชี้ขาดหนึ่งข้อ และปลายปากการูปวงรี ปากกาน้ำหมึกก็เขียนได้อย่างที่ควรจะเป็น งานวิจัย โค้ด และการตรวจสอบทำโดย Kimi K3 บน Fireworks

อัปเดตเมื่อ

ลายมือภาษาอังกฤษแบบตัวเขียนอ่านว่า “fountain pen” อยู่เหนือบรรทัดอักษรวิจิตรจีนบนกระดาษสีครีม วาดด้วยปากกาเฟาน์เทนปลายวงรี: หนาที่เส้นแนวตั้ง บางที่เส้นแนวนอน

เรนเดอร์โดยไปป์ไลน์ Metal ของ Lulucat Notes ซึ่งเป็นหัวข้อของโพสต์นี้

Lulucat Notes มีปากกาธรรมดาและไฮไลเตอร์ เครื่องมือถัดไปในโรดแมปคือปากกาน้ำหมึก — ปากกาเฟาน์เทนแบบมีทิศทางที่คุณรู้จักจาก Apple Notes และ Freeform ซึ่งเส้นแนวตั้งจะหนา ส่วนเส้นแนวนอนจะบาง สเปกของเครื่องมือนี้ไม่ใช่เอกสาร แต่เป็นภาพหน้าจอ: ลายมือสามบรรทัด คำว่า “pencilkit”, “无边记” และ “这种有方向的水笔能力” — ความสามารถของปากกาน้ำหมึกแบบมีทิศทางนี้

เช่นเดียวกับไปป์ไลน์ Metal ก่อนหน้านี้ งานนี้ทำโดย Kimi K3 โมเดลโอเพนซอร์สของ Moonshot ที่ทำงานบน Fireworks ทั้งการวัด การสร้างแบบจำลอง โค้ด และการตรวจสอบ คนให้ภาพหน้าจอ ตอบคำถามหนึ่งข้อ และตัดสินความรู้สึกบน 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 การฟิต กับจุด 66° ให้ค่า — เกือบเป็นเส้นตรงตาม และมีค่าสูงสุดบนแกนแนวตั้ง กล่าวได้ว่าเป็นปลายปากกาอิตาลิก: ร่องแนวนอนที่การเขียนแนวนอนไม่เคยเปิดออก

เวอร์ชันที่หนึ่ง: ความกว้างจากทิศทาง

โมเดลแรกตรงไปตรงมา คำนวณทิศทางสำหรับทุก input point แมปทิศทางเป็นความกว้างผ่านเส้นโค้งที่ฟิตไว้ แล้วอบผลลัพธ์เข้าไปในรัศมีของจุดตอนรับข้อมูล การประเมินทิศทางทำแบบใช้ข้อมูลในอดีตเท่านั้น — ค่าเฉลี่ยถ่วงน้ำหนักที่ลดลงแบบเอ็กซ์โพเนนเชียลจากจุดไม่กี่จุดท้ายของส่วนโค้ง คำนวณในปริภูมิมุมสองเท่าเพื่อไม่ให้การกลับทิศทางหักล้างค่าประเมิน ใช้เฉพาะจุดที่ผ่านมาแล้ว จึงทำให้ลายเส้นสดและลายเส้นที่ยืนยันแล้วตรงกันทุกไบต์

ตัวสร้างผ่าน unit check ของมัน เส้นทางสังเคราะห์แนวนอน แนวตั้ง 45° และการกลับทิศทั้งหมดอบความกว้างตามทฤษฎีได้ — 0.99, 2.52, 2.00 และ 0.99 points การเรนเดอร์ไม่ต้องใช้โค้ดใหม่เลย ลายเส้นที่อบรัศมีแล้วเป็นเพียงสายโซ่ของแสตมป์กลม และ point-sprite pipeline ของเราก็วาดสิ่งเหล่านี้อยู่แล้ว

บน iPad คนใช้เวลาประมาณสิบวินาทีในการปฏิเสธมัน: “นี่เป็นปากกาศิลป์ ไม่ใช่ปากกาน้ำหมึก”

คำอธิบายสองแบบเข้ากับภาพเดียวกัน

ทำไมมันถึงให้ความรู้สึกผิด? มีคำอธิบายที่เป็นไปได้สองแบบ และภาพหน้าจอไม่สามารถแยกทั้งสองแบบออกจากกันได้:

  • ล็อกทิศทาง เรขาคณิตของปลายปากกาบังคับให้เส้นแนวนอนบางและเส้นแนวตั้งหนา ไม่ว่ามือจะทำอะไร นั่นคือสิ่งที่เวอร์ชันหนึ่งนำไปใช้
  • แรงกดและความเร็ว ปากกาขับเคลื่อนด้วยแรงกด และรูปแบบในตัวอย่างเป็นเพียงพลวัตของลายมือ เส้นลากลงจะถูกกดหนักตามธรรมชาติ ส่วนเส้นเชื่อมจะเร็วและเบาตามธรรมชาติ

ทั้งสองแบบสร้างภาพหน้าจอที่มีเส้นแนวนอนบางและเส้นแนวตั้งหนาได้ ความแตกต่างอยู่ที่สิ่งที่จะเกิดขึ้นเมื่อกดแรงบนเส้นแนวนอน เวอร์ชันหนึ่งทำให้มันบางอยู่ ปากกาที่ขับเคลื่อนด้วยแรงกดจะทำให้มันหนาขึ้น เราจึงถามคนหนึ่งคำถามว่า เส้นแนวนอนที่กดแรงควรหนาขึ้นหรือไม่

“ไม่ เส้นแนวนอนยังบางอยู่”

ยืนยันได้ว่าเป็นการล็อกทิศทาง แต่ยังมีบางอย่างผิดอยู่ เพราะเวอร์ชันหนึ่งก็ล็อกทิศทางเช่นกัน

คำตอบอยู่ที่ปลายลายเส้น

เบาะแสถัดไปอยู่ที่ด้านบนของส่วนยกขึ้น เมื่อขยายดูลำต้นของ “l” และ “k” ในตัวอย่าง จะเห็นสองอย่าง เส้นตรงคงความกว้างเดิมตั้งแต่ต้นจนจบ และปลายลายเส้นเป็น รอยตัดเฉียงเรียบ — รูปร่างของปลายปากกาสิ่วขณะยกออกจากกระดาษ ไม่ใช่จุดกลม และไม่ใช่การเรียวจากแรงกด

ภาพตัดใกล้ของลำต้นส่วนยกขึ้นที่เขียนด้วยมือสองเส้นจากตัวอย่าง แต่ละเส้นจบด้านบนด้วยรอยตัดเฉียงเรียบ และมีความหนาสม่ำเสมอตลอดลำต้น

นี่คือความหมายของ “ความรู้สึกเหมือนปากกาศิลป์” เวอร์ชันหนึ่งจำลอง รูปลักษณ์ ของตัวอย่าง — ความกว้างเป็นฟังก์ชันของทิศทางที่ประเมินได้ — แต่ไม่ได้จำลอง ปากกา ตัวประมาณทิศทางเป็นเซนเซอร์ มันสั่นเมื่อเจออินพุตที่มีสัญญาณรบกวน ล้าหลังตรงมุม และทำให้ปลายลายเส้นทุกเส้นกลายเป็นวงกลม ปลายปากกาจริงไม่มีปัญหาเหล่านี้ เพราะมันไม่ได้คำนวณอะไรเลย ความกว้างคือเรขาคณิต

เวอร์ชันที่สอง: ปลายปากการูปวงรี

โมเดลสุดท้ายไม่มีตัวประมาณทิศทางเลย ปลายปากกาคือวงรีที่จัดแนวไว้ แกนยาวเป็นแนวนอน แกนสั้นคงที่ แสตมป์ของวงรีนี้ถูกวางถี่ตามเส้นทางของลายเส้น และทุกอย่างที่เหลือเกิดจากเรขาคณิต:

  • ลายเส้นแนวนอนเคลื่อนที่ไปตามขอบเปิด จึงมีความกว้าง เสมอ — เป็นเส้นบางคงที่ไม่ว่าแรงกดจะเท่าใด ตรงตามสเปกที่ยืนยันไว้

  • ลายเส้นแนวตั้งตัดผ่านแกนยาวทั้งหมด: ซึ่งเป็นด้านที่หนา

  • ลายเส้นเฉียงใช้ความกว้างคอร์ดของวงรีในแนวตั้งฉากกับทิศทางการเคลื่อนที่

  • ปลายลายเส้นเป็นรอยตัดรูปวงรี — ปลายเรียบทรงปลายปากกาจากตัวอย่าง ซึ่งได้มาโดยไม่ต้องทำอะไรเพิ่ม

  • แรงกดปรับขนาดเฉพาะแกนยาว ดังนั้นแรงกดจึงเพิ่มปริมาณหมึกบนเส้นลากลง และไม่มีทางทำให้เส้นแนวนอนอ้วนขึ้น

จากการวัดตัวอย่าง เราคงค่า pt และ pt ไว้ ซึ่งเป็นอัตราส่วน 2.5 เดิม แสตมป์วางห่างกัน 0.5 points ตามแนวส่วนโค้ง เมื่อใช้รัศมีสั้น 0.55 point คลื่นที่ขอบในกรณีเลวร้ายที่สุดมีขนาดประมาณหนึ่งในสิบของพิกเซล

การเรนเดอร์ต้องการ fragment shader ใหม่เพียงตัวเดียวและไม่ต้องแก้ไขอย่างอื่น รูปแบบ vertex — ตำแหน่ง เส้นผ่านศูนย์กลาง สี — มีทุกอย่างอยู่แล้ว เส้นผ่านศูนย์กลางคือแกนยาว ส่วนแกนสั้นเป็น uniform ต่อ pass เชดเดอร์ประเมิน ellipse SDF ด้วย ramp การครอบคลุมครึ่งพิกเซลแบบเดียวกับแสตมป์กลมของเรา ซึ่งทำให้เปรียบเทียบในระดับพิกเซลกับ reference implementation ของ Core Graphics (fillEllipse ต่อแสตมป์) ได้ การตรวจสอบผ่านขั้นตอนเดิม คลังข้อมูลสังเคราะห์ที่มีลายเส้นปากกาเฟาน์เทนสองเส้นถูกเรนเดอร์ด้วยทั้งสองวิธีและเปรียบเทียบทีละพิกเซล — ไม่พบความแตกต่างเชิงโครงสร้าง และลายเส้นแนวนอนที่สุ่มตรวจวัดได้ 12 px ในเรนเดอร์ทั้งสองตัว

การเรนเดอร์คำว่า “fountain” ที่เขียนด้วยมือแบบเดียวกันสองแบบวางข้างกัน ภาพซ้ายวาดด้วยแสตมป์กลมที่มีเส้นผ่านศูนย์กลางปรับตามทิศทาง จึงเห็นปลายมน ภาพขวาวาดด้วยแสตมป์วงรี จึงเห็นปลายตัดเรียบแบบปลายปากกาและความกว้างที่คงที่กว่า

เวอร์ชันหนึ่งอยู่ซ้าย เวอร์ชันสองอยู่ขวา อินพุตลายมือเดียวกัน ไปป์ไลน์เดียวกัน ปลายลายเส้นบอกคำตอบ

บน iPad ปากกาใหม่ผ่านทันที: “好,很好” — ดี ดีมาก

เก็บความพลาดไว้

เวอร์ชันหนึ่งไม่ได้ถูกทิ้ง มันเป็นแปรงที่น่าสนใจจริง ๆ เพียงแต่ไม่ใช่ปากกาน้ำหมึก ดังนั้นมันจึงถูกส่งออกเป็นรายการแรกในเมนูแปรงทดลองใหม่ ภายใต้ชื่อที่คนตั้งให้: 漏水的圆珠笔 หรือปากกาลูกลื่นที่รั่ว แถบเครื่องมือมีปุ่มรูปขวดทดลองเพิ่มขึ้น ปุ่มนี้เปิดรายการข้อความของการทดลอง การเพิ่มรายการถัดไปใช้เพียงหนึ่งบรรทัดใน registry โมเดลที่ถูกปฏิเสธไม่ใช่งานที่สูญเปล่า หากนำออกใช้เป็นการทดลองที่ติดป้ายไว้อย่างชัดเจนได้

วนซ้ำตลอดหนึ่งค่ำคืน

วงจรทั้งหมด — วัด สร้างแบบจำลอง สร้าง ลงอุปกรณ์ ถาม สร้างแบบจำลองใหม่ สร้างใหม่ ตรวจสอบ — ใช้เวลาหนึ่งค่ำคืน Kimi K3 บน Fireworks ดำเนินการด้านเทคนิคทั้งหมดของวงจร ออกแบบการสแกนเพื่อวัด เสนอคำถามชี้ขาดแทนการเดาอีกครั้ง ลบตัวประมาณทิศทางของตัวเองเมื่อหลักฐานเปลี่ยน และขยาย pixel-diff harness ก่อนแตะต้องแอป ความเร็วในการอนุมานของ Fireworks ทำให้วงจรยังโต้ตอบได้ diff ขนาดยาวของ Metal และ Swift สคริปต์วิเคราะห์พิกเซล และเครื่องมือ corpus มาถึงเร็วพอ จนคอขวดที่เหลืออยู่คือการตัดสินของมนุษย์

รูปแบบการทำงานร่วมกันเป็นแบบเดียวกับที่เราเขียนไว้ในโพสต์เกี่ยวกับไปป์ไลน์ Metal และยังใช้ได้อีกครั้ง โมเดลเร็วและแม่นยำ ส่วนมนุษย์รับผิดชอบรสนิยมและการตรวจสอบตั้งแต่ต้นจนจบ คำติชมจากความรู้สึกเพียงประโยคเดียว — “ปากกาศิลป์ ไม่ใช่ปากกาน้ำหมึก” — ก็เพียงพอให้โมเดลระบุข้อผิดพลาดในการสร้างแบบจำลองที่แน่นอน และแทนที่มันด้วยการออกแบบที่ง่ายกว่า

โมเดลที่ถูกต้องมีขนาดเล็กกว่าโมเดลที่ผิด เวอร์ชันสองถูกส่งออกพร้อมชิ้นส่วนที่เคลื่อนไหวน้อยกว่าเวอร์ชันหนึ่ง — ไม่มีตัวประมาณ ไม่มีหน้าต่างปรับให้เรียบ ไม่มีเลขชี้กำลังที่ฟิตไว้ ปลายปากกาไม่ได้คำนวณความกว้าง ปลายปากกานั่นแหละ คือ ความกว้าง