ตอนที่ 7: ความยาวและ Normalization: สกัดพลังบริสุทธิ์แห่ง 'ทิศทาง' ด้วยเวกเตอร์หนึ่งหน่วย!

1. 🎯 ตอนที่ 7: ความยาวเวกเตอร์และการทำ Normalization
2. 📖 เปิดฉาก (The Hook)
สวัสดีครับนักสร้างโลกจินตนาการทุกคน! ยินดีต้อนรับกลับสู่โรงงานผลิตภาพในซีรีส์ ปูพื้นฐานคณิตศาสตร์สู่โลกคอมพิวเตอร์กราฟิก ครับ
จากตอนที่ผ่านๆ มา พวกเราได้ใช้เวกเตอร์ (Vector) ในการทำสิ่งต่างๆ มากมาย ทั้งการหาทิศทางการเคลื่อนที่ การโจมตีศัตรู หรือแม้แต่การสะท้อนแสงเงา แต่มีปัญหาคลาสสิกอันหนึ่งที่โปรแกรมเมอร์มือใหม่มักจะเจอเวลาทำเกมครับ… ลองจินตนาการว่าคุณเขียนโค้ดให้ตัวละครเดินหน้าด้วยความเร็ว 1 หน่วย และเดินไปด้านข้างด้วยความเร็ว 1 หน่วย แต่พอผู้เล่นกดปุ่มเดินเฉียง (เฉียงขึ้นพร้อมกับไปด้านข้าง) ตัวละครดันวิ่งเร็วปรี๊ดดดดดด! เร็วกว่าตอนเดินตรงๆ ซะงั้น! หรืออีกตัวอย่างคือ พอเราคำนวณแสงสว่างจากหลอดไฟที่อยู่ไกลออกไป โมเดลของเรากลับสว่างจ้าจนแสบตา ขาวโพลนไปหมด!
ทำไมถึงเป็นแบบนั้น? คำตอบคือ “ความยาว (Magnitude)” ของลูกศรเวกเตอร์มันเข้ามาแทรกแซงการคำนวณของเราครับ ในโลกของการพัฒนาเกมหรือกราฟิกเอนจิน หลายๆ ครั้งเราต้องการแค่ “ทิศทางอันบริสุทธิ์” โดยไม่ต้องสนใจว่าลูกศรมันจะยาวแค่ไหน และนี่แหละครับคือที่มาของเวทมนตร์บทสำคัญที่กราฟิกโปรแกรมเมอร์ทุกคนต้องร่ายให้ติดเป็นนิสัย… สิ่งนั้นเรียกว่า “การทำ Normalization” เพื่อสร้าง “เวกเตอร์หนึ่งหน่วย (Unit Vector)” นั่นเองครับ!
3. 🧮 จากตัวเลขสู่กราฟิก (Math to Graphics Foundation)
ก่อนที่เราจะสกัดเอาทิศทางบริสุทธิ์ออกมาได้ เราต้องรู้วิธีวัดความยาวของลูกศรเวกเตอร์ซะก่อน ในทางคณิตศาสตร์ เราเรียกความยาวนี้ว่า Magnitude หรือ Norm (เขียนแทนด้วยสัญลักษณ์ขีดคร่อมสองเส้น $|\mathbf{v}|$ หรือบางครั้งก็ขีดเดียว $|\mathbf{v}|$)
สูตรการหาความยาวนั้น แท้จริงแล้วก็คือ “ทฤษฎีบทพีทาโกรัส (Pythagorean Theorem)” ที่เราคุ้นเคยกันดีในวิชาเรขาคณิตนั่นเองครับ! หากเรามีเวกเตอร์ 3 มิติ $\mathbf{v} = [v_x, v_y, v_z]$ ความยาวของมันจะคำนวณได้จากการเอาคอมโพเนนต์แต่ละแกนมายกกำลังสอง บวกกัน แล้วถอดรากที่สอง: $$ |\mathbf{v}| = \sqrt{v_x^2 + v_y^2 + v_z^2} $$
นอกจากนี้ หากยังจำตอนที่ 5 ได้ เราสามารถหาความยาวของเวกเตอร์ได้จากการเอาเวกเตอร์มา Dot กับตัวมันเอง แล้วค่อยถอดรากที่สอง ซึ่งเป็นสูตรที่สวยงามมากในพีชคณิตเชิงเส้น:
$$ |\mathbf{v}| = \sqrt{\mathbf{v} \cdot \mathbf{v}} $$
4. 📐 เจาะลึกเรขาคณิตและการประมวลผลภาพ (Geometry & Image Processing)
ในหลายๆ สถานการณ์ เราตั้งคำถามแค่ว่า “วัตถุหันหน้าไปทางไหน?” หรือ “แสงส่องมาจากทิศใด?” เราไม่ได้สนใจระยะทางเลยสักนิด!
ดังนั้น เราจึงต้องสร้าง Unit Vector (เวกเตอร์หนึ่งหน่วย) ซึ่งก็คือเวกเตอร์ที่มีความยาวเท่ากับ 1 พอดีเป๊ะ! (เปรียบเสมือนลูกศรที่ปลายของมันไปแตะอยู่บนขอบของวงกลมรัศมี 1 หน่วยหรือ Unit Circle พอดี) ในสัญลักษณ์ทางคณิตศาสตร์ เรามักจะสวม “หมวก” ให้มันเพื่อบอกความพิเศษนี้ เช่น $\hat{\mathbf{v}}$ (อ่านว่า v-hat)
กระบวนการแปลงเวกเตอร์ธรรมดาให้กลายเป็นเวกเตอร์หนึ่งหน่วย เราเรียกว่า Normalization ครับ วิธีทำก็ง่ายดายราวกับร่ายมนตร์ เพียงแค่ เอาความยาวของตัวมันเอง ไปหารคอมโพเนนต์ทุกๆ ตัวในเวกเตอร์นั้น (ซึ่งก็คือการคูณด้วยสเกลาร์ $1/|\mathbf{v}|$ นั่นเอง): $$ \hat{\mathbf{v}} = \frac{\mathbf{v}}{|\mathbf{v}|} $$
ข้อควรระวังปราบเซียน: กฎเหล็กคือเราไม่สามารถ Normalize เวกเตอร์ศูนย์ (Zero Vector) $\mathbf{0} = $ ได้นะครับ! เพราะความยาวของมันคือ 0 และในทางคณิตศาสตร์เราไม่สามารถหารด้วย 0 ได้ ใน Game Engine หากคุณเผลอสั่ง Normalize เวกเตอร์ศูนย์ เกมของคุณอาจจะแครช (Crash) หรือตัวละครหายวับไปในหลุมดำ (NaN) ได้ทันที!
5. 🎮 เวทมนตร์นี้ในโลกความจริง (Real-World Game Applications)
ทำไมเราถึงต้องใช้ Unit Vector กันล่ะ? ลองมาดูตัวอย่างคลาสสิกในโลกของเกมกันครับ:
- แก้ปัญหาเดินเฉียงแล้ววิ่งเร็ว (Diagonal Movement): สมมติว่าคุณรับค่าการกดปุ่มจากผู้เล่น (W และ D) ได้เวกเตอร์ความเร็วเป็น $\mathbf{v} =$ หากคุณนำมันไปใช้เลย ความยาวของมันคือ $\sqrt{1^2 + 1^2} = \sqrt{2} \approx 1.414$ แปลว่าตัวละครของคุณจะวิ่งเฉียงเร็วกว่าวิ่งตรงๆ เกือบ 40%! วิธีแก้คือ คุณต้องสั่ง
v = normalize(v)เพื่อบีบลูกศรให้ยาว 1 ก่อน จากนั้นค่อยเอาไปคูณกับค่าความเร็ว (Speed Scalar) ที่กำหนดไว้ - การสะท้อน (Reflection): เวลาลูกบิลเลียดเด้งชนขอบโต๊ะ หรือแสงสะท้อนกระจก สูตรการคำนวณเวกเตอร์สะท้อน $\mathbf{r} = \mathbf{i} - 2(\mathbf{i} \cdot \mathbf{n})\mathbf{n}$ จะทำงานได้ถูกต้องเป๊ะๆ ก็ต่อเมื่อ $\mathbf{n}$ (Normal Vector หรือเวกเตอร์ตั้งฉากพื้นผิว) เป็น Unit Vector เท่านั้นครับ!
- การหาทิศทางการมอง (Line of Sight / Direction): การหาเวกเตอร์ทิศทางจากศัตรูไปหาผู้เล่น เราจะใช้สูตร $\mathbf{d} = \text{Player} - \text{Enemy}$ เวกเตอร์ $\mathbf{d}$ ที่ได้จะยาวเท่ากับระยะห่างจริง แต่ถ้าเราแค่ต้องการสั่งให้กระสุนพุ่งไปในทิศนั้น เราต้อง
normalize(d)เพื่อเอาทิศทางบริสุทธิ์ ก่อนที่จะคูณความเร็วกระสุนเข้าไป
6. 🏭 เปิดโรงงานผลิตภาพ (The Graphics Pipeline)
ลึกลงไปใน Graphics Pipeline โดยเฉพาะใน Pixel Shader / Fragment Shader การ Normalization คือสิ่งที่ขาดไม่ได้เลยในการทำงานของระบบแสงและเงา!
นึกถึงทฤษฎี Lambertian Shading (จากตอนที่ 5) ความสว่างของพื้นผิวเกิดจากการทำ Dot Product ระหว่างเวกเตอร์ตั้งฉากพื้นผิว ($\mathbf{n}$) และทิศทางของแสง ($\mathbf{l}$) ตามสูตร: $$ \text{Brightness} = \max(0, \mathbf{n} \cdot \mathbf{l}) $$ เวทมนตร์นี้ตั้งอยู่บนเงื่อนไขที่ว่า $\mathbf{n}$ และ $\mathbf{l}$ ต้องมีความยาวเป็น 1 เท่านั้น! เพราะถ้าลูกศรทั้งสองมีความยาวเป็น 1, ผลลัพธ์จากการ Dot กันจะได้ค่า $\cos \theta$ พอดี (ซึ่งมีค่าอยู่ระหว่าง -1 ถึง 1 ทำให้ความสว่างไม่โอเวอร์)
แต่เดี๋ยวก่อน! ในขั้นตอน Vertex Shader เมื่อโมเดลของเราถูกสั่งขยายขนาด (Scale) จาก Matrix แปลงร่าง ลูกศรเวกเตอร์ Normal ก็จะถูกขยายให้ยาวขึ้นหรือบิดเบี้ยวไปด้วย เมื่อข้อมูลนี้ถูกส่งต่อเข้าไปใน Pixel Shader ลูกศรมันไม่ได้ยาว 1 หน่วยอีกต่อไปแล้วครับ รุ่นพี่ระดับเทพในวงการกราฟิกเตือนเสมอว่า “การลืม Normalize เวกเตอร์ใน Shader คือความผิดพลาดที่เจอบ่อยที่สุดในการเขียนโปรแกรมคำนวณแสง” ดังนั้น ก่อนจะคำนวณ $\mathbf{n} \cdot \mathbf{l}$ โปรแกรมเมอร์จะต้องจับลูกศรเข้าเครื่องบีบอัด normalize() เสมอ เพื่อให้การลงสีของพิกเซลถูกต้องแม่นยำตามหลักฟิสิกส์!
7. 🏁 บทสรุป (Level Cleared!)
เคลียร์ด่านความรู้เรื่อง Normalization ได้สำเร็จครับ! ตอนนี้เราได้ทราบแล้วว่า เวกเตอร์หนึ่งหน่วย (Unit Vector) คือการสกัดเอาแก่นแท้ของ “ทิศทาง” ออกมาจากเวกเตอร์ และทิ้ง “ความยาว” ที่อาจสร้างความปั่นป่วนในการคำนวณออกไป ไม่ว่าจะเป็นการคุมความเร็วตัวละคร หรือการลงแสงเงาให้โพลิกอน ฟังก์ชัน normalize ก็คืออัศวินขี่ม้าขาวของ Game Programmer เลยล่ะครับ
และในตอนนี้ เราก็ได้เรียนรู้ชิ้นส่วนพื้นฐานของ Vector มาอย่างครบถ้วนแล้ว! ในตอนถัดไป เราจะนำลูกศรและทิศทางเหล่านี้ ไปเจอกับตารางตัวเลขระดับบอส ที่จะเปลี่ยนโลกทั้งใบ พลิกมิติ และย่อขยายจักรวาลได้ในสมการเดียว สิ่งนั้นเรียกว่า “เมทริกซ์ (Matrix)” ครับ เตรียมสมองของคุณให้พร้อม แล้วพบกันตอนหน้า Level Cleared!
สนใจพูดคุยแลกเปลี่ยนเทคนิคการพัฒนาเกม คอมพิวเตอร์กราฟิก หรือออกแบบระบบซอฟต์แวร์? ทีมงาน WP Solution พร้อมให้บริการออกแบบและพัฒนาซอฟต์แวร์แบบครบวงจร ดูรายละเอียดบริการของเราได้ที่: www.wpsolution2017.com หรือพูดคุยปรึกษาเบื้องต้นได้ที่ Line: wisit.p