程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> .NET網頁編程 >> C# >> C#入門知識 >> SharpGL學習筆記(八) 矩陣堆棧和變換的綜合例子: 機器人,sharpgl學習筆記

SharpGL學習筆記(八) 矩陣堆棧和變換的綜合例子: 機器人,sharpgl學習筆記

編輯:C#入門知識

SharpGL學習筆記(八) 矩陣堆棧和變換的綜合例子: 機器人,sharpgl學習筆記


 

我們先引入關於"矩陣堆棧"的官方說法:

OpenGL的矩陣堆棧指的就是內存中專門用來存放矩陣數據的某塊特殊區域。
實際上,在創建、裝入、相乘模型變換和投影變換矩陣時,都已用到堆棧操作。一般說來,矩陣堆棧常用於構造具有繼承性的模型,即由一些簡單目標構成的復雜模型。例如,一輛自行車就是由兩個輪子、一個三角架及其它一些零部件構成的。它的繼承性表現在當自行車往前走時,首先是前輪旋轉,然後整個車身向前平移,接著是後輪旋轉,然後整個車身向前平移,如此進行下去,這樣自行車就往前走了。
矩陣堆棧對復雜模型運動過程中的多個變換操作之間的聯系與獨立十分有利。因為所有矩陣操作函數如LoadMatrix()、MultMatrix()、LoadIdentity()等只處理當前矩陣或堆棧頂部矩陣,這樣堆棧中下面的其它矩陣就不受影響。堆棧操作函數有以下兩個:
PushMatrix(void);
PopMatrix(void);
第一個函數表示將所有矩陣依次壓入堆棧中,頂部矩陣是第二個矩陣的備份;壓入的矩陣數不能太多,否則出錯。

第二個函數表示彈出堆棧頂部的矩陣,令原第二個矩陣成為頂部矩陣,接受當前操作,故原頂部矩陣被破壞;當堆棧中僅存一個矩陣時,不能進行彈出操作,否則出錯。

由此看出,矩陣堆棧操作與壓入矩陣的順序剛好相反,編程時要特別注意矩陣操作的順序。
為了更好地理解這兩個函數,我們可以形象地認為glPushMatrix()就是“記住自己在哪”,glPopMatrix()就是“返回自己原來所在地”。

我特意在"機器人"的代碼裡加入這段演示矩陣堆棧用法的例子, 讓朋友們能看得更明白清楚一些.

 

下面這段測試代碼是想讓第一個長方體縮放為2*1*1, 沿X軸轉45, 第二個長方體縮放為1*1*1,也就是不變形, 置於第一個長方體的左邊貼著.

 1 public void rtest(ref OpenGL gl, float xPos, float yPos, float zPos)
 2         {
 3             gl.PushMatrix();
 4             {
 5                 gl.Color(1f, 0f, 0f);
 6                 gl.Translate(xPos, yPos, zPos);
 7                 gl.Scale(2f, 1f, 1f);
 8                 gl.Rotate(45, 1f, 0f, 0f);
 9                 DrawCube(ref gl, 0, 0, 0, true);
10             }
11             gl.PopMatrix();
12 
13             gl.PushMatrix();
14             {
15                 gl.Color(0f, 1f, 0f);
16                 gl.Translate(xPos - 2f, yPos, zPos);
17                 DrawCube(ref gl, 0, 0, 0, true);
18             }
19             gl.PopMatrix();
20         }

看到的效果就是我想真正想要的.

 

我們修改下代碼, 把PushMatrix()和popMatrix()都注釋了, 就像下面這樣:

 1   public void rtest(ref OpenGL gl, float xPos, float yPos, float zPos)
 2         {
 3             //gl.PushMatrix();
 4             //{
 5                 gl.Color(1f, 0f, 0f);
 6                 gl.Translate(xPos, yPos, zPos);
 7                 gl.Scale(2f, 1f, 1f);
 8                 gl.Rotate(45, 1f, 0f, 0f);
 9                 DrawCube(ref gl, 0, 0, 0, true);
10             //}
11             //gl.PopMatrix();
12 
13             //gl.PushMatrix();
14             //{
15                 gl.Color(0f, 1f, 0f);
16                 gl.Translate(xPos - 2f, yPos, zPos);
17                 DrawCube(ref gl, 0, 0, 0, true);
18             //}
19             //gl.PopMatrix();
20         }

