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变换的知识后, 读者已经有足够的基础可以开始编写游戏了!

我当时还郁闷,  就学这点知识就可以开始写游戏? 那材质灯光呢? 开什么玩笑?

现在我就同意这一说法了, 因为"变换"的知识是重中之重, 请引起朋友们足够的重视, 踏实把它学好! 不要像笔者一样浮澡走弯路!

本节源代码下载

时间: 2024-10-29 04:44:00

SharpGL学习笔记(八) 矩阵堆栈和变换的综合例子: 机器人的相关文章

SharpGL学习笔记(三) 投影变换和视点变换

从本节开始,我们使用SharpGL带的VS2010扩展,来直接生成SharpGL工程. 如果你新建项目时,没有看到下面的SharpGL项目,那么请事先在SharpGL源代码中找到一个叫 ”SharpGL 2.0 Visual Studio Extension“目录 ,安装名为 SharpGL.vsix的vs2010扩展, 然后重启你的vs2010. 利用上面的SharpGL项目,直接生成一个完整的SharpGL工程,它带有Opengl窗体控件,基本代码已经都完成了, 你只需要修改3D绘图部分的代

SharpGL学习笔记(七) OpenGL的变换总结

笔者接触OpenGL最大的困难是: 经常调试一份代码时, 屏幕漆黑一片, 也不知道结果对不对,不知道如何是好! 这其实就是关于OpenGL"变换"的基础概念没有掌握好, 以至于对"将三维体正确的显示在屏幕上指定位置"这样的操作都无法完成. OpenGL变换包括计算机图形学中最基本的三维变换,即几何变换.投影变换.裁剪变换.视口变换,以及针对OpenGL的特殊变换概念理解和用法,如相机模拟.矩阵堆栈等,这些基础是开始真正走进三维世界无法绕过的基础. 所以笔者在前面花了

Lua学习笔记(八):数据结构

table是Lua中唯一的数据结构,其他语言所提供的数据结构,如:arrays.records.lists.queues.sets等,Lua都是通过table来实现,并且在Lua中table很好的实现了这些数据结构. 1.数组 在Lua中通过整数下标访问table中元素,既是数组,并且数组大小不固定,可动态增长.通常我们初始化数组时,就间接地定义了数组的大小,例如: 1 a = {} -- new array 2 for i=1, 1000 do 3 a[i] = 0 4 end 5 6 --数

angular学习笔记(八)

本篇介绍angular控制视图的显示和隐藏: 通过给元素添加ng-show属性或者ng-hide属性来控制视图的显示或隐藏: ng-show: 绑定的数据值为true时,显示元素,值为false时,隐藏元素 ng-hide: 绑定的数据值为true时,隐藏元素,值为false时,显示元素 (其实只要用到其中一个就可以了) 下面来看个简单的例子,点击按钮可以显示/隐藏元素: <!DOCTYPE html> <html ng-app> <head> <title>

Linux System Programming 学习笔记(八) 文件和目录管理

1. 文件和元数据 每个文件都是通过inode引用,每个inode索引节点都具有文件系统中唯一的inode number 一个inode索引节点是存储在Linux文件系统的磁盘介质上的物理对象,也是LInux内核通过数据结构表示的实体 inode存储相关联文件的元数据 ls -i 命令获取文件的inode number /* obtaining the metadata of a file */ #include <sys/types.h> #include <sys/stat.h>

马哥学习笔记八——LAMP编译安装之PHP及xcache

1.解决依赖关系: 请配置好yum源(可以是本地系统光盘)后执行如下命令: # yum -y groupinstall "X Software Development" 如果想让编译的php支持mcrypt扩展,此处还需要下载如下两个rpm包并安装之: libmcrypt-2.5.7-5.el5.i386.rpm libmcrypt-devel-2.5.7-5.el5.i386.rpm 2.编译安装php-5.4.13 首先下载源码包至本地目录. # tar xf php-5.4.13

初探swift语言的学习笔记八(保留了许多OC的实现)

尽管swift作为一门新语言,但还保留了许多OC的机制,使得swift和OC更好的融合在一起.如果没有OC基础的先GOOGLE一下. 如:KVO,DELEGATE,NOTIFICATION. 详见DEMO. import Foundation @objc // 需要打开objc标识,否则@optional编译出错 protocol kvoDemoDelegate { func willDoSomething() @optional func didDoSomething() //可选实现, }

《Hibernate学习笔记八》:组件映射

<Hibernate学习笔记八>:组件映射 前面介绍了一对一的单向.双向外键关联,例如,学生证和学生是一个一对一的关系.这篇博文主要是介绍下组件映射,即一个是另一个的一部分,例如,学生证的信息也可以作为学生信息的一部分,即在数据库中只存在学生一个表,而不是有学生和学生证两个表,并且这两个表中有一个一对一的关联关系. 如下: 有人或许会说,那我们就将学生和学生证的信息写在一个类中,则就不需要组件映射了,确实可以这样,但是,根据类的设计原则,我们一般都会将其设计为两个类,然后将学生证的信息作为一个

iOS学习笔记(八)——iOS网络通信http之NSURLConnection

转自:http://blog.csdn.net/xyz_lmn/article/details/8968182 移动互联网时代,网络通信已是手机终端必不可少的功能.我们的应用中也必不可少的使用了网络通信,增强客户端与服务器交互.这一篇提供了使用NSURLConnection实现http通信的方式. NSURLConnection提供了异步请求.同步请求两种通信方式. 1.异步请求 iOS5.0 SDK NSURLConnection类新增的sendAsynchronousRequest:queu