欢迎使用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)
0 0
原创粉丝点击