然後結果是這樣的:

顯示, 第二個DrawCube()受到了上面那些變換操作的影響, 像是繼承了上面的那個長方體的旋轉與縮放一樣.

因此, 在這裡如果兩個DrawCube()操作在其首尾各加入棧出棧功能括起來, 那麼這兩次DrawCube()就相互獨立了起來, 不會相互影響. 

 

最後我們給出一個運用"變換"的綜合性的例子, 它畫了一個手腳能動的機器人, 場景做360度旋轉, 可以觀察到機器人每個面.

這個例子改編自 徐明亮的《OpenGL游戲編程》這本書裡的一個例子, 原書例子是C++的代碼.

先貼出源代碼:

 

  1 using System;
  2 using System.Collections.Generic;
  3 using System.ComponentModel;
  4 using System.Data;
  5 using System.Drawing;
  6 using System.Linq;
  7 using System.Text;
  8 using System.Windows.Forms;
  9 using SharpGL;
 10 
 11 namespace SharpGLWinformsApplication1
 12 {
 13     public partial class SharpGLForm : Form
 14     {
 15         public float angle;                  // 機器人繞視點旋轉的角度
 16         float[] legAngle = new float[2];     // 腿的當前旋轉角度
 17         float[] armAngle = new float[2];     // 胳膊的當前旋轉角度
 18 
 19         bool leg1 = true;                    // 機器人腿的狀態,true向前,flase向後
 20         bool leg2 = false;
 21         bool arm1 = true;   
 22         bool arm2 = false;
 23 
 24         private float rotation = 0.0f;
 25         public SharpGLForm()
 26         {
 27             InitializeComponent();
 28             angle = 0.0f;                    // 設置初始角度為0
 29             legAngle[0] = legAngle[1] = 0.0f;
 30             armAngle[0] = armAngle[1] = 0.0f;
 31         }
 32 
 33         private void openGLControl_OpenGLDraw(object sender, PaintEventArgs e)
 34         {
 35             OpenGL gl = openGLControl.OpenGL;
 36             gl.Clear(OpenGL.GL_COLOR_BUFFER_BIT | OpenGL.GL_DEPTH_BUFFER_BIT);
 37 
 38             gl.LoadIdentity();
 39             gl.Rotate(rotation, 0.0f, 1.0f, 0.0f);
 40 
 41             drawGrid(gl);
 42             DrawRobot(ref gl, 0, 0, 0);
 43             //rtest(ref gl, 0, 0, 0);
 44             rotation += 1.0f;
 45         }
 46 
 47 
 48 
 49 
 50         private void openGLControl_OpenGLInitialized(object sender, EventArgs e)
 51         {
 52             OpenGL gl = openGLControl.OpenGL;
 53             gl.ClearColor(0, 0, 0, 0);
 54         }
 55 
 56         private void openGLControl_Resized(object sender, EventArgs e)
 57         {
 58             OpenGL gl = openGLControl.OpenGL;
 59 
 60             gl.MatrixMode(OpenGL.GL_PROJECTION);
 61 
 62             gl.LoadIdentity();
 63             gl.Perspective(60.0f, (double)Width / (double)Height, 0.01, 100.0);
 64             gl.LookAt(-5, 5, 15, 0, 0, 0, 0, 1, 0);
 65             gl.MatrixMode(OpenGL.GL_MODELVIEW);
 66         }
 67 
 68         //測試例子
 69         public void rtest(ref OpenGL gl, float xPos, float yPos, float zPos)
 70         {
 71             gl.PushMatrix();
 72             {
 73                 gl.Color(1f, 0f, 0f);
 74                 gl.Translate(xPos, yPos, zPos);
 75                 gl.Scale(2f, 1f, 1f);
 76                 gl.Rotate(45, 1f, 0f, 0f);
 77                 DrawCube(ref gl, 0, 0, 0, true);
 78             }
 79             gl.PopMatrix();
 80 
 81             gl.PushMatrix();
 82             {
 83                 gl.Color(0f, 1f, 0f);
 84                 gl.Translate(xPos - 2f, yPos, zPos);
 85                 DrawCube(ref gl, 0, 0, 0, true);
 86             }
 87             gl.PopMatrix();
 88         }
 89 
 90 
 91         public void DrawRobot(ref OpenGL Gl, float xPos, float yPos, float zPos)
 92         {
 93             Gl.PushMatrix();
 94             {
 95                 Gl.Translate(xPos, yPos, zPos);
 96 
 97                 ///繪制各個部分
 98                 //Gl.LoadIdentity();
 99                 DrawHead(ref Gl, 1f, 2f, 0f);     // 繪制頭部  2*2*2
100                 DrawTorso(ref Gl, 1.5f, 0.0f, 0.0f); //軀干,  3*5*2
101 
102                 Gl.PushMatrix();
103                 {
104                     //如果胳膊正在向前運動,則遞增角度,否則遞減角度 
105                     if (arm1)
106                         armAngle[0] = armAngle[0] + 1f;
107                     else
108                         armAngle[0] = armAngle[0] - 1f;
109 
110                     ///如果胳膊達到其最大角度則改變其狀態
111                     if (armAngle[0] >= 15.0f)
112                         arm1 = false;
113                     if (armAngle[0] <= -15.0f)
114                         arm1 = true;
115 
116                     //平移並旋轉後繪制胳膊
117                     Gl.Translate(0.0f, -0.5f, 0.0f);
118                     Gl.Rotate(armAngle[0], 1.0f, 0.0f, 0.0f);
119                     DrawArm(ref Gl, 2.5f, 0.0f, -0.5f);  //胳膊1, 1*4*1  
120                 }
121                 Gl.PopMatrix();
122 
123                 Gl.PushMatrix();
124                 {
125                     if (arm2)
126                         armAngle[1] = armAngle[1] + 1f;
127                     else
128                         armAngle[1] = armAngle[1] - 1f;
129 
130 
131                     if (armAngle[1] >= 15.0f)
132                         arm2 = false;
133                     if (armAngle[1] <= -15.0f)
134                         arm2 = true;
135 
136 
137                     Gl.Translate(0.0f, -0.5f, 0.0f);
138                     Gl.Rotate(armAngle[1], 1.0f, 0.0f, 0.0f);
139                     DrawArm(ref Gl, -1.5f, 0.0f, -0.5f); //胳膊2, 1*4*1
140                 }
141                 Gl.PopMatrix();
142 
143                 Gl.PushMatrix();
144                 {
145                     ///如果腿正在向前運動,則遞增角度,否則遞減角度 
146                     if (leg1)
147                         legAngle[0] = legAngle[0] + 1f;
148                     else
149                         legAngle[0] = legAngle[0] - 1f;
150 
151                     ///如果腿達到其最大角度則改變其狀態
152                     if (legAngle[0] >= 15.0f)
153                         leg1 = false;
154                     if (legAngle[0] <= -15.0f)
155                         leg1 = true;
156 
157                     ///平移並旋轉後繪制胳膊
158                     Gl.Translate(0.0f, -0.5f, 0.0f);
159                     Gl.Rotate(legAngle[0], 1.0f, 0.0f, 0.0f);
160                     DrawLeg(ref Gl, -0.5f, -5.0f, -0.5f); //腿部1,1*5*1 
161                 }
162                 Gl.PopMatrix();
163 
164                 Gl.PushMatrix();
165                 {
166                     if (leg2)
167                         legAngle[1] = legAngle[1] + 1f;
168                     else
169                         legAngle[1] = legAngle[1] - 1f;
170 
171                     if (legAngle[1] >= 15.0f)
172                         leg2 = false;
173                     if (legAngle[1] <= -15.0f)
174                         leg2 = true;
175 
176                     Gl.Translate(0.0f, -0.5f, 0.0f);
177                     Gl.Rotate(legAngle[1], 1.0f, 0.0f, 0.0f);
178                     DrawLeg(ref Gl, 1.5f, -5.0f, -0.5f); //腿部2, 1*5*1
179                 }
180                 Gl.PopMatrix();
181             }
182             Gl.PopMatrix();
183         }
184 
185         // 繪制一個手臂 
186         void DrawArm(ref OpenGL Gl, float xPos, float yPos, float zPos)
187         {
188             Gl.PushMatrix();
189             Gl.Color(1.0f, 0.0f, 0.0f);    // 紅色 
190             Gl.Translate(xPos, yPos, zPos);
191             Gl.Scale(1.0f, 4.0f, 1.0f);        // 手臂是1x4x1的立方體 
192             DrawCube(ref Gl, 0.0f, 0.0f, 0.0f,false);
193             Gl.PopMatrix();
194         }
195 
196         // 繪制一條腿 
197         void DrawLeg(ref OpenGL Gl, float xPos, float yPos, float zPos)
198         {
199             Gl.PushMatrix();
200             Gl.Color(1.0f, 1.0f, 0.0f);    // 黃色 
201             Gl.Translate(xPos, yPos, zPos);
202             Gl.Scale(1.0f, 5.0f, 1.0f);        // 腿是1x5x1長方體 
203             DrawCube(ref Gl, 0.0f, 0.0f, 0.0f,false);
204             Gl.PopMatrix();
205         }
206 
207         // 繪制頭部
208         void DrawHead(ref OpenGL Gl, float xPos, float yPos, float zPos)
209         {
210             Gl.PushMatrix();
211             Gl.Color(1.0f, 1.0f, 1.0f);    // 白色 
212             Gl.Translate(xPos, yPos, zPos);
213             Gl.Scale(2.0f, 2.0f, 2.0f);        //頭部是 2x2x2長方體
214             DrawCube(ref Gl, 0.0f, 0.0f, 0.0f,false);
215             Gl.PopMatrix();
216         }
217 
218         // 繪制機器人的軀干 
219         void DrawTorso(ref OpenGL Gl, float xPos, float yPos, float zPos)
220         {
221             Gl.PushMatrix();
222             Gl.Color(0.0f, 0.0f, 1.0f);     // 藍色
223             Gl.Translate(xPos, yPos, zPos);
224             Gl.Scale(3.0f, 5.0f, 2.0f);         // 軀干是3x5x2的長方體 
225             DrawCube(ref Gl, 0.0f, 0.0f, 0.0f,false);
226             Gl.PopMatrix();
227         }
228 
229         void drawGrid(OpenGL gl)
230         {
231             //繪制過程
232             gl.PushAttrib(OpenGL.GL_CURRENT_BIT);  //保存當前屬性
233             gl.PushMatrix();                        //壓入堆棧
234             gl.Translate(0f, -20f, 0f);
235             gl.Color(0f, 0f, 1f);
236 
237             //在X,Z平面上繪制網格
238             for (float i = -50; i <= 50; i += 1)
239             {
240                 //繪制線
241                 gl.Begin(OpenGL.GL_LINES);
242                 {
243                     if (i == 0)
244                         gl.Color(0f, 1f, 0f);
245                     else
246                         gl.Color(0f, 0f, 1f);
247 
248                     //X軸方向
249                     gl.Vertex(-50f, 0f, i);
250                     gl.Vertex(50f, 0f, i);
251                     //Z軸方向 
252                     gl.Vertex(i, 0f, -50f);
253                     gl.Vertex(i, 0f, 50f);
254       
255                 }
256                 gl.End();
257             }
258             gl.PopMatrix();
259             gl.PopAttrib();
260         }
261 
262         internal void DrawCube(ref OpenGL Gl, float xPos, float yPos, float zPos,bool isLine)
263         {
264             Gl.PushMatrix();
265             Gl.Translate(xPos, yPos, zPos);
266             if (isLine)
267                 Gl.Begin(OpenGL.GL_LINE_STRIP);
268             else
269                 Gl.Begin(OpenGL.GL_POLYGON);
270 
271             // 頂面
272             Gl.Vertex(0.0f, 0.0f, 0.0f);
273             Gl.Vertex(0.0f, 0.0f, -1.0f);
274             Gl.Vertex(-1.0f, 0.0f, -1.0f);
275             Gl.Vertex(-1.0f, 0.0f, 0.0f);
276 
277             // 前面
278             Gl.Vertex(0.0f, 0.0f, 0.0f);
279             Gl.Vertex(-1.0f, 0.0f, 0.0f);
280             Gl.Vertex(-1.0f, -1.0f, 0.0f);
281             Gl.Vertex(0.0f, -1.0f, 0.0f);
282 
283             // 右面
284             Gl.Vertex(0.0f, 0.0f, 0.0f);
285             Gl.Vertex(0.0f, -1.0f, 0.0f);
286             Gl.Vertex(0.0f, -1.0f, -1.0f);
287             Gl.Vertex(0.0f, 0.0f, -1.0f);
288 
289             // 左面
290             Gl.Vertex(-1.0f, 0.0f, 0.0f);
291             Gl.Vertex(-1.0f, 0.0f, -1.0f);
292             Gl.Vertex(-1.0f, -1.0f, -1.0f);
293             Gl.Vertex(-1.0f, -1.0f, 0.0f);
294 
295             // 底面 
296             Gl.Vertex(0.0f, 0.0f, 0.0f);
297             Gl.Vertex(0.0f, -1.0f, -1.0f);
298             Gl.Vertex(-1.0f, -1.0f, -1.0f);
299             Gl.Vertex(-1.0f, -1.0f, 0.0f);
300 
301 
302             // 後面
303             Gl.Vertex(0.0f, 0.0f, 0.0f);
304             Gl.Vertex(-1.0f, 0.0f, -1.0f);
305             Gl.Vertex(-1.0f, -1.0f, -1.0f);
306             Gl.Vertex(0.0f, -1.0f, -1.0f);
307             Gl.End();
308             Gl.PopMatrix();
309         }
310 
311 
312 
313     }
314 }

