综述

通过上一节说的绘制3D图形基础,我们应该对绘制3D图形有了基本的认识,接下来我们就进行一个实例,绘制一个3D机器人。 本节我们要完成的任务有:

1.绘制一个仿真3D机器人(样式自选,参考例图),至少包含头、躯干、四肢三个部分. 2.对机器人填充颜色。 3.增加点光源,使得机器人更加真实。 4.实现交互,使得能够控制机器人进行旋转、前进、后退等动作。 键盘”w”:前进 键盘”s”:后退 键盘”a”:顺时针旋转 键盘”d”:逆时针旋转

接下来我们就一步步实现机器人的绘制吧。

绘制球体

绘制球体我们有两种方法,一个叫glutSolidSphere,另一个叫glutWireSphere,这两个的区别在于,一个绘制的是实心的球体,另一个绘制的是现状描绘而成的球体,在绘制过程中,我们可以通过一个参数来对其进行控制。 glutSolidSphere是GLUT工具包中的一个函数,该函数用于渲染一个球体。球体球心位于原点。在OpenGL中默认的原点就是窗口客户区的中心。 函数原型

1

2

void glutSolidSphere(GLdoubleradius, GLint slices , GLint stacks);

void glutWireSphere(GLdouble radius, GLint slices, GLint stacks );

radius,球体的半径 slices,以Z轴上线段为直径分布的圆周线的条数(将Z轴看成地球的地轴,类似于经线) stacks,围绕在Z轴周围的线的条数(类似于地球上纬线) 一般而言, 后两个参数赋予较大的值, 渲染花费的时间要长, 效果更逼真。 然而我们可以发现,这里并没有定义球中心的参数,所以,我们可以利用平移函数组合实现。即利用glTranslated函数来实现。最后,我们定义的画球体的方法如下

1

2

3

4

5

6

7

8

9

10

11

//画球

void drawBall(double R, double x, double y,double z, int MODE) {

glPushMatrix();

glTranslated(x,y,z);

if (MODE==SOLID) {

glutSolidSphere(R,20,20);

} else if (MODE ==WIRE) {

glutWireSphere(R,20,20);

}

glPopMatrix();

}

其中两个常量定义如下

1

2

#define SOLID 1

#define WIRE 2

在这里我们还用到了上一节所说的PushMatrix和PopMatrix方法。如果不熟悉,请查看上一节的内容。

绘制长方体

同样地,利用变换平移放缩的方法,再加上类库的绘制正方体的方法,我们也可以轻松地实现绘制长方体的方法。 正方体怎样变成长方体,很简单,拉伸一下就好了。所以,我们用到了glScaled方法。 绘制长方体的方法如下

1

2

3

4

5

6

7

8

9

10

11

12

//画长方体

void drawSkewed(double l, double w, double h, double x, double y, double z, int MODE) {

glPushMatrix();

glScaled(l, w, h);

glTranslated(x, y, z);

if (MODE==SOLID) {

glutSolidCube(1);

} else if (MODE==WIRE) {

glutWireCube(1);

}

glPopMatrix();

}

这里仍然还是定义了绘图模式,是线条还是实体。

定义光照

在这里提供一篇博文,讲光照讲得比较细致 OPENGL光照 那么在这里我就直接贴上光照设置的实现

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

void init() {

//定义光源的颜色和位置

GLfloat ambient[] = { 0.5, 0.8, 0.1, 0.1 };

GLfloat diffuse[] = { 1.0, 1.0, 1.0, 1.0 };

GLfloat position[] = { -80.0, 50.0, 25.0, 1.0 };

//选择光照模型

GLfloat lmodel_ambient[] = { 0.4, 0.4, 0.4, 1.0 };

GLfloat local_view[] = { 0.0 };

glClearColor(0.0, 0.0, 0.0, 0.0);

glShadeModel(GL_SMOOTH);

//设置环境光

glLightfv(GL_LIGHT0, GL_AMBIENT, ambient);

//设置漫射光

glLightfv(GL_LIGHT0, GL_DIFFUSE, diffuse);

//设置光源位置

glLightfv(GL_LIGHT0, GL_POSITION, position);

glLightModelfv(GL_LIGHT_MODEL_AMBIENT, lmodel_ambient);

glLightModelfv(GL_LIGHT_MODEL_LOCAL_VIEWER, local_view);

//启动光照

glEnable(GL_LIGHTING);

//启用光源

glEnable(GL_LIGHT0);

}

