计算机图形学——OpenGl学习系列会动的机器人
实验题目:
使用OpenGl绘制可以行走的机器人
实验目的:
1、练习使用OpenGl的库函数;
2、学习OpenGl的动画制作;
实验环境:
VS2012+OpenGl
实验效果:
视频展示+后面有图+后面有源码
实验过程:
1、设计机器人形状:帽子+头部+躯体+手臂+大腿+小腿;其中,为了简单起见这些组成部分都是使用的立方体来表示。首先绘制一个单位立方体:
//绘制单位立方体
void DrawCube(float xPos,float yPos,float zPos)
{
glPushMatrix();
glTranslatef(xPos,yPos,zPos);
glBegin(GL_POLYGON);
glVertex3f(0.0f,0.0f,0.0f); //顶面
glVertex3f(0.0f,0.0f,-1.0f);
glVertex3f(-1.0f,0.0f,-1.0f);
glVertex3f(-1.0f,0.0f,0.0f);
glVertex3f(0.0f,0.0f,0.0f); //正面
glVertex3f(-1.0f,0.0f,0.0f);
glVertex3f(-1.0f,-1.0f,0.0f);
glVertex3f(0.0f,-1.0f,0.0f);
glVertex3f(0.0f,0.0f,0.0f); //右面
glVertex3f(0.0f,-1.0f,0.0f);
glVertex3f(0.0f,-1.0f,-1.0f);
glVertex3f(0.0f,0.0f,-1.0f);
glVertex3f(-1.0f,0.0f,0.0f); //左面
glVertex3f(-1.0f,0.0f,-1.0f);
glVertex3f(-1.0f,-1.0f,-1.0f);
glVertex3f(-1.0f,-1.0f,0.0f);
glVertex3f(0.0f,-1.0f,0.0f); //底面
glVertex3f(0.0f,-1.0f,-1.0f);
glVertex3f(-1.0f,-1.0f,-1.0f);
glVertex3f(-1.0f,-1.0f,0.0f);
glVertex3f(0.0f,0.0f,-1.0f); //背面
glVertex3f(-1.0f,0.0f,-1.0f);
glVertex3f(-1.0f,-1.0f,-1.0f);
glVertex3f(0.0f,-1.0f,-1.0f);
glEnd();
glPopMatrix();
}
然后通过OpenGl的glScalef(3.6f,0.8f,3.6f);函数来控制立方体的大小,从而使得机器人看上去比较协调。例如,绘制帽子:
//帽子
glPushMatrix();
glColor3f(0.6f,0.4f,0.12f); //褐色
glTranslatef(xPos+0.58,yPos+1.0,zPos+1.1);
glScalef(3.6f,0.8f,3.6f);
DrawCube(0.0f,0.0f,0.0f);
glPopMatrix();
2、将机器人的各个部分组装起来,主要是控制各个立方体的位置。这里只是简单的调试唯一参数,是机器人看起来比较正常。
3、让机器人手臂和腿部动起来,通过设置OpenGl的双缓冲区以及调用函数就可以重复绘制机器人同时改变机器人的位移,并且使用glRotatef函数控制机器人的手臂和腿部使其发生一定角度的旋转,从而达到摆臂行走的动作,这样就完成了运动。
//向前为true
bool right_hand = true;
bool right_leg = false;
int time = 0;
int max_time = 1000; //设定换臂时间
//画机器人
void DrawRobot(float xPos,float yPos,float zPos){
//绘制头
glPushMatrix();
//glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
DrawHead(-5.0f,2.5f,1.0f);
glPopMatrix();
glPushMatrix();
//右臂
if(right_hand){
glRotatef(-30.0f,-1.0,0.0,0.0);
}else{
glRotatef(30.0f,-1.0,0.0,0.0);
}
DrawUpArm(-3.2f,0.0f,0.25f);
glPopMatrix();
//躯体
DrawBody(-4.5f,0.0f,1.0f);
//左臂
glPushMatrix();
if(right_hand){
glRotatef(30.0f,-1.0,0.0,0.0);
if(time==max_time) right_hand = !right_hand;
}else{
glTranslatef(-0.5f,0.0f,-1.0f);
glRotatef(-30.0f,-1.0,0.0,0.0);
if(time==max_time) right_hand = !right_hand;
}
DrawUpArm(-8.0f,0.0f,0.25f);
glPopMatrix();
//右腿
glPushMatrix();
if(right_leg){
glRotatef(-15.0f,-1.0,0.0,0.0);
}else{
//glRotatef(15.0f,-1.0,0.0,0.0);
}
DrawUpLeg(-4.5f,-5.8f,0.25f);
glPopMatrix();
glPushMatrix();
if(right_leg){
glRotatef(-15.0f,-1.0,0.0,0.0);
}else{
//glRotatef(13.0f,-1.0,0.0,0.0);
}
DrawDownLeg(-4.5f,-10.2f,0.25f);
glPopMatrix();
//左腿
glPushMatrix();
if(right_leg){
//glRotatef(15.0f,-1.0,0.0,0.0);
}else{
glRotatef(-15.0f,-1.0,0.0,0.0);
}
DrawUpLeg(-6.5f,-5.8f,0.25f);
glPopMatrix();
glPushMatrix();
if(right_leg){
//glRotatef(15.0f,-1.0,0.0,0.0);
if(time==max_time) right_leg = !right_leg;
}else{
glRotatef(-15.0f,-1.0,0.0,0.0);
if(time==max_time) right_leg = !right_leg;
}
DrawDownLeg(-6.5f,-10.2f,0.25f);
glPopMatrix();
if(time==max_time) time = 0;
else time++;
}
double step =-60.0;
//处理场景的绘制
void Render(){
//在此处绘制
glClearColor(0.0f,0.0f,0.0f,0.0f); //设置清理色为黑色
glClear(GL_COLOR_BUFFER_BIT); //清理颜色/深度缓存
glPushMatrix(); //将当前矩阵压入矩阵堆栈
glLoadIdentity(); //复位矩阵
step+=0.001;
glTranslatef(0.0f,5.0f,step); //平移至(0,0,-30)
//glRotatef(90,0.0f,1.0f,0.0f);//绕其y轴旋转机器人
//glRotatef(15,1.0f,0.0f,0.0f);
DrawRobot(0.0f,0.0f,0.0f); //绘制机器人
glPopMatrix(); //弹出当前矩阵
glFlush();
SwapBuffers(g_HDC); //交换前后缓存
}
实验展示:
实现过程并没有多么困难的地方但是却比较复杂感觉,因为一些旋转角度和平移距离什么的都需要一遍遍运行看合不合适,最重要的还是矩阵的压栈和出栈那里,刚开始并不明白Pop和Push的用法,反复测试了几次才明白了,是用来控制当前在用矩阵的。里面的运动什么的主要是通过简单变量实现的,还需要继续完善。
实验源码:
#define WIN32_LEAN_AND_MEAN
#include <gl\glut.h>
#include <gl\GL.h>
#include <windows.h>
#pragma comment(lib, "openGL32.lib")
#pragma comment(lib, "glu32.lib")
float angle=0.0f;
HDC g_HDC;
bool fullScreen=false;
//绘制单位立方体
void DrawCube(float xPos,float yPos,float zPos)
{
glPushMatrix();
glTranslatef(xPos,yPos,zPos);
glBegin(GL_POLYGON);
glVertex3f(0.0f,0.0f,0.0f); //顶面
glVertex3f(0.0f,0.0f,-1.0f);
glVertex3f(-1.0f,0.0f,-1.0f);
glVertex3f(-1.0f,0.0f,0.0f);
glVertex3f(0.0f,0.0f,0.0f); //正面
glVertex3f(-1.0f,0.0f,0.0f);
glVertex3f(-1.0f,-1.0f,0.0f);
glVertex3f(0.0f,-1.0f,0.0f);
glVertex3f(0.0f,0.0f,0.0f); //右面
glVertex3f(0.0f,-1.0f,0.0f);
glVertex3f(0.0f,-1.0f,-1.0f);
glVertex3f(0.0f,0.0f,-1.0f);
glVertex3f(-1.0f,0.0f,0.0f); //左面
glVertex3f(-1.0f,0.0f,-1.0f);
glVertex3f(-1.0f,-1.0f,-1.0f);
glVertex3f(-1.0f,-1.0f,0.0f);
glVertex3f(0.0f,-1.0f,0.0f); //底面
glVertex3f(0.0f,-1.0f,-1.0f);
glVertex3f(-1.0f,-1.0f,-1.0f);
glVertex3f(-1.0f,-1.0f,0.0f);
glVertex3f(0.0f,0.0f,-1.0f); //背面
glVertex3f(-1.0f,0.0f,-1.0f);
glVertex3f(-1.0f,-1.0f,-1.0f);
glVertex3f(0.0f,-1.0f,-1.0f);
glEnd();
glPopMatrix();
}
//绘制头部
void DrawHead(float xPos,float yPos,float zPos)
{
//帽子
glPushMatrix();
glColor3f(0.6f,0.4f,0.12f); //褐色
glTranslatef(xPos+0.58,yPos+1.0,zPos+1.1);
glScalef(3.6f,0.8f,3.6f);
DrawCube(0.0f,0.0f,0.0f);
glPopMatrix();
//头
glPushMatrix();
glColor3f(1.0f,1.0f,1.0f); //白色
glTranslatef(xPos,yPos,zPos);
glScalef(2.0f,2.0f,2.0f); //机器人的头是一个2*2*2的立方体
DrawCube(0.0f,0.0f,0.0f);
glPopMatrix();
}
//绘制躯干
void DrawBody(float xPos,float yPos,float zPos)
{
glPushMatrix();
glColor3f(0.0f,0.0f,1.0f); //蓝色
glTranslatef(xPos,yPos,zPos);
glScalef(3.0f,5.5f,2.0f); //机器人的躯干是一个3*5.5*2的立方体
DrawCube(0.0f,0.0f,0.0f);
glPopMatrix();
}
//绘制手臂
void DrawUpArm(float xPos,float yPos,float zPos){
glPushMatrix();
glTranslatef(xPos,yPos,zPos);
glColor3f(0.0f,1.0f,1.0f); //青色
glScalef(0.8f,5.5f,0.8f); //机器人的上手臂是一个1*5.5*1的立方体
DrawCube(0.0f,0.0f,0.0f);
glPopMatrix();
}
//绘制大腿
void DrawUpLeg(float xPos,float yPos,float zPos){
glPushMatrix();
glColor3f(0.18f,0.56f,1.0f); //蓝色
glTranslatef(xPos,yPos,zPos);
glScalef(1.0f,4.0f,1.0f); //机器人的腿是一个1*4*1的立方体
DrawCube(0.0f,0.0f,0.0f);
glPopMatrix();
}
//绘制小腿
void DrawDownLeg(float xPos,float yPos,float zPos){
glPushMatrix();
glColor3f(0.18f,0.56f,1.0f); //蓝色
glTranslatef(xPos,yPos,zPos);
glScalef(1.0f,3.0f,1.0f); //机器人的腿是一个1*3*1的立方体
DrawCube(0.0f,0.0f,0.0f);
glPopMatrix();
}
//向前为true
bool right_hand = true;
bool right_leg = false;
int time = 0;
int max_time = 1000; //设定换臂时间
//画机器人
void DrawRobot(float xPos,float yPos,float zPos){
//绘制头
glPushMatrix();
//glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
DrawHead(-5.0f,2.5f,1.0f);
glPopMatrix();
glPushMatrix();
//右臂
if(right_hand){
glRotatef(-30.0f,-1.0,0.0,0.0);
}else{
glRotatef(30.0f,-1.0,0.0,0.0);
}
DrawUpArm(-3.2f,0.0f,0.25f);
glPopMatrix();
//躯体
DrawBody(-4.5f,0.0f,1.0f);
//左臂
glPushMatrix();
if(right_hand){
glRotatef(30.0f,-1.0,0.0,0.0);
if(time==max_time) right_hand = !right_hand;
}else{
glTranslatef(-0.5f,0.0f,-1.0f);
glRotatef(-30.0f,-1.0,0.0,0.0);
if(time==max_time) right_hand = !right_hand;
}
DrawUpArm(-8.0f,0.0f,0.25f);
glPopMatrix();
//右腿
glPushMatrix();
if(right_leg){
glRotatef(-15.0f,-1.0,0.0,0.0);
}else{
//glRotatef(15.0f,-1.0,0.0,0.0);
}
DrawUpLeg(-4.5f,-5.8f,0.25f);
glPopMatrix();
glPushMatrix();
if(right_leg){
glRotatef(-15.0f,-1.0,0.0,0.0);
}else{
//glRotatef(13.0f,-1.0,0.0,0.0);
}
DrawDownLeg(-4.5f,-10.2f,0.25f);
glPopMatrix();
//左腿
glPushMatrix();
if(right_leg){
//glRotatef(15.0f,-1.0,0.0,0.0);
}else{
glRotatef(-15.0f,-1.0,0.0,0.0);
}
DrawUpLeg(-6.5f,-5.8f,0.25f);
glPopMatrix();
glPushMatrix();
if(right_leg){
//glRotatef(15.0f,-1.0,0.0,0.0);
if(time==max_time) right_leg = !right_leg;
}else{
glRotatef(-15.0f,-1.0,0.0,0.0);
if(time==max_time) right_leg = !right_leg;
}
DrawDownLeg(-6.5f,-10.2f,0.25f);
glPopMatrix();
if(time==max_time) time = 0;
else time++;
}double step = -60.0;
//处理场景的绘制
void Render(){
//在此处绘制
glClearColor(0.0f,0.0f,0.0f,0.0f); //设置清理色为黑色
glClear(GL_COLOR_BUFFER_BIT); //清理颜色/深度缓存
glPushMatrix(); //将当前矩阵压入矩阵堆栈
glLoadIdentity(); //复位矩阵
glTranslatef(0.0f,5.0f,step+0.001); //平移至(0,0,-30)
//glRotatef(90,0.0f,1.0f,0.0f);//绕其y轴旋转机器人
//glRotatef(15,1.0f,0.0f,0.0f);
DrawRobot(0.0f,0.0f,0.0f); //绘制机器人
glPopMatrix(); //弹出当前矩阵
glFlush();
SwapBuffers(g_HDC); //交换前后缓存
}
//为设备环境设置像素格式的函数
void SetupPixelFormat(HDC hDC)
{
int nPixelFormat;
static PIXELFORMATDESCRIPTOR pfd=
{
sizeof(PIXELFORMATDESCRIPTOR),
1,
PFD_DRAW_TO_WINDOW|PFD_SUPPORT_OPENGL|PFD_DOUBLEBUFFER,
PFD_TYPE_RGBA,
32,
0,0,0,0,0,0,
0,
0,
0,
0,0,0,0,
16,
0,
0,
PFD_MAIN_PLANE,
0,
0,0,0
};
nPixelFormat=ChoosePixelFormat(hDC,&pfd);
SetPixelFormat(hDC,nPixelFormat,&pfd);
}
LRESULT CALLBACK WndProc(HWND hwnd,UINT message,WPARAM wParam,LPARAM lParam)
{
static HGLRC hRC;
static HDC hDC;
int width,height;
switch(message)
{
case WM_CREATE:
hDC=GetDC(hwnd);
g_HDC=hDC;
SetupPixelFormat(hDC);
hRC=wglCreateContext(hDC);
wglMakeCurrent(hDC,hRC);
return 0;
break;
case WM_CLOSE:
wglMakeCurrent(hDC,NULL);
wglDeleteContext(hRC);
PostQuitMessage(0);
return 0;
break;
case WM_SIZE:
height=HIWORD(lParam);
width=LOWORD(lParam);
if(height==0)
height=1;
glViewport(0,0,width,height);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
gluPerspective(54.0f,(GLfloat)width/(GLfloat)height,1.0f,1000.0f);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
return 0;
break;
default:
break;
}
return (DefWindowProc(hwnd,message,wParam,lParam));
}
int WINAPI WinMain(HINSTANCE hInstance,HINSTANCE hPrevInstance,LPSTR lpCmdLine,int nShowCmd)
{
WNDCLASSEX windowClass;
HWND hwnd;
MSG msg;
bool done;
DWORD dwExStyle;
DWORD dwStyle;
RECT windowRect;
int width=800;
int height=600;
int bits=32;
windowRect.left=(long)0;
windowRect.right=(long)width;
windowRect.top=(long)0;
windowRect.bottom=(long)height;
windowClass.cbSize=sizeof(WNDCLASSEX);
windowClass.style=CS_HREDRAW|CS_VREDRAW;
windowClass.lpfnWndProc=WndProc;
windowClass.cbClsExtra=0;
windowClass.cbWndExtra=0;
windowClass.hInstance=hInstance;
windowClass.hIcon=LoadIcon(NULL,IDI_APPLICATION);
windowClass.hCursor=LoadCursor(NULL,IDC_ARROW);
windowClass.hbrBackground=NULL;
windowClass.lpszMenuName=NULL;
windowClass.lpszClassName="MyClass";
windowClass.hIconSm=LoadIcon(NULL,IDI_WINLOGO);
if(!RegisterClassEx(&windowClass))
return 0;
if(fullScreen)
{
DEVMODE dmScreenSettings;
memset(&dmScreenSettings,0,sizeof(dmScreenSettings));
dmScreenSettings.dmSize=sizeof(dmScreenSettings);
dmScreenSettings.dmPelsWidth=width;
dmScreenSettings.dmPelsHeight=height;
dmScreenSettings.dmBitsPerPel=bits;
dmScreenSettings.dmFields=DM_BITSPERPEL|DM_PELSWIDTH|DM_PELSHEIGHT;
if(ChangeDisplaySettings(&dmScreenSettings,CDS_FULLSCREEN)!=DISP_CHANGE_SUCCESSFUL)
{
MessageBox(NULL,"Display mode failed",NULL,MB_OK);
fullScreen=FALSE;
}
}
if(fullScreen)
{
dwExStyle=WS_EX_APPWINDOW;
dwStyle=WS_POPUP;
ShowCursor(FALSE);
}
else
{
dwExStyle=WS_EX_APPWINDOW|WS_EX_WINDOWEDGE;
dwStyle=WS_OVERLAPPEDWINDOW;
}
AdjustWindowRectEx(&windowRect,dwStyle,FALSE,dwExStyle);
hwnd=CreateWindowEx(dwExStyle,"MyClass",
"Robot",
dwStyle|WS_CLIPCHILDREN|WS_CLIPSIBLINGS,
0,0,
windowRect.right-windowRect.left,
windowRect.bottom-windowRect.top,
NULL,
NULL,
hInstance,
NULL);
if(!hwnd)
return 0;
ShowWindow(hwnd,SW_SHOW);
UpdateWindow(hwnd);
done=false;
while(!done)
{
PeekMessage(&msg,hwnd,NULL,NULL,PM_REMOVE);
if(msg.message==WM_QUIT)
{
done=true;
}
else
{
Render();
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
if(fullScreen)
{
ChangeDisplaySettings(NULL,0);
ShowCursor(TRUE);
}
return msg.wParam;
}