opengl绘制长方体线框_OpenGL绘图实例十之绘制3D机器人
綜述
通過上一節(jié)說的繪制3D圖形基礎(chǔ),我們應(yīng)該對繪制3D圖形有了基本的認識,接下來我們就進行一個實例,繪制一個3D機器人。 本節(jié)我們要完成的任務(wù)有:
1.繪制一個仿真3D機器人(樣式自選,參考例圖),至少包含頭、軀干、四肢三個部分. 2.對機器人填充顏色。 3.增加點光源,使得機器人更加真實。 4.實現(xiàn)交互,使得能夠控制機器人進行旋轉(zhuǎn)、前進、后退等動作。 鍵盤”w”:前進 鍵盤”s”:后退 鍵盤”a”:順時針旋轉(zhuǎn) 鍵盤”d”:逆時針旋轉(zhuǎn)
接下來我們就一步步實現(xiàn)機器人的繪制吧。
繪制球體
繪制球體我們有兩種方法,一個叫g(shù)lutSolidSphere,另一個叫g(shù)lutWireSphere,這兩個的區(qū)別在于,一個繪制的是實心的球體,另一個繪制的是現(xiàn)狀描繪而成的球體,在繪制過程中,我們可以通過一個參數(shù)來對其進行控制。 glutSolidSphere是GLUT工具包中的一個函數(shù),該函數(shù)用于渲染一個球體。球體球心位于原點。在OpenGL中默認的原點就是窗口客戶區(qū)的中心。 函數(shù)原型
1
2
void glutSolidSphere(GLdoubleradius, GLint slices , GLint stacks);
void glutWireSphere(GLdouble radius, GLint slices, GLint stacks );
radius,球體的半徑 slices,以Z軸上線段為直徑分布的圓周線的條數(shù)(將Z軸看成地球的地軸,類似于經(jīng)線) stacks,圍繞在Z軸周圍的線的條數(shù)(類似于地球上緯線) 一般而言, 后兩個參數(shù)賦予較大的值, 渲染花費的時間要長, 效果更逼真。 然而我們可以發(fā)現(xiàn),這里并沒有定義球中心的參數(shù),所以,我們可以利用平移函數(shù)組合實現(xiàn)。即利用glTranslated函數(shù)來實現(xiàn)。最后,我們定義的畫球體的方法如下
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
在這里我們還用到了上一節(jié)所說的PushMatrix和PopMatrix方法。如果不熟悉,請查看上一節(jié)的內(nèi)容。
繪制長方體
同樣地,利用變換平移放縮的方法,再加上類庫的繪制正方體的方法,我們也可以輕松地實現(xiàn)繪制長方體的方法。 正方體怎樣變成長方體,很簡單,拉伸一下就好了。所以,我們用到了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光照 那么在這里我就直接貼上光照設(shè)置的實現(xiàn)
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);
//設(shè)置環(huán)境光
glLightfv(GL_LIGHT0, GL_AMBIENT, ambient);
//設(shè)置漫射光
glLightfv(GL_LIGHT0, GL_DIFFUSE, diffuse);
//設(shè)置光源位置
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);
}
其中設(shè)置了光源顏色,位置等參數(shù)。
圖形的移動
在這里我們實現(xiàn)了鼠標(biāo)的監(jiān)聽旋轉(zhuǎn)和鍵盤的監(jiān)聽旋轉(zhuǎn),在這里分別描述如下
1.鼠標(biāo)監(jiān)聽
對于鼠標(biāo)監(jiān)聽事件,在前面的文章中已經(jīng)做了說明,如果大家不熟悉可以看下面這篇文章 鼠標(biāo)監(jiān)聽 我們要實現(xiàn)的就是在拖動鼠標(biāo)的時候?qū)崿F(xiàn)圖形的旋轉(zhuǎn)功能。 在點擊鼠標(biāo)時,我們記錄下來點擊的位置,然后在鼠標(biāo)移動的時候記錄下當(dāng)前坐標(biāo)與上一個位置的坐標(biāo)之差。通過定義一個角度的變量,每次鼠標(biāo)移動的時候讓整個圖形旋轉(zhuǎn)角度加上這個差值,然后重新繪制圖形,就可以實現(xiàn)整個圖形的 旋轉(zhuǎn)了。 鼠標(biāo)監(jiān)聽的兩個方法如下,分別是鼠標(biāo)點擊和鼠標(biāo)移動。 定義兩個變量,旋轉(zhuǎn)角度
1
2
int spinX = 0;
int spinY = 0;
然后定義兩個鼠標(biāo)事件
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// 鼠標(biāo)移動事件
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;
}
//鼠標(biāo)點擊事件
void mouseClick(int btn, int state, int x, int y){
moveX = x;
moveY = y;
}
恩,通過定義上面的方法,然后在main中加入監(jiān)聽。
1
2
3
4
//鼠標(biāo)點擊事件,鼠標(biāo)點擊或者松開時調(diào)用
glutMouseFunc(mouseClick);
//鼠標(biāo)移動事件,鼠標(biāo)按下并移動時調(diào)用
glutMotionFunc(mouseMove);
display函數(shù)中,在繪圖前調(diào)用該方法即可
1
2
glRotated(spinX, 0, 1, 0);
glRotated(spinY, 1, 0, 0);
分別是繞y軸和x軸旋轉(zhuǎn)一定的角度。這樣就可以實現(xiàn)鼠標(biāo)的監(jiān)聽了。
2.鍵盤監(jiān)聽
鍵盤事件也很簡單,同樣是監(jiān)聽按鍵的按下。其中w、s鍵是用來控制機器人的遠近的。 定義一個變量叫 dis,代表遠近 鍵盤事件函數(shù)如下:
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函數(shù)中加入監(jiān)聽
1
2
//鍵盤事件
glutKeyboardFunc(keyPressed);
display函數(shù)中加入如下的變換
1
2
3
glRotated(spinX, 0, 1, 0);
glRotated(spinY, 1, 0, 0);
glTranslated(0, 0, des);
這樣通過上述方法,我們便可以實現(xiàn)鼠標(biāo)和鍵盤的監(jiān)聽了。
裁切物體
在這里我們可能要畫一個半圓,那么最方便的方法就是裁切了,利用下面的函數(shù),我們可以方便地實現(xiàn)。
void glClipPlane(GLenum plane, const GLdouble *equation);
定義一個裁剪平面。equation參數(shù)指向平面方程Ax + By + Cz + D = 0的4個系數(shù)。 equation=(0,-1,0,0),前三個參數(shù)(0,-1,0)可以理解為法線向下,只有向下的,即Y<0的才能顯示,最后一個參數(shù)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數(shù)組,然后利用glClipPlane方法來設(shè)置,然后開啟裁切,最后關(guān)閉裁切。 利用類似的方法我們可以寫出繪制半球的方法如下
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();
}
恩,通過上述方法,我們可以方便地繪制出一個半球體。
繪制機器人
有了上述的鋪墊,我們繪制機器人簡直易如反掌,同樣還可以實現(xiàn)各式各樣的監(jiān)聽。 主要就是位置的確定了。 display函數(shù)如下
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) {
//清除緩沖區(qū)顏色
glClear(GL_COLOR_BUFFER_BIT);
//定義白色
glColor3f(1.0, 1.0, 1.0);
//圓點放坐標(biāo)中心
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();
}
恩,通過調(diào)用這個函數(shù)我們便可以完成機器人的繪制了。
完整代碼
在這里提供完整代碼示例,僅供參考
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);
//設(shè)置環(huán)境光
glLightfv(GL_LIGHT0, GL_AMBIENT, ambient);
//設(shè)置漫射光
glLightfv(GL_LIGHT0, GL_DIFFUSE, diffuse);
//設(shè)置光源位置
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) {
//清除緩沖區(qū)顏色
glClear(GL_COLOR_BUFFER_BIT);
//定義白色
glColor3f(1.0, 1.0, 1.0);
//圓點放坐標(biāo)中心
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();
}
//鼠標(biāo)點擊事件
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 };
//設(shè)置環(huán)境光
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();
}
// 鼠標(biāo)移動事件
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);
//坐標(biāo)原點在屏幕中心
glLoadIdentity();
//操作模型視景
gluPerspective(60.0, (GLfloat) w/(GLfloat) h, 1.0, 20.0);
glMatrixMode(GL_MODELVIEW);
}
int main(int argc, char** argv) {
//初始化
glutInit(&argc, argv);
//設(shè)置顯示模式
glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB);
//初始化窗口大小
glutInitWindowSize(500, 500);
//定義左上角窗口位置
glutInitWindowPosition(100, 100);
//創(chuàng)建窗口
glutCreateWindow(argv[0]);
//初始化
init();
//顯示函數(shù)
glutDisplayFunc(display);
//窗口大小改變時的響應(yīng)
glutReshapeFunc(reshape);
//鼠標(biāo)點擊事件,鼠標(biāo)點擊或者松開時調(diào)用
glutMouseFunc(mouseClick);
//鼠標(biāo)移動事件,鼠標(biāo)按下并移動時調(diào)用
glutMotionFunc(mouseMove);
//鍵盤事件
glutKeyboardFunc(keyPressed);
//循環(huán)
glutMainLoop();
return 0;
}
僅供參考,如有問題,敬請指正。 運行結(jié)果如下 恩,大體就是這樣。
總結(jié)
本次實驗做的比較匆忙,只研究了一個晚上的時間,所以有些地方還是不太完善,希望發(fā)出來對小伙伴們有所啟發(fā),有所幫助。如果有問題,歡迎同我交流。謝大家!
總結(jié)
以上是生活随笔為你收集整理的opengl绘制长方体线框_OpenGL绘图实例十之绘制3D机器人的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: electron 屏幕标注_屏幕 |
- 下一篇: 大裁员和ChatGPT来袭,科技行业员工