其中设置了光源颜色,位置等参数。

图形的移动

在这里我们实现了鼠标的监听旋转和键盘的监听旋转,在这里分别描述如下

1.鼠标监听

对于鼠标监听事件,在前面的文章中已经做了说明,如果大家不熟悉可以看下面这篇文章 鼠标监听 我们要实现的就是在拖动鼠标的时候实现图形的旋转功能。 在点击鼠标时,我们记录下来点击的位置,然后在鼠标移动的时候记录下当前坐标与上一个位置的坐标之差。通过定义一个角度的变量,每次鼠标移动的时候让整个图形旋转角度加上这个差值,然后重新绘制图形,就可以实现整个图形的 旋转了。 鼠标监听的两个方法如下,分别是鼠标点击和鼠标移动。 定义两个变量,旋转角度

1

2

int spinX = 0;

int spinY = 0;

然后定义两个鼠标事件

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

// 鼠标移动事件

void mouseMove(int x, int y){

int dx = x - moveX;

int dy = y - moveY;

printf("dx;%dx,dy:%dy\n",dx,dy);

spinX += dx;

spinY += dy;

glutPostRedisplay();

moveX = x;

moveY = y;

}

//鼠标点击事件

void mouseClick(int btn, int state, int x, int y){

moveX = x;

moveY = y;

}

恩,通过定义上面的方法,然后在main中加入监听。

1

2

3

4

//鼠标点击事件,鼠标点击或者松开时调用

glutMouseFunc(mouseClick);

//鼠标移动事件,鼠标按下并移动时调用

glutMotionFunc(mouseMove);

display函数中,在绘图前调用该方法即可

1

2

glRotated(spinX, 0, 1, 0);

glRotated(spinY, 1, 0, 0);

分别是绕y轴和x轴旋转一定的角度。这样就可以实现鼠标的监听了。

2.键盘监听

键盘事件也很简单,同样是监听按键的按下。其中w、s键是用来控制机器人的远近的。 定义一个变量叫 dis,代表远近 键盘事件函数如下:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

//键盘事件

void keyPressed(unsigned char key, int x, int y) {

switch (key) {

case 'a':

spinX -= 2;

break;

case 'd':

spinX += 2;

break;

case 'w':

des += 2;

break;

case 's':

des -= 2;

break;

}

glutPostRedisplay();

}

在main函数中加入监听

1

2

//键盘事件

glutKeyboardFunc(keyPressed);

display函数中加入如下的变换

1

2

3

glRotated(spinX, 0, 1, 0);

glRotated(spinY, 1, 0, 0);

glTranslated(0, 0, des);

这样通过上述方法,我们便可以实现鼠标和键盘的监听了。

裁切物体

在这里我们可能要画一个半圆,那么最方便的方法就是裁切了,利用下面的函数,我们可以方便地实现。

void glClipPlane(GLenum plane, const GLdouble *equation);

定义一个裁剪平面。equation参数指向平面方程Ax + By + Cz + D = 0的4个系数。 equation=(0,-1,0,0),前三个参数(0,-1,0)可以理解为法线向下,只有向下的,即Y<0的才能显示,最后一个参数0表示从z=0平面开始。这样就是裁剪掉上半平面。 equation=(0,1,0,0)表示裁剪掉下半平面, equation=(1,0,0,0)表示裁剪掉左半平面, equation=(-1,0,0,0)表示裁剪掉右半平面, equation=(0,0,-1,0)表示裁剪掉前半平面, equation=(0,0,1,0)表示裁剪掉后半平面 代码示例如下

1

2

3

4

5

GLdouble eqn[4]={0.0,0.0,-1.0,0.0};

glClipPlane(GL_CLIP_PLANE0,eqn);

glEnable(GL_CLIP_PLANE0);

glutSolidSphere(headR,slices,slices);

glDisable(GL_CLIP_PLANE0);

首先我们必须要定义一个GLdouble数组,然后利用glClipPlane方法来设置,然后开启裁切,最后关闭裁切。 利用类似的方法我们可以写出绘制半球的方法如下

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

//画半球

void drawHalfBall(double R, double x, double y,double z, int MODE) {

glPushMatrix();

glTranslated(x,y,z);

GLdouble eqn[4]={0.0, 1.0, 0.0, 0.0};

glClipPlane(GL_CLIP_PLANE0,eqn);

glEnable(GL_CLIP_PLANE0);

if (MODE==SOLID) {

glutSolidSphere(R,20,20);

} else if (MODE ==WIRE) {

glutWireSphere(R,20,20);

}

glDisable(GL_CLIP_PLANE0);

glPopMatrix();

}

恩,通过上述方法,我们可以方便地绘制出一个半球体。

绘制机器人

有了上述的铺垫,我们绘制机器人简直易如反掌,同样还可以实现各式各样的监听。 主要就是位置的确定了。 display函数如下

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

void display(void) {

//清除缓冲区颜色

glClear(GL_COLOR_BUFFER_BIT);

//定义白色

glColor3f(1.0, 1.0, 1.0);

//圆点放坐标中心

glLoadIdentity();

//从哪个地方看

gluLookAt(-2.0, -1.0, 20.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0);

glPushMatrix();

glRotated(spinX, 0, 1, 0);

glRotated(spinY, 1, 0, 0);

glTranslated(0, 0, des);

//头

drawBall(2, 0, 1, 0, SOLID);

//身体

drawSkewed(5, 4.4, 4, 0, -0.75, 0, SOLID);

//肩膀

drawHalfBall(1, 3.5, -2.1, 0, SOLID);

drawHalfBall(1, -3.5, -2.1, 0, SOLID);

//胳膊

drawSkewed(1, 3, 1, 3.5, -1.3, 0, SOLID);

drawSkewed(1, 3, 1, -3.5, -1.3, 0, SOLID);

//手

drawBall(1, 3.5, -6.4, 0, SOLID);

drawBall(1, -3.5, -6.4, 0, SOLID);

//腿

drawSkewed(1.2, 3, 2, 1, -2.4, 0, SOLID);

drawSkewed(1.2, 3, 2, -1, -2.4, 0, SOLID);

//脚

drawSkewed(1.5, 1, 3, 0.9, -9.2, 0, SOLID);

drawSkewed(1.5, 1, 3, -0.9, -9.2, 0, SOLID);

glPopMatrix();

glutSwapBuffers();

}

恩,通过调用这个函数我们便可以完成机器人的绘制了。

完整代码

在这里提供完整代码示例,仅供参考

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

79

80

81

82

83

84

85

86

87

88

89

90

91

92

93

94

95

96

97

98

99

100

101

102

103

104

105

106

107

108

109

110

111

112

113

114

115

116

117

118

119

120

121

122

123

124

125

126

127

128

129

130

131

132

133

134

135

136

137

138

139

140

141

142

143

144

145

146

147

148

149

150

151

152

153

154

155

156

157

158

159

160

161

162

163

164

165

166

167

168

169

170

171

172

173

174

175

176

177

178

179

180

181

182

183

184

185

186

187

188

189

190

191

192

193

#include

#include

#include

#define SOLID 1

#define WIRE 2

int moveX,moveY;

int spinX = 0;

int spinY = 0;

int des = 0;

