欢迎使用CSDN-markdown编辑器
来源:互联网 发布:电脑学习软件 编辑:程序博客网 时间:2024/06/13 12:09
OpenGL学习小例子
——基于旋转,拾取,缩放,着色的实现
要求:根据OpenGL提供的直线,多边形绘制算法(橡皮筋效果),实现基于鼠标交互的卡通人物和其他环境物体的设计与绘制。使用颜色填充与反走样技术对卡通人物外貌以及衣着进行绘制,其他物体的绘制。实现对卡通人物或物体轮廓的交互控制,点击鼠标左键可以对人物或者物体进行拖拽移动调整。按“↑”按键能够实现卡通人物绕坐标原点(或指定点)进行旋转,按“z”键可实现对选中的人物或者物体进行放缩。 附加要求:选中其中的一个多边形区域,点击鼠标右键,弹出一个菜单,可以对该区域进行不同颜色的选择。可以设计发型、衣服的模版,当作文件进行存储,可以在窗口最右边设计一个模板库,显示保存的发型与衣服,拖拽到卡通人物上可以为卡通人物进行发型或者衣服的替换。
过程解析:
1.绘制身体的各个部分:脸、耳朵、眉毛等的单独函数;而且有些部位是可以移动的,所以把坐标传入作为函数的参数
2.绘制函数 drawObjects(GLenum mode),参数是模式,选择模式或者是绘制模式。当模式是绘制时,选择对应部位的颜色,并且把部位绘制出来;当模式是选择时,把部位的标号加载到名字栈中。然后在着色,绘制各个部位
3.绘制的最终回调函数myDisplay,作为glutDisplayFunc的回调函数,即最终绘制时所调用的函数,在本函数里面,先清除颜色缓存,然后转化为单位阵,在将旋转中心转移到中心,否则就是围绕着左下角的坐标原点旋转,写好旋转的函数之后,只需要根据上下的按键来改变theta的值,再然后重绘一次旋转的图形,就做到按键控制图形的旋转,再在渲染模式下绘制图形drawObjects(GL_RENDER);
4.myinit方法是把背景颜色设定成黑色
5.myReshape是为了在改变窗口的时候而设定的,首先调用函数glViewport函数来定义视口,然后进行窗口的裁剪,之后再开启反走样,保证视图变化之后的效果较好
6.myReshape是为了在改变窗口的时候而设定的,首先调用函数glViewport函数来定义视口,然后进行窗口的裁剪,之后再开启反走样,保证视图变化之后的效果较好
7.processHits方法是配合名字栈来使用,名字栈内存储着五官的标号,buffer即为名字栈,name代表着五官的标号,里面有两个for循环,第一个循环,hits代表转移到缓冲区中已命中的记录数,即已命中的记录条数
8.myMouseMove,首先是根据鼠标指定的位置选中区域,然后利用脸的颜色重绘原器官的位置,之后再在鼠标停留的位置调glutPostRedisplay函数重新绘制一次全图,造成一种移动的感官
9.myMouse把selectBuf作为拾取缓冲区,每一条命中记录都会存在这个缓冲区中,开启选择模式,初始化名字栈,并且在透视投影的条件下对图元进行操作,把矩阵保存,在初始化一个新的矩阵,这样做为了避免对矩阵操作之后对以后的矩阵变化造成困难,gluPickMatrix函数根据鼠标坐标x,y并圈定一块区域来进行操作,选择该区域的图元,drawObjects(GL_SELECT)函数,在选择模式下调用,把名字压栈并且绘制一次
10.创建菜单,根据glutAddMenuEntry函数来给菜单添加条目,第二个参数value的作用是,当你选择一项时,该值就会返回给glutCreateMenu里调用的函数。而在函数main_menu里面,选择的颜色参数传进去,在根据拾取图元的标记,把相应的颜色赋值给五官的颜色变量,最后调用重绘函数glutPostRedisplay,从而达到改变颜色的目的
代码
#include "stdafx.h"//隐藏控制台#pragma comment( linker, "/subsystem:\"windows\" /entry:\"mainCRTStartup\"")//五官的标记#define FACE 1 #define EARS 2 #define BROW 3#define LEFT_EYE 4#define RIGHT_EYE 5#define NOSE 6#define MOUTH 7#define HAIR 8 #define CROWN 9#define CAP 10//给个部位的颜色static int FACE_COLOR = 7;static int EARS_COLOR = 5;static int BROW_COLOR = 0;static int EYES_COLOR = 0;static int NOSE_COLOR = 6;static int MOUTH_COLOR = 1;static int HAIR_COLOR = 3;static int CROWN_COLOR = 6;static int CAP_COLOR = 4;static GLfloat theta = 0;//旋转的角度static GLfloat scaleX = 1.0;static GLfloat scaleY = 1.0;static GLfloat scaleZ = 1.0;//缩放的尺寸int select_part = 0; //点击时选中区域static GLfloat colors[8][3] = { { 0.0, 0.0, 0.0 }, { 1.0, 0.0, 0.0 }, { 0.0, 1.0, 0.0 }, { 0.0, 0.0, 1.0 }, { 0.0, 1.0, 1.0 }, { 1.0, 0.0, 1.0 }, { 1.0, 1.0, 0.0 }, { 1.0, 1.0, 1.0 } };//黑色0 红色1 绿色2 蓝色3 青色4 紫色5 黄色6 白色7 #define SIZE 512 #define WIN_WIDTH 700 #define WIN_HEIGHT 500 //裁剪窗口的大小#define VIEW_WIDTH 700 #define VIEW_HEIGHT 500 //视图窗口的大小,单位是像素//画脸void drawFace(){ glBegin(GL_POLYGON); glVertex2f(150, 300); for (int i = 150; i <= 350; i = i + 1) glVertex2f(i, 0.008*(i - 250)*(i - 250) + 120); glVertex2f(350, 300); glEnd();}//画耳朵void drawEars(){ //right ear glBegin(GL_POLYGON); glVertex2f(150, 270); glVertex2f(140, 280); glVertex2f(140, 230); glVertex2f(150, 240); glEnd(); //left ear glBegin(GL_POLYGON); glVertex2f(350, 270); glVertex2f(360, 280); glVertex2f(360, 230); glVertex2f(350, 240); glEnd();}//画眉毛void drawBrow(){ glLineWidth(10); glBegin(GL_LINES); glVertex2f(180, 290); glVertex2f(210, 270); glVertex2f(290, 270); glVertex2f(320, 290); glEnd();}//画眼睛int rex = 200;int rey = 250;void drawRightEye(int Ax, int Ay){ rex = Ax; rey = Ay; glBegin(GL_POLYGON); glVertex2f(rex - 20, rey + 20); glVertex2f(rex - 20, rey); glVertex2f(rex + 10, rey - 20); glVertex2f(rex + 10, rey); glEnd();}int lex = 300;int ley = 250;void drawLeftEye(int Ax, int Ay){ lex = Ax; ley = Ay; glBegin(GL_POLYGON); glVertex2f(lex - 10, ley); glVertex2f(lex - 10, ley - 20); glVertex2f(lex + 20, ley); glVertex2f(lex + 20, ley + 20); glEnd();}//画鼻子int nx = 250;int ny = 220;void drawNose(int Ax, int Ay){ nx = Ax; ny = Ay; glBegin(GL_TRIANGLES); glVertex2f(nx + 0, ny + 10); glVertex2f(nx - 10, ny - 10); glVertex2f(nx + 10, ny - 10); glEnd();}//画嘴巴int mx = 250;int my = 170;void drawMouth(int Ax, int Ay){ mx = Ax; my = Ay; glBegin(GL_POLYGON); glVertex2f(mx - 20, my + 10); glVertex2f(mx - 10, my - 10); glVertex2f(mx + 10, my - 10); glVertex2f(mx + 20, my + 10); glEnd();}//画头发void drawHair(){ glBegin(GL_TRIANGLE_FAN); glVertex2f(160, 300); glVertex2f(140, 270); for (int i = 100; i <= 400; i = i + 10) glVertex2f(i, 400 - 0.004444*(i - 250)*(i - 250));// glVertex2f(380, 270); glEnd();}//画皇冠int crownx = 250;int crowny = 436;void drawCrown(int movex, int movey){ crownx = movex; crowny = movey; glBegin(GL_POLYGON); glVertex2f(-40 + crownx, crowny - 36); glVertex2f(-52 + crownx, 24 + crowny); glVertex2f(-20 + crownx, crowny - 8); glVertex2f(0 + crownx, 36 + crowny); glVertex2f(20 + crownx, crowny - 8); glVertex2f(52 + crownx, 24 + crowny); glVertex2f(40 + crownx, crowny - 36); glEnd();}//绘制模板库void drawFormBoard(){ glBegin(GL_LINES); glVertex2f(500, 0); glVertex2f(500, 700); glEnd(); glBegin(GL_LINES); glVertex2f(500, 250); glVertex2f(700, 250); glEnd();}//绘制模板库里面的帽子int capX = 600;int capY = 400;void drawCap(int moveX, int moveY){ capX = moveX; capY = moveY; glBegin(GL_POLYGON); glVertex2f(0 + capX, capY - 20); glVertex2f(-52 + capX, capY - 40); glVertex2f(-100 + capX, capY - 40); glVertex2f(-48 + capX, capY - 8); glVertex2f(0 + capX, capY + 80); glVertex2f(48 + capX, capY - 8); glVertex2f(100 + capX, capY - 40); glVertex2f(52 + capX, capY - 40); glEnd();}//绘制函数 void drawObjects(GLenum mode){ //绘制模板库的线 glColor3f(1, 1, 1); drawFormBoard(); //画脸 if (mode == GL_SELECT) //此时模式是选取模式而不是渲染模式 glLoadName(FACE); glColor3f(colors[FACE_COLOR][0], colors[FACE_COLOR][1], colors[FACE_COLOR][2]); drawFace(); //画耳朵 if (mode == GL_SELECT) glLoadName(EARS); glColor3f(colors[EARS_COLOR][0], colors[EARS_COLOR][1], colors[EARS_COLOR][2]); drawEars(); //画眉毛 if (mode == GL_SELECT) glLoadName(BROW); glColor3f(colors[BROW_COLOR][0], colors[BROW_COLOR][1], colors[BROW_COLOR][2]); drawBrow(); //画眼睛 if (mode == GL_SELECT) glLoadName(LEFT_EYE); glColor3f(colors[EYES_COLOR][0], colors[EYES_COLOR][1], colors[EYES_COLOR][2]); drawLeftEye(lex, ley); if (mode == GL_SELECT) glLoadName(RIGHT_EYE); glColor3f(colors[EYES_COLOR][0], colors[EYES_COLOR][1], colors[EYES_COLOR][2]); drawRightEye(rex, rey); //画鼻子 if (mode == GL_SELECT) glLoadName(NOSE); glColor3f(colors[NOSE_COLOR][0], colors[NOSE_COLOR][1], colors[NOSE_COLOR][2]); drawNose(nx, ny); //画嘴巴 if (mode == GL_SELECT) glLoadName(MOUTH); glColor3f(colors[MOUTH_COLOR][0], colors[MOUTH_COLOR][1], colors[MOUTH_COLOR][2]); drawMouth(mx, my); //画头发 if (mode == GL_SELECT) glLoadName(HAIR); glColor3f(colors[HAIR_COLOR][0], colors[HAIR_COLOR][1], colors[HAIR_COLOR][2]); drawHair(); //画皇冠 if (mode == GL_SELECT) glLoadName(CROWN); glColor3f(colors[CROWN_COLOR][0], colors[CROWN_COLOR][1], colors[CROWN_COLOR][2]); drawCrown(crownx,crowny); //画模板库里面的帽子 if (mode == GL_SELECT) glLoadName(CAP); glColor3f(colors[CAP_COLOR][0], colors[CAP_COLOR][1], colors[CAP_COLOR][2]); drawCap(capX, capY);}void myDisplay(){ //清除缓存 glClear(GL_COLOR_BUFFER_BIT); //旋转 glLoadIdentity();//将当前的用户坐标系的原点移到了屏幕中心 //绕系统原点 //旋转函数的后三个坐标是指定围绕哪一个方向旋转,而不是围绕哪一个点旋转,同时也引入缩放函数 glTranslatef(250, 250, 0.0); glRotatef(theta, 0.0, 0.0, 1.0); glScalef(scaleX, scaleY, scaleZ); glTranslatef(-250, -250, 0.0); //RANDER模式绘制物体 drawObjects(GL_RENDER); //绘制 glFlush();}void myInit(){ glClearColor(0.0, 0.0, 0.0, 1.0); //指定清除之后的颜色,即设定背景是黑色}void myReshape(int w, int h){ glViewport(0, 0, w, h); //定义视口区域 glMatrixMode(GL_PROJECTION); //投影模型 glLoadIdentity(); //gluOrtho2D是二位裁剪函数,规定了窗口坐标与像素的比例,相当于一个比例尺 //此时像素跟坐标的比例是一比一,因为二者的设定都是500*500 gluOrtho2D(0, VIEW_WIDTH, 0, VIEW_HEIGHT); glMatrixMode(GL_MODELVIEW); //模型视图 glLoadIdentity(); //开启反走样 glEnable(GL_BLEND); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); glHint(GL_POLYGON_SMOOTH_HINT, GL_NICEST); glEnable(GL_POLYGON_SMOOTH); glEnable(GL_POINT_SMOOTH); glEnable(GL_LINE_SMOOTH);}static bool left_down = false;void processHits(GLint hits, GLuint buffer[]){ unsigned int i, j; GLuint names, *ptr; printf("本次选中%d个对象!\n", hits); ptr = (GLuint *)buffer; for (i = 0; i < hits; i++) {// for each hit names = *ptr; ptr = ptr + 3; for (j = 0; j < names; j++) { if (*ptr == 1){ printf("打脸\n"); } else if (*ptr == 2){ printf("耳朵\n"); //select_part =2; select_part = EARS; } else if (*ptr == 3){ printf("眉毛\n"); //select_part =3; select_part = BROW; } else if (*ptr == 4){ printf("左眼\n"); //select_part =4; select_part = LEFT_EYE; } else if (*ptr == 5){ printf("右眼\n"); //select_part =5; select_part = RIGHT_EYE; } else if (*ptr == 6){ printf("鼻子\n"); //select_part =6; select_part = NOSE; } else if (*ptr == 7){ printf("嘴巴\n"); //select_part =7; select_part = MOUTH; } else if (*ptr == 8){ printf("头发\n"); //select_part =8; select_part = HAIR; } else if (*ptr == 9){ printf("皇冠\n"); //select_part =8; select_part = CROWN; } else if (*ptr == 10){ printf("帽子\n"); //select_part =8; select_part = CAP; } ptr++; } } printf("select_part:%d\n", select_part);}void myMouseMove(int x, int y){ if (select_part == RIGHT_EYE){//(180 < x) && (x < 210) && (230 < (500 - y)) && ((500 - y) < 270) //500-y是因为视口坐标系跟像素矩形坐标系不一样 //覆盖原图 glColor3ub(colors[FACE_COLOR][0], colors[FACE_COLOR][1], colors[FACE_COLOR][2]); drawRightEye(rex, rey); //重绘 rex = x; rey = (500 - y); glColor3f(1.0, 1.0, 1.0); drawRightEye(rex, rey); glutPostRedisplay();//需要标记当前窗口需要重新绘制 } else if (select_part == LEFT_EYE){//(290 < x) && (x < 320) && (230 < (500 - y)) && ((500 - y) < 270) //cout<<"移动右眼"<<endl; //覆盖原图 glColor3ub(colors[FACE_COLOR][0], colors[FACE_COLOR][1], colors[FACE_COLOR][2]); drawLeftEye(lex, ley); //重绘 lex = x; ley = (500 - y); glColor3f(1.0, 1.0, 1.0); drawLeftEye(lex, ley); glutPostRedisplay(); } else if (select_part == MOUTH){//(230 < x) && (x < 270) && (160 < (500 - y)) && ((500 - y) < 180) //cout<<"移动嘴巴"<<endl; //覆盖原图 glColor3ub(colors[FACE_COLOR][0], colors[FACE_COLOR][1], colors[FACE_COLOR][2]); drawMouth(mx, my); //重绘 mx = x; my = (500 - y); glColor3f(1.0, 1.0, 1.0); drawMouth(mx, my); glutPostRedisplay(); } else if (select_part == NOSE){//(240<x) && (x<260) && (210<(500 - y)) && ((500 - y)<230) //cout<<"移动鼻子"<<endl; //覆盖原图 glColor3ub(colors[FACE_COLOR][0], colors[FACE_COLOR][1], colors[FACE_COLOR][2]); drawNose(mx, my); //重绘 nx = x; ny = (500 - y); glColor3f(colors[NOSE_COLOR][0], colors[NOSE_COLOR][1], colors[NOSE_COLOR][2]); drawNose(nx, ny); glutPostRedisplay(); } else if (select_part ==CROWN){//(230<x) && (x<270) && (420<(500 - y)) && ((500 - y)<460) //利用背景黑色覆盖皇冠 glColor3f(0.0, 0.0, 0.0); drawCrown(crownx, crowny); //重绘 crownx = x; crowny = (500 - y); glColor3f(colors[CROWN_COLOR][0], colors[CROWN_COLOR][1], colors[CROWN_COLOR][2]); glutPostRedisplay(); } else if (select_part == CAP){ //利用背景黑色覆盖帽子 glColor3f(0.0, 0.0, 0.0); drawCap(capX, capX); //重绘 capX = x; capY = (500 - y); glColor3f(colors[CAP_COLOR][0], colors[CAP_COLOR][1], colors[CAP_COLOR][2]); glutPostRedisplay(); }}//鼠标响应 ,抓取图元void myMouse(int button, int state, int x, int y){ GLuint selectBuf[SIZE]; GLint hits; GLint viewport[4]; //进行颜色选择 if (button == GLUT_LEFT_BUTTON && state == GLUT_UP) { left_down = false; //设置一个用于拾取操作的观察空间,viewport的四个参数分别是坐标和尺寸 glGetIntegerv(GL_VIEWPORT, viewport); // 创建名称缓冲区 glSelectBuffer(SIZE, selectBuf); //切换到选择模式 glRenderMode(GL_SELECT); // 初始化名称栈 glInitNames(); glPushName(0); glMatrixMode(GL_PROJECTION); //进行矩阵压栈操作,操作拾取图元之后进行弹栈 glPushMatrix(); glLoadIdentity(); //viewport是视口的y的坐标,要与鼠标的坐标进行转换 gluPickMatrix((GLdouble)x, (GLdouble)(viewport[3] - y), 3.0, 3.0, viewport); //二位物体投射到二位平面上 gluOrtho2D(0, VIEW_WIDTH, 0, VIEW_HEIGHT); drawObjects(GL_SELECT); glPopMatrix(); glMatrixMode(GL_MODELVIEW); //绘制模式(GL_RENDER),选择模式(GL_SELECT),反馈模式(GL_FEEDBACK),函数的返回值可以确定选择模式下的命中次数 /*返回值hits表示The number of hit records transferred to the select buffer(转移到缓冲区中已命中的记录数)。hits = glRenderMode(GL_RENDER); 返回的是命中的记录条数。根据条数用循环可以读出缓冲区中每个命中物体的名字,根据名字即可画出来。*/ hits = glRenderMode(GL_RENDER); processHits(hits, selectBuf); //再次绘制 glutPostRedisplay(); }}//根据鼠标按键来进行旋转void mySpecial(int key, int x, int y){ switch (key) { case GLUT_KEY_UP: theta = (theta + 10); glutPostRedisplay(); break; case GLUT_KEY_DOWN: theta = (theta - 10); glutPostRedisplay(); break; }}//根据键盘按键来操作缩放,先用Ctrl+空格锁定输入法,之后Z/A整体缩放,S/X横向缩放,Y/H纵向缩放void myKeyBoard(unsigned char key, int x, int y){ switch (key) { case 'x': case 'X': scaleX = scaleX + 0.1; glutPostRedisplay(); break; case 's': case 'S': scaleX = scaleX - 0.1; glutPostRedisplay(); break; case 'Y': case 'y': scaleY = scaleY + 0.1; glutPostRedisplay(); break; case 'h': case 'H': scaleY = scaleY - 0.1; glutPostRedisplay(); break; case 'Z': case 'z': scaleY = scaleY + 0.1; scaleX = scaleX + 0.1; glutPostRedisplay(); break; case 'a': case 'A': scaleY = scaleY - 0.1; scaleX = scaleX - 0.1; glutPostRedisplay(); break; }}void main_menu(int index) { if (index == -1) return; switch (select_part) { case FACE: FACE_COLOR = index; break; case EARS: EARS_COLOR = index; break; case BROW: BROW_COLOR = index; break; case LEFT_EYE: EYES_COLOR = index; break; case RIGHT_EYE: EYES_COLOR = index; break; case NOSE: NOSE_COLOR = index; break; case MOUTH: MOUTH_COLOR = index; break; case HAIR: HAIR_COLOR = index; break; case CROWN: CROWN_COLOR = index; break; case CAP: CAP_COLOR = index; break; } glutPostRedisplay();}void main(int argc, char** argv){ glutInit(&argc, argv); glutInitDisplayMode(GLUT_SINGLE | GLUT_RGB); glutInitWindowSize(700, 500); glutInitWindowPosition(500, 100); glutCreateWindow("test1"); myInit(); /*glutReshapeFunc是窗口改变的时候调用的函数, 在这个里面可以根据缩放后的窗口重新设置camera的内部参数*/ glutReshapeFunc(myReshape); glutDisplayFunc(myDisplay); /*使用glutMouseFunc函数注册鼠标响应事件, 使用glutKeyboardFunc函数注册键盘响应事件, 对键盘上特殊的4个方向按键的响应函数是glutSpecialFunc。 使用glutKeyboardFunc对键盘进行侦听,并作出对应按键的函数响应*/ glutMouseFunc(myMouse); glutSpecialFunc(mySpecial); glutKeyboardFunc(myKeyBoard); /*这个函数是处理当鼠标键摁下时,鼠标拖动的事件。当鼠标拖动时,将每一帧都调用一次这个函数*/ glutMotionFunc(myMouseMove); //右击事件 glutCreateMenu(main_menu); glutAddMenuEntry("选择颜色", -1); glutAddMenuEntry("黑色", 0); glutAddMenuEntry("红色", 1); glutAddMenuEntry("绿色", 2); glutAddMenuEntry("蓝色", 3); glutAddMenuEntry("青色", 4); glutAddMenuEntry("紫色", 5); glutAddMenuEntry("黄色", 6); glutAddMenuEntry("白色", 7); glutAttachMenu(GLUT_RIGHT_BUTTON); glutMainLoop();}![实验的最终结果,按键可以进行相应的变换](http://img.blog.csdn.net/20170407113852806?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvcXFfMzM5NDUyNDY=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast)
- 欢迎使用CSDN-markdown编辑器
- 欢迎使用CSDN-markdown编辑器
- 欢迎使用CSDN-markdown编辑器
- 欢迎使用CSDN-markdown编辑器
- 欢迎使用CSDN-markdown编辑器
- 欢迎使用CSDN-markdown编辑器
- 欢迎使用CSDN-markdown编辑器
- 欢迎使用CSDN-markdown编辑器
- 欢迎使用CSDN-markdown编辑器
- 欢迎使用CSDN-markdown编辑器
- 欢迎使用CSDN-markdown编辑器
- 欢迎使用CSDN-markdown编辑器
- 欢迎使用CSDN-markdown编辑器
- 欢迎使用CSDN-markdown编辑器
- 欢迎使用CSDN-markdown编辑器
- 欢迎使用CSDN-markdown编辑器
- 欢迎使用CSDN-markdown编辑器
- 欢迎使用CSDN-markdown编辑器
- CSS学习笔记:1 基础语法
- 事物冲突
- Android--手机卫士涉及的知识点总结(五)
- Two Sum
- javascript自制alert提示框
- 欢迎使用CSDN-markdown编辑器
- java-jvm-jstack-(监视器和锁的概念)
- BZOJ4810 [Ynoi2017]由乃的玉米田
- 编译环境搭建计划(2017.4.7 ~ 2017.4.9)
- NCV63-portal单点登录集成方案
- Spring 之 IOC/DI
- 99乘法表
- tensorflow03 《TensorFlow实战Google深度学习框架》笔记-04-01
- jquey ajax 将变量值封装json传入JAVA action获取解析