(二) 模型變換
模形變換就是指的在世界坐標系中(world space)做“移動”,“旋轉", "縮放"三種操作。
首先要說明的,在Opengl中,是用4x4矩陣進行坐標變換,OpenGL的4x4矩陣是按列排列的,就像下面這樣。

所謂的模型變換,就是對這個矩陣進行變換。
描述三維世界你就得先設計三維模形。 在設計三維模形的時候,它是們於模形坐標系中的,最終它會放置到世界坐標系統中,如果這個模形設計時處理的好的話,放到世界坐標系中時默認就是模形的中心定位於世界坐標系統的中心處。
這裡有一個延伸的知識點:模形放置到世界坐標系時中心不位於世界坐標系中心處,是怎麼回事?有什麼後果? 這個以後我們會做一個擴展性的討論。
上面說的是導入其它設計軟件制作的模形,比如3dsMax制作的模形的情況。如果我們自己用代碼畫的模形,自然是默認就位於世界坐標系統的中心。
這個世界坐標系,就像下圖一樣:
X軸箭頭方向為正,反過來為負
Y軸箭頭方向為正,反過來為負
Z軸箭頭方向正好指向我們的眼睛,因此圖上只是個小點,看不見,它負方向向屏幕裡面。
而下圖中的三維體的中心就剛好們於這個世界坐標系的中心。

下面的演示代碼在世界坐標系統中畫出一個三角形,再對它做一些變換操作。
1 using System;
2 using System.Windows.Forms;
3 using SharpGL;
4
5 namespace blankTest
6 {
7 public partial class Form1 : Form
8 {
9 private int mtype = 0;
10 public Form1()
11 {
12 InitializeComponent();
13
14 }
15
16 private void Form1_Load(object sender, EventArgs e)
17 {
18
19 }
20
21 private void openGLControl1_OpenGLInitialized(object sender, EventArgs e)
22 {
23
24 }
25
26 private void openGLControl1_Resize(object sender, EventArgs e)
27 {
28
29 }
30
31 private void openGLControl1_OpenGLDraw(object sender, PaintEventArgs e)
32 {
33 SharpGL.OpenGL gl = this.openGLControl1.OpenGL;
34
35 gl.ClearColor(0, 0, 0, 1);
36 gl.Clear(OpenGL.GL_COLOR_BUFFER_BIT | OpenGL.GL_DEPTH_BUFFER_BIT);
37
38 switch (mtype)
39 {
40 case 0:
41 translateSample(gl);
42 break;
43 case 1:
44 rotateSample(gl);
45 break;
46 case 2:
47 scaleSample(gl);
48 break;
49 }
50
51 gl.Finish();
52 gl.Flush();
53 }
54
55 private void scaleSample(SharpGL.OpenGL gl)
56 {
57 gl.LoadIdentity();
58 gl.Scale(1f, 1f, 1f);
59 gl.Translate(0, 0, -9f);
60 drawPT(gl);
61
62 gl.LoadIdentity();
63 gl.Scale(5f, 1f, 1f);
64 gl.Translate(0, 1, -9f);
65 drawPT(gl);
66 }
67
68 private void rotateSample(SharpGL.OpenGL gl)
69 {
70 gl.LoadIdentity();
71 gl.Rotate(0,0, 45);
72 gl.Translate(0, 0, -8f);
73 drawPT(gl);
74
75 gl.Rotate(0, 0, 45);
76 gl.Translate(-1, 1,0f);
77 drawPT(gl);
78 }
79
80 private void translateSample(SharpGL.OpenGL gl)
81 {
82 gl.LoadIdentity();
83 gl.Translate(0f, 0f, -3f);
84 drawPT(gl);
85
86 gl.LoadIdentity();
87 gl.Translate(0f, 1f, -3f);
88 drawPT(gl);
89 }
90
91 private void drawPT(SharpGL.OpenGL gl)
92 {
93 gl.PointSize(5f);
94 gl.Begin(OpenGL.GL_TRIANGLES);
95 {
96 gl.Vertex(0.0f, 0f, 0.0f);
97 gl.Vertex(-1.0f, -1f, 0.0f);
98 gl.Vertex(1.0f, -1f, 0.0f);
99 }
100 gl.End();
101 }
102
103 private void btnTranslate_Click(object sender, EventArgs e)
104 {
105 switch (((Button)sender).Name)
106 {
107 case "btnTranslate":
108 mtype = 0;
109 break;
110 case "btnRotate":
111 mtype = 1;
112 break;
113 case "btnScale":
114 mtype = 2;
115 break;
116 }
117 }
118
119 }
120 }
三個函數translateSample(), rotateSample(),scaleSample() 分別演示”平移“,”旋轉“,”縮放”。
我們來談談一些重點的地方:
在“平移”函數中,如果沒有gl.LoadIdentity()函數復位坐標系統到原點位置,那麼執行結果會不同,第二條gl.Translate(0f,1f,-3f)將會是相對於上一條gl.Translate的位置(0f,0f,-3f)移動。
而每次繪制三角形之前做一次gl.LoadIdentity(), 相當於每次繪圖時的中心點坐標都從原點算起。
private void translateSample(SharpGL.OpenGL gl)
{
gl.LoadIdentity();
gl.Translate(0f, 0f, -3f);
drawPT(gl);
gl.LoadIdentity();
gl.Translate(0f, 1f, -3f);
drawPT(gl);
}
gl.Translate中的Z軸設置為0,則物體不可見,因為這時候畫出來的三角形,貼著世界坐標系的Z軸。就像你的眼睛貼跟它零距離一般。
你可以想象成攝像機位於世界坐標系統(0,0,0), 走向Z軸的負方向。
為了說明白這點,博主畫了個示意圖如下:
圖中的柵格面就是世界坐標系上的的網格線,而三維box的底面(紅色面)貼在世界坐標系的Z軸為0的深度上。你得把這個Box向Z軸的負方向走上-1個或者更多個,才可以看見這個Box.

這段程序的運行效果如下:

平移

旋轉

縮放
本節源代碼下載