void init() {

//定义光源的颜色和位置

GLfloat ambient[] = { 0.5, 0.8, 0.1, 0.1 };

GLfloat diffuse[] = { 1.0, 1.0, 1.0, 1.0 };

GLfloat position[] = { -80.0, 50.0, 25.0, 1.0 };

//选择光照模型

GLfloat lmodel_ambient[] = { 0.4, 0.4, 0.4, 1.0 };

GLfloat local_view[] = { 0.0 };

glClearColor(0.0, 0.0, 0.0, 0.0);

glShadeModel(GL_SMOOTH);

//设置环境光

glLightfv(GL_LIGHT0, GL_AMBIENT, ambient);

//设置漫射光

glLightfv(GL_LIGHT0, GL_DIFFUSE, diffuse);

//设置光源位置

glLightfv(GL_LIGHT0, GL_POSITION, position);

glLightModelfv(GL_LIGHT_MODEL_AMBIENT, lmodel_ambient);

glLightModelfv(GL_LIGHT_MODEL_LOCAL_VIEWER, local_view);

//启动光照

glEnable(GL_LIGHTING);

//启用光源

glEnable(GL_LIGHT0);

}

//画球

void drawBall(double R, double x, double y,double z, int MODE) {

glPushMatrix();

glTranslated(x,y,z);

if (MODE == SOLID) {

glutSolidSphere(R,20,20);

} else if (MODE ==WIRE) {

glutWireSphere(R,20,20);

}

glPopMatrix();

}

//画半球

void drawHalfBall(double R, double x, double y,double z, int MODE) {

glPushMatrix();

glTranslated(x,y,z);

GLdouble eqn[4]={0.0, 1.0, 0.0, 0.0};

glClipPlane(GL_CLIP_PLANE0,eqn);

glEnable(GL_CLIP_PLANE0);

if (MODE == SOLID) {

glutSolidSphere(R,20,20);

} else if (MODE ==WIRE) {

glutWireSphere(R,20,20);

}

glDisable(GL_CLIP_PLANE0);

glPopMatrix();

}

//画长方体

void drawSkewed(double l, double w, double h, double x, double y, double z, int MODE) {

glPushMatrix();

glScaled(l, w, h);

glTranslated(x, y, z);

if (MODE == SOLID) {

glutSolidCube(1);

} else if (MODE ==WIRE) {

glutWireCube(1);

}

glPopMatrix();

}

void display(void) {

//清除缓冲区颜色

glClear(GL_COLOR_BUFFER_BIT);

//定义白色

glColor3f(1.0, 1.0, 1.0);

//圆点放坐标中心

glLoadIdentity();

//从哪个地方看

gluLookAt(-2.0, -1.0, 20.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0);

glPushMatrix();

glRotated(spinX, 0, 1, 0);

glRotated(spinY, 1, 0, 0);

glTranslated(0, 0, des);

//头

drawBall(2, 0, 1, 0, SOLID);

//身体

drawSkewed(5, 4.4, 4, 0, -0.75, 0, SOLID);

//肩膀

drawHalfBall(1, 3.5, -2.1, 0, SOLID);

drawHalfBall(1, -3.5, -2.1, 0, SOLID);

//胳膊

drawSkewed(1, 3, 1, 3.5, -1.3, 0, SOLID);

drawSkewed(1, 3, 1, -3.5, -1.3, 0, SOLID);

//手

drawBall(1, 3.5, -6.4, 0, SOLID);

drawBall(1, -3.5, -6.4, 0, SOLID);

//腿

drawSkewed(1.2, 3, 2, 1, -2.4, 0, SOLID);

drawSkewed(1.2, 3, 2, -1, -2.4, 0, SOLID);

//脚

drawSkewed(1.5, 1, 3, 0.9, -9.2, 0, SOLID);

drawSkewed(1.5, 1, 3, -0.9, -9.2, 0, SOLID);

glPopMatrix();

glutSwapBuffers();

}

//鼠标点击事件