這個代碼朋友們主要需注意下面兩個重點:

(1) 機器人的6個部分的坐標為什麼要取下面這樣的值?

因為畫每個部分都用了入棧出棧, 因此每個部分都是獨立相對於原點來計算的, 計算的時候你還得參考  長*高*寬 的信息, 我已經把它標注到注釋裡了.

DrawHead(ref Gl, 1f, 2f, 0f);     // 繪制頭部  2*2*2
DrawTorso(ref Gl, 1.5f, 0.0f, 0.0f); //軀干,  3*5*2
DrawArm(ref Gl, 2.5f, 0.0f, -0.5f);  //胳膊1, 1*4*1  
DrawArm(ref Gl, -1.5f, 0.0f, -0.5f); //胳膊2, 1*4*1
DrawLeg(ref Gl, -0.5f, -5.0f, -0.5f); //腿部1,1*5*1 
DrawLeg(ref Gl, 1.5f, -5.0f, -0.5f); //腿部2, 1*5*1

(2) 好好體會堆棧的操作, 主要在畫機器人的函數DrawRobot()裡.

 

程序運行時截取了一幀,效果如下:

 

 

OpenGL的"變換" 主題終於徹底講完了! 最初筆者接觸這些內容時, 感覺術語太多, 枯燥無趣. 但是它是OpenGL真正最基本的入門功夫. 就像 徐明亮在《OpenGL游戲編程》這本書裡說的: 說完OpenGL變換的知識後, 讀者已經有足夠的基礎可以開始編寫游戲了! 

我當時還郁悶,  就學這點知識就可以開始寫游戲? 那材質燈光呢? 開什麼玩笑?

現在我就同意這一說法了, 因為"變換"的知識是重中之重, 請引起朋友們足夠的重視, 踏實把它學好! 不要像筆者一樣浮澡走彎路!

 

本節源代碼下載

 

 原創文章 : http://www.cnblogs.com/hackpig/

 

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved