จากบทความก่อนหน้า Creative Coding ด้วย C++ บน Visual Studio 2022 และ openFrameWorks ที่มีการติดตั้งโปรแกรมเรียบร้อยแล้ว ในรอบนี้เราจะมาเขียนโปรแกรมเบื้องต้นกันด้วยภาษา C++
เรามาเริ่มที่การเขียนโปรแกรมคอมพิวเตอร์กราฟิก 2มิติกันก่อน สิ่งแรกที่เราต้องเข้าใจคือ Computer Graphics Coordinates จริงๆ เป็นหนึ่งในวิชา เรขภาพคอมพิวเตอร์ (Computer Graphic) ปัจจุบันเน้นการใช้เครื่องมือไปหมดแล้วไม่ค่อยเข้าใจหลักของ คณิตศาสตร์ และฟิสิกส์ ไปจนถึง อัลกอริทึม
Computer Graphics Coordinates
ในโลกของคอมพิวเตอร์กราฟิกส์ พิกัด (Coordinates) คือ ค่าตัวเลขที่ใช้ระบุตำแหน่งของจุดหรือวัตถุบนหน้าจอหรือในพื้นที่สามมิติ โดยทั่วไปจะใช้ระบบพิกัดคาร์ทีเซียน (Cartesian coordinate system)
ระบบพิกัดคาร์ทีเซียน
ระบบนี้จะใช้แกนสองแกน (ในพื้นที่สองมิติ) หรือสามแกน (ในพื้นที่สามมิติ) ที่ตั้งฉากกัน
- แกน X: แกนนอน
- แกน Y: แกนตั้ง
- แกน Z: แกนลึก (ในพื้นที่สามมิติ)
จุดตัดของแกนทั้งสอง (หรือสาม) เรียกว่า จุดกำเนิด (Origin) ซึ่งโดยทั่วไปจะมีพิกัด (0, 0) หรือ (0, 0, 0) ตำแหน่งของจุดใดๆ บนหน้าจอหรือในพื้นที่สามมิติ จะถูกระบุด้วยค่าตัวเลขตามแกน X, Y และ Z เช่น จุด (2, 3) หมายถึง จุดที่อยู่ห่างจากจุดกำเนิดไปทางแกน X 2 หน่วย และห่างจากจุดกำเนิดไปทางแกน Y 3 หน่วย
การทำงานของพิกัดในคอมพิวเตอร์กราฟิกส์
พิกัดมีบทบาทสำคัญในการสร้างและจัดการรูปภาพในคอมพิวเตอร์กราฟิกส์ โดยจะใช้พิกัดในการ:
- กำหนดตำแหน่งของวัตถุ: เช่น กำหนดตำแหน่งของจุดยอด (Vertex) ของรูปหลายเหลี่ยม ตำแหน่งของจุดเริ่มต้นและจุดสิ้นสุดของเส้นตรง หรือตำแหน่งของศูนย์กลางของวงกลม
- ควบคุมการแปลงทางเรขาคณิต: เช่น การเลื่อนตำแหน่ง (Translation) การหมุน (Rotation) การปรับขนาด (Scaling)
- สร้างเอฟเฟกต์ต่างๆ: เช่น การบิดเบือน (Distortion) การให้แสงเงา (Shading)
ตัวอย่างการใช้งาน
- ในการวาดเส้นตรง โปรแกรมจะใช้พิกัดของจุดเริ่มต้นและจุดสิ้นสุดในการสร้างเส้นตรงบนหน้าจอ (จุด เส้น ระนาบ) องค์ประกอบศิลป์ไหมละ (Vertex, Edge, Faces)
- ในการสร้างแบบจำลองสามมิติ โปรแกรมจะใช้พิกัดในการกำหนดตำแหน่งของจุดยอดของรูปหลายเหลี่ยมที่ประกอบกันเป็นแบบจำลอง
- ในการสร้างภาพเคลื่อนไหว โปรแกรมจะเปลี่ยนแปลงพิกัดของวัตถุในแต่ละเฟรม ทำให้เกิดการเคลื่อนไหวของวัตถุ กลายเป็นแอนิเมชัน
ชนิดของระบบพิกัด
นอกจากระบบพิกัดคาร์ทีเซียนแล้ว ยังมีระบบพิกัดอื่นๆ ที่ใช้ในคอมพิวเตอร์กราฟิกส์ เช่น
- ระบบพิกัดเชิงขั้ว (Polar coordinate system) ใช้ระยะทางจากจุดกำเนิดและมุมจากแกนอ้างอิงในการระบุตำแหน่งของจุด
- ระบบพิกัดทรงกลม (Spherical coordinate system) ใช้ระยะทางจากจุดกำเนิด มุมจากแกนอ้างอิงในแนวราบ และมุมจากแกนอ้างอิงในแนวตั้งในการระบุตำแหน่งของจุด
ซึ่ง 2 ตัวหลังนี้คงไม่ได้พูดถึงเท่าไรในบทความนี้ครับ
มาเริ่มต้นเขียนโปรแกรมกัน ให้ไปที่ Solution ของเราแล้วเปิดไฟล์ ofApp.cpp (มาจาก openframeworksApp) เปิดไฟล์นี้ขึ้นมาแล้วเขียนโปรแกรมดังนี้:
void ofApp::draw(){
ofDrawCircle(250,300,150);
}
ภาษา C++ โดยใช้ไลบรารี openFrameworks (OF) ซึ่งเป็นไลบรารี โดย
void ofApp::draw(){ ... }
นี่คือฟังก์ชัน draw()
ซึ่งเป็นส่วนหนึ่งของคลาส ofApp
ใน openFrameworks ฟังก์ชันนี้จะถูกเรียกซ้ำ ๆ เพื่อวาดภาพกราฟิกบนหน้าจอ โค้ดที่อยู่ภายในวงเล็บปีกกา {}
จะเป็นคำสั่งที่ใช้ในการวาดภาพ
ofDrawCircle(250, 300, 150);
นี่คือฟังก์ชันจากไลบรารี openFrameworks ที่ใช้สำหรับวาดวงกลม พารามิเตอร์สามตัวในวงเล็บ มีความหมายดังนี้
- 250: พิกัด X ของจุดศูนย์กลางวงกลม (ระยะห่างจากขอบซ้ายของหน้าจอ)
- 300: พิกัด Y ของจุดศูนย์กลางวงกลม (ระยะห่างจากขอบบนของหน้าจอ)
- 150: รัศมีของวงกลม
โค้ดนี้จะวาดวงกลมที่มีจุดศูนย์กลางอยู่ที่พิกัด (250, 300) และมีรัศมี 150 พิกเซล บนหน้าจอ ทุกครั้งที่ฟังก์ชัน draw()
ถูกเรียก วงกลมนี้ก็จะถูกวาดซ้ำ ทำให้เห็นเป็นวงกลมคงที่บนหน้าจอ
เขียนเพิ่มเล็กน้อย:
ofSetColor(255, 0, 0);
ofDrawRectangle(500, 300, 90, 90);
เป็นการเปลี่ยนสีของวงกลมให้กลายเป็นสีเขียว
มาลองศึกษาถึงการประกาศตัวแปรในภาษา C++ กันหน่อย ให้เปิดไฟล์ header library คือ ofApp.h ครับ
ประกาศตัวแปรต่อไปนี้:
int myGlobalVar;
ไฟล์ ofApp.h จะเป็นดังนี้
class ofApp : public ofBaseApp{
public:
void setup();
void update();
void draw();
void keyPressed(int key);
void keyReleased(int key);
void mouseMoved(int x, int y );
void mouseDragged(int x, int y, int button);
void mousePressed(int x, int y, int button);
void mouseReleased(int x, int y, int button);
void mouseEntered(int x, int y);
void mouseExited(int x, int y);
void windowResized(int w, int h);
void dragEvent(ofDragInfo dragInfo);
void gotMessage(ofMessage msg);
int myGlobalVar;
};
กลับไปที่ ไฟล์ ofApp.cpp แล้วเพิ่มในฟังก์ชัน setup() ดังนี้:
void ofApp::setup(){
myGlobalVar = 2350;
}
เพิ่มคำสั่งในฟังก์ชัน draw() ใหม่
void ofApp::draw(){
ofBackground(255); // ตั้งค่าพื้นหลังเป็นสีขาว
// กำหนดขนาดและตำแหน่งของรูปทรง
float size = 200;
float centerX = ofGetWidth() / 2;
float centerY = ofGetHeight() / 2;
// คำนวณพิกัดของจุดยอดแต่ละจุด
ofPoint p1(centerX, centerY - size);
ofPoint p2(centerX + size * cos(PI / 6), centerY - size * sin(PI / 6));
ofPoint p3(centerX + size * cos(PI / 6), centerY + size * sin(PI / 6));
ofPoint p4(centerX, centerY + size);
ofPoint p5(centerX - size * cos(PI / 6), centerY + size * sin(PI / 6));
ofPoint p6(centerX - size * cos(PI / 6), centerY - size * sin(PI / 6));
ofPoint p7(centerX, centerY);
// วาดเส้นเชื่อมต่อจุดต่างๆ
}
วาดรูปหกเหลี่ยมบนหน้าจอ มาดูคำอธิบายทีละส่วนกัน:
1. ofBackground(255);
- บรรทัดนี้ใช้สำหรับตั้งค่าสีพื้นหลังของหน้าจอเป็นสีขาว ค่า 255 หมายถึงสีขาวในระบบ RGB (Red, Green, Blue)
2. กำหนดขนาดและตำแหน่ง
float size = 200;
กำหนดตัวแปรsize
ซึ่งเป็นตัวกำหนดขนาดของรูปหกเหลี่ยม ในที่นี้คือ 200 พิกเซลfloat centerX = ofGetWidth() / 2;
คำนวณพิกัด X ของจุดศูนย์กลางหน้าจอ โดยใช้ฟังก์ชันofGetWidth()
เพื่อดึงความกว้างของหน้าจอ แล้วหารด้วย 2float centerY = ofGetHeight() / 2;
คำนวณพิกัด Y ของจุดศูนย์กลางหน้าจอ ในลักษณะเดียวกัน
3. คำนวณพิกัดจุดยอด
- ส่วนนี้ใช้
ofPoint
ในการกำหนดพิกัดของจุดยอดทั้ง 7 จุดของรูปหกเหลี่ยม (รวมจุดศูนย์กลาง) โดยใช้ตรีโกณมิติในการคำนวณตำแหน่งของจุดยอดแต่ละจุด โดยอ้างอิงจากจุดศูนย์กลาง และขนาดของรูปหกเหลี่ยม p1
ถึงp6
คือจุดยอดของหกเหลี่ยม โดยใช้มุม PI/6 (30 องศา) ในการคำนวณตำแหน่งp7
คือจุดศูนย์กลางของหกเหลี่ยม
ofDrawLine()
ส่วนนี้เป็นการวาดเส้นเชื่อมต่อ, ใช้ฟังก์ชัน ofDrawLine() เพื่อวาดเส้นเชื่อมต่อระหว่างจุดยอดต่างๆ เพื่อสร้างรูปหกเหลี่ยม เราลองเขียนดังนี้:
ofSetColor(0); // ตั้งค่าสีเส้นเป็นสีดำ
ofSetLineWidth(2); // ตั้งค่าความหนาของเส้น
ofDrawLine(p1, p2);
ofDrawLine(p2, p3);
ofDrawLine(p3, p4);
ofDrawLine(p4, p5);
ofDrawLine(p5, p6);
ofDrawLine(p6, p1);
ofDrawLine(p1, p7);
ofDrawLine(p2, p7);
ofDrawLine(p3, p7);
ofDrawLine(p4, p7);
ofDrawLine(p5, p7);
ofDrawLine(p6, p7);
ofDrawLine(p2, p5);
ofDrawLine(p3, p6);
เราจะได้เส้นกราฟิกสวยๆ ปรากฏมากมายให้เราได้ลองทำดูได้เลยครับ