void mouseClick(int btn, int state, int x, int y) {

moveX = x;

moveY = y;

GLfloat ambient[] = { (float)rand() / RAND_MAX, (float)rand() / RAND_MAX, (float)rand() / RAND_MAX, 0.1 };

//设置环境光

glLightfv(GL_LIGHT0, GL_AMBIENT, ambient);

//启用光源

glEnable(GL_LIGHT0);

}

//键盘事件

void keyPressed(unsigned char key, int x, int y) {

switch (key) {

case 'a':

spinX -= 2;

break;

case 'd':

spinX += 2;

break;

case 'w':

des += 2;

break;

case 's':

des -= 2;

break;

}

glutPostRedisplay();

}

// 鼠标移动事件

void mouseMove(int x, int y) {

int dx = x - moveX;

int dy = y - moveY;

printf("dx;%dx,dy:%dy\n",dx,dy);

spinX += dx;

spinY += dy;

glutPostRedisplay();

moveX = x;

moveY = y;

}

void reshape(int w, int h) {

//定义视口大小

glViewport(0, 0, (GLsizei) w, (GLsizei) h);

//投影显示

glMatrixMode(GL_PROJECTION);

//坐标原点在屏幕中心

glLoadIdentity();

//操作模型视景

gluPerspective(60.0, (GLfloat) w/(GLfloat) h, 1.0, 20.0);

glMatrixMode(GL_MODELVIEW);

}

int main(int argc, char** argv) {

//初始化

glutInit(&argc, argv);

//设置显示模式

glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB);

//初始化窗口大小

glutInitWindowSize(500, 500);

//定义左上角窗口位置

glutInitWindowPosition(100, 100);

//创建窗口

glutCreateWindow(argv[0]);

//初始化

init();

//显示函数

glutDisplayFunc(display);

//窗口大小改变时的响应

glutReshapeFunc(reshape);

//鼠标点击事件,鼠标点击或者松开时调用

glutMouseFunc(mouseClick);

//鼠标移动事件,鼠标按下并移动时调用

glutMotionFunc(mouseMove);

//键盘事件

glutKeyboardFunc(keyPressed);

//循环

glutMainLoop();

return 0;

}

仅供参考,如有问题,敬请指正。 运行结果如下 恩,大体就是这样。

总结

本次实验做的比较匆忙,只研究了一个晚上的时间,所以有些地方还是不太完善,希望发出来对小伙伴们有所启发,有所帮助。如果有问题,欢迎同我交流。谢大家!

opengl绘制长方体线框_OpenGL绘图实例十之绘制3D机器人相关推荐

  1. opengl绘制长方体线框_OpenGL绘制长方体

    #include   //引用相关包 void display(void) { glClear(GL_COLOR_BUFFER_BIT);  //清空颜色缓冲区 glColor3f(0,1,1);   ...

  2. opengl绘制长方体线框_OpenGL 绘制长方体 计算机图形学

    不同面填充模式显示出不同的效果,如下面三幅截图: 上图通过glPolygonMode(GL_FRONT, GL_LINE);函数的调用,实现了显示前面面上的边线,而下面的面显示为默认的面填充,显示效果 ...

  3. MATLAB显函数作图 参数方程作图 极坐标方程作图绘图实例 用 Matlab 绘制高颜值函数图像 放大看告别浓浓锯齿风

    1.1  显函数作图 1.2  参数方程作图 1.3  极坐标方程作图 1.1  显函数作图 图1.   图2. % Eg001 % fplot 用法 clf x = linspace(-6,6,10 ...

  4. NDK OpenGL ES 3.0 开发(二十):3D 模型

    该原创文章首发于微信公众号:字节流动 OpenGLES 3D 模型 OpenGLES 3D 模型本质上是由一系列三角形在 3D 空间(OpenGL 坐标系)中构建而成,另外还包含了用于描述三角形表面的 ...

  5. 【OpenGL】十、OpenGL 绘制点 ( 初始化 OpenGL 矩阵 | 设置投影矩阵 | 设置模型视图矩阵 | 绘制点 | 清除缓冲区 | 设置当前颜色值 | 设置点大小 | 绘制点 )

    文章目录 一.初始化 OpenGL 矩阵 1.设置投影矩阵 2.设置模型视图矩阵 二.绘制点 1.清除缓冲区 2.设置当前颜色值 3.设置绘制点的大小 4.绘制点 5.将缓冲区绘制到前台 三.部分代码 ...

  6. [Qt教程] 第20篇 2D绘图(十)图形视图框架(下)

    [Qt教程] 第20篇 2D绘图(十)图形视图框架(下) 楼主  发表于 2013-5-4 15:43:02 | 查看: 861| 回复: 0 图形视图框架(下) 版权声明 该文章原创于Qter开源社 ...

  7. Python 数据分析三剑客之 Matplotlib(十):3D 图的绘制

    CSDN 课程推荐:<Python 数据分析与挖掘>,讲师刘顺祥,浙江工商大学统计学硕士,数据分析师,曾担任唯品会大数据部担任数据分析师一职,负责支付环节的数据分析业务.曾与联想.亨氏.网 ...

  8. 使用OpenGL在电脑屏幕上绘图

    学过OpenGL的人都知道,要想利用OpenGL函数进行绘图,就要创建一个显示窗口.每次绘图,第一件事恐怕就是创建窗口了.肯定也有人跟我一样想过:能不能不在那个黑乎乎的窗口上绘图,而是直接绘制在电脑屏 ...

  9. Android绘图Canvas十八般武器之Shader详解及实战篇(上)

    本篇文章已授权微信公众号 guolin_blog (郭霖)独家发布 前言 Android中绘图离不开的就是Canvas了,Canvas是一个庞大的知识体系,有java层的,也有jni层深入到Frame ...

最新文章

  1. jieba中文分词源码分析(四)
  2. linux rz 上传文件夹_第二章Linux服务器环境搭建之Tomcat安装
  3. python编写性别比例_Python分析微信好友性别比例和省份城市分布比例的方法示例【基于itchat模块】...
  4. Treap与fhq_Treap模板(支持内存回收)
  5. 每天都在支付,你真的了解信息流和资金流?
  6. ASP.NET部署与安装_MSI制作图文教程.
  7. Android Q 不叫 Q,正式命名为 Android 10
  8. python批量复制粘贴_用python批量复制特定图片
  9. 蒙特卡洛树搜索_蒙特卡洛树搜索与Model-free DRL
  10. 笔记本重置找不到恢复环境_Win10 自带的疑问解答、备份、恢复还原、重置系统怎么使用?...
  11. mysql删除日志文件_mysql删除日志文件,定时清理日志
  12. 以图搜图在线网站汇总(共7个)
  13. 如何制作六一儿童节答题测试H5页面?
  14. 下载各种离线地图(包括高德英文版地图)
  15. DOS时代后,金山系再出神级办公软件,会像WPS一样普及
  16. STKMATLAB connect(四)卫星
  17. JS如何在高德地图多边形覆盖物填充平行折线的算法
  18. Unirech:阿里云国际版账户无法登陆,为什么账户会被风控?
  19. 概率统计-方差与正态分布(高斯分布)
  20. root登录报错无权限,root权限突然没有了

热门文章

  1. 程序员自我修养的4个阶段
  2. 在Angular里使用rxjs的异步API - Observable
  3. ABAPGit的安装方式
  4. SAP S/4HANA Layer Repository(LREP)的读取逻辑调试
  5. 使用SAP云平台Mobile Service开发移动应用
  6. SAP CRM中间件下载出错的错误排查
  7. nodejs --inspect-brk结合Chrome开发者工具的调试
  8. ABAP Smart Help调试截图
  9. Sublime text无法自动通过package control安装插件的研究
  10. Object family 在Object search中的default逻辑